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