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