License update
[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         res->alloc_argc = argc;
526         res->argc = argc;
527     }
528     return res;
529 }
530
531 struct mod_chanmode *
532 mod_chanmode_dup(struct mod_chanmode *orig, unsigned int extra)
533 {
534     struct mod_chanmode *res;
535     res = mod_chanmode_alloc(orig->argc + extra);
536     if (res) {
537         res->modes_set = orig->modes_set;
538         res->modes_clear = orig->modes_clear;
539         res->argc = orig->argc;
540         memcpy(res->args, orig->args, orig->argc*sizeof(orig->args[0]));
541     }
542     return res;
543 }
544
545 void
546 mod_chanmode_apply(struct userNode *who, struct chanNode *channel, struct mod_chanmode *change)
547 {
548     struct banNode *bn;
549     unsigned int ii, jj;
550
551     assert(change->argc <= change->alloc_argc);
552     channel->modes = (channel->modes & ~change->modes_clear) | change->modes_set;
553     if (change->modes_set & MODE_LIMIT)
554         channel->limit = change->new_limit;
555     if (change->modes_set & MODE_KEY)
556         strcpy(channel->key, change->new_key);
557     for (ii = 0; ii < change->argc; ++ii) {
558         switch (change->args[ii].mode) {
559         case MODE_BAN:
560             /* If any existing ban is a subset of the new ban,
561              * silently remove it.  The new ban is not allowed
562              * to be more specific than an existing ban.
563              */
564             for (jj=0; jj<channel->banlist.used; ++jj) {
565                 if (match_ircglobs(change->args[ii].hostmask, channel->banlist.list[jj]->ban)) {
566                     banList_remove(&channel->banlist, channel->banlist.list[jj]);
567                     free(channel->banlist.list[jj]);
568                     jj--;
569                 }
570             }
571             bn = calloc(1, sizeof(*bn));
572             safestrncpy(bn->ban, change->args[ii].hostmask, sizeof(bn->ban));
573             safestrncpy(bn->who, who->nick, sizeof(bn->who));
574             bn->set = now;
575             banList_append(&channel->banlist, bn);
576             break;
577         case MODE_REMOVE|MODE_BAN:
578             for (jj=0; jj<channel->banlist.used; ++jj) {
579                 if (strcmp(channel->banlist.list[jj]->ban, change->args[ii].hostmask))
580                     continue;
581                 free(channel->banlist.list[jj]);
582                 banList_remove(&channel->banlist, channel->banlist.list[jj]);
583                 break;
584             }
585             break;
586         case MODE_CHANOP:
587         case MODE_VOICE:
588         case MODE_VOICE|MODE_CHANOP:
589         case MODE_REMOVE|MODE_CHANOP:
590         case MODE_REMOVE|MODE_VOICE:
591         case MODE_REMOVE|MODE_VOICE|MODE_CHANOP:
592             if (change->args[ii].mode & MODE_REMOVE)
593                 change->args[ii].member->modes &= ~change->args[ii].mode;
594             else
595                 change->args[ii].member->modes |= change->args[ii].mode;
596             break;
597         default:
598             assert(0 && "Invalid mode argument");
599             continue;
600         }
601     }
602 }
603
604 void
605 mod_chanmode_free(struct mod_chanmode *change)
606 {
607     free(change);
608 }
609
610 int
611 mod_chanmode(struct userNode *who, struct chanNode *channel, char **modes, unsigned int argc, unsigned int flags)
612 {
613     struct mod_chanmode *change;
614     unsigned int ii;
615
616     if (!modes || !modes[0])
617         return 0;
618     if (!(change = mod_chanmode_parse(channel, modes, argc, flags)))
619         return 0;
620     if (flags & MC_ANNOUNCE)
621         mod_chanmode_announce(who, channel, change);
622     else
623         mod_chanmode_apply(who, channel, change);
624     if (flags & MC_NOTIFY)
625         for (ii = 0; ii < mcf_used; ++ii)
626             mcf_list[ii](channel, who, change);
627     mod_chanmode_free(change);
628     return 1;
629 }
630
631 int
632 irc_make_chanmode(struct chanNode *chan, char *out) {
633     struct mod_chanmode change;
634     change.modes_set = chan->modes;
635     change.modes_clear = change.argc = 0;
636     change.new_limit = chan->limit;
637     safestrncpy(change.new_key, chan->key, sizeof(change.new_key));
638     return strlen(mod_chanmode_format(&change, out));
639 }
640
641 char *
642 generate_hostmask(struct userNode *user, int options)
643 {
644     char *nickname, *ident, *hostname;
645     char *mask;
646     int len, ii;
647
648     /* figure out string parts */
649     if (options & GENMASK_OMITNICK)
650         nickname = NULL;
651     else if (options & GENMASK_USENICK)
652         nickname = user->nick;
653     else
654         nickname = "*";
655     if (options & GENMASK_STRICT_IDENT)
656         ident = user->ident;
657     else if (options & GENMASK_ANY_IDENT)
658         ident = "*";
659     else {
660         ident = alloca(strlen(user->ident)+2);
661         ident[0] = '*';
662         strcpy(ident+1, user->ident + ((*user->ident == '~')?1:0));
663     }
664     hostname = user->hostname;
665     if (IsHiddenHost(user) && user->handle_info && hidden_host_suffix && !(options & GENMASK_NO_HIDING)) {
666         hostname = alloca(strlen(user->handle_info->handle) + strlen(hidden_host_suffix) + 2);
667         sprintf(hostname, "%s.%s", user->handle_info->handle, hidden_host_suffix);
668     } else if (options & GENMASK_STRICT_HOST) {
669         if (options & GENMASK_BYIP)
670             hostname = inet_ntoa(user->ip);
671     } else if ((options & GENMASK_BYIP) || !hostname[strspn(hostname, "0123456789.")]) {
672         /* Should generate an IP-based hostmask.  By popular acclaim, a /16
673          * hostmask is used by default. */
674         unsigned masked_ip, mask, masklen;
675         masklen = 16;
676         mask = ~0 << masklen;
677         masked_ip = ntohl(user->ip.s_addr) & mask;
678         hostname = alloca(32);
679         if (options & GENMASK_SRVXMASK) {
680             sprintf(hostname, "%d.%d.%d.%d/%d", (masked_ip>>24)&0xFF, (masked_ip>>16)&0xFF, (masked_ip>>8)&0xFF, masked_ip&0xFF, masklen);
681         } else {
682             int ofs = 0;
683             for (ii=0; ii<4; ii++) {
684                 if (masklen) {
685                     ofs += sprintf(hostname+ofs, "%d.", (masked_ip>>24)&0xFF);
686                     masklen -= 8;
687                     masked_ip <<= 8;
688                 } else {
689                     ofs += sprintf(hostname+ofs, "*.");
690                 }
691             }
692             /* Truncate the last . */
693             hostname[ofs-1] = 0;
694         }
695     } else {
696         int cnt;
697         /* This heuristic could be made smarter.  Is it worth the effort? */
698         for (ii=cnt=0; hostname[ii]; ii++)
699             if (hostname[ii] == '.')
700                 cnt++;
701         if (cnt == 1) {
702             /* only a two-level domain name; leave hostname */
703         } else if (cnt == 2) {
704             for (ii=0; user->hostname[ii] != '.'; ii++) ;
705             /* Add 3 to account for the *. and \0. */
706             hostname = alloca(strlen(user->hostname+ii)+3);
707             sprintf(hostname, "*.%s", user->hostname+ii+1);
708         } else {
709             for (cnt=3, ii--; cnt; ii--)
710                 if (user->hostname[ii] == '.')
711                     cnt--;
712             /* The loop above will overshoot the dot one character;
713                we skip forward two (the one character and the dot)
714                when printing, so we only add one for the \0. */
715             hostname = alloca(strlen(user->hostname+ii)+1);
716             sprintf(hostname, "*.%s", user->hostname+ii+2);
717         }
718     }
719     /* Emit hostmask */
720     len = strlen(ident) + strlen(hostname) + 2;
721     if (nickname) {
722         len += strlen(nickname) + 1;
723         mask = malloc(len);
724         sprintf(mask, "%s!%s@%s", nickname, ident, hostname);
725     } else {
726         mask = malloc(len);
727         sprintf(mask, "%s@%s", ident, hostname);
728     }
729     return mask;
730 }
731
732 int
733 IsChannelName(const char *name) {
734     unsigned int ii;
735
736     if (*name !='#')
737         return 0;
738     for (ii=1; name[ii]; ++ii) {
739         if ((name[ii] > 0) && (name[ii] <= 32))
740             return 0;
741         if (name[ii] == ',')
742             return 0;
743         if (name[ii] == '\xa0')
744             return 0;
745     }
746     return 1;
747 }