Fix hostmask generation for hostnames without dots.
[srvx.git] / src / proto-common.c
1 /* proto-common.c - common IRC protocol parsing/sending support
2  * Copyright 2000-2006 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")))
85         *eol = 0;
86     log_replay(MAIN_LOG, false, buffer);
87     if (cManager.uplink->state != DISCONNECTED)
88         parse_line(buffer, 0);
89     lines_processed++;
90 }
91
92 void
93 socket_destroyed(struct io_fd *fd)
94 {
95     if (fd && fd->state != IO_CONNECTED)
96         log_module(MAIN_LOG, LOG_ERROR, "Connection to server lost.");
97     socket_io_fd = NULL;
98     cManager.uplink->state = DISCONNECTED;
99     if (self->uplink)
100         DelServer(self->uplink, 0, NULL);
101 }
102
103 void replay_event_loop(void)
104 {
105     while (!quit_services) {
106         if (!replay_connected) {
107             /* this time fudging is to get some of the logging right */
108             self->link = self->boot = now;
109             cManager.uplink->state = AUTHENTICATING;
110             irc_introduce(cManager.uplink->password);
111             replay_connected = 1;
112         } else if (!replay_read()) {
113             log_module(MAIN_LOG, LOG_ERROR, "Connection to server lost.");
114             close_socket();
115         }
116         timeq_run();
117     }
118 }
119
120 int
121 create_socket_client(struct uplinkNode *target)
122 {
123     int port = target->port;
124     const char *addr = target->host;
125
126     if (replay_file)
127         return feof(replay_file) ? 0 : 1;
128
129     if (socket_io_fd) {
130         /* Leave the existing socket open, say we failed. */
131         log_module(MAIN_LOG, LOG_ERROR, "Refusing to create second connection to %s:%d.", addr, port);
132         return 0;
133     }
134
135     log_module(MAIN_LOG, LOG_INFO, "Connecting to %s:%i...", addr, port);
136
137     socket_io_fd = ioset_connect(cManager.uplink->bind_addr, cManager.uplink->bind_addr_len, addr, port, 1, 0, NULL);
138     if (!socket_io_fd) {
139         log_module(MAIN_LOG, LOG_ERROR, "Connection to uplink failed: %s (%d)", strerror(errno), errno);
140         target->state = DISCONNECTED;
141         target->tries++;
142         return 0;
143     }
144     socket_io_fd->readable_cb = uplink_readable;
145     socket_io_fd->destroy_cb = socket_destroyed;
146     socket_io_fd->line_reads = 1;
147     socket_io_fd->wants_reads = 1;
148     log_module(MAIN_LOG, LOG_INFO, "Connection to server established.");
149     cManager.uplink = target;
150     target->state = AUTHENTICATING;
151     target->tries = 0;
152     return 1;
153 }
154
155 void
156 replay_read_line(void)
157 {
158     struct tm timestamp;
159     time_t new_time;
160
161     if (replay_line[0]) return;
162   read_line:
163     if (!fgets(replay_line, sizeof(replay_line), replay_file)) {
164         if (feof(replay_file)) {
165             quit_services = 1;
166             memset(replay_line, 0, sizeof(replay_line));
167             return;
168         }
169     }
170     if ((replay_line[0] != '[')
171         || (replay_line[3] != ':')
172         || (replay_line[6] != ':')
173         || (replay_line[9] != ' ')
174         || (replay_line[12] != '/')
175         || (replay_line[15] != '/')
176         || (replay_line[20] != ']')
177         || (replay_line[21] != ' ')) {
178         log_module(MAIN_LOG, LOG_ERROR, "Unrecognized timestamp in replay file: %s", replay_line);
179         goto read_line;
180     }
181     timestamp.tm_hour = strtoul(replay_line+1, NULL, 10);
182     timestamp.tm_min = strtoul(replay_line+4, NULL, 10);
183     timestamp.tm_sec = strtoul(replay_line+7, NULL, 10);
184     timestamp.tm_mon = strtoul(replay_line+10, NULL, 10) - 1;
185     timestamp.tm_mday = strtoul(replay_line+13, NULL, 10);
186     timestamp.tm_year = strtoul(replay_line+16, NULL, 10) - 1900;
187     timestamp.tm_isdst = 0;
188     new_time = mktime(&timestamp);
189     if (new_time == -1) {
190         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);
191     } else {
192         now = new_time;
193     }
194
195     if (strncmp(replay_line+22, "(info) ", 7))
196         goto read_line;
197     return;
198 }
199
200 static int
201 replay_read(void)
202 {
203     size_t len;
204     char read_line[MAXLEN];
205     while (1) {
206         replay_read_line();
207         /* if it's a sent line, break out to handle it */
208         if (!strncmp(replay_line+29, "   ", 3))
209             break;
210         if (!strncmp(replay_line+29, "W: ", 3)) {
211             log_module(MAIN_LOG, LOG_ERROR, "Expected response from services: %s", replay_line+32);
212             replay_line[0] = 0;
213         } else {
214             return 0;
215         }
216     }
217     log_replay(MAIN_LOG, false, replay_line+32);
218     safestrncpy(read_line, replay_line+32, sizeof(read_line));
219     len = strlen(read_line);
220     if (read_line[len-1] == '\n')
221         read_line[--len] = 0;
222     replay_line[0] = 0;
223     parse_line(read_line, 0);
224     lines_processed++;
225     return 1;
226 }
227
228 static void
229 replay_write(char *text)
230 {
231     replay_read_line();
232     if (strncmp(replay_line+29, "W: ", 3)) {
233         log_module(MAIN_LOG, LOG_ERROR, "Unexpected output during replay: %s", text);
234         return;
235     } else {
236         if (strcmp(replay_line+32, text)) {
237             log_module(MAIN_LOG, LOG_ERROR, "Incorrect output during replay:\nReceived: %sExpected: %s", text, replay_line+32);
238         } else {
239             log_replay(MAIN_LOG, true, text);
240         }
241         replay_line[0] = 0;
242     }
243 }
244
245 void putsock(const char *text, ...) PRINTF_LIKE(1, 2);
246
247 void
248 putsock(const char *text, ...)
249 {
250     va_list arg_list;
251     char buffer[MAXLEN];
252     int pos;
253
254     if (!cManager.uplink || cManager.uplink->state == DISCONNECTED) return;
255     buffer[0] = '\0';
256     va_start(arg_list, text);
257     pos = vsnprintf(buffer, MAXLEN - 2, text, arg_list);
258     va_end(arg_list);
259     if (pos < 0 || pos > (MAXLEN - 2)) pos = MAXLEN - 2;
260     buffer[pos] = 0;
261     if (!replay_file) {
262         log_replay(MAIN_LOG, true, buffer);
263         buffer[pos++] = '\n';
264         buffer[pos] = 0;
265         ioset_write(socket_io_fd, buffer, pos);
266     } else {
267         replay_write(buffer);
268     }
269 }
270
271 void
272 close_socket(void)
273 {
274     if (replay_file) {
275         replay_connected = 0;
276         socket_destroyed(socket_io_fd);
277     } else {
278         ioset_close(socket_io_fd, 1);
279         socket_io_fd = NULL;
280     }
281 }
282
283 #define CMD_FUNC(NAME) int NAME(UNUSED_ARG(const char *origin), UNUSED_ARG(unsigned int argc), UNUSED_ARG(char **argv))
284 typedef CMD_FUNC(cmd_func_t);
285
286 static void timed_ping_timeout(void *data);
287
288 /* Ping state is kept in the timeq (only one of these two can be in
289  * the queue at any given time). */
290 void
291 timed_send_ping(UNUSED_ARG(void *data))
292 {
293     irc_ping(self->name);
294     timeq_add(now + ping_timeout, timed_ping_timeout, 0);
295 }
296
297 static void
298 timed_ping_timeout(UNUSED_ARG(void *data))
299 {
300     /* Uplink "health" tracking could be accomplished by counting the
301        number of ping timeouts that happen for each uplink. After the
302        timeouts per time period exceeds some amount, the uplink could
303        be marked as unavalable.*/
304     irc_squit(self, "Ping timeout.", NULL);
305 }
306
307 static CMD_FUNC(cmd_pass)
308 {
309     const char *true_pass;
310
311     if (argc < 2)
312         return 0;
313     true_pass = cManager.uplink->their_password;
314     if (true_pass && strcmp(true_pass, argv[1])) {
315         /* It might be good to mark the uplink as unavailable when
316            this happens, though there should be a way of resetting
317            the flag. */
318         irc_squit(self, "Incorrect password received.", NULL);
319         return 1;
320     }
321
322     cManager.uplink->state = BURSTING;
323     return 1;
324 }
325
326 static CMD_FUNC(cmd_dummy)
327 {
328     /* we don't care about these messages */
329     return 1;
330 }
331
332 static CMD_FUNC(cmd_error)
333 {
334     if (argv[1]) log_module(MAIN_LOG, LOG_ERROR, "Error: %s", argv[1]);
335     log_module(MAIN_LOG, LOG_ERROR, "Error received from uplink, squitting.");
336
337     if (cManager.uplink->state != CONNECTED) {
338         /* Error messages while connected should be fine. */
339         cManager.uplink->flags |= UPLINK_UNAVAILABLE;
340         log_module(MAIN_LOG, LOG_ERROR, "Disabling uplink.");
341     }
342
343     close_socket();
344     return 1;
345 }
346
347 static CMD_FUNC(cmd_stats)
348 {
349     struct userNode *un;
350
351     if (argc < 2)
352         return 0;
353     if (!(un = GetUserH(origin)))
354         return 0;
355     switch (argv[1][0]) {
356     case 'u': {
357         unsigned int uptime = now - boot_time;
358         irc_numeric(un, RPL_STATSUPTIME, ":Server Up %d days %d:%02d:%02d",
359                     uptime/(24*60*60), (uptime/(60*60))%24, (uptime/60)%60, uptime%60);
360         irc_numeric(un, RPL_MAXCONNECTIONS, ":Highest connection count: %d (%d clients)",
361                     self->max_clients+1, self->max_clients);
362         break;
363     }
364     default: /* unrecognized/unhandled stats output */ break;
365     }
366     irc_numeric(un, 219, "%s :End of /STATS report", argv[1]);
367     return 1;
368 }
369
370 static CMD_FUNC(cmd_version)
371 {
372     struct userNode *user;
373     if (!(user = GetUserH(origin))) {
374         log_module(MAIN_LOG, LOG_ERROR, "Could not find VERSION origin user %s", origin);
375         return 0;
376     }
377     irc_numeric(user, 351, "%s.%s %s :%s", PACKAGE_TARNAME, PACKAGE_VERSION, self->name, CODENAME);
378     return 1;
379 }
380
381 static CMD_FUNC(cmd_admin)
382 {
383     struct userNode *user;
384     struct string_list *slist;
385
386     if (!(user = GetUserH(origin))) {
387         log_module(MAIN_LOG, LOG_ERROR, "Could not find ADMIN origin user %s", origin);
388         return 0;
389     }
390     if ((slist = conf_get_data("server/admin", RECDB_STRING_LIST)) && slist->used) {
391         unsigned int i;
392
393         irc_numeric(user, 256, ":Administrative info about %s", self->name);
394         for (i = 0; i < slist->used && i < 3; i++)
395             irc_numeric(user, 257 + i, ":%s", slist->list[i]);
396     } else {
397         irc_numeric(user, 423, ":No administrative info available");
398     }
399     return 1;
400 }
401
402 static void
403 recalc_bursts(struct server *eob_server)
404 {
405     unsigned int nn;
406     eob_server->burst = eob_server->self_burst;
407     if (eob_server->uplink != self)
408         eob_server->burst = eob_server->burst || eob_server->uplink->burst;
409     for (nn=0; nn < eob_server->children.used; nn++)
410         recalc_bursts(eob_server->children.list[nn]);
411 }
412
413 static struct chanmsg_func {
414     chanmsg_func_t func;
415     struct userNode *service;
416 } chanmsg_funcs[256]; /* indexed by trigger character */
417
418 static struct allchanmsg_func {
419     chanmsg_func_t func;
420     struct userNode *service;
421 } allchanmsg_funcs[ALLCHANMSG_FUNCS_MAX];
422
423 struct privmsg_desc {
424     struct userNode *user;
425     char *text;
426     unsigned int is_notice : 1;
427     unsigned int is_qualified : 1;
428 };
429
430 static void
431 privmsg_chan_helper(struct chanNode *cn, void *data)
432 {
433     struct privmsg_desc *pd = data;
434     struct modeNode *mn;
435     struct chanmsg_func *cf = &chanmsg_funcs[(unsigned char)pd->text[0]];
436     int x;
437
438     /* Don't complain if it can't find the modeNode because the channel might
439      * be -n */
440     if ((mn = GetUserMode(cn, pd->user)))
441         mn->idle_since = now;
442
443     /* Never send a NOTICE to a channel to one of the services */
444     if (!pd->is_notice && cf->func
445         && ((cn->modes & MODE_REGISTERED) || GetUserMode(cn, cf->service)))
446         cf->func(pd->user, cn, pd->text+1, cf->service);
447
448     /* This catches *all* text sent to the channel that the services server sees */
449     for (x = 0; x < ALLCHANMSG_FUNCS_MAX; x++) {
450        cf = (struct chanmsg_func *)&allchanmsg_funcs[x];
451        if (!cf->func)
452            break; /* end of list */
453        else
454            cf->func(pd->user, cn, pd->text, cf->service);
455     }
456 }
457
458 static void
459 privmsg_invalid(char *name, void *data)
460 {
461     struct privmsg_desc *pd = data;
462
463     if (*name == '$')
464         return;
465     irc_numeric(pd->user, ERR_NOSUCHNICK, "%s@%s :No such nick", name, self->name);
466 }
467
468 struct part_desc {
469     struct userNode *user;
470     const char *text;
471 };
472
473 static void
474 part_helper(struct chanNode *cn, void *data)
475 {
476     struct part_desc *desc = data;
477     DelChannelUser(desc->user, cn, desc->text, false);
478 }
479
480 static CMD_FUNC(cmd_part)
481 {
482     struct part_desc desc;
483
484     if (argc < 2)
485         return 0;
486     desc.user = GetUserH(origin);
487     if (!desc.user)
488         return 0;
489     desc.text = (argc > 2) ? argv[argc - 1] : NULL;
490     parse_foreach(argv[1], part_helper, NULL, NULL, NULL, &desc);
491     return 1;
492 }
493
494 void
495 reg_chanmsg_func(unsigned char prefix, struct userNode *service, chanmsg_func_t handler)
496 {
497     if (chanmsg_funcs[prefix].func)
498         log_module(MAIN_LOG, LOG_WARNING, "Re-registering new chanmsg handler for character `%c'.", prefix);
499     chanmsg_funcs[prefix].func = handler;
500     chanmsg_funcs[prefix].service = service;
501 }
502
503 void
504 reg_allchanmsg_func(struct userNode *service, chanmsg_func_t handler)
505 {
506     int x;
507     for (x = 0; x < ALLCHANMSG_FUNCS_MAX; x++) {
508        if (allchanmsg_funcs[x].func)
509          continue;
510        allchanmsg_funcs[x].func = handler;
511        allchanmsg_funcs[x].service = service;
512        break;
513     }
514 }
515
516 struct userNode *
517 get_chanmsg_bot(unsigned char prefix)
518 {
519     return chanmsg_funcs[prefix].service;
520 }
521
522 static mode_change_func_t *mcf_list;
523 static unsigned int mcf_size = 0, mcf_used = 0;
524
525 void
526 reg_mode_change_func(mode_change_func_t handler)
527 {
528     if (mcf_used == mcf_size) {
529         if (mcf_size) {
530             mcf_size <<= 1;
531             mcf_list = realloc(mcf_list, mcf_size*sizeof(mode_change_func_t));
532         } else {
533             mcf_size = 8;
534             mcf_list = malloc(mcf_size*sizeof(mode_change_func_t));
535         }
536     }
537     mcf_list[mcf_used++] = handler;
538 }
539
540 struct mod_chanmode *
541 mod_chanmode_alloc(unsigned int argc)
542 {
543     struct mod_chanmode *res;
544     if (argc > 1)
545         res = calloc(1, sizeof(*res) + (argc-1)*sizeof(res->args[0]));
546     else
547         res = calloc(1, sizeof(*res));
548     if (res) {
549 #if !defined(NDEBUG)
550         res->alloc_argc = argc;
551 #endif
552         res->argc = argc;
553     }
554     return res;
555 }
556
557 struct mod_chanmode *
558 mod_chanmode_dup(struct mod_chanmode *orig, unsigned int extra)
559 {
560     struct mod_chanmode *res;
561     res = mod_chanmode_alloc(orig->argc + extra);
562     if (res) {
563         res->modes_set = orig->modes_set;
564         res->modes_clear = orig->modes_clear;
565         res->new_limit = orig->new_limit;
566         memcpy(res->new_key, orig->new_key, sizeof(res->new_key));
567         memcpy(res->new_upass, orig->new_upass, sizeof(res->new_upass));
568         memcpy(res->new_apass, orig->new_apass, sizeof(res->new_apass));
569         res->argc = orig->argc;
570         memcpy(res->args, orig->args, orig->argc*sizeof(orig->args[0]));
571     }
572     return res;
573 }
574
575 void
576 mod_chanmode_apply(struct userNode *who, struct chanNode *channel, struct mod_chanmode *change)
577 {
578     struct banNode *bn;
579     unsigned int ii, jj;
580
581     assert(change->argc <= change->alloc_argc);
582     channel->modes = (channel->modes & ~change->modes_clear) | change->modes_set;
583     if (change->modes_set & MODE_LIMIT)
584         channel->limit = change->new_limit;
585     if (change->modes_set & MODE_KEY)
586         strcpy(channel->key, change->new_key);
587     if (change->modes_set & MODE_UPASS)
588        strcpy(channel->upass, change->new_upass);
589     if (change->modes_set & MODE_APASS)
590        strcpy(channel->apass, change->new_apass);
591     for (ii = 0; ii < change->argc; ++ii) {
592         switch (change->args[ii].mode) {
593         case MODE_BAN:
594             /* If any existing ban is a subset of the new ban,
595              * silently remove it.  The new ban is not allowed
596              * to be more specific than an existing ban.
597              */
598             for (jj=0; jj<channel->banlist.used; ++jj) {
599                 bn = channel->banlist.list[jj];
600                 if (match_ircglobs(change->args[ii].u.hostmask, bn->ban)) {
601                     banList_remove(&channel->banlist, bn);
602                     free(bn);
603                     jj--;
604                 }
605             }
606             bn = calloc(1, sizeof(*bn));
607             safestrncpy(bn->ban, change->args[ii].u.hostmask, sizeof(bn->ban));
608             if (who)
609                 safestrncpy(bn->who, who->nick, sizeof(bn->who));
610             else
611                 safestrncpy(bn->who, "<unknown>", sizeof(bn->who));
612             bn->set = now;
613             banList_append(&channel->banlist, bn);
614             break;
615         case MODE_REMOVE|MODE_BAN:
616             for (jj=0; jj<channel->banlist.used; ++jj) {
617                 bn = channel->banlist.list[jj];
618                 if (strcmp(bn->ban, change->args[ii].u.hostmask))
619                     continue;
620                 free(bn);
621                 banList_remove(&channel->banlist, bn);
622                 break;
623             }
624             break;
625         case MODE_CHANOP:
626         case MODE_VOICE:
627         case MODE_VOICE|MODE_CHANOP:
628         case MODE_REMOVE|MODE_CHANOP:
629         case MODE_REMOVE|MODE_VOICE:
630         case MODE_REMOVE|MODE_VOICE|MODE_CHANOP:
631             if (change->args[ii].mode & MODE_REMOVE)
632                 change->args[ii].u.member->modes &= ~change->args[ii].mode;
633             else
634                 change->args[ii].u.member->modes |= change->args[ii].mode;
635             break;
636         default:
637             assert(0 && "Invalid mode argument");
638             continue;
639         }
640     }
641 }
642
643 void
644 mod_chanmode_free(struct mod_chanmode *change)
645 {
646     free(change);
647 }
648
649 int
650 mod_chanmode(struct userNode *who, struct chanNode *channel, char **modes, unsigned int argc, unsigned int flags)
651 {
652     struct modeNode *member;
653     struct mod_chanmode *change;
654     unsigned int ii;
655     short base_oplevel;
656
657     if (!modes || !modes[0])
658         return 0;
659     if (who && (member = GetUserMode(channel, who)))
660         base_oplevel = member->oplevel;
661     else
662         base_oplevel = MAXOPLEVEL;
663     if (!(change = mod_chanmode_parse(channel, modes, argc, flags, base_oplevel)))
664         return 0;
665     if (flags & MC_ANNOUNCE)
666         mod_chanmode_announce(who, channel, change);
667     else
668         mod_chanmode_apply(who, channel, change);
669     if (flags & MC_NOTIFY)
670         for (ii = 0; ii < mcf_used; ++ii)
671             mcf_list[ii](channel, who, change);
672     mod_chanmode_free(change);
673     return 1;
674 }
675
676 int
677 irc_make_chanmode(struct chanNode *chan, char *out)
678 {
679     struct mod_chanmode change;
680     mod_chanmode_init(&change);
681     change.modes_set = chan->modes;
682     change.new_limit = chan->limit;
683     safestrncpy(change.new_key, chan->key, sizeof(change.new_key));
684     safestrncpy(change.new_upass, chan->upass, sizeof(change.new_upass));
685     safestrncpy(change.new_apass, chan->apass, sizeof(change.new_apass));
686     return strlen(mod_chanmode_format(&change, out));
687 }
688
689 char *
690 generate_hostmask(struct userNode *user, int options)
691 {
692     irc_in_addr_t ip;
693     char *nickname, *ident, *hostname, *mask;
694     int len, ii;
695
696     /* figure out string parts */
697     if (options & GENMASK_OMITNICK)
698         nickname = NULL;
699     else if (options & GENMASK_USENICK)
700         nickname = user->nick;
701     else
702         nickname = "*";
703     if (options & GENMASK_STRICT_IDENT)
704         ident = user->ident;
705     else if (options & GENMASK_ANY_IDENT)
706         ident = "*";
707     else {
708         ident = alloca(strlen(user->ident)+2);
709         ident[0] = '*';
710         strcpy(ident+1, user->ident + ((*user->ident == '~')?1:0));
711     }
712     hostname = user->hostname;
713     if (IsFakeHost(user) && IsHiddenHost(user) && !(options & GENMASK_NO_HIDING)) {
714         hostname = user->fakehost;
715     } else if (IsHiddenHost(user) && user->handle_info && hidden_host_suffix && !(options & GENMASK_NO_HIDING)) {
716         hostname = alloca(strlen(user->handle_info->handle) + strlen(hidden_host_suffix) + 2);
717         sprintf(hostname, "%s.%s", user->handle_info->handle, hidden_host_suffix);
718     } else if (options & GENMASK_STRICT_HOST) {
719         if (options & GENMASK_BYIP)
720             hostname = (char*)irc_ntoa(&user->ip);
721     } else if ((options & GENMASK_BYIP) || irc_pton(&ip, NULL, hostname)) {
722         /* Should generate an IP-based hostmask. */
723         hostname = alloca(IRC_NTOP_MAX_SIZE);
724         hostname[IRC_NTOP_MAX_SIZE-1] = '\0';
725         if (irc_in_addr_is_ipv4(user->ip)) {
726             /* By popular acclaim, a /16 hostmask is used. */
727             sprintf(hostname, "%d.%d.*", user->ip.in6_8[12], user->ip.in6_8[13]);
728         } else if (irc_in_addr_is_ipv6(user->ip)) {
729             /* Who knows what the default mask should be?  Use a /48 to start with. */
730             sprintf(hostname, "%x:%x:%x:*", user->ip.in6[0], user->ip.in6[1], user->ip.in6[2]);
731         } else {
732             /* Unknown type; just copy IP directly. */
733             irc_ntop(hostname, IRC_NTOP_MAX_SIZE, &user->ip);
734         }
735     } else {
736         int cnt;
737         /* This heuristic could be made smarter.  Is it worth the effort? */
738         for (ii=cnt=0; hostname[ii]; ii++)
739             if (hostname[ii] == '.')
740                 cnt++;
741         if (cnt == 0 || cnt == 1) {
742             /* only a one- or two-level domain name; leave hostname */
743         } else if (cnt == 2) {
744             for (ii=0; user->hostname[ii] != '.'; ii++) ;
745             /* Add 3 to account for the *. and \0. */
746             hostname = alloca(strlen(user->hostname+ii)+3);
747             sprintf(hostname, "*.%s", user->hostname+ii+1);
748         } else {
749             for (cnt=3, ii--; cnt; ii--)
750                 if (user->hostname[ii] == '.')
751                     cnt--;
752             /* The loop above will overshoot the dot one character;
753                we skip forward two (the one character and the dot)
754                when printing, so we only add one for the \0. */
755             hostname = alloca(strlen(user->hostname+ii)+1);
756             sprintf(hostname, "*.%s", user->hostname+ii+2);
757         }
758     }
759     /* Emit hostmask */
760     len = strlen(ident) + strlen(hostname) + 2;
761     if (nickname) {
762         len += strlen(nickname) + 1;
763         mask = malloc(len);
764         sprintf(mask, "%s!%s@%s", nickname, ident, hostname);
765     } else {
766         mask = malloc(len);
767         sprintf(mask, "%s@%s", ident, hostname);
768     }
769     return mask;
770 }
771
772 int
773 IsChannelName(const char *name) {
774     unsigned int ii;
775
776     if (*name !='#')
777         return 0;
778     for (ii=1; name[ii]; ++ii) {
779         if ((name[ii] > 0) && (name[ii] <= 32))
780             return 0;
781         if (name[ii] == ',')
782             return 0;
783         if (name[ii] == '\xa0')
784             return 0;
785     }
786     return 1;
787 }