summaryrefslogtreecommitdiff
path: root/src/botserv.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/botserv.c')
-rw-r--r--src/botserv.c1171
1 files changed, 1171 insertions, 0 deletions
diff --git a/src/botserv.c b/src/botserv.c
new file mode 100644
index 000000000..ed5af558d
--- /dev/null
+++ b/src/botserv.c
@@ -0,0 +1,1171 @@
+/* BotServ functions
+ *
+ * (C) 2003-2008 Anope Team
+ * Contact us at info@anope.org
+ *
+ * Please read COPYING and README for further 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"
+
+
+/*************************************************************************/
+
+BotInfo *botlists[256]; /* Hash list of bots */
+int nbots = 0;
+
+/*************************************************************************/
+
+static UserData *get_user_data(Channel * c, User * u);
+
+static void check_ban(ChannelInfo * ci, User * u, int ttbtype);
+static void bot_kick(ChannelInfo * ci, User * u, int message, ...);
+
+E void moduleAddBotServCmds(void);
+
+/*************************************************************************/
+/* *INDENT-OFF* */
+void moduleAddBotServCmds(void) {
+ modules_core_init(BotServCoreNumber, BotServCoreModules);
+}
+/* *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)
+{
+ if (s_BotServ) {
+ moduleAddBotServCmds();
+ }
+}
+
+/*************************************************************************/
+
+/* 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 = "";
+ }
+ anope_cmd_ctcp(s_BotServ, u->nick, "PING %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 = "";
+ }
+ anope_cmd_ctcp(bi->nick, u->nick, "PING %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 || !buf || !ci) {
+ return;
+ }
+
+ /* Answer to ping if needed, without breaking the buffer. */
+ if (!strnicmp(buf, "\1PING", 5)) {
+ anope_cmd_ctcp(ci->bi->nick, u->nick, "PING %s", 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) &&
+ (!(ci->botflags & BS_DONTKICKOPS)
+ || !(cstatus & (CUS_HALFOP | CUS_OP | CUS_OWNER | CUS_PROTECT)))
+
+ && (!(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;
+ int l = 0;
+ char *s = buf;
+
+ do {
+ if (isupper(*s))
+ i++;
+ else if (islower(*s))
+ l++;
+ } while (*s++);
+
+ /* i counts uppercase chars, l counts lowercase chars. Only
+ * alphabetic chars (so islower || isupper) qualify for the
+ * percentage of caps to kick for; the rest is ignored. -GD
+ */
+
+ if (i >= ci->capsmin && i * 100 / (i + l) >= 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 && !strcmp(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
+ && !strcmp(nbuf, bw->word))
+ || (!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;
+ }
+
+ /* free previous (sc)allocated memory (#850) */
+ free(wordbuf);
+ }
+ }
+ } 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);
+
+ /* free the normalized buffer before return (#850) */
+ Anope_Free(nbuf);
+
+ return;
+ }
+ }
+
+ /* Free the normalized buffer */
+ Anope_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 == *BSFantasyCharacter) {
+ cmd = strtok(buf, " ");
+
+ if (cmd && (cmd[0] == *BSFantasyCharacter)) {
+ char *params = strtok(NULL, "");
+ char *event_name = EVENT_BOT_FANTASY_NO_ACCESS;
+
+ /* Strip off the fantasy character */
+ cmd++;
+
+ if (check_access(u, ci, CA_FANTASIA))
+ event_name = EVENT_BOT_FANTASY;
+
+ if (params)
+ send_event(event_name, 4, cmd, u->nick, ci->name, params);
+ else
+ send_event(event_name, 3, cmd, u->nick, ci->name);
+ }
+ }
+}
+
+/*************************************************************************/
+
+/* 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;
+ uint16 tmp16;
+ uint32 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) { \
+ anope_cmd_global(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;
+
+ if (rdb_tag_table("anope_bs_core") == 0) {
+ alog("Unable to tag table 'anope_bs_core' - BotServ RDB save failed.");
+ rdb_close();
+ return;
+ }
+
+ for (i = 0; i < 256; i++) {
+ for (bi = botlists[i]; bi; bi = bi->next) {
+ if (rdb_save_bs_core(bi) == 0) {
+ alog("Unable to save BotInfo for %s - BotServ RDB save failed.", bi->nick);
+ rdb_close();
+ return;
+ }
+ }
+ }
+
+ if (rdb_clean_table("anope_bs_core") == 0)
+ alog("Unable to clean table 'anope_bs_core' - BotServ RDB save failed.");
+
+ rdb_close();
+#endif
+}
+
+/*************************************************************************/
+
+/* Inserts a bot in the bot list. I can't be much explicit mh? */
+
+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;
+
+ if (!nick) {
+ if (debug) {
+ alog("debug: makebot called with NULL values");
+ }
+ return NULL;
+ }
+
+ bi = scalloc(sizeof(BotInfo), 1);
+ bi->nick = sstrdup(nick);
+ bi->lastmsg = time(NULL);
+ insert_bot(bi);
+ nbots++;
+ return bi;
+}
+
+/*************************************************************************/
+
+
+/*************************************************************************/
+
+
+/*************************************************************************/
+
+BotInfo *findbot(char *nick)
+{
+ BotInfo *bi;
+ Uid *ud;
+
+ /* to keep make strict happy */
+ ud = NULL;
+
+ if (!nick || !*nick)
+ return NULL;
+
+ for (bi = botlists[tolower(*nick)]; bi; bi = bi->next) {
+ if (UseTS6 && ircd->ts6) {
+ ud = find_nickuid(nick);
+ }
+ if (!stricmp(nick, bi->nick)) {
+ return bi;
+ }
+ if (ud && UseTS6 && ircd->ts6) {
+ if (!stricmp(ud->nick, bi->nick)) {
+ return bi;
+ }
+ }
+ }
+
+ return NULL;
+}
+
+/*************************************************************************/
+
+/* Unassign a bot from a channel. Assumes u, ci and ci->bi are not NULL */
+
+void unassign(User * u, ChannelInfo * ci)
+{
+ send_event(EVENT_BOT_UNASSIGN, 2, ci->name, ci->bi->nick);
+
+ if (ci->c && ci->c->usercount >= BSMinUsers) {
+ anope_cmd_part(ci->bi->nick, ci->name, "UNASSIGN from %s",
+ 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,
+ common_get_vhost(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)
+{
+ if (!ci || !ci->c || !ci->bi)
+ return;
+
+ if (BSSmartJoin) {
+ /* We check for bans */
+ if (ci->c->bans && ci->c->bans->count) {
+ char buf[BUFSIZE];
+ char *av[4];
+ Entry *ban, *next;
+ int ac;
+
+ if (ircdcap->tsmode) {
+ snprintf(buf, BUFSIZE - 1, "%ld", (long int) time(NULL));
+ av[0] = ci->c->name;
+ av[1] = buf;
+ av[2] = sstrdup("-b");
+ ac = 4;
+ } else {
+ av[0] = ci->c->name;
+ av[1] = sstrdup("-b");
+ ac = 3;
+ }
+
+ for (ban = ci->c->bans->entries; ban; ban = next) {
+ next = ban->next;
+ if (entry_match
+ (ban, ci->bi->nick, ci->bi->user, ci->bi->host, 0)) {
+ anope_cmd_mode(whosends(ci), ci->name, "-b %s",
+ ban->mask);
+ if (ircdcap->tsmode)
+ av[3] = ban->mask;
+ else
+ av[2] = ban->mask;
+
+ do_cmode(whosends(ci), ac, av);
+ }
+ }
+
+ if (ircdcap->tsmode)
+ free(av[2]);
+ else
+ free(av[1]);
+ }
+
+ /* Should we be invited? */
+ if ((ci->c->mode & anope_get_invite_mode())
+ || (ci->c->limit && ci->c->usercount >= ci->c->limit))
+ anope_cmd_notice_ops(NULL, ci->c->name,
+ "%s invited %s into the channel.",
+ ci->bi->nick, ci->bi->nick);
+ }
+ anope_cmd_join(ci->bi->nick, ci->c->name, ci->c->creation_time);
+ anope_cmd_bot_chan_mode(ci->bi->nick, ci->c->name);
+ send_event(EVENT_BOT_JOIN, 2, ci->name, ci->bi->nick);
+}
+
+/*************************************************************************/
+
+/* 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[4];
+ int ac;
+ char mask[BUFSIZE];
+ char buf[BUFSIZE];
+
+ bd->ttb[ttbtype] = 0;
+
+ get_idealban(ci, u, mask, sizeof(mask));
+
+ if (ircdcap->tsmode) {
+ snprintf(buf, BUFSIZE - 1, "%ld", (long int) time(NULL));
+ av[0] = ci->name;
+ av[1] = buf;
+ av[2] = sstrdup("+b");
+ av[3] = mask;
+ ac = 4;
+ } else {
+ av[0] = ci->name;
+ av[1] = sstrdup("+b");
+ av[2] = mask;
+ ac = 3;
+ }
+
+ anope_cmd_mode(ci->bi->nick, ci->name, "+b %s", mask);
+ do_cmode(ci->bi->nick, ac, av);
+ send_event(EVENT_BOT_BAN, 3, u->nick, ci->name, mask);
+ if (ircdcap->tsmode)
+ free(av[2]);
+ else
+ 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);
+ va_end(args);
+
+ av[0] = ci->name;
+ av[1] = u->nick;
+ av[2] = buf;
+ anope_cmd_kick(ci->bi->nick, av[0], av[1], "%s", av[2]);
+ do_kick(ci->bi->nick, 3, av);
+ send_event(EVENT_BOT_KICK, 3, u->nick, ci->name, buf);
+}
+
+/*************************************************************************/
+
+/* Makes a simple ban and kicks the target */
+
+void bot_raw_ban(User * requester, ChannelInfo * ci, char *nick,
+ char *reason)
+{
+ int ac;
+ char *av[4];
+ char mask[BUFSIZE];
+ char buf[BUFSIZE];
+ User *u = finduser(nick);
+
+ if (!u)
+ return;
+
+ if (ircd->protectedumode) {
+ if (is_protected(u) && (requester != u)) {
+ anope_cmd_privmsg(ci->bi->nick, ci->name, "%s",
+ getstring2(NULL, PERMISSION_DENIED));
+ return;
+ }
+ }
+
+ if ((ci->flags & CI_PEACE) && stricmp(requester->nick, nick)
+ && (get_access(u, ci) >= get_access(requester, ci)))
+ return;
+
+ if (ircd->except) {
+ if (is_excepted(ci, u) == 1) {
+ anope_cmd_privmsg(ci->bi->nick, ci->name, "%s",
+ getstring2(NULL, BOT_EXCEPT));
+ return;
+ }
+ }
+
+ get_idealban(ci, u, mask, sizeof(mask));
+
+ if (ircdcap->tsmode) {
+ snprintf(buf, BUFSIZE - 1, "%ld", (long int) time(NULL));
+ av[0] = ci->name;
+ av[1] = buf;
+ av[2] = sstrdup("+b");
+ av[3] = mask;
+ ac = 4;
+ } else {
+ av[0] = ci->name;
+ av[1] = sstrdup("+b");
+ av[2] = mask;
+ ac = 3;
+ }
+
+ anope_cmd_mode(ci->bi->nick, ci->name, "+b %s", mask);
+ do_cmode(ci->bi->nick, ac, av);
+
+ /* We need to free our sstrdup'd "+b" -GD */
+ if (ircdcap->tsmode)
+ free(av[2]);
+ else
+ 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;
+ }
+
+ /* Check if we need to do a signkick or not -GD */
+ if ((ci->flags & CI_SIGNKICK)
+ || ((ci->flags & CI_SIGNKICK_LEVEL)
+ && !check_access(requester, ci, CA_SIGNKICK)))
+ anope_cmd_kick(ci->bi->nick, av[0], av[1], "%s (%s)", av[2],
+ requester->nick);
+ else
+ anope_cmd_kick(ci->bi->nick, av[0], av[1], "%s", av[2]);
+
+ do_kick(ci->bi->nick, 3, av);
+ send_event(EVENT_BOT_KICK, 3, av[1], av[0], av[2]);
+}
+
+/*************************************************************************/
+
+/* Makes a kick with a "dynamic" reason ;) */
+
+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 (ircd->protectedumode) {
+ if (is_protected(u) && (requester != u)) {
+ anope_cmd_privmsg(ci->bi->nick, ci->name, "%s",
+ getstring2(NULL, PERMISSION_DENIED));
+ return;
+ }
+ }
+
+ 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;
+ }
+
+ if ((ci->flags & CI_SIGNKICK)
+ || ((ci->flags & CI_SIGNKICK_LEVEL)
+ && !check_access(requester, ci, CA_SIGNKICK)))
+ anope_cmd_kick(ci->bi->nick, av[0], av[1], "%s (%s)", av[2],
+ requester->nick);
+ else
+ anope_cmd_kick(ci->bi->nick, av[0], av[1], "%s", av[2]);
+ do_kick(ci->bi->nick, 3, av);
+ send_event(EVENT_BOT_KICK, 3, av[1], av[0], av[2]);
+}
+
+/*************************************************************************/
+
+/* Makes a mode operation on a channel for a nick */
+
+void bot_raw_mode(User * requester, ChannelInfo * ci, char *mode,
+ char *nick)
+{
+ char *av[4];
+ int ac;
+ char buf[BUFSIZE];
+ User *u;
+
+ *buf = '\0';
+ u = finduser(nick);
+
+ if (!u || !is_on_chan(ci->c, u))
+ return;
+
+ snprintf(buf, BUFSIZE - 1, "%ld", (long int) time(NULL));
+
+ if (ircd->protectedumode) {
+ if (is_protected(u) && *mode == '-' && (requester != u)) {
+ anope_cmd_privmsg(ci->bi->nick, ci->name, "%s",
+ getstring2(NULL, PERMISSION_DENIED));
+ return;
+ }
+ }
+
+ if (*mode == '-' && (ci->flags & CI_PEACE)
+ && stricmp(requester->nick, nick)
+ && (get_access(u, ci) >= get_access(requester, ci)))
+ return;
+
+ if (ircdcap->tsmode) {
+ av[0] = ci->name;
+ av[1] = buf;
+ av[2] = mode;
+ av[3] = nick;
+ ac = 4;
+ anope_cmd_mode(ci->bi->nick, av[0], "%s %s", av[2], av[3]);
+ } else {
+ av[0] = ci->name;
+ av[1] = mode;
+ av[2] = nick;
+ ac = 3;
+ anope_cmd_mode(ci->bi->nick, av[0], "%s %s", av[1], av[2]);
+ }
+
+ do_cmode(ci->bi->nick, ac, av);
+}
+
+/*************************************************************************/
+/**
+ * Normalize buffer stripping control characters and colors
+ * @param A string to be parsed for control and color codes
+ * @return A string stripped of control and color codes
+ */
+char *normalizeBuffer(char *buf)
+{
+ char *newbuf;
+ int i, len, j = 0;
+
+ len = strlen(buf);
+ newbuf = (char *) smalloc(sizeof(char) * len + 1);
+
+ for (i = 0; i < len; i++) {
+ switch (buf[i]) {
+ /* ctrl char */
+ case 1:
+ break;
+ /* Bold ctrl char */
+ case 2:
+ break;
+ /* Color ctrl char */
+ case 3:
+ /* If the next character is a digit, its also removed */
+ if (isdigit(buf[i + 1])) {
+ i++;
+
+ /* not the best way to remove colors
+ * which are two digit but no worse then
+ * how the Unreal does with +S - TSL
+ */
+ if (isdigit(buf[i + 1])) {
+ i++;
+ }
+
+ /* Check for background color code
+ * and remove it as well
+ */
+ if (buf[i + 1] == ',') {
+ i++;
+
+ if (isdigit(buf[i + 1])) {
+ i++;
+ }
+ /* not the best way to remove colors
+ * which are two digit but no worse then
+ * how the Unreal does with +S - TSL
+ */
+ if (isdigit(buf[i + 1])) {
+ i++;
+ }
+ }
+ }
+
+ break;
+ /* line feed char */
+ case 10:
+ break;
+ /* carriage returns char */
+ case 13:
+ break;
+ /* Reverse ctrl char */
+ case 22:
+ break;
+ /* Underline ctrl char */
+ case 31:
+ break;
+ /* A valid char gets copied into the new buffer */
+ default:
+ newbuf[j] = buf[i];
+ j++;
+ }
+ }
+
+ /* Terminate the string */
+ newbuf[j] = 0;
+
+ return (newbuf);
+}