]> jfr.im git - irc/evilnet/x3.git/blame - src/proto-common.c
Fixed a typo in ROUTING EXAMPLE
[irc/evilnet/x3.git] / src / proto-common.c
CommitLineData
d76ed9a9
AS
1/* proto-common.c - common IRC protocol parsing/sending support
2 * Copyright 2000-2004 srvx Development Team
3 *
83ff05c3 4 * This file is part of x3.
d76ed9a9 5 *
d0f04f71 6 * x3 is free software; you can redistribute it and/or modify
d76ed9a9 7 * it under the terms of the GNU General Public License as published by
348683aa 8 * the Free Software Foundation; either version 3 of the License, or
d76ed9a9
AS
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"
63c95a47 26#include "spamserv.h"
d914d1cb 27#include "shun.h"
d76ed9a9
AS
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
39unsigned int lines_processed;
40FILE *replay_file;
41struct io_fd *socket_io_fd;
42int force_n2k;
43const char *hidden_host_suffix;
44
45static char replay_line[MAXLEN+80];
46static int ping_freq;
47static int ping_timeout;
48static int replay_connected;
49static unsigned int nicklen = NICKLEN; /* how long do we think servers allow nicks to be? */
50static struct userList dead_users;
51
52extern struct cManagerNode cManager;
53extern unsigned long burst_length;
54extern struct cManagerNode cManager;
55extern struct policer_params *oper_policer_params, *luser_policer_params;
56extern server_link_func_t *slf_list;
57extern unsigned int slf_size, slf_used;
58extern new_user_func_t *nuf_list;
59extern unsigned int nuf_size, nuf_used;
60extern del_user_func_t *duf_list;
61extern unsigned int duf_size, duf_used;
62extern time_t boot_time;
63
64void received_ping(void);
65
66static int replay_read(void);
67static dict_t irc_func_dict;
68
69typedef void (*foreach_chanfunc) (struct chanNode *chan, void *data);
70typedef void (*foreach_nonchan) (char *name, void *data);
71typedef void (*foreach_userfunc) (struct userNode *user, void *data);
72typedef void (*foreach_nonuser) (char *name, void *data);
73static void parse_foreach(char *target_list, foreach_chanfunc cf, foreach_nonchan nc, foreach_userfunc uf, foreach_nonuser nu, void *data);
ec311f39 74static void call_channel_mode_funcs(struct userNode *who, struct chanNode *channel, char **modes, unsigned int argc);
d76ed9a9
AS
75
76static void
77uplink_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
95void
96socket_destroyed(struct io_fd *fd)
97{
1136f709 98 if (fd && fd->state != IO_CONNECTED)
d76ed9a9
AS
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
106void 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 */
1136f709 111 self->link_time = self->boot = now;
d76ed9a9
AS
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
123int
124create_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
2f61d1d7 140 socket_io_fd = ioset_connect(cManager.uplink->bind_addr, cManager.uplink->bind_addr_len, addr, port, 1, 0, NULL);
d76ed9a9
AS
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;
d76ed9a9
AS
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
157void
158replay_read_line(void)
159{
160 struct tm timestamp;
161 time_t 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 == -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
202static int
203replay_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
230static void
231replay_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
247void putsock(const char *text, ...) PRINTF_LIKE(1, 2);
248
249void
250putsock(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
273void
274close_socket(void)
275{
276 if (replay_file) {
277 replay_connected = 0;
278 socket_destroyed(socket_io_fd);
279 } else {
1136f709 280 ioset_close(socket_io_fd, 3);
281 socket_io_fd = NULL;
d76ed9a9
AS
282 }
283}
284
285#define CMD_FUNC(NAME) int NAME(UNUSED_ARG(const char *origin), UNUSED_ARG(unsigned int argc), UNUSED_ARG(char **argv))
286typedef CMD_FUNC(cmd_func_t);
287
288static 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). */
292void
293timed_send_ping(UNUSED_ARG(void *data))
294{
295 irc_ping(self->name);
296 timeq_add(now + ping_timeout, timed_ping_timeout, 0);
297}
298
299static void
300timed_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
309static 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
328static CMD_FUNC(cmd_dummy)
329{
330 /* we don't care about these messages */
331 return 1;
332}
333
334static CMD_FUNC(cmd_error)
335{
0f6fe38c 336 if (argv[1]) log_module(MAIN_LOG, LOG_ERROR, "Error: %s", argv[1]);
d76ed9a9
AS
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
349static 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 int 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
372static 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 }
ceafd592 379 irc_numeric(user, 351, "%s %s %s", PACKAGE_TARNAME, PACKAGE_VERSION, self->name);
d76ed9a9
AS
380 return 1;
381}
382
383static 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
404static void
405recalc_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
415static struct chanmsg_func {
416 chanmsg_func_t func;
417 struct userNode *service;
418} chanmsg_funcs[256]; /* indexed by trigger character */
419
420static struct allchanmsg_func {
421 chanmsg_func_t func;
422 struct userNode *service;
423} allchanmsg_funcs[ALLCHANMSG_FUNCS_MAX];
424
425struct privmsg_desc {
426 struct userNode *user;
427 char *text;
428 unsigned int is_notice : 1;
429 unsigned int is_qualified : 1;
430};
431
432static void
433privmsg_chan_helper(struct chanNode *cn, void *data)
434{
435 struct privmsg_desc *pd = data;
436 struct modeNode *mn;
1136f709 437 struct chanmsg_func *cf;
d76ed9a9
AS
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 */
1136f709 446 cf = &chanmsg_funcs[(unsigned char)pd->text[0]];
412ba860 447 if (!pd->is_notice && cf->func)
1136f709 448 cf->func(pd->user, cn, pd->text+1, cf->service, pd->is_notice);
63c95a47 449 else
450 spamserv_channel_message(cn, pd->user, pd->text);
d76ed9a9
AS
451
452 /* This catches *all* text sent to the channel that the services server sees */
453 for (x = 0; x < ALLCHANMSG_FUNCS_MAX; x++) {
454 cf = (struct chanmsg_func *)&allchanmsg_funcs[x];
455 if (!cf->func)
456 break; /* end of list */
457 else
1136f709 458 cf->func(pd->user, cn, pd->text, cf->service, pd->is_notice);
d76ed9a9
AS
459 }
460}
461
462static void
463privmsg_invalid(char *name, void *data)
464{
465 struct privmsg_desc *pd = data;
466
467 if (*name == '$')
468 return;
469 irc_numeric(pd->user, ERR_NOSUCHNICK, "%s@%s :No such nick", name, self->name);
470}
471
1136f709 472struct part_desc {
473 struct userNode *user;
474 const char *text;
475};
476
d76ed9a9
AS
477static void
478part_helper(struct chanNode *cn, void *data)
479{
1136f709 480 struct part_desc *desc = data;
481 DelChannelUser(desc->user, cn, desc->text, false);
482}
483
484static CMD_FUNC(cmd_part)
485{
486 struct part_desc desc;
487
488 if (argc < 2)
489 return 0;
490 desc.user = GetUserH(origin);
491 if (!desc.user)
492 return 0;
493 desc.text = (argc > 2) ? argv[argc - 1] : NULL;
494 parse_foreach(argv[1], part_helper, NULL, NULL, NULL, &desc);
495 return 1;
d76ed9a9
AS
496}
497
498void
499reg_chanmsg_func(unsigned char prefix, struct userNode *service, chanmsg_func_t handler)
500{
501 if (chanmsg_funcs[prefix].func)
502 log_module(MAIN_LOG, LOG_WARNING, "Re-registering new chanmsg handler for character `%c'.", prefix);
503 chanmsg_funcs[prefix].func = handler;
504 chanmsg_funcs[prefix].service = service;
505}
506
507void
508reg_allchanmsg_func(struct userNode *service, chanmsg_func_t handler)
509{
510 int x;
511 for (x = 0; x < ALLCHANMSG_FUNCS_MAX; x++) {
512 if (allchanmsg_funcs[x].func)
513 continue;
514 allchanmsg_funcs[x].func = handler;
515 allchanmsg_funcs[x].service = service;
516 break;
517 }
518}
519
520struct userNode *
521get_chanmsg_bot(unsigned char prefix)
522{
523 return chanmsg_funcs[prefix].service;
524}
525
526static mode_change_func_t *mcf_list;
527static unsigned int mcf_size = 0, mcf_used = 0;
528
529void
530reg_mode_change_func(mode_change_func_t handler)
531{
532 if (mcf_used == mcf_size) {
533 if (mcf_size) {
31f23f13
AS
534 mcf_size <<= 1;
535 mcf_list = realloc(mcf_list, mcf_size*sizeof(mode_change_func_t));
d76ed9a9 536 } else {
31f23f13
AS
537 mcf_size = 8;
538 mcf_list = malloc(mcf_size*sizeof(mode_change_func_t));
d76ed9a9
AS
539 }
540 }
541 mcf_list[mcf_used++] = handler;
542}
543
1136f709 544static oper_func_t *of_list;
545
546static unsigned int of_size = 0, of_used = 0;
547
548void
549reg_oper_func(oper_func_t handler)
550{
551 if (of_used == of_size) {
552 if (of_size) {
553 of_size <<= 1;
554 of_list = realloc(of_list, of_size*sizeof(oper_func_t));
555 } else {
556 of_size = 8;
557 of_list = malloc(of_size*sizeof(oper_func_t));
558 }
559 }
560 of_list[of_used++] = handler;
561}
562
563static void
564call_oper_funcs(struct userNode *user)
565{
566 unsigned int n;
567 if (IsLocal(user))
568 return;
569 for (n=0; (n<of_used) && !user->dead; n++)
570 {
571 of_list[n](user);
572 }
573}
574
d76ed9a9
AS
575struct mod_chanmode *
576mod_chanmode_alloc(unsigned int argc)
577{
578 struct mod_chanmode *res;
579 if (argc > 1)
580 res = calloc(1, sizeof(*res) + (argc-1)*sizeof(res->args[0]));
581 else
582 res = calloc(1, sizeof(*res));
583 if (res) {
584#if !defined(NDEBUG)
585 res->alloc_argc = argc;
586#endif
587 res->argc = argc;
588 }
589 return res;
590}
591
592struct mod_chanmode *
593mod_chanmode_dup(struct mod_chanmode *orig, unsigned int extra)
594{
595 struct mod_chanmode *res;
596 res = mod_chanmode_alloc(orig->argc + extra);
597 if (res) {
598 res->modes_set = orig->modes_set;
599 res->modes_clear = orig->modes_clear;
600 res->new_limit = orig->new_limit;
601 memcpy(res->new_key, orig->new_key, sizeof(res->new_key));
2f61d1d7 602 memcpy(res->new_upass, orig->new_upass, sizeof(res->new_upass));
603 memcpy(res->new_apass, orig->new_apass, sizeof(res->new_apass));
d76ed9a9
AS
604 res->argc = orig->argc;
605 memcpy(res->args, orig->args, orig->argc*sizeof(orig->args[0]));
606 }
607 return res;
608}
609
610void
611mod_chanmode_apply(struct userNode *who, struct chanNode *channel, struct mod_chanmode *change)
612{
613 struct banNode *bn;
2aef5f4b 614 struct exemptNode *en;
d76ed9a9
AS
615 unsigned int ii, jj;
616
617 assert(change->argc <= change->alloc_argc);
618 channel->modes = (channel->modes & ~change->modes_clear) | change->modes_set;
619 if (change->modes_set & MODE_LIMIT)
620 channel->limit = change->new_limit;
621 if (change->modes_set & MODE_KEY)
622 strcpy(channel->key, change->new_key);
2f61d1d7 623 if (change->modes_set & MODE_UPASS)
624 strcpy(channel->upass, change->new_upass);
625 if (change->modes_set & MODE_APASS)
626 strcpy(channel->apass, change->new_apass);
d76ed9a9
AS
627 for (ii = 0; ii < change->argc; ++ii) {
628 switch (change->args[ii].mode) {
629 case MODE_BAN:
630 /* If any existing ban is a subset of the new ban,
631 * silently remove it. The new ban is not allowed
632 * to be more specific than an existing ban.
633 */
634 for (jj=0; jj<channel->banlist.used; ++jj) {
0d16e639 635 bn = channel->banlist.list[jj];
636 if (match_ircglobs(change->args[ii].u.hostmask, bn->ban)) {
637 banList_remove(&channel->banlist, bn);
638 free(bn);
d76ed9a9
AS
639 jj--;
640 }
641 }
642 bn = calloc(1, sizeof(*bn));
a32da4c7 643 safestrncpy(bn->ban, change->args[ii].u.hostmask, sizeof(bn->ban));
d76ed9a9
AS
644 if (who)
645 safestrncpy(bn->who, who->nick, sizeof(bn->who));
646 else
647 safestrncpy(bn->who, "<unknown>", sizeof(bn->who));
648 bn->set = now;
649 banList_append(&channel->banlist, bn);
650 break;
651 case MODE_REMOVE|MODE_BAN:
652 for (jj=0; jj<channel->banlist.used; ++jj) {
0d16e639 653 bn = channel->banlist.list[jj];
654 if (strcmp(bn->ban, change->args[ii].u.hostmask))
d76ed9a9 655 continue;
0d16e639 656 free(bn);
657 banList_remove(&channel->banlist, bn);
d76ed9a9
AS
658 break;
659 }
660 break;
2aef5f4b 661 case MODE_EXEMPT:
662 /* If any existing exempt is a subset of the new exempt,
663 * silently remove it. The new exempt is not allowed
664 * to be more specific than an existing exempt.
665 */
666 for (jj=0; jj<channel->exemptlist.used; ++jj) {
a32da4c7 667 if (match_ircglobs(change->args[ii].u.hostmask, channel->exemptlist.list[jj]->exempt)) {
2aef5f4b 668 exemptList_remove(&channel->exemptlist, channel->exemptlist.list[jj]);
669 free(channel->exemptlist.list[jj]);
670 jj--;
671 }
672 }
673 en = calloc(1, sizeof(*en));
a32da4c7 674 safestrncpy(en->exempt, change->args[ii].u.hostmask, sizeof(en->exempt));
2aef5f4b 675 if (who)
676 safestrncpy(en->who, who->nick, sizeof(en->who));
677 else
678 safestrncpy(en->who, "<unknown>", sizeof(en->who));
679 en->set = now;
680 exemptList_append(&channel->exemptlist, en);
681 break;
682 case MODE_REMOVE|MODE_EXEMPT:
683 for (jj=0; jj<channel->exemptlist.used; ++jj) {
a32da4c7 684 if (strcmp(channel->exemptlist.list[jj]->exempt, change->args[ii].u.hostmask))
2aef5f4b 685 continue;
686 free(channel->exemptlist.list[jj]);
687 exemptList_remove(&channel->exemptlist, channel->exemptlist.list[jj]);
688 break;
689 }
690 break;
4bffb7bd
AS
691 /* XXX Hack: this is the stupedest use of switch iv ever seen.
692 * you have to compare for EVERY POSSIBLE COMBINATION of bitmask
693 * because switch does only full comparison. This needs redone as if/else.
694 **/
d76ed9a9 695 case MODE_CHANOP:
55342ce8 696 case MODE_HALFOP:
d76ed9a9 697 case MODE_VOICE:
4bffb7bd
AS
698 case MODE_VOICE|MODE_CHANOP:
699 case MODE_VOICE|MODE_HALFOP:
700 case MODE_CHANOP|MODE_HALFOP:
55342ce8 701 case MODE_VOICE|MODE_CHANOP|MODE_HALFOP:
d76ed9a9 702 case MODE_REMOVE|MODE_CHANOP:
55342ce8 703 case MODE_REMOVE|MODE_HALFOP:
d76ed9a9 704 case MODE_REMOVE|MODE_VOICE:
4bffb7bd
AS
705 case MODE_REMOVE|MODE_VOICE|MODE_CHANOP:
706 case MODE_REMOVE|MODE_VOICE|MODE_HALFOP:
707 case MODE_REMOVE|MODE_CHANOP|MODE_HALFOP:
55342ce8 708 case MODE_REMOVE|MODE_VOICE|MODE_CHANOP|MODE_HALFOP:
d76ed9a9 709 if (change->args[ii].mode & MODE_REMOVE)
a32da4c7 710 change->args[ii].u.member->modes &= ~change->args[ii].mode;
d76ed9a9 711 else
a32da4c7 712 change->args[ii].u.member->modes |= change->args[ii].mode;
d76ed9a9
AS
713 break;
714 default:
715 assert(0 && "Invalid mode argument");
716 continue;
717 }
718 }
719}
720
721void
722mod_chanmode_free(struct mod_chanmode *change)
723{
724 free(change);
725}
726
727int
728mod_chanmode(struct userNode *who, struct chanNode *channel, char **modes, unsigned int argc, unsigned int flags)
729{
2f61d1d7 730 struct modeNode *member;
d76ed9a9
AS
731 struct mod_chanmode *change;
732 unsigned int ii;
2f61d1d7 733 short base_oplevel;
734
d76ed9a9
AS
735
736 if (!modes || !modes[0])
737 return 0;
2f61d1d7 738 if (who && (member = GetUserMode(channel, who)))
739 base_oplevel = member->oplevel;
740 else
741 base_oplevel = MAXOPLEVEL;
742 if (!(change = mod_chanmode_parse(channel, modes, argc, flags, base_oplevel)))
d76ed9a9 743 return 0;
ec311f39 744
745 call_channel_mode_funcs(who, channel, modes, argc);
746
d76ed9a9
AS
747 if (flags & MC_ANNOUNCE)
748 mod_chanmode_announce(who, channel, change);
749 else
750 mod_chanmode_apply(who, channel, change);
751 if (flags & MC_NOTIFY)
752 for (ii = 0; ii < mcf_used; ++ii)
753 mcf_list[ii](channel, who, change);
754 mod_chanmode_free(change);
755 return 1;
756}
757
758int
a32da4c7 759irc_make_chanmode(struct chanNode *chan, char *out)
760{
d76ed9a9
AS
761 struct mod_chanmode change;
762 mod_chanmode_init(&change);
763 change.modes_set = chan->modes;
764 change.new_limit = chan->limit;
765 safestrncpy(change.new_key, chan->key, sizeof(change.new_key));
2f61d1d7 766 safestrncpy(change.new_upass, chan->upass, sizeof(change.new_upass));
767 safestrncpy(change.new_apass, chan->apass, sizeof(change.new_apass));
d76ed9a9
AS
768 return strlen(mod_chanmode_format(&change, out));
769}
770
ec311f39 771static user_mode_func_t *um_list;
772static unsigned int um_size = 0, um_used = 0;
773
774void
775reg_user_mode_func(user_mode_func_t handler)
776{
777 if (um_used == um_size) {
778 if (um_size) {
779 um_size <<= 1;
780 um_list = realloc(um_list, um_size*sizeof(user_mode_func_t));
781 } else {
782 um_size = 8;
783 um_list = malloc(um_size*sizeof(user_mode_func_t));
784 }
785 }
786 um_list[um_used++] = handler;
787}
788
789void
790unreg_user_mode_func(user_mode_func_t handler)
791{
792 unsigned int i;
793 for (i=0; i<um_used; i++) {
794 if (um_list[i] == handler) break;
795 }
796 if (i == um_used) return;
797 memmove(um_list+i, um_list+i+1, (um_used-i-1)*sizeof(um_list[0]));
798 um_used--;
799}
800
801static void
802call_user_mode_funcs(struct userNode *user, const char *mode_change)
803{
804 unsigned int n;
805 for (n=0; n<um_used; n++) {
806 um_list[n](user, mode_change);
807 }
808}
809
810static channel_mode_func_t *cm_list;
811static unsigned int cm_size = 0, cm_used = 0;
812
813void
814reg_channel_mode_func(channel_mode_func_t handler)
815{
816 if (cm_used == cm_size) {
817 if (cm_size) {
818 cm_size <<= 1;
819 cm_list = realloc(cm_list, cm_size*sizeof(channel_mode_func_t));
820 } else {
821 cm_size = 8;
822 cm_list = malloc(cm_size*sizeof(channel_mode_func_t));
823 }
824 }
825 cm_list[cm_used++] = handler;
826}
827
828void
829unreg_channel_mode_func(channel_mode_func_t handler)
830{
831 unsigned int i;
832 for (i=0; i<cm_used; i++) {
833 if(cm_list[i] == handler) break;
834 }
835 if (i == cm_used) return;
836 memmove(cm_list+i, cm_list+i+1, (cm_used-i-1)*sizeof(cm_list[0]));
837 cm_used--;
838}
839
840static void
841call_channel_mode_funcs(struct userNode *who, struct chanNode *channel, char **modes, unsigned int argc)
842{
843 unsigned int n;
844 for (n=0; n<cm_used; n++) {
845 cm_list[n](who, channel, modes, argc);
846 }
847}
848
d76ed9a9
AS
849char *
850generate_hostmask(struct userNode *user, int options)
851{
2f61d1d7 852 irc_in_addr_t ip;
853 char *nickname, *ident, *hostname, *mask;
d76ed9a9
AS
854 int len, ii;
855
856 /* figure out string parts */
857 if (options & GENMASK_OMITNICK)
858 nickname = NULL;
859 else if (options & GENMASK_USENICK)
860 nickname = user->nick;
861 else
862 nickname = "*";
863 if (options & GENMASK_STRICT_IDENT)
182dd032
AS
864 // sethost - reed/apples
865 if (IsSetHost(user)) {
866 ident = alloca(strcspn(user->sethost, "@")+2);
867 safestrncpy(ident, user->sethost, strcspn(user->sethost, "@")+1);
868 }
869 else
d76ed9a9
AS
870 ident = user->ident;
871 else if (options & GENMASK_ANY_IDENT)
872 ident = "*";
873 else {
182dd032
AS
874 // sethost - reed/apples
875 if (IsSetHost(user)) {
876 ident = alloca(strcspn(user->sethost, "@")+3);
877 ident[0] = '*';
878 safestrncpy(ident+1, user->sethost, strcspn(user->sethost, "@")+1);
879 } else {
d76ed9a9
AS
880 ident = alloca(strlen(user->ident)+2);
881 ident[0] = '*';
882 strcpy(ident+1, user->ident + ((*user->ident == '~')?1:0));
883 }
182dd032 884 }
d76ed9a9
AS
885 hostname = user->hostname;
886 if (IsFakeHost(user) && IsHiddenHost(user) && !(options & GENMASK_NO_HIDING)) {
887 hostname = user->fakehost;
f16ad9e7 888 } else if (IsHiddenHost(user)) {
889 int style = 1;
890 char *data;
891 data = conf_get_data("server/hidden_host_type", RECDB_QSTRING);
892 if (data)
893 style = atoi(data);
894
895 if ((style == 1) && user->handle_info && hidden_host_suffix && !(options & GENMASK_NO_HIDING)) {
896 hostname = alloca(strlen(user->handle_info->handle) + strlen(hidden_host_suffix) + 2);
897 sprintf(hostname, "%s.%s", user->handle_info->handle, hidden_host_suffix);
898 } else if ((style == 2) && !(options & GENMASK_NO_HIDING)) {
899 hostname = alloca(strlen(user->crypthost));
900 sprintf(hostname, "%s", user->crypthost);
901 }
d76ed9a9
AS
902 } else if (options & GENMASK_STRICT_HOST) {
903 if (options & GENMASK_BYIP)
2f61d1d7 904 hostname = (char*)irc_ntoa(&user->ip);
905 } else if ((options & GENMASK_BYIP) || irc_pton(&ip, NULL, hostname)) {
906 /* Should generate an IP-based hostmask. */
907 hostname = alloca(IRC_NTOP_MAX_SIZE);
908 hostname[IRC_NTOP_MAX_SIZE-1] = '\0';
909 if (irc_in_addr_is_ipv4(user->ip)) {
910 /* By popular acclaim, a /16 hostmask is used. */
911 sprintf(hostname, "%d.%d.*", user->ip.in6_8[12], user->ip.in6_8[13]);
912 } else if (irc_in_addr_is_ipv6(user->ip)) {
913 /* Who knows what the default mask should be? Use a /48 to start with. */
914 sprintf(hostname, "%x:%x:%x:*", user->ip.in6[0], user->ip.in6[1], user->ip.in6[2]);
d76ed9a9 915 } else {
2f61d1d7 916 /* Unknown type; just copy IP directly. */
917 irc_ntop(hostname, IRC_NTOP_MAX_SIZE, &user->ip);
d76ed9a9
AS
918 }
919 } else {
920 int cnt;
921 /* This heuristic could be made smarter. Is it worth the effort? */
922 for (ii=cnt=0; hostname[ii]; ii++)
923 if (hostname[ii] == '.')
924 cnt++;
1136f709 925 if (cnt == 0 || cnt == 1) {
926 /* only a one- or two-level domain name; leave hostname */
d76ed9a9
AS
927 } else if (cnt == 2) {
928 for (ii=0; user->hostname[ii] != '.'; ii++) ;
929 /* Add 3 to account for the *. and \0. */
930 hostname = alloca(strlen(user->hostname+ii)+3);
931 sprintf(hostname, "*.%s", user->hostname+ii+1);
932 } else {
933 for (cnt=3, ii--; cnt; ii--)
934 if (user->hostname[ii] == '.')
935 cnt--;
936 /* The loop above will overshoot the dot one character;
937 we skip forward two (the one character and the dot)
938 when printing, so we only add one for the \0. */
939 hostname = alloca(strlen(user->hostname+ii)+1);
940 sprintf(hostname, "*.%s", user->hostname+ii+2);
941 }
942 }
182dd032
AS
943 // sethost - reed/apples
944 if (IsSetHost(user))
945 hostname = strchr(user->sethost, '@') + 1;
946
d76ed9a9
AS
947 /* Emit hostmask */
948 len = strlen(ident) + strlen(hostname) + 2;
949 if (nickname) {
950 len += strlen(nickname) + 1;
951 mask = malloc(len);
952 sprintf(mask, "%s!%s@%s", nickname, ident, hostname);
953 } else {
954 mask = malloc(len);
955 sprintf(mask, "%s@%s", ident, hostname);
956 }
957 return mask;
958}
959
960int
961IsChannelName(const char *name) {
962 unsigned int ii;
963
964 if (*name !='#')
965 return 0;
966 for (ii=1; name[ii]; ++ii) {
967 if ((name[ii] > 0) && (name[ii] <= 32))
968 return 0;
969 if (name[ii] == ',')
970 return 0;
971 if (name[ii] == '\xa0')
972 return 0;
973 }
974 return 1;
975}