1 /*
2 server-redirect.c : irssi
3
4 Copyright (C) 1999-2000 Timo Sirainen
5
6 This program 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 along
17 with this program; if not, write to the Free Software Foundation, Inc.,
18 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
19 */
20
21 #include "module.h"
22 #include "signals.h"
23 #include "misc.h"
24 #include "rawlog.h"
25
26 #include "irc-servers.h"
27 #include "servers-redirect.h"
28
29 #define DEFAULT_REDIRECT_TIMEOUT 60
30
31 /* Allow one non-expected redirections to come before the expected one
32 before aborting it. Some IRC bouncers/proxies reply to eg. PINGs
33 immediately. */
34 #define MAX_FAILURE_COUNT 1
35
36 typedef struct {
37 char *name;
38 int refcount;
39
40 int remote;
41 int timeout;
42 GSList *start, *stop, *opt; /* char *event, int argpos, ... */
43 } REDIRECT_CMD_REC;
44
45 struct _REDIRECT_REC {
46 REDIRECT_CMD_REC *cmd;
47 time_t created;
48 int failures;
49 char *prefix;
50
51 unsigned int destroyed:1;
52 unsigned int aborted:1;
53 unsigned int remote:1;
54 unsigned int first_signal_sent:1;
55
56 char *arg;
57 int count;
58 char *failure_signal, *default_signal, *first_signal, *last_signal;
59 GSList *signals; /* event, signal, ... */
60 };
61
62 static GHashTable *command_redirects; /* "command xxx" : REDIRECT_CMD_REC* */
63
64 /* Find redirection command record for specified command line. */
redirect_cmd_find(const char * command)65 static REDIRECT_CMD_REC *redirect_cmd_find(const char *command)
66 {
67 REDIRECT_CMD_REC *rec;
68 const char *p;
69 char *cmd;
70
71 p = strchr(command, ' ');
72 if (p == NULL)
73 rec = g_hash_table_lookup(command_redirects, command);
74 else {
75 cmd = g_strndup(command, (int) (p-command));
76 rec = g_hash_table_lookup(command_redirects, cmd);
77 g_free(cmd);
78 }
79 return rec;
80 }
81
redirect_cmd_destroy(REDIRECT_CMD_REC * rec)82 static void redirect_cmd_destroy(REDIRECT_CMD_REC *rec)
83 {
84 GSList *tmp;
85
86 for (tmp = rec->start; tmp != NULL; tmp = tmp->next->next)
87 g_free(tmp->data);
88 for (tmp = rec->stop; tmp != NULL; tmp = tmp->next->next)
89 g_free(tmp->data);
90 for (tmp = rec->opt; tmp != NULL; tmp = tmp->next->next)
91 g_free(tmp->data);
92 g_slist_free(rec->start);
93 g_slist_free(rec->stop);
94 g_slist_free(rec->opt);
95 g_free(rec->name);
96 g_free(rec);
97 }
98
redirect_cmd_ref(REDIRECT_CMD_REC * rec)99 static void redirect_cmd_ref(REDIRECT_CMD_REC *rec)
100 {
101 rec->refcount++;
102 }
103
redirect_cmd_unref(REDIRECT_CMD_REC * rec)104 static void redirect_cmd_unref(REDIRECT_CMD_REC *rec)
105 {
106 if (--rec->refcount <= 0)
107 redirect_cmd_destroy(rec);
108 }
109
server_redirect_destroy(REDIRECT_REC * rec)110 void server_redirect_destroy(REDIRECT_REC *rec)
111 {
112 redirect_cmd_unref(rec->cmd);
113
114 g_free_not_null(rec->prefix);
115 g_free_not_null(rec->arg);
116 g_free_not_null(rec->failure_signal);
117 g_free_not_null(rec->default_signal);
118 g_free_not_null(rec->first_signal);
119 g_free_not_null(rec->last_signal);
120 g_slist_foreach(rec->signals, (GFunc) g_free, NULL);
121 g_slist_free(rec->signals);
122 g_free(rec);
123 }
124
server_redirect_register(const char * command,int remote,int timeout,...)125 void server_redirect_register(const char *command,
126 int remote, int timeout, ...)
127 {
128 va_list va;
129 GSList *start, *stop, *opt, **list;
130 const char *event;
131 int argpos;
132
133 va_start(va, timeout);
134 start = stop = opt = NULL; list = &start;
135 for (;;) {
136 event = va_arg(va, const char *);
137 if (event == NULL) {
138 if (list == &start)
139 list = &stop;
140 else if (list == &stop)
141 list = &opt;
142 else
143 break;
144 continue;
145 }
146
147 argpos = va_arg(va, int);
148 *list = g_slist_append(*list, g_strdup(event));
149 *list = g_slist_append(*list, GINT_TO_POINTER(argpos));
150 }
151
152 va_end(va);
153
154 server_redirect_register_list(command, remote, timeout,
155 start, stop, opt);
156 }
157
server_redirect_register_list(const char * command,int remote,int timeout,GSList * start,GSList * stop,GSList * opt)158 void server_redirect_register_list(const char *command,
159 int remote, int timeout,
160 GSList *start, GSList *stop, GSList *opt)
161 {
162 REDIRECT_CMD_REC *rec;
163 gpointer key, value;
164
165 g_return_if_fail(command != NULL);
166 g_return_if_fail(stop != NULL);
167
168 if (g_hash_table_lookup_extended(command_redirects, command,
169 &key, &value)) {
170 /* Already registered - might have changed so destroy
171 the old one */
172 g_hash_table_remove(command_redirects, command);
173 redirect_cmd_unref(value);
174 }
175
176 rec = g_new0(REDIRECT_CMD_REC, 1);
177 redirect_cmd_ref(rec);
178 rec->name = g_strdup(command);
179 rec->remote = remote;
180 rec->timeout = timeout > 0 ? timeout : DEFAULT_REDIRECT_TIMEOUT;
181 rec->start = start;
182 rec->stop = stop;
183 rec->opt = opt;
184 g_hash_table_insert(command_redirects, rec->name, rec);
185 }
186
server_redirect_event(IRC_SERVER_REC * server,const char * command,int count,const char * arg,int remote,const char * failure_signal,...)187 void server_redirect_event(IRC_SERVER_REC *server, const char *command,
188 int count, const char *arg, int remote,
189 const char *failure_signal, ...)
190 {
191 GSList *signals;
192 const char *event, *signal;
193 va_list va;
194
195 va_start(va, failure_signal);
196 signals = NULL;
197 while ((event = va_arg(va, const char *)) != NULL) {
198 signal = va_arg(va, const char *);
199 if (signal == NULL) {
200 g_warning("server_redirect_event(%s): "
201 "signal not specified for event", command);
202 break;
203 }
204
205 signals = g_slist_append(signals, g_strdup(event));
206 signals = g_slist_append(signals, g_strdup(signal));
207 }
208
209 va_end(va);
210
211 server_redirect_event_list(server, command, count, arg, remote,
212 failure_signal, signals);
213 }
214
215 /* Find specified event from signals list. If it's found, remove it from the
216 list and return it's target signal. */
signal_list_move(GSList ** signals,const char * event)217 static char *signal_list_move(GSList **signals, const char *event)
218 {
219 GSList *link;
220 char *linkevent, *linksignal;
221
222 link = gslist_find_string(*signals, event);
223 if (link == NULL)
224 return NULL;
225
226 linkevent = link->data;
227 linksignal = link->next->data;
228
229 *signals = g_slist_remove(*signals, linkevent);
230 *signals = g_slist_remove(*signals, linksignal);
231
232 g_free(linkevent);
233 return linksignal;
234 }
235
server_redirect_event_list(IRC_SERVER_REC * server,const char * command,int count,const char * arg,int remote,const char * failure_signal,GSList * signals)236 void server_redirect_event_list(IRC_SERVER_REC *server, const char *command,
237 int count, const char *arg, int remote,
238 const char *failure_signal, GSList *signals)
239 {
240 REDIRECT_CMD_REC *cmdrec;
241 REDIRECT_REC *rec;
242
243 g_return_if_fail(IS_IRC_SERVER(server));
244 g_return_if_fail(command != NULL);
245 g_return_if_fail((g_slist_length(signals) & 1) == 0);
246
247 cmdrec = g_hash_table_lookup(command_redirects, command);
248 if (cmdrec == NULL) {
249 g_warning("Unknown redirection command: %s", command);
250 return;
251 }
252
253 redirect_cmd_ref(cmdrec);
254
255 rec = g_new0(REDIRECT_REC, 1);
256 rec->created = time(NULL);
257 rec->cmd = cmdrec;
258 rec->arg = g_strdup(arg);
259 rec->count = count;
260 rec->remote = remote != -1 ? remote : cmdrec->remote;
261 rec->failure_signal = g_strdup(failure_signal);
262
263 rec->default_signal = signal_list_move(&signals, "");
264 rec->first_signal = signal_list_move(&signals, "redirect first");
265 rec->last_signal = signal_list_move(&signals, "redirect last");
266 rec->signals = signals;
267
268 if (server->redirect_next != NULL)
269 server_redirect_destroy(server->redirect_next);
270 server->redirect_next = rec;
271 }
272
server_redirect_command(IRC_SERVER_REC * server,const char * command,REDIRECT_REC * redirect)273 void server_redirect_command(IRC_SERVER_REC *server, const char *command,
274 REDIRECT_REC *redirect)
275 {
276 REDIRECT_CMD_REC *cmdrec;
277
278 g_return_if_fail(IS_IRC_SERVER(server));
279 g_return_if_fail(command != NULL);
280
281 if (redirect == NULL) {
282 /* no redirection wanted, but still register the command
283 so future redirections wont get messed up. */
284 cmdrec = redirect_cmd_find(command);
285 if (cmdrec == NULL)
286 return;
287
288 redirect_cmd_ref(cmdrec);
289
290 redirect = g_new0(REDIRECT_REC, 1);
291 redirect->created = time(NULL);
292 redirect->cmd = cmdrec;
293 redirect->remote = cmdrec->remote;
294 }
295
296 server->redirects = g_slist_append(server->redirects, redirect);
297 }
298
redirect_args_match(const char * event_args,const char * arg,int pos)299 static int redirect_args_match(const char *event_args,
300 const char *arg, int pos)
301 {
302 const char *start;
303
304 if (pos == -1)
305 return TRUE;
306
307 /* skip to the start of the wanted argument */
308 while (pos > 0 && *event_args != '\0') {
309 while (*event_args != ' ' && *event_args != '\0') event_args++;
310 while (*event_args == ' ') event_args++;
311 pos--;
312 }
313
314 /* now compare the arguments */
315 start = event_args;
316 while (*arg != '\0') {
317 while (*arg != '\0' && *arg != ' ' && *event_args != '\0') {
318 if (i_toupper(*arg) != i_toupper(*event_args))
319 break;
320 arg++; event_args++;
321 }
322
323 if ((*arg == '\0' || *arg == ' ') &&
324 (*event_args == '\0' || *event_args == ' '))
325 return TRUE;
326
327 /* compare the next argument */
328 while (*arg != ' ' && *arg != '\0') arg++;
329 while (*arg == ' ') arg++;
330
331 event_args = start;
332 }
333
334 return FALSE;
335 }
336
redirect_cmd_list_find(GSList * list,const char * event)337 static GSList *redirect_cmd_list_find(GSList *list, const char *event)
338 {
339 while (list != NULL) {
340 const char *str = list->data;
341
342 if (g_strcmp0(str, event) == 0)
343 break;
344 list = list->next->next;
345 }
346
347 return list;
348 }
349
350 #define MATCH_NONE 0
351 #define MATCH_START 1
352 #define MATCH_STOP 2
353
redirect_match(REDIRECT_REC * redirect,const char * event,const char * args,int * match)354 static const char *redirect_match(REDIRECT_REC *redirect, const char *event,
355 const char *args, int *match)
356 {
357 GSList *tmp, *cmdpos;
358 const char *signal;
359 int match_list;
360
361 if (redirect->aborted)
362 return NULL;
363
364 /* get the signal for redirection event - if it's not found we'll
365 use the default signal */
366 signal = NULL;
367 for (tmp = redirect->signals; tmp != NULL; tmp = tmp->next->next) {
368 if (g_strcmp0(tmp->data, event) == 0) {
369 signal = tmp->next->data;
370 break;
371 }
372 }
373
374 /* find the argument position */
375 if (redirect->destroyed) {
376 /* stop event is already found for this redirection, but
377 we'll still want to look for optional events */
378 cmdpos = redirect_cmd_list_find(redirect->cmd->opt, event);
379 if (cmdpos == NULL)
380 return NULL;
381
382 match_list = MATCH_STOP;
383 } else {
384 /* look from start/stop lists */
385 cmdpos = redirect_cmd_list_find(redirect->cmd->start, event);
386 if (cmdpos != NULL)
387 match_list = MATCH_START;
388 else {
389 cmdpos = redirect_cmd_list_find(redirect->cmd->stop,
390 event);
391 if (cmdpos != NULL)
392 match_list = MATCH_STOP;
393 else if (redirect->default_signal != NULL &&
394 args == NULL &&
395 strncmp(event, "event ", 6) == 0 &&
396 i_isdigit(event[6])) {
397 /* If there is a default signal, the
398 * redirection has already started and
399 * this is a numeric, use it */
400 /* XXX this should depend on the
401 * REDIRECT_CMD_REC, not REDIRECT_REC */
402 if (signal == NULL)
403 signal = redirect->default_signal;
404 match_list = MATCH_START;
405 } else {
406 match_list = MATCH_NONE;
407 }
408 }
409 }
410
411 if (signal == NULL && cmdpos == NULL) {
412 /* event not found from specified redirection events nor
413 registered command events, and no default signal */
414 return NULL;
415 }
416
417 /* check that arguments match */
418 if (args != NULL && redirect->arg != NULL && cmdpos != NULL &&
419 !redirect_args_match(args, redirect->arg,
420 GPOINTER_TO_INT(cmdpos->next->data)))
421 return NULL;
422
423 *match = match_list;
424 return signal != NULL ? signal : redirect->default_signal;
425 }
426
redirect_abort(IRC_SERVER_REC * server,REDIRECT_REC * rec)427 static void redirect_abort(IRC_SERVER_REC *server, REDIRECT_REC *rec)
428 {
429 char *str;
430
431 server->redirects =
432 g_slist_remove(server->redirects, rec);
433
434 if (rec->aborted || !rec->destroyed) {
435 /* emit the failure signal */
436 if (rec->failure_signal != NULL)
437 str = g_strdup_printf("FAILED %s: %s", rec->cmd->name, rec->failure_signal);
438 else
439 str = g_strdup_printf("FAILED %s", rec->cmd->name);
440
441 rawlog_redirect(server->rawlog, str);
442 g_free(str);
443
444 if (rec->failure_signal != NULL)
445 signal_emit(rec->failure_signal, 1, server);
446 } else if (rec->last_signal != NULL) {
447 /* emit the last signal */
448 signal_emit(rec->last_signal, 1, server);
449 }
450
451 server->redirect_active = g_slist_remove(server->redirect_active, rec);
452
453 server_redirect_destroy(rec);
454 }
455
456 #define REDIRECT_IS_TIMEOUTED(rec) \
457 ((now-(rec)->created) > (rec)->cmd->timeout)
458
459
redirect_find(IRC_SERVER_REC * server,const char * event,const char * args,const char ** signal,int * match)460 static REDIRECT_REC *redirect_find(IRC_SERVER_REC *server, const char *event,
461 const char *args, const char **signal,
462 int *match)
463 {
464 REDIRECT_REC *redirect;
465 GSList *tmp, *next;
466 time_t now;
467 const char *match_signal;
468
469 /* find the redirection */
470 *signal = NULL; redirect = NULL;
471 for (tmp = server->redirects; tmp != NULL; tmp = tmp->next) {
472 REDIRECT_REC *rec = tmp->data;
473
474 /* already active, don't try to start it again */
475 if (g_slist_find(server->redirect_active, rec) != NULL)
476 continue;
477
478 match_signal = redirect_match(rec, event, args, match);
479 if (match_signal != NULL && *match != MATCH_NONE) {
480 redirect = rec;
481 *signal = match_signal;
482 break;
483 }
484 }
485
486 /* remove the destroyed, non-remote and timeouted remote
487 redirections that should have happened before this redirection */
488 now = time(NULL);
489 for (tmp = server->redirects; tmp != NULL; tmp = next) {
490 REDIRECT_REC *rec = tmp->data;
491
492 if (rec == redirect)
493 break;
494
495 next = tmp->next;
496 if (rec->destroyed) {
497 /* redirection is finished, destroy it */
498 redirect_abort(server, rec);
499 } else if (redirect != NULL) {
500 /* check if redirection failed */
501 if (rec->aborted ||
502 rec->failures++ >= MAX_FAILURE_COUNT) {
503 /* enough failures, abort it now */
504 if (!rec->remote || REDIRECT_IS_TIMEOUTED(rec))
505 redirect_abort(server, rec);
506 }
507 }
508 }
509
510 return redirect;
511 }
512
513 static const char *
server_redirect_get(IRC_SERVER_REC * server,const char * prefix,const char * event,const char * args,REDIRECT_REC ** redirect,int * match)514 server_redirect_get(IRC_SERVER_REC *server, const char *prefix,
515 const char *event, const char *args,
516 REDIRECT_REC **redirect, int *match)
517 {
518 const char *signal = NULL;
519 GSList *ptr, *next;
520 REDIRECT_REC *r;
521
522 *redirect = NULL;
523 *match = MATCH_NONE;
524
525 if (server->redirects == NULL)
526 return NULL;
527
528 for (ptr = server->redirect_active; ptr != NULL; ptr = next) {
529 next = ptr->next;
530 r = ptr->data;
531 if (prefix != NULL && r->prefix != NULL &&
532 g_strcmp0(prefix, r->prefix)) {
533 /* not from this server */
534 continue;
535 }
536 /* redirection is already started, now we'll just need to
537 keep redirecting until stop-event is found. */
538 *redirect = r;
539 signal = redirect_match(*redirect, event, NULL, match);
540 if (signal == NULL) {
541 /* not a numeric, so we've lost the
542 stop event.. */
543 (*redirect)->aborted = TRUE;
544 redirect_abort(server, *redirect);
545
546 *redirect = NULL;
547 }
548 if (*redirect != NULL)
549 break;
550 }
551
552 if (*redirect == NULL) {
553 /* find the redirection */
554 *redirect = redirect_find(server, event, args, &signal, match);
555 }
556
557 /* remember which server is replying to our request */
558 if (*redirect != NULL && prefix != NULL && (*redirect)->prefix == NULL)
559 (*redirect)->prefix = g_strdup(prefix);
560
561 if (*redirect != NULL && (*redirect)->first_signal != NULL &&
562 !(*redirect)->first_signal_sent) {
563 /* emit the first_signal */
564 (*redirect)->first_signal_sent = TRUE;
565 signal_emit((*redirect)->first_signal, 1, server);
566 }
567
568 return signal;
569 }
570
server_redirect_get_signal(IRC_SERVER_REC * server,const char * prefix,const char * event,const char * args)571 const char *server_redirect_get_signal(IRC_SERVER_REC *server,
572 const char *prefix,
573 const char *event,
574 const char *args)
575 {
576 REDIRECT_REC *redirect;
577 const char *signal;
578 int match;
579
580 signal = server_redirect_get(server, prefix, event, args, &redirect, &match);
581 if (redirect == NULL)
582 ;
583 else if (match != MATCH_STOP) {
584 if (g_slist_find(server->redirect_active, redirect) == NULL)
585 server->redirect_active = g_slist_prepend(server->redirect_active, redirect);
586 } else {
587 /* stop event - remove this redirection next time this
588 function is called (can't destroy now or our return
589 value would be corrupted) */
590 if (--redirect->count <= 0)
591 redirect->destroyed = TRUE;
592 server->redirect_active = g_slist_remove(server->redirect_active, redirect);
593 }
594
595 return signal;
596 }
597
server_redirect_peek_signal(IRC_SERVER_REC * server,const char * prefix,const char * event,const char * args,int * redirected)598 const char *server_redirect_peek_signal(IRC_SERVER_REC *server,
599 const char *prefix,
600 const char *event,
601 const char *args,
602 int *redirected)
603 {
604 REDIRECT_REC *redirect;
605 const char *signal;
606 int match;
607
608 signal = server_redirect_get(server, prefix, event, args, &redirect, &match);
609 *redirected = match != MATCH_NONE;
610 return signal;
611 }
612
sig_disconnected(IRC_SERVER_REC * server)613 static void sig_disconnected(IRC_SERVER_REC *server)
614 {
615 if (!IS_IRC_SERVER(server))
616 return;
617
618 g_slist_free(server->redirect_active);
619 server->redirect_active = NULL;
620 g_slist_foreach(server->redirects,
621 (GFunc) server_redirect_destroy, NULL);
622 g_slist_free(server->redirects);
623 server->redirects = NULL;
624
625 if (server->redirect_next != NULL) {
626 server_redirect_destroy(server->redirect_next);
627 server->redirect_next = NULL;
628 }
629 }
630
cmd_redirect_destroy(char * key,REDIRECT_CMD_REC * cmd)631 static void cmd_redirect_destroy(char *key, REDIRECT_CMD_REC *cmd)
632 {
633 redirect_cmd_unref(cmd);
634 }
635
servers_redirect_init(void)636 void servers_redirect_init(void)
637 {
638 command_redirects = g_hash_table_new((GHashFunc) g_str_hash, (GCompareFunc) g_str_equal);
639
640 /* WHOIS - register as remote command by default
641 with a default timeout */
642 server_redirect_register("whois", TRUE, 0,
643 "event 311", 1, /* Begins the WHOIS */
644 NULL,
645 "event 401", 1, /* No such nick */
646 "event 318", 1, /* End of WHOIS */
647 "event 402", 1, /* No such server */
648 "event 431", 1, /* No nickname given */
649 "event 461", 1, /* Not enough parameters */
650 NULL,
651 "event 318", 1, /* After 401, we should get 318, but in OPN we don't.. */
652 NULL);
653
654 /* WHOWAS */
655 server_redirect_register("whowas", FALSE, 0,
656 "event 314", 1, /* Begins the WHOWAS */
657 "event 406", 1, /* There was no such nick */
658 NULL,
659 "event 369", 1, /* End of WHOWAS */
660 NULL,
661 NULL);
662
663 /* WHO */
664 server_redirect_register("who", FALSE, 0,
665 "event 352", 1, /* An element of the WHO */
666 "event 354", -1, /* WHOX element */
667 "event 401", 1, /* No such nick/channel */
668 NULL,
669 "event 315", 1, /* End of WHO */
670 "event 403", 1, /* no such channel */
671 NULL,
672 NULL);
673
674 /* LIST */
675 server_redirect_register("list", FALSE, 0,
676 "event 321", 1, /* Begins the LIST */
677 NULL,
678 "event 323", 1, /* End of LIST */
679 NULL,
680 NULL);
681
682 /* ISON */
683 server_redirect_register("ison", FALSE, 0,
684 NULL,
685 "event 303", -1, /* ISON */
686 NULL,
687 NULL);
688
689 /* USERHOST */
690 server_redirect_register("userhost", FALSE, 0,
691 "event 401", 1, /* no such nick */
692 NULL,
693 "event 302", -1, /* Userhost */
694 "event 461", -1, /* Not enough parameters */
695 NULL,
696 NULL);
697
698 /* MODE user */
699 server_redirect_register("mode user", FALSE, 0,
700 NULL,
701 "event mode", 0, /* MODE-reply */
702 "event 501", -1, /* Uknown MODE flag */
703 "event 502", -1, /* Can't change mode for other users */
704 "event 403", 1, /* That channel doesn't exist (tried to change mode to others) */
705 NULL,
706 NULL);
707
708 /* MODE #channel */
709 server_redirect_register("mode channel", FALSE, 0,
710 NULL,
711 "event 324", 1, /* MODE-reply */
712 "event 403", 1, /* no such channel */
713 "event 442", 1, /* "you're not on that channel" */
714 "event 479", 1, /* "Cannot join channel (illegal name)" IMHO this is not a logical reply from server. */
715 NULL,
716 "event 329", 1, /* Channel create time */
717 NULL);
718
719 /* MODE #channel b */
720 server_redirect_register("mode b", FALSE, 0,
721 "event 367", 1,
722 NULL,
723 "event 368", 1, /* End of Channel ban List */
724 "event 403", 1, /* no such channel */
725 "event 442", 1, /* "you're not on that channel" */
726 "event 479", 1, /* "Cannot join channel (illegal name)" IMHO this is not a logical reply from server. */
727 NULL,
728 NULL);
729
730 /* MODE #channel e */
731 server_redirect_register("mode e", FALSE, 0,
732 "event 348", 1,
733 NULL,
734 "event 349", 1, /* End of ban exceptions */
735 "event 482", 1, /* not channel operator - OPN's ircd doesn't want non-ops to see ban exceptions */
736 "event 403", 1, /* no such channel */
737 "event 442", 1, /* "you're not on that channel" */
738 "event 479", 1, /* "Cannot join channel (illegal name)" IMHO this is not a logical reply from server. */
739 "event 472", -1, /* unknown mode (you should check e-mode's existence from 004 event instead of relying on this) */
740 NULL,
741 NULL);
742
743 /* MODE #channel I */
744 server_redirect_register("mode I", FALSE, 0,
745 "event 346", 1,
746 NULL,
747 "event 347", 1, /* End of invite list */
748 "event 482", 1, /* not channel operator - OPN's ircd doesn't want non-ops to see ban exceptions */
749 "event 403", 1, /* no such channel */
750 "event 442", 1, /* "you're not on that channel" */
751 "event 479", 1, /* "Cannot join channel (illegal name)" IMHO this is not a logical reply from server. */
752 "event 472", -1, /* unknown mode (you should check I-mode's existence from 004 event instead of relying on this) */
753 NULL,
754 NULL);
755
756 /* PING - use default timeout */
757 server_redirect_register("ping", TRUE, 0,
758 NULL,
759 "event 402", -1, /* no such server */
760 "event pong", -1, /* PONG */
761 NULL,
762 NULL);
763
764 signal_add("server disconnected", (SIGNAL_FUNC) sig_disconnected);
765 }
766
servers_redirect_deinit(void)767 void servers_redirect_deinit(void)
768 {
769 g_hash_table_foreach(command_redirects,
770 (GHFunc) cmd_redirect_destroy, NULL);
771 g_hash_table_destroy(command_redirects);
772
773 signal_remove("server disconnected", (SIGNAL_FUNC) sig_disconnected);
774 }
775