1
0
mirror of https://github.com/irssi/irssi.git synced 2024-12-04 14:46:39 -05:00

Remove long unmaintained botnet module.

git-svn-id: http://svn.irssi.org/repos/irssi/trunk@4723 dbcabf3a-b0e7-0310-adc4-f8d773084564
This commit is contained in:
Emanuele Giaquinta 2008-03-04 18:15:51 +00:00 committed by exg
parent df0536c603
commit 3e37906cf7
17 changed files with 0 additions and 3248 deletions

View File

@ -4,7 +4,6 @@ man_MANS = \
irssi.1
doc_DATA = \
botnet.txt \
design.txt \
formats.txt \
manual.txt \

View File

@ -1,316 +0,0 @@
Irssi's botnet description
Copyright (c) 1999-2000 Timo Sirainen
0. History
draft v0.1 : 21.8.1999
Just a first draft of my botnet design I did on a boring friday
work afternoon :) I'll try to implement this to irssi some day, it
feels pretty interesting now so it might be pretty soon even. Any
comments are welcome :)
draft v0.2 : 21.11.1999
Exactly three months since the first draft :) Now I actually have
some code done, just committed pretty simple botnet to irssi CVS.
Made several changes to this document.. Still missing much details
but the basic idea should be clear.
draft v0.3 : 21.05.2000
Strange, again the same day. I really didn't plan this :)
Reformatted the text, added lots of text, implemented more of the
stuff.
1. General
1.1 Description
A small description of what botnet would do: A group of bots
efficiently working together to perform their tasks. Like when
someone's trying to take over your channel, bots will quickly
decide who deops/kicks whom instead of multiple bots deopping or
trying to kick the same people.
Irssi's botnet is pretty much based on trust. Some malicious bot
can quite well mess up the whole botnet. Connecting the bots to
each other via ssh would be a good idea.
1.2 Configuration
example config file:
mybotnet =
{
priority=5;
nick=mybot;
uplinks = (
{ host = "main.botnet.org"; password = "mypass"; },
{ host = "alter.botnet.org"; password = "pass2"; }
);
downlinks = (
{ password = "thepass"; valid_addrs = ( "192.168.0.*" ); },
{ password = "blah"; valid_addrs = ( "*.botnet.org" ); },
{ password = "localpass"; valid_addrs = ( "127.*" ); }
);
}
When connecting to botnet, the bot first tries to connect to the
first bot in uplinks list, then the second, etc. Setting port to -1
will prevent connecting to the bot, 0 uses the default.
1.3 Botnet master
To avoid total chaos inside botnet, the bots shouldn't do (almost)
anything without a command from botnet's master. The master should
know everything, and give commands to clients that can perform the
task best.
Master is the bot with the highest priority. If there's multiple
with the same priority, the one that already was the master will
stay master. When joining two botnets to one, the uplink's master
stays. If link to master breaks, the closest bot to it will choose
a new one.
The priorities should be given so that the bots that have the
fastest connections and are in the middle of the botnet have the
highest priorities.
1.4 Command format
Commands that are sent inside botnet are in the following format:
<from_nick> <to_nick> COMMAND [command specific data..]
If to_nick is '-', the command should be sent to everyone.
2. Handshake
First host checks from bots' valid_addrs who is connecting. If
there's no matches it just disconnects the client.
CLIENT: PASS <password>
HOST : (if error, disconnect)
CLIENT: NICK <nick>
HOST : NICKERROR | CONNECTED
If nick is already in use, the host sends NICKERROR and waits for
new nick.
Now we're connected to botnet. The commands from now on use the
format specified in section 1.4.
Both the client and the host sends information to other side of
all the clients they serve (if any):
BOTINFO <nick> <connected_to_nick> <priority>
BOTINFOs must be sent sorted so that connected_to_nick bot is
always known. Like first comes the bots connected to the
host/client, then the bots connected to them etc.
If the client had downlinks, nick collisions might happen. The
uplink is responsible for noticing them from BOTINFO commands.
It should automatically replace the nicks with new ones and
send nick change command to client and all it's downlinks. For
example if host received:
BOTINFO bot highbot 10
And the bot already exists, the BOTINFO is changed to:
BOTINFO bot2 highbot 10
And the client and it's downlinks are notified:
BOTNICK bot2 bot
After sending BOTINFOs, the host tells the current master:
MASTER <nick>
The client now checks if it's priority is higher than the current
master's. If it is, it will send the MASTER command without any
parameters.
3. Bot connections
3.1 General
Everyone's connections should be kept in n-way tree. Example:
[highuplink]
_____________/ | | \
/ | [h5] [h6]
[h1] | / | \
/ \ | [h7] | [h8]
[h2] [h3] | | \
| [uplink] [h9] [h10]
[h4] / | \
[up2] | [up1]
/ | | |
[up3] [up4] | [up5]
|
[we]
/ \
[client1] [client2]
/ \
[c3] [c4]
Botnet should try to keep together even if some hub in the middle
crashes. Each bot should have at least two uplinks in case one
dies. For example if [uplink] dies, [we] and [up1] could connect
to [up2], and [up2] could connect to [highuplink].
When connection is closed to some bot, a notice is sent by the
bot's uplink:
BOTQUIT <nick>
The quit notice should be sent only about the bot that was
disconnected. Bots should figure out themselves the other bots and
remove them too from their lists.
3.2 Lag
Each bot should send PING commands to their up/downlinks every
now and then (1min?) to check that the connection is still active.
If the PONG isn't received in 10 seconds, it's priority should be
temporarily lowered to -1. If the PONG isn't received in 3
minutes, the whole connection should be closed.
Master should know lag times of every bots. It could then
automatically raise/lower bots' priorities depending on how big
their lag is. Master could also lower it's own priority and pass
the master status to someone else with lower lag.
If there's lot of lag (>3sec?) somewhere and something urgent
happens, the botnet could split and behave independently.
4. IRC networks
4.1 Server connections
When bot is connected to some irc server and is ready to take
commands, it says:
IRCJOIN <tag> <ircnet> <server> <nick>
Tag is the bot specific unique tag of the server, so that the bot
can connect multiple times to same IRC network. All IRC related
commands should specify the server tag where it should be sent.
If bot quits an irc network, it says:
IRCQUIT <tag>
4.2 IRC commands
Master asks a bot to send some command to IRC server by saying:
CMD <id> <tag> <command>
<command> can't really be anything, since the bot should also be
able to reply to it. The <id> is for identifying the command/reply
pair. Master should keep the command in memory until it receives
the reply:
CMDREPLY <id> <last> <reply>
The command could get a reply of multiple lines, so <last>
specifies if the reply is the last line (1 or 0).
If the command failed for some reason, the bot will reply with
CMDFAIL <id>
and master should send the command to some other bot.
4.3 Channels
When joined/left channels, the bot says:
CHANJOIN <tag> <channel>
CHANPART <tag> <channel>
After BOTJOIN, master tries to op the bot. When bot receives +o,
it says:
CHANOP <tag> <channel>
If it is the first opped bot in channel, master orders the bot to
op the rest of the bots.
If the bot is kicked, it says:
CHANKICK <tag> <channel>
When master notices that bot is kicked, it first checks if there's
any other opped bots in channel. If not, it waits for a random
pause, 5-10sec before letting the bot join the channel again so
that it won't get autorejoin ban.
If bot can't join channel, it says:
CHANBANNED <tag> <channel>
(or)
CHANCANTJOIN <tag> <channel>
When received BOTBANNED, master tries to unban bot or set a ban
exception. BOTCANTJOIN results as invite to channel.
4.4 Channel information
When master notices that bot is the first one joined to channel,
it asks the bot for some channel information:
CMD <id> <tag> NAMES <channel>
CMD <id> <tag> WHO <channel>
CMD <id> <tag> MODE <channel>
CMD <id> <tag> MODE b <channel>
CMD <id> <tag> MODE e <channel> (if IRC network supports this)
CMD <id> <tag> MODE I <channel> (if IRC network supports this)
It's also possible that if several bots join immediately after the
first bot, the commands are shared between all the bots.
Bots should cache the information as much as possible, at least
NAMES command.
4.5 Channel priorities
Every channel has a priority: LOW, NORMAL, HIGH.
Normally LOW operates just as NORMAL channels, except when some
channel has HIGH priority and bots are really busy, LOW channels
just wait until there's time for them.
In NORMAL channels, the most urgent operations (kicks, ops, deops)
are performed quite soon even while bots are busy handling HIGH
priority commands.
Channels shouldn't normally be HIGH priority, but if attack
against channel is detected (like someone comes from split, gets
ops and gets to op someone else), it's priority is set to HIGH.
When channel's priority is HIGH, botnet does everything it can to
get rid of unauthorized opped people as fast as possible.
LOW channel's priority can also be raised to HIGH, but it's
priority is dropped back to LOW if some NORMAL channel's priority
is raised to HIGH too.
Master notifies about channel's priority change by saying:
CHANPRIORITY <ircnet> <channel> <LOW/NORMAL/HIGH>

View File

