summaryrefslogtreecommitdiff
path: root/src/irc/core
diff options
context:
space:
mode:
Diffstat (limited to 'src/irc/core')
-rw-r--r--src/irc/core/Makefile.am6
-rw-r--r--src/irc/core/channels-query.c63
-rw-r--r--src/irc/core/ctcp.c4
-rw-r--r--src/irc/core/irc-commands.c125
-rw-r--r--src/irc/core/irc-servers.c6
-rw-r--r--src/irc/core/irc-servers.h6
-rw-r--r--src/irc/core/irc.c37
-rw-r--r--src/irc/core/lag.c73
-rw-r--r--src/irc/core/server-idle.h22
-rw-r--r--src/irc/core/servers-idle.c (renamed from src/irc/core/server-idle.c)119
-rw-r--r--src/irc/core/servers-idle.h34
-rw-r--r--src/irc/core/servers-redirect.c569
-rw-r--r--src/irc/core/servers-redirect.h66
13 files changed, 842 insertions, 288 deletions
diff --git a/src/irc/core/Makefile.am b/src/irc/core/Makefile.am
index d4ee0fa3..9a12b7f4 100644
--- a/src/irc/core/Makefile.am
+++ b/src/irc/core/Makefile.am
@@ -30,7 +30,8 @@ libirc_core_a_SOURCES = \
modes.c \
mode-lists.c \
netsplit.c \
- server-idle.c
+ servers-idle.c \
+ servers-redirect.c
noinst_HEADERS = \
bans.h \
@@ -49,4 +50,5 @@ noinst_HEADERS = \
mode-lists.h \
module.h \
netsplit.h \
- server-idle.h
+ servers-idle.h \
+ servers-redirect.h
diff --git a/src/irc/core/channels-query.c b/src/irc/core/channels-query.c
index c8a29866..12f21610 100644
--- a/src/irc/core/channels-query.c
+++ b/src/irc/core/channels-query.c
@@ -209,11 +209,10 @@ static void channel_send_query(IRC_SERVER_REC *server, int query)
for (tmp = chans; tmp != NULL; tmp = tmp->next) {
chanrec = tmp->data;
- server_redirect_event((SERVER_REC *) server, chanstr, 4,
- "event 403", "chanquery mode abort", 1,
- "event 442", "chanquery mode abort", 1, /* "you're not on that channel" */
- "event 479", "chanquery mode abort", 1, /* "Cannot join channel (illegal name)" IMHO this is not a logical reply from server. */
- "event 324", "chanquery mode", 1, NULL);
+ server_redirect_event(server, "mode channel", chanstr, -1,
+ "chanquery mode abort",
+ "event 324", "chanquery mode",
+ "", "chanquery mode abort", NULL);
}
break;
@@ -223,11 +222,11 @@ static void channel_send_query(IRC_SERVER_REC *server, int query)
for (tmp = chans; tmp != NULL; tmp = tmp->next) {
chanrec = tmp->data;
- server_redirect_event((SERVER_REC *) server, chanstr, 3,
- "event 401", "chanquery who abort", 1,
- "event 403", "chanquery who abort", 1,
- "event 315", "chanquery who end", 1,
- "event 352", "silent event who", 1, NULL);
+ server_redirect_event(server, "who", chanstr, -1,
+ "chanquery who abort",
+ "event 315", "chanquery who end",
+ "event 352", "silent event who",
+ "", "chanquery who abort", NULL);
}
break;
@@ -240,12 +239,11 @@ static void channel_send_query(IRC_SERVER_REC *server, int query)
mode requests - if channels are joined manually
irssi could ask modes separately but afterwards
join the two b/e/I modes together */
- server_redirect_event((SERVER_REC *) server, chanstr, 4,
- "event 403", "chanquery mode abort", 1,
- "event 442", "chanquery mode abort", 1, /* "you're not on that channel" */
- "event 479", "chanquery mode abort", 1, /* "Cannot join channel (illegal name)" IMHO this is not a logical reply from server. */
- "event 368", "chanquery ban end", 1,
- "event 367", "chanquery ban", 1, NULL);
+ server_redirect_event(server, "mode b", chanstr, -1,
+ "chanquery mode abort",
+ "event 367", "chanquery ban",
+ "event 368", "chanquery ban end",
+ "", "chanquery mode abort", NULL);
}
break;
@@ -254,12 +252,11 @@ static void channel_send_query(IRC_SERVER_REC *server, int query)
for (tmp = chans; tmp != NULL; tmp = tmp->next) {
chanrec = tmp->data;
- server_redirect_event((SERVER_REC *) server, chanstr, 4,
- "event 403", "chanquery mode abort", 1,
- "event 442", "chanquery mode abort", 1, /* "you're not on that channel" */
- "event 479", "chanquery mode abort", 1, /* "Cannot join channel (illegal name)" IMHO this is not a logical reply from server. */
- "event 349", "chanquery eban end", 1,
- "event 348", "chanquery eban", 1, NULL);
+ server_redirect_event(server, "mode e", chanstr, -1,
+ "chanquery mode abort",
+ "event 348", "chanquery eban",
+ "event 349", "chanquery eban end",
+ "", "chanquery mode abort", NULL);
}
break;
@@ -268,12 +265,11 @@ static void channel_send_query(IRC_SERVER_REC *server, int query)
for (tmp = chans; tmp != NULL; tmp = tmp->next) {
chanrec = tmp->data;
- server_redirect_event((SERVER_REC *) server, chanstr, 4,
- "event 403", "chanquery mode abort", 1,
- "event 442", "chanquery mode abort", 1, /* "you're not on that channel" */
- "event 479", "chanquery mode abort", 1, /* "Cannot join channel (illegal name)" IMHO this is not a logical reply from server. */
- "event 347", "chanquery ilist end", 1,
- "event 346", "chanquery ilist", 1, NULL);
+ server_redirect_event(server, "mode I", chanstr, -1,
+ "chanquery mode abort",
+ "event 346", "chanquery ilist",
+ "event 347", "chanquery ilist end",
+ "", "chanquery mode abort", NULL);
}
break;
@@ -423,14 +419,6 @@ static void event_channel_mode(IRC_SERVER_REC *server, const char *data,
g_free(params);
}
-static void multi_query_remove(IRC_SERVER_REC *server, const char *event, const char *data)
-{
- GSList *queue;
-
- while ((queue = server_redirect_getqueue((SERVER_REC *) server, event, data)) != NULL)
- server_redirect_remove_next((SERVER_REC *) server, event, queue);
-}
-
static void event_end_of_who(IRC_SERVER_REC *server, const char *data)
{
IRC_CHANNEL_REC *chanrec;
@@ -447,7 +435,6 @@ static void event_end_of_who(IRC_SERVER_REC *server, const char *data)
/* instead of multiple End of WHO replies we get
only this one... */
server->one_endofwho = TRUE;
- multi_query_remove(server, "event 315", data);
/* check that the WHO actually did return something
(that it understood #chan1,#chan2,..) */
@@ -549,8 +536,6 @@ static void multi_command_error(IRC_SERVER_REC *server, const char *data,
char *params, *channel, **chans;
int n;
- multi_query_remove(server, event, data);
-
params = event_get_params(data, 2, NULL, &channel);
chans = g_strsplit(channel, ",", -1);
diff --git a/src/irc/core/ctcp.c b/src/irc/core/ctcp.c
index e062a735..b3d56fb6 100644
--- a/src/irc/core/ctcp.c
+++ b/src/irc/core/ctcp.c
@@ -26,7 +26,7 @@
#include "settings.h"
#include "irc-servers.h"
-#include "server-idle.h"
+#include "servers-idle.h"
#include "ignore.h"
static void ctcp_queue_clean(IRC_SERVER_REC *server)
@@ -57,7 +57,7 @@ void ctcp_send_reply(IRC_SERVER_REC *server, const char *data)
return;
/* Add to first in idle queue */
- tag = server_idle_add(server, data, NULL, 0, NULL);
+ tag = server_idle_add(server, data);
server->ctcpqueue =
g_slist_append(server->ctcpqueue, GINT_TO_POINTER(tag));
}
diff --git a/src/irc/core/irc-commands.c b/src/irc/core/irc-commands.c
index 94df20d9..18f837e4 100644
--- a/src/irc/core/irc-commands.c
+++ b/src/irc/core/irc-commands.c
@@ -244,9 +244,6 @@ static void cmd_list(const char *data, IRC_SERVER_REC *server,
irc_send_cmdv(server, "LIST %s", str);
cmd_params_free(free_arg);
-
- /* add default redirection */
- server_redirect_default((SERVER_REC *) server, "bogus command list");
}
/* SYNTAX: WHO [<nicks> | <channels> | **] */
@@ -275,9 +272,6 @@ static void cmd_who(const char *data, IRC_SERVER_REC *server,
irc_send_cmdv(server, *rest == '\0' ? "WHO %s" : "WHO %s %s",
channel, rest);
cmd_params_free(free_arg);
-
- /* add default redirection */
- server_redirect_default((SERVER_REC *) server, "bogus command who");
}
static void cmd_names(const char *data, IRC_SERVER_REC *server,
@@ -323,43 +317,10 @@ static void cmd_nick(const char *data, IRC_SERVER_REC *server, WI_ITEM_REC *item
if (!cmd_get_params(data, &free_arg, 1, &nick))
return;
- if (strcmp(nick, server->nick) == 0) {
- /* don't bother trying to change the nick to the one you
- already have */
- cmd_params_free(free_arg);
- return;
- }
-
- server->nick_changing = TRUE;
irc_send_cmdv(server, "NICK %s", nick);
-
- nick = g_strdup_printf("%s :%s", nick, nick);
- server_redirect_event(SERVER(server), nick, 5,
- "event nick", "nickchange over", -1,
- "event 433", "nickchange over", 1,
- /* 437: ircnet = target unavailable,
- dalnet = banned in channel,
- can't change nick */
- "event 437", "nickchange over", -1,
- "event 432", "nickchange over", 1,
- "event 438", "nickchange over", 1, NULL);
- g_free(nick);
cmd_params_free(free_arg);
}
-static void sig_nickchange_over(IRC_SERVER_REC *server, const char *data,
- const char *nick, const char *addr)
-{
- char *signal;
-
- server->nick_changing = FALSE;
-
- signal = g_strconcat("event ", current_server_event, NULL);
- g_strdown(signal+6);
- signal_emit(signal, 4, server, data, nick, addr);
- g_free(signal);
-}
-
static char *get_redirect_nicklist(const char *nicks, int *free)
{
char *str, *ret;
@@ -419,20 +380,21 @@ static void cmd_whois(const char *data, IRC_SERVER_REC *server,
event_402 = "whois event noserver";
}
- server->whois_found = FALSE;
- irc_send_cmd_split(server, tmpstr->str, 2, server->max_whois_in_cmd);
-
/* do automatic /WHOWAS if any of the nicks wasn't found */
query = get_redirect_nicklist(query, &free_nick);
str = g_strconcat(qserver, " ", query, NULL);
- server_redirect_event(SERVER(server), str, 2,
- "event 318", "event 318", 1,
- "event 402", event_402, 1,
- "event 401", "whois not found", 1,
- "event 311", "whois event", 1, NULL);
+ server_redirect_event(server, "whois", str, *qserver != '\0',
+ NULL,
+ "event 318", "event 318",
+ "event 402", event_402,
+ "event 401", "whois not found",
+ "event 311", "whois event", NULL);
g_free(str);
+ server->whois_found = FALSE;
+ irc_send_cmd_split(server, tmpstr->str, 2, server->max_whois_in_cmd);
+
if (free_nick) g_free(query);
cmd_params_free(free_arg);
}
@@ -451,13 +413,14 @@ static void sig_whois_not_found(IRC_SERVER_REC *server, const char *data)
g_return_if_fail(data != NULL);
params = event_get_params(data, 2, NULL, &nick);
- irc_send_cmdv(server, "WHOWAS %s 1", nick);
server->whowas_found = FALSE;
- server_redirect_event(SERVER(server), nick, 1,
- "event 369", "whowas event end", 1,
- "event 314", "whowas event", 1,
- "event 406", "event empty", 1, NULL);
+ server_redirect_event(server, "whowas", nick, -1, NULL,
+ "event 314", "whowas event",
+ "event 369", "whowas event end",
+ "", "event empty", NULL);
+ irc_send_cmdv(server, "WHOWAS %s 1", nick);
+
g_free(params);
}
@@ -470,7 +433,7 @@ static void event_whowas(IRC_SERVER_REC *server, const char *data, const char *n
/* SYNTAX: WHOWAS [<nicks> [<count>]] */
static void cmd_whowas(const char *data, IRC_SERVER_REC *server)
{
- char *nicks, *count;
+ char *nicks, *count, *nicks_redir;
void *free_arg;
int free_nick;
@@ -480,15 +443,15 @@ static void cmd_whowas(const char *data, IRC_SERVER_REC *server)
return;
if (*nicks == '\0') nicks = server->nick;
+ nicks_redir = get_redirect_nicklist(nicks, &free_nick);
+ server_redirect_event(server, "whowas", nicks_redir, -1, NULL,
+ "event 314", "whowas event", NULL);
+ if (free_nick) g_free(nicks_redir);
+
server->whowas_found = FALSE;
irc_send_cmdv(server, *count == '\0' ? "WHOWAS %s" :
"WHOWAS %s %s", nicks, count);
- nicks = get_redirect_nicklist(nicks, &free_nick);
- server_redirect_event(SERVER(server), nicks, 1,
- "event 369", "event 369", 1,
- "event 314", "whowas event", 1, NULL);
- if (free_nick) g_free(nicks);
cmd_params_free(free_arg);
}
@@ -914,48 +877,6 @@ static void command_2self(const char *data, IRC_SERVER_REC *server)
cmd_params_free(free_arg);
}
-static void sig_connected(IRC_SERVER_REC *server)
-{
- g_return_if_fail(server != NULL);
-
- /* FIXME: these two aren't probably needed? this whole redirection
- thing might need some rethinking :) */
- /* WHOIS */
- /*server_redirect_init(SERVER(server), "", 2,
- "event 318", "event 402", "event 401",
- "event 301", "event 311", "event 312", "event 313",
- "event 317", "event 319", NULL);*/
-
- /* NICK */
- /*server_redirect_init(SERVER(server), "", 5,
- "event nick", "event 433", "event 437",
- "event 432", "event 438", NULL);*/
-
- /* problem (doesn't really apply currently since there's no GUI):
-
- second argument of server_redirect_init() is the command that
- generates the redirection automatically when it's called, but the
- command handler doesn't really know about the redirection itself.
-
- every time the command is called, this redirection is generated.
- this is a problem if the redirection is wanted sometimes but not
- always. for example /WHO #channel could create a window with a
- list of people in channel redirecting WHO's events to it's own use,
- but /WHO -nogui #channel would use the default WHO handler which
- doesn't know anything about redirection. with GUI /WHO the
- redirection would be done twice then..
-
- so the kludgy workaround currently is this: make the default
- handler handle the redirection always.. when default WHO/LIST
- handler is called, they call
- server_redirect_default("bogus command who") or ..list..
-
- this is really a problem if some script/plugin wants to override
- some default command to use redirections.. */
- server_redirect_init(SERVER(server), "bogus command who", 2, "event 401", "event 315", "event 352", NULL);
- server_redirect_init(SERVER(server), "bogus command list", 1, "event 321", "event 322", "event 323", NULL);
-}
-
void irc_commands_init(void)
{
tmpstr = g_string_new(NULL);
@@ -1048,11 +969,9 @@ void irc_commands_init(void)
signal_add("channel destroyed", (SIGNAL_FUNC) sig_channel_destroyed);
signal_add("server disconnected", (SIGNAL_FUNC) sig_server_disconnected);
- signal_add("nickchange over", (SIGNAL_FUNC) sig_nickchange_over);
signal_add("whois not found", (SIGNAL_FUNC) sig_whois_not_found);
signal_add("whois event", (SIGNAL_FUNC) event_whois);
signal_add("whowas event", (SIGNAL_FUNC) event_whowas);
- signal_add("server connected", (SIGNAL_FUNC) sig_connected);
command_set_options("connect", "+ircnet");
command_set_options("topic", "delete");
@@ -1119,11 +1038,9 @@ void irc_commands_deinit(void)
signal_remove("channel destroyed", (SIGNAL_FUNC) sig_channel_destroyed);
signal_remove("server disconnected", (SIGNAL_FUNC) sig_server_disconnected);
- signal_remove("nickchange over", (SIGNAL_FUNC) sig_nickchange_over);
signal_remove("whois not found", (SIGNAL_FUNC) sig_whois_not_found);
signal_remove("whois event", (SIGNAL_FUNC) event_whois);
signal_remove("whowas event", (SIGNAL_FUNC) event_whowas);
- signal_remove("server connected", (SIGNAL_FUNC) sig_connected);
g_string_free(tmpstr, TRUE);
}
diff --git a/src/irc/core/irc-servers.c b/src/irc/core/irc-servers.c
index 5591e313..a1310be1 100644
--- a/src/irc/core/irc-servers.c
+++ b/src/irc/core/irc-servers.c
@@ -33,8 +33,9 @@
#include "irc-servers-setup.h"
#include "irc-servers.h"
#include "channel-rejoin.h"
-#include "server-idle.h"
+#include "servers-idle.h"
#include "servers-reconnect.h"
+#include "servers-redirect.h"
#include "modes.h"
#include "settings.h"
@@ -304,6 +305,7 @@ static void server_cmd_timeout(IRC_SERVER_REC *server, GTimeVal *now)
/* add to rawlog without CR+LF */
cmd[len-2] = '\0';
rawlog_output(server->rawlog, cmd);
+ server_redirect_command(server, cmd);
/* remove from queue */
g_free(cmd);
@@ -506,6 +508,7 @@ void irc_servers_init(void)
irc_servers_setup_init();
irc_servers_reconnect_init();
+ servers_redirect_init();
servers_idle_init();
}
@@ -527,5 +530,6 @@ void irc_servers_deinit(void)
irc_servers_setup_deinit();
irc_servers_reconnect_deinit();
+ servers_redirect_deinit();
servers_idle_deinit();
}
diff --git a/src/irc/core/irc-servers.h b/src/irc/core/irc-servers.h
index 1add7cdd..4866091a 100644
--- a/src/irc/core/irc-servers.h
+++ b/src/irc/core/irc-servers.h
@@ -37,12 +37,16 @@ struct _IRC_SERVER_CONNECT_REC {
struct _IRC_SERVER_REC {
#include "server-rec.h"
+ /* For deciding if event should be redirected */
+ GSList *redirects;
+ void *redirect_next;
+ void *redirect_continue;
+
char *real_address; /* address the irc server gives */
char *usermode; /* The whole mode string .. */
char *userhost; /* /USERHOST <nick> - set when joined to first channel */
int channels_formed; /* channels formed in irc network */
- unsigned int nick_changing:1; /* We've sent nick change command to server */
unsigned int whois_coming:1; /* Mostly just to display away message right.. */
unsigned int whois_found:1; /* Did WHOIS return any entries? */
unsigned int whowas_found:1; /* Did WHOWAS return any entries? */
diff --git a/src/irc/core/irc.c b/src/irc/core/irc.c
index a7e58bb6..8b967d4e 100644
--- a/src/irc/core/irc.c
+++ b/src/irc/core/irc.c
@@ -62,8 +62,10 @@ void irc_send_cmd_full(IRC_SERVER_REC *server, const char *cmd,
cmd = str;
}
- if (send_now)
+ if (send_now) {
rawlog_output(server->rawlog, cmd);
+ server_redirect_command(server, cmd);
+ }
if (!raw) {
/* Add CR+LF to command */
@@ -253,39 +255,34 @@ char *event_get_params(const char *data, int count, ...)
return duprec;
}
-static void irc_server_event(IRC_SERVER_REC *server, const char *line, const char *nick, const char *address)
+static void irc_server_event(IRC_SERVER_REC *server, const char *line,
+ const char *nick, const char *address)
{
- char *event, *args, *callcmd;
- GSList *list;
+ const char *signal;
+ char *event, *args;
g_return_if_fail(line != NULL);
- /* get command.. */
+ /* split event / args */
event = g_strconcat("event ", line, NULL);
args = strchr(event+6, ' ');
if (args != NULL) *args++ = '\0'; else args = "";
while (*args == ' ') args++;
+ g_strdown(event);
- list = server_redirect_getqueue((SERVER_REC *) server, event, args);
- if (list == NULL)
- callcmd = g_strdup(event);
- else {
- /* event is redirected somewhere else.. */
- REDIRECT_REC *rec;
-
- rec = list->data;
- callcmd = g_strdup(rec->name);
- rawlog_redirect(server->rawlog, callcmd);
- server_redirect_remove_next((SERVER_REC *) server, event, list);
- }
+ /* check if event needs to be redirected */
+ signal = server_redirect_get_signal(server, event, args);
+ if (signal == NULL)
+ signal = event;
+ else
+ rawlog_redirect(server->rawlog, signal);
+ /* emit it */
current_server_event = event+6;
- g_strdown(callcmd);
- if (!signal_emit(callcmd, 4, server, args, nick, address))
+ if (!signal_emit(signal, 4, server, args, nick, address))
signal_emit_id(signal_default_event, 4, server, line, nick, address);
current_server_event = NULL;
- g_free(callcmd);
g_free(event);
}
diff --git a/src/irc/core/lag.c b/src/irc/core/lag.c
index d2c62813..8670956b 100644
--- a/src/irc/core/lag.c
+++ b/src/irc/core/lag.c
@@ -24,6 +24,7 @@
#include "settings.h"
#include "irc-servers.h"
+#include "servers-redirect.h"
typedef struct {
IRC_SERVER_REC *server;
@@ -53,6 +54,20 @@ static void lag_free(LAG_REC *rec)
g_free(rec);
}
+static void lag_send(LAG_REC *lag)
+{
+ IRC_SERVER_REC *server;
+
+ g_get_current_time(&lag->time);
+
+ server = lag->server;
+ server->lag_sent = server->lag_last_check = time(NULL);
+ server_redirect_event(server, "ping", NULL, FALSE,
+ "lag ping error",
+ "event pong", "lag pong", NULL);
+ irc_send_cmdv(server, "PING %s", server->real_address);
+}
+
static void lag_get(IRC_SERVER_REC *server)
{
LAG_REC *lag;
@@ -61,57 +76,42 @@ static void lag_get(IRC_SERVER_REC *server)
/* nick changes may fail this check, so we should never do this
while there's nick change request waiting for reply in server.. */
- if (server->nick_changing)
- return;
-
lag = g_new0(LAG_REC, 1);
lags = g_slist_append(lags, lag);
lag->server = server;
- g_get_current_time(&lag->time);
+ lag_send(lag);
+}
- if (server->lag_sent == 0)
- server->lag_sent = time(NULL);
- server->lag_last_check = time(NULL);
+/* we didn't receive PONG for some reason .. try again */
+static void lag_ping_error(IRC_SERVER_REC *server)
+{
+ LAG_REC *lag;
- irc_send_cmdv(server, "NOTICE %s :\001IRSSILAG %ld %ld\001",
- server->nick, lag->time.tv_sec, lag->time.tv_usec);
+ lag = lag_find(server);
+ if (lag != NULL)
+ lag_send(lag);
}
-/* we use "ctcp reply" signal here, because "ctcp reply irssilag" can be
- ignored with /IGNORE * CTCPS */
-static void sig_irssilag(IRC_SERVER_REC *server, const char *data,
- const char *nick, const char *addr,
- const char *target)
+static void lag_event_pong(IRC_SERVER_REC *server, const char *data,
+ const char *nick, const char *addr)
{
- GTimeVal now, sent;
+ GTimeVal now;
LAG_REC *lag;
g_return_if_fail(data != NULL);
- if (strncmp(data, "IRSSILAG ", 9) != 0)
- return;
- data += 9;
-
lag = lag_find(server);
if (lag == NULL) {
/* not expecting lag reply.. */
return;
}
- if (g_strcasecmp(nick, server->nick) != 0) {
- /* we didn't sent this - not a lag notice */
- return;
- }
-
- /* OK, it is a lag notice. */
server->lag_sent = 0;
- if (sscanf(data, "%ld %ld", &sent.tv_sec, &sent.tv_usec) == 2) {
- g_get_current_time(&now);
- server->lag = (int) get_timeval_diff(&now, &sent);
- signal_emit("server lag", 1, server);
- }
+ g_get_current_time(&now);
+ server->lag = (int) get_timeval_diff(&now, &lag->time);
+ signal_emit("server lag", 1, server);
lag_free(lag);
}
@@ -154,11 +154,6 @@ static int sig_check_lag(void)
return 1;
}
-static void sig_empty(void)
-{
- /* don't print the "CTCP IRSSILAG reply .." text */
-}
-
void lag_init(void)
{
settings_add_int("misc", "lag_check_time", 30);
@@ -166,8 +161,8 @@ void lag_init(void)
lags = NULL;
timeout_tag = g_timeout_add(1000, (GSourceFunc) sig_check_lag, NULL);
- signal_add("ctcp reply", (SIGNAL_FUNC) sig_irssilag);
- signal_add("ctcp reply irssilag", (SIGNAL_FUNC) sig_empty);
+ signal_add_first("lag pong", (SIGNAL_FUNC) lag_event_pong);
+ signal_add("lag ping error", (SIGNAL_FUNC) lag_ping_error);
}
void lag_deinit(void)
@@ -175,6 +170,6 @@ void lag_deinit(void)
g_source_remove(timeout_tag);
while (lags != NULL)
lag_free(lags->data);
- signal_remove("ctcp reply", (SIGNAL_FUNC) sig_irssilag);
- signal_remove("ctcp reply irssilag", (SIGNAL_FUNC) sig_empty);
+ signal_remove("lag pong", (SIGNAL_FUNC) lag_event_pong);
+ signal_remove("lag ping error", (SIGNAL_FUNC) lag_ping_error);
}
diff --git a/src/irc/core/server-idle.h b/src/irc/core/server-idle.h
deleted file mode 100644
index 8d89f8f5..00000000
--- a/src/irc/core/server-idle.h
+++ /dev/null
@@ -1,22 +0,0 @@
-#ifndef __SERVER_IDLE_H
-#define __SERVER_IDLE_H
-
-/* Add new idle command to queue */
-int server_idle_add(IRC_SERVER_REC *server, const char *cmd, const char *arg, int last, ...);
-
-/* Add new idle command to first of queue */
-int server_idle_add_first(IRC_SERVER_REC *server, const char *cmd, const char *arg, int last, ...);
-
-/* Add new idle command to specified position of queue */
-int server_idle_insert(IRC_SERVER_REC *server, const char *cmd, const char *arg, int tag, int last, ...);
-
-/* Check if record is still in queue */
-int server_idle_find(IRC_SERVER_REC *server, int tag);
-
-/* Remove record from idle queue */
-int server_idle_remove(IRC_SERVER_REC *server, int tag);
-
-void servers_idle_init(void);
-void servers_idle_deinit(void);
-
-#endif
diff --git a/src/irc/core/server-idle.c b/src/irc/core/servers-idle.c
index 9443fdf7..fb979dab 100644
--- a/src/irc/core/server-idle.c
+++ b/src/irc/core/servers-idle.c
@@ -22,49 +22,54 @@
#include "signals.h"
#include "irc-servers.h"
-#include "server-idle.h"
+#include "servers-idle.h"
#include "servers-redirect.h"
typedef struct {
- char *event;
- char *signal;
- int argpos;
-} REDIRECT_IDLE_REC;
-
-typedef struct {
char *cmd;
char *arg;
int tag;
- int last;
+ char *redirect_cmd;
+ int remote;
+ char *failure_signal;
GSList *redirects;
} SERVER_IDLE_REC;
static int idle_tag, idlepos;
/* Add new idle command to queue */
-static SERVER_IDLE_REC *server_idle_create(const char *cmd, const char *arg, int last, va_list args)
+static SERVER_IDLE_REC *
+server_idle_create(const char *cmd, const char *redirect_cmd, const char *arg,
+ int remote, const char *failure_signal, va_list va)
{
- REDIRECT_IDLE_REC *rrec;
SERVER_IDLE_REC *rec;
- char *event;
+ const char *event, *signal;
g_return_val_if_fail(cmd != NULL, FALSE);
rec = g_new0(SERVER_IDLE_REC, 1);
-
- rec->tag = ++idlepos;
- rec->arg = arg == NULL ? NULL : g_strdup(arg);
rec->cmd = g_strdup(cmd);
- rec->last = last;
+ rec->arg = g_strdup(arg);
+ rec->tag = ++idlepos;
- while ((event = va_arg(args, char *)) != NULL) {
- rrec = g_new(REDIRECT_IDLE_REC, 1);
- rec->redirects = g_slist_append(rec->redirects, rrec);
+ rec->redirect_cmd = g_strdup(redirect_cmd);
+ rec->remote = remote;
+ rec->failure_signal = g_strdup(failure_signal);
+
+ while ((event = va_arg(va, const char *)) != NULL) {
+ signal = va_arg(va, const char *);
+ if (signal == NULL) {
+ g_warning("server_idle_create(%s): "
+ "signal not specified for event",
+ redirect_cmd);
+ break;
+ }
- rrec->event = g_strdup(event);
- rrec->signal = g_strdup(va_arg(args, char *));
- rrec->argpos = va_arg(args, int);
+ rec->redirects =
+ g_slist_append(rec->redirects, g_strdup(event));
+ rec->redirects =
+ g_slist_append(rec->redirects, g_strdup(signal));
}
return rec;
@@ -87,78 +92,82 @@ static SERVER_IDLE_REC *server_idle_find_rec(IRC_SERVER_REC *server, int tag)
}
/* Add new idle command to queue */
-int server_idle_add(IRC_SERVER_REC *server, const char *cmd, const char *arg, int last, ...)
+int server_idle_add_redir(IRC_SERVER_REC *server, const char *cmd,
+ const char *redirect_cmd, const char *arg,
+ int remote, const char *failure_signal, ...)
{
- va_list args;
+ va_list va;
SERVER_IDLE_REC *rec;
g_return_val_if_fail(server != NULL, -1);
- va_start(args, last);
- rec = server_idle_create(cmd, arg, last, args);
+ va_start(va, failure_signal);
+ rec = server_idle_create(cmd, redirect_cmd, arg, remote,
+ failure_signal, va);
server->idles = g_slist_append(server->idles, rec);
- va_end(args);
+ va_end(va);
return rec->tag;
}
/* Add new idle command to first of queue */
-int server_idle_add_first(IRC_SERVER_REC *server, const char *cmd, const char *arg, int last, ...)
+int server_idle_add_first_redir(IRC_SERVER_REC *server, const char *cmd,
+ const char *redirect_cmd, const char *arg,
+ int remote, const char *failure_signal, ...)
{
- va_list args;
+ va_list va;
SERVER_IDLE_REC *rec;
g_return_val_if_fail(server != NULL, -1);
- va_start(args, last);
- rec = server_idle_create(cmd, arg, last, args);
+ va_start(va, failure_signal);
+ rec = server_idle_create(cmd, redirect_cmd, arg, remote,
+ failure_signal, va);
server->idles = g_slist_prepend(server->idles, rec);
- va_end(args);
+ va_end(va);
return rec->tag;
}
/* Add new idle command to specified position of queue */
-int server_idle_insert(IRC_SERVER_REC *server, const char *cmd, const char *arg, int tag, int last, ...)
+int server_idle_insert_redir(IRC_SERVER_REC *server, const char *cmd, int tag,
+ const char *redirect_cmd, const char *arg,
+ int remote, const char *failure_signal, ...)
{
- va_list args;
+ va_list va;
SERVER_IDLE_REC *rec;
int pos;
g_return_val_if_fail(server != NULL, -1);
- va_start(args, last);
+ va_start(va, failure_signal);
/* find the position of tag in idle list */
rec = server_idle_find_rec(server, tag);
pos = g_slist_index(server->idles, rec);
- rec = server_idle_create(cmd, arg, last, args);
+ rec = server_idle_create(cmd, redirect_cmd, arg, remote,
+ failure_signal, va);
server->idles = pos < 0 ?
g_slist_append(server->idles, rec) :
g_slist_insert(server->idles, rec, pos);
- va_end(args);
+ va_end(va);
+
return rec->tag;
}
static void server_idle_destroy(IRC_SERVER_REC *server, SERVER_IDLE_REC *rec)
{
- GSList *tmp;
-
g_return_if_fail(server != NULL);
server->idles = g_slist_remove(server->idles, rec);
- for (tmp = rec->redirects; tmp != NULL; tmp = tmp->next) {
- REDIRECT_IDLE_REC *rec = tmp->data;
-
- g_free(rec->event);
- g_free(rec->signal);
- g_free(rec);
- }
+ g_slist_foreach(rec->redirects, (GFunc) g_free, NULL);
g_slist_free(rec->redirects);
g_free_not_null(rec->arg);
+ g_free_not_null(rec->redirect_cmd);
+ g_free_not_null(rec->failure_signal);
g_free(rec->cmd);
g_free(rec);
}
@@ -188,26 +197,20 @@ int server_idle_remove(IRC_SERVER_REC *server, int tag)
static void server_idle_next(IRC_SERVER_REC *server)
{
SERVER_IDLE_REC *rec;
- GSList *tmp;
- int group;
g_return_if_fail(server != NULL);
- if (server->idles == NULL) return;
+ if (server->idles == NULL)
+ return;
rec = server->idles->data;
/* Send command */
- irc_send_cmd(server, rec->cmd);
-
- /* Add server redirections */
- group = 0;
- for (tmp = rec->redirects; tmp != NULL; tmp = tmp->next) {
- REDIRECT_IDLE_REC *rrec = tmp->data;
-
- group = server_redirect_single_event((SERVER_REC *) server, rec->arg, rec->last > 0,
- group, rrec->event, rrec->signal, rrec->argpos);
- if (rec->last > 0) rec->last--;
+ if (rec->redirect_cmd != NULL) {
+ server_redirect_event_list(server, rec->redirect_cmd, rec->arg,
+ rec->remote, rec->failure_signal,
+ rec->redirects);
}
+ irc_send_cmd(server, rec->cmd);
server_idle_destroy(server, rec);
}
diff --git a/src/irc/core/servers-idle.h b/src/irc/core/servers-idle.h
new file mode 100644
index 00000000..12b67a96
--- /dev/null
+++ b/src/irc/core/servers-idle.h
@@ -0,0 +1,34 @@
+#ifndef __SERVERS_IDLE_H
+#define __SERVERS_IDLE_H
+
+/* Add new idle command to queue */
+int server_idle_add_redir(IRC_SERVER_REC *server, const char *cmd,
+ const char *redirect_cmd, const char *arg,
+ int remote, const char *failure_signal, ...);
+#define server_idle_add(server, cmd) \
+ server_idle_add_redir(server, cmd, NULL, NULL, 0, NULL, NULL)
+
+/* Add new idle command to first of queue */
+int server_idle_add_first_redir(IRC_SERVER_REC *server, const char *cmd,
+ const char *redirect_cmd, const char *arg,
+ int remote, const char *failure_signal, ...);
+#define server_idle_add_first(server, cmd) \
+ server_idle_add_first_redir(server, cmd, NULL, NULL, 0, NULL, NULL)
+
+/* Add new idle command to specified position of queue */
+int server_idle_insert_redir(IRC_SERVER_REC *server, const char *cmd, int tag,
+ const char *redirect_cmd, const char *arg,
+ int remote, const char *failure_signal, ...);
+#define server_idle_insert(server, cmd, tag) \
+ server_idle_insert_redir(server, cmd, tag, NULL, NULL, 0, NULL, NULL)
+
+/* Check if record is still in queue */
+int server_idle_find(IRC_SERVER_REC *server, int tag);
+
+/* Remove record from idle queue */
+int server_idle_remove(IRC_SERVER_REC *server, int tag);
+
+void servers_idle_init(void);
+void servers_idle_deinit(void);
+
+#endif
diff --git a/src/irc/core/servers-redirect.c b/src/irc/core/servers-redirect.c
new file mode 100644
index 00000000..814c6b27
--- /dev/null
+++ b/src/irc/core/servers-redirect.c
@@ -0,0 +1,569 @@
+/*
+ server-redirect.c : irssi
+
+ Copyright (C) 1999-2000 Timo Sirainen
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+*/
+
+#include "module.h"
+#include "signals.h"
+#include "misc.h"
+
+#include "irc-servers.h"
+#include "servers-redirect.h"
+
+typedef struct {
+ int refcount;
+
+ int remote;
+ int timeout;
+ GSList *start, *stop; /* char *event, int argpos, ... */
+} REDIRECT_CMD_REC;
+
+typedef struct {
+ REDIRECT_CMD_REC *cmd;
+ time_t created;
+ int destroyed;
+
+ char *arg;
+ int remote;
+ char *failure_signal, *default_signal;
+ GSList *signals; /* event, signal, ... */
+} REDIRECT_REC;
+
+static GHashTable *command_redirects; /* "command xxx" : REDIRECT_CMD_REC* */
+
+/* Find redirection command record for specified command line. */
+static REDIRECT_CMD_REC *redirect_cmd_find(const char *command)
+{
+ REDIRECT_CMD_REC *rec;
+ const char *p;
+ char *cmd;
+
+ p = strchr(command, ' ');
+ if (p == NULL)
+ rec = g_hash_table_lookup(command_redirects, command);
+ else {
+ cmd = g_strndup(command, (int) (p-command));
+ rec = g_hash_table_lookup(command_redirects, cmd);
+ g_free(cmd);
+ }
+ return rec;
+}
+
+static void redirect_cmd_destroy(REDIRECT_CMD_REC *rec)
+{
+ GSList *tmp;
+
+ for (tmp = rec->start; tmp != NULL; tmp = tmp->next->next)
+ g_free(tmp->data);
+ for (tmp = rec->stop; tmp != NULL; tmp = tmp->next->next)
+ g_free(tmp->data);
+ g_slist_free(rec->start);
+ g_slist_free(rec->stop);
+ g_free(rec);
+}
+
+static void redirect_cmd_ref(REDIRECT_CMD_REC *rec)
+{
+ rec->refcount++;
+}
+
+static void redirect_cmd_unref(REDIRECT_CMD_REC *rec)
+{
+ if (--rec->refcount <= 0)
+ redirect_cmd_destroy(rec);
+}
+
+static void redirect_destroy(REDIRECT_REC *rec)
+{
+ redirect_cmd_unref(rec->cmd);
+
+ g_free_not_null(rec->arg);
+ g_free_not_null(rec->failure_signal);
+ g_free_not_null(rec->default_signal);
+ g_slist_foreach(rec->signals, (GFunc) g_free, NULL);
+ g_slist_free(rec->signals);
+ g_free(rec);
+}
+
+void server_redirect_register(const char *command,
+ int remote, int timeout, ...)
+{
+ va_list va;
+ GSList *start, *stop, **list;
+ const char *event;
+ int argpos;
+
+ va_start(va, timeout);
+ start = stop = NULL; list = &start;
+ for (;;) {
+ event = va_arg(va, const char *);
+ if (event == NULL) {
+ if (list == &stop)
+ break;
+ list = &stop;
+ continue;
+ }
+
+ argpos = va_arg(va, int);
+ *list = g_slist_append(*list, g_strdup(event));
+ *list = g_slist_append(*list, GINT_TO_POINTER(argpos));
+ }
+
+ va_end(va);
+
+ server_redirect_register_list(command, remote, timeout, start, stop);
+}
+
+void server_redirect_register_list(const char *command,
+ int remote, int timeout,
+ GSList *start, GSList *stop)
+{
+ REDIRECT_CMD_REC *rec;
+ gpointer key, value;
+
+ g_return_if_fail(command != NULL);
+ g_return_if_fail(stop != NULL);
+
+ if (g_hash_table_lookup_extended(command_redirects, command,
+ &key, &value)) {
+ /* Already registered - might have changed so destroy
+ the old one */
+ g_hash_table_remove(command_redirects, command);
+ redirect_cmd_unref(value);
+ g_free(key);
+ }
+
+ rec = g_new0(REDIRECT_CMD_REC, 1);
+ redirect_cmd_ref(rec);
+ rec->remote = remote;
+ rec->timeout = timeout;
+ rec->start = start;
+ rec->stop = stop;
+ g_hash_table_insert(command_redirects, g_strdup(command), rec);
+}
+
+void server_redirect_event(IRC_SERVER_REC *server, const char *command,
+ const char *arg, int remote,
+ const char *failure_signal, ...)
+{
+ GSList *signals;
+ const char *event, *signal;
+ va_list va;
+
+ va_start(va, failure_signal);
+ signals = NULL;
+ while ((event = va_arg(va, const char *)) != NULL) {
+ signal = va_arg(va, const char *);
+ if (signal == NULL) {
+ g_warning("server_redirect_event(%s): "
+ "signal not specified for event", command);
+ break;
+ }
+
+ signals = g_slist_append(signals, g_strdup(event));
+ signals = g_slist_append(signals, g_strdup(signal));
+ }
+
+ va_end(va);
+
+ server_redirect_event_list(server, command, arg, remote,
+ failure_signal, signals);
+}
+
+void server_redirect_event_list(IRC_SERVER_REC *server, const char *command,
+ const char *arg, int remote,
+ const char *failure_signal, GSList *signals)
+{
+ REDIRECT_CMD_REC *cmdrec;
+ REDIRECT_REC *rec;
+ GSList *default_signal;
+ char *default_signal_key;
+
+ g_return_if_fail(IS_IRC_SERVER(server));
+ g_return_if_fail(command != NULL);
+ g_return_if_fail((g_slist_length(signals) & 1) == 0);
+
+ if (server->redirect_next != NULL) {
+ redirect_destroy(server->redirect_next);
+ server->redirect_next = NULL;
+ }
+
+ cmdrec = g_hash_table_lookup(command_redirects, command);
+ if (cmdrec == NULL) {
+ g_warning("Unknown redirection command: %s", command);
+ return;
+ }
+
+ redirect_cmd_ref(cmdrec);
+
+ rec = g_new0(REDIRECT_REC, 1);
+ rec->created = time(NULL);
+ rec->cmd = cmdrec;
+ rec->arg = g_strdup(arg);
+ rec->remote = remote != -1 ? remote : cmdrec->remote;
+ rec->failure_signal = g_strdup(failure_signal);
+
+ default_signal = gslist_find_string(signals, "");
+ if (default_signal != NULL) {
+ default_signal_key = default_signal->data;
+ rec->default_signal = default_signal->next->data;
+
+ signals = g_slist_remove(signals, default_signal_key);
+ signals = g_slist_remove(signals, rec->default_signal);
+ g_free(default_signal_key);
+ }
+ rec->signals = signals;
+
+ server->redirect_next = rec;
+}
+
+void server_redirect_command(IRC_SERVER_REC *server, const char *command)
+{
+ REDIRECT_CMD_REC *cmdrec;
+ REDIRECT_REC *rec;
+
+ g_return_if_fail(IS_IRC_SERVER(server));
+ g_return_if_fail(command != NULL);
+
+ if (server->redirect_next != NULL) {
+ rec = server->redirect_next;
+ server->redirect_next = NULL;
+ } else {
+ cmdrec = redirect_cmd_find(command);
+ if (cmdrec == NULL)
+ return;
+
+ /* no redirection wanted, but still register the command
+ so future redirections wont get messed up. */
+ redirect_cmd_ref(cmdrec);
+
+ rec = g_new0(REDIRECT_REC, 1);
+ rec->created = time(NULL);
+ rec->cmd = cmdrec;
+ rec->remote = cmdrec->remote;
+ }
+
+ server->redirects = g_slist_append(server->redirects, rec);
+}
+
+static int redirect_args_match(const char *event_args,
+ const char *arg, int pos)
+{
+ const char *start;
+
+ if (pos == -1)
+ return TRUE;
+
+ /* skip to the start of the wanted argument */
+ while (pos > 0 && *event_args != '\0') {
+ while (*event_args != ' ' && *event_args != '\0') event_args++;
+ while (*event_args == ' ') event_args++;
+ pos--;
+ }
+
+ /* now compare the arguments */
+ start = event_args;
+ while (*arg != '\0') {
+ while (*arg != '\0' && *arg != ' ' && *event_args != '\0') {
+ if (*arg != *event_args)
+ break;
+ arg++; event_args++;
+ }
+
+ if ((*arg == '\0' || *arg == ' ') &&
+ (*event_args == '\0' || *event_args == ' '))
+ return TRUE;
+
+ /* compare the next argument */
+ while (*arg != ' ' && *arg != '\0') arg++;
+ while (*arg == ' ') arg++;
+
+ event_args = start;
+ }
+
+ return FALSE;
+}
+
+static GSList *redirect_cmd_list_find(GSList *list, const char *event)
+{
+ while (list != NULL) {
+ const char *str = list->data;
+
+ if (strcmp(str, event) == 0)
+ break;
+ list = list->next->next;
+ }
+
+ return list;
+}
+
+static const char *redirect_match(REDIRECT_REC *redirect, const char *event,
+ const char *args, int *match_stop)
+{
+ GSList *tmp, *cmdpos;
+ int stop_signal;
+
+ for (tmp = redirect->signals; tmp != NULL; tmp = tmp->next->next) {
+ if (strcmp(tmp->data, event) != 0)
+ continue;
+
+ /* find the argument position */
+ cmdpos = redirect_cmd_list_find(redirect->cmd->start, event);
+ if (cmdpos != NULL)
+ stop_signal = FALSE;
+ else {
+ cmdpos = redirect_cmd_list_find(redirect->cmd->stop,
+ event);
+ stop_signal = cmdpos != NULL;
+ }
+
+ /* check that arguments match */
+ if (args != NULL && redirect->arg != NULL && cmdpos != NULL &&
+ !redirect_args_match(args, redirect->arg,
+ GPOINTER_TO_INT(cmdpos->next->data)))
+ continue;
+
+ *match_stop = stop_signal;
+ return tmp->next->data;
+ }
+
+ *match_stop = redirect_cmd_list_find(redirect->cmd->stop,
+ event) != NULL;
+ return NULL;
+}
+
+static REDIRECT_REC *redirect_find(IRC_SERVER_REC *server, const char *event,
+ const char *args, const char **signal,
+ int *match_stop)
+{
+ REDIRECT_REC *redirect;
+ GSList *tmp, *next;
+ time_t now;
+
+ /* find the redirection */
+ *signal = NULL; redirect = NULL;
+ for (tmp = server->redirects; tmp != NULL; tmp = tmp->next) {
+ REDIRECT_REC *rec = tmp->data;
+
+ *signal = redirect_match(rec, event, args, match_stop);
+ if (*signal != NULL) {
+ redirect = rec;
+ break;
+ }
+ }
+
+ /* remove the destroyed, non-remote and timeouted remote
+ redirections that should have happened before this redirection */
+ now = time(NULL);
+ for (tmp = server->redirects; tmp != NULL; tmp = next) {
+ REDIRECT_REC *rec = tmp->data;
+
+ if (rec == redirect)
+ break;
+
+ next = tmp->next;
+ if (rec->destroyed ||
+ (rec->remote && (now-rec->created) > rec->cmd->timeout) ||
+ (redirect != NULL && !rec->remote)) {
+ server->redirects =
+ g_slist_remove(server->redirects, rec);
+ if (!rec->destroyed && rec->failure_signal != NULL) {
+ /* emit the failure signal */
+ signal_emit(rec->failure_signal, 1, server);
+ }
+ redirect_destroy(rec);
+ }
+ }
+
+ return redirect;
+}
+
+const char *server_redirect_get_signal(IRC_SERVER_REC *server,
+ const char *event,
+ const char *args)
+{
+ REDIRECT_REC *redirect;
+ const char *signal;
+ int match_stop;
+
+ if (server->redirects == NULL)
+ return NULL;
+
+ if (server->redirect_continue == NULL) {
+ /* find the redirection */
+ redirect = redirect_find(server, event, args,
+ &signal, &match_stop);
+ } else {
+ /* redirection is already started, now we'll just need to
+ keep redirecting until stop-event is found. */
+ redirect = server->redirect_continue;
+ signal = redirect_match(redirect, event, NULL, &match_stop);
+ if (signal == NULL) {
+ /* unknown event - redirect to the default signal.
+ FIXME: if stop event isn't properly got, this
+ could break everything. Add some checks that if
+ we get eg. 10 different unknown events after this,
+ or if one of them matches to another redirection,
+ abort this. */
+ signal = redirect->default_signal;
+ }
+ }
+
+ if (!match_stop || redirect == NULL)
+ server->redirect_continue = redirect;
+ else {
+ /* stop event - remove this redirection next time this
+ function is called (can't destroy now or our return
+ value would be corrupted) */
+ redirect->destroyed = TRUE;
+ server->redirect_continue = NULL;
+ }
+
+ return signal;
+}
+
+static void sig_disconnected(IRC_SERVER_REC *server)
+{
+ if (!IS_IRC_SERVER(server))
+ return;
+
+ g_slist_foreach(server->redirects, (GFunc) redirect_destroy, NULL);
+ g_slist_free(server->redirects);
+
+ if (server->redirect_next != NULL)
+ redirect_destroy(server->redirect_next);
+}
+
+static void cmd_redirect_destroy(char *key, REDIRECT_CMD_REC *cmd)
+{
+ g_free(key);
+ redirect_cmd_unref(cmd);
+}
+
+void servers_redirect_init(void)
+{
+ command_redirects = g_hash_table_new((GHashFunc) g_str_hash, (GCompareFunc) g_str_equal);
+
+ /* WHOIS - register as remote command by default
+ with a timeout of one minute */
+ server_redirect_register("whois", TRUE, 60,
+ "event 311", 1, /* Begins the WHOIS */
+ "event 401", 1, /* No such nick */
+ NULL,
+ "event 318", 1, /* End of WHOIS */
+ "event 402", 1, /* No such server */
+ NULL);
+
+ /* WHOWAS */
+ server_redirect_register("whowas", FALSE, 0,
+ "event 314", 1, /* Begins the WHOWAS */
+ "event 406", 1, /* There was no such nick */
+ NULL,
+ "event 369", 1, /* End of WHOWAS */
+ NULL);
+
+ /* WHO */
+ server_redirect_register("who", FALSE, 0,
+ "event 352", 1, /* Begins the WHO */
+ "event 401", 1, /* No such nick/channel */
+ NULL,
+ "event 315", 1, /* End of WHO */
+ "event 403", 1, /* no such channel */
+ NULL);
+
+ /* LIST */
+ server_redirect_register("list", FALSE, 0,
+ "event 321", 1, /* Begins the LIST */
+ NULL,
+ "event 323", 1, /* End of LIST */
+ NULL);
+
+ /* ISON */
+ server_redirect_register("ison", FALSE, 0,
+ NULL,
+ "event 303", 1, /* ISON */
+ NULL);
+
+ /* USERHOST */
+ server_redirect_register("userhost", FALSE, 0,
+ "event 401", 1, /* no such nick */
+ NULL,
+ "event 302", 1, /* Userhost */
+ "event 461", 1, /* Not enough parameters */
+ NULL);
+
+ /* MODE #channel */
+ server_redirect_register("mode channel", FALSE, 0,
+ NULL,
+ "event 324", 1, /* MODE-reply */
+ "event 403", 1, /* no such channel */
+ "event 442", 1, /* "you're not on that channel" */
+ "event 479", 1, /* "Cannot join channel (illegal name)" IMHO this is not a logical reply from server. */
+ NULL);
+
+ /* MODE #channel b */
+ server_redirect_register("mode b", FALSE, 0,
+ "event 367", 1,
+ NULL,
+ "event 368", 1, /* End of Channel ban List */
+ "event 403", 1, /* no such channel */
+ "event 442", 1, /* "you're not on that channel" */
+ "event 479", 1, /* "Cannot join channel (illegal name)" IMHO this is not a logical reply from server. */
+ NULL);
+
+ /* MODE #channel e */
+ server_redirect_register("mode e", FALSE, 0,
+ "event 348", 1,
+ NULL,
+ "event 349", 1, /* End of ban exceptions */
+ "event 482", 1, /* not channel operator - OPN's ircd doesn't want non-ops to see ban exceptions */
+ "event 403", 1, /* no such channel */
+ "event 442", 1, /* "you're not on that channel" */
+ "event 479", 1, /* "Cannot join channel (illegal name)" IMHO this is not a logical reply from server. */
+ "event 472", -1, /* unknown mode (you should check e-mode's existance from 004 event instead of relying on this) */
+ NULL);
+
+ /* MODE #channel I */
+ server_redirect_register("mode e", FALSE, 0,
+ "event 346", 1,
+ NULL,
+ "event 347", 1, /* End of invite list */
+ "event 403", 1, /* no such channel */
+ "event 442", 1, /* "you're not on that channel" */
+ "event 479", 1, /* "Cannot join channel (illegal name)" IMHO this is not a logical reply from server. */
+ "event 472", -1, /* unknown mode (you should check I-mode's existance from 004 event instead of relying on this) */
+ NULL);
+
+ /* PING */
+ server_redirect_register("ping", TRUE, 60,
+ NULL,
+ "event 402", -1, /* no such server */
+ "event pong", -1, /* PONG */
+ NULL);
+
+ signal_add("server disconnected", (SIGNAL_FUNC) sig_disconnected);
+}
+
+void servers_redirect_deinit(void)
+{
+ g_hash_table_foreach(command_redirects,
+ (GHFunc) cmd_redirect_destroy, NULL);
+ g_hash_table_destroy(command_redirects);
+
+ signal_remove("server disconnected", (SIGNAL_FUNC) sig_disconnected);
+}
diff --git a/src/irc/core/servers-redirect.h b/src/irc/core/servers-redirect.h
new file mode 100644
index 00000000..1cb6dac4
--- /dev/null
+++ b/src/irc/core/servers-redirect.h
@@ -0,0 +1,66 @@
+#ifndef __SERVERS_REDIRECT_H
+#define __SERVERS_REDIRECT_H
+
+/* Register new redirection command.
+
+ remote - Specifies if the command is by default a remote command
+ (eg. sent to another server). server_redirect_event() may override this.
+
+ timeout - If remote is TRUE, specifies how many seconds to wait for
+ reply before aborting.
+
+ ... - char *start, int argpos, char *start, int argpos, ..., NULL,
+ char *stop, int argpos, char *stop, int argpos, ..., NULL
+ List of events that start and stop this redirection.
+ Start event list may be just NULL, but there must be at least one
+ stop event. `argpos' specifies the word number in event string which
+ is compared to wanted argument, -1 = don't compare, TRUE always. */
+void server_redirect_register(const char *command,
+ int remote, int timeout, ...);
+/* start/stop lists shouldn't be free'd after, and their strings
+ should be dynamically allocated */
+void server_redirect_register_list(const char *command,
+ int remote, int timeout,
+ GSList *start, GSList *stop);
+
+/* Specify that the next command sent to server will be redirected.
+ NOTE: This command MUST be called before irc_send_cmd().
+
+ command - Specifies the registered command that should be used for this
+ redirection.
+
+ arg - The argument to be compared in event strings. You can give multiple
+ arguments separated with space.
+
+ remote - Specifies if the command is a remote command, -1 = use default.
+
+ failure_signal - If irssi can't find the stop signal for the redirection,
+ this signal is called.
+
+ ... - char *event, char *redirect_signal, ..., NULL
+ If the `event' is "", all the events belonging to the redirection but not
+ specified here, will be sent there. */
+void server_redirect_event(IRC_SERVER_REC *server, const char *command,
+ const char *arg, int remote,
+ const char *failure_signal, ...);
+/* Signals list shouldn't be free'd after, and it's strings should be
+ dynamically allocated */
+void server_redirect_event_list(IRC_SERVER_REC *server, const char *command,
+ const char *arg, int remote,
+ const char *failure_signal, GSList *signals);
+
+/* INTERNAL: */
+
+/* irc_send_cmd() calls this to make sure redirecting knows
+ what's sent to server */
+void server_redirect_command(IRC_SERVER_REC *server, const char *command);
+/* Returns the redirection signal for specified event.
+ This is the function that contains the real redirecting logic. */
+const char *server_redirect_get_signal(IRC_SERVER_REC *server,
+ const char *event,
+ const char *args);
+
+void servers_redirect_init(void);
+void servers_redirect_deinit(void);
+
+#endif