]> jfr.im git - irc/evilnet/x3.git/blob - src/proto-common.c
srvx sync: When MAIN_LOG is NULL, report to stderr
[irc/evilnet/x3.git] / src / proto-common.c
1 /* proto-common.c - common IRC protocol parsing/sending support
2 * Copyright 2000-2004 srvx Development Team
3 *
4 * This file is part of x3.
5 *
6 * x3 is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation; either version 3 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"
26 #include "spamserv.h"
27 #include "shun.h"
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
39 unsigned int lines_processed;
40 FILE *replay_file;
41 struct io_fd *socket_io_fd;
42 int force_n2k;
43 const char *hidden_host_suffix;
44
45 static char replay_line[MAXLEN+80];
46 static int ping_freq;
47 static int ping_timeout;
48 static int replay_connected;
49 static unsigned int nicklen = NICKLEN; /* how long do we think servers allow nicks to be? */
50 static struct userList dead_users;
51
52 extern struct cManagerNode cManager;
53 extern unsigned long burst_length;
54 extern struct cManagerNode cManager;
55 extern struct policer_params *oper_policer_params, *luser_policer_params;
56 extern server_link_func_t *slf_list;
57 extern unsigned int slf_size, slf_used;
58 extern new_user_func_t *nuf_list;
59 extern unsigned int nuf_size, nuf_used;
60 extern del_user_func_t *duf_list;
61 extern unsigned int duf_size, duf_used;
62 extern time_t boot_time;
63
64 void received_ping(void);
65
66 static int replay_read(void);
67 static dict_t irc_func_dict;
68
69 typedef void (*foreach_chanfunc) (struct chanNode *chan, void *data);
70 typedef void (*foreach_nonchan) (char *name, void *data);
71 typedef void (*foreach_userfunc) (struct userNode *user, void *data);
72 typedef void (*foreach_nonuser) (char *name, void *data);
73 static void parse_foreach(char *target_list, foreach_chanfunc cf, foreach_nonchan nc, foreach_userfunc uf, foreach_nonuser nu, void *data);
74 static void call_channel_mode_funcs(struct userNode *who, struct chanNode *channel, char **modes, unsigned int argc);
75
76 static void
77 uplink_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
95 void
96 socket_destroyed(struct io_fd *fd)
97 {
98 if (fd && fd->state != IO_CONNECTED)
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
106 void 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_time = 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
123 int
124 create_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
140 socket_io_fd = ioset_connect(cManager.uplink->bind_addr, cManager.uplink->bind_addr_len, addr, port, 1, 0, NULL);
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 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
157 void
158 replay_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
202 static int
203 replay_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
230 static void
231 replay_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
247 void putsock(const char *text, ...) PRINTF_LIKE(1, 2);
248
249 void
250 putsock(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
273 void
274 close_socket(void)
275 {
276 if (replay_file) {
277 replay_connected = 0;
278 socket_destroyed(socket_io_fd);
279 } else {
280 ioset_close(socket_io_fd, 3);
281 socket_io_fd = NULL;
282 }
283 }
284
285 #define CMD_FUNC(NAME) int NAME(UNUSED_ARG(const char *origin), UNUSED_ARG(unsigned int argc), UNUSED_ARG(char **argv))
286 typedef CMD_FUNC(cmd_func_t);
287
288 static 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). */
292 void
293 timed_send_ping(UNUSED_ARG(void *data))
294 {
295 irc_ping(self->name);
296 timeq_add(now + ping_timeout, timed_ping_timeout, 0);
297 }
298
299 static void
300 timed_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
309 static 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
328 static CMD_FUNC(cmd_dummy)
329 {
330 /* we don't care about these messages */
331 return 1;
332 }
333
334 static 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
349 static 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
372 static 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 }
379 irc_numeric(user, 351, "%s %s %s", PACKAGE_TARNAME, PACKAGE_VERSION, self->name);
380 return 1;
381 }
382
383 static 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
404 static void
405 recalc_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
415 static struct chanmsg_func {
416 chanmsg_func_t func;
417 struct userNode *service;
418 } chanmsg_funcs[256]; /* indexed by trigger character */
419
420 static struct allchanmsg_func {
421 chanmsg_func_t func;
422 struct userNode *service;
423 } allchanmsg_funcs[ALLCHANMSG_FUNCS_MAX];
424
425 struct privmsg_desc {
426 struct userNode *user;
427 char *text;
428 unsigned int is_notice : 1;
429 unsigned int is_qualified : 1;
430 };
431
432 static void
433 privmsg_chan_helper(struct chanNode *cn, void *data)
434 {
435 struct privmsg_desc *pd = data;
436 struct modeNode *mn;
437 struct chanmsg_func *cf;
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 */
446 cf = &chanmsg_funcs[(unsigned char)pd->text[0]];
447 if (!pd->is_notice && cf->func)
448 cf->func(pd->user, cn, pd->text+1, cf->service, pd->is_notice);
449 else
450 spamserv_channel_message(cn, pd->user, pd->text);
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
458 cf->func(pd->user, cn, pd->text, cf->service, pd->is_notice);
459 }
460 }
461
462 static void
463 privmsg_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
472 struct part_desc {
473 struct userNode *user;
474 const char *text;
475 };
476
477 static void
478 part_helper(struct chanNode *cn, void *data)
479 {
480 struct part_desc *desc = data;
481 DelChannelUser(desc->user, cn, desc->text, false);
482 }
483
484 static CMD_FUNC(cmd_part)
485 {
486 struct part_desc desc;
487
488 if (argc < 2)
489 return 0;
490 desc.user = GetUserH(origin);
491 if (!desc.user)
492 return 0;
493 desc.text = (argc > 2) ? argv[argc - 1] : NULL;
494 parse_foreach(argv[1], part_helper, NULL, NULL, NULL, &desc);
495 return 1;
496 }
497
498 void
499 reg_chanmsg_func(unsigned char prefix, struct userNode *service, chanmsg_func_t handler)
500 {
501 if (chanmsg_funcs[prefix].func)
502 log_module(MAIN_LOG, LOG_WARNING, "Re-registering new chanmsg handler for character `%c'.", prefix);
503 chanmsg_funcs[prefix].func = handler;
504 chanmsg_funcs[prefix].service = service;
505 }
506
507 void
508 reg_allchanmsg_func(struct userNode *service, chanmsg_func_t handler)
509 {
510 int x;
511 for (x = 0; x < ALLCHANMSG_FUNCS_MAX; x++) {
512 if (allchanmsg_funcs[x].func)
513 continue;
514 allchanmsg_funcs[x].func = handler;
515 allchanmsg_funcs[x].service = service;
516 break;
517 }
518 }
519
520 struct userNode *
521 get_chanmsg_bot(unsigned char prefix)
522 {
523 return chanmsg_funcs[prefix].service;
524 }
525
526 static mode_change_func_t *mcf_list;
527 static unsigned int mcf_size = 0, mcf_used = 0;
528
529 void
530 reg_mode_change_func(mode_change_func_t handler)
531 {
532 if (mcf_used == mcf_size) {
533 if (mcf_size) {
534 mcf_size <<= 1;
535 mcf_list = realloc(mcf_list, mcf_size*sizeof(mode_change_func_t));
536 } else {
537 mcf_size = 8;
538 mcf_list = malloc(mcf_size*sizeof(mode_change_func_t));
539 }
540 }
541 mcf_list[mcf_used++] = handler;
542 }
543
544 static oper_func_t *of_list;
545
546 static unsigned int of_size = 0, of_used = 0;
547
548 void
549 reg_oper_func(oper_func_t handler)
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));
555 } else {
556 of_size = 8;
557 of_list = malloc(of_size*sizeof(oper_func_t));
558 }
559 }
560 of_list[of_used++] = handler;
561 }
562
563 static void
564 call_oper_funcs(struct userNode *user)
565 {
566 unsigned int n;
567 if (IsLocal(user))
568 return;
569 for (n=0; (n<of_used) && !user->dead; n++)
570 {
571 of_list[n](user);
572 }
573 }
574
575 struct mod_chanmode *
576 mod_chanmode_alloc(unsigned int argc)
577 {
578 struct mod_chanmode *res;
579 if (argc > 1)
580 res = calloc(1, sizeof(*res) + (argc-1)*sizeof(res->args[0]));
581 else
582 res = calloc(1, sizeof(*res));
583 if (res) {
584 #if !defined(NDEBUG)
585 res->alloc_argc = argc;
586 #endif
587 res->argc = argc;
588 }
589 return res;
590 }
591
592 struct mod_chanmode *
593 mod_chanmode_dup(struct mod_chanmode *orig, unsigned int extra)
594 {
595 struct mod_chanmode *res;
596 res = mod_chanmode_alloc(orig->argc + extra);
597 if (res) {
598 res->modes_set = orig->modes_set;
599 res->modes_clear = orig->modes_clear;
600 res->new_limit = orig->new_limit;
601 memcpy(res->new_key, orig->new_key, sizeof(res->new_key));
602 memcpy(res->new_upass, orig->new_upass, sizeof(res->new_upass));
603 memcpy(res->new_apass, orig->new_apass, sizeof(res->new_apass));
604 res->argc = orig->argc;
605 memcpy(res->args, orig->args, orig->argc*sizeof(orig->args[0]));
606 }
607 return res;
608 }
609
610 void
611 mod_chanmode_apply(struct userNode *who, struct chanNode *channel, struct mod_chanmode *change)
612 {
613 struct banNode *bn;
614 struct exemptNode *en;
615 unsigned int ii, jj;
616
617 assert(change->argc <= change->alloc_argc);
618 channel->modes = (channel->modes & ~change->modes_clear) | change->modes_set;
619 if (change->modes_set & MODE_LIMIT)
620 channel->limit = change->new_limit;
621 if (change->modes_set & MODE_KEY)
622 strcpy(channel->key, change->new_key);
623 if (change->modes_set & MODE_UPASS)
624 strcpy(channel->upass, change->new_upass);
625 if (change->modes_set & MODE_APASS)
626 strcpy(channel->apass, change->new_apass);
627 for (ii = 0; ii < change->argc; ++ii) {
628 switch (change->args[ii].mode) {
629 case MODE_BAN:
630 /* If any existing ban is a subset of the new ban,
631 * silently remove it. The new ban is not allowed
632 * to be more specific than an existing ban.
633 */
634 for (jj=0; jj<channel->banlist.used; ++jj) {
635 bn = channel->banlist.list[jj];
636 if (match_ircglobs(change->args[ii].u.hostmask, bn->ban)) {
637 banList_remove(&channel->banlist, bn);
638 free(bn);
639 jj--;
640 }
641 }
642 bn = calloc(1, sizeof(*bn));
643 safestrncpy(bn->ban, change->args[ii].u.hostmask, sizeof(bn->ban));
644 if (who)
645 safestrncpy(bn->who, who->nick, sizeof(bn->who));
646 else
647 safestrncpy(bn->who, "<unknown>", sizeof(bn->who));
648 bn->set = now;
649 banList_append(&channel->banlist, bn);
650 break;
651 case MODE_REMOVE|MODE_BAN:
652 for (jj=0; jj<channel->banlist.used; ++jj) {
653 bn = channel->banlist.list[jj];
654 if (strcmp(bn->ban, change->args[ii].u.hostmask))
655 continue;
656 free(bn);
657 banList_remove(&channel->banlist, bn);
658 break;
659 }
660 break;
661 case MODE_EXEMPT:
662 /* If any existing exempt is a subset of the new exempt,
663 * silently remove it. The new exempt is not allowed
664 * to be more specific than an existing exempt.
665 */
666 for (jj=0; jj<channel->exemptlist.used; ++jj) {
667 if (match_ircglobs(change->args[ii].u.hostmask, channel->exemptlist.list[jj]->exempt)) {
668 exemptList_remove(&channel->exemptlist, channel->exemptlist.list[jj]);
669 free(channel->exemptlist.list[jj]);
670 jj--;
671 }
672 }
673 en = calloc(1, sizeof(*en));
674 safestrncpy(en->exempt, change->args[ii].u.hostmask, sizeof(en->exempt));
675 if (who)
676 safestrncpy(en->who, who->nick, sizeof(en->who));
677 else
678 safestrncpy(en->who, "<unknown>", sizeof(en->who));
679 en->set = now;
680 exemptList_append(&channel->exemptlist, en);
681 break;
682 case MODE_REMOVE|MODE_EXEMPT:
683 for (jj=0; jj<channel->exemptlist.used; ++jj) {
684 if (strcmp(channel->exemptlist.list[jj]->exempt, change->args[ii].u.hostmask))
685 continue;
686 free(channel->exemptlist.list[jj]);
687 exemptList_remove(&channel->exemptlist, channel->exemptlist.list[jj]);
688 break;
689 }
690 break;
691 /* XXX Hack: this is the stupedest use of switch iv ever seen.
692 * you have to compare for EVERY POSSIBLE COMBINATION of bitmask
693 * because switch does only full comparison. This needs redone as if/else.
694 **/
695 case MODE_CHANOP:
696 case MODE_HALFOP:
697 case MODE_VOICE:
698 case MODE_VOICE|MODE_CHANOP:
699 case MODE_VOICE|MODE_HALFOP:
700 case MODE_CHANOP|MODE_HALFOP:
701 case MODE_VOICE|MODE_CHANOP|MODE_HALFOP:
702 case MODE_REMOVE|MODE_CHANOP:
703 case MODE_REMOVE|MODE_HALFOP:
704 case MODE_REMOVE|MODE_VOICE:
705 case MODE_REMOVE|MODE_VOICE|MODE_CHANOP:
706 case MODE_REMOVE|MODE_VOICE|MODE_HALFOP:
707 case MODE_REMOVE|MODE_CHANOP|MODE_HALFOP:
708 case MODE_REMOVE|MODE_VOICE|MODE_CHANOP|MODE_HALFOP:
709 if (change->args[ii].mode & MODE_REMOVE)
710 change->args[ii].u.member->modes &= ~change->args[ii].mode;
711 else
712 change->args[ii].u.member->modes |= change->args[ii].mode;
713 break;
714 default:
715 assert(0 && "Invalid mode argument");
716 continue;
717 }
718 }
719 }
720
721 void
722 mod_chanmode_free(struct mod_chanmode *change)
723 {
724 free(change);
725 }
726
727 int
728 mod_chanmode(struct userNode *who, struct chanNode *channel, char **modes, unsigned int argc, unsigned int flags)
729 {
730 struct modeNode *member;
731 struct mod_chanmode *change;
732 unsigned int ii;
733 short base_oplevel;
734
735
736 if (!modes || !modes[0])
737 return 0;
738 if (who && (member = GetUserMode(channel, who)))
739 base_oplevel = member->oplevel;
740 else
741 base_oplevel = MAXOPLEVEL;
742 if (!(change = mod_chanmode_parse(channel, modes, argc, flags, base_oplevel)))
743 return 0;
744
745 call_channel_mode_funcs(who, channel, modes, argc);
746
747 if (flags & MC_ANNOUNCE)
748 mod_chanmode_announce(who, channel, change);
749 else
750 mod_chanmode_apply(who, channel, change);
751 if (flags & MC_NOTIFY)
752 for (ii = 0; ii < mcf_used; ++ii)
753 mcf_list[ii](channel, who, change);
754 mod_chanmode_free(change);
755 return 1;
756 }
757
758 int
759 irc_make_chanmode(struct chanNode *chan, char *out)
760 {
761 struct mod_chanmode change;
762 mod_chanmode_init(&change);
763 change.modes_set = chan->modes;
764 change.new_limit = chan->limit;
765 safestrncpy(change.new_key, chan->key, sizeof(change.new_key));
766 safestrncpy(change.new_upass, chan->upass, sizeof(change.new_upass));
767 safestrncpy(change.new_apass, chan->apass, sizeof(change.new_apass));
768 return strlen(mod_chanmode_format(&change, out));
769 }
770
771 static user_mode_func_t *um_list;
772 static unsigned int um_size = 0, um_used = 0;
773
774 void
775 reg_user_mode_func(user_mode_func_t handler)
776 {
777 if (um_used == um_size) {
778 if (um_size) {
779 um_size <<= 1;
780 um_list = realloc(um_list, um_size*sizeof(user_mode_func_t));
781 } else {
782 um_size = 8;
783 um_list = malloc(um_size*sizeof(user_mode_func_t));
784 }
785 }
786 um_list[um_used++] = handler;
787 }
788
789 void
790 unreg_user_mode_func(user_mode_func_t handler)
791 {
792 unsigned int i;
793 for (i=0; i<um_used; i++) {
794 if (um_list[i] == handler) break;
795 }
796 if (i == um_used) return;
797 memmove(um_list+i, um_list+i+1, (um_used-i-1)*sizeof(um_list[0]));
798 um_used--;
799 }
800
801 static void
802 call_user_mode_funcs(struct userNode *user, const char *mode_change)
803 {
804 unsigned int n;
805 for (n=0; n<um_used; n++) {
806 um_list[n](user, mode_change);
807 }
808 }
809
810 static channel_mode_func_t *cm_list;
811 static unsigned int cm_size = 0, cm_used = 0;
812
813 void
814 reg_channel_mode_func(channel_mode_func_t handler)
815 {
816 if (cm_used == cm_size) {
817 if (cm_size) {
818 cm_size <<= 1;
819 cm_list = realloc(cm_list, cm_size*sizeof(channel_mode_func_t));
820 } else {
821 cm_size = 8;
822 cm_list = malloc(cm_size*sizeof(channel_mode_func_t));
823 }
824 }
825 cm_list[cm_used++] = handler;
826 }
827
828 void
829 unreg_channel_mode_func(channel_mode_func_t handler)
830 {
831 unsigned int i;
832 for (i=0; i<cm_used; i++) {
833 if(cm_list[i] == handler) break;
834 }
835 if (i == cm_used) return;
836 memmove(cm_list+i, cm_list+i+1, (cm_used-i-1)*sizeof(cm_list[0]));
837 cm_used--;
838 }
839
840 static void
841 call_channel_mode_funcs(struct userNode *who, struct chanNode *channel, char **modes, unsigned int argc)
842 {
843 unsigned int n;
844 for (n=0; n<cm_used; n++) {
845 cm_list[n](who, channel, modes, argc);
846 }
847 }
848
849 char *
850 generate_hostmask(struct userNode *user, int options)
851 {
852 irc_in_addr_t ip;
853 char *nickname, *ident, *hostname, *mask;
854 int len, ii;
855
856 /* figure out string parts */
857 if (options & GENMASK_OMITNICK)
858 nickname = NULL;
859 else if (options & GENMASK_USENICK)
860 nickname = user->nick;
861 else
862 nickname = "*";
863 if (options & GENMASK_STRICT_IDENT)
864 // sethost - reed/apples
865 if (IsSetHost(user)) {
866 ident = alloca(strcspn(user->sethost, "@")+2);
867 safestrncpy(ident, user->sethost, strcspn(user->sethost, "@")+1);
868 }
869 else
870 ident = user->ident;
871 else if (options & GENMASK_ANY_IDENT)
872 ident = "*";
873 else {
874 // sethost - reed/apples
875 if (IsSetHost(user)) {
876 ident = alloca(strcspn(user->sethost, "@")+3);
877 ident[0] = '*';
878 safestrncpy(ident+1, user->sethost, strcspn(user->sethost, "@")+1);
879 } else {
880 ident = alloca(strlen(user->ident)+2);
881 ident[0] = '*';
882 strcpy(ident+1, user->ident + ((*user->ident == '~')?1:0));
883 }
884 }
885 hostname = user->hostname;
886 if (IsFakeHost(user) && IsHiddenHost(user) && !(options & GENMASK_NO_HIDING)) {
887 hostname = user->fakehost;
888 } else if (IsHiddenHost(user)) {
889 int style = 1;
890 char *data;
891 data = conf_get_data("server/hidden_host_type", RECDB_QSTRING);
892 if (data)
893 style = atoi(data);
894
895 if ((style == 1) && user->handle_info && hidden_host_suffix && !(options & GENMASK_NO_HIDING)) {
896 hostname = alloca(strlen(user->handle_info->handle) + strlen(hidden_host_suffix) + 2);
897 sprintf(hostname, "%s.%s", user->handle_info->handle, hidden_host_suffix);
898 } else if ((style == 2) && !(options & GENMASK_NO_HIDING)) {
899 hostname = alloca(strlen(user->crypthost));
900 sprintf(hostname, "%s", user->crypthost);
901 }
902 } else if (options & GENMASK_STRICT_HOST) {
903 if (options & GENMASK_BYIP)
904 hostname = (char*)irc_ntoa(&user->ip);
905 } else if ((options & GENMASK_BYIP) || irc_pton(&ip, NULL, hostname)) {
906 /* Should generate an IP-based hostmask. */
907 hostname = alloca(IRC_NTOP_MAX_SIZE);
908 hostname[IRC_NTOP_MAX_SIZE-1] = '\0';
909 if (irc_in_addr_is_ipv4(user->ip)) {
910 /* By popular acclaim, a /16 hostmask is used. */
911 sprintf(hostname, "%d.%d.*", user->ip.in6_8[12], user->ip.in6_8[13]);
912 } else if (irc_in_addr_is_ipv6(user->ip)) {
913 /* Who knows what the default mask should be? Use a /48 to start with. */
914 sprintf(hostname, "%x:%x:%x:*", user->ip.in6[0], user->ip.in6[1], user->ip.in6[2]);
915 } else {
916 /* Unknown type; just copy IP directly. */
917 irc_ntop(hostname, IRC_NTOP_MAX_SIZE, &user->ip);
918 }
919 } else {
920 int cnt;
921 /* This heuristic could be made smarter. Is it worth the effort? */
922 for (ii=cnt=0; hostname[ii]; ii++)
923 if (hostname[ii] == '.')
924 cnt++;
925 if (cnt == 0 || cnt == 1) {
926 /* only a one- or two-level domain name; leave hostname */
927 } else if (cnt == 2) {
928 for (ii=0; user->hostname[ii] != '.'; ii++) ;
929 /* Add 3 to account for the *. and \0. */
930 hostname = alloca(strlen(user->hostname+ii)+3);
931 sprintf(hostname, "*.%s", user->hostname+ii+1);
932 } else {
933 for (cnt=3, ii--; cnt; ii--)
934 if (user->hostname[ii] == '.')
935 cnt--;
936 /* The loop above will overshoot the dot one character;
937 we skip forward two (the one character and the dot)
938 when printing, so we only add one for the \0. */
939 hostname = alloca(strlen(user->hostname+ii)+1);
940 sprintf(hostname, "*.%s", user->hostname+ii+2);
941 }
942 }
943 // sethost - reed/apples
944 if (IsSetHost(user))
945 hostname = strchr(user->sethost, '@') + 1;
946
947 /* Emit hostmask */
948 len = strlen(ident) + strlen(hostname) + 2;
949 if (nickname) {
950 len += strlen(nickname) + 1;
951 mask = malloc(len);
952 sprintf(mask, "%s!%s@%s", nickname, ident, hostname);
953 } else {
954 mask = malloc(len);
955 sprintf(mask, "%s@%s", ident, hostname);
956 }
957 return mask;
958 }
959
960 int
961 IsChannelName(const char *name) {
962 unsigned int ii;
963
964 if (*name !='#')
965 return 0;
966 for (ii=1; name[ii]; ++ii) {
967 if ((name[ii] > 0) && (name[ii] <= 32))
968 return 0;
969 if (name[ii] == ',')
970 return 0;
971 if (name[ii] == '\xa0')
972 return 0;
973 }
974 return 1;
975 }
976
977
978 unsigned int
979 irc_user_modes(const struct userNode *user, char modes[], size_t length)
980 {
981 unsigned int ii, jj;
982
983 for (ii = jj = 0; (jj < length) && (irc_user_mode_chars[ii] != '\0'); ++ii) {
984 if ((user->modes & (1 << ii)) && (irc_user_mode_chars[ii] != ' '))
985 modes[jj++] = irc_user_mode_chars[ii];
986 }
987
988 ii = jj;
989 while (jj < length)
990 modes[jj++] = '\0';
991
992 return ii;
993 }
994