mirror of
https://github.com/irssi/irssi.git
synced 2026-08-17 23:52:22 +02:00
Added bot plugin, it also has almost-functional botnet.
Changed configure.in's functionality so that you could tell what modules you want to build in main irssi binary and it will create automatically the .c files that need to call the module_init()/deinit() functions. Fixed several minor things.. git-svn-id: http://svn.irssi.org/repos/irssi/trunk@230 dbcabf3a-b0e7-0310-adc4-f8d773084564
This commit is contained in:
parent
487da41745
commit
76605ad0ae
41 changed files with 2809 additions and 246 deletions
503
src/irc/bot/bot-users.c
Normal file
503
src/irc/bot/bot-users.c
Normal file
|
|
@ -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);
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue