]> jfr.im git - irc/evilnet/x3.git/blob - src/proto-common.c
This is a spam checking module, remember to disable it just comment out the nick...
[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 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"
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->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
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 = 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 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
158 void
159 replay_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
203 static int
204 replay_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
231 static void
232 replay_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
248 void putsock(const char *text, ...) PRINTF_LIKE(1, 2);
249
250 void
251 putsock(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
274 void
275 close_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))
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 = &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 */
446 if (!pd->is_notice && cf->func
447 && ((cn->modes & MODE_REGISTERED) || GetUserMode(cn, cf->service)))
448 cf->func(pd->user, cn, pd->text+1, cf->service);
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);
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 static void
473 part_helper(struct chanNode *cn, void *data)
474 {
475 DelChannelUser(data, cn, false, 0);
476 }
477
478 void
479 reg_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
487 void
488 reg_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
500 struct userNode *
501 get_chanmsg_bot(unsigned char prefix)
502 {
503 return chanmsg_funcs[prefix].service;
504 }
505
506 static mode_change_func_t *mcf_list;
507 static unsigned int mcf_size = 0, mcf_used = 0;
508
509 void
510 reg_mode_change_func(mode_change_func_t handler)
511 {
512 if (mcf_used == mcf_size) {
513 if (mcf_size) {
514 mcf_size <<= 1;
515 mcf_list = realloc(mcf_list, mcf_size*sizeof(mode_change_func_t));
516 } else {
517 mcf_size = 8;
518 mcf_list = malloc(mcf_size*sizeof(mode_change_func_t));
519 }
520 }
521 mcf_list[mcf_used++] = handler;
522 }
523
524 struct mod_chanmode *
525 mod_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
541 struct mod_chanmode *
542 mod_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));
551 memcpy(res->new_upass, orig->new_upass, sizeof(res->new_upass));
552 memcpy(res->new_apass, orig->new_apass, sizeof(res->new_apass));
553 res->argc = orig->argc;
554 memcpy(res->args, orig->args, orig->argc*sizeof(orig->args[0]));
555 }
556 return res;
557 }
558
559 void
560 mod_chanmode_apply(struct userNode *who, struct chanNode *channel, struct mod_chanmode *change)
561 {
562 struct banNode *bn;
563 struct exemptNode *en;
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);
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);
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) {
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);
588 jj--;
589 }
590 }
591 bn = calloc(1, sizeof(*bn));
592 safestrncpy(bn->ban, change->args[ii].u.hostmask, sizeof(bn->ban));
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) {
602 bn = channel->banlist.list[jj];
603 if (strcmp(bn->ban, change->args[ii].u.hostmask))
604 continue;
605 free(bn);
606 banList_remove(&channel->banlist, bn);
607 break;
608 }
609 break;
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) {
616 if (match_ircglobs(change->args[ii].u.hostmask, channel->exemptlist.list[jj]->exempt)) {
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));
623 safestrncpy(en->exempt, change->args[ii].u.hostmask, sizeof(en->exempt));
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) {
633 if (strcmp(channel->exemptlist.list[jj]->exempt, change->args[ii].u.hostmask))
634 continue;
635 free(channel->exemptlist.list[jj]);
636 exemptList_remove(&channel->exemptlist, channel->exemptlist.list[jj]);
637 break;
638 }
639 break;
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 **/
644 case MODE_CHANOP:
645 case MODE_HALFOP:
646 case MODE_VOICE:
647 case MODE_VOICE|MODE_CHANOP:
648 case MODE_VOICE|MODE_HALFOP:
649 case MODE_CHANOP|MODE_HALFOP:
650 case MODE_VOICE|MODE_CHANOP|MODE_HALFOP:
651 case MODE_REMOVE|MODE_CHANOP:
652 case MODE_REMOVE|MODE_HALFOP:
653 case MODE_REMOVE|MODE_VOICE:
654 case MODE_REMOVE|MODE_VOICE|MODE_CHANOP:
655 case MODE_REMOVE|MODE_VOICE|MODE_HALFOP:
656 case MODE_REMOVE|MODE_CHANOP|MODE_HALFOP:
657 case MODE_REMOVE|MODE_VOICE|MODE_CHANOP|MODE_HALFOP:
658 if (change->args[ii].mode & MODE_REMOVE)
659 change->args[ii].u.member->modes &= ~change->args[ii].mode;
660 else
661 change->args[ii].u.member->modes |= change->args[ii].mode;
662 break;
663 default:
664 assert(0 && "Invalid mode argument");
665 continue;
666 }
667 }
668 }
669
670 void
671 mod_chanmode_free(struct mod_chanmode *change)
672 {
673 free(change);
674 }
675
676 int
677 mod_chanmode(struct userNode *who, struct chanNode *channel, char **modes, unsigned int argc, unsigned int flags)
678 {
679 struct modeNode *member;
680 struct mod_chanmode *change;
681 unsigned int ii;
682 short base_oplevel;
683
684
685 if (!modes || !modes[0])
686 return 0;
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)))
692 return 0;
693
694 call_channel_mode_funcs(who, channel, modes, argc);
695
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
707 int
708 irc_make_chanmode(struct chanNode *chan, char *out)
709 {
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));
715 safestrncpy(change.new_upass, chan->upass, sizeof(change.new_upass));
716 safestrncpy(change.new_apass, chan->apass, sizeof(change.new_apass));
717 return strlen(mod_chanmode_format(&change, out));
718 }
719
720 static user_mode_func_t *um_list;
721 static unsigned int um_size = 0, um_used = 0;
722
723 void
724 reg_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
738 void
739 unreg_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
750 static void
751 call_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
759 static channel_mode_func_t *cm_list;
760 static unsigned int cm_size = 0, cm_used = 0;
761
762 void
763 reg_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
777 void
778 unreg_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
789 static void
790 call_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
798 char *
799 generate_hostmask(struct userNode *user, int options)
800 {
801 irc_in_addr_t ip;
802 char *nickname, *ident, *hostname, *mask;
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)
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
819 ident = user->ident;
820 else if (options & GENMASK_ANY_IDENT)
821 ident = "*";
822 else {
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 {
829 ident = alloca(strlen(user->ident)+2);
830 ident[0] = '*';
831 strcpy(ident+1, user->ident + ((*user->ident == '~')?1:0));
832 }
833 }
834 hostname = user->hostname;
835 if (IsFakeHost(user) && IsHiddenHost(user) && !(options & GENMASK_NO_HIDING)) {
836 hostname = user->fakehost;
837 } else if (IsHiddenHost(user) && user->handle_info && hidden_host_suffix && !(options & GENMASK_NO_HIDING)) {
838 hostname = alloca(strlen(user->handle_info->handle) + strlen(hidden_host_suffix) + 2);
839 sprintf(hostname, "%s.%s", user->handle_info->handle, hidden_host_suffix);
840 } else if (options & GENMASK_STRICT_HOST) {
841 if (options & GENMASK_BYIP)
842 hostname = (char*)irc_ntoa(&user->ip);
843 } else if ((options & GENMASK_BYIP) || irc_pton(&ip, NULL, hostname)) {
844 /* Should generate an IP-based hostmask. */
845 hostname = alloca(IRC_NTOP_MAX_SIZE);
846 hostname[IRC_NTOP_MAX_SIZE-1] = '\0';
847 if (irc_in_addr_is_ipv4(user->ip)) {
848 /* By popular acclaim, a /16 hostmask is used. */
849 sprintf(hostname, "%d.%d.*", user->ip.in6_8[12], user->ip.in6_8[13]);
850 } else if (irc_in_addr_is_ipv6(user->ip)) {
851 /* Who knows what the default mask should be? Use a /48 to start with. */
852 sprintf(hostname, "%x:%x:%x:*", user->ip.in6[0], user->ip.in6[1], user->ip.in6[2]);
853 } else {
854 /* Unknown type; just copy IP directly. */
855 irc_ntop(hostname, IRC_NTOP_MAX_SIZE, &user->ip);
856 }
857 } else {
858 int cnt;
859 /* This heuristic could be made smarter. Is it worth the effort? */
860 for (ii=cnt=0; hostname[ii]; ii++)
861 if (hostname[ii] == '.')
862 cnt++;
863 if (cnt == 1) {
864 /* only a two-level domain name; leave hostname */
865 } else if (cnt == 2) {
866 for (ii=0; user->hostname[ii] != '.'; ii++) ;
867 /* Add 3 to account for the *. and \0. */
868 hostname = alloca(strlen(user->hostname+ii)+3);
869 sprintf(hostname, "*.%s", user->hostname+ii+1);
870 } else {
871 for (cnt=3, ii--; cnt; ii--)
872 if (user->hostname[ii] == '.')
873 cnt--;
874 /* The loop above will overshoot the dot one character;
875 we skip forward two (the one character and the dot)
876 when printing, so we only add one for the \0. */
877 hostname = alloca(strlen(user->hostname+ii)+1);
878 sprintf(hostname, "*.%s", user->hostname+ii+2);
879 }
880 }
881 // sethost - reed/apples
882 if (IsSetHost(user))
883 hostname = strchr(user->sethost, '@') + 1;
884
885 /* Emit hostmask */
886 len = strlen(ident) + strlen(hostname) + 2;
887 if (nickname) {
888 len += strlen(nickname) + 1;
889 mask = malloc(len);
890 sprintf(mask, "%s!%s@%s", nickname, ident, hostname);
891 } else {
892 mask = malloc(len);
893 sprintf(mask, "%s@%s", ident, hostname);
894 }
895 return mask;
896 }
897
898 int
899 IsChannelName(const char *name) {
900 unsigned int ii;
901
902 if (*name !='#')
903 return 0;
904 for (ii=1; name[ii]; ++ii) {
905 if ((name[ii] > 0) && (name[ii] <= 32))
906 return 0;
907 if (name[ii] == ',')
908 return 0;
909 if (name[ii] == '\xa0')
910 return 0;
911 }
912 return 1;
913 }