diff options
Diffstat (limited to 'src/irc/core/netsplit.c')
-rw-r--r-- | src/irc/core/netsplit.c | 270 |
1 files changed, 270 insertions, 0 deletions
diff --git a/src/irc/core/netsplit.c b/src/irc/core/netsplit.c new file mode 100644 index 00000000..b455f740 --- /dev/null +++ b/src/irc/core/netsplit.c @@ -0,0 +1,270 @@ +/* + netsplit.c : irssi + + Copyright (C) 1999 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 "commands.h" +#include "misc.h" +#include "common-setup.h" + +#include "irc-server.h" +#include "netsplit.h" + +static int split_tag; + +static NETSPLIT_REC *netsplit_add(IRC_SERVER_REC *server, const char *nick, const char *address, const char *servers) +{ + NETSPLIT_REC *rec; + NETSPLIT_CHAN_REC *splitchan; + NICK_REC *nickrec; + GSList *tmp; + char *p, *dupservers; + + g_return_val_if_fail(server != NULL, NULL); + g_return_val_if_fail(nick != NULL, NULL); + g_return_val_if_fail(address != NULL, NULL); + + dupservers = g_strdup(servers); + p = strchr(dupservers, ' '); + if (p == NULL) { + g_free(dupservers); + g_return_val_if_fail(p != NULL, NULL); + } + *p++ = '\0'; + + rec = g_new0(NETSPLIT_REC, 1); + rec->nick = g_strdup(nick); + rec->address = g_strdup(address); + rec->destroy = time(NULL)+NETSPLIT_MAX_REMEMBER; + + /* get splitted servers */ + rec->server = g_strdup(dupservers); + rec->destserver = g_strdup(p); + g_free(dupservers); + + /* copy the channel nick records.. */ + for (tmp = server->channels; tmp != NULL; tmp = tmp->next) { + CHANNEL_REC *channel = tmp->data; + + nickrec = nicklist_find(channel, nick); + if (nickrec == NULL) + continue; + + splitchan = g_new0(NETSPLIT_CHAN_REC, 1); + splitchan->name = g_strdup(channel->name); + memcpy(&splitchan->nick, nickrec, sizeof(NICK_REC)); + + rec->channels = g_slist_append(rec->channels, splitchan); + } + + g_hash_table_insert(server->splits, rec->nick, rec); + signal_emit("netsplit add", 1, rec); + return rec; +} + +static void netsplit_destroy(NETSPLIT_REC *rec) +{ + GSList *tmp; + + g_return_if_fail(rec != NULL); + + signal_emit("netsplit remove", 1, rec); + for (tmp = rec->channels; tmp != NULL; tmp = tmp->next) { + NETSPLIT_CHAN_REC *rec = tmp->data; + + g_free(rec->name); + g_free(rec); + } + + g_free(rec->server); + g_free(rec->destserver); + g_free(rec->nick); + g_free(rec->address); + g_free(rec); +} + +static void netsplit_destroy_hash(gpointer key, NETSPLIT_REC *rec) +{ + netsplit_destroy(rec); +} + +NETSPLIT_REC *netsplit_find(IRC_SERVER_REC *server, const char *nick, const char *address) +{ + NETSPLIT_REC *rec; + + g_return_val_if_fail(server != NULL, NULL); + g_return_val_if_fail(nick != NULL, NULL); + + rec = g_hash_table_lookup(server->splits, nick); + if (rec == NULL) return NULL; + + return (address == NULL || g_strcasecmp(rec->address, address) == 0) ? rec : NULL; +} + +NICK_REC *netsplit_find_channel(IRC_SERVER_REC *server, const char *nick, const char *address, const char *channel) +{ + NETSPLIT_REC *rec; + GSList *tmp; + + rec = netsplit_find(server, nick, address); + if (rec == NULL) return NULL; + + for (tmp = rec->channels; tmp != NULL; tmp = tmp->next) { + NETSPLIT_CHAN_REC *rec = tmp->data; + + if (g_strcasecmp(rec->name, channel) == 0) + return &rec->nick; + } + + return NULL; +} + +static int is_split(const char *msg) +{ + char *params, *host1, *host2, *p; + int ok; + + g_return_val_if_fail(msg != NULL, FALSE); + + /* must have only two words */ + p = strchr(msg, ' '); + if (p == NULL || strchr(p+1, ' ') != NULL) return FALSE; + + /* check that it looks ok.. */ + if (!match_wildcards("*.* *.*", msg) || + match_wildcards("*..*", msg) || strstr(msg, "))") != NULL) + return FALSE; + + /* get the two hosts */ + ok = FALSE; + params = cmd_get_params(msg, 2, &host1, &host2); + if (g_strcasecmp(host1, host2) != 0) { /* hosts can't be same.. */ + /* check that domain length is 2 or 3 */ + p = strrchr(host1, '.'); + if (p != NULL && (strlen(p+1) == 2 || strlen(p+1) == 3)) { + p = strrchr(host2, '.'); + if (p != NULL && (strlen(p+1) == 2 || strlen(p+1) == 3)) { + /* it looks just like a netsplit to me. */ + ok = TRUE; + } + } + } + g_free(params); + + return ok; +} + +static void split_set_timeout(gpointer key, NETSPLIT_REC *rec, NETSPLIT_REC *orig) +{ + if (rec == orig) { + /* original nick, destroy it in a few seconds.. */ + rec->destroy = time(NULL)+4; + } else if (g_strcasecmp(rec->server, orig->server) == 0 && + g_strcasecmp(rec->destserver, orig->destserver) == 0) { + /* same servers -> split over -> destroy old records sooner.. */ + rec->destroy = time(NULL)+60; + } +} + +static void event_join(gchar *data, IRC_SERVER_REC *server, gchar *nick, gchar *address) +{ + NETSPLIT_REC *rec; + + /* check if split is over */ + rec = g_hash_table_lookup(server->splits, nick); + if (rec == NULL) return; + + if (g_strcasecmp(rec->address, address) == 0) { + /* yep, looks like it is. for same people that had the same + splitted servers set the timeout to one minute. + + .. if the user just changed server, he/she can't use the + same nick (unless the server is broken) so don't bother + checking that the nick's server matches the split. */ + g_hash_table_foreach(server->splits, (GHFunc) split_set_timeout, rec); + } else { + /* back from different address.. just destroy it. */ + g_hash_table_remove(server->splits, rec->nick); + netsplit_destroy(rec); + } +} + +static void event_quit(const char *data, IRC_SERVER_REC *server, const char *nick, const char *address) +{ + g_return_if_fail(data != NULL); + + if (*data == ':') data++; + if (g_strcasecmp(nick, server->nick) != 0 && is_split(data)) { + /* netsplit! */ + netsplit_add(server, nick, address, data); + } +} + +static void sig_disconnected(IRC_SERVER_REC *server) +{ + g_return_if_fail(server != NULL); + + g_hash_table_foreach(server->splits, (GHFunc) netsplit_destroy_hash, NULL); + g_hash_table_destroy(server->splits); +} + +static int split_server_check(gpointer key, NETSPLIT_REC *rec, IRC_SERVER_REC *server) +{ + /* Check if this split record is too old.. */ + if (rec->destroy > time(NULL)) + return FALSE; + + netsplit_destroy(rec); + return TRUE; +} + +static int split_check_old(void) +{ + GSList *tmp; + + for (tmp = servers; tmp != NULL; tmp = tmp->next) { + IRC_SERVER_REC *server = tmp->data; + + g_hash_table_foreach_remove(server->splits, (GHRFunc) split_server_check, server); + } + + return 1; +} + +void netsplit_init(void) +{ + split_tag = g_timeout_add(1000, (GSourceFunc) split_check_old, NULL); + signal_add_first("event join", (SIGNAL_FUNC) event_join); + signal_add_first("event quit", (SIGNAL_FUNC) event_quit); + signal_add("server disconnected", (SIGNAL_FUNC) sig_disconnected); +} + +void netsplit_deinit(void) +{ + GSList *tmp; + + for (tmp = servers; tmp != NULL; tmp = tmp->next) + sig_disconnected(tmp->data); + + g_source_remove(split_tag); + signal_remove("event join", (SIGNAL_FUNC) event_join); + signal_remove("event quit", (SIGNAL_FUNC) event_quit); + signal_remove("server disconnected", (SIGNAL_FUNC) sig_disconnected); +} |