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