@ -1,32 +0,0 @@
moduledir = $(libdir)/irssi/modules
module_LTLIBRARIES = libirc_bot.la
libirc_bot_la_LDFLAGS = -avoid-version
INCLUDES = \
-I$(top_srcdir)/src \
-I$(top_srcdir)/src/core/ \
-I$(top_srcdir)/src/irc/core/ \
$(GLIB_CFLAGS)
libirc_bot_la_LIBADD = -lcrypt
libirc_bot_la_SOURCES = \
bot.c \
bot-irc-commands.c \
bot-events.c \
bot-users.c \
botnet.c \
botnet-connection.c \
botnet-users.c
noinst_HEADERS = \
bot.h \
bot-users.h \
botnet.h \
botnet-users.h \
module.h
EXTRA_DIST = \
users.sample \
botnets.sample

View File

@ -1,201 +0,0 @@
/*
bot-events.c : IRC bot plugin for 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.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
#include "module.h"
#include "signals.h"
#include "commands.h"
#include "irc.h"
#include "irc-servers.h"
#include "irc-channels.h"
#include "nicklist.h"
#include "modes.h"
#include "netsplit.h"
#include "bot-users.h"
static int get_flags(USER_REC *user, IRC_CHANNEL_REC *channel)
{
USER_CHAN_REC *userchan;
g_return_val_if_fail(user != NULL, 0);
g_return_val_if_fail(channel != NULL, 0);
userchan = g_hash_table_lookup(user->channels, channel->name);
return (user->flags | (userchan == NULL ? 0 : userchan->flags)) &
(~user->not_flags);
}
static void event_massjoin(IRC_CHANNEL_REC *channel, GSList *users)
{
USER_REC *user;
USER_CHAN_REC *userchan;
NICK_REC *nick;
GString *modestr, *nickstr;
int flags;
g_return_if_fail(channel != NULL);
g_return_if_fail(users != NULL);
modestr = g_string_new(NULL);
nickstr = g_string_new(NULL);
for (; users != NULL; users = users->next) {
user = users->data;
userchan = g_hash_table_lookup(user->channels, channel->name);
nick = userchan->nickrec;
flags = get_flags(user, channel);
if (!nick->op && (flags & USER_AUTO_OP)) {
g_string_sprintfa(modestr, "+o");
g_string_sprintfa(nickstr, "%s,", nick->nick);
}
if (!nick->voice && !nick->op && (flags & USER_AUTO_VOICE)) {
g_string_sprintfa(modestr, "+v");
g_string_sprintfa(nickstr, "%s,", nick->nick);
}
}
if (nickstr->len > 0) {
g_string_truncate(nickstr, nickstr->len-1);
g_string_sprintfa(modestr, " %s", nickstr->str);
channel_set_mode(channel->server, channel->name, modestr->str);
}
g_string_free(modestr, TRUE);
g_string_free(nickstr, TRUE);
}
/* Parse channel mode string */
static void parse_channel_mode(IRC_CHANNEL_REC *channel, const char *mode,
const char *nick, const char *address)
{
NETSPLIT_CHAN_REC *splitnick;
NICK_REC *nickrec;
USER_REC *user;
GString *str;
char *ptr, *curmode, type, *dup, *modestr;
int flags;
g_return_if_fail(channel != NULL);
g_return_if_fail(nick != NULL);
g_return_if_fail(mode != NULL);
user = botuser_find(nick, address);
flags = user == NULL ? 0 : get_flags(user, channel);
if (!channel->chanop || (flags & USER_MASTER) ||
g_strcasecmp(nick, channel->server->nick) == 0) {
/* can't do anything or we/master did mode change,
don't bother checking what */
return;
}
/* check if unwanted people got ops */
str = g_string_new(NULL);
dup = modestr = g_strdup(mode);
type = '+';
curmode = cmd_get_param(&modestr);
for (; *curmode != '\0'; curmode++) {
if (*curmode == '+' || *curmode == '-') {
type = *curmode;
continue;
}
if (!HAS_MODE_ARG(type, *curmode))
ptr = NULL;
else {
ptr = cmd_get_param(&modestr);
if (*ptr == '\0') continue;
}
if (*curmode != 'o')
continue;
if (type == '-' && strcmp(channel->server->nick, ptr) == 0) {
/* we aren't chanop anymore .. */
g_string_truncate(str, 0);
break;
}
if (type != '+')
continue;
/* check that op is valid */
nickrec = nicklist_find(CHANNEL(channel), ptr);
if (nickrec == NULL || nickrec->host == NULL)
continue;
user = botuser_find(ptr, nickrec->host);
flags = user == NULL ? 0 : get_flags(user, channel);
if (flags & USER_OP)
continue;
if (address == NULL) {
/* server opped, check if user was opped before netsplit. */
splitnick = netsplit_find_channel(channel->server, nickrec->nick, nickrec->host, channel->name);
if (splitnick != NULL && splitnick->op)
continue;
}
/* this one isn't supposed to get ops! */
g_string_sprintfa(str, "%s ", ptr);
}
g_free(dup);
if (str->len != 0)
signal_emit("command deop", 3, str->str, channel->server, channel);
g_string_free(str, TRUE);
}
static void event_mode(IRC_SERVER_REC *server, const char *data,
const char *nick, const char *address)
{
IRC_CHANNEL_REC *chanrec;
char *params, *channel, *mode;
g_return_if_fail(data != NULL);
params = event_get_params(data, 2 | PARAM_FLAG_GETREST, &channel, &mode);
if (ischannel(*channel)) {
/* channel mode change */
chanrec = irc_channel_find(server, channel);
if (chanrec != NULL)
parse_channel_mode(chanrec, mode, nick, address);
}
g_free(params);
}
void bot_events_init(void)
{
signal_add_last("bot massjoin", (SIGNAL_FUNC) event_massjoin);
signal_add("event mode", (SIGNAL_FUNC) event_mode);
}
void bot_events_deinit(void)
{
signal_remove("bot massjoin", (SIGNAL_FUNC) event_massjoin);
signal_remove("event mode", (SIGNAL_FUNC) event_mode);
}

View File

@ -1,164 +0,0 @@
/*
bot-irc-commands.c : IRC bot plugin for 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 "commands.h"
#include "irc.h"
#include "irc-servers.h"
#include "channels.h"
#include "nicklist.h"
#include "irc-masks.h"
#include "bot-users.h"
#include "botnet-users.h"
static void event_privmsg(IRC_SERVER_REC *server, const char *data,
const char *nick, const char *address)
{
char *params, *target, *msg, *args, *str;
g_return_if_fail(data != NULL);
params = event_get_params(data, 2 | PARAM_FLAG_GETREST, &target, &msg);
if (ischannel(*target)) {
g_free(params);
return;
}
/* private message for us */
str = g_strconcat("bot command ", msg, NULL);
args = strchr(str+12, ' ');
if (args != NULL) *args++ = '\0'; else args = "";
g_strdown(str);
if (signal_emit(str, 4, server, args, nick, address)) {
/* msg was a command - the msg event. */
signal_stop();
}
g_free(str);
g_free(params);
}
static void botcmd_op(IRC_SERVER_REC *server, const char *data,
const char *nick, const char *address)
{
CHANNEL_REC *channel;
USER_REC *user;
USER_CHAN_REC *userchan;
GSList *tmp;
g_return_if_fail(data != NULL);
if (*data == '\0') {
/* no password given? .. */
return;
}
user = botuser_find(nick, address);
if (user == NULL || (user->not_flags & USER_OP) ||
!botuser_verify_password(user, data)) {
/* not found, can't op with this mask or failed password */
return;
}
/* find the channels where to op.. */
for (tmp = server->channels; tmp != NULL; tmp = tmp->next) {
channel = tmp->data;
userchan = g_hash_table_lookup(user->channels, channel->name);
if ((user->flags & USER_OP) || (userchan->flags & USER_OP))
signal_emit("command op", 3, nick, server, channel);
}
}
static void botcmd_ident(IRC_SERVER_REC *server, const char *data,
const char *nick, const char *address)
{
USER_REC *user;
char *mask;
g_return_if_fail(data != NULL);
user = botuser_find(nick, address);
if (user != NULL) {
/* Already know this host */
return;
}
user = botuser_find(nick, NULL);
if (user == NULL || !botuser_verify_password(user, data)) {
/* failed password */
return;
}
/* add the new mask */
mask = irc_get_mask(nick, address, IRC_MASK_USER | IRC_MASK_DOMAIN);
botcmd_user_add_mask(user, mask);
irc_send_cmdv(server, "NOTICE %s :Added new mask %s", nick, mask);
g_free(mask);
}
static void botcmd_pass(IRC_SERVER_REC *server, const char *data,
const char *nick, const char *address)
{
USER_REC *user;
char *params, *pass, *newpass;
g_return_if_fail(data != NULL);
params = event_get_params(data, 2, &pass, &newpass);
user = botuser_find(nick, address);
if (user == NULL || *pass == '\0') {
g_free(params);
return;
}
if (user->password != NULL &&
(*newpass == '\0' || !botuser_verify_password(user, pass))) {
g_free(params);
return;
}
/* change the password */
botcmd_user_set_password(user, user->password == NULL ? pass : newpass);
irc_send_cmdv(server, "NOTICE %s :Password changed", nick);
g_free(params);
}
void bot_irc_commands_init(void)
{
signal_add("event privmsg", (SIGNAL_FUNC) event_privmsg);
signal_add_last("bot command op", (SIGNAL_FUNC) botcmd_op);
signal_add_last("bot command ident", (SIGNAL_FUNC) botcmd_ident);
signal_add_last("bot command pass", (SIGNAL_FUNC) botcmd_pass);
}
void bot_irc_commands_deinit(void)
{
signal_remove("event privmsg", (SIGNAL_FUNC) event_privmsg);
signal_remove("bot command op", (SIGNAL_FUNC) botcmd_op);
signal_remove("bot command ident", (SIGNAL_FUNC) botcmd_ident);
signal_remove("bot command pass", (SIGNAL_FUNC) botcmd_pass);
}

