diff options
Diffstat (limited to 'src/irc/bot/bot-users.c')
-rw-r--r-- | src/irc/bot/bot-users.c | 503 |
1 files changed, 503 insertions, 0 deletions
diff --git a/src/irc/bot/bot-users.c b/src/irc/bot/bot-users.c new file mode 100644 index 00000000..5db917a1 --- /dev/null +++ b/src/irc/bot/bot-users.c @@ -0,0 +1,503 @@ +/* + bot-users.c : IRC bot plugin for irssi - user handling + + 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 +*/ + +#define _XOPEN_SOURCE /* for crypt() */ +#include "module.h" +#include "signals.h" +#include "misc.h" +#include "lib-config/iconfig.h" + +#include "channels.h" +#include "nicklist.h" +#include "masks.h" + +#include "bot-users.h" + +#define WRITE_USERS_INTERVAL (60*15) + +static char *user_flags = "oavm"; /* Keep these in the same order as USER_xxx flags */ + +static CONFIG_REC *userconfig; +static GHashTable *users; + +static int writeusers_tag; +static time_t last_write; + +int botuser_flags2value(const char *flags) +{ + char *pos; + int val; + + g_return_val_if_fail(flags != NULL, 0); + + val = 0; + while (*flags != '\0') { + pos = strchr(user_flags, *flags); + if (pos != NULL) + val |= 1 << (int) (pos-user_flags); + flags++; + } + + return val; +} + +char *botuser_value2flags(int value) +{ + char *str, *p; + int n; + + p = str = g_malloc(USER_FLAG_COUNT+1); + for (n = 0; n < USER_FLAG_COUNT; n++) { + if (value & (1 << n)) + *p++ = user_flags[n]; + } + *p = '\0'; + + return str; +} + +/* save channel specific user record */ +static void botuser_save_chan(const char *key, USER_CHAN_REC *rec, CONFIG_NODE *node) +{ + CONFIG_NODE *noderec; + char *str; + + noderec = config_node_section(node, rec->channel, NODE_TYPE_BLOCK); + + str = rec->flags == 0 ? NULL : + botuser_value2flags(rec->flags); + config_node_set_str(userconfig, noderec, "flags", str); + g_free_not_null(str); +} + +static void botuser_config_save(USER_REC *user) +{ + CONFIG_NODE *node, *subnode, *noderec; + GSList *tmp; + char *str; + + node = config_node_traverse(userconfig, "users", TRUE); + node = config_node_section(node, user->nick, NODE_TYPE_BLOCK); + + str = user->flags == 0 ? NULL : + botuser_value2flags(user->flags); + config_node_set_str(userconfig, node, "flags", str); + g_free_not_null(str); + + config_node_set_str(userconfig, node, "password", user->password); + + /* Save masks */ + if (user->masks == NULL) + config_node_set_str(userconfig, node, "masks", NULL); + else { + subnode = config_node_section(node, "masks", NODE_TYPE_LIST); + + for (tmp = user->masks; tmp != NULL; tmp = tmp->next) { + USER_MASK_REC *rec = tmp->data; + + noderec = config_node_section(subnode, NULL, NODE_TYPE_BLOCK); + config_node_set_str(userconfig, noderec, "mask", rec->mask); + + str = user->flags == 0 ? NULL : + botuser_value2flags(rec->not_flags); + config_node_set_str(userconfig, noderec, "not_flags", str); + g_free_not_null(str); + } + } + + /* Save channels */ + if (g_hash_table_size(user->channels) == 0) + config_node_set_str(userconfig, node, "channels", NULL); + else { + subnode = config_node_section(node, "channels", NODE_TYPE_LIST); + g_hash_table_foreach(user->channels, (GHFunc) botuser_save_chan, subnode); + } +} + +static USER_MASK_REC *botuser_create_mask(USER_REC *user, const char *mask) +{ + USER_MASK_REC *rec; + + rec = g_new0(USER_MASK_REC, 1); + rec->mask = g_strdup(mask); + + user->masks = g_slist_append(user->masks, rec); + return rec; +} + +USER_MASK_REC *botuser_add_mask(USER_REC *user, const char *mask) +{ + USER_MASK_REC *rec; + + rec = botuser_create_mask(user, mask); + botuser_config_save(user); + return rec; +} + +static int botuser_find_mask(USER_REC *user, const char *nick, const char *host) +{ + GSList *tmp; + + g_return_val_if_fail(user != NULL, FALSE); + g_return_val_if_fail(nick != NULL, FALSE); + g_return_val_if_fail(host != NULL, FALSE); + + /* Check that masks match */ + for (tmp = user->masks; tmp != NULL; tmp = tmp->next) { + USER_MASK_REC *rec = tmp->data; + + if (irc_mask_match_address(rec->mask, nick, host)) { + user->not_flags = rec->not_flags; + return TRUE; + } + } + + return FALSE; +} + +static void botuser_getusers_hash(void *key, USER_REC *user, GList **list) +{ + *list = g_list_append(*list, user); +} + +USER_REC *botuser_find(const char *nick, const char *host) +{ + USER_REC *user; + char *stripnick; + GList *list, *tmp; + + g_return_val_if_fail(nick != NULL, NULL); + + /* First check for user with same nick */ + stripnick = nick_strip(nick); + user = g_hash_table_lookup(users, stripnick); + g_free(stripnick); + + if (user != NULL && host != NULL && + !botuser_find_mask(user, nick, host)) { + /* mask didn't match, check for more.. */ + user = NULL; + } + + if (user != NULL || host == NULL) + return user; + + /* Check for different nicks.. */ + list = NULL; + g_hash_table_foreach(users, (GHFunc) botuser_getusers_hash, &list); + for (tmp = list; tmp != NULL; tmp = tmp->next) { + if (botuser_find_mask(tmp->data, nick, host)) { + user = tmp->data; + break; + } + } + g_list_free(list); + + return user; +} + +USER_REC *botuser_find_rec(CHANNEL_REC *channel, NICK_REC *nick) +{ + USER_REC *user, *rec; + USER_CHAN_REC *userchan; + GList *list, *tmp; + + g_return_val_if_fail(channel != NULL, NULL); + g_return_val_if_fail(nick != NULL, NULL); + + user = NULL; list = NULL; + g_hash_table_foreach(users, (GHFunc) botuser_getusers_hash, &list); + for (tmp = list; tmp != NULL; tmp = tmp->next) { + rec = tmp->data; + + userchan = g_hash_table_lookup(rec->channels, channel->name); + if (userchan != NULL && userchan->nickrec == nick) { + user = rec; + break; + } + } + g_list_free(list); + + return user; +} + +static USER_CHAN_REC *botuser_channel(USER_REC *user, const char *channel) +{ + USER_CHAN_REC *rec; + + g_return_val_if_fail(user != NULL, NULL); + g_return_val_if_fail(channel != NULL, NULL); + + rec = g_hash_table_lookup(user->channels, channel); + if (rec != NULL) return rec; + + rec = g_new0(USER_CHAN_REC, 1); + rec->channel = g_strdup(channel); + g_hash_table_insert(user->channels, rec->channel, rec); + return rec; +} + +void botuser_set_password(USER_REC *user, const char *password) +{ + char *pass, salt[3]; + + g_return_if_fail(user != NULL); + g_return_if_fail(password != NULL); + + salt[0] = rand()%20 + 'A'; + salt[1] = rand()%20 + 'A'; + salt[2] = '\0'; + pass = crypt(password, salt); + + if (user->password != NULL) g_free(user->password); + user->password = g_strdup(pass); + botuser_config_save(user); +} + +int botuser_verify_password(USER_REC *user, const char *password) +{ + char *pass, salt[3]; + + g_return_val_if_fail(user != NULL, FALSE); + g_return_val_if_fail(password != NULL, FALSE); + + if (user->password == NULL || strlen(user->password) < 3) + return FALSE; + + salt[0] = user->password[0]; + salt[1] = user->password[1]; + salt[2] = '\0'; + pass = crypt(password, salt); + return strcmp(user->password, pass) == 0; +} + +static void event_massjoin(CHANNEL_REC *channel, GSList *nicks) +{ + USER_REC *user; + USER_CHAN_REC *userchan; + GSList *users; + + g_return_if_fail(channel != NULL); + g_return_if_fail(nicks != NULL); + + users = NULL; + for (; nicks != NULL; nicks = nicks->next) { + NICK_REC *rec = nicks->data; + + user = botuser_find(rec->nick, rec->host); + if (user != NULL) { + userchan = botuser_channel(user, channel->name); + userchan->nickrec = rec; + users = g_slist_append(users, user); + } + } + + if (users != NULL) { + signal_emit("bot massjoin", 2, channel, users); + g_slist_free(users); + } +} + +/* channel synced - find everyone's NICK_REC's */ +static void sig_channel_sync(CHANNEL_REC *channel) +{ + USER_REC *user; + USER_CHAN_REC *userchan; + GSList *tmp, *nicks; + + g_return_if_fail(channel != NULL); + + nicks = nicklist_getnicks(channel); + for (tmp = nicks; tmp != NULL; tmp = tmp->next) { + NICK_REC *rec = tmp->data; + + if (rec->send_massjoin) + continue; /* This will be checked in "massjoin" signal */ + + user = botuser_find(rec->nick, rec->host); + if (user != NULL) { + userchan = botuser_channel(user, channel->name); + userchan->nickrec = rec; + } + } + g_slist_free(nicks); +} + +/* user left channel - remove from users record */ +static void sig_nicklist_remove(CHANNEL_REC *channel, NICK_REC *nick) +{ + USER_REC *user; + USER_CHAN_REC *userchan; + + g_return_if_fail(channel != NULL); + g_return_if_fail(nick != NULL); + + user = botuser_find_rec(channel, nick); + userchan = user == NULL ? NULL : + g_hash_table_lookup(user->channels, channel->name); + if (userchan != NULL) userchan->nickrec = NULL; +} + +/* Free memory used by user channel record */ +static void user_destroy_chan(const char *key, USER_CHAN_REC *rec) +{ + g_free(rec->channel); + g_free(rec); +} + +static void usermask_destroy(USER_MASK_REC *rec) +{ + g_free(rec->mask); + g_free(rec); +} + +/* Free memory used by user record */ +static void user_destroy(const char *key, USER_REC *user) +{ + g_slist_foreach(user->masks, (GFunc) usermask_destroy, NULL); + g_slist_free(user->masks); + + g_hash_table_foreach(user->channels, (GHFunc) user_destroy_chan, NULL); + g_hash_table_destroy(user->channels); + + g_free_not_null(user->password); + g_free(user->nick); + g_free(user); +} + +static int sig_write_users(void) +{ + if (last_write + WRITE_USERS_INTERVAL <= time(NULL)) { + last_write = time(NULL); + config_write(userconfig, NULL, -1); + } + return 1; +} + +static void botuser_config_read_user(CONFIG_NODE *node) +{ + USER_REC *user; + USER_CHAN_REC *userchan; + USER_MASK_REC *usermask; + CONFIG_NODE *subnode; + GSList *tmp; + char *value; + + g_return_if_fail(node != NULL); + + /* nick = { ... } */ + if (node->key == NULL || node->value == NULL) + return; + + /* Add new user */ + user = g_new0(USER_REC, 1); + user->nick = g_strdup(node->key); + g_hash_table_insert(users, user->nick, user); + + /* password, flags */ + user->password = g_strdup(config_node_get_str(node, "password", NULL)); + user->flags = botuser_flags2value(config_node_get_str(node, "flags", "")); + + /* get masks */ + user->masks = NULL; + subnode = config_node_section(node, "masks", -1); + tmp = subnode == NULL ? NULL : subnode->value; + for (; tmp != NULL; tmp = tmp->next) { + subnode = tmp->data; + + value = config_node_get_str(subnode, "mask", NULL); + if (value == NULL) continue; /* mask is required */ + + usermask = botuser_create_mask(user, value); + value = config_node_get_str(subnode, "not_flags", ""); + usermask->not_flags = botuser_flags2value(value); + } + + /* get channels - must be last, messes up pvalue */ + user->channels = g_hash_table_new((GHashFunc) g_istr_hash, (GCompareFunc) g_istr_equal); + subnode = config_node_section(node, "channels", -1); + tmp = subnode == NULL ? NULL : subnode->value; + for (; tmp != NULL; tmp = tmp->next) { + subnode = tmp->data; + + value = config_node_get_str(subnode, "channel", NULL); + if (value == NULL) continue; /* channel is required */ + + /* create user channel specific record */ + userchan = g_new0(USER_CHAN_REC, 1); + userchan->channel = g_strdup(value); + g_hash_table_insert(user->channels, userchan->channel, userchan); + + value = config_node_get_str(subnode, "flags", ""); + userchan->flags = botuser_flags2value(value); + } +} + +static void botuser_config_read(void) +{ + CONFIG_NODE *node; + GSList *tmp; + char *fname; + + /* Read users from ~/.irssi/users */ + fname = g_strdup_printf("%s/.irssi/users", g_get_home_dir()); + userconfig = config_open(fname, 0600); + g_free(fname); + + if (userconfig == NULL) + return; /* access denied?! */ + + config_parse(userconfig); + + node = config_node_traverse(userconfig, "users", FALSE); + tmp = node == NULL ? NULL : node->value; + for (; tmp != NULL; tmp = tmp->next) + botuser_config_read_user(tmp->data); +} + +void bot_users_init(void) +{ + users = g_hash_table_new((GHashFunc) g_istr_hash, (GCompareFunc) g_istr_equal); + + last_write = time(NULL); + writeusers_tag = g_timeout_add(10000, (GSourceFunc) sig_write_users, NULL); + + botuser_config_read(); + signal_add_last("massjoin", (SIGNAL_FUNC) event_massjoin); + signal_add_last("channel sync", (SIGNAL_FUNC) sig_channel_sync); + signal_add_last("nicklist remove", (SIGNAL_FUNC) sig_nicklist_remove); +} + +void bot_users_deinit(void) +{ + if (userconfig != NULL) { + config_write(userconfig, NULL, -1); + config_close(userconfig); + } + + g_source_remove(writeusers_tag); + + g_hash_table_foreach(users, (GHFunc) user_destroy, NULL); + g_hash_table_destroy(users); + + signal_remove("massjoin", (SIGNAL_FUNC) event_massjoin); + signal_remove("channel sync", (SIGNAL_FUNC) sig_channel_sync); + signal_remove("nicklist remove", (SIGNAL_FUNC) sig_nicklist_remove); +} |