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