View File

@ -1,579 +0,0 @@
/*
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.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
#ifndef _XOPEN_SOURCE
#define _XOPEN_SOURCE /* for crypt() */
#endif
#include "module.h"
#include "signals.h"
#include "misc.h"
#include "lib-config/iconfig.h"
#include "irc.h"
#include "irc-servers.h"
#include "irc-channels.h"
#include "irc-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;
if (rec->flags == 0) {
/* no flags in this channel - no need to save to config */
config_node_set_str(userconfig, node, rec->channel, NULL);
return;
}
noderec = config_node_section(node, rec->channel, NODE_TYPE_BLOCK);
str = 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;
user->last_modify = time(NULL);
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);
config_node_set_int(userconfig, node, "last_modify", (int) user->last_modify);
/* 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 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 (mask_match_address(NULL, rec->mask, nick, host)) {
user->not_flags = rec->not_flags;
return TRUE;
}
}
return FALSE;
}
static USER_MASK_REC *botuser_find_full_mask(USER_REC *user, const char *mask)
{
GSList *tmp;
g_return_val_if_fail(user != NULL, FALSE);
g_return_val_if_fail(mask != NULL, FALSE);
for (tmp = user->masks; tmp != NULL; tmp = tmp->next) {
USER_MASK_REC *rec = tmp->data;
if (g_strcasecmp(rec->mask, mask) == 0)
return rec;
}
return NULL;
}
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 = irc_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;
}
USER_CHAN_REC *botuser_get_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;
}
USER_REC *botuser_add(const char *nick)
{
USER_REC *user;
/* Add new user */
user = g_new0(USER_REC, 1);
user->nick = g_strdup(nick);
g_hash_table_insert(users, user->nick, user);
botuser_config_save(user);
return user;
}
void botuser_set_flags(USER_REC *user, int flags)
{
user->flags = flags;
botuser_config_save(user);
}
void botuser_set_channel_flags(USER_REC *user, const char *channel, int flags)
{
USER_CHAN_REC *rec;
rec = botuser_get_channel(user, channel);
if (rec != NULL) rec->flags = flags;
botuser_config_save(user);
}
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;
}
void botuser_set_mask_notflags(USER_REC *user, const char *mask, int not_flags)
{
USER_MASK_REC *rec;
rec = botuser_find_full_mask(user, mask);
if (rec == NULL) rec = botuser_create_mask(user, mask);
rec->not_flags = not_flags;
botuser_config_save(user);
}
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;
}
void botuser_save(const char *fname)
{
config_write(userconfig, fname, 0600);
}
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_get_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_get_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, modify time */
user->password = g_strdup(config_node_get_str(node, "password", NULL));
user->flags = botuser_flags2value(config_node_get_str(node, "flags", ""));
user->last_modify = (time_t) config_node_get_int(node, "last_modify", 0);
/* 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/users", get_irssi_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);
}

View File

@ -1,56 +0,0 @@
#ifndef __BOT_USERS_H
#define __BOT_USERS_H
#define USER_OP 0x0001
#define USER_AUTO_OP 0x0002
#define USER_AUTO_VOICE 0x0004
#define USER_MASTER 0x0008
#define USER_FLAG_COUNT 4
/* Channel specific flags */
typedef struct {
char *channel;
int flags;
NICK_REC *nickrec; /* Nick record in channel,
FIXME: User can be in channel with multiple nicks too! */
} USER_CHAN_REC;
typedef struct {
char *mask;
int not_flags; /* do not let this mask use these flags.. */
} USER_MASK_REC;
/* User specific flags */
typedef struct {
char *nick;
int flags;
char *password;
GSList *masks;
GHashTable *channels;
int not_flags; /* active not_flags based on current host mask,
botuser_find() updates this */
time_t last_modify; /* last time the user settings were modified */
} USER_REC;
int botuser_flags2value(const char *flags);
char *botuser_value2flags(int value);
USER_REC *botuser_find(const char *nick, const char *host);
USER_REC *botuser_find_rec(CHANNEL_REC *channel, NICK_REC *nick);
USER_REC *botuser_add(const char *nick);
void botuser_set_flags(USER_REC *user, int flags);
void botuser_set_channel_flags(USER_REC *user, const char *channel, int flags);
USER_MASK_REC *botuser_add_mask(USER_REC *user, const char *mask);
void botuser_set_mask_notflags(USER_REC *user, const char *mask, int not_flags);
void botuser_set_password(USER_REC *user, const char *password);
int botuser_verify_password(USER_REC *user, const char *password);
void botuser_save(const char *fname);
#endif

View File

