]> jfr.im git - irc/evilnet/x3.git/blame - src/proto-common.c
Merged in srvx 1.4-RC1 changes. DNSBL parts are missing as it hasnt even been impleme...
[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{
ff3b058a 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 */
111 self->link = self->boot = now;
112 cManager.uplink->state = AUTHENTICATING;
113 irc_introduce(cManager.uplink->password);
114 replay_connected = 1;
115 } else if (!replay_read()) {
116 log_module(MAIN_LOG, LOG_ERROR, "Connection to server lost.");
117 close_socket();
118 }
119 timeq_run();
120 }
121}
122
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 {
ff3b058a 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{
ff3b058a 336 if (argv[1]) log_module(MAIN_LOG, LOG_ERROR, "Error from ircd: %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;
437 struct chanmsg_func *cf = &chanmsg_funcs[(unsigned char)pd->text[0]];
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 */
ff3b058a 446 if (!pd->is_notice && cf->func && ((cn->modes & MODE_REGISTERED) || GetUserMode(cn, cf->service)))
447 cf->func(pd->user, cn, pd->text+1, cf->service); /* XXX- taken out in 1.4rc1 patchset but causes errors */
63c95a47 448 else
449 spamserv_channel_message(cn, pd->user, pd->text);
d76ed9a9
AS
450
451 /* This catches *all* text sent to the channel that the services server sees */
452 for (x = 0; x < ALLCHANMSG_FUNCS_MAX; x++) {
453 cf = (struct chanmsg_func *)&allchanmsg_funcs[x];
454 if (!cf->func)
455 break; /* end of list */
456 else
0d16e639 457 cf->func(pd->user, cn, pd->text, cf->service);
d76ed9a9
AS
458 }
459}
460
461static void
462privmsg_invalid(char *name, void *data)
463{
464 struct privmsg_desc *pd = data;
465
466 if (*name == '$')
467 return;
468 irc_numeric(pd->user, ERR_NOSUCHNICK, "%s@%s :No such nick", name, self->name);
469}
470
ff3b058a 471struct part_desc {
472 struct userNode *user;
473 const char *text;
474};
475
d76ed9a9
AS
476static void
477part_helper(struct chanNode *cn, void *data)
478{
ff3b058a 479 struct part_desc *desc = data;
480 DelChannelUser(desc->user, cn, desc->text, false);
481}
482
483static CMD_FUNC(cmd_part)
484{
485 struct part_desc desc;
486
487 if (argc < 2)
488 return 0;
489 desc.user = GetUserH(origin);
490 if (!desc.user)
491 return 0;
492 desc.text = (argc > 2) ? argv[argc - 1] : NULL;
493 parse_foreach(argv[1], part_helper, NULL, NULL, NULL, &desc);
494 return 1;
d76ed9a9
AS
495}
496
497void
498reg_chanmsg_func(unsigned char prefix, struct userNode *service, chanmsg_func_t handler)
499{
500 if (chanmsg_funcs[prefix].func)
501 log_module(MAIN_LOG, LOG_WARNING, "Re-registering new chanmsg handler for character `%c'.", prefix);
502 chanmsg_funcs[prefix].func = handler;
503 chanmsg_funcs[prefix].service = service;
504}
505
506void
507reg_allchanmsg_func(struct userNode *service, chanmsg_func_t handler)
508{
509 int x;
510 for (x = 0; x < ALLCHANMSG_FUNCS_MAX; x++) {
511 if (allchanmsg_funcs[x].func)
512 continue;
513 allchanmsg_funcs[x].func = handler;
514 allchanmsg_funcs[x].service = service;
515 break;
516 }
517}
518
519struct userNode *
520get_chanmsg_bot(unsigned char prefix)
521{
522 return chanmsg_funcs[prefix].service;
523}
524
525static mode_change_func_t *mcf_list;
526static unsigned int mcf_size = 0, mcf_used = 0;
527
528void
529reg_mode_change_func(mode_change_func_t handler)
530{
531 if (mcf_used == mcf_size) {
532 if (mcf_size) {
31f23f13
AS
533 mcf_size <<= 1;
534 mcf_list = realloc(mcf_list, mcf_size*sizeof(mode_change_func_t));
d76ed9a9 535 } else {
31f23f13
AS
536 mcf_size = 8;
537 mcf_list = malloc(mcf_size*sizeof(mode_change_func_t));
d76ed9a9
AS
538 }
539 }
540 mcf_list[mcf_used++] = handler;
541}
542
543struct mod_chanmode *
544mod_chanmode_alloc(unsigned int argc)
545{
546 struct mod_chanmode *res;
547 if (argc > 1)
548 res = calloc(1, sizeof(*res) + (argc-1)*sizeof(res->args[0]));
549 else
550 res = calloc(1, sizeof(*res));
551 if (res) {
552#if !defined(NDEBUG)
553 res->alloc_argc = argc;
554#endif
555 res->argc = argc;
556 }
557 return res;
558}
559
560struct mod_chanmode *
561mod_chanmode_dup(struct mod_chanmode *orig, unsigned int extra)
562{
563 struct mod_chanmode *res;
564 res = mod_chanmode_alloc(orig->argc + extra);
565 if (res) {
566 res->modes_set = orig->modes_set;
567 res->modes_clear = orig->modes_clear;
568 res->new_limit = orig->new_limit;
569 memcpy(res->new_key, orig->new_key, sizeof(res->new_key));
2f61d1d7 570 memcpy(res->new_upass, orig->new_upass, sizeof(res->new_upass));
571 memcpy(res->new_apass, orig->new_apass, sizeof(res->new_apass));
d76ed9a9
AS
572 res->argc = orig->argc;
573 memcpy(res->args, orig->args, orig->argc*sizeof(orig->args[0]));
574 }
575 return res;
576}
577
578void
579mod_chanmode_apply(struct userNode *who, struct chanNode *channel, struct mod_chanmode *change)
580{
581 struct banNode *bn;
2aef5f4b 582 struct exemptNode *en;
d76ed9a9
AS
583 unsigned int ii, jj;
584
585 assert(change->argc <= change->alloc_argc);
586 channel->modes = (channel->modes & ~change->modes_clear) | change->modes_set;
587 if (change->modes_set & MODE_LIMIT)
588 channel->limit = change->new_limit;
589 if (change->modes_set & MODE_KEY)
590 strcpy(channel->key, change->new_key);
2f61d1d7 591 if (change->modes_set & MODE_UPASS)
592 strcpy(channel->upass, change->new_upass);
593 if (change->modes_set & MODE_APASS)
594 strcpy(channel->apass, change->new_apass);
d76ed9a9
AS
595 for (ii = 0; ii < change->argc; ++ii) {
596 switch (change->args[ii].mode) {
597 case MODE_BAN:
598 /* If any existing ban is a subset of the new ban,
599 * silently remove it. The new ban is not allowed
600 * to be more specific than an existing ban.
601 */
602 for (jj=0; jj<channel->banlist.used; ++jj) {
0d16e639 603 bn = channel->banlist.list[jj];
604 if (match_ircglobs(change->args[ii].u.hostmask, bn->ban)) {
605 banList_remove(&channel->banlist, bn);
606 free(bn);
d76ed9a9
AS
607 jj--;
608 }
609 }
610 bn = calloc(1, sizeof(*bn));
a32da4c7 611 safestrncpy(bn->ban, change->args[ii].u.hostmask, sizeof(bn->ban));
d76ed9a9
AS
612 if (who)
613 safestrncpy(bn->who, who->nick, sizeof(bn->who));
614 else
615 safestrncpy(bn->who, "<unknown>", sizeof(bn->who));
616 bn->set = now;
617 banList_append(&channel->banlist, bn);
618 break;
619 case MODE_REMOVE|MODE_BAN:
620 for (jj=0; jj<channel->banlist.used; ++jj) {
0d16e639 621 bn = channel->banlist.list[jj];
622 if (strcmp(bn->ban, change->args[ii].u.hostmask))
d76ed9a9 623 continue;
0d16e639 624 free(bn);
625 banList_remove(&channel->banlist, bn);
d76ed9a9
AS
626 break;
627 }
628 break;
2aef5f4b 629 case MODE_EXEMPT:
630 /* If any existing exempt is a subset of the new exempt,
631 * silently remove it. The new exempt is not allowed
632 * to be more specific than an existing exempt.
633 */
634 for (jj=0; jj<channel->exemptlist.used; ++jj) {
a32da4c7 635 if (match_ircglobs(change->args[ii].u.hostmask, channel->exemptlist.list[jj]->exempt)) {
2aef5f4b 636 exemptList_remove(&channel->exemptlist, channel->exemptlist.list[jj]);
637 free(channel->exemptlist.list[jj]);
638 jj--;
639 }
640 }
641 en = calloc(1, sizeof(*en));
a32da4c7 642 safestrncpy(en->exempt, change->args[ii].u.hostmask, sizeof(en->exempt));
2aef5f4b 643 if (who)
644 safestrncpy(en->who, who->nick, sizeof(en->who));
645 else
646 safestrncpy(en->who, "<unknown>", sizeof(en->who));
647 en->set = now;
648 exemptList_append(&channel->exemptlist, en);
649 break;
650 case MODE_REMOVE|MODE_EXEMPT:
651 for (jj=0; jj<channel->exemptlist.used; ++jj) {
a32da4c7 652 if (strcmp(channel->exemptlist.list[jj]->exempt, change->args[ii].u.hostmask))
2aef5f4b 653 continue;
654 free(channel->exemptlist.list[jj]);
655 exemptList_remove(&channel->exemptlist, channel->exemptlist.list[jj]);
656 break;
657 }
658 break;
4bffb7bd
AS
659 /* XXX Hack: this is the stupedest use of switch iv ever seen.
660 * you have to compare for EVERY POSSIBLE COMBINATION of bitmask
661 * because switch does only full comparison. This needs redone as if/else.
662 **/
d76ed9a9 663 case MODE_CHANOP:
55342ce8 664 case MODE_HALFOP:
d76ed9a9 665 case MODE_VOICE:
4bffb7bd
AS
666 case MODE_VOICE|MODE_CHANOP:
667 case MODE_VOICE|MODE_HALFOP:
668 case MODE_CHANOP|MODE_HALFOP:
55342ce8 669 case MODE_VOICE|MODE_CHANOP|MODE_HALFOP:
d76ed9a9 670 case MODE_REMOVE|MODE_CHANOP:
55342ce8 671 case MODE_REMOVE|MODE_HALFOP:
d76ed9a9 672 case MODE_REMOVE|MODE_VOICE:
4bffb7bd
AS
673 case MODE_REMOVE|MODE_VOICE|MODE_CHANOP:
674 case MODE_REMOVE|MODE_VOICE|MODE_HALFOP:
675 case MODE_REMOVE|MODE_CHANOP|MODE_HALFOP:
55342ce8 676 case MODE_REMOVE|MODE_VOICE|MODE_CHANOP|MODE_HALFOP:
d76ed9a9 677 if (change->args[ii].mode & MODE_REMOVE)
a32da4c7 678 change->args[ii].u.member->modes &= ~change->args[ii].mode;
d76ed9a9 679 else
a32da4c7 680 change->args[ii].u.member->modes |= change->args[ii].mode;
d76ed9a9
AS
681 break;
682 default:
683 assert(0 && "Invalid mode argument");
684 continue;
685 }
686 }
687}
688
689void
690mod_chanmode_free(struct mod_chanmode *change)
691{
692 free(change);
693}
694
695int
696mod_chanmode(struct userNode *who, struct chanNode *channel, char **modes, unsigned int argc, unsigned int flags)
697{
2f61d1d7 698 struct modeNode *member;
d76ed9a9
AS
699 struct mod_chanmode *change;
700 unsigned int ii;
2f61d1d7 701 short base_oplevel;
702
d76ed9a9
AS
703
704 if (!modes || !modes[0])
705 return 0;
2f61d1d7 706 if (who && (member = GetUserMode(channel, who)))
707 base_oplevel = member->oplevel;
708 else
709 base_oplevel = MAXOPLEVEL;
710 if (!(change = mod_chanmode_parse(channel, modes, argc, flags, base_oplevel)))
d76ed9a9 711 return 0;
ec311f39 712
713 call_channel_mode_funcs(who, channel, modes, argc);
714
d76ed9a9
AS
715 if (flags & MC_ANNOUNCE)
716 mod_chanmode_announce(who, channel, change);
717 else
718 mod_chanmode_apply(who, channel, change);
719 if (flags & MC_NOTIFY)
720 for (ii = 0; ii < mcf_used; ++ii)
721 mcf_list[ii](channel, who, change);
722 mod_chanmode_free(change);
723 return 1;
724}
725
726int
a32da4c7 727irc_make_chanmode(struct chanNode *chan, char *out)
728{
d76ed9a9
AS
729 struct mod_chanmode change;
730 mod_chanmode_init(&change);
731 change.modes_set = chan->modes;
732 change.new_limit = chan->limit;
733 safestrncpy(change.new_key, chan->key, sizeof(change.new_key));
2f61d1d7 734 safestrncpy(change.new_upass, chan->upass, sizeof(change.new_upass));
735 safestrncpy(change.new_apass, chan->apass, sizeof(change.new_apass));
d76ed9a9
AS
736 return strlen(mod_chanmode_format(&change, out));
737}
738
ec311f39 739static user_mode_func_t *um_list;
740static unsigned int um_size = 0, um_used = 0;
741
742void
743reg_user_mode_func(user_mode_func_t handler)
744{
745 if (um_used == um_size) {
746 if (um_size) {
747 um_size <<= 1;
748 um_list = realloc(um_list, um_size*sizeof(user_mode_func_t));
749 } else {
750 um_size = 8;
751 um_list = malloc(um_size*sizeof(user_mode_func_t));
752 }
753 }
754 um_list[um_used++] = handler;
755}
756
757void
758unreg_user_mode_func(user_mode_func_t handler)
759{
760 unsigned int i;
761 for (i=0; i<um_used; i++) {
762 if (um_list[i] == handler) break;
763 }
764 if (i == um_used) return;
765 memmove(um_list+i, um_list+i+1, (um_used-i-1)*sizeof(um_list[0]));
766 um_used--;
767}
768
769static void
770call_user_mode_funcs(struct userNode *user, const char *mode_change)
771{
772 unsigned int n;
773 for (n=0; n<um_used; n++) {
774 um_list[n](user, mode_change);
775 }
776}
777
778static channel_mode_func_t *cm_list;
779static unsigned int cm_size = 0, cm_used = 0;
780
781void
782reg_channel_mode_func(channel_mode_func_t handler)
783{
784 if (cm_used == cm_size) {
785 if (cm_size) {
786 cm_size <<= 1;
787 cm_list = realloc(cm_list, cm_size*sizeof(channel_mode_func_t));
788 } else {
789 cm_size = 8;
790 cm_list = malloc(cm_size*sizeof(channel_mode_func_t));
791 }
792 }
793 cm_list[cm_used++] = handler;
794}
795
796void
797unreg_channel_mode_func(channel_mode_func_t handler)
798{
799 unsigned int i;
800 for (i=0; i<cm_used; i++) {
801 if(cm_list[i] == handler) break;
802 }
803 if (i == cm_used) return;
804 memmove(cm_list+i, cm_list+i+1, (cm_used-i-1)*sizeof(cm_list[0]));
805 cm_used--;
806}
807
808static void
809call_channel_mode_funcs(struct userNode *who, struct chanNode *channel, char **modes, unsigned int argc)
810{
811 unsigned int n;
812 for (n=0; n<cm_used; n++) {
813 cm_list[n](who, channel, modes, argc);
814 }
815}
816
d76ed9a9
AS
817char *
818generate_hostmask(struct userNode *user, int options)
819{
2f61d1d7 820 irc_in_addr_t ip;
821 char *nickname, *ident, *hostname, *mask;
d76ed9a9
AS
822 int len, ii;
823
824 /* figure out string parts */
825 if (options & GENMASK_OMITNICK)
826 nickname = NULL;
827 else if (options & GENMASK_USENICK)
828 nickname = user->nick;
829 else
830 nickname = "*";
831 if (options & GENMASK_STRICT_IDENT)
182dd032
AS
832 // sethost - reed/apples
833 if (IsSetHost(user)) {
834 ident = alloca(strcspn(user->sethost, "@")+2);
835 safestrncpy(ident, user->sethost, strcspn(user->sethost, "@")+1);
836 }
837 else
d76ed9a9
AS
838 ident = user->ident;
839 else if (options & GENMASK_ANY_IDENT)
840 ident = "*";
841 else {
182dd032
AS
842 // sethost - reed/apples
843 if (IsSetHost(user)) {
844 ident = alloca(strcspn(user->sethost, "@")+3);
845 ident[0] = '*';
846 safestrncpy(ident+1, user->sethost, strcspn(user->sethost, "@")+1);
847 } else {
d76ed9a9
AS
848 ident = alloca(strlen(user->ident)+2);
849 ident[0] = '*';
850 strcpy(ident+1, user->ident + ((*user->ident == '~')?1:0));
851 }
182dd032 852 }
d76ed9a9
AS
853 hostname = user->hostname;
854 if (IsFakeHost(user) && IsHiddenHost(user) && !(options & GENMASK_NO_HIDING)) {
855 hostname = user->fakehost;
f16ad9e7 856 } else if (IsHiddenHost(user)) {
857 int style = 1;
858 char *data;
859 data = conf_get_data("server/hidden_host_type", RECDB_QSTRING);
860 if (data)
861 style = atoi(data);
862
863 if ((style == 1) && user->handle_info && hidden_host_suffix && !(options & GENMASK_NO_HIDING)) {
864 hostname = alloca(strlen(user->handle_info->handle) + strlen(hidden_host_suffix) + 2);
865 sprintf(hostname, "%s.%s", user->handle_info->handle, hidden_host_suffix);
866 } else if ((style == 2) && !(options & GENMASK_NO_HIDING)) {
867 hostname = alloca(strlen(user->crypthost));
868 sprintf(hostname, "%s", user->crypthost);
869 }
d76ed9a9
AS
870 } else if (options & GENMASK_STRICT_HOST) {
871 if (options & GENMASK_BYIP)
2f61d1d7 872 hostname = (char*)irc_ntoa(&user->ip);
873 } else if ((options & GENMASK_BYIP) || irc_pton(&ip, NULL, hostname)) {
874 /* Should generate an IP-based hostmask. */
875 hostname = alloca(IRC_NTOP_MAX_SIZE);
876 hostname[IRC_NTOP_MAX_SIZE-1] = '\0';
877 if (irc_in_addr_is_ipv4(user->ip)) {
878 /* By popular acclaim, a /16 hostmask is used. */
879 sprintf(hostname, "%d.%d.*", user->ip.in6_8[12], user->ip.in6_8[13]);
880 } else if (irc_in_addr_is_ipv6(user->ip)) {
881 /* Who knows what the default mask should be? Use a /48 to start with. */
882 sprintf(hostname, "%x:%x:%x:*", user->ip.in6[0], user->ip.in6[1], user->ip.in6[2]);
d76ed9a9 883 } else {
2f61d1d7 884 /* Unknown type; just copy IP directly. */
885 irc_ntop(hostname, IRC_NTOP_MAX_SIZE, &user->ip);
d76ed9a9
AS
886 }
887 } else {
888 int cnt;
889 /* This heuristic could be made smarter. Is it worth the effort? */
890 for (ii=cnt=0; hostname[ii]; ii++)
891 if (hostname[ii] == '.')
892 cnt++;
ff3b058a 893 if (cnt == 0 || cnt == 1) {
894 /* only a one- or two-level domain name; leave hostname */
d76ed9a9
AS
895 } else if (cnt == 2) {
896 for (ii=0; user->hostname[ii] != '.'; ii++) ;
897 /* Add 3 to account for the *. and \0. */
898 hostname = alloca(strlen(user->hostname+ii)+3);
899 sprintf(hostname, "*.%s", user->hostname+ii+1);
900 } else {
901 for (cnt=3, ii--; cnt; ii--)
902 if (user->hostname[ii] == '.')
903 cnt--;
904 /* The loop above will overshoot the dot one character;
905 we skip forward two (the one character and the dot)
906 when printing, so we only add one for the \0. */
907 hostname = alloca(strlen(user->hostname+ii)+1);
908 sprintf(hostname, "*.%s", user->hostname+ii+2);
909 }
910 }
182dd032
AS
911 // sethost - reed/apples
912 if (IsSetHost(user))
913 hostname = strchr(user->sethost, '@') + 1;
914
d76ed9a9
AS
915 /* Emit hostmask */
916 len = strlen(ident) + strlen(hostname) + 2;
917 if (nickname) {
918 len += strlen(nickname) + 1;
919 mask = malloc(len);
920 sprintf(mask, "%s!%s@%s", nickname, ident, hostname);
921 } else {
922 mask = malloc(len);
923 sprintf(mask, "%s@%s", ident, hostname);
924 }
925 return mask;
926}
927
928int
929IsChannelName(const char *name) {
930 unsigned int ii;
931
932 if (*name !='#')
933 return 0;
934 for (ii=1; name[ii]; ++ii) {
935 if ((name[ii] > 0) && (name[ii] <= 32))
936 return 0;
937 if (name[ii] == ',')
938 return 0;
939 if (name[ii] == '\xa0')
940 return 0;
941 }
942 return 1;
943}