/* notify-ison.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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */ #include "module.h" #include "signals.h" #include "misc.h" #include "settings.h" #include "irc.h" #include "irc-servers.h" #include "servers-redirect.h" #include "notifylist.h" #define DEFAULT_NOTIFY_CHECK_TIME "1min" #define DEFAULT_NOTIFY_WHOIS_TIME "5min" typedef struct { char *nick; int hostok; } ISON_REC; static int notify_tag; static int notify_whois_time; NOTIFY_NICK_REC *notify_nick_create(IRC_SERVER_REC *server, const char *nick) { MODULE_SERVER_REC *mserver; NOTIFY_NICK_REC *rec; mserver = MODULE_DATA(server); rec = g_new0(NOTIFY_NICK_REC, 1); rec->nick = g_strdup(nick); mserver->notify_users = g_slist_append(mserver->notify_users, rec); return rec; } void notify_nick_destroy(NOTIFY_NICK_REC *rec) { g_free(rec->nick); g_free_not_null(rec->user); g_free_not_null(rec->host); g_free_not_null(rec->realname); g_free_not_null(rec->awaymsg); g_free(rec); } NOTIFY_NICK_REC *notify_nick_find(IRC_SERVER_REC *server, const char *nick) { MODULE_SERVER_REC *mserver; NOTIFY_NICK_REC *rec; GSList *tmp; mserver = MODULE_DATA(server); for (tmp = mserver->notify_users; tmp != NULL; tmp = tmp->next) { rec = tmp->data; if (g_strcasecmp(rec->nick, nick) == 0) return rec; } return NULL; } static void ison_send(IRC_SERVER_REC *server, GString *cmd) { MODULE_SERVER_REC *mserver; mserver = MODULE_DATA(server); mserver->ison_count++; g_string_truncate(cmd, cmd->len-1); g_string_prepend(cmd, "ISON :"); server_redirect_event(server, "ison", 1, NULL, -1, NULL, "event 303", "notifylist event", NULL); irc_send_cmd(server, cmd->str); g_string_truncate(cmd, 0); } /* timeout function: send /ISON commands to server to check if someone in notify list is in IRC */ static void notifylist_timeout_server(IRC_SERVER_REC *server) { MODULE_SERVER_REC *mserver; GSList *tmp; GString *cmd; char *nick, *ptr; int len; g_return_if_fail(server != NULL); if (!IS_IRC_SERVER(server)) return; mserver = MODULE_DATA(server); if (mserver->ison_count > 0) { /* still not received all replies to previous /ISON commands.. */ return; } cmd = g_string_new(NULL); for (tmp = notifies; tmp != NULL; tmp = tmp->next) { NOTIFYLIST_REC *rec = tmp->data; if (!notifylist_ircnets_match(rec, server->connrec->chatnet)) continue; nick = g_strdup(rec->mask); ptr = strchr(nick, '!'); if (ptr != NULL) *ptr = '\0'; len = strlen(nick); if (cmd->len+len+1 > 510) ison_send(server, cmd); g_string_append_printf(cmd, "%s ", nick); g_free(nick); } if (cmd->len > 0) ison_send(server, cmd); g_string_free(cmd, TRUE); } static int notifylist_timeout_func(void) { g_slist_foreach(servers, (GFunc) notifylist_timeout_server, NULL); return 1; } static void ison_save_users(MODULE_SERVER_REC *mserver, char *online) { char *ptr; while (online != NULL && *online != '\0') { ptr = strchr(online, ' '); if (ptr != NULL) *ptr++ = '\0'; mserver->ison_tempusers = g_slist_append(mserver->ison_tempusers, g_strdup(online)); online = ptr; } } static void whois_send(IRC_SERVER_REC *server, const char *nicks, const char *whois_request) { char *p, *str; /* "nick1,nick2" -> "nick1,nick2 nick1 nick2" because End of WHOIS give nick1,nick2 while other whois events give nick1 or nick2 */ str = g_strconcat(nicks, " ", nicks, NULL); for (p = str+strlen(nicks)+1; *p != '\0'; p++) if (*p == ',') *p = ' '; server_redirect_event(server, "whois", 1, str, TRUE, "notifylist event whois end", "event 318", "notifylist event whois end", "event 311", "notifylist event whois", "event 301", "notifylist event whois away", "", "event empty", NULL); g_free(str); irc_send_cmdv(server, "WHOIS %s", whois_request); } static void whois_send_server(IRC_SERVER_REC *server, char *nick) { char *str; str = g_strdup_printf("%s %s", nick, nick); whois_send(server, nick, str); g_free(str); } /* try to send as many nicks in one WHOIS as possible */ static void whois_list_send(IRC_SERVER_REC *server, GSList *nicks) { GSList *tmp; GString *str; char *nick; int count; str = g_string_new(NULL); count = 0; for (tmp = nicks; tmp != NULL; tmp = tmp->next) { nick = tmp->data; count++; g_string_append_printf(str, "%s,", nick); if (count >= server->max_whois_in_cmd) { g_string_truncate(str, str->len-1); whois_send(server, str->str, str->str); g_string_truncate(str, 0); count = 0; } } if (str->len > 0) { g_string_truncate(str, str->len-1); whois_send(server, str->str, str->str); } g_string_free(str, TRUE); } static void ison_check_joins(IRC_SERVER_REC *server) { MODULE_SERVER_REC *mserver; NOTIFYLIST_REC *notify; NOTIFY_NICK_REC *rec; GSList *tmp, *newnicks; int send_whois; time_t now; mserver = MODULE_DATA(server); now = time(NULL); newnicks = NULL; for (tmp = mserver->ison_tempusers; tmp != NULL; tmp = tmp->next) { char *nick = tmp->data; notify = notifylist_find(nick, server->connrec->chatnet); send_whois = notify != NULL && notify->away_check; rec = notify_nick_find(server, nick); if (rec != NULL) { /* check if we want to send WHOIS yet.. */ if (now-rec->last_whois < notify_whois_time) continue; } else { rec = notify_nick_create(server, nick); if (!send_whois) newnicks = g_slist_append(newnicks, nick); } if (send_whois) { /* we need away message - send the WHOIS reply to the nick's server */ rec->last_whois = now; whois_send_server(server, nick); } } whois_list_send(server, newnicks); g_slist_free(newnicks); } static void ison_check_parts(IRC_SERVER_REC *server) { MODULE_SERVER_REC *mserver; GSList *tmp, *next; mserver = MODULE_DATA(server); for (tmp = mserver->notify_users; tmp != NULL; tmp = next) { NOTIFY_NICK_REC *rec = tmp->data; next = tmp->next; if (gslist_find_icase_string(mserver->ison_tempusers, rec->nick) != NULL) continue; notifylist_left(server, rec); } } static void event_ison(IRC_SERVER_REC *server, const char *data) { MODULE_SERVER_REC *mserver; char *params, *online; g_return_if_fail(data != NULL); g_return_if_fail(server != NULL); params = event_get_params(data, 2, NULL, &online); mserver = MODULE_DATA(server); ison_save_users(mserver, online); if (--mserver->ison_count > 0) { /* wait for the rest of the /ISON replies */ g_free(params); return; } ison_check_joins(server); ison_check_parts(server); /* free memory used by temp list */ g_slist_foreach(mserver->ison_tempusers, (GFunc) g_free, NULL); g_slist_free(mserver->ison_tempusers); mserver->ison_tempusers = NULL; g_free(params); } static void read_settings(void) { if (notify_tag != -1) g_source_remove(notify_tag); notify_tag = g_timeout_add(settings_get_time("notify_check_time"), (GSourceFunc) notifylist_timeout_func, NULL); notify_whois_time = settings_get_time("notify_whois_time")/1000; } void notifylist_ison_init(void) { settings_add_time("misc", "notify_check_time", DEFAULT_NOTIFY_CHECK_TIME); settings_add_time("misc", "notify_whois_time", DEFAULT_NOTIFY_WHOIS_TIME); notify_tag = -1; read_settings(); signal_add("notifylist event", (SIGNAL_FUNC) event_ison); signal_add("setup changed", (SIGNAL_FUNC) read_settings); } void notifylist_ison_deinit(void) { g_source_remove(notify_tag); signal_remove("notifylist event", (SIGNAL_FUNC) event_ison); signal_remove("setup changed", (SIGNAL_FUNC) read_settings); }