2001-11-11 13:59:19 -05:00
|
|
|
/*
|
|
|
|
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"
|
2001-11-14 18:23:00 -05:00
|
|
|
#include "rawlog.h"
|
2001-11-11 13:59:19 -05:00
|
|
|
|
|
|
|
#include "irc-servers.h"
|
|
|
|
#include "servers-redirect.h"
|
|
|
|
|
2001-11-11 17:44:47 -05:00
|
|
|
#define DEFAULT_REDIRECT_TIMEOUT 60
|
|
|
|
|
2001-12-01 15:17:57 -05:00
|
|
|
/* Allow one non-expected redirections to come before the expected one
|
2001-11-16 18:42:09 -05:00
|
|
|
before aborting it. Some IRC bouncers/proxies reply to eg. PINGs
|
|
|
|
immediately. */
|
2001-12-01 15:17:57 -05:00
|
|
|
#define MAX_FAILURE_COUNT 1
|
2001-11-16 18:42:09 -05:00
|
|
|
|
2001-11-11 13:59:19 -05:00
|
|
|
typedef struct {
|
2001-11-14 18:23:00 -05:00
|
|
|
char *name;
|
2001-11-11 13:59:19 -05:00
|
|
|
int refcount;
|
|
|
|
|
|
|
|
int remote;
|
|
|
|
int timeout;
|
2001-11-16 19:07:24 -05:00
|
|
|
GSList *start, *stop, *opt; /* char *event, int argpos, ... */
|
2001-11-11 13:59:19 -05:00
|
|
|
} REDIRECT_CMD_REC;
|
|
|
|
|
2001-11-11 15:20:43 -05:00
|
|
|
struct _REDIRECT_REC {
|
2001-11-11 13:59:19 -05:00
|
|
|
REDIRECT_CMD_REC *cmd;
|
|
|
|
time_t created;
|
2001-11-18 10:07:03 -05:00
|
|
|
int failures;
|
|
|
|
|
|
|
|
unsigned int destroyed:1;
|
|
|
|
unsigned int aborted:1;
|
|
|
|
unsigned int remote:1;
|
2001-12-07 16:41:46 -05:00
|
|
|
unsigned int first_signal_sent:1;
|
2001-11-11 13:59:19 -05:00
|
|
|
|
|
|
|
char *arg;
|
2001-11-12 17:15:04 -05:00
|
|
|
int count;
|
2001-12-07 16:41:46 -05:00
|
|
|
char *failure_signal, *default_signal, *first_signal, *last_signal;
|
2001-11-11 13:59:19 -05:00
|
|
|
GSList *signals; /* event, signal, ... */
|
2001-11-11 15:20:43 -05:00
|
|
|
};
|
2001-11-11 13:59:19 -05:00
|
|
|
|
|
|
|
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);
|
2001-11-16 19:07:24 -05:00
|
|
|
for (tmp = rec->opt; tmp != NULL; tmp = tmp->next->next)
|
|
|
|
g_free(tmp->data);
|
2001-11-11 13:59:19 -05:00
|
|
|
g_slist_free(rec->start);
|
|
|
|
g_slist_free(rec->stop);
|
2001-11-16 19:07:24 -05:00
|
|
|
g_slist_free(rec->opt);
|
2001-11-14 18:23:00 -05:00
|
|
|
g_free(rec->name);
|
|
|
|
g_free(rec);
|
2001-11-11 13:59:19 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
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);
|
|
|
|
}
|
|
|
|
|
2001-11-11 15:20:43 -05:00
|
|
|
void server_redirect_destroy(REDIRECT_REC *rec)
|
2001-11-11 13:59:19 -05:00
|
|
|
{
|
|
|
|
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;
|
2001-11-16 19:07:24 -05:00
|
|
|
GSList *start, *stop, *opt, **list;
|
2001-11-11 13:59:19 -05:00
|
|
|
const char *event;
|
|
|
|
int argpos;
|
|
|
|
|
|
|
|
va_start(va, timeout);
|
2001-11-16 19:07:24 -05:00
|
|
|
start = stop = opt = NULL; list = &start;
|
2001-11-11 13:59:19 -05:00
|
|
|
for (;;) {
|
|
|
|
event = va_arg(va, const char *);
|
|
|
|
if (event == NULL) {
|
2001-11-16 19:07:24 -05:00
|
|
|
if (list == &start)
|
|
|
|
list = &stop;
|
|
|
|
else if (list == &stop)
|
|
|
|
list = &opt;
|
|
|
|
else
|
|
|
|
break;
|
2001-11-11 13:59:19 -05:00
|
|
|
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);
|
|
|
|
|
2001-11-16 19:07:24 -05:00
|
|
|
server_redirect_register_list(command, remote, timeout,
|
|
|
|
start, stop, opt);
|
2001-11-11 13:59:19 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
void server_redirect_register_list(const char *command,
|
|
|
|
int remote, int timeout,
|
2001-11-16 19:07:24 -05:00
|
|
|
GSList *start, GSList *stop, GSList *opt)
|
2001-11-11 13:59:19 -05:00
|
|
|
{
|
|
|
|
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);
|
|
|
|
}
|
|
|
|
|
|
|
|
rec = g_new0(REDIRECT_CMD_REC, 1);
|
|
|
|
redirect_cmd_ref(rec);
|
2001-11-14 18:23:00 -05:00
|
|
|
rec->name = g_strdup(command);
|
2001-11-11 13:59:19 -05:00
|
|
|
rec->remote = remote;
|
2001-11-11 17:44:47 -05:00
|
|
|
rec->timeout = timeout > 0 ? timeout : DEFAULT_REDIRECT_TIMEOUT;
|
2001-11-11 13:59:19 -05:00
|
|
|
rec->start = start;
|
|
|
|
rec->stop = stop;
|
2001-11-16 19:07:24 -05:00
|
|
|
rec->opt = opt;
|
2001-11-14 18:23:00 -05:00
|
|
|
g_hash_table_insert(command_redirects, rec->name, rec);
|
2001-11-11 13:59:19 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
void server_redirect_event(IRC_SERVER_REC *server, const char *command,
|
2001-11-12 17:15:04 -05:00
|
|
|
int count, const char *arg, int remote,
|
2001-11-11 13:59:19 -05:00
|
|
|
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);
|
|
|
|
|
2001-11-12 17:15:04 -05:00
|
|
|
server_redirect_event_list(server, command, count, arg, remote,
|
2001-11-11 13:59:19 -05:00
|
|
|
failure_signal, signals);
|
|
|
|
}
|
|
|
|
|
2001-12-07 16:41:46 -05:00
|
|
|
/* Find specified event from signals list. If it's found, remove it from the
|
|
|
|
list and return it's target signal. */
|
|
|
|
static char *signal_list_move(GSList **signals, const char *event)
|
|
|
|
{
|
|
|
|
GSList *link;
|
|
|
|
char *linkevent, *linksignal;
|
|
|
|
|
|
|
|
link = gslist_find_string(*signals, event);
|
|
|
|
if (link == NULL)
|
|
|
|
return NULL;
|
|
|
|
|
|
|
|
linkevent = link->data;
|
|
|
|
linksignal = link->next->data;
|
|
|
|
|
|
|
|
*signals = g_slist_remove(*signals, linkevent);
|
|
|
|
*signals = g_slist_remove(*signals, linksignal);
|
|
|
|
|
|
|
|
g_free(linkevent);
|
|
|
|
return linksignal;
|
|
|
|
}
|
|
|
|
|
2001-11-11 13:59:19 -05:00
|
|
|
void server_redirect_event_list(IRC_SERVER_REC *server, const char *command,
|
2001-11-12 17:15:04 -05:00
|
|
|
int count, const char *arg, int remote,
|
2001-11-11 13:59:19 -05:00
|
|
|
const char *failure_signal, GSList *signals)
|
|
|
|
{
|
|
|
|
REDIRECT_CMD_REC *cmdrec;
|
|
|
|
REDIRECT_REC *rec;
|
|
|
|
|
|
|
|
g_return_if_fail(IS_IRC_SERVER(server));
|
|
|
|
g_return_if_fail(command != NULL);
|
|
|
|
g_return_if_fail((g_slist_length(signals) & 1) == 0);
|
|
|
|
|
|
|
|
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);
|
2001-11-12 17:15:04 -05:00
|
|
|
rec->count = count;
|
2001-11-11 13:59:19 -05:00
|
|
|
rec->remote = remote != -1 ? remote : cmdrec->remote;
|
|
|
|
rec->failure_signal = g_strdup(failure_signal);
|
|
|
|
|
2001-12-07 16:41:46 -05:00
|
|
|
rec->default_signal = signal_list_move(&signals, "");
|
|
|
|
rec->first_signal = signal_list_move(&signals, "redirect first");
|
|
|
|
rec->last_signal = signal_list_move(&signals, "redirect last");
|
2001-11-11 13:59:19 -05:00
|
|
|
rec->signals = signals;
|
|
|
|
|
2001-11-12 17:15:04 -05:00
|
|
|
if (server->redirect_next != NULL)
|
|
|
|
server_redirect_destroy(server->redirect_next);
|
2001-11-11 13:59:19 -05:00
|
|
|
server->redirect_next = rec;
|
|
|
|
}
|
|
|
|
|
2001-11-11 15:20:43 -05:00
|
|
|
void server_redirect_command(IRC_SERVER_REC *server, const char *command,
|
|
|
|
REDIRECT_REC *redirect)
|
2001-11-11 13:59:19 -05:00
|
|
|
{
|
|
|
|
REDIRECT_CMD_REC *cmdrec;
|
|
|
|
|
|
|
|
g_return_if_fail(IS_IRC_SERVER(server));
|
|
|
|
g_return_if_fail(command != NULL);
|
|
|
|
|
2001-11-11 15:20:43 -05:00
|
|
|
if (redirect == NULL) {
|
2001-11-12 17:15:04 -05:00
|
|
|
/* no redirection wanted, but still register the command
|
|
|
|
so future redirections wont get messed up. */
|
2001-11-11 13:59:19 -05:00
|
|
|
cmdrec = redirect_cmd_find(command);
|
|
|
|
if (cmdrec == NULL)
|
|
|
|
return;
|
|
|
|
|
|
|
|
redirect_cmd_ref(cmdrec);
|
|
|
|
|
2001-11-11 15:20:43 -05:00
|
|
|
redirect = g_new0(REDIRECT_REC, 1);
|
|
|
|
redirect->created = time(NULL);
|
|
|
|
redirect->cmd = cmdrec;
|
|
|
|
redirect->remote = cmdrec->remote;
|
2001-11-11 13:59:19 -05:00
|
|
|
}
|
|
|
|
|
2001-11-11 15:20:43 -05:00
|
|
|
server->redirects = g_slist_append(server->redirects, redirect);
|
2001-11-11 13:59:19 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
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') {
|
2001-11-14 17:43:48 -05:00
|
|
|
if (toupper(*arg) != toupper(*event_args))
|
2001-11-11 13:59:19 -05:00
|
|
|
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;
|
|
|
|
}
|
|
|
|
|
2001-12-01 15:17:57 -05:00
|
|
|
#define MATCH_NONE 0
|
|
|
|
#define MATCH_START 1
|
|
|
|
#define MATCH_STOP 2
|
|
|
|
|
2001-11-11 13:59:19 -05:00
|
|
|
static const char *redirect_match(REDIRECT_REC *redirect, const char *event,
|
2001-12-01 15:17:57 -05:00
|
|
|
const char *args, int *match)
|
2001-11-11 13:59:19 -05:00
|
|
|
{
|
|
|
|
GSList *tmp, *cmdpos;
|
2001-12-01 15:17:57 -05:00
|
|
|
const char *signal;
|
|
|
|
int match_list;
|
2001-11-11 13:59:19 -05:00
|
|
|
|
2001-11-25 15:55:45 -05:00
|
|
|
if (redirect->aborted)
|
|
|
|
return NULL;
|
|
|
|
|
2001-11-11 15:20:43 -05:00
|
|
|
/* get the signal for redirection event - if it's not found we'll
|
|
|
|
use the default signal */
|
|
|
|
signal = NULL;
|
2001-11-11 13:59:19 -05:00
|
|
|
for (tmp = redirect->signals; tmp != NULL; tmp = tmp->next->next) {
|
2001-11-11 15:20:43 -05:00
|
|
|
if (strcmp(tmp->data, event) == 0) {
|
|
|
|
signal = tmp->next->data;
|
|
|
|
break;
|
2001-11-11 13:59:19 -05:00
|
|
|
}
|
2001-11-11 15:20:43 -05:00
|
|
|
}
|
2001-11-11 13:59:19 -05:00
|
|
|
|
2001-11-11 15:20:43 -05:00
|
|
|
/* find the argument position */
|
2001-11-16 19:07:24 -05:00
|
|
|
if (redirect->destroyed) {
|
|
|
|
/* stop event is already found for this redirection, but
|
|
|
|
we'll still want to look for optional events */
|
|
|
|
cmdpos = redirect_cmd_list_find(redirect->cmd->opt, event);
|
|
|
|
if (cmdpos == NULL)
|
|
|
|
return NULL;
|
|
|
|
|
2001-12-01 15:17:57 -05:00
|
|
|
match_list = MATCH_STOP;
|
2001-11-16 19:07:24 -05:00
|
|
|
} else {
|
|
|
|
/* look from start/stop lists */
|
|
|
|
cmdpos = redirect_cmd_list_find(redirect->cmd->start, event);
|
|
|
|
if (cmdpos != NULL)
|
2001-12-01 15:17:57 -05:00
|
|
|
match_list = MATCH_START;
|
2001-11-16 19:07:24 -05:00
|
|
|
else {
|
|
|
|
cmdpos = redirect_cmd_list_find(redirect->cmd->stop,
|
|
|
|
event);
|
2001-12-01 15:17:57 -05:00
|
|
|
match_list = cmdpos != NULL ? MATCH_STOP : MATCH_NONE;
|
2001-11-16 19:07:24 -05:00
|
|
|
}
|
2001-11-11 15:20:43 -05:00
|
|
|
}
|
2001-11-11 13:59:19 -05:00
|
|
|
|
2001-11-11 15:20:43 -05:00
|
|
|
if (signal == NULL && cmdpos == NULL) {
|
|
|
|
/* event not found from specified redirection events nor
|
|
|
|
registered command events */
|
|
|
|
return NULL;
|
2001-11-11 13:59:19 -05:00
|
|
|
}
|
|
|
|
|
2001-11-11 15:20:43 -05:00
|
|
|
/* check that arguments match */
|
|
|
|
if (args != NULL && redirect->arg != NULL && cmdpos != NULL &&
|
|
|
|
!redirect_args_match(args, redirect->arg,
|
|
|
|
GPOINTER_TO_INT(cmdpos->next->data)))
|
|
|
|
return NULL;
|
|
|
|
|
2001-12-01 15:17:57 -05:00
|
|
|
*match = match_list;
|
2001-11-11 15:20:43 -05:00
|
|
|
return signal != NULL ? signal : redirect->default_signal;
|
2001-11-11 13:59:19 -05:00
|
|
|
}
|
|
|
|
|
2001-11-15 11:29:25 -05:00
|
|
|
static void redirect_abort(IRC_SERVER_REC *server, REDIRECT_REC *rec)
|
|
|
|
{
|
|
|
|
char *str;
|
|
|
|
|
|
|
|
server->redirects =
|
|
|
|
g_slist_remove(server->redirects, rec);
|
|
|
|
|
2001-11-18 10:07:03 -05:00
|
|
|
if (rec->aborted || !rec->destroyed) {
|
2001-11-15 11:29:25 -05:00
|
|
|
/* emit the failure signal */
|
|
|
|
str = g_strdup_printf(rec->failure_signal != NULL ?
|
|
|
|
"FAILED %s: %s" : "FAILED %s",
|
|
|
|
rec->cmd->name, rec->failure_signal);
|
|
|
|
rawlog_redirect(server->rawlog, str);
|
|
|
|
g_free(str);
|
|
|
|
|
|
|
|
if (rec->failure_signal != NULL)
|
|
|
|
signal_emit(rec->failure_signal, 1, server);
|
2001-12-07 16:41:46 -05:00
|
|
|
} else if (rec->last_signal != NULL) {
|
|
|
|
/* emit the last signal */
|
|
|
|
signal_emit(rec->last_signal, 1, server);
|
2001-11-15 11:29:25 -05:00
|
|
|
}
|
2001-12-07 16:41:46 -05:00
|
|
|
|
2001-11-15 11:29:25 -05:00
|
|
|
server_redirect_destroy(rec);
|
|
|
|
}
|
|
|
|
|
2001-12-01 15:36:44 -05:00
|
|
|
#define REDIRECT_IS_TIMEOUTED(rec) \
|
|
|
|
((now-(rec)->created) > (rec)->cmd->timeout)
|
|
|
|
|
|
|
|
|
2001-11-11 13:59:19 -05:00
|
|
|
static REDIRECT_REC *redirect_find(IRC_SERVER_REC *server, const char *event,
|
|
|
|
const char *args, const char **signal,
|
2001-12-01 15:17:57 -05:00
|
|
|
int *match)
|
2001-11-11 13:59:19 -05:00
|
|
|
{
|
|
|
|
REDIRECT_REC *redirect;
|
|
|
|
GSList *tmp, *next;
|
|
|
|
time_t now;
|
2001-12-01 15:17:57 -05:00
|
|
|
const char *match_signal;
|
2001-11-11 13:59:19 -05:00
|
|
|
|
|
|
|
/* find the redirection */
|
|
|
|
*signal = NULL; redirect = NULL;
|
|
|
|
for (tmp = server->redirects; tmp != NULL; tmp = tmp->next) {
|
|
|
|
REDIRECT_REC *rec = tmp->data;
|
|
|
|
|
2001-12-01 15:17:57 -05:00
|
|
|
match_signal = redirect_match(rec, event, args, match);
|
|
|
|
if (match_signal != NULL && *match != MATCH_NONE) {
|
2001-11-16 19:07:24 -05:00
|
|
|
redirect = rec;
|
2001-12-01 15:17:57 -05:00
|
|
|
*signal = match_signal;
|
2001-11-16 19:07:24 -05:00
|
|
|
break;
|
2001-11-11 13:59:19 -05:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/* 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;
|
|
|
|
|
2001-12-01 15:36:44 -05:00
|
|
|
next = tmp->next;
|
|
|
|
if (rec->destroyed) {
|
|
|
|
/* redirection is finished, destroy it */
|
|
|
|
redirect_abort(server, rec);
|
|
|
|
} else if (redirect != NULL) {
|
|
|
|
/* check if redirection failed */
|
|
|
|
if (rec->aborted ||
|
2001-12-03 18:46:12 -05:00
|
|
|
rec->failures++ >= MAX_FAILURE_COUNT) {
|
2001-12-01 15:36:44 -05:00
|
|
|
/* enough failures, abort it now */
|
|
|
|
if (!rec->remote || REDIRECT_IS_TIMEOUTED(rec))
|
|
|
|
redirect_abort(server, rec);
|
|
|
|
}
|
2001-11-16 18:42:09 -05:00
|
|
|
}
|
2001-11-11 13:59:19 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
return redirect;
|
|
|
|
}
|
|
|
|
|
2001-11-11 17:44:47 -05:00
|
|
|
static const char *
|
|
|
|
server_redirect_get(IRC_SERVER_REC *server, const char *event,
|
2001-12-01 15:17:57 -05:00
|
|
|
const char *args, REDIRECT_REC **redirect, int *match)
|
2001-11-11 13:59:19 -05:00
|
|
|
{
|
|
|
|
const char *signal;
|
2001-11-11 17:44:47 -05:00
|
|
|
|
|
|
|
*redirect = NULL;
|
2001-12-01 15:17:57 -05:00
|
|
|
*match = MATCH_NONE;
|
2001-11-11 13:59:19 -05:00
|
|
|
|
|
|
|
if (server->redirects == NULL)
|
|
|
|
return NULL;
|
|
|
|
|
|
|
|
if (server->redirect_continue == NULL) {
|
|
|
|
/* find the redirection */
|
2001-12-01 15:17:57 -05:00
|
|
|
*redirect = redirect_find(server, event, args, &signal, match);
|
2001-11-11 13:59:19 -05:00
|
|
|
} else {
|
|
|
|
/* redirection is already started, now we'll just need to
|
|
|
|
keep redirecting until stop-event is found. */
|
2001-11-11 17:44:47 -05:00
|
|
|
*redirect = server->redirect_continue;
|
2001-12-01 15:17:57 -05:00
|
|
|
signal = redirect_match(*redirect, event, NULL, match);
|
2001-11-11 13:59:19 -05:00
|
|
|
if (signal == NULL) {
|
2001-11-18 10:07:03 -05:00
|
|
|
/* unknown event - redirect to the default signal. */
|
|
|
|
if (strncmp(event, "event ", 6) == 0 &&
|
|
|
|
isdigit(event[6])) {
|
|
|
|
signal = (*redirect)->default_signal;
|
2001-12-14 11:11:13 -05:00
|
|
|
if (*match == MATCH_NONE)
|
|
|
|
*match = MATCH_START;
|
2001-11-18 10:07:03 -05:00
|
|
|
} else {
|
|
|
|
/* not a numeric, so we've lost the
|
|
|
|
stop event.. */
|
|
|
|
(*redirect)->aborted = TRUE;
|
2001-11-25 15:55:45 -05:00
|
|
|
redirect_abort(server, *redirect);
|
|
|
|
|
|
|
|
*redirect = NULL;
|
|
|
|
signal = NULL;
|
2001-11-18 10:07:03 -05:00
|
|
|
}
|
2001-11-11 13:59:19 -05:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2001-12-07 16:41:46 -05:00
|
|
|
if (*redirect != NULL && (*redirect)->first_signal != NULL &&
|
|
|
|
!(*redirect)->first_signal_sent) {
|
|
|
|
/* emit the first_signal */
|
|
|
|
(*redirect)->first_signal_sent = TRUE;
|
|
|
|
signal_emit((*redirect)->first_signal, 1, server);
|
|
|
|
}
|
|
|
|
|
2001-11-11 17:44:47 -05:00
|
|
|
return signal;
|
|
|
|
}
|
|
|
|
|
|
|
|
const char *server_redirect_get_signal(IRC_SERVER_REC *server,
|
|
|
|
const char *event,
|
|
|
|
const char *args)
|
|
|
|
{
|
|
|
|
REDIRECT_REC *redirect;
|
|
|
|
const char *signal;
|
2001-12-01 15:17:57 -05:00
|
|
|
int match;
|
2001-11-11 17:44:47 -05:00
|
|
|
|
2001-12-01 15:17:57 -05:00
|
|
|
signal = server_redirect_get(server, event, args, &redirect, &match);
|
|
|
|
if (match != MATCH_STOP || redirect == NULL)
|
2001-11-11 13:59:19 -05:00
|
|
|
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) */
|
2001-11-12 17:15:04 -05:00
|
|
|
if (--redirect->count <= 0)
|
|
|
|
redirect->destroyed = TRUE;
|
2001-11-11 13:59:19 -05:00
|
|
|
server->redirect_continue = NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
return signal;
|
|
|
|
}
|
|
|
|
|
2001-11-11 17:44:47 -05:00
|
|
|
const char *server_redirect_peek_signal(IRC_SERVER_REC *server,
|
|
|
|
const char *event,
|
2001-12-07 18:41:45 -05:00
|
|
|
const char *args,
|
|
|
|
int *redirected)
|
2001-11-11 17:44:47 -05:00
|
|
|
{
|
2001-12-07 18:41:45 -05:00
|
|
|
REDIRECT_REC *redirect;
|
|
|
|
const char *signal;
|
2001-12-01 15:17:57 -05:00
|
|
|
int match;
|
2001-11-11 17:44:47 -05:00
|
|
|
|
2001-12-07 18:41:45 -05:00
|
|
|
signal = server_redirect_get(server, event, args, &redirect, &match);
|
|
|
|
*redirected = match != MATCH_NONE;
|
|
|
|
return signal;
|
2001-11-11 17:44:47 -05:00
|
|
|
}
|
|
|
|
|
2001-11-11 13:59:19 -05:00
|
|
|
static void sig_disconnected(IRC_SERVER_REC *server)
|
|
|
|
{
|
|
|
|
if (!IS_IRC_SERVER(server))
|
|
|
|
return;
|
|
|
|
|
2001-11-11 15:20:43 -05:00
|
|
|
g_slist_foreach(server->redirects,
|
|
|
|
(GFunc) server_redirect_destroy, NULL);
|
2001-11-11 13:59:19 -05:00
|
|
|
g_slist_free(server->redirects);
|
2001-11-18 05:55:14 -05:00
|
|
|
server->redirects = NULL;
|
2001-11-11 13:59:19 -05:00
|
|
|
|
2001-11-18 05:55:14 -05:00
|
|
|
if (server->redirect_next != NULL) {
|
|
|
|
server_redirect_destroy(server->redirect_next);
|
|
|
|
server->redirect_next = NULL;
|
|
|
|
}
|
2001-11-11 13:59:19 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
static void cmd_redirect_destroy(char *key, REDIRECT_CMD_REC *cmd)
|
|
|
|
{
|
|
|
|
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
|
2001-11-11 17:44:47 -05:00
|
|
|
with a default timeout */
|
|
|
|
server_redirect_register("whois", TRUE, 0,
|
2001-11-11 13:59:19 -05:00
|
|
|
"event 311", 1, /* Begins the WHOIS */
|
|
|
|
NULL,
|
2001-11-16 19:07:24 -05:00
|
|
|
"event 401", 1, /* No such nick */
|
2001-11-11 13:59:19 -05:00
|
|
|
"event 318", 1, /* End of WHOIS */
|
|
|
|
"event 402", 1, /* No such server */
|
2001-11-16 19:07:24 -05:00
|
|
|
NULL,
|
|
|
|
"event 318", 1, /* After 401, we should get 318, but in OPN we don't.. */
|
2001-11-11 13:59:19 -05:00
|
|
|
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 */
|
2001-11-16 19:07:24 -05:00
|
|
|
NULL,
|
2001-11-11 13:59:19 -05:00
|
|
|
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 */
|
2001-11-16 19:07:24 -05:00
|
|
|
NULL,
|
2001-11-11 13:59:19 -05:00
|
|
|
NULL);
|
|
|
|
|
|
|
|
/* LIST */
|
|
|
|
server_redirect_register("list", FALSE, 0,
|
|
|
|
"event 321", 1, /* Begins the LIST */
|
|
|
|
NULL,
|
|
|
|
"event 323", 1, /* End of LIST */
|
2001-11-16 19:07:24 -05:00
|
|
|
NULL,
|
2001-11-11 13:59:19 -05:00
|
|
|
NULL);
|
|
|
|
|
|
|
|
/* ISON */
|
|
|
|
server_redirect_register("ison", FALSE, 0,
|
|
|
|
NULL,
|
2001-11-16 09:18:26 -05:00
|
|
|
"event 303", -1, /* ISON */
|
2001-11-16 19:07:24 -05:00
|
|
|
NULL,
|
2001-11-11 13:59:19 -05:00
|
|
|
NULL);
|
|
|
|
|
|
|
|
/* USERHOST */
|
|
|
|
server_redirect_register("userhost", FALSE, 0,
|
|
|
|
"event 401", 1, /* no such nick */
|
|
|
|
NULL,
|
2001-11-16 09:18:26 -05:00
|
|
|
"event 302", -1, /* Userhost */
|
|
|
|
"event 461", -1, /* Not enough parameters */
|
2001-11-16 19:07:24 -05:00
|
|
|
NULL,
|
2001-11-11 13:59:19 -05:00
|
|
|
NULL);
|
|
|
|
|
2001-12-06 18:10:41 -05:00
|
|
|
/* MODE user */
|
|
|
|
server_redirect_register("mode user", FALSE, 0,
|
|
|
|
NULL,
|
|
|
|
"event mode", 0, /* MODE-reply */
|
|
|
|
"event 501", -1, /* Uknown MODE flag */
|
|
|
|
"event 502", -1, /* Can't change mode for other users */
|
|
|
|
"event 403", 1, /* That channel doesn't exist (tried to change mode to others) */
|
|
|
|
NULL,
|
|
|
|
NULL);
|
|
|
|
|
2001-11-11 13:59:19 -05:00
|
|
|
/* 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. */
|
2001-11-16 19:07:24 -05:00
|
|
|
NULL,
|
|
|
|
"event 329", 1, /* Channel create time */
|
2001-11-11 13:59:19 -05:00
|
|
|
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. */
|
2001-11-16 19:07:24 -05:00
|
|
|
NULL,
|
2001-11-11 13:59:19 -05:00
|
|
|
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) */
|
2001-11-16 19:07:24 -05:00
|
|
|
NULL,
|
2001-11-11 13:59:19 -05:00
|
|
|
NULL);
|
|
|
|
|
|
|
|
/* MODE #channel I */
|
2001-11-11 15:27:46 -05:00
|
|
|
server_redirect_register("mode I", FALSE, 0,
|
2001-11-11 13:59:19 -05:00
|
|
|
"event 346", 1,
|
|
|
|
NULL,
|
|
|
|
"event 347", 1, /* End of invite list */
|
2001-11-16 09:49:17 -05:00
|
|
|
"event 482", 1, /* not channel operator - OPN's ircd doesn't want non-ops to see ban exceptions */
|
2001-11-11 13:59:19 -05:00
|
|
|
"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) */
|
2001-11-16 19:07:24 -05:00
|
|
|
NULL,
|
2001-11-11 13:59:19 -05:00
|
|
|
NULL);
|
|
|
|
|
2001-11-11 17:44:47 -05:00
|
|
|
/* PING - use default timeout */
|
|
|
|
server_redirect_register("ping", TRUE, 0,
|
2001-11-11 13:59:19 -05:00
|
|
|
NULL,
|
|
|
|
"event 402", -1, /* no such server */
|
|
|
|
"event pong", -1, /* PONG */
|
2001-11-16 19:07:24 -05:00
|
|
|
NULL,
|
2001-11-11 13:59:19 -05:00
|
|
|
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);
|
|
|
|
}
|