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