@ -1,52 +0,0 @@
/*
bot.c : IRC bot plugin for 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 "modules.h"
void bot_irc_commands_deinit(void);
void bot_irc_commands_init(void);
void bot_events_init(void);
void bot_events_deinit(void);
void bot_users_init(void);
void bot_users_deinit(void);
void botnet_init(void);
void botnet_deinit(void);
void irc_bot_init(void)
{
bot_users_init();
bot_irc_commands_init();
bot_events_init();
botnet_init();
module_register("bot", "irc");
}
void irc_bot_deinit(void)
{
bot_users_deinit();
bot_irc_commands_deinit();
bot_events_deinit();
botnet_deinit();
}

View File

@ -1,26 +0,0 @@
#ifndef __BOT_H
#define __BOT_H
typedef struct
{
PLUGIN_REC *plugin;
gboolean loaded;
GHashTable *users;
GSList *botnets;
gchar *nick;
gint rank;
time_t last_write;
}
PLUGIN_DATA;
void plugin_bot_events(PLUGIN_REC *plugin);
#include "botnet.h"
#include "users.h"
#define MODULE_NAME "bot"
#endif

View File

@ -1,592 +0,0 @@
/*
botnet-connection.c : IRC bot plugin for 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 "network.h"
#include "net-nonblock.h"
#include "signals.h"
#include "commands.h"
#include "misc.h"
#include "line-split.h"
#include "lib-config/iconfig.h"
#include "botnet.h"
#include "bot-users.h"
#define BOTNET_RECONNECT_TIME (60*5)
static int reconnect_tag;
static int sig_reconnect(void)
{
GSList *tmp;
for (tmp = botnets; tmp != NULL; tmp = tmp->next) {
BOTNET_REC *rec = tmp->data;
if (rec->reconnect)
botnet_connect(rec);
}
return 1;
}
static void sig_bot_read(BOT_REC *bot)
{
BOTNET_REC *botnet;
char tmpbuf[1024], *str;
int ret, recvlen, reconnect;
botnet = bot->botnet;
for (;;) {
recvlen = bot->handle == NULL ? -1 :
net_receive(bot->handle, tmpbuf, sizeof(tmpbuf));
ret = line_split(tmpbuf, recvlen, &str, &bot->buffer);
if (ret == 0)
break;
if (ret == -1) {
/* connection lost */
reconnect = !bot->disconnect && bot->uplink;
bot_destroy(bot);
if (reconnect) {
/* wasn't intentional disconnection from
our uplink, reconnect */
botnet_connect(botnet);
}
break;
}
fprintf(stderr, "%s\r\n", str);
signal_emit("botnet event", 2, bot, str);
}
}
static void connect_downlink(BOTNET_REC *botnet, GIOChannel *handle,
IPADDR *ip, const char *host)
{
BOT_DOWNLINK_REC *downlink;
BOT_REC *bot;
g_return_if_fail(botnet != NULL);
/* identify the bot who's trying to connect.. */
downlink = bot_downlink_find(botnet, ip, host);
if (downlink == NULL || downlink->password == NULL) {
/* unknown bot, close connection /
bot didn't have password, don't let it connect to us */
net_disconnect(handle);
return;
}
bot = g_new0(BOT_REC, 1);
bot->botnet = botnet;
bot->link = downlink;
g_node_append_data(botnet->bots, bot);
/* connected.. */
bot->handle = handle;
bot->read_tag = g_input_add(handle, G_INPUT_READ, (GInputFunction) sig_bot_read, bot);
}
typedef struct {
char *botnet;
IPADDR ip;
GIOChannel *handle;
} BOT_CONNECT_REC;
static void sig_host_got(RESOLVED_NAME_REC *name, BOT_CONNECT_REC *rec)
{
BOTNET_REC *botnet;
botnet = botnet_find(rec->botnet);
if (botnet == NULL || !botnet->connected) {
/* this botnet isn't connected anymore.. */
net_disconnect(rec->handle);
} else {
connect_downlink(botnet, rec->handle, &rec->ip,
name->error ? NULL : name->name);
}
g_free(rec->botnet);
g_free(rec);
}
static void sig_botnet_listen(BOTNET_REC *botnet)
{
BOT_CONNECT_REC *rec;
IPADDR ip;
GIOChannel *handle;
g_return_if_fail(botnet != NULL);
/* accept connection */
handle = net_accept(botnet->listen_handle, &ip, NULL);
if (handle == NULL)
return;
rec = g_new0(BOT_CONNECT_REC, 1);
rec->botnet = g_strdup(botnet->name);
memcpy(&rec->ip, &ip, sizeof(IPADDR));
rec->handle = handle;
if (!net_gethostbyaddr_nonblock(&ip, (NET_HOST_CALLBACK) sig_host_got, rec)) {
/* failed for some reason, try without host */
connect_downlink(botnet, handle, &ip, NULL);
g_free(rec->botnet);
g_free(rec);
}
}
static int botnet_listen(BOTNET_REC *botnet)
{
IPADDR addr;
int port;
g_return_val_if_fail(botnet != NULL, FALSE);
if (botnet->port <= 0)
return FALSE;
port = botnet->port;
if (botnet->addr == NULL)
botnet->listen_handle = net_listen(NULL, &port);
else {
net_host2ip(botnet->addr, &addr);
botnet->listen_handle = net_listen(&addr, &port);
}
if (botnet->listen_handle == NULL) {
g_warning("Couldn't start listening botnet\n");
return FALSE;
}
botnet->listen_tag = g_input_add(botnet->listen_handle, G_INPUT_READ,
(GInputFunction) sig_botnet_listen, botnet);
return TRUE;
}
static void sig_botnet_connected(GIOChannel *handle, BOT_UPLINK_REC *uplink)
{
BOTNET_REC *botnet;
BOT_REC *bot;
g_return_if_fail(uplink != NULL);
botnet = uplink->botnet;
if (handle == NULL) {
/* error, try another bot */
botnet_connect(botnet);
return;
}
/* connected to bot */
bot = g_new0(BOT_REC, 1);
bot->botnet = botnet;
bot->link = uplink;
bot->uplink = TRUE;
bot->handle = handle;
bot->read_tag = g_input_add(handle, G_INPUT_READ, (GInputFunction) sig_bot_read, bot);
botnet->uplink = bot;
g_node_append_data(botnet->bots, bot);
/* send nick/pass */
bot_send_cmdv(bot, "PASS %s", uplink->password);
bot_send_cmdv(bot, "NICK %s", botnet->nick);
}
void botnet_connect(BOTNET_REC *botnet)
{
BOT_REC *bot;
BOT_UPLINK_REC *uplink, *best;
GSList *tmp;
time_t now;
g_return_if_fail(botnet != NULL);
botnet->reconnect = FALSE;
if (botnet->bots == NULL) {
/* create bot record for us */
bot = g_new0(BOT_REC, 1);
bot->botnet = botnet;
bot->nick = g_strdup(botnet->nick);
bot->priority = botnet->priority;
bot->connected = TRUE;
bot->master = TRUE;
bot->read_tag = -1;
botnet->connected = TRUE;
botnet->master = bot;
botnet->bots = g_node_new(bot);
}
if (botnet->listen_handle == NULL) {
/* start listening */
botnet_listen(botnet);
}
if (botnet->uplinks == NULL) {
/* we have no uplinks */
return;
}
/* find some bot where we can try to connect to */
now = time(NULL);
uplink = best = NULL;
for (tmp = botnet->uplinks; tmp != NULL; tmp = tmp->next) {
uplink = tmp->data;
if (uplink->last_connect+BOTNET_RECONNECT_TIME > now)
continue;
if (uplink->last_connect == 0) {
/* haven't yet tried to connect to this bot */
best = uplink;
break;
}
if (best == NULL || uplink->last_connect < best->last_connect)
best = uplink;
}
if (best == NULL) {
/* reconnect later */
botnet->reconnect = TRUE;
return;
}
/* connect to uplink */
best->last_connect = time(NULL);
net_connect_nonblock(best->host, best->port, NULL, (NET_CALLBACK) sig_botnet_connected, best);
}
static int botnet_send_botinfo(GNode *node, BOT_REC *client)
{
BOT_REC *parent, *bot;
bot = node->data;
parent = node->parent == NULL ? NULL : node->parent->data;
if (parent == NULL && client->uplink) parent = client;
bot_send_cmdv(client, "%s - BOTINFO %s %s %d", bot->botnet->nick, bot->nick,
parent != NULL ? parent->nick : "-", bot->priority);
return FALSE;
}
/* send botnet links to specified bot */
static void botnet_send_links(BOT_REC *bot, int downlinks)
{
GNode *node;
if (!downlinks) {
/* send uplinks */
if (bot->botnet->uplink == NULL)
return;
node = g_node_find(bot->botnet->bots, G_IN_ORDER,
G_TRAVERSE_ALL, bot->botnet->uplink);
if (node == NULL)
return;
g_node_traverse(node, G_LEVEL_ORDER, G_TRAVERSE_ALL, -1,
(GNodeTraverseFunc) botnet_send_botinfo, bot);
return;
}
/* send downlinks = all non-uplink nodes */
for (node = bot->botnet->bots->children; node != NULL; node = node->next) {
BOT_REC *rec = node->data;
if (rec == bot || rec->uplink || !rec->connected)
continue;
g_node_traverse(node, G_LEVEL_ORDER, G_TRAVERSE_ALL, -1,
(GNodeTraverseFunc) botnet_send_botinfo, bot);
}
}
static void botnet_connect_event_uplink(BOT_REC *bot, const char *data)
{
BOTNET_REC *botnet;
BOT_REC *ownbot;
char *str, *p;
int num;
botnet = bot->botnet;
g_return_if_fail(botnet != NULL);
if (g_strcasecmp(data, "NICKERROR") == 0) {
/* nick already in use, change it by adding a number
at the end of it */
p = botnet->nick+strlen(botnet->nick);
while (p > botnet->nick && i_isdigit(p[-1])) p--;
num = *p == '\0' ? 2 : atoi(p)+1; *p = '\0';
str = g_strdup_printf("%s%d", botnet->nick, num);
g_free(botnet->nick); botnet->nick = str;
ownbot = botnet->bots->data;
g_free(ownbot->nick); ownbot->nick = g_strdup(str);
/* try again.. */
bot_send_cmdv(bot, "NICK %s", botnet->nick);
return;
}
if (g_strcasecmp(data, "CONNECTED") == 0) {
/* connected, wait for SYNC command */
bot->connected = TRUE;
return;
}
/* error? what? */
}
static void botnet_event(BOT_REC *bot, const char *data)
{
BOT_DOWNLINK_REC *downlink;
char *fname;
g_return_if_fail(bot != NULL);
g_return_if_fail(data != NULL);
if (bot->connected)
return;
signal_stop_by_name("botnet event");
if (bot->uplink) {
botnet_connect_event_uplink(bot, data);
return;
}
downlink = bot->link;
if (!bot->pass_ok && g_strncasecmp(data, "PASS ", 5) == 0) {
/* password sent, check that it matches */
if (strcmp(data+5, downlink->password) == 0) {
/* ok, connected! */
bot->pass_ok = TRUE;
} else {
/* wrong password, disconnect */
bot_disconnect(bot);
}
return;
}
if (g_strncasecmp(data, "NICK ", 5) == 0) {
/* set bot's nick */
if (!bot->pass_ok) {
/* password has to be sent before nick, kill the
stupid bot. */
bot_disconnect(bot);
return;
}
if (g_strcasecmp(bot->botnet->nick, data+5) == 0 ||
bot_find_nick(bot->botnet, data+5) != NULL) {
/* nick already exists */
bot_send_cmd(bot, "NICKERROR");
return;
}
/* set the nick */
bot->nick = g_strdup(data+5);
bot->connected = TRUE;
bot_send_cmd(bot, "CONNECTED");
/* send info about all the bots that are connected now
to this botnet */
botnet_send_botinfo(bot->botnet->bots, bot);
botnet_send_links(bot, FALSE);
botnet_send_links(bot, TRUE);
bot_send_cmdv(bot, "%s - MASTER %s", bot->botnet->nick, bot->botnet->master->nick);
/* send our current user configuration */
fname = g_strdup_printf("%s/users.temp", get_irssi_dir());
botuser_save(fname);
botnet_send_file(bot->botnet, bot->nick, fname);
g_free(fname);
/* send sync msg */
bot_send_cmdv(bot, "%s - SYNC", bot->botnet->nick);
return;
}
/* pass/nick not sent yet */
bot_send_cmd(bot, "ERROR");
}
static void botnet_event_sync(BOT_REC *bot)
{
/* send our record to host */
botnet_send_botinfo(bot->botnet->bots, bot);
/* send our downlinks to host */
botnet_send_links(bot, TRUE);
signal_stop_by_name("botnet event");
}
static BOT_REC *bot_add(BOTNET_REC *botnet, const char *nick, const char *parent)
{
GNode *node;
BOT_REC *rec;
g_return_val_if_fail(botnet != NULL, NULL);
g_return_val_if_fail(nick != NULL, NULL);
node = bot_find_nick(botnet, nick);
if (node != NULL) return node->data;
node = bot_find_nick(botnet, parent);
if (node == NULL) return NULL;
rec = g_new0(BOT_REC, 1);
rec->botnet = botnet;
rec->nick = g_strdup(nick);
rec->read_tag = -1;
rec->connected = TRUE;
g_node_append_data(node, rec);
return rec;
}
static void botnet_event_botinfo(BOT_REC *bot, const char *data, const char *sender)
{
char *nick, *parent, *priority;
void *free_arg;
BOT_REC *rec;
/*str = g_strdup_printf("BOTINFO %s", data);
botnet_broadcast(bot->botnet, bot, sender, str);
g_free(str);*/
if (!cmd_get_params(data, &free_arg, 3, &nick, &parent, &priority))
return;
if (*parent == '-' && parent[1] == '\0')
parent = NULL;
if (parent == NULL && bot->botnet->uplink != NULL &&
bot->botnet->uplink == bot) {
/* our uplink */
if (bot->nick == NULL) bot->nick = g_strdup(nick);
rec = bot;
} else {
rec = bot_add(bot->botnet, nick, parent);
}
if (rec != NULL) {
rec->priority = atoi(priority);
}
cmd_params_free(free_arg);
}
static void botnet_event_botquit(BOT_REC *bot, const char *data)
{
GNode *node;
node = bot_find_nick(bot->botnet, data);
if (node != NULL) bot_destroy(node->data);
signal_stop_by_name("botnet event");
}
static void sig_bot_disconnected(BOT_REC *bot)
{
BOT_REC *master, *tmpbot;
GNode *node;
char *str;
if (!bot->botnet->connected)
return;
if (bot->connected && bot->handle != NULL) {
/* send notice to rest of the botnet about quit */
str = g_strdup_printf("BOTQUIT %s", bot->nick);
botnet_broadcast(bot->botnet, bot, NULL, str);
g_free(str);
}
if (bot->master) {
/* master quit */
node = bot_find_path(bot->botnet, bot->nick);
tmpbot = node == NULL ? NULL : node->data;
if (tmpbot != NULL && tmpbot->disconnect) {
/* we lost the connection to master - find new
master for the botnet*/
master = botnet_find_master(bot->botnet, NULL);
botnet_set_master(bot->botnet, master);
str = g_strdup_printf("MASTER %s", master->nick);
botnet_broadcast(bot->botnet, bot, NULL, str);
g_free(str);
}
}
}
static int print_bot(GNode *node)
{
BOT_REC *bot = node->data;
fprintf(stderr, "%s %d %d\r\n", bot->nick, bot->connected, bot->disconnect);
return FALSE;
}
static void cmd_bots(void)
{
BOTNET_REC *botnet = botnet_find("ircnet");
fprintf(stderr, "\r\n");
g_node_traverse(botnet->bots, G_LEVEL_ORDER, G_TRAVERSE_ALL, -1,
(GNodeTraverseFunc) print_bot, NULL);
}
void botnet_connection_init(void)
{
reconnect_tag = g_timeout_add(BOTNET_RECONNECT_TIME*1000, (GSourceFunc) sig_reconnect, NULL);
signal_add("botnet event", (SIGNAL_FUNC) botnet_event);
signal_add("botnet event sync", (SIGNAL_FUNC) botnet_event_sync);
signal_add("botnet event botinfo", (SIGNAL_FUNC) botnet_event_botinfo);
signal_add("botnet event botquit", (SIGNAL_FUNC) botnet_event_botquit);
signal_add("bot disconnected", (SIGNAL_FUNC) sig_bot_disconnected);
command_bind("bots", NULL, (SIGNAL_FUNC) cmd_bots);
}
void botnet_connection_deinit(void)
{
g_source_remove(reconnect_tag);
signal_remove("botnet event", (SIGNAL_FUNC) botnet_event);
signal_remove("botnet event sync", (SIGNAL_FUNC) botnet_event_sync);
signal_remove("botnet event botinfo", (SIGNAL_FUNC) botnet_event_botinfo);
signal_remove("botnet event botquit", (SIGNAL_FUNC) botnet_event_botquit);
signal_remove("bot disconnected", (SIGNAL_FUNC) sig_bot_disconnected);
command_unbind("bots", (SIGNAL_FUNC) cmd_bots);
}

