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