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