1
0
mirror of https://github.com/anope/anope.git synced 2026-06-12 19:14:47 +02:00
Files
anope/botserv.c
T
dane dane@31f1291d-b8d6-0310-a050-a5561fc1590b 811ce26663 BUILD : 1.6.0 (4) BUGS : none NOTES : File cleanup and new AnopeManager script (bin/am) to work with Anope source control. Version schema change with no impact on cpp directives.
git-svn-id: svn://svn.anope.org/anope/trunk@4 31f1291d-b8d6-0310-a050-a5561fc1590b


git-svn-id: http://anope.svn.sourceforge.net/svnroot/anope/trunk@2 5417fbe8-f217-4b02-8779-1006273d7864
2004-03-28 23:00:59 +00:00

2513 lines
86 KiB
C

/* BotServ functions
*
* (C) 2003 Anope Team
* Contact us at info@anope.org
*
* Please read COPYING and README for furhter details.
*
* Based on the original code of Epona by Lara.
* Based on the original code of Services by Andy Church.
*
* $Id$
*
*/
/*************************************************************************/
#include "services.h"
#include "pseudo.h"
/**
* RFC: defination of a valid nick
* nickname = ( letter / special ) *8( letter / digit / special / "-" )
* letter = %x41-5A / %x61-7A ; A-Z / a-z
* digit = %x30-39 ; 0-9
* special = %x5B-60 / %x7B-7D ; "[", "]", "\", "`", "_", "^", "{", "|", "}"
**/
#define isvalidnick(c) ( isalnum(c) || ((c) >='\x5B' && (c) <='\x60') || ((c) >='\x7B' && (c) <='\x7D') || (c)=='-' )
/*************************************************************************/
BotInfo *botlists[256]; /* Hash list of bots */
int nbots = 0;
/*************************************************************************/
static BotInfo *makebot(char *nick);
static UserData *get_user_data(Channel * c, User * u);
static void unassign(User * u, ChannelInfo * ci);
static void check_ban(ChannelInfo * ci, User * u, int ttbtype);
static void bot_kick(ChannelInfo * ci, User * u, int message, ...);
static void bot_raw_ban(User * requester, ChannelInfo * ci, char *nick,
char *reason);
static void bot_raw_kick(User * requester, ChannelInfo * ci, char *nick,
char *reason);
static void bot_raw_mode(User * requester, ChannelInfo * ci, char *mode,
char *nick);
static void bot_raw_unban(ChannelInfo * ci, char *nick);
static int do_help(User * u);
static int do_bot(User * u);
static int do_botlist(User * u);
static int do_assign(User * u);
static int do_unassign(User * u);
static int do_info(User * u);
static int do_set(User * u);
static int do_kickcmd(User * u);
static int do_badwords(User * u);
static int do_say(User * u);
static int do_act(User * u);
void moduleAddBotServCmds(void);
/*************************************************************************/
/* *INDENT-OFF* */
void moduleAddBotServCmds(void) {
Command *c;
c = createCommand("HELP",do_help,NULL, -1,-1,-1,-1,-1); addCoreCommand(BOTSERV,c);
c = createCommand("BOTLIST", do_botlist, NULL, BOT_HELP_BOTLIST, -1,-1,-1,-1); addCoreCommand(BOTSERV,c);
c = createCommand("ASSIGN", do_assign, NULL, BOT_HELP_ASSIGN, -1,-1,-1,-1); addCoreCommand(BOTSERV,c);
c = createCommand("UNASSIGN", do_unassign, NULL, BOT_HELP_UNASSIGN, -1,-1,-1,-1); addCoreCommand(BOTSERV,c);
c = createCommand("INFO", do_info, NULL, BOT_HELP_INFO, -1,-1,-1,-1); addCoreCommand(BOTSERV,c);
c = createCommand("SET", do_set, NULL, BOT_HELP_SET,-1, BOT_SERVADMIN_HELP_SET,BOT_SERVADMIN_HELP_SET, BOT_SERVADMIN_HELP_SET); addCoreCommand(BOTSERV,c);
c = createCommand("SET DONTKICKOPS", NULL, NULL, BOT_HELP_SET_DONTKICKOPS, -1,-1,-1,-1); addCoreCommand(BOTSERV,c);
c = createCommand("SET DONTKICKVOICES", NULL, NULL, BOT_HELP_SET_DONTKICKVOICES, -1,-1,-1,-1); addCoreCommand(BOTSERV,c);
c = createCommand("SET FANTASY", NULL, NULL, BOT_HELP_SET_FANTASY, -1,-1,-1,-1); addCoreCommand(BOTSERV,c);
c = createCommand("SET GREET", NULL, NULL, BOT_HELP_SET_GREET, -1,-1,-1,-1); addCoreCommand(BOTSERV,c);
c = createCommand("SET SYMBIOSIS", NULL, NULL, BOT_HELP_SET_SYMBIOSIS, -1,-1,-1,-1); addCoreCommand(BOTSERV,c);
c = createCommand("KICK", do_kickcmd, NULL, BOT_HELP_KICK, -1,-1,-1,-1); addCoreCommand(BOTSERV,c);
c = createCommand("KICK BADWORDS", NULL, NULL, BOT_HELP_KICK_BADWORDS, -1,-1,-1,-1); addCoreCommand(BOTSERV,c);
c = createCommand("KICK BOLDS", NULL, NULL, BOT_HELP_KICK_BOLDS, -1,-1,-1,-1); addCoreCommand(BOTSERV,c);
c = createCommand("KICK CAPS", NULL, NULL, BOT_HELP_KICK_CAPS, -1,-1,-1,-1); addCoreCommand(BOTSERV,c);
c = createCommand("KICK COLORS", NULL, NULL, BOT_HELP_KICK_COLORS, -1,-1,-1,-1); addCoreCommand(BOTSERV,c);
c = createCommand("KICK FLOOD", NULL, NULL, BOT_HELP_KICK_FLOOD, -1,-1,-1,-1); addCoreCommand(BOTSERV,c);
c = createCommand("KICK REPEAT", NULL, NULL, BOT_HELP_KICK_REPEAT, -1,-1,-1,-1); addCoreCommand(BOTSERV,c);
c = createCommand("KICK REVERSES", NULL, NULL, BOT_HELP_KICK_REVERSES, -1,-1,-1,-1); addCoreCommand(BOTSERV,c);
c = createCommand("KICK UNDERLINES", NULL, NULL, BOT_HELP_KICK_UNDERLINES, -1,-1,-1,-1); addCoreCommand(BOTSERV,c);
c = createCommand("BADWORDS", do_badwords, NULL, BOT_HELP_BADWORDS, -1,-1,-1,-1); addCoreCommand(BOTSERV,c);
c = createCommand("SAY", do_say, NULL, BOT_HELP_SAY, -1,-1,-1,-1); addCoreCommand(BOTSERV,c);
c = createCommand("ACT", do_act, NULL, BOT_HELP_ACT, -1,-1,-1,-1); addCoreCommand(BOTSERV,c);
/* Services admins commands */
c = createCommand("BOT", do_bot, is_services_admin, -1,-1, BOT_SERVADMIN_HELP_BOT,BOT_SERVADMIN_HELP_BOT, BOT_SERVADMIN_HELP_BOT); addCoreCommand(BOTSERV,c);
c = createCommand("SET NOBOT", NULL, NULL, -1,-1, BOT_SERVADMIN_HELP_SET_NOBOT,BOT_SERVADMIN_HELP_SET_NOBOT, BOT_SERVADMIN_HELP_SET_NOBOT); addCoreCommand(BOTSERV,c);
c = createCommand("SET PRIVATE", NULL, NULL, -1,-1, BOT_SERVADMIN_HELP_SET_PRIVATE,BOT_SERVADMIN_HELP_SET_PRIVATE, BOT_SERVADMIN_HELP_SET_PRIVATE); addCoreCommand(BOTSERV,c);
}
/* *INDENT-ON* */
/*************************************************************************/
/*************************************************************************/
/* Return information on memory use. Assumes pointers are valid. */
void get_botserv_stats(long *nrec, long *memuse)
{
long count = 0, mem = 0;
int i;
BotInfo *bi;
for (i = 0; i < 256; i++) {
for (bi = botlists[i]; bi; bi = bi->next) {
count++;
mem += sizeof(*bi);
mem += strlen(bi->nick) + 1;
mem += strlen(bi->user) + 1;
mem += strlen(bi->host) + 1;
mem += strlen(bi->real) + 1;
}
}
*nrec = count;
*memuse = mem;
}
/*************************************************************************/
/*************************************************************************/
/* BotServ initialization. */
void bs_init(void)
{
Command *cmd;
moduleAddBotServCmds();
cmd = findCommand(BOTSERV, "SET SYMBIOSIS");
if (cmd)
cmd->help_param1 = s_ChanServ;
}
/*************************************************************************/
/* Main BotServ routine. */
void botserv(User * u, char *buf)
{
char *cmd, *s;
cmd = strtok(buf, " ");
if (!cmd) {
return;
} else if (stricmp(cmd, "\1PING") == 0) {
if (!(s = strtok(NULL, "")))
s = "\1";
notice(s_BotServ, u->nick, "\1PING %s", s);
} else if (skeleton) {
notice_lang(s_BotServ, u, SERVICE_OFFLINE, s_BotServ);
} else {
mod_run_cmd(s_BotServ, u, BOTSERV, cmd);
}
}
/*************************************************************************/
/* Handles all messages sent to bots. (Currently only answers to pings ;) */
void botmsgs(User * u, BotInfo * bi, char *buf)
{
char *cmd = strtok(buf, " ");
char *s;
if (!cmd || !u)
return;
if (!stricmp(cmd, "\1PING")) {
if (!(s = strtok(NULL, "")))
s = "\1";
notice(bi->nick, u->nick, "\1PING %s", s);
}
}
/*************************************************************************/
/* Handles all messages that are sent to registered channels where a
* bot is on.
*
*/
void botchanmsgs(User * u, ChannelInfo * ci, char *buf)
{
int c;
int16 cstatus = 0;
char *cmd;
UserData *ud;
if (!u)
return;
/* Answer to ping if needed, without breaking the buffer. */
if (!strnicmp(buf, "\1PING", 5))
notice(ci->bi->nick, u->nick, buf);
/* If it's a /me, cut the CTCP part at the beginning (not
* at the end, because one character just doesn't matter,
* but the ACTION may create strange behaviours with the
* caps or badwords kickers */
if (!strnicmp(buf, "\1ACTION ", 8))
buf += 8;
/* Now we can make kicker stuff. We try to order the checks
* from the fastest one to the slowest one, since there's
* no need to process other kickers if an user is kicked before
* the last kicker check.
*
* But FIRST we check whether the user is protected in any
* way.
*/
/* We first retrieve the user status on the channel if needed */
if (ci->botflags & (BS_DONTKICKOPS | BS_DONTKICKVOICES))
cstatus = chan_get_user_status(ci->c, u);
if (buf && !check_access(u, ci, CA_NOKICK) &&
#ifdef HAS_HALFOP
#if defined(IRC_UNREAL) || defined (IRC_VIAGRA)
(!(ci->botflags & BS_DONTKICKOPS)
|| !(cstatus & (CUS_HALFOP | CUS_OP | CUS_OWNER | CUS_PROTECT)))
# elif defined (IRC_ULTIMATE3)
(!(ci->botflags & BS_DONTKICKOPS)
|| !(cstatus & (CUS_HALFOP | CUS_OP | CUS_PROTECT)))
# else
(!(ci->botflags & BS_DONTKICKOPS)
|| !(cstatus & (CUS_HALFOP | CUS_OP)))
# endif
#else
(!(ci->botflags & BS_DONTKICKOPS) || !(cstatus & CUS_OP))
#endif
&& (!(ci->botflags & BS_DONTKICKVOICES) || !(cstatus & CUS_VOICE))) {
/* Bolds kicker */
if ((ci->botflags & BS_KICK_BOLDS) && strchr(buf, 2)) {
check_ban(ci, u, TTB_BOLDS);
bot_kick(ci, u, BOT_REASON_BOLD);
return;
}
/* Color kicker */
if ((ci->botflags & BS_KICK_COLORS) && strchr(buf, 3)) {
check_ban(ci, u, TTB_COLORS);
bot_kick(ci, u, BOT_REASON_COLOR);
return;
}
/* Reverses kicker */
if ((ci->botflags & BS_KICK_REVERSES) && strchr(buf, 22)) {
check_ban(ci, u, TTB_REVERSES);
bot_kick(ci, u, BOT_REASON_REVERSE);
return;
}
/* Underlines kicker */
if ((ci->botflags & BS_KICK_UNDERLINES) && strchr(buf, 31)) {
check_ban(ci, u, TTB_UNDERLINES);
bot_kick(ci, u, BOT_REASON_UNDERLINE);
return;
}
/* Caps kicker */
if ((ci->botflags & BS_KICK_CAPS)
&& ((c = strlen(buf)) >= ci->capsmin)) {
int i = 0;
char *s = buf;
do {
if (isupper(*s))
i++;
} while (*s++);
if (i >= ci->capsmin && i * 100 / c >= ci->capspercent) {
check_ban(ci, u, TTB_CAPS);
bot_kick(ci, u, BOT_REASON_CAPS);
return;
}
}
/* Bad words kicker */
if (ci->botflags & BS_KICK_BADWORDS) {
int i;
int mustkick = 0;
BadWord *bw;
for (i = 0, bw = ci->badwords; i < ci->bwcount; i++, bw++) {
if (!bw->in_use)
continue;
if (bw->type == BW_ANY && stristr(buf, bw->word)) {
mustkick = 1;
} else if (bw->type == BW_SINGLE) {
int len = strlen(bw->word);
if (!stricmp(buf, bw->word)) {
mustkick = 1;
/* two next if are quite odd isn't it? =) */
} else if ((strchr(buf, ' ') == buf + len)
&& (stristr(buf, bw->word) == buf)) {
mustkick = 1;
} else
if ((strrchr(buf, ' ') ==
buf + strlen(buf) - len - 1)
&& (stristr(buf, bw->word) ==
buf + strlen(buf) - len)) {
mustkick = 1;
} else {
char *wordbuf = scalloc(len + 3, 1);
wordbuf[0] = ' ';
wordbuf[len + 1] = ' ';
wordbuf[len + 2] = '\0';
memcpy(wordbuf + 1, bw->word, len);
if (stristr(buf, wordbuf))
mustkick = 1;
}
} else if (bw->type == BW_START) {
int len = strlen(bw->word);
if (!strnicmp(buf, bw->word, len)) {
mustkick = 1;
} else {
char *wordbuf = scalloc(len + 2, 1);
memcpy(wordbuf + 1, bw->word, len);
wordbuf[0] = ' ';
wordbuf[len + 1] = '\0';
if (stristr(buf, wordbuf))
mustkick = 1;
free(wordbuf);
}
} else if (bw->type == BW_END) {
int len = strlen(bw->word);
if (!strnicmp(buf + strlen(buf) - len, bw->word, len)) {
mustkick = 1;
} else {
char *wordbuf = scalloc(len + 2, 1);
memcpy(wordbuf, bw->word, len);
wordbuf[len] = ' ';
wordbuf[len + 1] = '\0';
if (stristr(buf, wordbuf))
mustkick = 1;
free(wordbuf);
}
}
if (mustkick) {
check_ban(ci, u, TTB_BADWORDS);
if (BSGentleBWReason)
bot_kick(ci, u, BOT_REASON_BADWORD_GENTLE);
else
bot_kick(ci, u, BOT_REASON_BADWORD, bw->word);
return;
}
}
}
/* Flood kicker */
if (ci->botflags & BS_KICK_FLOOD) {
time_t now = time(NULL);
ud = get_user_data(ci->c, u);
if (!ud)
return;
if (now - ud->last_start > ci->floodsecs) {
ud->last_start = time(NULL);
ud->lines = 0;
}
ud->lines++;
if (ud->lines >= ci->floodlines) {
check_ban(ci, u, TTB_FLOOD);
bot_kick(ci, u, BOT_REASON_FLOOD);
return;
}
}
/* Repeat kicker */
if (ci->botflags & BS_KICK_REPEAT) {
ud = get_user_data(ci->c, u);
if (!ud)
return;
if (ud->lastline && stricmp(ud->lastline, buf)) {
free(ud->lastline);
ud->lastline = sstrdup(buf);
ud->times = 0;
} else {
if (!ud->lastline)
ud->lastline = sstrdup(buf);
ud->times++;
}
if (ud->times >= ci->repeattimes) {
check_ban(ci, u, TTB_REPEAT);
bot_kick(ci, u, BOT_REASON_REPEAT);
return;
}
}
}
/* return if the user is on the ignore list */
if (get_ignore(u->nick) != NULL) {
return;
}
/* Fantaisist commands */
if (buf && (ci->botflags & BS_FANTASY) && *buf == '!'
&& check_access(u, ci, CA_FANTASIA)) {
cmd = strtok(buf, " ");
if (cmd) {
#if defined(IRC_UNREAL) || defined (IRC_VIAGRA)
if (!stricmp(cmd, "!deowner")) {
if (is_founder(u, ci))
bot_raw_mode(u, ci, "-q", u->nick);
} else
#endif
if (!stricmp(cmd, "!kb")) {
char *target = strtok(NULL, " ");
char *reason = strtok(NULL, "");
if (!target && check_access(u, ci, CA_BANME)) {
bot_raw_ban(u, ci, u->nick, "Requested");
} else if (target && check_access(u, ci, CA_BAN)) {
if (!stricmp(target, ci->bi->nick)) {
bot_raw_ban(u, ci, u->nick, "Oops!");
} else {
if (!reason)
bot_raw_ban(u, ci, target, "Requested");
else
bot_raw_ban(u, ci, target, reason);
}
}
} else if ((!stricmp(cmd, "!kick")) || (!stricmp(cmd, "!k"))) {
char *target = strtok(NULL, " ");
char *reason = strtok(NULL, "");
if (!target && check_access(u, ci, CA_KICKME)) {
bot_raw_kick(u, ci, u->nick, "Requested");
} else if (target && check_access(u, ci, CA_KICK)) {
if (!stricmp(target, ci->bi->nick))
bot_raw_kick(u, ci, u->nick, "Oops!");
else if (!reason)
bot_raw_kick(u, ci, target, "Requested");
else
bot_raw_kick(u, ci, target, reason);
}
#if defined(IRC_UNREAL) || defined (IRC_VIAGRA)
} else if (!stricmp(cmd, "!owner")) {
if (is_founder(u, ci))
bot_raw_mode(u, ci, "+q", u->nick);
#endif
} else if (!stricmp(cmd, "!seen")) {
char *target = strtok(NULL, " ");
char buf[BUFSIZE];
if (target) {
User *u2;
NickAlias *na;
ChanAccess *access;
if (!stricmp(ci->bi->nick, target)) {
/* If we look for the bot */
snprintf(buf, sizeof(buf),
getstring(u->na, BOT_SEEN_BOT), u->nick);
send_cmd(ci->bi->nick, "PRIVMSG %s :%s", ci->name,
buf);
} else if (!(na = findnick(target))
|| (na->status & NS_VERBOTEN)) {
/* If the nick is not registered or forbidden */
snprintf(buf, sizeof(buf),
getstring(u->na, BOT_SEEN_UNKNOWN),
target);
send_cmd(ci->bi->nick, "PRIVMSG %s :%s", ci->name,
buf);
} else if ((u2 = nc_on_chan(ci->c, na->nc))) {
/* If the nick we're looking for is on the channel,
* there are three possibilities: it's yourself,
* it's the nick we look for, it's an alias of the
* nick we look for.
*/
if (u == u2 || (u->na && u->na->nc == na->nc))
snprintf(buf, sizeof(buf),
getstring(u->na, BOT_SEEN_YOU),
u->nick);
else if (!stricmp(u2->nick, target))
snprintf(buf, sizeof(buf),
getstring(u->na, BOT_SEEN_ON_CHANNEL),
u2->nick);
else
snprintf(buf, sizeof(buf),
getstring(u->na,
BOT_SEEN_ON_CHANNEL_AS),
target, u2->nick);
send_cmd(ci->bi->nick, "PRIVMSG %s :%s", ci->name,
buf);
} else if ((access = get_access_entry(na->nc, ci))) {
/* User is on the access list but not present actually.
Special case: if access->last_seen is 0 it's that we
never seen the user.
*/
if (access->last_seen) {
char durastr[192];
duration(u->na, durastr, sizeof(durastr),
time(NULL) - access->last_seen);
snprintf(buf, sizeof(buf),
getstring(u->na, BOT_SEEN_ON), target,
durastr);
} else {
snprintf(buf, sizeof(buf),
getstring(u->na, BOT_SEEN_NEVER),
target);
}
send_cmd(ci->bi->nick, "PRIVMSG %s :%s", ci->name,
buf);
} else {
/* All other cases */
snprintf(buf, sizeof(buf),
getstring(u->na, BOT_SEEN_UNKNOWN),
target);
send_cmd(ci->bi->nick, "PRIVMSG %s :%s", ci->name,
buf);
}
}
} else if (!stricmp(cmd, "!unban")
&& check_access(u, ci, CA_UNBAN)) {
char *target = strtok(NULL, " ");
if (!target)
bot_raw_unban(ci, u->nick);
else
bot_raw_unban(ci, target);
} else {
CSModeUtil *util = csmodeutils;
do {
if (!stricmp(cmd, util->bsname)) {
char *target = strtok(NULL, " ");
if (!target
&& check_access(u, ci, util->levelself))
bot_raw_mode(u, ci, util->mode, u->nick);
else if (target
&& check_access(u, ci, util->level))
bot_raw_mode(u, ci, util->mode, target);
}
} while ((++util)->name != NULL);
}
}
}
}
/*************************************************************************/
/* Load/save data files. */
#define SAFE(x) do { \
if ((x) < 0) { \
if (!forceload) \
fatal("Read error on %s", BotDBName); \
failed = 1; \
break; \
} \
} while (0)
void load_bs_dbase(void)
{
dbFILE *f;
int c, ver;
int16 tmp16;
int32 tmp32;
BotInfo *bi;
int failed = 0;
if (!(f = open_db(s_BotServ, BotDBName, "r", BOT_VERSION)))
return;
ver = get_file_version(f);
while (!failed && (c = getc_db(f)) != 0) {
char *s;
if (c != 1)
fatal("Invalid format in %s %d", BotDBName, c);
SAFE(read_string(&s, f));
bi = makebot(s);
free(s);
SAFE(read_string(&bi->user, f));
SAFE(read_string(&bi->host, f));
SAFE(read_string(&bi->real, f));
if (ver >= 10) {
SAFE(read_int16(&tmp16, f));
bi->flags = tmp16;
}
SAFE(read_int32(&tmp32, f));
bi->created = tmp32;
SAFE(read_int16(&tmp16, f));
bi->chancount = tmp16;
}
close_db(f);
}
#undef SAFE
/*************************************************************************/
#define SAFE(x) do { \
if ((x) < 0) { \
restore_db(f); \
log_perror("Write error on %s", BotDBName); \
if (time(NULL) - lastwarn > WarningTimeout) { \
wallops(NULL, "Write error on %s: %s", BotDBName, \
strerror(errno)); \
lastwarn = time(NULL); \
} \
return; \
} \
} while (0)
void save_bs_dbase(void)
{
dbFILE *f;
BotInfo *bi;
static time_t lastwarn = 0;
int i;
if (!(f = open_db(s_BotServ, BotDBName, "w", BOT_VERSION)))
return;
for (i = 0; i < 256; i++) {
for (bi = botlists[i]; bi; bi = bi->next) {
SAFE(write_int8(1, f));
SAFE(write_string(bi->nick, f));
SAFE(write_string(bi->user, f));
SAFE(write_string(bi->host, f));
SAFE(write_string(bi->real, f));
SAFE(write_int16(bi->flags, f));
SAFE(write_int32(bi->created, f));
SAFE(write_int16(bi->chancount, f));
}
}
SAFE(write_int8(0, f));
close_db(f);
}
#undef SAFE
/*************************************************************************/
void save_bs_rdb_dbase(void)
{
#ifdef USE_RDB
int i;
BotInfo *bi;
if (!rdb_open())
return;
rdb_clear_table("anope_bs_core");
for (i = 0; i < 256; i++) {
for (bi = botlists[i]; bi; bi = bi->next) {
rdb_save_bs_core(bi);
}
}
rdb_close();
#endif
}
/*************************************************************************/
/* Inserts a bot in the bot list. I can't be much explicit mh? */
static void insert_bot(BotInfo * bi)
{
BotInfo *ptr, *prev;
for (prev = NULL, ptr = botlists[tolower(*bi->nick)];
ptr != NULL && stricmp(ptr->nick, bi->nick) < 0;
prev = ptr, ptr = ptr->next);
bi->prev = prev;
bi->next = ptr;
if (!prev)
botlists[tolower(*bi->nick)] = bi;
else
prev->next = bi;
if (ptr)
ptr->prev = bi;
}
/*************************************************************************/
static BotInfo *makebot(char *nick)
{
BotInfo *bi;
bi = scalloc(sizeof(BotInfo), 1);
bi->nick = sstrdup(nick);
bi->lastmsg = time(NULL);
insert_bot(bi);
nbots++;
return bi;
}
/*************************************************************************/
static void change_bot_nick(BotInfo * bi, char *newnick)
{
if (bi->next)
bi->next->prev = bi->prev;
if (bi->prev)
bi->prev->next = bi->next;
else
botlists[tolower(*bi->nick)] = bi->next;
if (bi->nick)
free(bi->nick);
bi->nick = sstrdup(newnick);
insert_bot(bi);
}
/*************************************************************************/
static int delbot(BotInfo * bi)
{
cs_remove_bot(bi);
if (bi->next)
bi->next->prev = bi->prev;
if (bi->prev)
bi->prev->next = bi->next;
else
botlists[tolower(*bi->nick)] = bi->next;
nbots--;
free(bi->nick);
free(bi->user);
free(bi->host);
free(bi->real);
free(bi);
return 1;
}
/*************************************************************************/
BotInfo *findbot(char *nick)
{
BotInfo *bi;
if (!nick || !*nick)
return NULL;
for (bi = botlists[tolower(*nick)]; bi; bi = bi->next)
if (!stricmp(nick, bi->nick))
return bi;
return NULL;
}
/*************************************************************************/
/* Unassign a bot from a channel. Assumes u, ci and ci->bi are not NULL */
static void unassign(User * u, ChannelInfo * ci)
{
if (ci->c && ci->c->usercount >= BSMinUsers) {
send_cmd(ci->bi->nick, "PART %s :UNASSIGN from %s", ci->name,
u->nick);
}
ci->bi->chancount--;
ci->bi = NULL;
}
/*************************************************************************/
/* Returns ban data associated with an user if it exists, allocates it
otherwise. */
static BanData *get_ban_data(Channel * c, User * u)
{
char mask[BUFSIZE];
BanData *bd, *next;
time_t now = time(NULL);
if (!c || !u)
return NULL;
snprintf(mask, sizeof(mask), "%s@%s", u->username, GetHost(u));
for (bd = c->bd; bd; bd = next) {
if (now - bd->last_use > BSKeepData) {
if (bd->next)
bd->next->prev = bd->prev;
if (bd->prev)
bd->prev->next = bd->next;
else
c->bd = bd->next;
if (bd->mask)
free(bd->mask);
next = bd->next;
free(bd);
continue;
}
if (!stricmp(bd->mask, mask)) {
bd->last_use = now;
return bd;
}
next = bd->next;
}
/* If we fall here it is that we haven't found the record */
bd = scalloc(sizeof(BanData), 1);
bd->mask = sstrdup(mask);
bd->last_use = now;
bd->prev = NULL;
bd->next = c->bd;
if (bd->next)
bd->next->prev = bd;
c->bd = bd;
return bd;
}
/*************************************************************************/
/* Returns BotServ data associated with an user on a given channel.
* Allocates it if necessary.
*/
static UserData *get_user_data(Channel * c, User * u)
{
struct c_userlist *user;
if (!c || !u)
return NULL;
for (user = c->users; user; user = user->next) {
if (user->user == u) {
if (user->ud) {
time_t now = time(NULL);
/* Checks whether data is obsolete */
if (now - user->ud->last_use > BSKeepData) {
if (user->ud->lastline)
free(user->ud->lastline);
/* We should not free and realloc, but reset to 0
instead. */
memset(user->ud, 0, sizeof(UserData));
user->ud->last_use = now;
}
return user->ud;
} else {
user->ud = scalloc(sizeof(UserData), 1);
user->ud->last_use = time(NULL);
return user->ud;
}
}
}
return NULL;
}
/*************************************************************************/
/* Makes the bot join a channel and op himself. */
void bot_join(ChannelInfo * ci)
{
int i;
if (!ci || !ci->c || !ci->bi)
return;
if (BSSmartJoin) {
/* We check for bans */
int count = ci->c->bancount;
if (count) {
char botmask[BUFSIZE];
char **bans = scalloc(sizeof(char *) * count, 1);
char *av[3];
memcpy(bans, ci->c->bans, sizeof(char *) * count);
snprintf(botmask, sizeof(botmask), "%s!%s@%s", ci->bi->nick,
ci->bi->user, ci->bi->host);
av[0] = ci->c->name;
av[1] = sstrdup("-b");
for (i = 0; i < count; i++) {
if (match_wild_nocase(ci->c->bans[i], botmask)) {
send_cmd(ci->bi->nick, "MODE %s -b %s", ci->name,
bans[i]);
av[2] = sstrdup(bans[i]);
do_cmode(ci->bi->nick, 3, av);
free(av[2]);
}
}
free(av[1]);
free(bans);
}
/* Should we be invited? */
if ((ci->c->mode & CMODE_i)
|| (ci->c->limit && ci->c->usercount >= ci->c->limit))
send_cmd(NULL, "NOTICE @%s :%s invited %s into the channel.",
ci->c->name, ci->bi->nick, ci->bi->nick);
}
#ifdef IRC_BAHAMUT
send_cmd(ci->bi->nick, "SJOIN %ld %s", ci->c->creation_time,
ci->c->name);
#elif defined(IRC_HYBRID)
send_cmd(NULL, "SJOIN %ld %s + :%s", time(NULL), ci->c->name,
ci->bi->nick);
#else
send_cmd(ci->bi->nick, "JOIN %s", ci->c->name);
#endif
#if defined(IRC_UNREAL) || defined (IRC_VIAGRA)
send_cmd(ci->bi->nick, "MODE %s +ao %s %s", ci->c->name, ci->bi->nick,
ci->bi->nick);
#elif defined(IRC_PTLINK)
/* PTLinks requieres an IRCop to u-line changes, so use ChanServ */
send_cmd(s_ChanServ, "MODE %s +ao %s %s", ci->c->name, ci->bi->nick,
ci->bi->nick);
#else
send_cmd(ci->bi->nick, "MODE %s +o %s", ci->c->name, ci->bi->nick);
#endif
}
/*************************************************************************/
/* This makes the bot rejoin all channel he is on when he gets killed
* or changed.
*/
void bot_rejoin_all(BotInfo * bi)
{
int i;
ChannelInfo *ci;
for (i = 0; i < 256; i++)
for (ci = chanlists[i]; ci; ci = ci->next)
if (ci->bi == bi && ci->c && (ci->c->usercount >= BSMinUsers))
bot_join(ci);
}
/*************************************************************************/
/* This makes a ban if the user has to have one. In every cases it increments
the kick count for the user. */
static void check_ban(ChannelInfo * ci, User * u, int ttbtype)
{
BanData *bd = get_ban_data(ci->c, u);
if (!bd)
return;
bd->ttb[ttbtype]++;
if (bd->ttb[ttbtype] == ci->ttb[ttbtype]) {
char *av[3];
char mask[BUFSIZE];
bd->ttb[ttbtype] = 0;
av[0] = ci->name;
av[1] = sstrdup("+b");
get_idealban(ci, u, mask, sizeof(mask));
av[2] = mask;
send_cmd(ci->bi->nick, "MODE %s +b %s", av[0], av[2]);
do_cmode(ci->bi->nick, 3, av);
free(av[1]);
}
}
/*************************************************************************/
/* This makes a bot kick an user. Works somewhat like notice_lang in fact ;) */
static void bot_kick(ChannelInfo * ci, User * u, int message, ...)
{
va_list args;
char buf[1024];
const char *fmt;
char *av[3];
if (!ci || !ci->bi || !ci->c || !u)
return;
va_start(args, message);
fmt = getstring(u->na, message);
if (!fmt)
return;
vsnprintf(buf, sizeof(buf), fmt, args);
av[0] = ci->name;
av[1] = u->nick;
av[2] = buf;
send_cmd(ci->bi->nick, "KICK %s %s :%s", av[0], av[1], av[2]);
do_kick(ci->bi->nick, 3, av);
}
/*************************************************************************/
/* Makes a simple ban and kicks the target */
static void bot_raw_ban(User * requester, ChannelInfo * ci, char *nick,
char *reason)
{
char *av[3];
char mask[BUFSIZE];
User *u = finduser(nick);
if (!u)
return;
#if defined(IRC_ULTIMATE) || defined(IRC_ULTIMATE3)
if (is_protected(u) && (requester != u)) {
send_cmd(ci->bi->nick, "PRIVMSG %s :%s", ci->name,
getstring2(NULL, PERMISSION_DENIED));
return;
}
#endif
if ((ci->flags & CI_PEACE) && stricmp(requester->nick, nick)
&& (get_access(u, ci) >= get_access(requester, ci)))
return;
#if defined (IRC_ULTIMATE) || defined(IRC_ULTIMATE3) || defined(IRC_UNREAL) || defined(IRC_VIAGRA) || defined(IRC_HYBRID)
if (is_excepted(ci, u) == 1) {
send_cmd(ci->bi->nick, "PRIVMSG %s :%s", ci->name,
getstring2(NULL, BOT_EXCEPT));
return;
}
#endif
av[0] = ci->name;
av[1] = sstrdup("+b");
get_idealban(ci, u, mask, sizeof(mask));
av[2] = mask;
send_cmd(ci->bi->nick, "MODE %s +b %s", av[0], av[2]);
do_cmode(ci->bi->nick, 3, av);
free(av[1]);
av[0] = ci->name;
av[1] = nick;
if (!reason) {
av[2] = ci->bi->nick;
} else {
if (strlen(reason) > 200)
reason[200] = '\0';
av[2] = reason;
}
send_cmd(ci->bi->nick, "KICK %s %s :%s", av[0], av[1], av[2]);
do_kick(ci->bi->nick, 3, av);
}
/*************************************************************************/
/* Makes a kick with a "dynamic" reason ;) */
static void bot_raw_kick(User * requester, ChannelInfo * ci, char *nick,
char *reason)
{
char *av[3];
User *u = finduser(nick);
if (!u || !is_on_chan(ci->c, u))
return;
#if defined(IRC_ULTIMATE) || defined(IRC_ULTIMATE3)
if (is_protected(u) && (requester != u)) {
send_cmd(ci->bi->nick, "PRIVMSG %s :%s", ci->name,
getstring2(NULL, PERMISSION_DENIED));
return;
}
#endif
if ((ci->flags & CI_PEACE) && stricmp(requester->nick, nick)
&& (get_access(u, ci) >= get_access(requester, ci)))
return;
av[0] = ci->name;
av[1] = nick;
if (!reason) {
av[2] = ci->bi->nick;
} else {
if (strlen(reason) > 200)
reason[200] = '\0';
av[2] = reason;
}
send_cmd(ci->bi->nick, "KICK %s %s :%s", av[0], av[1], av[2]);
do_kick(ci->bi->nick, 3, av);
}
/*************************************************************************/
/* Makes a mode operation on a channel for a nick */
static void bot_raw_mode(User * requester, ChannelInfo * ci, char *mode,
char *nick)
{
char *av[3];
User *u = finduser(nick);
if (!u || !is_on_chan(ci->c, u))
return;
#if defined(IRC_ULTIMATE) || defined(IRC_ULTIMATE3)
if (is_protected(u) && *mode == '-' && (requester != u)) {
send_cmd(ci->bi->nick, "PRIVMSG %s :%s", ci->name,
getstring2(NULL, PERMISSION_DENIED));
return;
}
#endif
if (*mode == '-' && (ci->flags & CI_PEACE)
&& stricmp(requester->nick, nick)
&& (get_access(u, ci) >= get_access(requester, ci)))
return;
av[0] = ci->name;
av[1] = mode;
av[2] = nick;
send_cmd(ci->bi->nick, "MODE %s %s %s", av[0], av[1], av[2]);
do_cmode(ci->bi->nick, 3, av);
}
/*************************************************************************/
/* Removes all bans for a nick on a channel */
static void bot_raw_unban(ChannelInfo * ci, char *nick)
{
#ifndef IRC_BAHAMUT
int count, i;
char *av[3], **bans;
User *u;
#endif
if (!ci || !ci->c || !ci->bi || !nick)
return;
#ifndef IRC_BAHAMUT
if (!(u = finduser(nick)))
return;
#else
if (!finduser(nick))
return;
#endif
#ifndef IRC_BAHAMUT
av[0] = ci->name;
av[1] = sstrdup("-b");
count = ci->c->bancount;
bans = scalloc(sizeof(char *) * count, 1);
memcpy(bans, ci->c->bans, sizeof(char *) * count);
for (i = 0; i < count; i++) {
if (match_usermask(bans[i], u)) {
send_cmd(ci->bi->nick, "MODE %s -b %s", ci->name, bans[i]);
av[2] = bans[i];
do_cmode(ci->bi->nick, 3, av);
}
}
free(bans);
free(av[1]);
#else
send_cmd(ServerName, "SVSMODE %s -b %s", ci->name, nick);
#endif
}
/*************************************************************************/
/*************************************************************************/
static int do_help(User * u)
{
char *cmd = strtok(NULL, "");
if (!cmd) {
notice_help(s_BotServ, u, BOT_HELP, BSMinUsers);
if (is_services_oper(u))
notice_help(s_BotServ, u, BOT_SERVADMIN_HELP);
moduleDisplayHelp(4, u);
} else {
mod_help_cmd(s_BotServ, u, BOTSERV, cmd);
}
return MOD_CONT;
}
/*************************************************************************/
static int do_bot(User * u)
{
BotInfo *bi;
char *cmd = strtok(NULL, " ");
char *ch = NULL;
if (!cmd)
syntax_error(s_BotServ, u, "BOT", BOT_BOT_SYNTAX);
else if (!stricmp(cmd, "ADD")) {
char *nick = strtok(NULL, " ");
char *user = strtok(NULL, " ");
char *host = strtok(NULL, " ");
char *real = strtok(NULL, "");
if (!nick || !user || !host || !real)
syntax_error(s_BotServ, u, "BOT", BOT_BOT_SYNTAX);
else if (readonly)
notice_lang(s_BotServ, u, BOT_BOT_READONLY);
else if (findbot(nick))
notice_lang(s_BotServ, u, BOT_BOT_ALREADY_EXISTS, nick);
else {
User *u2;
NickAlias *na;
/**
* Check the nick is valid re RFC 2812
**/
if (isdigit(nick[0]) || nick[0] == '-') {
notice_lang(s_BotServ, u, BOT_BAD_NICK);
return MOD_CONT;
}
for (ch = nick; *ch && (ch - nick) < NICKMAX; ch++) {
if (!isvalidnick(*ch)) {
notice_lang(s_BotServ, u, BOT_BAD_NICK);
return MOD_CONT;
}
}
if (!isValidHost(host, 3)) {
notice_lang(s_BotServ, u, BOT_BAD_HOST);
return MOD_CONT;
}
for (ch = user; *ch && (ch - user) < USERMAX; ch++) {
if (!isalnum(*ch)) {
notice_lang(s_BotServ, u, BOT_BAD_IDENT);
return MOD_CONT;
}
}
/**
* Check the host is valid re RFC 2812
**/
/* Check whether it's a services client's nick and return if so - Certus */
if ((s_NickServ && (stricmp(nick, s_NickServ) == 0))
|| (s_NickServAlias && !stricmp(nick, s_NickServAlias))) {
notice_lang(s_BotServ, u, BOT_BOT_CREATION_FAILED);
return MOD_CONT;
} else if ((s_ChanServ && (stricmp(nick, s_ChanServ) == 0))
|| (s_ChanServAlias
&& !stricmp(nick, s_ChanServAlias))) {
notice_lang(s_BotServ, u, BOT_BOT_CREATION_FAILED);
return MOD_CONT;
} else if ((s_MemoServ && (stricmp(nick, s_MemoServ) == 0))
|| (s_MemoServAlias
&& !stricmp(nick, s_MemoServAlias))) {
notice_lang(s_BotServ, u, BOT_BOT_CREATION_FAILED);
return MOD_CONT;
} else if ((s_BotServ && (stricmp(nick, s_BotServ) == 0))
|| (s_BotServAlias
&& !stricmp(nick, s_BotServAlias))) {
notice_lang(s_BotServ, u, BOT_BOT_CREATION_FAILED);
return MOD_CONT;
} else if ((s_HelpServ && (stricmp(nick, s_HelpServ) == 0))
|| (s_HelpServAlias
&& !stricmp(nick, s_HelpServAlias))) {
notice_lang(s_BotServ, u, BOT_BOT_CREATION_FAILED);
return MOD_CONT;
} else if ((s_OperServ && (stricmp(nick, s_OperServ) == 0))
|| (s_OperServAlias
&& !stricmp(nick, s_OperServAlias))) {
notice_lang(s_BotServ, u, BOT_BOT_CREATION_FAILED);
return MOD_CONT;
} else
if ((s_GlobalNoticer
&& (stricmp(nick, s_GlobalNoticer) == 0))
|| (s_GlobalNoticerAlias
&& !stricmp(nick, s_GlobalNoticerAlias))) {
notice_lang(s_BotServ, u, BOT_BOT_CREATION_FAILED);
return MOD_CONT;
} else if ((s_HostServ && (stricmp(nick, s_HostServ) == 0))
|| (s_HostServAlias
&& !stricmp(nick, s_HostServAlias))) {
notice_lang(s_BotServ, u, BOT_BOT_CREATION_FAILED);
return MOD_CONT;
}
/* We check whether the nick is registered, and drop it if so. */
if ((na = findnick(nick))) {
if (NSSecureAdmins && nick_is_services_admin(na->nc)
&& !is_services_root(u)) {
notice_lang(s_BotServ, u, PERMISSION_DENIED);
return MOD_CONT;
}
delnick(na);
}
bi = makebot(nick);
if (!bi) {
notice_lang(s_BotServ, u, BOT_BOT_CREATION_FAILED);
return MOD_CONT;
}
bi->user = sstrdup(user);
bi->host = sstrdup(host);
bi->real = sstrdup(real);
bi->created = time(NULL);
bi->chancount = 0;
/* We check whether user with this nick is online, and kill it if so */
if ((u2 = finduser(nick)))
kill_user(s_BotServ, u2->nick,
"This nick is now used by Services");
/* We make the bot online, ready to serve */
#if defined(IRC_UNREAL) || defined (IRC_VIAGRA)
NEWNICK(bi->nick, bi->user, bi->host, bi->real, "+qS", 1);
#elif defined(IRC_ULTIMATE) || defined (IRC_ULTIMATE3)
NEWNICK(bi->nick, bi->user, bi->host, bi->real, "+pS", 1);
#else
NEWNICK(bi->nick, bi->user, bi->host, bi->real, "+", 1);
#endif
notice_lang(s_BotServ, u, BOT_BOT_ADDED, bi->nick, bi->user,
bi->host, bi->real);
}
} else if (!stricmp(cmd, "CHANGE")) {
char *oldnick = strtok(NULL, " ");
char *nick = strtok(NULL, " ");
char *user = strtok(NULL, " ");
char *host = strtok(NULL, " ");
char *real = strtok(NULL, "");
if (!oldnick || !nick)
syntax_error(s_BotServ, u, "BOT", BOT_BOT_SYNTAX);
else if (readonly)
notice_lang(s_BotServ, u, BOT_BOT_READONLY);
else if (!(bi = findbot(oldnick)))
notice_lang(s_BotServ, u, BOT_DOES_NOT_EXIST, oldnick);
else {
User *u2;
NickAlias *na;
/* Checks whether there *are* changes.
* Case sensitive because we may want to change just the case.
* And we must finally check that the nick is not already
* taken by another bot.
*/
if (!strcmp(bi->nick, nick)
&& ((user) ? !strcmp(bi->user, user) : 1)
&& ((host) ? !strcmp(bi->host, host) : 1)
&& ((real) ? !strcmp(bi->real, real) : 1)) {
notice_lang(s_BotServ, u, BOT_BOT_ANY_CHANGES);
return MOD_CONT;
}
/* Check whether it's a services client's nick and return if so - Certus */
if ((s_NickServ && !stricmp(nick, s_NickServ))
|| (s_NickServAlias && !stricmp(nick, s_NickServAlias))) {
notice_lang(s_BotServ, u, BOT_BOT_CREATION_FAILED);
return MOD_CONT;
} else if ((s_ChanServ && !stricmp(nick, s_ChanServ))
|| (s_ChanServAlias
&& !stricmp(nick, s_ChanServAlias))) {
notice_lang(s_BotServ, u, BOT_BOT_CREATION_FAILED);
return MOD_CONT;
} else if ((s_MemoServ && !stricmp(nick, s_MemoServ))
|| (s_MemoServAlias
&& !stricmp(nick, s_MemoServAlias))) {
notice_lang(s_BotServ, u, BOT_BOT_CREATION_FAILED);
return MOD_CONT;
} else if ((s_BotServ && !stricmp(nick, s_BotServ))
|| (s_BotServAlias
&& !stricmp(nick, s_BotServAlias))) {
notice_lang(s_BotServ, u, BOT_BOT_CREATION_FAILED);
return MOD_CONT;
} else if ((s_HelpServ && !stricmp(nick, s_HelpServ))
|| (s_HelpServAlias
&& !stricmp(nick, s_HelpServAlias))) {
notice_lang(s_BotServ, u, BOT_BOT_CREATION_FAILED);
return MOD_CONT;
} else if ((s_OperServ && !stricmp(nick, s_OperServ))
|| (s_OperServAlias
&& !stricmp(nick, s_OperServAlias))) {
notice_lang(s_BotServ, u, BOT_BOT_CREATION_FAILED);
return MOD_CONT;
} else if ((s_GlobalNoticer && !stricmp(nick, s_GlobalNoticer))
|| (s_GlobalNoticerAlias
&& !stricmp(nick, s_GlobalNoticerAlias))) {
notice_lang(s_BotServ, u, BOT_BOT_CREATION_FAILED);
return MOD_CONT;
} else if ((s_HostServ && !stricmp(nick, s_HostServ))
|| (s_HostServAlias
&& !stricmp(nick, s_HostServAlias))) {
notice_lang(s_BotServ, u, BOT_BOT_CREATION_FAILED);
return MOD_CONT;
}
/**
* Check the nick is valid re RFC 2812
**/
if (isdigit(nick[0]) || nick[0] == '-') {
notice_lang(s_BotServ, u, BOT_BAD_NICK);
return MOD_CONT;
}
for (ch = nick; *ch && (ch - nick) < NICKMAX; ch++) {
if (!isvalidnick(*ch)) {
notice_lang(s_BotServ, u, BOT_BAD_NICK);
return MOD_CONT;
}
}
if (!isValidHost(host, 3)) {
notice_lang(s_BotServ, u, BOT_BAD_HOST);
return MOD_CONT;
}
for (ch = user; *ch && (ch - user) < USERMAX; ch++) {
if (!isalnum(*ch)) {
notice_lang(s_BotServ, u, BOT_BAD_IDENT);
return MOD_CONT;
}
}
if (stricmp(bi->nick, nick) && findbot(nick)) {
notice_lang(s_BotServ, u, BOT_BOT_ALREADY_EXISTS, nick);
return MOD_CONT;
}
if (stricmp(bi->nick, nick)) {
/* The new nick is really different, so we remove the Q line for
the old nick. */
#ifndef IRC_HYBRID
send_cmd(NULL, "UNSQLINE %s", bi->nick);
#endif
/* We check whether the nick is registered, and drop it if so */
if ((na = findnick(nick)))
delnick(na);
/* We check whether user with this nick is online, and kill it if so */
if ((u2 = finduser(nick)))
kill_user(s_BotServ, u2->nick,
"This nick is now used by Services");
}
if (strcmp(nick, bi->nick))
change_bot_nick(bi, nick);
if (user && strcmp(user, bi->user)) {
free(bi->user);
bi->user = sstrdup(user);
}
if (host && strcmp(host, bi->host)) {
free(bi->host);
bi->host = sstrdup(host);
}
if (real && strcmp(real, bi->real)) {
free(bi->real);
bi->real = sstrdup(real);
}
/* If only the nick changes, we just make the bot change his nick,
else we must make it quit and rejoin. */
if (!user)
send_cmd(oldnick, "NICK %s", bi->nick);
else {
send_cmd(oldnick, "QUIT :Quit: Be right back");
#if defined(IRC_UNREAL)
NEWNICK(bi->nick, bi->user, bi->host, bi->real, "+qS", 1);
#elif defined(IRC_ULTIMATE)
NEWNICK(bi->nick, bi->user, bi->host, bi->real, "+pS", 1);
#else
NEWNICK(bi->nick, bi->user, bi->host, bi->real, "+", 1);
#endif
bot_rejoin_all(bi);
}
notice_lang(s_BotServ, u, BOT_BOT_CHANGED, oldnick, bi->nick,
bi->user, bi->host, bi->real);
}
} else if (!stricmp(cmd, "DEL")) {
char *nick = strtok(NULL, " ");
if (!nick)
syntax_error(s_BotServ, u, "BOT", BOT_BOT_SYNTAX);
else if (readonly)
notice_lang(s_BotServ, u, BOT_BOT_READONLY);
else if (!(bi = findbot(nick)))
notice_lang(s_BotServ, u, BOT_DOES_NOT_EXIST, nick);
else {
send_cmd(bi->nick,
"QUIT :Quit: Help! I'm being deleted by %s!",
u->nick);
#ifndef IRC_HYBRID
send_cmd(NULL, "UNSQLINE %s", bi->nick);
#endif
delbot(bi);
notice_lang(s_BotServ, u, BOT_BOT_DELETED, nick);
}
} else if (!stricmp(cmd, "LIST"))
do_botlist(u);
else
syntax_error(s_BotServ, u, "BOT", BOT_BOT_SYNTAX);
return MOD_CONT;
}
/*************************************************************************/
static int do_botlist(User * u)
{
int i, count = 0;
BotInfo *bi;
if (!nbots) {
notice_lang(s_BotServ, u, BOT_BOTLIST_EMPTY);
return MOD_CONT;
}
for (i = 0; i < 256; i++) {
for (bi = botlists[i]; bi; bi = bi->next) {
if (!(bi->flags & BI_PRIVATE)) {
if (!count)
notice_lang(s_BotServ, u, BOT_BOTLIST_HEADER);
count++;
notice_user(s_BotServ, u, " %-15s (%s@%s)", bi->nick,
bi->user, bi->host);
}
}
}
if (is_oper(u) && count < nbots) {
notice_lang(s_BotServ, u, BOT_BOTLIST_PRIVATE_HEADER);
for (i = 0; i < 256; i++) {
for (bi = botlists[i]; bi; bi = bi->next) {
if (bi->flags & BI_PRIVATE) {
notice_user(s_BotServ, u, " %-15s (%s@%s)",
bi->nick, bi->user, bi->host);
count++;
}
}
}
}
if (!count)
notice_lang(s_BotServ, u, BOT_BOTLIST_EMPTY);
else
notice_lang(s_BotServ, u, BOT_BOTLIST_FOOTER, count);
return MOD_CONT;
}
/*************************************************************************/
static int do_assign(User * u)
{
char *chan = strtok(NULL, " ");
char *nick = strtok(NULL, " ");
BotInfo *bi;
ChannelInfo *ci;
if (readonly)
notice_lang(s_BotServ, u, BOT_ASSIGN_READONLY);
else if (!chan || !nick)
syntax_error(s_BotServ, u, "ASSIGN", BOT_ASSIGN_SYNTAX);
else if (!(bi = findbot(nick)))
notice_lang(s_BotServ, u, BOT_DOES_NOT_EXIST, nick);
else if (bi->flags & BI_PRIVATE && !is_oper(u))
notice_lang(s_BotServ, u, PERMISSION_DENIED);
else if (!(ci = cs_findchan(chan)))
notice_lang(s_BotServ, u, CHAN_X_NOT_REGISTERED, chan);
else if (ci->flags & CI_VERBOTEN)
notice_lang(s_BotServ, u, CHAN_X_FORBIDDEN, chan);
else if ((ci->bi) && (stricmp(ci->bi->nick, nick) == 0))
notice_lang(s_BotServ, u, BOT_ASSIGN_ALREADY, ci->bi->nick, chan);
else if ((ci->botflags & BS_NOBOT)
|| (!check_access(u, ci, CA_ASSIGN) && !is_services_admin(u)))
notice_lang(s_BotServ, u, PERMISSION_DENIED);
else {
if (ci->bi)
unassign(u, ci);
ci->bi = bi;
bi->chancount++;
if (ci->c && ci->c->usercount >= BSMinUsers) {
bot_join(ci);
}
notice_lang(s_BotServ, u, BOT_ASSIGN_ASSIGNED, bi->nick, ci->name);
}
return MOD_CONT;
}
/*************************************************************************/
static int do_unassign(User * u)
{
char *chan = strtok(NULL, " ");
ChannelInfo *ci;
if (readonly)
notice_lang(s_BotServ, u, BOT_ASSIGN_READONLY);
else if (!chan)
syntax_error(s_BotServ, u, "UNASSIGN", BOT_UNASSIGN_SYNTAX);
else if (!(ci = cs_findchan(chan)))
notice_lang(s_BotServ, u, CHAN_X_NOT_REGISTERED, chan);
else if (ci->flags & CI_VERBOTEN)
notice_lang(s_BotServ, u, CHAN_X_FORBIDDEN, chan);
else if (!is_services_admin(u) && !check_access(u, ci, CA_ASSIGN))
notice_lang(s_BotServ, u, ACCESS_DENIED);
else {
if (ci->bi)
unassign(u, ci);
notice_lang(s_BotServ, u, BOT_UNASSIGN_UNASSIGNED, ci->name);
}
return MOD_CONT;
}
/*************************************************************************/
static void send_bot_channels(User * u, BotInfo * bi)
{
int i;
ChannelInfo *ci;
char buf[307], *end;
*buf = 0;
end = buf;
for (i = 0; i < 256; i++) {
for (ci = chanlists[i]; ci; ci = ci->next) {
if (ci->bi == bi) {
if (strlen(buf) + strlen(ci->name) > 300) {
notice_user(s_BotServ, u, buf);
*buf = 0;
end = buf;
}
end +=
snprintf(end, sizeof(buf) - (end - buf), " %s ",
ci->name);
}
}
}
if (*buf)
notice_user(s_BotServ, u, buf);
return;
}
static int do_info(User * u)
{
BotInfo *bi;
ChannelInfo *ci;
char *query = strtok(NULL, " ");
int need_comma = 0, is_servadmin = is_services_admin(u);
char buf[BUFSIZE], *end;
const char *commastr = getstring(u->na, COMMA_SPACE);
if (!query)
syntax_error(s_BotServ, u, "INFO", BOT_INFO_SYNTAX);
else if ((bi = findbot(query))) {
char buf[BUFSIZE];
struct tm *tm;
notice_lang(s_BotServ, u, BOT_INFO_BOT_HEADER, bi->nick);
notice_lang(s_BotServ, u, BOT_INFO_BOT_MASK, bi->user, bi->host);
notice_lang(s_BotServ, u, BOT_INFO_BOT_REALNAME, bi->real);
tm = localtime(&bi->created);
strftime_lang(buf, sizeof(buf), u, STRFTIME_DATE_TIME_FORMAT, tm);
notice_lang(s_BotServ, u, BOT_INFO_BOT_CREATED, buf);
notice_lang(s_BotServ, u, BOT_INFO_BOT_OPTIONS,
getstring(u->na,
(bi->
flags & BI_PRIVATE) ? BOT_INFO_OPT_PRIVATE :
BOT_INFO_OPT_NONE));
notice_lang(s_BotServ, u, BOT_INFO_BOT_USAGE, bi->chancount);
if (is_services_admin(u))
send_bot_channels(u, bi);
} else if ((ci = cs_findchan(query))) {
if (!is_servadmin && !is_founder(u, ci)) {
notice_lang(s_BotServ, u, PERMISSION_DENIED);
return MOD_CONT;
}
notice_lang(s_BotServ, u, BOT_INFO_CHAN_HEADER, ci->name);
if (ci->bi)
notice_lang(s_BotServ, u, BOT_INFO_CHAN_BOT, ci->bi->nick);
else
notice_lang(s_BotServ, u, BOT_INFO_CHAN_BOT_NONE);
if (ci->botflags & BS_KICK_BADWORDS) {
if (ci->ttb[TTB_BADWORDS])
notice_lang(s_BotServ, u, BOT_INFO_CHAN_KICK_BADWORDS_BAN,
getstring(u->na, BOT_INFO_ACTIVE),
ci->ttb[TTB_BADWORDS]);
else
notice_lang(s_BotServ, u, BOT_INFO_CHAN_KICK_BADWORDS,
getstring(u->na, BOT_INFO_ACTIVE));
} else
notice_lang(s_BotServ, u, BOT_INFO_CHAN_KICK_BADWORDS,
getstring(u->na, BOT_INFO_INACTIVE));
if (ci->botflags & BS_KICK_BOLDS) {
if (ci->ttb[TTB_BOLDS])
notice_lang(s_BotServ, u, BOT_INFO_CHAN_KICK_BOLDS_BAN,
getstring(u->na, BOT_INFO_ACTIVE),
ci->ttb[TTB_BOLDS]);
else
notice_lang(s_BotServ, u, BOT_INFO_CHAN_KICK_BOLDS,
getstring(u->na, BOT_INFO_ACTIVE));
} else
notice_lang(s_BotServ, u, BOT_INFO_CHAN_KICK_BOLDS,
getstring(u->na, BOT_INFO_INACTIVE));
if (ci->botflags & BS_KICK_CAPS) {
if (ci->ttb[TTB_CAPS])
notice_lang(s_BotServ, u, BOT_INFO_CHAN_KICK_CAPS_BAN,
getstring(u->na, BOT_INFO_ACTIVE),
ci->ttb[TTB_CAPS], ci->capsmin,
ci->capspercent);
else
notice_lang(s_BotServ, u, BOT_INFO_CHAN_KICK_CAPS_ON,
getstring(u->na, BOT_INFO_ACTIVE), ci->capsmin,
ci->capspercent);
} else
notice_lang(s_BotServ, u, BOT_INFO_CHAN_KICK_CAPS_OFF,
getstring(u->na, BOT_INFO_INACTIVE));
if (ci->botflags & BS_KICK_COLORS) {
if (ci->ttb[TTB_COLORS])
notice_lang(s_BotServ, u, BOT_INFO_CHAN_KICK_COLORS_BAN,
getstring(u->na, BOT_INFO_ACTIVE),
ci->ttb[TTB_COLORS]);
else
notice_lang(s_BotServ, u, BOT_INFO_CHAN_KICK_COLORS,
getstring(u->na, BOT_INFO_ACTIVE));
} else
notice_lang(s_BotServ, u, BOT_INFO_CHAN_KICK_COLORS,
getstring(u->na, BOT_INFO_INACTIVE));
if (ci->botflags & BS_KICK_FLOOD) {
if (ci->ttb[TTB_FLOOD])
notice_lang(s_BotServ, u, BOT_INFO_CHAN_KICK_FLOOD_BAN,
getstring(u->na, BOT_INFO_ACTIVE),
ci->ttb[TTB_FLOOD], ci->floodlines,
ci->floodsecs);
else
notice_lang(s_BotServ, u, BOT_INFO_CHAN_KICK_FLOOD_ON,
getstring(u->na, BOT_INFO_ACTIVE),
ci->floodlines, ci->floodsecs);
} else
notice_lang(s_BotServ, u, BOT_INFO_CHAN_KICK_FLOOD_OFF,
getstring(u->na, BOT_INFO_INACTIVE));
if (ci->botflags & BS_KICK_REPEAT) {
if (ci->ttb[TTB_REPEAT])
notice_lang(s_BotServ, u, BOT_INFO_CHAN_KICK_REPEAT_BAN,
getstring(u->na, BOT_INFO_ACTIVE),
ci->ttb[TTB_REPEAT], ci->repeattimes);
else
notice_lang(s_BotServ, u, BOT_INFO_CHAN_KICK_REPEAT_ON,
getstring(u->na, BOT_INFO_ACTIVE),
ci->repeattimes);
} else
notice_lang(s_BotServ, u, BOT_INFO_CHAN_KICK_REPEAT_OFF,
getstring(u->na, BOT_INFO_INACTIVE));
if (ci->botflags & BS_KICK_REVERSES) {
if (ci->ttb[TTB_REVERSES])
notice_lang(s_BotServ, u, BOT_INFO_CHAN_KICK_REVERSES_BAN,
getstring(u->na, BOT_INFO_ACTIVE),
ci->ttb[TTB_REVERSES]);
else
notice_lang(s_BotServ, u, BOT_INFO_CHAN_KICK_REVERSES,
getstring(u->na, BOT_INFO_ACTIVE));
} else
notice_lang(s_BotServ, u, BOT_INFO_CHAN_KICK_REVERSES,
getstring(u->na, BOT_INFO_INACTIVE));
if (ci->botflags & BS_KICK_UNDERLINES) {
if (ci->ttb[TTB_UNDERLINES])
notice_lang(s_BotServ, u,
BOT_INFO_CHAN_KICK_UNDERLINES_BAN,
getstring(u->na, BOT_INFO_ACTIVE),
ci->ttb[TTB_UNDERLINES]);
else
notice_lang(s_BotServ, u, BOT_INFO_CHAN_KICK_UNDERLINES,
getstring(u->na, BOT_INFO_ACTIVE));
} else
notice_lang(s_BotServ, u, BOT_INFO_CHAN_KICK_UNDERLINES,
getstring(u->na, BOT_INFO_INACTIVE));
end = buf;
*end = 0;
if (ci->botflags & BS_DONTKICKOPS) {
end += snprintf(end, sizeof(buf) - (end - buf), "%s",
getstring(u->na, BOT_INFO_OPT_DONTKICKOPS));
need_comma = 1;
}
if (ci->botflags & BS_DONTKICKVOICES) {
end += snprintf(end, sizeof(buf) - (end - buf), "%s%s",
need_comma ? commastr : "",
getstring(u->na, BOT_INFO_OPT_DONTKICKVOICES));
need_comma = 1;
}
if (ci->botflags & BS_FANTASY) {
end += snprintf(end, sizeof(buf) - (end - buf), "%s%s",
need_comma ? commastr : "",
getstring(u->na, BOT_INFO_OPT_FANTASY));
need_comma = 1;
}
if (ci->botflags & BS_GREET) {
end += snprintf(end, sizeof(buf) - (end - buf), "%s%s",
need_comma ? commastr : "",
getstring(u->na, BOT_INFO_OPT_GREET));
need_comma = 1;
}
if (ci->botflags & BS_NOBOT) {
end += snprintf(end, sizeof(buf) - (end - buf), "%s%s",
need_comma ? commastr : "",
getstring(u->na, BOT_INFO_OPT_NOBOT));
need_comma = 1;
}
if (ci->botflags & BS_SYMBIOSIS) {
end += snprintf(end, sizeof(buf) - (end - buf), "%s%s",
need_comma ? commastr : "",
getstring(u->na, BOT_INFO_OPT_SYMBIOSIS));
need_comma = 1;
}
notice_lang(s_BotServ, u, BOT_INFO_CHAN_OPTIONS,
*buf ? buf : getstring(u->na, BOT_INFO_OPT_NONE));
} else
notice_lang(s_BotServ, u, BOT_INFO_NOT_FOUND, query);
return MOD_CONT;
}
/*************************************************************************/
static int do_set(User * u)
{
char *chan = strtok(NULL, " ");
char *option = strtok(NULL, " ");
char *value = strtok(NULL, " ");
int is_servadmin = is_services_admin(u);
ChannelInfo *ci;
if (readonly)
notice_lang(s_BotServ, u, BOT_SET_DISABLED);
else if (!chan || !option || !value)
syntax_error(s_BotServ, u, "SET", BOT_SET_SYNTAX);
else if (is_servadmin && !stricmp(option, "PRIVATE")) {
BotInfo *bi;
if ((bi = findbot(chan))) {
if (!stricmp(value, "ON")) {
bi->flags |= BI_PRIVATE;
notice_lang(s_BotServ, u, BOT_SET_PRIVATE_ON, bi->nick);
} else if (!stricmp(value, "OFF")) {
bi->flags &= ~BI_PRIVATE;
notice_lang(s_BotServ, u, BOT_SET_PRIVATE_OFF, bi->nick);
} else {
syntax_error(s_BotServ, u, "SET PRIVATE",
BOT_SET_PRIVATE_SYNTAX);
}
} else {
notice_lang(s_BotServ, u, BOT_DOES_NOT_EXIST, chan);
}
return MOD_CONT;
} else if (!(ci = cs_findchan(chan)))
notice_lang(s_BotServ, u, CHAN_X_NOT_REGISTERED, chan);
else if (ci->flags & CI_VERBOTEN)
notice_lang(s_BotServ, u, CHAN_X_FORBIDDEN, chan);
else if (!is_servadmin && !check_access(u, ci, CA_SET))
notice_lang(s_BotServ, u, ACCESS_DENIED);
else {
if (!stricmp(option, "DONTKICKOPS")) {
if (!stricmp(value, "ON")) {
ci->botflags |= BS_DONTKICKOPS;
notice_lang(s_BotServ, u, BOT_SET_DONTKICKOPS_ON,
ci->name);
} else if (!stricmp(value, "OFF")) {
ci->botflags &= ~BS_DONTKICKOPS;
notice_lang(s_BotServ, u, BOT_SET_DONTKICKOPS_OFF,
ci->name);
} else {
syntax_error(s_BotServ, u, "SET DONTKICKOPS",
BOT_SET_DONTKICKOPS_SYNTAX);
}
} else if (!stricmp(option, "DONTKICKVOICES")) {
if (!stricmp(value, "ON")) {
ci->botflags |= BS_DONTKICKVOICES;
notice_lang(s_BotServ, u, BOT_SET_DONTKICKVOICES_ON,
ci->name);
} else if (!stricmp(value, "OFF")) {
ci->botflags &= ~BS_DONTKICKVOICES;
notice_lang(s_BotServ, u, BOT_SET_DONTKICKVOICES_OFF,
ci->name);
} else {
syntax_error(s_BotServ, u, "SET DONTKICKVOICES",
BOT_SET_DONTKICKVOICES_SYNTAX);
}
} else if (!stricmp(option, "FANTASY")) {
if (!stricmp(value, "ON")) {
ci->botflags |= BS_FANTASY;
notice_lang(s_BotServ, u, BOT_SET_FANTASY_ON, ci->name);
} else if (!stricmp(value, "OFF")) {
ci->botflags &= ~BS_FANTASY;
notice_lang(s_BotServ, u, BOT_SET_FANTASY_OFF, ci->name);
} else {
syntax_error(s_BotServ, u, "SET FANTASY",
BOT_SET_FANTASY_SYNTAX);
}
} else if (!stricmp(option, "GREET")) {
if (!stricmp(value, "ON")) {
ci->botflags |= BS_GREET;
notice_lang(s_BotServ, u, BOT_SET_GREET_ON, ci->name);
} else if (!stricmp(value, "OFF")) {
ci->botflags &= ~BS_GREET;
notice_lang(s_BotServ, u, BOT_SET_GREET_OFF, ci->name);
} else {
syntax_error(s_BotServ, u, "SET GREET",
BOT_SET_GREET_SYNTAX);
}
} else if (is_servadmin && !stricmp(option, "NOBOT")) {
if (!stricmp(value, "ON")) {
ci->botflags |= BS_NOBOT;
if (ci->bi)
unassign(u, ci);
notice_lang(s_BotServ, u, BOT_SET_NOBOT_ON, ci->name);
} else if (!stricmp(value, "OFF")) {
ci->botflags &= ~BS_NOBOT;
notice_lang(s_BotServ, u, BOT_SET_NOBOT_OFF, ci->name);
} else {
syntax_error(s_BotServ, u, "SET NOBOT",
BOT_SET_NOBOT_SYNTAX);
}
} else if (!stricmp(option, "SYMBIOSIS")) {
if (!stricmp(value, "ON")) {
ci->botflags |= BS_SYMBIOSIS;
notice_lang(s_BotServ, u, BOT_SET_SYMBIOSIS_ON, ci->name);
} else if (!stricmp(value, "OFF")) {
ci->botflags &= ~BS_SYMBIOSIS;
notice_lang(s_BotServ, u, BOT_SET_SYMBIOSIS_OFF, ci->name);
} else {
syntax_error(s_BotServ, u, "SET SYMBIOSIS",
BOT_SET_SYMBIOSIS_SYNTAX);
}
} else {
notice_help(s_BotServ, u, BOT_SET_UNKNOWN, option);
}
}
return MOD_CONT;
}
/*************************************************************************/
static int do_kickcmd(User * u)
{
char *chan = strtok(NULL, " ");
char *option = strtok(NULL, " ");
char *value = strtok(NULL, " ");
char *ttb = strtok(NULL, " ");
ChannelInfo *ci;
if (readonly)
notice_lang(s_BotServ, u, BOT_KICK_DISABLED);
else if (!chan || !option || !value)
syntax_error(s_BotServ, u, "KICK", BOT_KICK_SYNTAX);
else if (stricmp(value, "ON") && stricmp(value, "OFF"))
syntax_error(s_BotServ, u, "KICK", BOT_KICK_SYNTAX);
else if (!(ci = cs_findchan(chan)))
notice_lang(s_BotServ, u, CHAN_X_NOT_REGISTERED, chan);
else if (ci->flags & CI_VERBOTEN)
notice_lang(s_BotServ, u, CHAN_X_FORBIDDEN, chan);
else if (!is_services_admin(u) && !check_access(u, ci, CA_SET))
notice_lang(s_BotServ, u, ACCESS_DENIED);
else {
if (!stricmp(option, "BADWORDS")) {
if (!stricmp(value, "ON")) {
if (ttb) {
ci->ttb[TTB_BADWORDS] = atol(ttb);
if (ci->ttb[TTB_BADWORDS] < 0) {
notice_lang(s_BotServ, u, BOT_KICK_BAD_TTB, ttb);
return MOD_CONT;
}
} else
ci->ttb[TTB_BADWORDS] = 0;
ci->botflags |= BS_KICK_BADWORDS;
if (ci->ttb[TTB_BADWORDS])
notice_lang(s_BotServ, u, BOT_KICK_BADWORDS_ON_BAN,
ci->ttb[TTB_BADWORDS]);
else
notice_lang(s_BotServ, u, BOT_KICK_BADWORDS_ON);
} else {
ci->botflags &= ~BS_KICK_BADWORDS;
notice_lang(s_BotServ, u, BOT_KICK_BADWORDS_OFF);
}
} else if (!stricmp(option, "BOLDS")) {
if (!stricmp(value, "ON")) {
if (ttb) {
ci->ttb[TTB_BOLDS] = atol(ttb);
if (ci->ttb[TTB_BOLDS] < 0) {
notice_lang(s_BotServ, u, BOT_KICK_BAD_TTB, ttb);
return MOD_CONT;
}
} else
ci->ttb[TTB_BOLDS] = 0;
ci->botflags |= BS_KICK_BOLDS;
if (ci->ttb[TTB_BOLDS])
notice_lang(s_BotServ, u, BOT_KICK_BOLDS_ON_BAN,
ci->ttb[TTB_BOLDS]);
else
notice_lang(s_BotServ, u, BOT_KICK_BOLDS_ON);
} else {
ci->botflags &= ~BS_KICK_BOLDS;
notice_lang(s_BotServ, u, BOT_KICK_BOLDS_OFF);
}
} else if (!stricmp(option, "CAPS")) {
if (!stricmp(value, "ON")) {
char *min = strtok(NULL, " ");
char *percent = strtok(NULL, " ");
if (ttb) {
ci->ttb[TTB_CAPS] = atol(ttb);
if (ci->ttb[TTB_CAPS] < 0) {
notice_lang(s_BotServ, u, BOT_KICK_BAD_TTB, ttb);
return MOD_CONT;
}
} else
ci->ttb[TTB_CAPS] = 0;
if (!min)
ci->capsmin = 10;
else
ci->capsmin = atol(min);
if (ci->capsmin < 1)
ci->capsmin = 10;
if (!percent)
ci->capspercent = 25;
else
ci->capspercent = atol(percent);
if (ci->capspercent < 1 || ci->capspercent > 100)
ci->capspercent = 25;
ci->botflags |= BS_KICK_CAPS;
if (ci->ttb[TTB_CAPS])
notice_lang(s_BotServ, u, BOT_KICK_CAPS_ON_BAN,
ci->capsmin, ci->capspercent,
ci->ttb[TTB_CAPS]);
else
notice_lang(s_BotServ, u, BOT_KICK_CAPS_ON,
ci->capsmin, ci->capspercent);
} else {
ci->botflags &= ~BS_KICK_CAPS;
notice_lang(s_BotServ, u, BOT_KICK_CAPS_OFF);
}
} else if (!stricmp(option, "COLORS")) {
if (!stricmp(value, "ON")) {
if (ttb) {
ci->ttb[TTB_COLORS] = atol(ttb);
if (ci->ttb[TTB_COLORS] < 0) {
notice_lang(s_BotServ, u, BOT_KICK_BAD_TTB, ttb);
return MOD_CONT;
}
} else
ci->ttb[TTB_COLORS] = 0;
ci->botflags |= BS_KICK_COLORS;
if (ci->ttb[TTB_COLORS])
notice_lang(s_BotServ, u, BOT_KICK_COLORS_ON_BAN,
ci->ttb[TTB_COLORS]);
else
notice_lang(s_BotServ, u, BOT_KICK_COLORS_ON);
} else {
ci->botflags &= ~BS_KICK_COLORS;
notice_lang(s_BotServ, u, BOT_KICK_COLORS_OFF);
}
} else if (!stricmp(option, "FLOOD")) {
if (!stricmp(value, "ON")) {
char *lines = strtok(NULL, " ");
char *secs = strtok(NULL, " ");
if (ttb) {
ci->ttb[TTB_FLOOD] = atol(ttb);
if (ci->ttb[TTB_FLOOD] < 0) {
notice_lang(s_BotServ, u, BOT_KICK_BAD_TTB, ttb);
return MOD_CONT;
}
} else
ci->ttb[TTB_FLOOD] = 0;
if (!lines)
ci->floodlines = 6;
else
ci->floodlines = atol(lines);
if (ci->floodlines < 2)
ci->floodlines = 6;
if (!secs)
ci->floodsecs = 10;
else
ci->floodsecs = atol(secs);
if (ci->floodsecs < 1 || ci->floodsecs > BSKeepData)
ci->floodsecs = 10;
ci->botflags |= BS_KICK_FLOOD;
if (ci->ttb[TTB_FLOOD])
notice_lang(s_BotServ, u, BOT_KICK_FLOOD_ON_BAN,
ci->floodlines, ci->floodsecs,
ci->ttb[TTB_FLOOD]);
else
notice_lang(s_BotServ, u, BOT_KICK_FLOOD_ON,
ci->floodlines, ci->floodsecs);
} else {
ci->botflags &= ~BS_KICK_FLOOD;
notice_lang(s_BotServ, u, BOT_KICK_FLOOD_OFF);
}
} else if (!stricmp(option, "REPEAT")) {
if (!stricmp(value, "ON")) {
char *times = strtok(NULL, " ");
if (ttb) {
ci->ttb[TTB_REPEAT] = atol(ttb);
if (ci->ttb[TTB_REPEAT] < 0) {
notice_lang(s_BotServ, u, BOT_KICK_BAD_TTB, ttb);
return MOD_CONT;
}
} else
ci->ttb[TTB_REPEAT] = 0;
if (!times)
ci->repeattimes = 3;
else
ci->repeattimes = atol(times);
if (ci->repeattimes < 2)
ci->repeattimes = 3;
ci->botflags |= BS_KICK_REPEAT;
if (ci->ttb[TTB_REPEAT])
notice_lang(s_BotServ, u, BOT_KICK_REPEAT_ON_BAN,
ci->repeattimes, ci->ttb[TTB_REPEAT]);
else
notice_lang(s_BotServ, u, BOT_KICK_REPEAT_ON,
ci->repeattimes);
} else {
ci->botflags &= ~BS_KICK_REPEAT;
notice_lang(s_BotServ, u, BOT_KICK_REPEAT_OFF);
}
} else if (!stricmp(option, "REVERSES")) {
if (!stricmp(value, "ON")) {
if (ttb) {
ci->ttb[TTB_REVERSES] = atol(ttb);
if (ci->ttb[TTB_REVERSES] < 0) {
notice_lang(s_BotServ, u, BOT_KICK_BAD_TTB, ttb);
return MOD_CONT;
}
} else
ci->ttb[TTB_REVERSES] = 0;
ci->botflags |= BS_KICK_REVERSES;
if (ci->ttb[TTB_REVERSES])
notice_lang(s_BotServ, u, BOT_KICK_REVERSES_ON_BAN,
ci->ttb[TTB_REVERSES]);
else
notice_lang(s_BotServ, u, BOT_KICK_REVERSES_ON);
} else {
ci->botflags &= ~BS_KICK_REVERSES;
notice_lang(s_BotServ, u, BOT_KICK_REVERSES_OFF);
}
} else if (!stricmp(option, "UNDERLINES")) {
if (!stricmp(value, "ON")) {
if (ttb) {
ci->ttb[TTB_UNDERLINES] = atol(ttb);
if (ci->ttb[TTB_UNDERLINES] < 0) {
notice_lang(s_BotServ, u, BOT_KICK_BAD_TTB, ttb);
return MOD_CONT;
}
} else
ci->ttb[TTB_UNDERLINES] = 0;
ci->botflags |= BS_KICK_UNDERLINES;
if (ci->ttb[TTB_UNDERLINES])
notice_lang(s_BotServ, u, BOT_KICK_UNDERLINES_ON_BAN,
ci->ttb[TTB_UNDERLINES]);
else
notice_lang(s_BotServ, u, BOT_KICK_UNDERLINES_ON);
} else {
ci->botflags &= ~BS_KICK_UNDERLINES;
notice_lang(s_BotServ, u, BOT_KICK_UNDERLINES_OFF);
}
} else
notice_help(s_BotServ, u, BOT_KICK_UNKNOWN, option);
}
return MOD_CONT;
}
/*************************************************************************/
static int badwords_del_callback(User * u, int num, va_list args)
{
BadWord *bw;
ChannelInfo *ci = va_arg(args, ChannelInfo *);
int *last = va_arg(args, int *);
if (num < 1 || num > ci->bwcount)
return 0;
*last = num;
bw = &ci->badwords[num - 1];
if (bw->word)
free(bw->word);
bw->word = NULL;
bw->in_use = 0;
return 1;
}
static int badwords_list(User * u, int index, ChannelInfo * ci,
int *sent_header)
{
BadWord *bw = &ci->badwords[index];
if (!bw->in_use)
return 0;
if (!*sent_header) {
notice_lang(s_BotServ, u, BOT_BADWORDS_LIST_HEADER, ci->name);
*sent_header = 1;
}
notice_lang(s_BotServ, u, BOT_BADWORDS_LIST_FORMAT, index + 1,
bw->word,
((bw->type ==
BW_SINGLE) ? "(SINGLE)" : ((bw->type ==
BW_START) ? "(START)"
: ((bw->type ==
BW_END) ? "(END)" : "")))
);
return 1;
}
static int badwords_list_callback(User * u, int num, va_list args)
{
ChannelInfo *ci = va_arg(args, ChannelInfo *);
int *sent_header = va_arg(args, int *);
if (num < 1 || num > ci->bwcount)
return 0;
return badwords_list(u, num - 1, ci, sent_header);
}
static int do_badwords(User * u)
{
char *chan = strtok(NULL, " ");
char *cmd = strtok(NULL, " ");
char *word = strtok(NULL, "");
ChannelInfo *ci;
BadWord *bw;
int i;
int need_args = (cmd
&& (!stricmp(cmd, "LIST") || !stricmp(cmd, "CLEAR")));
if (!cmd || (need_args ? 0 : !word)) {
syntax_error(s_BotServ, u, "BADWORDS", BOT_BADWORDS_SYNTAX);
} else if (!(ci = cs_findchan(chan))) {
notice_lang(s_BotServ, u, CHAN_X_NOT_REGISTERED, chan);
} else if (ci->flags & CI_VERBOTEN) {
notice_lang(s_BotServ, u, CHAN_X_FORBIDDEN, chan);
} else if (!check_access(u, ci, CA_BADWORDS)
&& (!need_args || !is_services_admin(u))) {
notice_lang(s_BotServ, u, ACCESS_DENIED);
} else if (stricmp(cmd, "ADD") == 0) {
char *opt, *pos;
int type = BW_ANY;
if (readonly) {
notice_lang(s_BotServ, u, BOT_BADWORDS_DISABLED);
return MOD_CONT;
}
pos = strrchr(word, ' ');
if (pos) {
opt = pos + 1;
if (*opt) {
if (!stricmp(opt, "SINGLE"))
type = BW_SINGLE;
else if (!stricmp(opt, "START"))
type = BW_START;
else if (!stricmp(opt, "END"))
type = BW_END;
if (type != BW_ANY)
*pos = 0;
}
}
for (bw = ci->badwords, i = 0; i < ci->bwcount; bw++, i++) {
if (bw->word && !stricmp(bw->word, word)) {
notice_lang(s_BotServ, u, BOT_BADWORDS_ALREADY_EXISTS,
bw->word, ci->name);
return MOD_CONT;
}
}
for (i = 0; i < ci->bwcount; i++) {
if (!ci->badwords[i].in_use)
break;
}
if (i == ci->bwcount) {
if (i < BSBadWordsMax) {
ci->bwcount++;
ci->badwords =
srealloc(ci->badwords, sizeof(BadWord) * ci->bwcount);
} else {
notice_lang(s_BotServ, u, BOT_BADWORDS_REACHED_LIMIT,
BSBadWordsMax);
return MOD_CONT;
}
}
bw = &ci->badwords[i];
bw->in_use = 1;
bw->word = sstrdup(word);
bw->type = type;
notice_lang(s_BotServ, u, BOT_BADWORDS_ADDED, bw->word, ci->name);
} else if (stricmp(cmd, "DEL") == 0) {
if (readonly) {
notice_lang(s_BotServ, u, BOT_BADWORDS_DISABLED);
return MOD_CONT;
}
/* Special case: is it a number/list? Only do search if it isn't. */
if (isdigit(*word) && strspn(word, "1234567890,-") == strlen(word)) {
int count, deleted, last = -1;
deleted =
process_numlist(word, &count, badwords_del_callback, u, ci,
&last);
if (!deleted) {
if (count == 1) {
notice_lang(s_BotServ, u, BOT_BADWORDS_NO_SUCH_ENTRY,
last, ci->name);
} else {
notice_lang(s_BotServ, u, BOT_BADWORDS_NO_MATCH,
ci->name);
}
} else if (deleted == 1) {
notice_lang(s_BotServ, u, BOT_BADWORDS_DELETED_ONE,
ci->name);
} else {
notice_lang(s_BotServ, u, BOT_BADWORDS_DELETED_SEVERAL,
deleted, ci->name);
}
} else {
for (i = 0; i < ci->bwcount; i++) {
if (ci->badwords[i].in_use
&& !stricmp(ci->badwords[i].word, word))
break;
}
if (i == ci->bwcount) {
notice_lang(s_BotServ, u, BOT_BADWORDS_NOT_FOUND, word,
chan);
return MOD_CONT;
}
bw = &ci->badwords[i];
notice_lang(s_BotServ, u, BOT_BADWORDS_DELETED, bw->word,
ci->name);
if (bw->word)
free(bw->word);
bw->word = NULL;
bw->in_use = 0;
}
} else if (stricmp(cmd, "LIST") == 0) {
int sent_header = 0;
if (ci->bwcount == 0) {
notice_lang(s_BotServ, u, BOT_BADWORDS_LIST_EMPTY, chan);
return MOD_CONT;
}
if (word && strspn(word, "1234567890,-") == strlen(word)) {
process_numlist(word, NULL, badwords_list_callback, u, ci,
&sent_header);
} else {
for (i = 0; i < ci->bwcount; i++) {
if (!(ci->badwords[i].in_use))
continue;
if (word && ci->badwords[i].word
&& !match_wild_nocase(word, ci->badwords[i].word))
continue;
badwords_list(u, i, ci, &sent_header);
}
}
if (!sent_header)
notice_lang(s_BotServ, u, BOT_BADWORDS_NO_MATCH, chan);
} else if (stricmp(cmd, "CLEAR") == 0) {
if (readonly) {
notice_lang(s_BotServ, u, BOT_BADWORDS_DISABLED);
return MOD_CONT;
}
for (i = 0; i < ci->bwcount; i++)
if (ci->badwords[i].word)
free(ci->badwords[i].word);
free(ci->badwords);
ci->badwords = NULL;
ci->bwcount = 0;
notice_lang(s_BotServ, u, BOT_BADWORDS_CLEAR);
} else {
syntax_error(s_BotServ, u, "BADWORDS", BOT_BADWORDS_SYNTAX);
}
return MOD_CONT;
}
/*************************************************************************/
static int do_say(User * u)
{
ChannelInfo *ci;
char *chan = strtok(NULL, " ");
char *text = strtok(NULL, "");
if (!chan || !text)
syntax_error(s_BotServ, u, "SAY", BOT_SAY_SYNTAX);
else if (!(ci = cs_findchan(chan)))
notice_lang(s_BotServ, u, CHAN_X_NOT_REGISTERED, chan);
else if (ci->flags & CI_VERBOTEN)
notice_lang(s_BotServ, u, CHAN_X_FORBIDDEN, chan);
else if (!ci->bi)
notice_help(s_BotServ, u, BOT_NOT_ASSIGNED);
else if (!ci->c || ci->c->usercount < BSMinUsers)
notice_lang(s_BotServ, u, BOT_NOT_ON_CHANNEL, ci->name);
else if (!check_access(u, ci, CA_SAY))
notice_lang(s_BotServ, u, ACCESS_DENIED);
else {
if (text[0] != '\001') {
send_cmd(ci->bi->nick, "PRIVMSG %s :%s", ci->name, text);
ci->bi->lastmsg = time(NULL);
if (logchan && LogBot)
send_cmd(ci->bi->nick, "PRIVMSG %s :SAY %s %s %s",
LogChannel, u->nick, ci->name, text);
} else {
syntax_error(s_BotServ, u, "SAY", BOT_SAY_SYNTAX);
}
}
return MOD_CONT;
}
/*************************************************************************/
static int do_act(User * u)
{
ChannelInfo *ci;
char *chan = strtok(NULL, " ");
char *text = strtok(NULL, "");
if (!chan || !text)
syntax_error(s_BotServ, u, "ACT", BOT_ACT_SYNTAX);
else if (!(ci = cs_findchan(chan)))
notice_lang(s_BotServ, u, CHAN_X_NOT_REGISTERED, chan);
else if (ci->flags & CI_VERBOTEN)
notice_lang(s_BotServ, u, CHAN_X_FORBIDDEN, chan);
else if (!ci->bi)
notice_help(s_BotServ, u, BOT_NOT_ASSIGNED);
else if (!ci->c || ci->c->usercount < BSMinUsers)
notice_lang(s_BotServ, u, BOT_NOT_ON_CHANNEL, ci->name);
else if (!check_access(u, ci, CA_SAY))
notice_lang(s_BotServ, u, ACCESS_DENIED);
else {
send_cmd(ci->bi->nick, "PRIVMSG %s :%cACTION %s%c", ci->name, 1,
text, 1);
ci->bi->lastmsg = time(NULL);
if (logchan && LogBot)
send_cmd(ci->bi->nick, "PRIVMSG %s :ACT %s %s %s", LogChannel,
u->nick, ci->name, text);
}
return MOD_CONT;
}
/*************************************************************************/