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