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