View File

@ -1,219 +0,0 @@
/*
botnet-users.c : IRC bot plugin for 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 "commands.h"
#include "irc.h"
#include "irc-servers.h"
#include "channels.h"
#include "nicklist.h"
#include "masks.h"
#include "bot-users.h"
#include "botnet.h"
void botcmd_user_add(const char *nick)
{
char *str;
botuser_add(nick);
str = g_strdup_printf("USER_ADD %s", nick);
botnet_broadcast(NULL, NULL, NULL, str);
g_free(str);
}
void botcmd_user_set_flags(USER_REC *user, int flags)
{
char *str, *flagstr;
botuser_set_flags(user, flags);
flagstr = botuser_value2flags(flags);
str = g_strdup_printf("USER_FLAGS %s %s", user->nick, flagstr);
g_free(flagstr);
botnet_broadcast(NULL, NULL, NULL, str);
g_free(str);
}
void botcmd_user_set_channel_flags(USER_REC *user, const char *channel, int flags)
{
char *str, *flagstr;
botuser_set_channel_flags(user, channel, flags);
flagstr = botuser_value2flags(flags);
str = g_strdup_printf("USER_CHAN_FLAGS %s %s %s", user->nick, channel, flagstr);
g_free(flagstr);
botnet_broadcast(NULL, NULL, NULL, str);
g_free(str);
}
void botcmd_user_add_mask(USER_REC *user, const char *mask)
{
char *str;
botuser_add_mask(user, mask);
str = g_strdup_printf("USER_ADD_MASK %s %s", user->nick, mask);
botnet_broadcast(NULL, NULL, NULL, str);
g_free(str);
}
void botcmd_user_set_mask_notflags(USER_REC *user, const char *mask, int not_flags)
{
char *str, *flagstr;
botuser_set_mask_notflags(user, mask, not_flags);
flagstr = botuser_value2flags(not_flags);
str = g_strdup_printf("USER_MASK_NOTFLAGS %s %s %s", user->nick, mask, flagstr);
g_free(flagstr);
botnet_broadcast(NULL, NULL, NULL, str);
g_free(str);
}
void botcmd_user_set_password(USER_REC *user, const char *password)
{
char *str;
botuser_set_password(user, password);
str = g_strdup_printf("USER_PASS %s %s", user->nick, password);
botnet_broadcast(NULL, NULL, NULL, str);
g_free(str);
}
static void botnet_event_user_add(BOT_REC *bot, const char *data, const char *sender)
{
char *nick;
void *free_arg;
if (!cmd_get_params(data, &free_arg, 1, &nick))
return;
botuser_add(nick);
cmd_params_free(free_arg);
}
static void botnet_event_user_flags(BOT_REC *bot, const char *data, const char *sender)
{
USER_REC *user;
char *nick, *flags;
void *free_arg;
if (!cmd_get_params(data, &free_arg, 2, &nick, &flags))
return;
user = botuser_find(nick, NULL);
if (user == NULL) user = botuser_add(nick);
botuser_set_flags(user, botuser_flags2value(flags));
cmd_params_free(free_arg);
}
static void botnet_event_user_chan_flags(BOT_REC *bot, const char *data, const char *sender)
{
USER_REC *user;
char *nick, *channel, *flags;
void *free_arg;
if (!cmd_get_params(data, &free_arg, 3, &nick, &channel, &flags))
return;
user = botuser_find(nick, NULL);
if (user == NULL) user = botuser_add(nick);
botuser_set_channel_flags(user, channel, botuser_flags2value(flags));
cmd_params_free(free_arg);
}
static void botnet_event_user_add_mask(BOT_REC *bot, const char *data, const char *sender)
{
USER_REC *user;
char *nick, *mask;
void *free_arg;
if (!cmd_get_params(data, &free_arg, 2, &nick, &mask))
return;
user = botuser_find(nick, NULL);
if (user == NULL) user = botuser_add(nick);
botuser_add_mask(user, mask);
cmd_params_free(free_arg);
}
static void botnet_event_user_mask_notflags(BOT_REC *bot, const char *data, const char *sender)
{
USER_REC *user;
char *nick, *mask, *not_flags;
void *free_arg;
if (!cmd_get_params(data, &free_arg, 3, &nick, &mask, &not_flags))
return;
user = botuser_find(nick, NULL);
if (user == NULL) user = botuser_add(nick);
botuser_set_mask_notflags(user, mask, botuser_flags2value(not_flags));
cmd_params_free(free_arg);
}
static void botnet_event_user_pass(BOT_REC *bot, const char *data, const char *sender)
{
USER_REC *user;
char *nick, *pass;
void *free_arg;
if (!cmd_get_params(data, &free_arg, 2, &nick, &pass))
return;
user = botuser_find(nick, NULL);
if (user == NULL) user = botuser_add(nick);
botuser_set_password(user, pass);
cmd_params_free(free_arg);
}
void botnet_users_init(void)
{
signal_add("botnet event user_add", (SIGNAL_FUNC) botnet_event_user_add);
signal_add("botnet event user_flags", (SIGNAL_FUNC) botnet_event_user_flags);
signal_add("botnet event user_chan_flags", (SIGNAL_FUNC) botnet_event_user_chan_flags);
signal_add("botnet event user_add_mask", (SIGNAL_FUNC) botnet_event_user_add_mask);
signal_add("botnet event user_mask_notflags", (SIGNAL_FUNC) botnet_event_user_mask_notflags);
signal_add("botnet event user_pass", (SIGNAL_FUNC) botnet_event_user_pass);
}
void botnet_users_deinit(void)
{
signal_remove("botnet event user_add", (SIGNAL_FUNC) botnet_event_user_add);
signal_remove("botnet event user_flags", (SIGNAL_FUNC) botnet_event_user_flags);
signal_remove("botnet event user_chan_flags", (SIGNAL_FUNC) botnet_event_user_chan_flags);
signal_remove("botnet event user_add_mask", (SIGNAL_FUNC) botnet_event_user_add_mask);
signal_remove("botnet event user_mask_notflags", (SIGNAL_FUNC) botnet_event_user_mask_notflags);
signal_remove("botnet event user_pass", (SIGNAL_FUNC) botnet_event_user_pass);
}

View File

@ -1,13 +0,0 @@
#ifndef __BOTNET_USERS_H
#define __BOTNET_USERS_H
void botcmd_user_add(const char *nick);
void botcmd_user_set_flags(USER_REC *user, int flags);
void botcmd_user_set_channel_flags(USER_REC *user, const char *channel, int flags);
void botcmd_user_add_mask(USER_REC *user, const char *mask);
void botcmd_user_set_mask_notflags(USER_REC *user, const char *mask, int not_flags);
void botcmd_user_set_password(USER_REC *user, const char *password);
#endif

View File

@ -1,827 +0,0 @@
/*
botnet.c : IRC bot plugin for 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 "network.h"
#include "net-nonblock.h"
#include "signals.h"
#include "commands.h"
#include "misc.h"
#include "line-split.h"
#include "lib-config/iconfig.h"
#include "botnet.h"
void botnet_connection_init(void);
void botnet_connection_deinit(void);
void botnet_users_deinit(void);
void botnet_users_init(void);
GSList *botnets;
void bot_send_cmd(BOT_REC *bot, const char *data)
{
g_return_if_fail(bot != NULL);
g_return_if_fail(data != NULL);
net_transmit(bot->handle, data, strlen(data));
net_transmit(bot->handle, "\n", 1);
}
void bot_send_cmdv(BOT_REC *bot, const char *format, ...)
{
va_list args;
char *str;
va_start(args, format);
str = g_strdup_vprintf(format, args);
bot_send_cmd(bot, str);
g_free(str);
va_end(args);
}
static void botnet_broadcast_single(BOTNET_REC *botnet, BOT_REC *except_bot,
const char *source, const char *data)
{
GNode *node;
char *str;
g_return_if_fail(botnet != NULL);
g_return_if_fail(data != NULL);
str = g_strdup_printf("%s - %s", source != NULL ? source :
botnet->nick, data);
for (node = botnet->bots->children; node != NULL; node = node->next) {
BOT_REC *rec = node->data;
if (rec != except_bot && rec->handle != NULL)
bot_send_cmd(rec, str);
}
g_free(str);
}
/* broadcast a message to everyone in bot network, except for `except_bot'
if it's not NULL. If botnet is NULL, the message is sent to all botnets. */
void botnet_broadcast(BOTNET_REC *botnet, BOT_REC *except_bot,
const char *source, const char *data)
{
GSList *tmp;
g_return_if_fail(data != NULL);
if (botnet != NULL) {
botnet_broadcast_single(botnet, except_bot, source, data);
return;
}
/* broadcast to all botnets */
for (tmp = botnets; tmp != NULL; tmp = tmp->next) {
BOTNET_REC *rec = tmp->data;
botnet_broadcast_single(rec, except_bot, source, data);
}
}
void botnet_send_cmd(BOTNET_REC *botnet, const char *source,
const char *target, const char *data)
{
GNode *node;
char *str;
g_return_if_fail(botnet != NULL);
g_return_if_fail(target != NULL);
g_return_if_fail(data != NULL);
node = bot_find_path(botnet, target);
if (node == NULL) {
g_warning("Can't find route for target %s", target);
return;
}
str = g_strdup_printf("%s %s %s", source != NULL ? source :
botnet->nick, target, data);
bot_send_cmd(node->data, str);
g_free(str);
}
static void escape_buffer(char *buffer, int len)
{
char *dest, *tempbuf, *p;
dest = buffer;
tempbuf = p = g_malloc(len*2+2);
while (len > 0) {
if (*buffer == '\0') {
*p++ = '\\';
*p++ = '0';
} else if (*buffer == '\r') {
*p++ = '\\';
*p++ = 'r';
} else if (*buffer == '\n') {
*p++ = '\\';
*p++ = 'n';
} else if (*buffer == '\\') {
*p++ = '\\';
*p++ = '\\';
} else {
*p++ = *buffer;
}
len--; buffer++;
}
*p++ = '\0';
len = (int) (p-tempbuf);
memcpy(dest, tempbuf, len);
g_free(tempbuf);
}
int botnet_send_file(BOTNET_REC *botnet, const char *target, const char *fname)
{
GNode *node;
GString *str;
char buffer[1024];
int f, len;
node = bot_find_path(botnet, target);
if (node == NULL) {
g_warning("Can't find route for target %s", target);
return FALSE;
}
f = open(fname, O_RDONLY);
if (f == -1) return FALSE;
str = g_string_new(NULL);
g_string_sprintf(str, "%s %s FILE %s", botnet->nick, target, g_basename(fname));
bot_send_cmd(node->data, str->str);
while ((len = read(f, buffer, sizeof(buffer)/2-2)) > 0) {
escape_buffer(buffer, len);
g_string_sprintf(str, "%s %s FILE %s", botnet->nick, target, buffer);
bot_send_cmd(node->data, str->str);
}
g_string_sprintf(str, "%s %s FILE", botnet->nick, target);
bot_send_cmd(node->data, str->str);
g_string_free(str, TRUE);
close(f);
return TRUE;
}
BOTNET_REC *botnet_find(const char *name)
{
GSList *tmp;
g_return_val_if_fail(name != NULL, NULL);
for (tmp = botnets; tmp != NULL; tmp = tmp->next) {
BOTNET_REC *rec = tmp->data;
if (g_strcasecmp(rec->name, name) == 0)
return rec;
}
return NULL;
}
typedef struct {
gconstpointer key;
int priority;
GNode *node;
} BOT_FIND_REC;
static int gnode_find_nick(GNode *node, BOT_FIND_REC *rec)
{
BOT_REC *bot = node->data;
if (bot == NULL) return FALSE;
if (bot->nick != NULL && g_strcasecmp(bot->nick, rec->key) == 0) {
rec->node = node;
return TRUE;
}
return FALSE;
}
GNode *bot_find_nick(BOTNET_REC *botnet, const char *nick)
{
BOT_FIND_REC rec;
g_return_val_if_fail(botnet != NULL, NULL);
g_return_val_if_fail(nick != NULL, NULL);
rec.key = nick;
rec.node = NULL;
g_node_traverse(botnet->bots, 0, G_TRAVERSE_ALL, -1,
(GNodeTraverseFunc) gnode_find_nick, &rec);
return rec.node;
}
/* Return the bot who we should send the message if we wanted `nick' to get it. */
GNode *bot_find_path(BOTNET_REC *botnet, const char *nick)
{
BOT_FIND_REC rec;
GNode *node;
g_return_val_if_fail(botnet != NULL, NULL);
g_return_val_if_fail(nick != NULL, NULL);
rec.key = nick;
rec.node = NULL;
for (node = botnet->bots->children; node != NULL; node = node->next) {
g_node_traverse(node, 0, G_TRAVERSE_ALL, -1,
(GNodeTraverseFunc) gnode_find_nick, &rec);
if (rec.node != NULL) return node;
}
return rec.node;
}
/* check if `addr' is an IP address - this is checked to make sure that
if we have an address like "192.168.0.*", it wouldn't match to host name
192.168.0.host.org */
static int is_ip_mask(const char *addr)
{
while (*addr != '\0') {
if (!i_isdigit(*addr) && *addr != '.' &&
*addr != '*' && *addr != '?') return FALSE;
addr++;
}
return TRUE;
}
BOT_DOWNLINK_REC *bot_downlink_find(BOTNET_REC *botnet, IPADDR *ip, const char *host)
{
GSList *tmp, *tmp2;
char ipname[MAX_IP_LEN];
g_return_val_if_fail(botnet != NULL, NULL);
g_return_val_if_fail(ip != NULL, NULL);
net_ip2host(ip, ipname);
for (tmp = botnet->downlinks; tmp != NULL; tmp = tmp->next) {
BOT_DOWNLINK_REC *rec = tmp->data;
for (tmp2 = rec->valid_addrs; tmp2 != NULL; tmp2 = tmp2->next) {
if (match_wildcards(tmp2->data, ipname))
return rec;
if (match_wildcards(tmp2->data, host) &&
!is_ip_mask(tmp2->data))
return rec;
}
}
return NULL;
}
static int gnode_find_master(GNode *node, BOT_FIND_REC *rec)
{
BOT_REC *bot = node->data;
if (bot == NULL) return FALSE;
if (!bot->disconnect && bot->priority > rec->priority) {
rec->node = node;
return TRUE;
}
return FALSE;
}
BOT_REC *botnet_find_master(BOTNET_REC *botnet, BOT_REC *old_master)
{
BOT_FIND_REC rec;
g_return_val_if_fail(botnet != NULL, NULL);
rec.node = NULL;
rec.priority = old_master == NULL ? -1 : old_master->priority;
g_node_traverse(botnet->bots, 0, G_TRAVERSE_ALL, -1,
(GNodeTraverseFunc) gnode_find_master, &rec);
return rec.node == NULL ? old_master : rec.node->data;
}
void botnet_set_master(BOTNET_REC *botnet, BOT_REC *bot)
{
g_return_if_fail(botnet != NULL);
g_return_if_fail(bot != NULL);
if (botnet->master != NULL)
botnet->master->master = FALSE;
bot->master = TRUE;
botnet->master = bot;
}
void bot_nick_destroy(BOT_CHANNEL_REC *rec, NICK_REC *nick)
{
g_return_if_fail(rec != NULL);
g_return_if_fail(nick != NULL);
rec->nicks = g_slist_remove(rec->nicks, nick);
g_free(nick->nick);
g_free_not_null(nick->realname);
g_free_not_null(nick->host);
g_free(nick);
}
void bot_channel_destroy(BOT_IRCNET_REC *ircnet, BOT_CHANNEL_REC *rec)
{
g_return_if_fail(ircnet != NULL);
g_return_if_fail(rec != NULL);
ircnet->channels = g_slist_remove(ircnet->channels, rec);
while (rec->nicks != NULL)
bot_nick_destroy(rec, rec->nicks->data);
g_slist_foreach(rec->banlist, (GFunc) g_free, NULL);
g_slist_foreach(rec->ebanlist, (GFunc) g_free, NULL);
g_slist_foreach(rec->invitelist, (GFunc) g_free, NULL);
g_slist_free(rec->banlist);
g_slist_free(rec->ebanlist);
g_slist_free(rec->invitelist);
g_free_not_null(rec->mode);
g_free_not_null(rec->key);
g_free(rec->name);
g_free(rec);
}
void bot_ircnet_destroy(BOT_REC *bot, BOT_IRCNET_REC *rec)
{
g_return_if_fail(bot != NULL);
g_return_if_fail(rec != NULL);
bot->ircnets = g_slist_remove(bot->ircnets, bot);
while (rec->channels != NULL)
bot_channel_destroy(rec, rec->channels->data);
g_free(rec->tag);
g_free(rec->ircnet);
g_free(rec->server);
g_free(rec->nick);
g_free(rec);
}
void bot_disconnect(BOT_REC *bot)
{
bot->disconnect = TRUE;
signal_emit("bot disconnected", 1, bot);
if (bot->read_tag != -1) {
g_source_remove(bot->read_tag);
bot->read_tag = -1;
}
if (bot->handle != NULL) {
net_disconnect(bot->handle);
bot->handle = NULL;
}
}
static void bot_mark_disconnect(GNode *node)
{
BOT_REC *bot = node->data;
bot->disconnect = TRUE;
}
#define bot_mark_disconnects(node) \
g_node_traverse(node, G_LEVEL_ORDER, G_TRAVERSE_ALL, -1, \
(GNodeTraverseFunc) bot_mark_disconnect, NULL)
void bot_destroy(BOT_REC *bot)
{
GNode *node;
g_return_if_fail(bot != NULL);
node = g_node_find(bot->botnet->bots, 0, G_TRAVERSE_ALL, bot);
if (node != NULL) {
if (!bot->disconnect)
bot_mark_disconnects(node);
}
bot_disconnect(bot);
if (node != NULL) {
while (node->children != NULL)
bot_destroy(node->children->data);
g_node_destroy(node);
}
if (bot->botnet->uplink == bot)
bot->botnet->uplink = NULL;
if (bot->botnet->master == bot)
bot->botnet->master = NULL;
while (bot->ircnets != NULL)
bot_ircnet_destroy(bot, bot->ircnets->data);
line_split_free(bot->buffer);
g_free_not_null(bot->nick);
g_free(bot);
}
void bot_downlink_destroy(BOT_DOWNLINK_REC *rec)
{
rec->botnet->downlinks = g_slist_remove(rec->botnet->downlinks, rec);
g_slist_foreach(rec->valid_addrs, (GFunc) g_free, NULL);
g_slist_free(rec->valid_addrs);
g_free_not_null(rec->password);
g_free(rec);
}
void bot_uplink_destroy(BOT_UPLINK_REC *rec)
{
rec->botnet->uplinks = g_slist_remove(rec->botnet->uplinks, rec);
g_free(rec->host);
g_free_not_null(rec->password);
g_free(rec);
}
void botnet_disconnect(BOTNET_REC *botnet)
{
botnet->connected = FALSE;
bot_destroy(botnet->bots->data);
botnet->bots = NULL;
if (botnet->listen_tag != -1) {
g_source_remove(botnet->listen_tag);
botnet->listen_tag = -1;
}
if (botnet->listen_handle != NULL) {
net_disconnect(botnet->listen_handle);
botnet->listen_handle = NULL;
}
}
static void botnet_destroy(BOTNET_REC *botnet)
{
botnets = g_slist_remove(botnets, botnet);
while (botnet->uplinks != NULL)
bot_uplink_destroy(botnet->uplinks->data);
while (botnet->downlinks != NULL)
bot_downlink_destroy(botnet->downlinks->data);
botnet_disconnect(botnet);
g_free_not_null(botnet->addr);
g_free(botnet->name);
g_free(botnet->nick);
g_free(botnet);
}
static void botnet_event(BOT_REC *bot, const char *data)
{
char *source, *target, *command, *args, *event;
void *free_arg;
if (!bot->connected)
return;
if (!cmd_get_params(data, &free_arg, 4 | PARAM_FLAG_GETREST,
&source, &target, &command, &args))
return;
if (*target == '-' && target[1] == '\0')
target = NULL;
g_strdown(command);
event = g_strconcat("botnet event ", command, NULL);
signal_emit(event, 4, bot, args, source, target);
g_free(event);
cmd_params_free(free_arg);
}
/* broadcast the signal forward */
static void botnet_event_broadcast(BOT_REC *bot, const char *data)
{
char *source, *target, *command;
void *free_arg;
if (!bot->connected)
return;
if (!cmd_get_params(data, &free_arg, 3 | PARAM_FLAG_GETREST,
&source, &target, &command))
return;
if (g_strcasecmp(target, bot->botnet->nick) == 0) {
/* message was for us */
cmd_params_free(free_arg);
return;
}
if (*target == '-' && target[1] == '\0') {
/* broadcast */
botnet_broadcast(bot->botnet, bot, source, command);
} else {
/* send to specified target */
botnet_send_cmd(bot->botnet, source, target, command);
}
cmd_params_free(free_arg);
}
static void botnet_event_master(BOT_REC *bot, const char *data, const char *sender)
{
BOTNET_REC *botnet;
BOT_REC *master;
GNode *node;
char *str;
botnet = bot->botnet;
node = bot_find_nick(bot->botnet, data);
master = node == NULL ? NULL : node->data;
master = botnet_find_master(bot->botnet, master);
g_return_if_fail(master != NULL);
if (node == NULL || node->data != master) {
/* no, we don't agree with that master -
send our own to everyone. */
bot = NULL;
}
botnet_set_master(botnet, master);
str = g_strdup_printf("MASTER %s", master->nick);
botnet_broadcast(botnet, bot, sender, str);
g_free(str);
signal_stop_by_name("botnet event");
}
static int unescape_data(const char *input, char *output)
{
int len;
len = 0;
while (*input != '\0') {
if (*input != '\\')
*output++ = *input;
else {
input++;
g_return_val_if_fail(*input != '\0', len);
switch (*input) {
case '\\':
*output++ = '\\';
break;
case '0':
*output++ = '\0';
break;
case 'r':
*output++ = '\r';
break;
case 'n':
*output++ = '\n';
break;
}
}
input++;
len++;
}
return len;
}
static void botnet_event_file(BOT_REC *bot, const char *data, const char *sender, const char *target)
{
GNode *node;
char *tempbuf, *str;
int len;
if (g_strcasecmp(target, bot->botnet->nick) != 0)
return;
node = bot_find_nick(bot->botnet, sender);
g_return_if_fail(node != NULL);
bot = node->data;
if (bot->file_handle <= 0) {
/* first line - data contains file name */
str = g_strdup_printf("%s/%s", get_irssi_dir(), data);
bot->file_handle = open(str, O_CREAT|O_TRUNC|O_WRONLY, 0600);
g_free(str);
} else if (*data == '\0') {
/* no data - end of file */
if (bot->file_handle > 0) {
close(bot->file_handle);
bot->file_handle = -1;
}
} else {
/* file data */
tempbuf = g_malloc(strlen(data)*2+2);
len = unescape_data(data, tempbuf);
write(bot->file_handle, tempbuf, len);
g_free(tempbuf);
}
}
static void botnet_config_read_ips(BOT_DOWNLINK_REC *rec, CONFIG_NODE *node)
{
GSList *tmp;
g_return_if_fail(rec != NULL);
g_return_if_fail(node != NULL);
node = config_node_section(node, "valid_addrs", -1);
tmp = node == NULL ? NULL : node->value;
for (; tmp != NULL; tmp = tmp->next) {
node = tmp->data;
rec->valid_addrs = g_slist_append(rec->valid_addrs, g_strdup(node->value));
}
}
static void botnet_config_read_uplink(BOTNET_REC *botnet, CONFIG_NODE *node)
{
BOT_UPLINK_REC *rec;
char *value;
g_return_if_fail(botnet != NULL);
g_return_if_fail(node != NULL);
value = config_node_get_str(node, "host", NULL);
if (value == NULL) return; /* host required */
rec = g_new0(BOT_UPLINK_REC, 1);
rec->botnet = botnet;
rec->host = g_strdup(value);
rec->port = config_node_get_int(node, "port", DEFAULT_BOTNET_PORT);
rec->password = g_strdup(config_node_get_str(node, "password", NULL));
botnet->uplinks = g_slist_append(botnet->uplinks, rec);
}
static void botnet_config_read_downlink(BOTNET_REC *botnet, CONFIG_NODE *node)
{
BOT_DOWNLINK_REC *rec;
g_return_if_fail(botnet != NULL);
g_return_if_fail(node != NULL);
rec = g_new0(BOT_DOWNLINK_REC, 1);
botnet_config_read_ips(rec, node);
if (rec->valid_addrs == NULL) {
g_free(rec);
return;
}
rec->botnet = botnet;
rec->password = g_strdup(config_node_get_str(node, "password", NULL));
botnet->downlinks = g_slist_append(botnet->downlinks, rec);
}
static void botnet_config_read_botnet(CONFIG_NODE *node)
{
CONFIG_NODE *subnode;
BOTNET_REC *botnet;
GSList *tmp;
g_return_if_fail(node != NULL);
if (node->key == NULL || node->value == NULL)
return;
/* New botnet */
botnet = g_new0(BOTNET_REC, 1);
botnet->name = g_strdup(node->key);
botnet->nick = g_strdup(config_node_get_str(node, "nick", "bot"));
botnet->priority = config_node_get_int(node, "priority", DEFAULT_BOTNET_PRIORITY);
botnet->autoconnect = config_node_get_bool(node, "autoconnect", FALSE);
botnet->addr = g_strdup(config_node_get_str(node, "listen_addr", NULL));
botnet->port = config_node_get_int(node, "listen_port", DEFAULT_BOTNET_PORT);
botnet->listen_tag = -1;
/* read uplinks */
subnode = config_node_section(node, "uplinks", -1);
tmp = subnode == NULL ? NULL : subnode->value;
for (; tmp != NULL; tmp = tmp->next)
botnet_config_read_uplink(botnet, tmp->data);
/* read downlinks */
subnode = config_node_section(node, "downlinks", -1);
tmp = subnode == NULL ? NULL : subnode->value;
for (; tmp != NULL; tmp = tmp->next)
botnet_config_read_downlink(botnet, tmp->data);
botnets = g_slist_append(botnets, botnet);
}
static void botnet_config_read(void)
{
CONFIG_REC *config;
CONFIG_NODE *node;
GSList *tmp;
char *fname;
/* Read botnets from ~/.irssi/botnets */
fname = g_strdup_printf("%s/botnets", get_irssi_dir());
config = config_open(fname, -1);
g_free(fname);
if (config == NULL)
return;
config_parse(config);
node = config_node_traverse(config, "botnets", FALSE);
tmp = node == NULL ? NULL : node->value;
for (; tmp != NULL; tmp = tmp->next)
botnet_config_read_botnet(tmp->data);
config_close(config);
}
/* FIXME: this command is just temporary */
static void cmd_botnet(const char *data)
{
BOTNET_REC *botnet;
char *str;
botnet = botnets->data;
str = g_strdup_printf("BCAST %s", data);
botnet_broadcast(botnet, NULL, NULL, str);
g_free(str);
}
static void autoconnect_botnets(void)
{
GSList *tmp;
for (tmp = botnets; tmp != NULL; tmp = tmp->next) {
BOTNET_REC *rec = tmp->data;
if (rec->autoconnect)
botnet_connect(rec);
}
}
void botnet_init(void)
{
botnet_config_read();
botnet_connection_init();
botnet_users_init();
signal_add("botnet event", (SIGNAL_FUNC) botnet_event);
signal_add_last("botnet event", (SIGNAL_FUNC) botnet_event_broadcast);
signal_add("botnet event master", (SIGNAL_FUNC) botnet_event_master);
signal_add("botnet event file", (SIGNAL_FUNC) botnet_event_file);
command_bind("botnet", NULL, (SIGNAL_FUNC) cmd_botnet);
autoconnect_botnets();
}
void botnet_deinit(void)
{
while (botnets)
botnet_destroy(botnets->data);
botnet_connection_deinit();
botnet_users_deinit();
signal_remove("botnet event", (SIGNAL_FUNC) botnet_event);
signal_remove("botnet event", (SIGNAL_FUNC) botnet_event_broadcast);
signal_remove("botnet event master", (SIGNAL_FUNC) botnet_event_master);
signal_remove("botnet event file", (SIGNAL_FUNC) botnet_event_file);
command_unbind("botnet", (SIGNAL_FUNC) cmd_botnet);
}

