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