botserv.c

Go to the documentation of this file.
00001 /* BotServ functions
00002  *
00003  * (C) 2003-2007 Anope Team
00004  * Contact us at info@anope.org
00005  *
00006  * Please read COPYING and README for further details.
00007  *
00008  * Based on the original code of Epona by Lara.
00009  * Based on the original code of Services by Andy Church.
00010  * 
00011  * $Id: botserv.c 1270 2007-08-27 03:23:06Z drstein $
00012  *
00013  */
00014 
00015 /*************************************************************************/
00016 
00017 #include "services.h"
00018 #include "pseudo.h"
00019 
00020 
00021 /*************************************************************************/
00022 
00023 BotInfo *botlists[256];         /* Hash list of bots */
00024 int nbots = 0;
00025 
00026 /*************************************************************************/
00027 
00028 static UserData *get_user_data(Channel * c, User * u);
00029 
00030 static void check_ban(ChannelInfo * ci, User * u, int ttbtype);
00031 static void bot_kick(ChannelInfo * ci, User * u, int message, ...);
00032 
00033 E void moduleAddBotServCmds(void);
00034 
00035 /*************************************************************************/
00036 /* *INDENT-OFF* */
00037 void moduleAddBotServCmds(void) {
00038     modules_core_init(BotServCoreNumber, BotServCoreModules);
00039 }
00040 /* *INDENT-ON* */
00041 /*************************************************************************/
00042 /*************************************************************************/
00043 
00044 /* Return information on memory use.  Assumes pointers are valid. */
00045 
00046 void get_botserv_stats(long *nrec, long *memuse)
00047 {
00048     long count = 0, mem = 0;
00049     int i;
00050     BotInfo *bi;
00051 
00052     for (i = 0; i < 256; i++) {
00053         for (bi = botlists[i]; bi; bi = bi->next) {
00054             count++;
00055             mem += sizeof(*bi);
00056             mem += strlen(bi->nick) + 1;
00057             mem += strlen(bi->user) + 1;
00058             mem += strlen(bi->host) + 1;
00059             mem += strlen(bi->real) + 1;
00060         }
00061     }
00062 
00063     *nrec = count;
00064     *memuse = mem;
00065 }
00066 
00067 /*************************************************************************/
00068 /*************************************************************************/
00069 
00070 /* BotServ initialization. */
00071 
00072 void bs_init(void)
00073 {
00074     if (s_BotServ) {
00075         moduleAddBotServCmds();
00076     }
00077 }
00078 
00079 /*************************************************************************/
00080 
00081 /* Main BotServ routine. */
00082 
00083 void botserv(User * u, char *buf)
00084 {
00085     char *cmd, *s;
00086 
00087     cmd = strtok(buf, " ");
00088 
00089     if (!cmd) {
00090         return;
00091     } else if (stricmp(cmd, "\1PING") == 0) {
00092         if (!(s = strtok(NULL, ""))) {
00093             s = "";
00094         }
00095         anope_cmd_ctcp(s_BotServ, u->nick, "PING %s", s);
00096     } else if (skeleton) {
00097         notice_lang(s_BotServ, u, SERVICE_OFFLINE, s_BotServ);
00098     } else {
00099         mod_run_cmd(s_BotServ, u, BOTSERV, cmd);
00100     }
00101 
00102 }
00103 
00104 /*************************************************************************/
00105 
00106 /* Handles all messages sent to bots. (Currently only answers to pings ;) */
00107 
00108 void botmsgs(User * u, BotInfo * bi, char *buf)
00109 {
00110     char *cmd = strtok(buf, " ");
00111     char *s;
00112 
00113     if (!cmd || !u)
00114         return;
00115 
00116     if (!stricmp(cmd, "\1PING")) {
00117         if (!(s = strtok(NULL, ""))) {
00118             s = "";
00119         }
00120         anope_cmd_ctcp(bi->nick, u->nick, "PING %s", s);
00121     }
00122 }
00123 
00124 /*************************************************************************/
00125 
00126 /* Handles all messages that are sent to registered channels where a
00127  * bot is on.
00128  *
00129  */
00130 
00131 void botchanmsgs(User * u, ChannelInfo * ci, char *buf)
00132 {
00133     int c;
00134     int16 cstatus = 0;
00135     char *cmd;
00136     UserData *ud;
00137 
00138     if (!u || !buf || !ci) {
00139         return;
00140     }
00141 
00142     /* Answer to ping if needed, without breaking the buffer. */
00143     if (!strnicmp(buf, "\1PING", 5)) {
00144         anope_cmd_ctcp(ci->bi->nick, u->nick, "PING %s", buf);
00145     }
00146 
00147     /* If it's a /me, cut the CTCP part at the beginning (not
00148      * at the end, because one character just doesn't matter,
00149      * but the ACTION may create strange behaviours with the
00150      * caps or badwords kickers */
00151     if (!strnicmp(buf, "\1ACTION ", 8))
00152         buf += 8;
00153 
00154     /* Now we can make kicker stuff. We try to order the checks
00155      * from the fastest one to the slowest one, since there's
00156      * no need to process other kickers if an user is kicked before
00157      * the last kicker check.
00158      *
00159      * But FIRST we check whether the user is protected in any
00160      * way.
00161      */
00162 
00163     /* We first retrieve the user status on the channel if needed */
00164     if (ci->botflags & (BS_DONTKICKOPS | BS_DONTKICKVOICES))
00165         cstatus = chan_get_user_status(ci->c, u);
00166 
00167     if (buf && !check_access(u, ci, CA_NOKICK) &&
00168         (!(ci->botflags & BS_DONTKICKOPS)
00169          || !(cstatus & (CUS_HALFOP | CUS_OP | CUS_OWNER | CUS_PROTECT)))
00170 
00171         && (!(ci->botflags & BS_DONTKICKVOICES) || !(cstatus & CUS_VOICE))) {
00172         /* Bolds kicker */
00173         if ((ci->botflags & BS_KICK_BOLDS) && strchr(buf, 2)) {
00174             check_ban(ci, u, TTB_BOLDS);
00175             bot_kick(ci, u, BOT_REASON_BOLD);
00176             return;
00177         }
00178 
00179         /* Color kicker */
00180         if ((ci->botflags & BS_KICK_COLORS) && strchr(buf, 3)) {
00181             check_ban(ci, u, TTB_COLORS);
00182             bot_kick(ci, u, BOT_REASON_COLOR);
00183             return;
00184         }
00185 
00186         /* Reverses kicker */
00187         if ((ci->botflags & BS_KICK_REVERSES) && strchr(buf, 22)) {
00188             check_ban(ci, u, TTB_REVERSES);
00189             bot_kick(ci, u, BOT_REASON_REVERSE);
00190             return;
00191         }
00192 
00193         /* Underlines kicker */
00194         if ((ci->botflags & BS_KICK_UNDERLINES) && strchr(buf, 31)) {
00195             check_ban(ci, u, TTB_UNDERLINES);
00196             bot_kick(ci, u, BOT_REASON_UNDERLINE);
00197             return;
00198         }
00199 
00200         /* Caps kicker */
00201         if ((ci->botflags & BS_KICK_CAPS)
00202             && ((c = strlen(buf)) >= ci->capsmin)) {
00203             int i = 0;
00204             int l = 0;
00205             char *s = buf;
00206 
00207             do {
00208                 if (isupper(*s))
00209                     i++;
00210                 else if (islower(*s))
00211                     l++;
00212             } while (*s++);
00213 
00214             /* i counts uppercase chars, l counts lowercase chars. Only
00215              * alphabetic chars (so islower || isupper) qualify for the
00216              * percentage of caps to kick for; the rest is ignored. -GD
00217              */
00218 
00219             if (i >= ci->capsmin && i * 100 / (i + l) >= ci->capspercent) {
00220                 check_ban(ci, u, TTB_CAPS);
00221                 bot_kick(ci, u, BOT_REASON_CAPS);
00222                 return;
00223             }
00224         }
00225 
00226         /* Bad words kicker */
00227         if (ci->botflags & BS_KICK_BADWORDS) {
00228             int i;
00229             int mustkick = 0;
00230             char *nbuf;
00231             BadWord *bw;
00232 
00233             /* Normalize the buffer */
00234             nbuf = normalizeBuffer(buf);
00235 
00236             for (i = 0, bw = ci->badwords; i < ci->bwcount; i++, bw++) {
00237                 if (!bw->in_use)
00238                     continue;
00239 
00240                 if (bw->type == BW_ANY
00241                     && ((BSCaseSensitive && strstr(nbuf, bw->word))
00242                         || (!BSCaseSensitive && stristr(nbuf, bw->word)))) {
00243                     mustkick = 1;
00244                 } else if (bw->type == BW_SINGLE) {
00245                     int len = strlen(bw->word);
00246 
00247                     if ((BSCaseSensitive && !strcmp(nbuf, bw->word))
00248                         || (!BSCaseSensitive
00249                             && (!stricmp(nbuf, bw->word)))) {
00250                         mustkick = 1;
00251                         /* two next if are quite odd isn't it? =) */
00252                     } else if ((strchr(nbuf, ' ') == nbuf + len)
00253                                &&
00254                                ((BSCaseSensitive
00255                                  && !strcmp(nbuf, bw->word))
00256                                 || (!BSCaseSensitive
00257                                     && (stristr(nbuf, bw->word) ==
00258                                         nbuf)))) {
00259                         mustkick = 1;
00260                     } else {
00261                         if ((strrchr(nbuf, ' ') ==
00262                              nbuf + strlen(nbuf) - len - 1)
00263                             &&
00264                             ((BSCaseSensitive
00265                               && (strstr(nbuf, bw->word) ==
00266                                   nbuf + strlen(nbuf) - len))
00267                              || (!BSCaseSensitive
00268                                  && (stristr(nbuf, bw->word) ==
00269                                      nbuf + strlen(nbuf) - len)))) {
00270                             mustkick = 1;
00271                         } else {
00272                             char *wordbuf = scalloc(len + 3, 1);
00273 
00274                             wordbuf[0] = ' ';
00275                             wordbuf[len + 1] = ' ';
00276                             wordbuf[len + 2] = '\0';
00277                             memcpy(wordbuf + 1, bw->word, len);
00278 
00279                             if ((BSCaseSensitive
00280                                  && (strstr(nbuf, wordbuf)))
00281                                 || (!BSCaseSensitive
00282                                     && (stristr(nbuf, wordbuf)))) {
00283                                 mustkick = 1;
00284                             }
00285                         }
00286                     }
00287                 } else if (bw->type == BW_START) {
00288                     int len = strlen(bw->word);
00289 
00290                     if ((BSCaseSensitive
00291                          && (!strncmp(nbuf, bw->word, len)))
00292                         || (!BSCaseSensitive
00293                             && (!strnicmp(nbuf, bw->word, len)))) {
00294                         mustkick = 1;
00295                     } else {
00296                         char *wordbuf = scalloc(len + 2, 1);
00297 
00298                         memcpy(wordbuf + 1, bw->word, len);
00299                         wordbuf[0] = ' ';
00300                         wordbuf[len + 1] = '\0';
00301 
00302                         if ((BSCaseSensitive && (strstr(nbuf, wordbuf)))
00303                             || (!BSCaseSensitive
00304                                 && (stristr(nbuf, wordbuf))))
00305                             mustkick = 1;
00306 
00307                         free(wordbuf);
00308                     }
00309                 } else if (bw->type == BW_END) {
00310                     int len = strlen(bw->word);
00311 
00312                     if ((BSCaseSensitive
00313                          &&
00314                          (!strncmp
00315                           (nbuf + strlen(nbuf) - len, bw->word, len)))
00316                         || (!BSCaseSensitive
00317                             &&
00318                             (!strnicmp
00319                              (nbuf + strlen(nbuf) - len, bw->word,
00320                               len)))) {
00321                         mustkick = 1;
00322                     } else {
00323                         char *wordbuf = scalloc(len + 2, 1);
00324 
00325                         memcpy(wordbuf, bw->word, len);
00326                         wordbuf[len] = ' ';
00327                         wordbuf[len + 1] = '\0';
00328 
00329                         if ((BSCaseSensitive && (strstr(nbuf, wordbuf)))
00330                             || (!BSCaseSensitive
00331                                 && (stristr(nbuf, wordbuf))))
00332                             mustkick = 1;
00333 
00334                         free(wordbuf);
00335                     }
00336                 }
00337 
00338                 if (mustkick) {
00339                     check_ban(ci, u, TTB_BADWORDS);
00340                     if (BSGentleBWReason)
00341                         bot_kick(ci, u, BOT_REASON_BADWORD_GENTLE);
00342                     else
00343                         bot_kick(ci, u, BOT_REASON_BADWORD, bw->word);
00344                     return;
00345                 }
00346             }
00347 
00348             /* Free the normalized buffer */
00349             if (nbuf)
00350                 free(nbuf);
00351         }
00352 
00353         /* Flood kicker */
00354         if (ci->botflags & BS_KICK_FLOOD) {
00355             time_t now = time(NULL);
00356 
00357             ud = get_user_data(ci->c, u);
00358             if (!ud) {
00359                 return;
00360             }
00361 
00362             if (now - ud->last_start > ci->floodsecs) {
00363                 ud->last_start = time(NULL);
00364                 ud->lines = 0;
00365             }
00366 
00367             ud->lines++;
00368             if (ud->lines >= ci->floodlines) {
00369                 check_ban(ci, u, TTB_FLOOD);
00370                 bot_kick(ci, u, BOT_REASON_FLOOD);
00371                 return;
00372             }
00373         }
00374 
00375         /* Repeat kicker */
00376         if (ci->botflags & BS_KICK_REPEAT) {
00377             ud = get_user_data(ci->c, u);
00378             if (!ud) {
00379                 return;
00380             }
00381             if (ud->lastline && stricmp(ud->lastline, buf)) {
00382                 free(ud->lastline);
00383                 ud->lastline = sstrdup(buf);
00384                 ud->times = 0;
00385             } else {
00386                 if (!ud->lastline)
00387                     ud->lastline = sstrdup(buf);
00388                 ud->times++;
00389             }
00390 
00391             if (ud->times >= ci->repeattimes) {
00392                 check_ban(ci, u, TTB_REPEAT);
00393                 bot_kick(ci, u, BOT_REASON_REPEAT);
00394                 return;
00395             }
00396         }
00397     }
00398 
00399 
00400     /* return if the user is on the ignore list  */
00401     if (get_ignore(u->nick) != NULL) {
00402         return;
00403     }
00404 
00405     /* Fantaisist commands */
00406 
00407     if (buf && (ci->botflags & BS_FANTASY) && *buf == *BSFantasyCharacter) {
00408         cmd = strtok(buf, " ");
00409 
00410         if (cmd && (cmd[0] == *BSFantasyCharacter)) {
00411             char *params = strtok(NULL, "");
00412             char *event_name = EVENT_BOT_FANTASY_NO_ACCESS;
00413 
00414             /* Strip off the fantasy character */
00415             cmd++;
00416 
00417             if (check_access(u, ci, CA_FANTASIA))
00418                 event_name = EVENT_BOT_FANTASY;
00419 
00420             if (params)
00421                 send_event(event_name, 4, cmd, u->nick, ci->name, params);
00422             else
00423                 send_event(event_name, 3, cmd, u->nick, ci->name);
00424         }
00425     }
00426 }
00427 
00428 /*************************************************************************/
00429 
00430 /* Load/save data files. */
00431 
00432 
00433 #define SAFE(x) do {                    \
00434     if ((x) < 0) {                  \
00435     if (!forceload)                 \
00436         fatal("Read error on %s", BotDBName);   \
00437     failed = 1;                 \
00438     break;                      \
00439     }                           \
00440 } while (0)
00441 
00442 void load_bs_dbase(void)
00443 {
00444     dbFILE *f;
00445     int c, ver;
00446     uint16 tmp16;
00447     uint32 tmp32;
00448     BotInfo *bi;
00449     int failed = 0;
00450 
00451     if (!(f = open_db(s_BotServ, BotDBName, "r", BOT_VERSION)))
00452         return;
00453 
00454     ver = get_file_version(f);
00455 
00456     while (!failed && (c = getc_db(f)) != 0) {
00457         char *s;
00458 
00459         if (c != 1)
00460             fatal("Invalid format in %s %d", BotDBName, c);
00461 
00462         SAFE(read_string(&s, f));
00463         bi = makebot(s);
00464         free(s);
00465         SAFE(read_string(&bi->user, f));
00466         SAFE(read_string(&bi->host, f));
00467         SAFE(read_string(&bi->real, f));
00468         if (ver >= 10) {
00469             SAFE(read_int16(&tmp16, f));
00470             bi->flags = tmp16;
00471         }
00472         SAFE(read_int32(&tmp32, f));
00473         bi->created = tmp32;
00474         SAFE(read_int16(&tmp16, f));
00475         bi->chancount = tmp16;
00476     }
00477 
00478     close_db(f);
00479 }
00480 
00481 #undef SAFE
00482 
00483 /*************************************************************************/
00484 
00485 #define SAFE(x) do {                        \
00486     if ((x) < 0) {                      \
00487     restore_db(f);                      \
00488     log_perror("Write error on %s", BotDBName);     \
00489     if (time(NULL) - lastwarn > WarningTimeout) {       \
00490         anope_cmd_global(NULL, "Write error on %s: %s", BotDBName,  \
00491             strerror(errno));           \
00492         lastwarn = time(NULL);              \
00493     }                           \
00494     return;                         \
00495     }                               \
00496 } while (0)
00497 
00498 void save_bs_dbase(void)
00499 {
00500     dbFILE *f;
00501     BotInfo *bi;
00502     static time_t lastwarn = 0;
00503     int i;
00504 
00505     if (!(f = open_db(s_BotServ, BotDBName, "w", BOT_VERSION)))
00506         return;
00507 
00508     for (i = 0; i < 256; i++) {
00509         for (bi = botlists[i]; bi; bi = bi->next) {
00510             SAFE(write_int8(1, f));
00511             SAFE(write_string(bi->nick, f));
00512             SAFE(write_string(bi->user, f));
00513             SAFE(write_string(bi->host, f));
00514             SAFE(write_string(bi->real, f));
00515             SAFE(write_int16(bi->flags, f));
00516             SAFE(write_int32(bi->created, f));
00517             SAFE(write_int16(bi->chancount, f));
00518         }
00519     }
00520     SAFE(write_int8(0, f));
00521 
00522     close_db(f);
00523 
00524 }
00525 
00526 #undef SAFE
00527 
00528 /*************************************************************************/
00529 
00530 void save_bs_rdb_dbase(void)
00531 {
00532 #ifdef USE_RDB
00533     int i;
00534     BotInfo *bi;
00535 
00536     if (!rdb_open())
00537         return;
00538 
00539     if (rdb_tag_table("anope_bs_core") == 0) {
00540         alog("Unable to tag table 'anope_bs_core' - BotServ RDB save failed.");
00541         return;
00542     }
00543 
00544     for (i = 0; i < 256; i++) {
00545         for (bi = botlists[i]; bi; bi = bi->next) {
00546             if (rdb_save_bs_core(bi) == 0) {
00547                 alog("Unable to save BotInfo for %s - BotServ RDB save failed.", bi->nick);
00548                 return;
00549             }
00550         }
00551     }
00552 
00553     if (rdb_clean_table("anope_bs_core") == 0) {
00554         alog("Unable to clean table 'anope_bs_core' - BotServ RDB save failed.");
00555         return;
00556     }
00557 
00558     rdb_close();
00559 #endif
00560 }
00561 
00562 /*************************************************************************/
00563 
00564 /* Inserts a bot in the bot list. I can't be much explicit mh? */
00565 
00566 void insert_bot(BotInfo * bi)
00567 {
00568     BotInfo *ptr, *prev;
00569 
00570     for (prev = NULL, ptr = botlists[tolower(*bi->nick)];
00571          ptr != NULL && stricmp(ptr->nick, bi->nick) < 0;
00572          prev = ptr, ptr = ptr->next);
00573     bi->prev = prev;
00574     bi->next = ptr;
00575     if (!prev)
00576         botlists[tolower(*bi->nick)] = bi;
00577     else
00578         prev->next = bi;
00579     if (ptr)
00580         ptr->prev = bi;
00581 }
00582 
00583 /*************************************************************************/
00584 
00585 BotInfo *makebot(char *nick)
00586 {
00587     BotInfo *bi;
00588 
00589     if (!nick) {
00590         if (debug) {
00591             alog("debug: makebot called with NULL values");
00592         }
00593         return NULL;
00594     }
00595 
00596     bi = scalloc(sizeof(BotInfo), 1);
00597     bi->nick = sstrdup(nick);
00598     bi->lastmsg = time(NULL);
00599     insert_bot(bi);
00600     nbots++;
00601     return bi;
00602 }
00603 
00604 /*************************************************************************/
00605 
00606 
00607 /*************************************************************************/
00608 
00609 
00610 /*************************************************************************/
00611 
00612 BotInfo *findbot(char *nick)
00613 {
00614     BotInfo *bi;
00615     Uid *ud;
00616 
00617     /* to keep make strict happy */
00618     ud = NULL;
00619 
00620     if (!nick || !*nick)
00621         return NULL;
00622 
00623     for (bi = botlists[tolower(*nick)]; bi; bi = bi->next) {
00624         if (UseTS6 && ircd->ts6) {
00625             ud = find_nickuid(nick);
00626         }
00627         if (!stricmp(nick, bi->nick)) {
00628             return bi;
00629         }
00630         if (ud && UseTS6 && ircd->ts6) {
00631             if (!stricmp(ud->nick, bi->nick)) {
00632                 return bi;
00633             }
00634         }
00635     }
00636 
00637     return NULL;
00638 }
00639 
00640 /*************************************************************************/
00641 
00642 /* Unassign a bot from a channel. Assumes u, ci and ci->bi are not NULL */
00643 
00644 void unassign(User * u, ChannelInfo * ci)
00645 {
00646     send_event(EVENT_BOT_UNASSIGN, 2, ci->name, ci->bi->nick);
00647 
00648     if (ci->c && ci->c->usercount >= BSMinUsers) {
00649         anope_cmd_part(ci->bi->nick, ci->name, "UNASSIGN from %s",
00650                        u->nick);
00651     }
00652     ci->bi->chancount--;
00653     ci->bi = NULL;
00654 }
00655 
00656 /*************************************************************************/
00657 
00658 /* Returns ban data associated with an user if it exists, allocates it
00659    otherwise. */
00660 
00661 static BanData *get_ban_data(Channel * c, User * u)
00662 {
00663     char mask[BUFSIZE];
00664     BanData *bd, *next;
00665     time_t now = time(NULL);
00666 
00667     if (!c || !u)
00668         return NULL;
00669 
00670     snprintf(mask, sizeof(mask), "%s@%s", u->username,
00671              common_get_vhost(u));
00672 
00673     for (bd = c->bd; bd; bd = next) {
00674         if (now - bd->last_use > BSKeepData) {
00675             if (bd->next)
00676                 bd->next->prev = bd->prev;
00677             if (bd->prev)
00678                 bd->prev->next = bd->next;
00679             else
00680                 c->bd = bd->next;
00681             if (bd->mask)
00682                 free(bd->mask);
00683             next = bd->next;
00684             free(bd);
00685             continue;
00686         }
00687         if (!stricmp(bd->mask, mask)) {
00688             bd->last_use = now;
00689             return bd;
00690         }
00691         next = bd->next;
00692     }
00693 
00694     /* If we fall here it is that we haven't found the record */
00695     bd = scalloc(sizeof(BanData), 1);
00696     bd->mask = sstrdup(mask);
00697     bd->last_use = now;
00698 
00699     bd->prev = NULL;
00700     bd->next = c->bd;
00701     if (bd->next)
00702         bd->next->prev = bd;
00703     c->bd = bd;
00704 
00705     return bd;
00706 }
00707 
00708 /*************************************************************************/
00709 
00710 /* Returns BotServ data associated with an user on a given channel.
00711  * Allocates it if necessary.
00712  */
00713 
00714 static UserData *get_user_data(Channel * c, User * u)
00715 {
00716     struct c_userlist *user;
00717 
00718     if (!c || !u)
00719         return NULL;
00720 
00721     for (user = c->users; user; user = user->next) {
00722         if (user->user == u) {
00723             if (user->ud) {
00724                 time_t now = time(NULL);
00725 
00726                 /* Checks whether data is obsolete */
00727                 if (now - user->ud->last_use > BSKeepData) {
00728                     if (user->ud->lastline)
00729                         free(user->ud->lastline);
00730                     /* We should not free and realloc, but reset to 0
00731                        instead. */
00732                     memset(user->ud, 0, sizeof(UserData));
00733                     user->ud->last_use = now;
00734                 }
00735 
00736                 return user->ud;
00737             } else {
00738                 user->ud = scalloc(sizeof(UserData), 1);
00739                 user->ud->last_use = time(NULL);
00740                 return user->ud;
00741             }
00742         }
00743     }
00744 
00745     return NULL;
00746 }
00747 
00748 /*************************************************************************/
00749 
00750 /* Makes the bot join a channel and op himself. */
00751 
00752 void bot_join(ChannelInfo * ci)
00753 {
00754     int i;
00755 
00756     if (!ci || !ci->c || !ci->bi)
00757         return;
00758 
00759     if (BSSmartJoin) {
00760         /* We check for bans */
00761         int count = ci->c->bancount;
00762         if (count) {
00763             char botmask[BUFSIZE];
00764             char **bans = scalloc(sizeof(char *) * count, 1);
00765             char *av[3];
00766 
00767             memcpy(bans, ci->c->bans, sizeof(char *) * count);
00768             snprintf(botmask, sizeof(botmask), "%s!%s@%s", ci->bi->nick,
00769                      ci->bi->user, ci->bi->host);
00770 
00771             av[0] = ci->c->name;
00772             av[1] = sstrdup("-b");
00773             for (i = 0; i < count; i++) {
00774                 if (match_wild_nocase(ci->c->bans[i], botmask)) {
00775                     anope_cmd_mode(ci->bi->nick, ci->name, "-b %s",
00776                                    bans[i]);
00777                     av[2] = sstrdup(bans[i]);
00778                     do_cmode(ci->bi->nick, 3, av);
00779                     free(av[2]);
00780                 }
00781             }
00782             free(av[1]);
00783             free(bans);
00784         }
00785 
00786         /* Should we be invited? */
00787         if ((ci->c->mode & anope_get_invite_mode())
00788             || (ci->c->limit && ci->c->usercount >= ci->c->limit))
00789             anope_cmd_notice_ops(NULL, ci->c->name,
00790                                  "%s invited %s into the channel.",
00791                                  ci->bi->nick, ci->bi->nick);
00792     }
00793     anope_cmd_join(ci->bi->nick, ci->c->name, ci->c->creation_time);
00794     anope_cmd_bot_chan_mode(ci->bi->nick, ci->c->name);
00795     send_event(EVENT_BOT_JOIN, 2, ci->name, ci->bi->nick);
00796 }
00797 
00798 /*************************************************************************/
00799 
00800 /* This makes the bot rejoin all channel he is on when he gets killed
00801  * or changed.
00802  */
00803 
00804 void bot_rejoin_all(BotInfo * bi)
00805 {
00806     int i;
00807     ChannelInfo *ci;
00808 
00809     for (i = 0; i < 256; i++)
00810         for (ci = chanlists[i]; ci; ci = ci->next)
00811             if (ci->bi == bi && ci->c && (ci->c->usercount >= BSMinUsers))
00812                 bot_join(ci);
00813 }
00814 
00815 /*************************************************************************/
00816 
00817 /* This makes a ban if the user has to have one. In every cases it increments
00818    the kick count for the user. */
00819 
00820 static void check_ban(ChannelInfo * ci, User * u, int ttbtype)
00821 {
00822     BanData *bd = get_ban_data(ci->c, u);
00823 
00824     if (!bd)
00825         return;
00826 
00827     bd->ttb[ttbtype]++;
00828     if (bd->ttb[ttbtype] == ci->ttb[ttbtype]) {
00829         char *av[3];
00830         char mask[BUFSIZE];
00831 
00832         bd->ttb[ttbtype] = 0;
00833 
00834         av[0] = ci->name;
00835         av[1] = sstrdup("+b");
00836         get_idealban(ci, u, mask, sizeof(mask));
00837         av[2] = mask;
00838         anope_cmd_mode(ci->bi->nick, av[0], "+b %s", av[2]);
00839         do_cmode(ci->bi->nick, 3, av);
00840         send_event(EVENT_BOT_BAN, 3, u->nick, ci->name, mask);
00841         free(av[1]);
00842     }
00843 }
00844 
00845 /*************************************************************************/
00846 
00847 /* This makes a bot kick an user. Works somewhat like notice_lang in fact ;) */
00848 
00849 static void bot_kick(ChannelInfo * ci, User * u, int message, ...)
00850 {
00851     va_list args;
00852     char buf[1024];
00853     const char *fmt;
00854     char *av[3];
00855 
00856     if (!ci || !ci->bi || !ci->c || !u)
00857         return;
00858 
00859     va_start(args, message);
00860     fmt = getstring(u->na, message);
00861     if (!fmt)
00862         return;
00863     vsnprintf(buf, sizeof(buf), fmt, args);
00864     va_end(args);
00865 
00866     av[0] = ci->name;
00867     av[1] = u->nick;
00868     av[2] = buf;
00869     anope_cmd_kick(ci->bi->nick, av[0], av[1], "%s", av[2]);
00870     do_kick(ci->bi->nick, 3, av);
00871     send_event(EVENT_BOT_KICK, 3, u->nick, ci->name, buf);
00872 }
00873 
00874 /*************************************************************************/
00875 
00876 /* Makes a simple ban and kicks the target */
00877 
00878 void bot_raw_ban(User * requester, ChannelInfo * ci, char *nick,
00879                  char *reason)
00880 {
00881     int ac;
00882     char *av[4];
00883     char mask[BUFSIZE];
00884     char buf[BUFSIZE];
00885     User *u = finduser(nick);
00886 
00887     if (!u)
00888         return;
00889 
00890     if (ircd->protectedumode) {
00891         if (is_protected(u) && (requester != u)) {
00892             anope_cmd_privmsg(ci->bi->nick, ci->name, "%s",
00893                               getstring2(NULL, PERMISSION_DENIED));
00894             return;
00895         }
00896     }
00897 
00898     if ((ci->flags & CI_PEACE) && stricmp(requester->nick, nick)
00899         && (get_access(u, ci) >= get_access(requester, ci)))
00900         return;
00901 
00902     if (ircd->except) {
00903         if (is_excepted(ci, u) == 1) {
00904             anope_cmd_privmsg(ci->bi->nick, ci->name, "%s",
00905                               getstring2(NULL, BOT_EXCEPT));
00906             return;
00907         }
00908     }
00909 
00910     get_idealban(ci, u, mask, sizeof(mask));
00911 
00912     if (ircdcap->tsmode) {
00913         snprintf(buf, BUFSIZE - 1, "%ld", (long int) time(NULL));
00914         av[0] = ci->name;
00915         av[1] = buf;
00916         av[2] = sstrdup("+b");
00917         av[3] = mask;
00918         ac = 4;
00919     } else {
00920         av[0] = ci->name;
00921         av[1] = sstrdup("+b");
00922         av[2] = mask;
00923         ac = 3;
00924     }
00925 
00926     anope_cmd_mode(ci->bi->nick, ci->name, "+b %s", mask);
00927     do_cmode(ci->bi->nick, ac, av);
00928 
00929     /* We need to free our sstrdup'd "+b" -GD */
00930     if (ircdcap->tsmode)
00931         free(av[2]);
00932     else
00933         free(av[1]);
00934 
00935     av[0] = ci->name;
00936     av[1] = nick;
00937 
00938     if (!reason) {
00939         av[2] = ci->bi->nick;
00940     } else {
00941         if (strlen(reason) > 200)
00942             reason[200] = '\0';
00943         av[2] = reason;
00944     }
00945 
00946     /* Check if we need to do a signkick or not -GD */
00947     if ((ci->flags & CI_SIGNKICK)
00948         || ((ci->flags & CI_SIGNKICK_LEVEL)
00949             && !check_access(requester, ci, CA_SIGNKICK)))
00950         anope_cmd_kick(ci->bi->nick, av[0], av[1], "%s (%s)", av[2],
00951                        requester->nick);
00952     else
00953         anope_cmd_kick(ci->bi->nick, av[0], av[1], "%s", av[2]);
00954 
00955     do_kick(ci->bi->nick, 3, av);
00956 }
00957 
00958 /*************************************************************************/
00959 
00960 /* Makes a kick with a "dynamic" reason ;) */
00961 
00962 void bot_raw_kick(User * requester, ChannelInfo * ci, char *nick,
00963                   char *reason)
00964 {
00965     char *av[3];
00966     User *u = finduser(nick);
00967 
00968     if (!u || !is_on_chan(ci->c, u))
00969         return;
00970 
00971     if (ircd->protectedumode) {
00972         if (is_protected(u) && (requester != u)) {
00973             anope_cmd_privmsg(ci->bi->nick, ci->name, "%s",
00974                               getstring2(NULL, PERMISSION_DENIED));
00975             return;
00976         }
00977     }
00978 
00979     if ((ci->flags & CI_PEACE) && stricmp(requester->nick, nick)
00980         && (get_access(u, ci) >= get_access(requester, ci)))
00981         return;
00982 
00983     av[0] = ci->name;
00984     av[1] = nick;
00985 
00986     if (!reason) {
00987         av[2] = ci->bi->nick;
00988     } else {
00989         if (strlen(reason) > 200)
00990             reason[200] = '\0';
00991         av[2] = reason;
00992     }
00993 
00994     if ((ci->flags & CI_SIGNKICK)
00995         || ((ci->flags & CI_SIGNKICK_LEVEL)
00996             && !check_access(requester, ci, CA_SIGNKICK)))
00997         anope_cmd_kick(ci->bi->nick, av[0], av[1], "%s (%s)", av[2],
00998                        requester->nick);
00999     else
01000         anope_cmd_kick(ci->bi->nick, av[0], av[1], "%s", av[2]);
01001     do_kick(ci->bi->nick, 3, av);
01002 }
01003 
01004 /*************************************************************************/
01005 
01006 /* Makes a mode operation on a channel for a nick */
01007 
01008 void bot_raw_mode(User * requester, ChannelInfo * ci, char *mode,
01009                   char *nick)
01010 {
01011     char *av[4];
01012     int ac;
01013     char buf[BUFSIZE];
01014     User *u;
01015 
01016     *buf = '\0';
01017     u = finduser(nick);
01018 
01019     if (!u || !is_on_chan(ci->c, u))
01020         return;
01021 
01022     snprintf(buf, BUFSIZE - 1, "%ld", (long int) time(NULL));
01023 
01024     if (ircd->protectedumode) {
01025         if (is_protected(u) && *mode == '-' && (requester != u)) {
01026             anope_cmd_privmsg(ci->bi->nick, ci->name, "%s",
01027                               getstring2(NULL, PERMISSION_DENIED));
01028             return;
01029         }
01030     }
01031 
01032     if (*mode == '-' && (ci->flags & CI_PEACE)
01033         && stricmp(requester->nick, nick)
01034         && (get_access(u, ci) >= get_access(requester, ci)))
01035         return;
01036 
01037     if (ircdcap->tsmode) {
01038         av[0] = ci->name;
01039         av[1] = buf;
01040         av[2] = mode;
01041         av[3] = nick;
01042         ac = 4;
01043         anope_cmd_mode(ci->bi->nick, av[0], "%s %s", av[2], av[3]);
01044     } else {
01045         av[0] = ci->name;
01046         av[1] = mode;
01047         av[2] = nick;
01048         ac = 3;
01049         anope_cmd_mode(ci->bi->nick, av[0], "%s %s", av[1], av[2]);
01050     }
01051 
01052     do_cmode(ci->bi->nick, ac, av);
01053 }
01054 
01055 /*************************************************************************/
01061 char *normalizeBuffer(char *buf)
01062 {
01063     char *newbuf;
01064     int i, len, j = 0;
01065 
01066     len = strlen(buf);
01067     newbuf = (char *) smalloc(sizeof(char) * len + 1);
01068 
01069     for (i = 0; i < len; i++) {
01070         switch (buf[i]) {
01071             /* ctrl char */
01072         case 1:
01073             break;
01074             /* Bold ctrl char */
01075         case 2:
01076             break;
01077             /* Color ctrl char */
01078         case 3:
01079             /* If the next character is a digit, its also removed */
01080             if (isdigit(buf[i + 1])) {
01081                 i++;
01082 
01083                 /* not the best way to remove colors
01084                  * which are two digit but no worse then
01085                  * how the Unreal does with +S - TSL
01086                  */
01087                 if (isdigit(buf[i + 1])) {
01088                     i++;
01089                 }
01090 
01091                 /* Check for background color code
01092                  * and remove it as well
01093                  */
01094                 if (buf[i + 1] == ',') {