i18n fixes
[srvx.git] / src / proto-common.c
1 /* proto-common.c - common IRC protocol parsing/sending support
2  * Copyright 2000-2004 srvx Development Team
3  *
4  * This file is part of srvx.
5  *
6  * srvx is free software; you can redistribute it and/or modify
7  * it under the terms of the GNU General Public License as published by
8  * the Free Software Foundation; either version 2 of the License, or
9  * (at your option) any later version.
10  *
11  * This program is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14  * GNU General Public License for more details.
15  *
16  * You should have received a copy of the GNU General Public License
17  * along with srvx; if not, write to the Free Software Foundation,
18  * Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA.
19  */
20
21 #include "conf.h"
22 #include "gline.h"
23 #include "ioset.h"
24 #include "log.h"
25 #include "nickserv.h"
26 #include "timeq.h"
27 #ifdef HAVE_SYS_SOCKET_H
28 #include <sys/socket.h>
29 #endif
30 #ifdef HAVE_NETINET_IN_H
31 #include <netinet/in.h>
32 #endif
33 #ifdef HAVE_ARPA_INET_H
34 #include <arpa/inet.h>
35 #endif
36
37 unsigned int lines_processed;
38 FILE *replay_file;
39 struct io_fd *socket_io_fd;
40 int force_n2k;
41 const char *hidden_host_suffix;
42
43 static char replay_line[MAXLEN+80];
44 static int ping_freq;
45 static int ping_timeout;
46 static int replay_connected;
47 static unsigned int nicklen = NICKLEN; /* how long do we think servers allow nicks to be? */
48 static struct userList dead_users;
49
50 extern struct cManagerNode cManager;
51 extern unsigned long burst_length;
52 extern struct cManagerNode cManager;
53 extern struct policer_params *oper_policer_params, *luser_policer_params;
54 extern server_link_func_t *slf_list;
55 extern unsigned int slf_size, slf_used;
56 extern new_user_func_t *nuf_list;
57 extern unsigned int nuf_size, nuf_used;
58 extern del_user_func_t *duf_list;
59 extern unsigned int duf_size, duf_used;
60 extern time_t boot_time;
61
62 void received_ping(void);
63
64 static int replay_read(void);
65 static dict_t irc_func_dict;
66
67 typedef void (*foreach_chanfunc) (struct chanNode *chan, void *data);
68 typedef void (*foreach_nonchan) (char *name, void *data);
69 typedef void (*foreach_userfunc) (struct userNode *user, void *data);
70 typedef void (*foreach_nonuser) (char *name, void *data);
71 static void parse_foreach(char *target_list, foreach_chanfunc cf, foreach_nonchan nc, foreach_userfunc uf, foreach_nonuser nu, void *data);
72
73 static void
74 uplink_readable(struct io_fd *fd) {
75     static char buffer[MAXLEN];
76     char *eol;
77     int pos;
78
79     pos = ioset_line_read(fd, buffer, sizeof(buffer));
80     if (pos <= 0) {
81         close_socket();
82         return;
83     }
84     if ((eol = strpbrk(buffer, "\r\n"))) *eol = 0;
85     log_replay(MAIN_LOG, false, buffer);
86     if (cManager.uplink->state != DISCONNECTED)
87         parse_line(buffer, 0);
88     lines_processed++;
89 }
90
91 void
92 socket_destroyed(struct io_fd *fd)
93 {
94     if (fd && fd->eof)
95         log_module(MAIN_LOG, LOG_ERROR, "Connection to server lost.");
96     socket_io_fd = NULL;
97     cManager.uplink->state = DISCONNECTED;
98     if (self->uplink) DelServer(self->uplink, 0, NULL);
99 }
100
101 void replay_event_loop(void)
102 {
103     while (!quit_services) {
104         if (!replay_connected) {
105             /* this time fudging is to get some of the logging right */
106             self->link = self->boot = now;
107             cManager.uplink->state = AUTHENTICATING;
108             irc_introduce(cManager.uplink->password);
109             replay_connected = 1;
110         } else if (!replay_read()) {
111             log_module(MAIN_LOG, LOG_ERROR, "Connection to server lost.");
112             close_socket();
113         }
114         timeq_run();
115     }
116 }
117
118 int
119 create_socket_client(struct uplinkNode *target)
120 {
121     int port = target->port;
122     const char *addr = target->host;
123
124     if (replay_file)
125         return feof(replay_file) ? 0 : 1;
126
127     if (socket_io_fd) {
128         /* Leave the existing socket open, say we failed. */
129         log_module(MAIN_LOG, LOG_ERROR, "Refusing to create second connection to %s:%d.", addr, port);
130         return 0;
131     }
132
133     log_module(MAIN_LOG, LOG_INFO, "Connecting to %s:%i...", addr, port);
134
135     socket_io_fd = ioset_connect((struct sockaddr*)cManager.uplink->bind_addr, sizeof(struct sockaddr), addr, port, 1, 0, NULL);
136     if (!socket_io_fd) {
137         log_module(MAIN_LOG, LOG_ERROR, "Connection to uplink failed: %s (%d)", strerror(errno), errno);
138         target->state = DISCONNECTED;
139         target->tries++;
140         return 0;
141     }
142     socket_io_fd->readable_cb = uplink_readable;
143     socket_io_fd->destroy_cb = socket_destroyed;
144     socket_io_fd->line_reads = 1;
145     socket_io_fd->wants_reads = 1;
146     log_module(MAIN_LOG, LOG_INFO, "Connection to server established.");
147     cManager.uplink = target;
148     target->state = AUTHENTICATING;
149     target->tries = 0;
150     return 1;
151 }
152
153 void
154 replay_read_line(void)
155 {
156     struct tm timestamp;
157     time_t new_time;
158
159     if (replay_line[0]) return;
160   read_line:
161     if (!fgets(replay_line, sizeof(replay_line), replay_file)) {
162         if (feof(replay_file)) {
163             quit_services = 1;
164             memset(replay_line, 0, sizeof(replay_line));
165             return;
166         }
167     }
168     if ((replay_line[0] != '[')
169         || (replay_line[3] != ':')
170         || (replay_line[6] != ':')
171         || (replay_line[9] != ' ')
172         || (replay_line[12] != '/')
173         || (replay_line[15] != '/')
174         || (replay_line[20] != ']')
175         || (replay_line[21] != ' ')) {
176         log_module(MAIN_LOG, LOG_ERROR, "Unrecognized timestamp in replay file: %s", replay_line);
177         goto read_line;
178     }
179     timestamp.tm_hour = strtoul(replay_line+1, NULL, 10);
180     timestamp.tm_min = strtoul(replay_line+4, NULL, 10);
181     timestamp.tm_sec = strtoul(replay_line+7, NULL, 10);
182     timestamp.tm_mon = strtoul(replay_line+10, NULL, 10) - 1;
183     timestamp.tm_mday = strtoul(replay_line+13, NULL, 10);
184     timestamp.tm_year = strtoul(replay_line+16, NULL, 10) - 1900;
185     timestamp.tm_isdst = 0;
186     new_time = mktime(&timestamp);
187     if (new_time == -1) {
188         log_module(MAIN_LOG, LOG_ERROR, "Unable to parse time struct tm_sec=%d tm_min=%d tm_hour=%d tm_mday=%d tm_mon=%d tm_year=%d", timestamp.tm_sec, timestamp.tm_min, timestamp.tm_hour, timestamp.tm_mday, timestamp.tm_mon, timestamp.tm_year);
189     } else {
190         now = new_time;
191     }
192
193     if (strncmp(replay_line+22, "(info) ", 7))
194         goto read_line;
195     return;
196 }
197
198 static int
199 replay_read(void)
200 {
201     size_t len;
202     char read_line[MAXLEN];
203     while (1) {
204         replay_read_line();
205         /* if it's a sent line, break out to handle it */
206         if (!strncmp(replay_line+29, "   ", 3))
207             break;
208         if (!strncmp(replay_line+29, "W: ", 3)) {
209             log_module(MAIN_LOG, LOG_ERROR, "Expected response from services: %s", replay_line+32);
210             replay_line[0] = 0;
211         } else {
212             return 0;
213         }
214     }
215     log_replay(MAIN_LOG, false, replay_line+32);
216     safestrncpy(read_line, replay_line+32, sizeof(read_line));
217     len = strlen(read_line);
218     if (read_line[len-1] == '\n')
219         read_line[--len] = 0;
220     replay_line[0] = 0;
221     parse_line(read_line, 0);
222     lines_processed++;
223     return 1;
224 }
225
226 static void
227 replay_write(char *text)
228 {
229     replay_read_line();
230     if (strncmp(replay_line+29, "W: ", 3)) {
231         log_module(MAIN_LOG, LOG_ERROR, "Unexpected output during replay: %s", text);
232         return;
233     } else {
234         if (strcmp(replay_line+32, text)) {
235             log_module(MAIN_LOG, LOG_ERROR, "Incorrect output during replay:\nReceived: %sExpected: %s", text, replay_line+32);
236         } else {
237             log_replay(MAIN_LOG, true, text);
238         }
239         replay_line[0] = 0;
240     }
241 }
242
243 void putsock(const char *text, ...) PRINTF_LIKE(1, 2);
244
245 void
246 putsock(const char *text, ...)
247 {
248     va_list arg_list;
249     char buffer[MAXLEN];
250     int pos;
251
252     if (!cManager.uplink || cManager.uplink->state == DISCONNECTED) return;
253     buffer[0] = '\0';
254     va_start(arg_list, text);
255     pos = vsnprintf(buffer, MAXLEN - 2, text, arg_list);
256     va_end(arg_list);
257     if (pos < 0 || pos > (MAXLEN - 2)) pos = MAXLEN - 2;
258     buffer[pos] = 0;
259     if (!replay_file) {
260         log_replay(MAIN_LOG, true, buffer);
261         buffer[pos++] = '\n';
262         buffer[pos] = 0;
263         ioset_write(socket_io_fd, buffer, pos);
264     } else {
265         replay_write(buffer);
266     }
267 }
268
269 void
270 close_socket(void)
271 {
272     if (replay_file) {
273         replay_connected = 0;
274         socket_destroyed(socket_io_fd);
275     } else {
276         ioset_close(socket_io_fd->fd, 1);
277     }
278 }
279
280 #define CMD_FUNC(NAME) int NAME(UNUSED_ARG(const char *origin), UNUSED_ARG(unsigned int argc), UNUSED_ARG(char **argv))
281 typedef CMD_FUNC(cmd_func_t);
282
283 static void timed_ping_timeout(void *data);
284
285 /* Ping state is kept in the timeq (only one of these two can be in
286  * the queue at any given time). */
287 void
288 timed_send_ping(UNUSED_ARG(void *data))
289 {
290     irc_ping(self->name);
291     timeq_add(now + ping_timeout, timed_ping_timeout, 0);
292 }
293
294 static void
295 timed_ping_timeout(UNUSED_ARG(void *data))
296 {
297     /* Uplink "health" tracking could be accomplished by counting the
298        number of ping timeouts that happen for each uplink. After the
299        timeouts per time period exceeds some amount, the uplink could
300        be marked as unavalable.*/
301     irc_squit(self, "Ping timeout.", NULL);
302 }
303
304 static CMD_FUNC(cmd_pass)
305 {
306     const char *true_pass;
307
308     if (argc < 2)
309         return 0;
310     true_pass = cManager.uplink->their_password;
311     if (true_pass && strcmp(true_pass, argv[1])) {
312         /* It might be good to mark the uplink as unavailable when
313            this happens, though there should be a way of resetting
314            the flag. */
315         irc_squit(self, "Incorrect password received.", NULL);
316         return 1;
317     }
318
319     cManager.uplink->state = BURSTING;
320     return 1;
321 }
322
323 static CMD_FUNC(cmd_dummy)
324 {
325     /* we don't care about these messages */
326     return 1;
327 }
328
329 static CMD_FUNC(cmd_error)
330 {
331     if (argv[1]) log_module(MAIN_LOG, LOG_ERROR, "Error: %s", argv[1]);
332     log_module(MAIN_LOG, LOG_ERROR, "Error received from uplink, squitting.");
333
334     if (cManager.uplink->state != CONNECTED) {
335         /* Error messages while connected should be fine. */
336         cManager.uplink->flags |= UPLINK_UNAVAILABLE;
337         log_module(MAIN_LOG, LOG_ERROR, "Disabling uplink.");
338     }
339
340     close_socket();
341     return 1;
342 }
343
344 static CMD_FUNC(cmd_stats)
345 {
346     struct userNode *un;
347
348     if (argc < 2)
349         return 0;
350     if (!(un = GetUserH(origin)))
351         return 0;
352     switch (argv[1][0]) {
353     case 'u': {
354         unsigned int uptime = now - boot_time;
355         irc_numeric(un, RPL_STATSUPTIME, ":Server Up %d days %d:%02d:%02d",
356                     uptime/(24*60*60), (uptime/(60*60))%24, (uptime/60)%60, uptime%60);
357         irc_numeric(un, RPL_MAXCONNECTIONS, ":Highest connection count: %d (%d clients)",
358                     self->max_clients+1, self->max_clients);
359         break;
360     }
361     default: /* unrecognized/unhandled stats output */ break;
362     }
363     irc_numeric(un, 219, "%s :End of /STATS report", argv[1]);
364     return 1;
365 }
366
367 static CMD_FUNC(cmd_whois)
368 {
369     struct userNode *from;
370     struct userNode *who;
371
372     if (argc < 3)
373         return 0;
374     if (!(from = GetUserH(origin))) {
375         log_module(MAIN_LOG, LOG_ERROR, "Could not find WHOIS origin user %s", origin);
376         return 0;
377     }
378     if(!(who = GetUserH(argv[2]))) {
379         irc_numeric(from, ERR_NOSUCHNICK, "%s@%s :No such nick", argv[2], self->name);
380         return 1;
381     }
382     if (IsHiddenHost(who) && !IsOper(from)) {
383         /* Just stay quiet. */
384         return 1;
385     }
386     irc_numeric(from, RPL_WHOISUSER, "%s %s %s * :%s", who->nick, who->ident, who->hostname, who->info);
387     irc_numeric(from, RPL_WHOISSERVER, "%s %s :%s", who->nick, who->uplink->name, who->uplink->description);
388     if (IsOper(who)) {
389         irc_numeric(from, RPL_WHOISOPERATOR, "%s :is a megalomaniacal power hungry tyrant", who->nick);
390     }
391     irc_numeric(from, RPL_ENDOFWHOIS, "%s :End of /WHOIS list", who->nick);
392     return 1;
393 }
394
395 static CMD_FUNC(cmd_version)
396 {
397     struct userNode *user;
398     if (!(user = GetUserH(origin))) {
399         log_module(MAIN_LOG, LOG_ERROR, "Could not find VERSION origin user %s", origin);
400         return 0;
401     }
402     irc_numeric(user, 351, "%s.%s %s :%s", PACKAGE_TARNAME, PACKAGE_VERSION, self->name, CODENAME);
403     return 1;
404 }
405
406 static CMD_FUNC(cmd_admin)
407 {
408     struct userNode *user;
409     struct string_list *slist;
410
411     if (!(user = GetUserH(origin))) {
412         log_module(MAIN_LOG, LOG_ERROR, "Could not find ADMIN origin user %s", origin);
413         return 0;
414     }
415     if ((slist = conf_get_data("server/admin", RECDB_STRING_LIST)) && slist->used) {
416         unsigned int i;
417
418         irc_numeric(user, 256, ":Administrative info about %s", self->name);
419         for (i = 0; i < slist->used && i < 3; i++)
420             irc_numeric(user, 257 + i, ":%s", slist->list[i]);
421     } else {
422         irc_numeric(user, 423, ":No administrative info available");
423     }
424     return 1;
425 }
426
427 static void
428 recalc_bursts(struct server *eob_server)
429 {
430     unsigned int nn;
431     eob_server->burst = eob_server->self_burst;
432     if (eob_server->uplink != self)
433         eob_server->burst = eob_server->burst || eob_server->uplink->burst;
434     for (nn=0; nn < eob_server->children.used; nn++)
435         recalc_bursts(eob_server->children.list[nn]);
436 }
437
438 static struct chanmsg_func {
439     chanmsg_func_t func;
440     struct userNode *service;
441 } chanmsg_funcs[256]; /* indexed by trigger character */
442
443 struct privmsg_desc {
444     struct userNode *user;
445     char *text;
446     unsigned int is_notice : 1;
447     unsigned int is_qualified : 1;
448 };
449
450 static void
451 privmsg_chan_helper(struct chanNode *cn, void *data)
452 {
453     struct privmsg_desc *pd = data;
454     struct modeNode *mn;
455     struct chanmsg_func *cf = &chanmsg_funcs[(unsigned char)pd->text[0]];
456
457     /* Don't complain if it can't find the modeNode because the channel might
458      * be -n */
459     if ((mn = GetUserMode(cn, pd->user)))
460         mn->idle_since = now;
461
462     /* Never send a NOTICE to a channel to one of the services */
463     if (!pd->is_notice && cf->func && GetUserMode(cn, cf->service))
464         cf->func(pd->user, cn, pd->text+1, cf->service);
465 }
466
467 static void
468 privmsg_invalid(char *name, void *data)
469 {
470     struct privmsg_desc *pd = data;
471
472     if (*name == '$')
473         return;
474     irc_numeric(pd->user, ERR_NOSUCHNICK, "%s@%s :No such nick", name, self->name);
475 }
476
477 static void
478 part_helper(struct chanNode *cn, void *data)
479 {
480     DelChannelUser(data, cn, false, 0);
481 }
482
483 void
484 reg_chanmsg_func(unsigned char prefix, struct userNode *service, chanmsg_func_t handler)
485 {
486     if (chanmsg_funcs[prefix].func)
487         log_module(MAIN_LOG, LOG_WARNING, "Re-registering new chanmsg handler for character `%c'.", prefix);
488     chanmsg_funcs[prefix].func = handler;
489     chanmsg_funcs[prefix].service = service;
490 }
491
492 struct userNode *
493 get_chanmsg_bot(unsigned char prefix)
494 {
495     return chanmsg_funcs[prefix].service;
496 }
497
498 static mode_change_func_t *mcf_list;
499 static unsigned int mcf_size = 0, mcf_used = 0;
500
501 void
502 reg_mode_change_func(mode_change_func_t handler)
503 {
504     if (mcf_used == mcf_size) {
505         if (mcf_size) {
506             mcf_size <<= 1;
507             mcf_list = realloc(mcf_list, mcf_size*sizeof(mode_change_func_t));
508         } else {
509             mcf_size = 8;
510             mcf_list = malloc(mcf_size*sizeof(mode_change_func_t));
511         }
512     }
513     mcf_list[mcf_used++] = handler;
514 }
515
516 struct mod_chanmode *
517 mod_chanmode_alloc(unsigned int argc)
518 {
519     struct mod_chanmode *res;
520     if (argc > 1)
521         res = calloc(1, sizeof(*res) + (argc-1)*sizeof(res->args[0]));
522     else
523         res = calloc(1, sizeof(*res));
524     if (res) {
525 #if !defined(NDEBUG)
526         res->alloc_argc = argc;
527 #endif
528         res->argc = argc;
529     }
530     return res;
531 }
532
533 struct mod_chanmode *
534 mod_chanmode_dup(struct mod_chanmode *orig, unsigned int extra)
535 {
536     struct mod_chanmode *res;
537     res = mod_chanmode_alloc(orig->argc + extra);
538     if (res) {
539         res->modes_set = orig->modes_set;
540         res->modes_clear = orig->modes_clear;
541         res->new_limit = orig->new_limit;
542         memcpy(res->new_key, orig->new_key, sizeof(res->new_key));
543         res->argc = orig->argc;
544         memcpy(res->args, orig->args, orig->argc*sizeof(orig->args[0]));
545     }
546     return res;
547 }
548
549 void
550 mod_chanmode_apply(struct userNode *who, struct chanNode *channel, struct mod_chanmode *change)
551 {
552     struct banNode *bn;
553     unsigned int ii, jj;
554
555     assert(change->argc <= change->alloc_argc);
556     channel->modes = (channel->modes & ~change->modes_clear) | change->modes_set;
557     if (change->modes_set & MODE_LIMIT)
558         channel->limit = change->new_limit;
559     if (change->modes_set & MODE_KEY)
560         strcpy(channel->key, change->new_key);
561     for (ii = 0; ii < change->argc; ++ii) {
562         switch (change->args[ii].mode) {
563         case MODE_BAN:
564             /* If any existing ban is a subset of the new ban,
565              * silently remove it.  The new ban is not allowed
566              * to be more specific than an existing ban.
567              */
568             for (jj=0; jj<channel->banlist.used; ++jj) {
569                 if (match_ircglobs(change->args[ii].hostmask, channel->banlist.list[jj]->ban)) {
570                     banList_remove(&channel->banlist, channel->banlist.list[jj]);
571                     free(channel->banlist.list[jj]);
572                     jj--;
573                 }
574             }
575             bn = calloc(1, sizeof(*bn));
576             safestrncpy(bn->ban, change->args[ii].hostmask, sizeof(bn->ban));
577             safestrncpy(bn->who, who->nick, sizeof(bn->who));
578             bn->set = now;
579             banList_append(&channel->banlist, bn);
580             break;
581         case MODE_REMOVE|MODE_BAN:
582             for (jj=0; jj<channel->banlist.used; ++jj) {
583                 if (strcmp(channel->banlist.list[jj]->ban, change->args[ii].hostmask))
584                     continue;
585                 free(channel->banlist.list[jj]);
586                 banList_remove(&channel->banlist, channel->banlist.list[jj]);
587                 break;
588             }
589             break;
590         case MODE_CHANOP:
591         case MODE_VOICE:
592         case MODE_VOICE|MODE_CHANOP:
593         case MODE_REMOVE|MODE_CHANOP:
594         case MODE_REMOVE|MODE_VOICE:
595         case MODE_REMOVE|MODE_VOICE|MODE_CHANOP:
596             if (change->args[ii].mode & MODE_REMOVE)
597                 change->args[ii].member->modes &= ~change->args[ii].mode;
598             else
599                 change->args[ii].member->modes |= change->args[ii].mode;
600             break;
601         default:
602             assert(0 && "Invalid mode argument");
603             continue;
604         }
605     }
606 }
607
608 void
609 mod_chanmode_free(struct mod_chanmode *change)
610 {
611     free(change);
612 }
613
614 int
615 mod_chanmode(struct userNode *who, struct chanNode *channel, char **modes, unsigned int argc, unsigned int flags)
616 {
617     struct mod_chanmode *change;
618     unsigned int ii;
619
620     if (!modes || !modes[0])
621         return 0;
622     if (!(change = mod_chanmode_parse(channel, modes, argc, flags)))
623         return 0;
624     if (flags & MC_ANNOUNCE)
625         mod_chanmode_announce(who, channel, change);
626     else
627         mod_chanmode_apply(who, channel, change);
628     if (flags & MC_NOTIFY)
629         for (ii = 0; ii < mcf_used; ++ii)
630             mcf_list[ii](channel, who, change);
631     mod_chanmode_free(change);
632     return 1;
633 }
634
635 int
636 irc_make_chanmode(struct chanNode *chan, char *out) {
637     struct mod_chanmode change;
638     change.modes_set = chan->modes;
639     change.modes_clear = change.argc = 0;
640     change.new_limit = chan->limit;
641     safestrncpy(change.new_key, chan->key, sizeof(change.new_key));
642     return strlen(mod_chanmode_format(&change, out));
643 }
644
645 char *
646 generate_hostmask(struct userNode *user, int options)
647 {
648     char *nickname, *ident, *hostname;
649     char *mask;
650     int len, ii;
651
652     /* figure out string parts */
653     if (options & GENMASK_OMITNICK)
654         nickname = NULL;
655     else if (options & GENMASK_USENICK)
656         nickname = user->nick;
657     else
658         nickname = "*";
659     if (options & GENMASK_STRICT_IDENT)
660         ident = user->ident;
661     else if (options & GENMASK_ANY_IDENT)
662         ident = "*";
663     else {
664         ident = alloca(strlen(user->ident)+2);
665         ident[0] = '*';
666         strcpy(ident+1, user->ident + ((*user->ident == '~')?1:0));
667     }
668     hostname = user->hostname;
669     if (IsHiddenHost(user) && user->handle_info && hidden_host_suffix && !(options & GENMASK_NO_HIDING)) {
670         hostname = alloca(strlen(user->handle_info->handle) + strlen(hidden_host_suffix) + 2);
671         sprintf(hostname, "%s.%s", user->handle_info->handle, hidden_host_suffix);
672     } else if (options & GENMASK_STRICT_HOST) {
673         if (options & GENMASK_BYIP)
674             hostname = inet_ntoa(user->ip);
675     } else if ((options & GENMASK_BYIP) || !hostname[strspn(hostname, "0123456789.")]) {
676         /* Should generate an IP-based hostmask.  By popular acclaim, a /16
677          * hostmask is used by default. */
678         unsigned masked_ip, mask, masklen;
679         masklen = 16;
680         mask = ~0 << masklen;
681         masked_ip = ntohl(user->ip.s_addr) & mask;
682         hostname = alloca(32);
683         if (options & GENMASK_SRVXMASK) {
684             sprintf(hostname, "%d.%d.%d.%d/%d", (masked_ip>>24)&0xFF, (masked_ip>>16)&0xFF, (masked_ip>>8)&0xFF, masked_ip&0xFF, masklen);
685         } else {
686             int ofs = 0;
687             for (ii=0; ii<4; ii++) {
688                 if (masklen) {
689                     ofs += sprintf(hostname+ofs, "%d.", (masked_ip>>24)&0xFF);
690                     masklen -= 8;
691                     masked_ip <<= 8;
692                 } else {
693                     ofs += sprintf(hostname+ofs, "*.");
694                 }
695             }
696             /* Truncate the last . */
697             hostname[ofs-1] = 0;
698         }
699     } else {
700         int cnt;
701         /* This heuristic could be made smarter.  Is it worth the effort? */
702         for (ii=cnt=0; hostname[ii]; ii++)
703             if (hostname[ii] == '.')
704                 cnt++;
705         if (cnt == 1) {
706             /* only a two-level domain name; leave hostname */
707         } else if (cnt == 2) {
708             for (ii=0; user->hostname[ii] != '.'; ii++) ;
709             /* Add 3 to account for the *. and \0. */
710             hostname = alloca(strlen(user->hostname+ii)+3);
711             sprintf(hostname, "*.%s", user->hostname+ii+1);
712         } else {
713             for (cnt=3, ii--; cnt; ii--)
714                 if (user->hostname[ii] == '.')
715                     cnt--;
716             /* The loop above will overshoot the dot one character;
717                we skip forward two (the one character and the dot)
718                when printing, so we only add one for the \0. */
719             hostname = alloca(strlen(user->hostname+ii)+1);
720             sprintf(hostname, "*.%s", user->hostname+ii+2);
721         }
722     }
723     /* Emit hostmask */
724     len = strlen(ident) + strlen(hostname) + 2;
725     if (nickname) {
726         len += strlen(nickname) + 1;
727         mask = malloc(len);
728         sprintf(mask, "%s!%s@%s", nickname, ident, hostname);
729     } else {
730         mask = malloc(len);
731         sprintf(mask, "%s@%s", ident, hostname);
732     }
733     return mask;
734 }
735
736 int
737 IsChannelName(const char *name) {
738     unsigned int ii;
739
740     if (*name !='#')
741         return 0;
742     for (ii=1; name[ii]; ++ii) {
743         if ((name[ii] > 0) && (name[ii] <= 32))
744             return 0;
745         if (name[ii] == ',')
746             return 0;
747         if (name[ii] == '\xa0')
748             return 0;
749     }
750     return 1;
751 }