forked from PsychoticNinja/irssi
bind protocol-specific commands. Added #define command_bind_irc() for easier access. CMD_IRC_SERVER(server) check should be done at the beginning of each command requiring IRC server as active server, it handles it correctly the cases when it is not. Did some other cleanups as well. git-svn-id: http://svn.irssi.org/repos/irssi/trunk@1955 dbcabf3a-b0e7-0310-adc4-f8d773084564
275 lines
7.3 KiB
C
275 lines
7.3 KiB
C
/*
|
|
channel-rejoin.c : rejoin to channel if it's "temporarily unavailable"
|
|
this has nothing to do with autorejoin if kicked
|
|
|
|
Copyright (C) 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 "irc-channels.h"
|
|
#include "irc-commands.h"
|
|
#include "channel-rejoin.h"
|
|
|
|
#define REJOIN_TIMEOUT (1000*60*5) /* try to rejoin every 5 minutes */
|
|
|
|
static int rejoin_tag;
|
|
|
|
static void rejoin_destroy(IRC_SERVER_REC *server, REJOIN_REC *rec)
|
|
{
|
|
g_return_if_fail(IS_IRC_SERVER(server));
|
|
g_return_if_fail(rec != NULL);
|
|
|
|
server->rejoin_channels =
|
|
g_slist_remove(server->rejoin_channels, rec);
|
|
|
|
signal_emit("channel rejoin remove", 2, server, rec);
|
|
|
|
g_free(rec->channel);
|
|
g_free_not_null(rec->key);
|
|
g_free(rec);
|
|
}
|
|
|
|
static REJOIN_REC *rejoin_find(IRC_SERVER_REC *server, const char *channel)
|
|
{
|
|
GSList *tmp;
|
|
|
|
g_return_val_if_fail(IS_IRC_SERVER(server), NULL);
|
|
g_return_val_if_fail(channel != NULL, NULL);
|
|
|
|
for (tmp = server->rejoin_channels; tmp != NULL; tmp = tmp->next) {
|
|
REJOIN_REC *rec = tmp->data;
|
|
|
|
if (g_strcasecmp(rec->channel, channel) == 0)
|
|
return rec;
|
|
}
|
|
|
|
return NULL;
|
|
}
|
|
|
|
#define channel_have_key(chan) \
|
|
((chan) != NULL && (chan)->key != NULL && (chan)->key[0] != '\0')
|
|
|
|
static void channel_rejoin(IRC_SERVER_REC *server, const char *channel)
|
|
{
|
|
IRC_CHANNEL_REC *chanrec;
|
|
REJOIN_REC *rec;
|
|
|
|
g_return_if_fail(IS_IRC_SERVER(server));
|
|
g_return_if_fail(channel != NULL);
|
|
|
|
chanrec = irc_channel_find(server, channel);
|
|
if (chanrec == NULL || chanrec->joined) return;
|
|
|
|
rec = rejoin_find(server, channel);
|
|
if (rec != NULL) {
|
|
/* already exists */
|
|
rec->joining = FALSE;
|
|
|
|
/* update channel key */
|
|
g_free_and_null(rec->key);
|
|
if (channel_have_key(chanrec))
|
|
rec->key = g_strdup(chanrec->key);
|
|
} else {
|
|
/* new rejoin */
|
|
rec = g_new0(REJOIN_REC, 1);
|
|
rec->channel = g_strdup(channel);
|
|
if (channel_have_key(chanrec))
|
|
rec->key = g_strdup(chanrec->key);
|
|
|
|
server->rejoin_channels =
|
|
g_slist_append(server->rejoin_channels, rec);
|
|
signal_emit("channel rejoin new", 2, server, rec);
|
|
}
|
|
|
|
chanrec->left = TRUE;
|
|
channel_destroy(CHANNEL(chanrec));
|
|
}
|
|
|
|
static void event_duplicate_channel(IRC_SERVER_REC *server, const char *data)
|
|
{
|
|
CHANNEL_REC *chanrec;
|
|
char *params, *channel, *p;
|
|
|
|
g_return_if_fail(data != NULL);
|
|
|
|
params = event_get_params(data, 3, NULL, NULL, &channel);
|
|
p = strchr(channel, ' ');
|
|
if (p != NULL) *p = '\0';
|
|
|
|
if (channel[0] == '!' && channel[1] != '!') {
|
|
chanrec = channel_find(SERVER(server), channel);
|
|
if (chanrec != NULL && !chanrec->names_got) {
|
|
/* duplicate channel - this should only happen when
|
|
there's some sync problem with servers, rejoining
|
|
after a while should help.
|
|
|
|
note that this same 407 is sent when trying to
|
|
create !!channel that already exists so we don't
|
|
want to try rejoining then. */
|
|
channel_rejoin(server, channel);
|
|
signal_stop();
|
|
}
|
|
}
|
|
|
|
g_free(params);
|
|
}
|
|
|
|
static void event_target_unavailable(IRC_SERVER_REC *server, const char *data)
|
|
{
|
|
char *params, *channel;
|
|
IRC_CHANNEL_REC *chanrec;
|
|
|
|
g_return_if_fail(data != NULL);
|
|
|
|
params = event_get_params(data, 2, NULL, &channel);
|
|
if (ischannel(*channel)) {
|
|
chanrec = irc_channel_find(server, channel);
|
|
if (chanrec != NULL && chanrec->joined) {
|
|
/* dalnet event - can't change nick while
|
|
banned in channel */
|
|
} else {
|
|
/* channel is unavailable - try to join again
|
|
a bit later */
|
|
channel_rejoin(server, channel);
|
|
signal_stop();
|
|
}
|
|
}
|
|
|
|
g_free(params);
|
|
}
|
|
|
|
/* join ok/failed - remove from rejoins list. this happens always after join
|
|
except if the "target unavailable" error happens again */
|
|
static void sig_remove_rejoin(IRC_CHANNEL_REC *channel)
|
|
{
|
|
REJOIN_REC *rec;
|
|
|
|
if (!IS_IRC_CHANNEL(channel) || channel->server == NULL)
|
|
return;
|
|
|
|
rec = rejoin_find(channel->server, channel->name);
|
|
if (rec != NULL && rec->joining) {
|
|
/* join failed, remove the rejoin */
|
|
rejoin_destroy(channel->server, rec);
|
|
}
|
|
}
|
|
|
|
static void sig_disconnected(IRC_SERVER_REC *server)
|
|
{
|
|
if (!IS_IRC_SERVER(server))
|
|
return;
|
|
|
|
while (server->rejoin_channels != NULL)
|
|
rejoin_destroy(server, server->rejoin_channels->data);
|
|
}
|
|
|
|
static void server_rejoin_channels(IRC_SERVER_REC *server)
|
|
{
|
|
GSList *tmp, *next;
|
|
GString *channels, *keys;
|
|
int use_keys;
|
|
|
|
g_return_if_fail(IS_IRC_SERVER(server));
|
|
|
|
channels = g_string_new(NULL);
|
|
keys = g_string_new(NULL);
|
|
|
|
use_keys = FALSE;
|
|
for (tmp = server->rejoin_channels; tmp != NULL; tmp = next) {
|
|
REJOIN_REC *rec = tmp->data;
|
|
next = tmp->next;
|
|
|
|
if (rec->joining) {
|
|
/* we missed the join (failed) message,
|
|
remove from rejoins.. */
|
|
rejoin_destroy(server, rec);
|
|
continue;
|
|
}
|
|
|
|
rec->joining = TRUE;
|
|
g_string_sprintfa(channels, "%s,", rec->channel);
|
|
if (rec->key == NULL)
|
|
g_string_append(keys, "x,");
|
|
else {
|
|
g_string_sprintfa(keys, "%s,", rec->key);
|
|
use_keys = TRUE;
|
|
}
|
|
}
|
|
|
|
if (channels->len > 0) {
|
|
g_string_truncate(channels, channels->len-1);
|
|
g_string_truncate(keys, keys->len-1);
|
|
|
|
if (use_keys) g_string_sprintfa(channels, " %s", keys->str);
|
|
server->channels_join(SERVER(server), channels->str, TRUE);
|
|
}
|
|
|
|
g_string_free(channels, TRUE);
|
|
g_string_free(keys, TRUE);
|
|
}
|
|
|
|
static int sig_rejoin(void)
|
|
{
|
|
GSList *tmp;
|
|
|
|
for (tmp = servers; tmp != NULL; tmp = tmp->next) {
|
|
IRC_SERVER_REC *rec = tmp->data;
|
|
|
|
if (IS_IRC_SERVER(rec) && rec->rejoin_channels != NULL)
|
|
server_rejoin_channels(rec);
|
|
}
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
static void cmd_rmrejoins(const char *data, IRC_SERVER_REC *server)
|
|
{
|
|
CMD_IRC_SERVER(server);
|
|
|
|
while (server->rejoin_channels != NULL)
|
|
rejoin_destroy(server, server->rejoin_channels->data);
|
|
}
|
|
|
|
void channel_rejoin_init(void)
|
|
{
|
|
rejoin_tag = g_timeout_add(REJOIN_TIMEOUT,
|
|
(GSourceFunc) sig_rejoin, NULL);
|
|
|
|
command_bind_irc("rmrejoins", NULL, (SIGNAL_FUNC) cmd_rmrejoins);
|
|
signal_add_first("event 407", (SIGNAL_FUNC) event_duplicate_channel);
|
|
signal_add_first("event 437", (SIGNAL_FUNC) event_target_unavailable);
|
|
signal_add_first("channel joined", (SIGNAL_FUNC) sig_remove_rejoin);
|
|
signal_add_first("channel destroyed", (SIGNAL_FUNC) sig_remove_rejoin);
|
|
signal_add("server disconnected", (SIGNAL_FUNC) sig_disconnected);
|
|
}
|
|
|
|
void channel_rejoin_deinit(void)
|
|
{
|
|
g_source_remove(rejoin_tag);
|
|
|
|
command_unbind("rmrejoins", (SIGNAL_FUNC) cmd_rmrejoins);
|
|
signal_remove("event 407", (SIGNAL_FUNC) event_duplicate_channel);
|
|
signal_remove("event 437", (SIGNAL_FUNC) event_target_unavailable);
|
|
signal_remove("channel joined", (SIGNAL_FUNC) sig_remove_rejoin);
|
|
signal_remove("channel destroyed", (SIGNAL_FUNC) sig_remove_rejoin);
|
|
signal_remove("server disconnected", (SIGNAL_FUNC) sig_disconnected);
|
|
}
|