View File

@ -1,134 +0,0 @@
#ifndef __BOT_BOTNET_H
#define __BOT_BOTNET_H
#include "nicklist.h"
#define DEFAULT_BOTNET_PORT 2255
#define DEFAULT_BOTNET_PRIORITY 5
typedef struct _botnet_rec BOTNET_REC;
typedef struct {
char *name;
GSList *nicks; /* NICK_RECs */
unsigned int chanop:1;
GSList *banlist;
GSList *ebanlist;
GSList *invitelist;
char *mode;
int limit;
char *key;
} BOT_CHANNEL_REC;
typedef struct {
char *tag; /* same as server->tag */
char *ircnet;
char *server;
char *nick;
GSList *channels;
} BOT_IRCNET_REC;
typedef struct {
BOTNET_REC *botnet;
void *link; /* NULL, BOT_UPLINK_REC or BOT_DOWNLINK_REC */
unsigned int uplink:1; /* this is our uplink */
unsigned int pass_ok:1; /* downlink's password was ok */
unsigned int connected:1; /* bot is in this botnet now */
unsigned int disconnect:1; /* just disconnecting this bot.. */
unsigned int master:1; /* this bot is the bot network's current master */
char *nick; /* bot's unique nick in botnet */
int priority;
GIOChannel *handle;
int read_tag;
LINEBUF_REC *buffer;
int file_handle; /* if bot is sending a file to us */
GSList *ircnets;
} BOT_REC;
typedef struct {
BOTNET_REC *botnet;
char *host;
int port;
char *password;
time_t last_connect;
} BOT_UPLINK_REC;
typedef struct {
BOTNET_REC *botnet;
GSList *valid_addrs; /* IP/host masks where this bot is allowed to connect */
char *password;
} BOT_DOWNLINK_REC;
struct _botnet_rec {
unsigned int connected:1;
unsigned int autoconnect:1;
unsigned int reconnect:1;
char *name; /* botnet name */
char *nick; /* our nick in botnet */
int priority; /* our priority in botnet */
char *addr; /* in what address we should listen, NULL = all */
int port; /* what port we should listen, 0 = default, -1 = don't listen */
GIOChannel *listen_handle;
int listen_tag;
GSList *uplinks;
GSList *downlinks;
GNode *bots;
BOT_REC *uplink; /* our current uplink */
BOT_REC *master; /* link to current master */
};
extern GSList *botnets;
void bot_send_cmd(BOT_REC *bot, const char *data);
void bot_send_cmdv(BOT_REC *bot, const char *format, ...);
/* broadcast a message to everyone in bot network, except for `except_bot'
if it's not NULL. If botnet is NULL, the message is sent to all botnets. */
void botnet_broadcast(BOTNET_REC *botnet, BOT_REC *except_bot,
const char *source, const char *data);
void botnet_send_cmd(BOTNET_REC *botnet, const char *source,
const char *target, const char *data);
int botnet_send_file(BOTNET_REC *botnet, const char *target, const char *fname);
BOT_REC *botnet_find_master(BOTNET_REC *botnet, BOT_REC *old_master);
void botnet_set_master(BOTNET_REC *botnet, BOT_REC *bot);
BOTNET_REC *botnet_find(const char *name);
GNode *bot_find_nick(BOTNET_REC *botnet, const char *nick);
/* Return the bot who we should send the message if we wanted `nick' to get it. */
GNode *bot_find_path(BOTNET_REC *botnet, const char *nick);
BOT_DOWNLINK_REC *bot_downlink_find(BOTNET_REC *botnet, IPADDR *ip, const char *host);
void bot_nick_destroy(BOT_CHANNEL_REC *rec, NICK_REC *nick);
void bot_channel_destroy(BOT_IRCNET_REC *ircnet, BOT_CHANNEL_REC *rec);
void bot_ircnet_destroy(BOT_REC *bot, BOT_IRCNET_REC *rec);
void bot_disconnect(BOT_REC *bot);
void bot_destroy(BOT_REC *bot);
void bot_downlink_destroy(BOT_DOWNLINK_REC *rec);
void bot_uplink_destroy(BOT_UPLINK_REC *rec);
void botnet_connect(BOTNET_REC *botnet);
void botnet_disconnect(BOTNET_REC *botnet);
#endif

View File

@ -1,15 +0,0 @@
botnets = {
irssinet = {
nick = irssibot;
priority = 5;
autoconnect = yes;
uplinks = (
{ host = "main.botnet.org"; password = "mypass"; }
);
downlinks = (
{ password = "thepass"; valid_addrs = ( "192.168.0.*" ); },
{ password = "blah"; valid_addrs = ( "*.botnet.org" ); },
{ password = "localpass"; valid_addrs = ( "127.*" ); }
);
};
};

View File

@ -1,3 +0,0 @@
#include "common.h"
#define MODULE_NAME "irc/bot"

View File

@ -1,18 +0,0 @@
users =
{
mynick = {
flags = oa;
masks = (
{ mask="*!*@somewhere" },
{ mask="*!*@somewhere.else"; not_flags=a; }
);
};
other = {
masks = ( { mask="*!nick@home.org"; } );
channels = (
{ channel = "#irssi";flags = oa; },
{ channel = "#chan";flags = oa; }
);
};
};