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