diff options
Diffstat (limited to '')
-rw-r--r-- | src/fe-common/irc/fe-netsplit.c | 389 |
1 files changed, 389 insertions, 0 deletions
diff --git a/src/fe-common/irc/fe-netsplit.c b/src/fe-common/irc/fe-netsplit.c new file mode 100644 index 0000000..b76d4ce --- /dev/null +++ b/src/fe-common/irc/fe-netsplit.c @@ -0,0 +1,389 @@ +/* + fe-netsplit.c : irssi + + 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., + 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. +*/ + +#include "module.h" +#include <irssi/src/fe-common/irc/module-formats.h> +#include <irssi/src/core/signals.h> +#include <irssi/src/core/levels.h> +#include <irssi/src/core/settings.h> + +#include <irssi/src/irc/core/irc-servers.h> +#include <irssi/src/irc/core/irc-commands.h> +#include <irssi/src/core/ignore.h> +#include <irssi/src/irc/core/netsplit.h> + +#include <irssi/src/fe-common/core/printtext.h> + +#define SPLIT_WAIT_TIME 5 /* how many seconds to wait for the QUIT split messages to stop */ + +static int split_tag; +static int netsplit_max_nicks, netsplit_nicks_hide_threshold; +static int printing_splits; + +static int get_last_split(IRC_SERVER_REC *server) +{ + GSList *tmp; + time_t last; + + last = 0; + for (tmp = server->split_servers; tmp != NULL; tmp = tmp->next) { + NETSPLIT_SERVER_REC *rec = tmp->data; + + if (rec->last > last) last = rec->last; + } + + return last; +} + +typedef struct { + char *name; + int nick_count, maxnickpos; + GString *nicks; +} TEMP_SPLIT_CHAN_REC; + +typedef struct { + IRC_SERVER_REC *server_rec; + GSList *servers; /* if many servers splitted from the same one */ + GSList *channels; +} TEMP_SPLIT_REC; + +static GSList *get_source_servers(const char *server, GSList **servers) +{ + GSList *list, *next, *tmp; + + list = NULL; + for (tmp = *servers; tmp != NULL; tmp = next) { + NETSPLIT_SERVER_REC *rec = tmp->data; + next = tmp->next; + + if (g_ascii_strcasecmp(rec->server, server) == 0) { + rec->prints = 0; + list = g_slist_append(list, rec); + *servers = g_slist_remove(*servers, rec); + } + } + + return list; +} + +static TEMP_SPLIT_CHAN_REC *find_split_chan(TEMP_SPLIT_REC *rec, + const char *name) +{ + GSList *tmp; + + for (tmp = rec->channels; tmp != NULL; tmp = tmp->next) { + TEMP_SPLIT_CHAN_REC *chanrec = tmp->data; + + if (g_ascii_strcasecmp(chanrec->name, name) == 0) + return chanrec; + } + + return NULL; +} + +static void get_server_splits(void *key, NETSPLIT_REC *split, + TEMP_SPLIT_REC *rec) +{ + TEMP_SPLIT_CHAN_REC *chanrec; + GSList *tmp; + + if (split->printed || + g_slist_find(rec->servers, split->server) == NULL) + return; + + split->printed = TRUE; + for (tmp = split->channels; tmp != NULL; tmp = tmp->next) { + NETSPLIT_CHAN_REC *splitchan = tmp->data; + + if (ignore_check(SERVER(rec->server_rec), split->nick, + split->address, splitchan->name, "", + MSGLEVEL_QUITS)) + continue; + + chanrec = find_split_chan(rec, splitchan->name); + if (chanrec == NULL) { + chanrec = g_new0(TEMP_SPLIT_CHAN_REC, 1); + chanrec->name = splitchan->name; + chanrec->nicks = g_string_new(NULL); + + rec->channels = g_slist_append(rec->channels, chanrec); + } + + split->server->prints++; + chanrec->nick_count++; + if (netsplit_nicks_hide_threshold <= 0 || + chanrec->nick_count <= netsplit_nicks_hide_threshold) { + if (splitchan->op) + g_string_append_c(chanrec->nicks, '@'); + else if (splitchan->voice) + g_string_append_c(chanrec->nicks, '+'); + g_string_append_printf(chanrec->nicks, "%s, ", split->nick); + + if (chanrec->nick_count == netsplit_max_nicks) + chanrec->maxnickpos = chanrec->nicks->len; + } + } +} + +static void print_server_splits(IRC_SERVER_REC *server, TEMP_SPLIT_REC *rec, const char *filter_channel) +{ + GString *destservers; + char *sourceserver; + GSList *tmp; + + g_return_if_fail(rec->servers != NULL); + + destservers = g_string_new(NULL); + for (tmp = rec->servers; tmp != NULL; tmp = tmp->next) { + NETSPLIT_SERVER_REC *rec = tmp->data; + + if (rec->prints > 0) { + g_string_append_printf(destservers, "%s, ", + rec->destserver); + } + } + if (destservers->len == 0) { + /* no nicks to print in this server */ + g_string_free(destservers, TRUE); + return; + } + g_string_truncate(destservers, destservers->len-2); + + sourceserver = ((NETSPLIT_SERVER_REC *) (rec->servers->data))->server; + for (tmp = rec->channels; tmp != NULL; tmp = tmp->next) { + TEMP_SPLIT_CHAN_REC *chan = tmp->data; + + if (filter_channel != NULL && + strcasecmp(chan->name, filter_channel) != 0) + continue; + + g_string_truncate(chan->nicks, chan->nicks->len-2); + + if (netsplit_max_nicks > 0 && + chan->nick_count > netsplit_max_nicks) { + g_string_truncate(chan->nicks, chan->maxnickpos); + printformat(server, chan->name, MSGLEVEL_QUITS, + IRCTXT_NETSPLIT_MORE, sourceserver, + destservers->str, chan->nicks->str, + chan->nick_count - netsplit_max_nicks); + } else { + printformat(server, chan->name, MSGLEVEL_QUITS, + IRCTXT_NETSPLIT, sourceserver, + destservers->str, chan->nicks->str); + } + } + + g_string_free(destservers, TRUE); +} + +static void temp_split_chan_free(TEMP_SPLIT_CHAN_REC *rec) +{ + g_string_free(rec->nicks, TRUE); + g_free(rec); +} + +static void print_splits(IRC_SERVER_REC *server, const char *filter_channel) +{ + TEMP_SPLIT_REC temp; + GSList *servers; + + printing_splits = TRUE; + + servers = g_slist_copy(server->split_servers); + while (servers != NULL) { + NETSPLIT_SERVER_REC *sserver = servers->data; + + /* get all the splitted servers that have the same + source server */ + temp.servers = get_source_servers(sserver->server, &servers); + temp.server_rec = server; + temp.channels = NULL; + + g_hash_table_foreach(server->splits, + (GHFunc) get_server_splits, &temp); + print_server_splits(server, &temp, filter_channel); + + g_slist_foreach(temp.channels, + (GFunc) temp_split_chan_free, NULL); + g_slist_free(temp.servers); + g_slist_free(temp.channels); + } + + printing_splits = FALSE; +} + +static int check_server_splits(IRC_SERVER_REC *server) +{ + time_t last; + + g_return_val_if_fail(IS_IRC_SERVER(server), FALSE); + + last = get_last_split(server); + if (time(NULL)-last < SPLIT_WAIT_TIME) + return FALSE; + + print_splits(server, NULL); + return TRUE; +} + +/* something is going to be printed to screen, print our current netsplit + message before it. */ +static void sig_print_starting(TEXT_DEST_REC *dest) +{ + IRC_SERVER_REC *rec; + + if (printing_splits) + return; + + if (!IS_IRC_SERVER(dest->server)) + return; + + rec = IRC_SERVER(dest->server); + if (rec->split_servers != NULL) { + /* if split_servers exists, the server rec should be + still valid. otherwise, calling server->ischannel + may not be safe. */ + if (dest->target != NULL && !server_ischannel((SERVER_REC *) rec, dest->target)) + return; + + print_splits(rec, NULL); + } +} + +static int sig_check_splits(void) +{ + GSList *tmp; + int stop; + + stop = TRUE; + for (tmp = servers; tmp != NULL; tmp = tmp->next) { + IRC_SERVER_REC *rec = tmp->data; + + if (!IS_IRC_SERVER(rec)) + continue; + + if (rec->split_servers != NULL) { + if (!check_server_splits(rec)) + stop = FALSE; + } + } + + if (stop) { + g_source_remove(split_tag); + signal_remove("print starting", (SIGNAL_FUNC) sig_print_starting); + split_tag = -1; + } + return 1; +} + +static void sig_netsplit_servers(void) +{ + if (settings_get_bool("hide_netsplit_quits") && split_tag == -1) { + split_tag = g_timeout_add(1000, + (GSourceFunc) sig_check_splits, + NULL); + signal_add("print starting", (SIGNAL_FUNC) sig_print_starting); + } +} + +static int split_equal(NETSPLIT_REC *n1, NETSPLIT_REC *n2) +{ + return g_ascii_strcasecmp(n1->nick, n2->nick); +} + +static void split_get(void *key, NETSPLIT_REC *rec, GSList **list) +{ + *list = g_slist_insert_sorted(*list, rec, + (GCompareFunc) split_equal); +} + +static void split_print(NETSPLIT_REC *rec, SERVER_REC *server) +{ + NETSPLIT_CHAN_REC *chan; + char *chanstr; + + chan = rec->channels->data; + chanstr = chan == NULL ? + g_strdup("") : + g_strconcat(chan->op ? "@" : (chan->voice ? "+" : ""), chan->name, NULL); + + printformat(server, NULL, MSGLEVEL_CLIENTCRAP, IRCTXT_NETSPLITS_LINE, + rec->nick, chanstr, rec->server->server, + rec->server->destserver); + + g_free(chanstr); +} + +/* SYNTAX: NETSPLIT */ +static void cmd_netsplit(const char *data, IRC_SERVER_REC *server) +{ + GSList *list; + + CMD_IRC_SERVER(server); + + if (server->split_servers == NULL) { + printformat(server, NULL, MSGLEVEL_CLIENTNOTICE, + IRCTXT_NO_NETSPLITS); + return; + } + + printformat(server, NULL, MSGLEVEL_CLIENTCRAP, IRCTXT_NETSPLITS_HEADER); + + list = NULL; + g_hash_table_foreach(server->splits, (GHFunc) split_get, &list); + g_slist_foreach(list, (GFunc) split_print, server); + g_slist_free(list); + + printformat(server, NULL, MSGLEVEL_CLIENTCRAP, IRCTXT_NETSPLITS_FOOTER); +} + +static void read_settings(void) +{ + netsplit_max_nicks = settings_get_int("netsplit_max_nicks"); + netsplit_nicks_hide_threshold = + settings_get_int("netsplit_nicks_hide_threshold"); + if (netsplit_nicks_hide_threshold < netsplit_max_nicks) + netsplit_max_nicks = netsplit_nicks_hide_threshold; +} + +void fe_netsplit_init(void) +{ + settings_add_int("misc", "netsplit_max_nicks", 10); + settings_add_int("misc", "netsplit_nicks_hide_threshold", 15); + split_tag = -1; + printing_splits = FALSE; + + read_settings(); + signal_add("netsplit new", (SIGNAL_FUNC) sig_netsplit_servers); + signal_add("setup changed", (SIGNAL_FUNC) read_settings); + command_bind_irc("netsplit", NULL, (SIGNAL_FUNC) cmd_netsplit); +} + +void fe_netsplit_deinit(void) +{ + if (split_tag != -1) { + g_source_remove(split_tag); + signal_remove("print starting", (SIGNAL_FUNC) sig_print_starting); + } + + signal_remove("netsplit new", (SIGNAL_FUNC) sig_netsplit_servers); + signal_remove("setup changed", (SIGNAL_FUNC) read_settings); + command_unbind("netsplit", (SIGNAL_FUNC) cmd_netsplit); +} |