Support ircu's XQUERY and XRESPONSE.
[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 unsigned long 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_time = 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     unsigned long 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 == (unsigned long)-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 long uptime;
357         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     extern unsigned short offchannel_allowed[256];
434     struct privmsg_desc *pd = data;
435     struct modeNode *mn;
436     struct chanmsg_func *cf;
437     int x;
438
439     /* Don't complain if it can't find the modeNode because the channel might
440      * be -n */
441     if ((mn = GetUserMode(cn, pd->user)))
442         mn->idle_since = now;
443
444     /* Never send a NOTICE to a channel to one of the services */
445     cf = &chanmsg_funcs[(unsigned char)pd->text[0]];
446     if (cf->func && !pd->is_notice
447         && (offchannel_allowed[(unsigned char)pd->text[0]]
448             || (GetUserMode(cn, cf->service) && !IsDeaf(cf->service))))
449         cf->func(pd->user, cn, pd->text+1, cf->service, pd->is_notice);
450
451     /* This catches *all* text sent to the channel that the services server sees */
452     for (x = 0; x < ALLCHANMSG_FUNCS_MAX; x++) {
453        cf = (struct chanmsg_func *)&allchanmsg_funcs[x];
454        if (!cf->func)
455            break; /* end of list */
456        else
457            cf->func(pd->user, cn, pd->text, cf->service, pd->is_notice);
458     }
459 }
460
461 static void
462 privmsg_invalid(char *name, void *data)
463 {
464     struct privmsg_desc *pd = data;
465
466     if (*name == '$')
467         return;
468     irc_numeric(pd->user, ERR_NOSUCHNICK, "%s@%s :No such nick", name, self->name);
469 }
470
471 struct part_desc {
472     struct userNode *user;
473     const char *text;
474 };
475
476 static void
477 part_helper(struct chanNode *cn, void *data)
478 {
479     struct part_desc *desc = data;
480     DelChannelUser(desc->user, cn, desc->text, false);
481 }
482
483 static CMD_FUNC(cmd_part)
484 {
485     struct part_desc desc;
486
487     if (argc < 2)
488         return 0;
489     desc.user = GetUserH(origin);
490     if (!desc.user)
491         return 0;
492     desc.text = (argc > 2) ? argv[argc - 1] : NULL;
493     parse_foreach(argv[1], part_helper, NULL, NULL, NULL, &desc);
494     return 1;
495 }
496
497 void
498 reg_chanmsg_func(unsigned char prefix, struct userNode *service, chanmsg_func_t handler)
499 {
500     if (chanmsg_funcs[prefix].func)
501         log_module(MAIN_LOG, LOG_WARNING, "Re-registering new chanmsg handler for character `%c'.", prefix);
502     chanmsg_funcs[prefix].func = handler;
503     chanmsg_funcs[prefix].service = service;
504 }
505
506 void
507 reg_allchanmsg_func(struct userNode *service, chanmsg_func_t handler)
508 {
509     int x;
510     for (x = 0; x < ALLCHANMSG_FUNCS_MAX; x++) {
511        if (allchanmsg_funcs[x].func)
512          continue;
513        allchanmsg_funcs[x].func = handler;
514        allchanmsg_funcs[x].service = service;
515        break;
516     }
517 }
518
519 struct userNode *
520 get_chanmsg_bot(unsigned char prefix)
521 {
522     return chanmsg_funcs[prefix].service;
523 }
524
525 static mode_change_func_t *mcf_list;
526 static unsigned int mcf_size = 0, mcf_used = 0;
527
528 void
529 reg_mode_change_func(mode_change_func_t handler)
530 {
531     if (mcf_used == mcf_size) {
532         if (mcf_size) {
533             mcf_size <<= 1;
534             mcf_list = realloc(mcf_list, mcf_size*sizeof(mode_change_func_t));
535         } else {
536             mcf_size = 8;
537             mcf_list = malloc(mcf_size*sizeof(mode_change_func_t));
538         }
539     }
540     mcf_list[mcf_used++] = handler;
541 }
542
543 static oper_func_t *of_list;
544 static unsigned int of_size = 0, of_used = 0;
545
546 void
547 reg_oper_func(oper_func_t handler)
548 {
549     if (of_used == of_size) {
550         if (of_size) {
551             of_size <<= 1;
552             of_list = realloc(of_list, of_size*sizeof(oper_func_t));
553         } else {
554             of_size = 8;
555             of_list = malloc(of_size*sizeof(oper_func_t));
556         }
557     }
558     of_list[of_used++] = handler;
559 }
560
561 static void
562 call_oper_funcs(struct userNode *user)
563 {
564     unsigned int n;
565     if (IsLocal(user))
566         return;
567     for (n=0; (n<of_used) && !user->dead; n++)
568     {
569         of_list[n](user);
570     }
571 }
572
573 static xquery_func_t *xqf_list;
574 static unsigned int xqf_size = 0, xqf_used = 0;
575
576 void
577 reg_xquery_func(xquery_func_t handler)
578 {
579     if (xqf_used == xqf_size) {
580         if (xqf_size) {
581             xqf_size <<= 1;
582             xqf_list = realloc(xqf_list, xqf_size*sizeof(xquery_func_t));
583         } else {
584             xqf_size = 8;
585             xqf_list = malloc(xqf_size*sizeof(xquery_func_t));
586         }
587     }
588     xqf_list[xqf_used++] = handler;
589 }
590
591 static void
592 call_xquery_funcs(struct server *source, const char routing[], const char query[])
593 {
594     unsigned int n;
595     for (n=0; n < xqf_used; n++)
596     {
597         xqf_list[n](source, routing, query);
598     }
599 }
600
601 struct mod_chanmode *
602 mod_chanmode_alloc(unsigned int argc)
603 {
604     struct mod_chanmode *res;
605     if (argc > 1)
606         res = calloc(1, sizeof(*res) + (argc-1)*sizeof(res->args[0]));
607     else
608         res = calloc(1, sizeof(*res));
609     if (res) {
610 #if !defined(NDEBUG)
611         res->alloc_argc = argc;
612 #endif
613         res->argc = argc;
614     }
615     return res;
616 }
617
618 struct mod_chanmode *
619 mod_chanmode_dup(struct mod_chanmode *orig, unsigned int extra)
620 {
621     struct mod_chanmode *res;
622     res = mod_chanmode_alloc(orig->argc + extra);
623     if (res) {
624         res->modes_set = orig->modes_set;
625         res->modes_clear = orig->modes_clear;
626         res->new_limit = orig->new_limit;
627         memcpy(res->new_key, orig->new_key, sizeof(res->new_key));
628         memcpy(res->new_upass, orig->new_upass, sizeof(res->new_upass));
629         memcpy(res->new_apass, orig->new_apass, sizeof(res->new_apass));
630         res->argc = orig->argc;
631         memcpy(res->args, orig->args, orig->argc*sizeof(orig->args[0]));
632     }
633     return res;
634 }
635
636 void
637 mod_chanmode_apply(struct userNode *who, struct chanNode *channel, struct mod_chanmode *change)
638 {
639     struct banNode *bn;
640     unsigned int ii, jj;
641
642     assert(change->argc <= change->alloc_argc);
643     channel->modes = (channel->modes & ~change->modes_clear) | change->modes_set;
644     if (change->modes_set & MODE_LIMIT)
645         channel->limit = change->new_limit;
646     if (change->modes_set & MODE_KEY)
647         strcpy(channel->key, change->new_key);
648     if (change->modes_set & MODE_UPASS)
649        strcpy(channel->upass, change->new_upass);
650     if (change->modes_set & MODE_APASS)
651        strcpy(channel->apass, change->new_apass);
652     for (ii = 0; ii < change->argc; ++ii) {
653         switch (change->args[ii].mode) {
654         case MODE_BAN:
655             /* If any existing ban is a subset of the new ban,
656              * silently remove it.  The new ban is not allowed
657              * to be more specific than an existing ban.
658              */
659             for (jj=0; jj<channel->banlist.used; ++jj) {
660                 bn = channel->banlist.list[jj];
661                 if (match_ircglobs(change->args[ii].u.hostmask, bn->ban)) {
662                     banList_remove(&channel->banlist, bn);
663                     free(bn);
664                     jj--;
665                 }
666             }
667             bn = calloc(1, sizeof(*bn));
668             safestrncpy(bn->ban, change->args[ii].u.hostmask, sizeof(bn->ban));
669             if (who)
670                 safestrncpy(bn->who, who->nick, sizeof(bn->who));
671             else
672                 safestrncpy(bn->who, "<unknown>", sizeof(bn->who));
673             bn->set = now;
674             banList_append(&channel->banlist, bn);
675             break;
676         case MODE_REMOVE|MODE_BAN:
677             for (jj=0; jj<channel->banlist.used; ++jj) {
678                 bn = channel->banlist.list[jj];
679                 if (strcmp(bn->ban, change->args[ii].u.hostmask))
680                     continue;
681                 free(bn);
682                 banList_remove(&channel->banlist, bn);
683                 break;
684             }
685             break;
686         case MODE_CHANOP:
687         case MODE_VOICE:
688         case MODE_VOICE|MODE_CHANOP:
689         case MODE_REMOVE|MODE_CHANOP:
690         case MODE_REMOVE|MODE_VOICE:
691         case MODE_REMOVE|MODE_VOICE|MODE_CHANOP:
692             if (change->args[ii].mode & MODE_REMOVE)
693                 change->args[ii].u.member->modes &= ~change->args[ii].mode;
694             else
695                 change->args[ii].u.member->modes |= change->args[ii].mode;
696             break;
697         default:
698             assert(0 && "Invalid mode argument");
699             continue;
700         }
701     }
702 }
703
704 void
705 mod_chanmode_free(struct mod_chanmode *change)
706 {
707     free(change);
708 }
709
710 int
711 mod_chanmode(struct userNode *who, struct chanNode *channel, char **modes, unsigned int argc, unsigned int flags)
712 {
713     struct modeNode *member;
714     struct mod_chanmode *change;
715     unsigned int ii;
716     short base_oplevel;
717
718     if (!modes || !modes[0])
719         return 0;
720     if (who && (member = GetUserMode(channel, who)))
721         base_oplevel = member->oplevel;
722     else
723         base_oplevel = MAXOPLEVEL;
724     if (!(change = mod_chanmode_parse(channel, modes, argc, flags, base_oplevel)))
725         return 0;
726     if (flags & MC_ANNOUNCE)
727         mod_chanmode_announce(who, channel, change);
728     else
729         mod_chanmode_apply(who, channel, change);
730     if (flags & MC_NOTIFY)
731         for (ii = 0; ii < mcf_used; ++ii)
732             mcf_list[ii](channel, who, change);
733     mod_chanmode_free(change);
734     return 1;
735 }
736
737 int
738 irc_make_chanmode(struct chanNode *chan, char *out)
739 {
740     struct mod_chanmode change;
741     mod_chanmode_init(&change);
742     change.modes_set = chan->modes;
743     change.new_limit = chan->limit;
744     safestrncpy(change.new_key, chan->key, sizeof(change.new_key));
745     safestrncpy(change.new_upass, chan->upass, sizeof(change.new_upass));
746     safestrncpy(change.new_apass, chan->apass, sizeof(change.new_apass));
747     return strlen(mod_chanmode_format(&change, out));
748 }
749
750 char *
751 generate_hostmask(struct userNode *user, int options)
752 {
753     irc_in_addr_t ip;
754     char *nickname, *ident, *hostname, *mask;
755     int len, ii;
756
757     /* figure out string parts */
758     if (options & GENMASK_OMITNICK)
759         nickname = NULL;
760     else if (options & GENMASK_USENICK)
761         nickname = user->nick;
762     else
763         nickname = "*";
764     if (options & GENMASK_STRICT_IDENT)
765         ident = user->ident;
766     else if (options & GENMASK_ANY_IDENT)
767         ident = "*";
768     else if (IsFakeIdent(user) && IsHiddenHost(user) && !(options & GENMASK_NO_HIDING))
769         ident = user->fakeident;
770     else {
771         ident = alloca(strlen(user->ident)+2);
772         ident[0] = '*';
773         strcpy(ident+1, user->ident + ((*user->ident == '~')?1:0));
774     }
775     hostname = user->hostname;
776     if (IsFakeHost(user) && IsHiddenHost(user) && !(options & GENMASK_NO_HIDING)) {
777         hostname = user->fakehost;
778     } else if (IsHiddenHost(user) && user->handle_info && hidden_host_suffix && !(options & GENMASK_NO_HIDING)) {
779         hostname = alloca(strlen(user->handle_info->handle) + strlen(hidden_host_suffix) + 2);
780         sprintf(hostname, "%s.%s", user->handle_info->handle, hidden_host_suffix);
781     } else if (options & GENMASK_STRICT_HOST) {
782         if (options & GENMASK_BYIP)
783             hostname = (char*)irc_ntoa(&user->ip);
784     } else if ((options & GENMASK_BYIP) || irc_pton(&ip, NULL, hostname)) {
785         /* Should generate an IP-based hostmask. */
786         hostname = alloca(IRC_NTOP_MAX_SIZE);
787         hostname[IRC_NTOP_MAX_SIZE-1] = '\0';
788         if (irc_in_addr_is_ipv4(user->ip)) {
789             /* By popular acclaim, a /16 hostmask is used. */
790             sprintf(hostname, "%d.%d.*", user->ip.in6_8[12], user->ip.in6_8[13]);
791         } else if (irc_in_addr_is_ipv6(user->ip)) {
792             /* Who knows what the default mask should be?  Use a /48 to start with. */
793             sprintf(hostname, "%x:%x:%x:*", ntohs(user->ip.in6[0]), ntohs(user->ip.in6[1]), ntohs(user->ip.in6[2]));
794         } else {
795             /* Unknown type; just copy IP directly. */
796             irc_ntop(hostname, IRC_NTOP_MAX_SIZE, &user->ip);
797         }
798     } else {
799         int cnt;
800         /* This heuristic could be made smarter.  Is it worth the effort? */
801         for (ii=cnt=0; hostname[ii]; ii++)
802             if (hostname[ii] == '.')
803                 cnt++;
804         if (cnt == 0 || cnt == 1) {
805             /* only a one- or two-level domain name; leave hostname */
806         } else if (cnt == 2) {
807             for (ii=0; user->hostname[ii] != '.'; ii++) ;
808             /* Add 3 to account for the *. and \0. */
809             hostname = alloca(strlen(user->hostname+ii)+3);
810             sprintf(hostname, "*.%s", user->hostname+ii+1);
811         } else {
812             for (cnt=3, ii--; cnt; ii--)
813                 if (user->hostname[ii] == '.')
814                     cnt--;
815             /* The loop above will overshoot the dot one character;
816                we skip forward two (the one character and the dot)
817                when printing, so we only add one for the \0. */
818             hostname = alloca(strlen(user->hostname+ii)+1);
819             sprintf(hostname, "*.%s", user->hostname+ii+2);
820         }
821     }
822     /* Emit hostmask */
823     len = strlen(ident) + strlen(hostname) + 2;
824     if (nickname) {
825         len += strlen(nickname) + 1;
826         mask = malloc(len);
827         sprintf(mask, "%s!%s@%s", nickname, ident, hostname);
828     } else {
829         mask = malloc(len);
830         sprintf(mask, "%s@%s", ident, hostname);
831     }
832     return mask;
833 }
834
835 int
836 IsChannelName(const char *name) {
837     unsigned int ii;
838
839     if (*name !='#')
840         return 0;
841     for (ii=1; name[ii]; ++ii) {
842         if ((name[ii] > 0) && (name[ii] <= 32))
843             return 0;
844         if (name[ii] == ',')
845             return 0;
846         if (name[ii] == '\xa0')
847             return 0;
848     }
849     return 1;
850 }
851
852 unsigned int
853 irc_user_modes(const struct userNode *user, char modes[], size_t length)
854 {
855     unsigned int ii, jj;
856
857     for (ii = jj = 0; (jj < length) && (irc_user_mode_chars[ii] != '\0'); ++ii) {
858         if ((user->modes & (1 << ii)) && (irc_user_mode_chars[ii] != ' '))
859             modes[jj++] = irc_user_mode_chars[ii];
860     }
861
862     ii = jj;
863     while (jj < length)
864         modes[jj++] = '\0';
865
866     return ii;
867 }