added CMODE_AUDITORIUM & CMODE_NOFLOOD
[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 struct mod_chanmode *
581 mod_chanmode_alloc(unsigned int argc)
582 {
583     struct mod_chanmode *res;
584     if (argc > 1)
585         res = calloc(1, sizeof(*res) + (argc-1)*sizeof(res->args[0]));
586     else
587         res = calloc(1, sizeof(*res));
588     if (res) {
589 #if !defined(NDEBUG)
590         res->alloc_argc = argc;
591 #endif
592         res->argc = argc;
593     }
594     return res;
595 }
596
597 struct mod_chanmode *
598 mod_chanmode_dup(struct mod_chanmode *orig, unsigned int extra)
599 {
600     struct mod_chanmode *res;
601     res = mod_chanmode_alloc(orig->argc + extra);
602     if (res) {
603         res->modes_set = orig->modes_set;
604         res->modes_clear = orig->modes_clear;
605         res->new_limit = orig->new_limit;
606         res->new_access = orig->new_access;
607         memcpy(res->new_altchan, orig->new_altchan, sizeof(res->new_altchan));
608         memcpy(res->new_noflood, orig->new_noflood, sizeof(res->new_noflood));
609         memcpy(res->new_key, orig->new_key, sizeof(res->new_key));
610         memcpy(res->new_upass, orig->new_upass, sizeof(res->new_upass));
611         memcpy(res->new_apass, orig->new_apass, sizeof(res->new_apass));
612         res->argc = orig->argc;
613         memcpy(res->args, orig->args, orig->argc*sizeof(orig->args[0]));
614     }
615     return res;
616 }
617
618 void
619 mod_chanmode_apply(struct userNode *who, struct chanNode *channel, struct mod_chanmode *change)
620 {
621     struct banNode *bn;
622     unsigned int ii, jj;
623
624     assert(change->argc <= change->alloc_argc);
625     channel->modes = (channel->modes & ~change->modes_clear) | change->modes_set;
626     if (change->modes_set & MODE_LIMIT)
627         channel->limit = change->new_limit;
628     if (change->modes_set & MODE_ACCESS)
629         channel->access = change->new_access;
630     if (change->modes_set & MODE_KEY)
631         strcpy(channel->key, change->new_key);
632     if (change->modes_set & MODE_ALTCHAN)
633         strcpy(channel->altchan, change->new_altchan);
634     if (change->modes_set & MODE_NOFLOOD)
635         strcpy(channel->noflood, change->new_noflood);
636     if (change->modes_set & MODE_UPASS)
637        strcpy(channel->upass, change->new_upass);
638     if (change->modes_set & MODE_APASS)
639        strcpy(channel->apass, change->new_apass);
640     for (ii = 0; ii < change->argc; ++ii) {
641         switch (change->args[ii].mode) {
642         case MODE_BAN:
643             /* If any existing ban is a subset of the new ban,
644              * silently remove it.  The new ban is not allowed
645              * to be more specific than an existing ban.
646              */
647             for (jj=0; jj<channel->banlist.used; ++jj) {
648                 bn = channel->banlist.list[jj];
649                 if (match_ircglobs(change->args[ii].u.hostmask, bn->ban)) {
650                     banList_remove(&channel->banlist, bn);
651                     free(bn);
652                     jj--;
653                 }
654             }
655             bn = calloc(1, sizeof(*bn));
656             safestrncpy(bn->ban, change->args[ii].u.hostmask, sizeof(bn->ban));
657             if (who)
658                 safestrncpy(bn->who, who->nick, sizeof(bn->who));
659             else
660                 safestrncpy(bn->who, "<unknown>", sizeof(bn->who));
661             bn->set = now;
662             banList_append(&channel->banlist, bn);
663             break;
664         case MODE_REMOVE|MODE_BAN:
665             for (jj=0; jj<channel->banlist.used; ++jj) {
666                 bn = channel->banlist.list[jj];
667                 if (strcmp(bn->ban, change->args[ii].u.hostmask))
668                     continue;
669                 free(bn);
670                 banList_remove(&channel->banlist, bn);
671                 break;
672             }
673             break;
674         case MODE_CHANOP:
675         case MODE_VOICE:
676         case MODE_VOICE|MODE_CHANOP:
677         case MODE_REMOVE|MODE_CHANOP:
678         case MODE_REMOVE|MODE_VOICE:
679         case MODE_REMOVE|MODE_VOICE|MODE_CHANOP:
680             if (change->args[ii].mode & MODE_REMOVE)
681                 change->args[ii].u.member->modes &= ~change->args[ii].mode;
682             else
683                 change->args[ii].u.member->modes |= change->args[ii].mode;
684             break;
685         default:
686             assert(0 && "Invalid mode argument");
687             continue;
688         }
689     }
690 }
691
692 void
693 mod_chanmode_free(struct mod_chanmode *change)
694 {
695     free(change);
696 }
697
698 int
699 mod_chanmode(struct userNode *who, struct chanNode *channel, char **modes, unsigned int argc, unsigned int flags)
700 {
701     struct modeNode *member;
702     struct mod_chanmode *change;
703     unsigned int ii;
704     short base_oplevel;
705
706     if (!modes || !modes[0])
707         return 0;
708     if (who && (member = GetUserMode(channel, who)))
709         base_oplevel = member->oplevel;
710     else
711         base_oplevel = MAXOPLEVEL;
712     if (!(change = mod_chanmode_parse(channel, modes, argc, flags, base_oplevel)))
713         return 0;
714     if (flags & MC_ANNOUNCE)
715         mod_chanmode_announce(who, channel, change);
716     else
717         mod_chanmode_apply(who, channel, change);
718     if (flags & MC_NOTIFY)
719         for (ii = 0; ii < mcf_used; ++ii)
720             mcf_list[ii](channel, who, change);
721     mod_chanmode_free(change);
722     return 1;
723 }
724
725 int
726 irc_make_chanmode(struct chanNode *chan, char *out)
727 {
728     struct mod_chanmode change;
729     mod_chanmode_init(&change);
730     change.modes_set = chan->modes;
731     change.new_limit = chan->limit;
732     change.new_access = chan->access;
733     safestrncpy(change.new_altchan, chan->altchan, sizeof(change.new_altchan));
734     safestrncpy(change.new_noflood, chan->noflood, sizeof(change.new_noflood));
735     safestrncpy(change.new_key, chan->key, sizeof(change.new_key));
736     safestrncpy(change.new_upass, chan->upass, sizeof(change.new_upass));
737     safestrncpy(change.new_apass, chan->apass, sizeof(change.new_apass));
738     return strlen(mod_chanmode_format(&change, out));
739 }
740
741 char *
742 generate_hostmask(struct userNode *user, int options)
743 {
744     irc_in_addr_t ip;
745     char *nickname, *ident, *hostname, *mask;
746     int len, ii;
747
748     /* figure out string parts */
749     if (options & GENMASK_OMITNICK)
750         nickname = NULL;
751     else if (options & GENMASK_USENICK)
752         nickname = user->nick;
753     else
754         nickname = "*";
755     if (options & GENMASK_STRICT_IDENT)
756         ident = user->ident;
757     else if (options & GENMASK_ANY_IDENT)
758         ident = "*";
759     else if (IsFakeIdent(user) && IsHiddenHost(user) && !(options & GENMASK_NO_HIDING))
760         ident = user->fakeident;
761     else {
762         ident = alloca(strlen(user->ident)+2);
763         ident[0] = '*';
764         strcpy(ident+1, user->ident + ((*user->ident == '~')?1:0));
765     }
766     hostname = user->hostname;
767     if (IsFakeHost(user) && IsHiddenHost(user) && !(options & GENMASK_NO_HIDING)) {
768         if(user->fakehost && user->fakehost[0] == '$') {
769             hostname = alloca(strlen(user->handle_info->handle) + strlen(user->fakehost));
770             sprintf(hostname, "%s%s", user->handle_info->handle, user->fakehost+1);
771         } else {
772             hostname = user->fakehost;
773         }
774     } else if (IsHiddenHost(user) && user->handle_info && hidden_host_suffix && !(options & GENMASK_NO_HIDING)) {
775         hostname = alloca(strlen(user->handle_info->handle) + strlen(hidden_host_suffix) + 2);
776         sprintf(hostname, "%s.%s", user->handle_info->handle, hidden_host_suffix);
777     } else if (options & GENMASK_STRICT_HOST) {
778         if (options & GENMASK_BYIP)
779             hostname = (char*)irc_ntoa(&user->ip);
780     } else if ((options & GENMASK_BYIP) || irc_pton(&ip, NULL, hostname)) {
781         /* Should generate an IP-based hostmask. */
782         hostname = alloca(IRC_NTOP_MAX_SIZE);
783         hostname[IRC_NTOP_MAX_SIZE-1] = '\0';
784         if (irc_in_addr_is_ipv4(user->ip)) {
785             /* By popular acclaim, a /16 hostmask is used. */
786             sprintf(hostname, "%d.%d.*", user->ip.in6_8[12], user->ip.in6_8[13]);
787         } else if (irc_in_addr_is_ipv6(user->ip)) {
788             /* Who knows what the default mask should be?  Use a /48 to start with. */
789             sprintf(hostname, "%x:%x:%x:*", ntohs(user->ip.in6[0]), ntohs(user->ip.in6[1]), ntohs(user->ip.in6[2]));
790         } else {
791             /* Unknown type; just copy IP directly. */
792             irc_ntop(hostname, IRC_NTOP_MAX_SIZE, &user->ip);
793         }
794     } else {
795         int cnt;
796         /* This heuristic could be made smarter.  Is it worth the effort? */
797         for (ii=cnt=0; hostname[ii]; ii++)
798             if (hostname[ii] == '.')
799                 cnt++;
800         if (cnt == 0 || cnt == 1) {
801             /* only a one- or two-level domain name; leave hostname */
802         } else if (cnt == 2) {
803             for (ii=0; user->hostname[ii] != '.'; ii++) ;
804             /* Add 3 to account for the *. and \0. */
805             hostname = alloca(strlen(user->hostname+ii)+3);
806             sprintf(hostname, "*.%s", user->hostname+ii+1);
807         } else {
808             for (cnt=3, ii--; cnt; ii--)
809                 if (user->hostname[ii] == '.')
810                     cnt--;
811             /* The loop above will overshoot the dot one character;
812                we skip forward two (the one character and the dot)
813                when printing, so we only add one for the \0. */
814             hostname = alloca(strlen(user->hostname+ii)+1);
815             sprintf(hostname, "*.%s", user->hostname+ii+2);
816         }
817     }
818     /* Emit hostmask */
819     len = strlen(ident) + strlen(hostname) + 2;
820     if (nickname) {
821         len += strlen(nickname) + 1;
822         mask = malloc(len);
823         sprintf(mask, "%s!%s@%s", nickname, ident, hostname);
824     } else {
825         mask = malloc(len);
826         sprintf(mask, "%s@%s", ident, hostname);
827     }
828     return mask;
829 }
830
831 int
832 IsChannelName(const char *name) {
833     unsigned int ii;
834
835     if (*name !='#')
836         return 0;
837     for (ii=1; name[ii]; ++ii) {
838         if ((name[ii] > 0) && (name[ii] <= 32))
839             return 0;
840         if (name[ii] == ',')
841             return 0;
842         if (name[ii] == '\xa0')
843             return 0;
844     }
845     return 1;
846 }
847
848 unsigned int
849 irc_user_modes(const struct userNode *user, char modes[], size_t length)
850 {
851     unsigned int ii, jj;
852
853     for (ii = jj = 0; (jj < length) && (irc_user_mode_chars[ii] != '\0'); ++ii) {
854         if ((user->modes & (1 << ii)) && (irc_user_mode_chars[ii] != ' '))
855             modes[jj++] = irc_user_mode_chars[ii];
856     }
857
858     ii = jj;
859     while (jj < length)
860         modes[jj++] = '\0';
861
862     return ii;
863 }