summaryrefslogtreecommitdiff
path: root/src/botserv.c
diff options
context:
space:
mode:
authorrob rob@31f1291d-b8d6-0310-a050-a5561fc1590b <rob rob@31f1291d-b8d6-0310-a050-a5561fc1590b@5417fbe8-f217-4b02-8779-1006273d7864>2004-07-19 14:08:30 +0000
committerrob rob@31f1291d-b8d6-0310-a050-a5561fc1590b <rob rob@31f1291d-b8d6-0310-a050-a5561fc1590b@5417fbe8-f217-4b02-8779-1006273d7864>2004-07-19 14:08:30 +0000
commita1479a87757fb5e9b48f3c8bb5dd9cc247406f1a (patch)
tree06d23a51082a398c512ed8f1e39bdac60d4dd658 /src/botserv.c
parent7fffea77edbe72cd66f7a55f41a3966204f9cf86 (diff)
BUILD : 1.7.4 (264) BUGS : N/A NOTES : Switched to autoconf - try to commit part 1
git-svn-id: svn://svn.anope.org/anope/trunk@264 31f1291d-b8d6-0310-a050-a5561fc1590b git-svn-id: http://anope.svn.sourceforge.net/svnroot/anope/trunk@169 5417fbe8-f217-4b02-8779-1006273d7864
Diffstat (limited to 'src/botserv.c')
-rw-r--r--src/botserv.c2593
1 files changed, 2593 insertions, 0 deletions
diff --git a/src/botserv.c b/src/botserv.c
new file mode 100644
index 000000000..418e029d6
--- /dev/null
+++ b/src/botserv.c
@@ -0,0 +1,2593 @@
+/* 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;
+
+/*************************************************************************/
+
+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);
+char *normalizeBuffer(char *);
+/*************************************************************************/
+/* *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) || defined(IRC_RAGE2)
+ (!(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;
+ char *nbuf;
+ BadWord *bw;
+
+ /* Normalize the buffer */
+ nbuf = normalizeBuffer(buf);
+
+ for (i = 0, bw = ci->badwords; i < ci->bwcount; i++, bw++) {
+ if (!bw->in_use)
+ continue;
+
+ if (bw->type == BW_ANY
+ && ((BSCaseSensitive && strstr(nbuf, bw->word))
+ || (!BSCaseSensitive && stristr(nbuf, bw->word)))) {
+ mustkick = 1;
+ } else if (bw->type == BW_SINGLE) {
+ int len = strlen(bw->word);
+
+ if ((BSCaseSensitive && strstr(nbuf, bw->word))
+ || (!BSCaseSensitive
+ && (!stricmp(nbuf, bw->word)))) {
+ mustkick = 1;
+ /* two next if are quite odd isn't it? =) */
+ } else if ((strchr(nbuf, ' ') == nbuf + len)
+ &&
+ ((BSCaseSensitive
+ && (strstr(nbuf, bw->word) == nbuf))
+ || (!BSCaseSensitive
+ && (stristr(nbuf, bw->word) ==
+ nbuf)))) {
+ mustkick = 1;
+ } else
+ if ((strrchr(nbuf, ' ') ==
+ nbuf + strlen(nbuf) - len - 1)
+ &&
+ ((BSCaseSensitive
+ && (strstr(nbuf, bw->word) ==
+ nbuf + strlen(nbuf) - len))
+ || (!BSCaseSensitive
+ && (stristr(nbuf, bw->word) ==
+ nbuf + strlen(nbuf) - 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 ((BSCaseSensitive && (strstr(nbuf, wordbuf)))
+ || (!BSCaseSensitive
+ && (stristr(nbuf, wordbuf))))
+ mustkick = 1;
+ }
+ } else if (bw->type == BW_START) {
+ int len = strlen(bw->word);
+
+ if ((BSCaseSensitive
+ && (!strncmp(nbuf, bw->word, len)))
+ || (!BSCaseSensitive
+ && (!strnicmp(nbuf, 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 ((BSCaseSensitive && (strstr(nbuf, wordbuf)))
+ || (!BSCaseSensitive
+ && (stristr(nbuf, wordbuf))))
+ mustkick = 1;
+
+ free(wordbuf);
+ }
+ } else if (bw->type == BW_END) {
+ int len = strlen(bw->word);
+
+ if ((BSCaseSensitive
+ &&
+ (!strncmp
+ (nbuf + strlen(nbuf) - len, bw->word, len)))
+ || (!BSCaseSensitive
+ &&
+ (!strnicmp
+ (nbuf + strlen(nbuf) - 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 ((BSCaseSensitive && (strstr(nbuf, wordbuf)))
+ || (!BSCaseSensitive
+ && (stristr(nbuf, 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;
+ }
+ }
+
+ /* Free the normalized buffer */
+ if (nbuf)
+ free(nbuf);
+ }
+
+ /* 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;
+}
+
+/*************************************************************************/
+
+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_mode(ci->bi->nick, ci->name, "%s", 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_mode(ci->bi->nick, ci->c->name, "+ao %s %s", ci->bi->nick,
+ ci->bi->nick);
+#elif defined(IRC_PTLINK)
+ /* PTLinks requieres an IRCop to u-line changes, so use ChanServ */
+ send_mode(s_ChanServ, ci->c->name, "+ao %s %s", ci->bi->nick,
+ ci->bi->nick);
+#else
+ send_mode(ci->bi->nick, ci->c->name, "+o %s", ci->bi->nick);
+#endif
+}
+
+/*************************************************************************/
+
+/