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