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