]> jfr.im git - irc/evilnet/x3.git/blame - src/proto-common.c
fix another compile warning
[irc/evilnet/x3.git] / src / proto-common.c
CommitLineData
d76ed9a9
AS
1/* proto-common.c - common IRC protocol parsing/sending support
2 * Copyright 2000-2004 srvx Development Team
3 *
83ff05c3 4 * This file is part of x3.
d76ed9a9 5 *
d0f04f71 6 * x3 is free software; you can redistribute it and/or modify
d76ed9a9
AS
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation; either version 2 of the License, or
9 * (at your option) any later version.
10 *
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
15 *
16 * You should have received a copy of the GNU General Public License
17 * along with srvx; if not, write to the Free Software Foundation,
18 * Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
19 */
20
21#include "conf.h"
22#include "gline.h"
23#include "ioset.h"
24#include "log.h"
25#include "nickserv.h"
63c95a47 26#include "spamserv.h"
d914d1cb 27#include "shun.h"
d76ed9a9
AS
28#include "timeq.h"
29#ifdef HAVE_SYS_SOCKET_H
30#include <sys/socket.h>
31#endif
32#ifdef HAVE_NETINET_IN_H
33#include <netinet/in.h>
34#endif
35#ifdef HAVE_ARPA_INET_H
36#include <arpa/inet.h>
37#endif
38
39unsigned int lines_processed;
40FILE *replay_file;
41struct io_fd *socket_io_fd;
42int force_n2k;
43const char *hidden_host_suffix;
44
45static char replay_line[MAXLEN+80];
46static int ping_freq;
47static int ping_timeout;
48static int replay_connected;
49static unsigned int nicklen = NICKLEN; /* how long do we think servers allow nicks to be? */
50static struct userList dead_users;
51
52extern struct cManagerNode cManager;
53extern unsigned long burst_length;
54extern struct cManagerNode cManager;
55extern struct policer_params *oper_policer_params, *luser_policer_params;
56extern server_link_func_t *slf_list;
57extern unsigned int slf_size, slf_used;
58extern new_user_func_t *nuf_list;
59extern unsigned int nuf_size, nuf_used;
60extern del_user_func_t *duf_list;
61extern unsigned int duf_size, duf_used;
62extern time_t boot_time;
63
64void received_ping(void);
65
66static int replay_read(void);
67static dict_t irc_func_dict;
68
69typedef void (*foreach_chanfunc) (struct chanNode *chan, void *data);
70typedef void (*foreach_nonchan) (char *name, void *data);
71typedef void (*foreach_userfunc) (struct userNode *user, void *data);
72typedef void (*foreach_nonuser) (char *name, void *data);
73static void parse_foreach(char *target_list, foreach_chanfunc cf, foreach_nonchan nc, foreach_userfunc uf, foreach_nonuser nu, void *data);
ec311f39 74static void call_channel_mode_funcs(struct userNode *who, struct chanNode *channel, char **modes, unsigned int argc);
d76ed9a9
AS
75
76static void
77uplink_readable(struct io_fd *fd) {
78 static char buffer[MAXLEN];
79 char *eol;
80 int pos;
81
82 pos = ioset_line_read(fd, buffer, sizeof(buffer));
83 if (pos <= 0) {
84 close_socket();
85 return;
86 }
87 if ((eol = strpbrk(buffer, "\r\n")))
88 *eol = 0;
89 log_replay(MAIN_LOG, false, buffer);
90 if (cManager.uplink->state != DISCONNECTED)
91 parse_line(buffer, 0);
92 lines_processed++;
93}
94
95void
96socket_destroyed(struct io_fd *fd)
97{
98 if (fd && fd->eof)
99 log_module(MAIN_LOG, LOG_ERROR, "Connection to server lost.");
100 socket_io_fd = NULL;
101 cManager.uplink->state = DISCONNECTED;
102 if (self->uplink)
103 DelServer(self->uplink, 0, NULL);
104}
105
106void replay_event_loop(void)
107{
108 while (!quit_services) {
109 if (!replay_connected) {
110 /* this time fudging is to get some of the logging right */
111 self->link = self->boot = now;
112 cManager.uplink->state = AUTHENTICATING;
113 irc_introduce(cManager.uplink->password);
114 replay_connected = 1;
115 } else if (!replay_read()) {
116 log_module(MAIN_LOG, LOG_ERROR, "Connection to server lost.");
117 close_socket();
118 }
119 timeq_run();
120 }
121}
122
123int
124create_socket_client(struct uplinkNode *target)
125{
126 int port = target->port;
127 const char *addr = target->host;
128
129 if (replay_file)
130 return feof(replay_file) ? 0 : 1;
131
132 if (socket_io_fd) {
133 /* Leave the existing socket open, say we failed. */
134 log_module(MAIN_LOG, LOG_ERROR, "Refusing to create second connection to %s:%d.", addr, port);
135 return 0;
136 }
137
138 log_module(MAIN_LOG, LOG_INFO, "Connecting to %s:%i...", addr, port);
139
2f61d1d7 140 socket_io_fd = ioset_connect(cManager.uplink->bind_addr, cManager.uplink->bind_addr_len, addr, port, 1, 0, NULL);
d76ed9a9
AS
141 if (!socket_io_fd) {
142 log_module(MAIN_LOG, LOG_ERROR, "Connection to uplink failed: %s (%d)", strerror(errno), errno);
143 target->state = DISCONNECTED;
144 target->tries++;
145 return 0;
146 }
147 socket_io_fd->readable_cb = uplink_readable;
148 socket_io_fd->destroy_cb = socket_destroyed;
149 socket_io_fd->line_reads = 1;
150 socket_io_fd->wants_reads = 1;
151 log_module(MAIN_LOG, LOG_INFO, "Connection to server established.");
152 cManager.uplink = target;
153 target->state = AUTHENTICATING;
154 target->tries = 0;
155 return 1;
156}
157
158void
159replay_read_line(void)
160{
161 struct tm timestamp;
162 time_t new_time;
163
164 if (replay_line[0]) return;
165 read_line:
166 if (!fgets(replay_line, sizeof(replay_line), replay_file)) {
167 if (feof(replay_file)) {
168 quit_services = 1;
169 memset(replay_line, 0, sizeof(replay_line));
170 return;
171 }
172 }
173 if ((replay_line[0] != '[')
174 || (replay_line[3] != ':')
175 || (replay_line[6] != ':')
176 || (replay_line[9] != ' ')
177 || (replay_line[12] != '/')
178 || (replay_line[15] != '/')
179 || (replay_line[20] != ']')
180 || (replay_line[21] != ' ')) {
181 log_module(MAIN_LOG, LOG_ERROR, "Unrecognized timestamp in replay file: %s", replay_line);
182 goto read_line;
183 }
184 timestamp.tm_hour = strtoul(replay_line+1, NULL, 10);
185 timestamp.tm_min = strtoul(replay_line+4, NULL, 10);
186 timestamp.tm_sec = strtoul(replay_line+7, NULL, 10);
187 timestamp.tm_mon = strtoul(replay_line+10, NULL, 10) - 1;
188 timestamp.tm_mday = strtoul(replay_line+13, NULL, 10);
189 timestamp.tm_year = strtoul(replay_line+16, NULL, 10) - 1900;
190 timestamp.tm_isdst = 0;
191 new_time = mktime(&timestamp);
192 if (new_time == -1) {
193 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);
194 } else {
195 now = new_time;
196 }
197
198 if (strncmp(replay_line+22, "(info) ", 7))
199 goto read_line;
200 return;
201}
202
203static int
204replay_read(void)
205{
206 size_t len;
207 char read_line[MAXLEN];
208 while (1) {
209 replay_read_line();
210 /* if it's a sent line, break out to handle it */
211 if (!strncmp(replay_line+29, " ", 3))
212 break;
213 if (!strncmp(replay_line+29, "W: ", 3)) {
214 log_module(MAIN_LOG, LOG_ERROR, "Expected response from services: %s", replay_line+32);
215 replay_line[0] = 0;
216 } else {
217 return 0;
218 }
219 }
220 log_replay(MAIN_LOG, false, replay_line+32);
221 safestrncpy(read_line, replay_line+32, sizeof(read_line));
222 len = strlen(read_line);
223 if (read_line[len-1] == '\n')
224 read_line[--len] = 0;
225 replay_line[0] = 0;
226 parse_line(read_line, 0);
227 lines_processed++;
228 return 1;
229}
230
231static void
232replay_write(char *text)
233{
234 replay_read_line();
235 if (strncmp(replay_line+29, "W: ", 3)) {
236 log_module(MAIN_LOG, LOG_ERROR, "Unexpected output during replay: %s", text);
237 return;
238 } else {
239 if (strcmp(replay_line+32, text)) {
240 log_module(MAIN_LOG, LOG_ERROR, "Incorrect output during replay:\nReceived: %sExpected: %s", text, replay_line+32);
241 } else {
242 log_replay(MAIN_LOG, true, text);
243 }
244 replay_line[0] = 0;
245 }
246}
247
248void putsock(const char *text, ...) PRINTF_LIKE(1, 2);
249
250void
251putsock(const char *text, ...)
252{
253 va_list arg_list;
254 char buffer[MAXLEN];
255 int pos;
256
257 if (!cManager.uplink || cManager.uplink->state == DISCONNECTED) return;
258 buffer[0] = '\0';
259 va_start(arg_list, text);
260 pos = vsnprintf(buffer, MAXLEN - 2, text, arg_list);
261 va_end(arg_list);
262 if (pos < 0 || pos > (MAXLEN - 2)) pos = MAXLEN - 2;
263 buffer[pos] = 0;
264 if (!replay_file) {
265 log_replay(MAIN_LOG, true, buffer);
266 buffer[pos++] = '\n';
267 buffer[pos] = 0;
268 ioset_write(socket_io_fd, buffer, pos);
269 } else {
270 replay_write(buffer);
271 }
272}
273
274void
275close_socket(void)
276{
277 if (replay_file) {
278 replay_connected = 0;
279 socket_destroyed(socket_io_fd);
280 } else {
281 ioset_close(socket_io_fd->fd, 1);
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{
336 if (argv[1]) log_module(MAIN_LOG, LOG_ERROR, "Error: %s", argv[1]);
337 log_module(MAIN_LOG, LOG_ERROR, "Error received from uplink, squitting.");
338
339 if (cManager.uplink->state != CONNECTED) {
340 /* Error messages while connected should be fine. */
341 cManager.uplink->flags |= UPLINK_UNAVAILABLE;
342 log_module(MAIN_LOG, LOG_ERROR, "Disabling uplink.");
343 }
344
345 close_socket();
346 return 1;
347}
348
349static CMD_FUNC(cmd_stats)
350{
351 struct userNode *un;
352
353 if (argc < 2)
354 return 0;
355 if (!(un = GetUserH(origin)))
356 return 0;
357 switch (argv[1][0]) {
358 case 'u': {
359 unsigned int uptime = now - boot_time;
360 irc_numeric(un, RPL_STATSUPTIME, ":Server Up %d days %d:%02d:%02d",
361 uptime/(24*60*60), (uptime/(60*60))%24, (uptime/60)%60, uptime%60);
362 irc_numeric(un, RPL_MAXCONNECTIONS, ":Highest connection count: %d (%d clients)",
363 self->max_clients+1, self->max_clients);
364 break;
365 }
366 default: /* unrecognized/unhandled stats output */ break;
367 }
368 irc_numeric(un, 219, "%s :End of /STATS report", argv[1]);
369 return 1;
370}
371
372static CMD_FUNC(cmd_version)
373{
374 struct userNode *user;
375 if (!(user = GetUserH(origin))) {
376 log_module(MAIN_LOG, LOG_ERROR, "Could not find VERSION origin user %s", origin);
377 return 0;
378 }
ceafd592 379 irc_numeric(user, 351, "%s %s %s", PACKAGE_TARNAME, PACKAGE_VERSION, self->name);
d76ed9a9
AS
380 return 1;
381}
382
383static CMD_FUNC(cmd_admin)
384{
385 struct userNode *user;
386 struct string_list *slist;
387
388 if (!(user = GetUserH(origin))) {
389 log_module(MAIN_LOG, LOG_ERROR, "Could not find ADMIN origin user %s", origin);
390 return 0;
391 }
392 if ((slist = conf_get_data("server/admin", RECDB_STRING_LIST)) && slist->used) {
393 unsigned int i;
394
395 irc_numeric(user, 256, ":Administrative info about %s", self->name);
396 for (i = 0; i < slist->used && i < 3; i++)
397 irc_numeric(user, 257 + i, ":%s", slist->list[i]);
398 } else {
399 irc_numeric(user, 423, ":No administrative info available");
400 }
401 return 1;
402}
403
404static void
405recalc_bursts(struct server *eob_server)
406{
407 unsigned int nn;
408 eob_server->burst = eob_server->self_burst;
409 if (eob_server->uplink != self)
410 eob_server->burst = eob_server->burst || eob_server->uplink->burst;
411 for (nn=0; nn < eob_server->children.used; nn++)
412 recalc_bursts(eob_server->children.list[nn]);
413}
414
415static struct chanmsg_func {
416 chanmsg_func_t func;
417 struct userNode *service;
418} chanmsg_funcs[256]; /* indexed by trigger character */
419
420static struct allchanmsg_func {
421 chanmsg_func_t func;
422 struct userNode *service;
423} allchanmsg_funcs[ALLCHANMSG_FUNCS_MAX];
424
425struct privmsg_desc {
426 struct userNode *user;
427 char *text;
428 unsigned int is_notice : 1;
429 unsigned int is_qualified : 1;
430};
431
432static void
433privmsg_chan_helper(struct chanNode *cn, void *data)
434{
435 struct privmsg_desc *pd = data;
436 struct modeNode *mn;
437 struct chanmsg_func *cf = &chanmsg_funcs[(unsigned char)pd->text[0]];
438 int x;
439
440 /* Don't complain if it can't find the modeNode because the channel might
441 * be -n */
442 if ((mn = GetUserMode(cn, pd->user)))
443 mn->idle_since = now;
444
445 /* Never send a NOTICE to a channel to one of the services */
0d16e639 446 if (!pd->is_notice && cf->func
447 && ((cn->modes & MODE_REGISTERED) || GetUserMode(cn, cf->service)))
ec1a68c8 448 cf->func(pd->user, cn, pd->text+1, cf->service);
63c95a47 449 else
450 spamserv_channel_message(cn, pd->user, pd->text);
d76ed9a9
AS
451
452 /* This catches *all* text sent to the channel that the services server sees */
453 for (x = 0; x < ALLCHANMSG_FUNCS_MAX; x++) {
454 cf = (struct chanmsg_func *)&allchanmsg_funcs[x];
455 if (!cf->func)
456 break; /* end of list */
457 else
0d16e639 458 cf->func(pd->user, cn, pd->text, cf->service);
d76ed9a9
AS
459 }
460}
461
462static void
463privmsg_invalid(char *name, void *data)
464{
465 struct privmsg_desc *pd = data;
466
467 if (*name == '$')
468 return;
469 irc_numeric(pd->user, ERR_NOSUCHNICK, "%s@%s :No such nick", name, self->name);
470}
471
472static void
473part_helper(struct chanNode *cn, void *data)
474{
475 DelChannelUser(data, cn, false, 0);
476}
477
478void
479reg_chanmsg_func(unsigned char prefix, struct userNode *service, chanmsg_func_t handler)
480{
481 if (chanmsg_funcs[prefix].func)
482 log_module(MAIN_LOG, LOG_WARNING, "Re-registering new chanmsg handler for character `%c'.", prefix);
483 chanmsg_funcs[prefix].func = handler;
484 chanmsg_funcs[prefix].service = service;
485}
486
487void
488reg_allchanmsg_func(struct userNode *service, chanmsg_func_t handler)
489{
490 int x;
491 for (x = 0; x < ALLCHANMSG_FUNCS_MAX; x++) {
492 if (allchanmsg_funcs[x].func)
493 continue;
494 allchanmsg_funcs[x].func = handler;
495 allchanmsg_funcs[x].service = service;
496 break;
497 }
498}
499
500struct userNode *
501get_chanmsg_bot(unsigned char prefix)
502{
503 return chanmsg_funcs[prefix].service;
504}
505
506static mode_change_func_t *mcf_list;
507static unsigned int mcf_size = 0, mcf_used = 0;
508
509void
510reg_mode_change_func(mode_change_func_t handler)
511{
512 if (mcf_used == mcf_size) {
513 if (mcf_size) {
31f23f13
AS
514 mcf_size <<= 1;
515 mcf_list = realloc(mcf_list, mcf_size*sizeof(mode_change_func_t));
d76ed9a9 516 } else {
31f23f13
AS
517 mcf_size = 8;
518 mcf_list = malloc(mcf_size*sizeof(mode_change_func_t));
d76ed9a9
AS
519 }
520 }
521 mcf_list[mcf_used++] = handler;
522}
523
524struct mod_chanmode *
525mod_chanmode_alloc(unsigned int argc)
526{
527 struct mod_chanmode *res;
528 if (argc > 1)
529 res = calloc(1, sizeof(*res) + (argc-1)*sizeof(res->args[0]));
530 else
531 res = calloc(1, sizeof(*res));
532 if (res) {
533#if !defined(NDEBUG)
534 res->alloc_argc = argc;
535#endif
536 res->argc = argc;
537 }
538 return res;
539}
540
541struct mod_chanmode *
542mod_chanmode_dup(struct mod_chanmode *orig, unsigned int extra)
543{
544 struct mod_chanmode *res;
545 res = mod_chanmode_alloc(orig->argc + extra);
546 if (res) {
547 res->modes_set = orig->modes_set;
548 res->modes_clear = orig->modes_clear;
549 res->new_limit = orig->new_limit;
550 memcpy(res->new_key, orig->new_key, sizeof(res->new_key));
2f61d1d7 551 memcpy(res->new_upass, orig->new_upass, sizeof(res->new_upass));
552 memcpy(res->new_apass, orig->new_apass, sizeof(res->new_apass));
d76ed9a9
AS
553 res->argc = orig->argc;
554 memcpy(res->args, orig->args, orig->argc*sizeof(orig->args[0]));
555 }
556 return res;
557}
558
559void
560mod_chanmode_apply(struct userNode *who, struct chanNode *channel, struct mod_chanmode *change)
561{
562 struct banNode *bn;
2aef5f4b 563 struct exemptNode *en;
d76ed9a9
AS
564 unsigned int ii, jj;
565
566 assert(change->argc <= change->alloc_argc);
567 channel->modes = (channel->modes & ~change->modes_clear) | change->modes_set;
568 if (change->modes_set & MODE_LIMIT)
569 channel->limit = change->new_limit;
570 if (change->modes_set & MODE_KEY)
571 strcpy(channel->key, change->new_key);
2f61d1d7 572 if (change->modes_set & MODE_UPASS)
573 strcpy(channel->upass, change->new_upass);
574 if (change->modes_set & MODE_APASS)
575 strcpy(channel->apass, change->new_apass);
d76ed9a9
AS
576 for (ii = 0; ii < change->argc; ++ii) {
577 switch (change->args[ii].mode) {
578 case MODE_BAN:
579 /* If any existing ban is a subset of the new ban,
580 * silently remove it. The new ban is not allowed
581 * to be more specific than an existing ban.
582 */
583 for (jj=0; jj<channel->banlist.used; ++jj) {
0d16e639 584 bn = channel->banlist.list[jj];
585 if (match_ircglobs(change->args[ii].u.hostmask, bn->ban)) {
586 banList_remove(&channel->banlist, bn);
587 free(bn);
d76ed9a9
AS
588 jj--;
589 }
590 }
591 bn = calloc(1, sizeof(*bn));
a32da4c7 592 safestrncpy(bn->ban, change->args[ii].u.hostmask, sizeof(bn->ban));
d76ed9a9
AS
593 if (who)
594 safestrncpy(bn->who, who->nick, sizeof(bn->who));
595 else
596 safestrncpy(bn->who, "<unknown>", sizeof(bn->who));
597 bn->set = now;
598 banList_append(&channel->banlist, bn);
599 break;
600 case MODE_REMOVE|MODE_BAN:
601 for (jj=0; jj<channel->banlist.used; ++jj) {
0d16e639 602 bn = channel->banlist.list[jj];
603 if (strcmp(bn->ban, change->args[ii].u.hostmask))
d76ed9a9 604 continue;
0d16e639 605 free(bn);
606 banList_remove(&channel->banlist, bn);
d76ed9a9
AS
607 break;
608 }
609 break;
2aef5f4b 610 case MODE_EXEMPT:
611 /* If any existing exempt is a subset of the new exempt,
612 * silently remove it. The new exempt is not allowed
613 * to be more specific than an existing exempt.
614 */
615 for (jj=0; jj<channel->exemptlist.used; ++jj) {
a32da4c7 616 if (match_ircglobs(change->args[ii].u.hostmask, channel->exemptlist.list[jj]->exempt)) {
2aef5f4b 617 exemptList_remove(&channel->exemptlist, channel->exemptlist.list[jj]);
618 free(channel->exemptlist.list[jj]);
619 jj--;
620 }
621 }
622 en = calloc(1, sizeof(*en));
a32da4c7 623 safestrncpy(en->exempt, change->args[ii].u.hostmask, sizeof(en->exempt));
2aef5f4b 624 if (who)
625 safestrncpy(en->who, who->nick, sizeof(en->who));
626 else
627 safestrncpy(en->who, "<unknown>", sizeof(en->who));
628 en->set = now;
629 exemptList_append(&channel->exemptlist, en);
630 break;
631 case MODE_REMOVE|MODE_EXEMPT:
632 for (jj=0; jj<channel->exemptlist.used; ++jj) {
a32da4c7 633 if (strcmp(channel->exemptlist.list[jj]->exempt, change->args[ii].u.hostmask))
2aef5f4b 634 continue;
635 free(channel->exemptlist.list[jj]);
636 exemptList_remove(&channel->exemptlist, channel->exemptlist.list[jj]);
637 break;
638 }
639 break;
4bffb7bd
AS
640 /* XXX Hack: this is the stupedest use of switch iv ever seen.
641 * you have to compare for EVERY POSSIBLE COMBINATION of bitmask
642 * because switch does only full comparison. This needs redone as if/else.
643 **/
d76ed9a9 644 case MODE_CHANOP:
55342ce8 645 case MODE_HALFOP:
d76ed9a9 646 case MODE_VOICE:
4bffb7bd
AS
647 case MODE_VOICE|MODE_CHANOP:
648 case MODE_VOICE|MODE_HALFOP:
649 case MODE_CHANOP|MODE_HALFOP:
55342ce8 650 case MODE_VOICE|MODE_CHANOP|MODE_HALFOP:
d76ed9a9 651 case MODE_REMOVE|MODE_CHANOP:
55342ce8 652 case MODE_REMOVE|MODE_HALFOP:
d76ed9a9 653 case MODE_REMOVE|MODE_VOICE:
4bffb7bd
AS
654 case MODE_REMOVE|MODE_VOICE|MODE_CHANOP:
655 case MODE_REMOVE|MODE_VOICE|MODE_HALFOP:
656 case MODE_REMOVE|MODE_CHANOP|MODE_HALFOP:
55342ce8 657 case MODE_REMOVE|MODE_VOICE|MODE_CHANOP|MODE_HALFOP:
d76ed9a9 658 if (change->args[ii].mode & MODE_REMOVE)
a32da4c7 659 change->args[ii].u.member->modes &= ~change->args[ii].mode;
d76ed9a9 660 else
a32da4c7 661 change->args[ii].u.member->modes |= change->args[ii].mode;
d76ed9a9
AS
662 break;
663 default:
664 assert(0 && "Invalid mode argument");
665 continue;
666 }
667 }
668}
669
670void
671mod_chanmode_free(struct mod_chanmode *change)
672{
673 free(change);
674}
675
676int
677mod_chanmode(struct userNode *who, struct chanNode *channel, char **modes, unsigned int argc, unsigned int flags)
678{
2f61d1d7 679 struct modeNode *member;
d76ed9a9
AS
680 struct mod_chanmode *change;
681 unsigned int ii;
2f61d1d7 682 short base_oplevel;
683
d76ed9a9
AS
684
685 if (!modes || !modes[0])
686 return 0;
2f61d1d7 687 if (who && (member = GetUserMode(channel, who)))
688 base_oplevel = member->oplevel;
689 else
690 base_oplevel = MAXOPLEVEL;
691 if (!(change = mod_chanmode_parse(channel, modes, argc, flags, base_oplevel)))
d76ed9a9 692 return 0;
ec311f39 693
694 call_channel_mode_funcs(who, channel, modes, argc);
695
d76ed9a9
AS
696 if (flags & MC_ANNOUNCE)
697 mod_chanmode_announce(who, channel, change);
698 else
699 mod_chanmode_apply(who, channel, change);
700 if (flags & MC_NOTIFY)
701 for (ii = 0; ii < mcf_used; ++ii)
702 mcf_list[ii](channel, who, change);
703 mod_chanmode_free(change);
704 return 1;
705}
706
707int
a32da4c7 708irc_make_chanmode(struct chanNode *chan, char *out)
709{
d76ed9a9
AS
710 struct mod_chanmode change;
711 mod_chanmode_init(&change);
712 change.modes_set = chan->modes;
713 change.new_limit = chan->limit;
714 safestrncpy(change.new_key, chan->key, sizeof(change.new_key));
2f61d1d7 715 safestrncpy(change.new_upass, chan->upass, sizeof(change.new_upass));
716 safestrncpy(change.new_apass, chan->apass, sizeof(change.new_apass));
d76ed9a9
AS
717 return strlen(mod_chanmode_format(&change, out));
718}
719
ec311f39 720static user_mode_func_t *um_list;
721static unsigned int um_size = 0, um_used = 0;
722
723void
724reg_user_mode_func(user_mode_func_t handler)
725{
726 if (um_used == um_size) {
727 if (um_size) {
728 um_size <<= 1;
729 um_list = realloc(um_list, um_size*sizeof(user_mode_func_t));
730 } else {
731 um_size = 8;
732 um_list = malloc(um_size*sizeof(user_mode_func_t));
733 }
734 }
735 um_list[um_used++] = handler;
736}
737
738void
739unreg_user_mode_func(user_mode_func_t handler)
740{
741 unsigned int i;
742 for (i=0; i<um_used; i++) {
743 if (um_list[i] == handler) break;
744 }
745 if (i == um_used) return;
746 memmove(um_list+i, um_list+i+1, (um_used-i-1)*sizeof(um_list[0]));
747 um_used--;
748}
749
750static void
751call_user_mode_funcs(struct userNode *user, const char *mode_change)
752{
753 unsigned int n;
754 for (n=0; n<um_used; n++) {
755 um_list[n](user, mode_change);
756 }
757}
758
759static channel_mode_func_t *cm_list;
760static unsigned int cm_size = 0, cm_used = 0;
761
762void
763reg_channel_mode_func(channel_mode_func_t handler)
764{
765 if (cm_used == cm_size) {
766 if (cm_size) {
767 cm_size <<= 1;
768 cm_list = realloc(cm_list, cm_size*sizeof(channel_mode_func_t));
769 } else {
770 cm_size = 8;
771 cm_list = malloc(cm_size*sizeof(channel_mode_func_t));
772 }
773 }
774 cm_list[cm_used++] = handler;
775}
776
777void
778unreg_channel_mode_func(channel_mode_func_t handler)
779{
780 unsigned int i;
781 for (i=0; i<cm_used; i++) {
782 if(cm_list[i] == handler) break;
783 }
784 if (i == cm_used) return;
785 memmove(cm_list+i, cm_list+i+1, (cm_used-i-1)*sizeof(cm_list[0]));
786 cm_used--;
787}
788
789static void
790call_channel_mode_funcs(struct userNode *who, struct chanNode *channel, char **modes, unsigned int argc)
791{
792 unsigned int n;
793 for (n=0; n<cm_used; n++) {
794 cm_list[n](who, channel, modes, argc);
795 }
796}
797
d76ed9a9
AS
798char *
799generate_hostmask(struct userNode *user, int options)
800{
2f61d1d7 801 irc_in_addr_t ip;
802 char *nickname, *ident, *hostname, *mask;
d76ed9a9
AS
803 int len, ii;
804
805 /* figure out string parts */
806 if (options & GENMASK_OMITNICK)
807 nickname = NULL;
808 else if (options & GENMASK_USENICK)
809 nickname = user->nick;
810 else
811 nickname = "*";
812 if (options & GENMASK_STRICT_IDENT)
182dd032
AS
813 // sethost - reed/apples
814 if (IsSetHost(user)) {
815 ident = alloca(strcspn(user->sethost, "@")+2);
816 safestrncpy(ident, user->sethost, strcspn(user->sethost, "@")+1);
817 }
818 else
d76ed9a9
AS
819 ident = user->ident;
820 else if (options & GENMASK_ANY_IDENT)
821 ident = "*";
822 else {
182dd032
AS
823 // sethost - reed/apples
824 if (IsSetHost(user)) {
825 ident = alloca(strcspn(user->sethost, "@")+3);
826 ident[0] = '*';
827 safestrncpy(ident+1, user->sethost, strcspn(user->sethost, "@")+1);
828 } else {
d76ed9a9
AS
829 ident = alloca(strlen(user->ident)+2);
830 ident[0] = '*';
831 strcpy(ident+1, user->ident + ((*user->ident == '~')?1:0));
832 }
182dd032 833 }
d76ed9a9
AS
834 hostname = user->hostname;
835 if (IsFakeHost(user) && IsHiddenHost(user) && !(options & GENMASK_NO_HIDING)) {
836 hostname = user->fakehost;
f16ad9e7 837 } else if (IsHiddenHost(user)) {
838 int style = 1;
839 char *data;
840 data = conf_get_data("server/hidden_host_type", RECDB_QSTRING);
841 if (data)
842 style = atoi(data);
843
844 if ((style == 1) && user->handle_info && hidden_host_suffix && !(options & GENMASK_NO_HIDING)) {
845 hostname = alloca(strlen(user->handle_info->handle) + strlen(hidden_host_suffix) + 2);
846 sprintf(hostname, "%s.%s", user->handle_info->handle, hidden_host_suffix);
847 } else if ((style == 2) && !(options & GENMASK_NO_HIDING)) {
848 hostname = alloca(strlen(user->crypthost));
849 sprintf(hostname, "%s", user->crypthost);
850 }
d76ed9a9
AS
851 } else if (options & GENMASK_STRICT_HOST) {
852 if (options & GENMASK_BYIP)
2f61d1d7 853 hostname = (char*)irc_ntoa(&user->ip);
854 } else if ((options & GENMASK_BYIP) || irc_pton(&ip, NULL, hostname)) {
855 /* Should generate an IP-based hostmask. */
856 hostname = alloca(IRC_NTOP_MAX_SIZE);
857 hostname[IRC_NTOP_MAX_SIZE-1] = '\0';
858 if (irc_in_addr_is_ipv4(user->ip)) {
859 /* By popular acclaim, a /16 hostmask is used. */
860 sprintf(hostname, "%d.%d.*", user->ip.in6_8[12], user->ip.in6_8[13]);
861 } else if (irc_in_addr_is_ipv6(user->ip)) {
862 /* Who knows what the default mask should be? Use a /48 to start with. */
863 sprintf(hostname, "%x:%x:%x:*", user->ip.in6[0], user->ip.in6[1], user->ip.in6[2]);
d76ed9a9 864 } else {
2f61d1d7 865 /* Unknown type; just copy IP directly. */
866 irc_ntop(hostname, IRC_NTOP_MAX_SIZE, &user->ip);
d76ed9a9
AS
867 }
868 } else {
869 int cnt;
870 /* This heuristic could be made smarter. Is it worth the effort? */
871 for (ii=cnt=0; hostname[ii]; ii++)
872 if (hostname[ii] == '.')
873 cnt++;
874 if (cnt == 1) {
875 /* only a two-level domain name; leave hostname */
876 } else if (cnt == 2) {
877 for (ii=0; user->hostname[ii] != '.'; ii++) ;
878 /* Add 3 to account for the *. and \0. */
879 hostname = alloca(strlen(user->hostname+ii)+3);
880 sprintf(hostname, "*.%s", user->hostname+ii+1);
881 } else {
882 for (cnt=3, ii--; cnt; ii--)
883 if (user->hostname[ii] == '.')
884 cnt--;
885 /* The loop above will overshoot the dot one character;
886 we skip forward two (the one character and the dot)
887 when printing, so we only add one for the \0. */
888 hostname = alloca(strlen(user->hostname+ii)+1);
889 sprintf(hostname, "*.%s", user->hostname+ii+2);
890 }
891 }
182dd032
AS
892 // sethost - reed/apples
893 if (IsSetHost(user))
894 hostname = strchr(user->sethost, '@') + 1;
895
d76ed9a9
AS
896 /* Emit hostmask */
897 len = strlen(ident) + strlen(hostname) + 2;
898 if (nickname) {
899 len += strlen(nickname) + 1;
900 mask = malloc(len);
901 sprintf(mask, "%s!%s@%s", nickname, ident, hostname);
902 } else {
903 mask = malloc(len);
904 sprintf(mask, "%s@%s", ident, hostname);
905 }
906 return mask;
907}
908
909int
910IsChannelName(const char *name) {
911 unsigned int ii;
912
913 if (*name !='#')
914 return 0;
915 for (ii=1; name[ii]; ++ii) {
916 if ((name[ii] > 0) && (name[ii] <= 32))
917 return 0;
918 if (name[ii] == ',')
919 return 0;
920 if (name[ii] == '\xa0')
921 return 0;
922 }
923 return 1;
924}