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