#include "module.h" #define AUTHOR "SGR" #define VERSION "1.01" /* ----------------------------------------------------------- * Name: bs_antispam * Author: SGR * Date: 08/07/2004 * ----------------------------------------------------------- * Functions: kill_spammy_luser, my_spam_checking_privmsg * StripControlCodes, m_bs_antispam SGR_Module_Help_BOTSERV_ANTISPAM_FULL SGR_Module_Help_BOTSERV_ANTISPAM * Limitations: None Known. * Tested: Ultimate(2.8.x), Bahamut-1.8.1+AcidChat, Viagra. * ----------------------------------------------------------- * This modules has 13 configurable options * * Change Log: * * 1: Works. * 2: Added Support for Anope-1.7.x (revision 370+) * ----------------------------------------------------------- */ /* ---------------------------------------------------------------------- */ /* START OF CONFIGURATION BLOCK - please read the comments :) */ /* ---------------------------------------------------------------------- */ /* [REQUIRED] If you are using Anope-1.7.x or later, leave this alone. * Otherwise change it to '#undef ANOPE17x' (no quotes). * * THIS IS IMPORTANT * */ #define ANOPE17x /* [REQUIRED] Action - What action should I take on users who trigger the Spam checker? * This setting must be numerical ONLY. * * This is the default - and can be changed from within IRC. * * Set to 0: Take no action. * Set to 1: Echo the message to the LogChannel for opers to deal-with. * Set to 2: Kick the user from all channels. * Set to 3: Kill the user * Set to 4: AKill the User */ #define ACTION 2 /* [OPTIONAL] Up to 6 notices can be sent to users who trigger ACTION from the spam * checker. Should you not wish to use these, comment them out. */ #define ACTION_NOTICE1 "You appear to be mass advertising. Mass advertising is banned on this network." #define ACTION_NOTICE2 "Please do NOT spam, and ensure you read the LOGON NEWS noticed to you when you" #define ACTION_NOTICE3 "connected to this network." //#define ACTION_NOTICE4 "Define this if you want it." //#define ACTION_NOTICE5 "Define this if you want it." //#define ACTION_NOTICE6 "Define this if you want it." /* [REQUIRED] - The reason used when action is set to 3 or 4. [The Kill and * AKILL reason]. */ #define AKILL_AND_KILL_REASON "Spamming or Mass Advertising is NOT permitted here. READ THE LOGON NEWS!" /* [REQUIRED] - The reason used when action is set to 2. [The KICK reason]. */ #define KICK_REASON "Spamming or Mass Advertising is NOT permitted here. READ THE LOGON NEWS!" /* When action is set to 4 [AKILL], how long should the akill be placed for? [In seconds] * This setting must be numerical ONLY. * * 60 = 1 min * 300 = 5 mins * 900 = 15 mins [Recommended] * 1800 = 30 mins * 3600 = 1 hour * 86400 = 1 Day * 604800 = 1 week */ #define AKILL_TIME_IN_SECS 900 /* [OPTIONAL] With the below UNDEFINED, the module will check ALL privmsg's * and notices that are sent to ANY BotServ by non opers to see if they * would trigger the spam checker. If this IS defined, it will ONLY check * messages sent to the bot who's name appears in the setting. Thus, the * checker would be useless if there is not a services psudo-client with the * setting given. */ //#define SINGLE_BOT_ONLY "FishBot" /* ---------------------------------------------------------------------- */ /* DO NOT EDIT BELOW THIS LINE UNLESS YOU KNOW WHAT YOU ARE DOING */ /* ---------------------------------------------------------------------- */ /* Too add more words to the below list, make sure the string is surounded by * "quotes". All entries must be followed by a ',' EXCEPT the LAST entry, * which needs to be followed by '};' [only]. You should NOT edit the line * 'char *words[] = {' * * The convention I have used is to have 5 strings per line. I would * suggest that when you add new words, you add them in a new line * inbetween the 1st and 2nd ones. For example, to add the words * 'moo', 'spam', 'can', 'noo' and 'boo' to the list I would insert this line: * * "moo","spam","can","noo","boo", * * in between: * * "http",".com",".tk",".org",".info", * ".net",".us","www",".cx",".nu", * * So I'm left with: * * char *words[] = { * "http",".com",".tk",".org",".info", * "moo","spam","can","noo","boo", * ".net",".us","www",".cx",".nu", * ".cc",".ru","come",".htm",".php", * * NOTE: THE QUOTES AND SPEECH MARKS _ARE_ IMPORTANT. The strings are * NOT CaSe SeNsItIve and are matched in the style: *[string]* * - wildcards are permitted - so * and ? characters are NOT * treated literally. * * i.e. a word like "sp?nk" will trigger: spank, splnk, SpInK etc. * There is no point having an * wildcard at the beginning or end * of a word. As "sp?nk" would also match theSpanKMyMonkey. * * NUM_OF_WORDS _must_ equal the number of words in the list. */ char *words[] = { /* 67 Words in the list.*/ "http",".com",".tk",".org",".info", /* 5 */ ".net",".us","www",".cx",".nu", /* 5 */ ".cc",".ru","come",".htm",".php", /* 5 */ ".jsp",".asp","join","irc:","core", /* 5 */ "anal","anus","rectum","teen","s?x", /* 5 */ "f*uck","p?rn","cunt","pussy","fetish", /* 5 */ "c?ck","penis","suck","cl?t","kinky", /* 5 */ "s?xy","teen","toy","?p?nk","v*a?r?", /* 5 */ "v?c?d?n","nigeria","foreign","fr?e","win", /* 5 */ "won","c?ngrat","diet","smoking","weight", /* 5 */ "d?gr??","fat","thin","cash","cheap", /* 5 */ ".mov","pic","mpg",".avi",".exe", /* 5 */ ".pif",".scr",".js","deal","credit", /* 5 */ "vid","pix" /* 2 */ }; /* TOTAL: 67 */ #define NUM_OF_WORDS 67 /* THIS _MUST_ BE VALID AND CORRECT */ /* ---------------------------------------------------------------------- */ /* END OF CONFIGURATION SECTION */ /* ---------------------------------------------------------------------- */ #define MyFree(x) if ((x) != NULL) free(x) int SGR_Module_Help_BOTSERV_ANTISPAM_FULL(User *u); void SGR_Module_Help_BOTSERV_ANTISPAM(User *u); int my_spam_checking_privmsg(char *source, int ac, char **av); void kill_spammy_luser(User *u); const char *StripControlCodes(unsigned char *text); int m_bs_antispam(User *u); static int action = ACTION; static int logging = 1; int AnopeInit(int argc, char **argv) { Message *msg = NULL; #ifdef SINGLE_BOT_ONLY int i = 0; int j = 0; #endif Command *c = NULL; alog("Loading module bs_antispam.so"); #ifdef SINGLE_BOT_ONLY for (i = 0; i < 256; i++) { for (bi = botlists[i]; bi; bi = bi->next) { if (stricmp(SINGLE_BOT_ONLY, bi->nick)==0) { j = 1; break; } } if (j == 1) { break; } } if (!j) { alog("[bs_antispam] I'm running in SINGLE bot mode - the bot I'm attached to is: %s.", SINGLE_BOT_ONLY); alog("[bs_antispam] ERROR: There is no %s bot named %s.", s_BotServ, SINGLE_BOT_ONLY); alog("[bs_antispam] ERROR: Check the setting of SINGLE_BOT_ONLY in the modules configuration block!"); alog("[bs_antispam] ERROR: You will need to make a bot with the nick '%s' for this module to load.". SINGLE_BOT_ONLY); alog("[bs_antispam] Module Unloading Automatically!"); return MOD_STOP; } alog("[bs_antispam] I'm running in SINGLE bot mode - the bot I'm attached to is: %s.", SINGLE_BOT_ONLY); alog("[bs_antispam] This means I will only take action on users who notice or privmsg"); alog("[bs_antispam] %s with a string containing one of the trigger words.", SINGLE_BOT_ONLY); #else alog("[bs_antispam] I'm running in ALL bot mode - I'm attached to ALL %s Bots.", s_BotServ); alog("[bs_antispam] This means I will take action on users who notice or privmsg any"); alog("[bs_antispam] %s bot with a string containing one of the trigger words.", s_BotServ); #endif msg = createMessage("PRIVMSG", my_spam_checking_privmsg); moduleAddMessage(msg, MOD_TAIL); msg = createMessage("NOTICE", my_spam_checking_privmsg); moduleAddMessage(msg, MOD_TAIL); c = createCommand("ANTISPAM", m_bs_antispam, is_services_admin, -1, -1, -1, -1, -1); moduleAddCommand(BOTSERV, c, MOD_HEAD); moduleAddHelp(c,SGR_Module_Help_BOTSERV_ANTISPAM_FULL); moduleSetBotHelp(SGR_Module_Help_BOTSERV_ANTISPAM); alog("[bs_antispam] For more info see /msg %s HELP ANTISPAM", s_BotServ); alog("[bs_antispam] Yayness!(tm) - MODULE LOADED AND ACTIVE"); return MOD_CONT; } int m_bs_antispam(User *u) { char *arg1 = strtok(NULL, " "); if (!is_services_admin(u)) { notice_lang(s_BotServ, u, ACCESS_DENIED); return MOD_CONT; } if (!arg1) { if (action < 1) { notice(s_BotServ, u->nick, "The current ANTISPAM setting is: OFF"); notice(s_BotServ, u->nick, "No action will be taken on users who trigger the spam checker."); } else if (action == 1) { notice(s_BotServ, u->nick, "The current ANTISPAM setting is: ON - LOGONLY"); notice(s_BotServ, u->nick, "The offending message will be reported to the LogChan only"); } else if (action == 2) { notice(s_BotServ, u->nick, "The current ANTISPAM setting is: ON - KICK"); notice(s_BotServ, u->nick, "The offending user will be kicked from all channels"); } else if (action == 3) { notice(s_BotServ, u->nick, "The current ANTISPAM setting is: ON - KILL"); notice(s_BotServ, u->nick, "The offending user will be KILLed off the network."); } else if (action == 4) { notice(s_BotServ, u->nick, "The current ANTISPAM setting is: ON - AKILL"); notice(s_BotServ, u->nick, "The offending user will be AKILLed for %d seconds.", AKILL_TIME_IN_SECS); } return MOD_CONT; } if (stricmp(arg1,"OFF") == 0 ) { action = 0; notice(s_BotServ, u->nick, "The current ANTISPAM setting is: OFF"); notice(s_BotServ, u->nick, "No action will be taken on users who trigger the spam checker."); #ifndef ANOPE17x wallops(s_BotServ, "%s disabled the %s spam checker", u->nick, s_BotServ); #else anope_cmd_global(s_BotServ, "%s disabled the %s spam checker", u->nick, s_BotServ); #endif return MOD_CONT; } else if (stricmp(arg1,"LOGCHAN") == 0 ) { action = 1; notice(s_BotServ, u->nick, "The current ANTISPAM setting is: ON - LOGONLY"); notice(s_BotServ, u->nick, "The offending message will be reported to the LogChan only"); #ifndef ANOPE17x wallops(s_BotServ, "%s set the %s spam checker to LOGCHAN", u->nick, s_BotServ); #else anope_cmd_global(s_BotServ, "%s set the %s spam checker to LOGCHAN", u->nick, s_BotServ); #endif return MOD_CONT; } else if (stricmp(arg1,"KICK") == 0 ) { action = 2; notice(s_BotServ, u->nick, "The current ANTISPAM setting is: ON - KICK"); notice(s_BotServ, u->nick, "The offending user will be kicked from all channels"); #ifndef ANOPE17x wallops(s_BotServ, "%s set the %s spam checker to KICK", u->nick, s_BotServ); #else anope_cmd_global(s_BotServ, "%s set the %s spam checker to KICK", u->nick, s_BotServ); #endif return MOD_CONT; } else if (stricmp(arg1,"KILL") == 0 ) { action = 3; notice(s_BotServ, u->nick, "The current ANTISPAM setting is: ON - KILL"); notice(s_BotServ, u->nick, "The offending user will be KILLed off the network."); #ifndef ANOPE17x wallops(s_BotServ, "%s set the %s spam checker to KILL", u->nick, s_BotServ); #else anope_cmd_global(s_BotServ, "%s set the %s spam checker to KILL", u->nick, s_BotServ); #endif return MOD_CONT; } else if (stricmp(arg1,"AKILL") == 0 ) { action = 4; notice(s_BotServ, u->nick, "The current ANTISPAM setting is: ON - AKILL"); notice(s_BotServ, u->nick, "The offending user will be AKILLed for %d seconds.", AKILL_TIME_IN_SECS); #ifndef ANOPE17x wallops(s_BotServ, "%s set the %s spam checker to AKILL", u->nick, s_BotServ); #else anope_cmd_global(s_BotServ, "%s set the %s spam checker to AKILL", u->nick, s_BotServ); #endif return MOD_CONT; } else { notice(s_BotServ, u->nick, "SYNTAX: ANTISPAM [OFF|LOGCHAN|KICK|KILL|AKILL]"); return MOD_CONT; } return MOD_CONT; } void SGR_Module_Help_BOTSERV_ANTISPAM(User *u) { if (is_services_admin(u)) { notice(s_BotServ, u->nick, " ANTISPAM Configure the spam checker."); } return; } int SGR_Module_Help_BOTSERV_ANTISPAM_FULL(User *u) { if (is_services_admin(u)) { notice(s_BotServ, u->nick, " "); notice(s_BotServ, u->nick, "Syntax: ANTISPAM [OFF|LOGCHAN|KICK|KILL|AKILL]"); notice(s_BotServ, u->nick, " "); notice(s_BotServ, u->nick, "Use this command without an argument to see the current setting."); #ifdef SINGLE_BOT_ONLY notice(s_BotServ, u->nick, "When enabled, %s will act as a spam checking bot.", SINGLE_BOT_ONLY); notice(s_BotServ, u->nick, "All private messages received by %s will be checked to see ", SINGLE_BOT_ONLY); #else notice(s_BotServ, u->nick, "When enabled, %s will act as a spam checker utility - ", s_BotServ); notice(s_BotServ, u->nick, "all private messages received by %s will be checked to see ", s_BotServ); #endif notice(s_BotServ, u->nick, "if they contain a word that matches a known 'spam word'. If this"); notice(s_BotServ, u->nick, "is the case, the ACTION set will be performed on the user."); notice(s_BotServ, u->nick, " "); notice(s_BotServ, u->nick, "If set to LOGCHAN, no action will be taken on the user, but the "); notice(s_BotServ, u->nick, "message they 'spammed' will be dumped to the logchannel. "); notice(s_BotServ, u->nick, "If set to KICK, the user will be kicked from all channels they are"); notice(s_BotServ, u->nick, "currently on."); notice(s_BotServ, u->nick, "If set to KILL, the user will be killed off the network."); notice(s_BotServ, u->nick, "If set to AKILL, the user will be akilled from the network."); notice(s_BotServ, u->nick, " "); notice(s_BotServ, u->nick, "Opers and above are not affected, however, if an oper has the"); notice(s_BotServ, u->nick, "same hostmask as a user an akill placed by this module may "); notice(s_BotServ, u->nick, "affect them"); } return MOD_CONT; } int my_spam_checking_privmsg(char *source, int ac, char **av) { User *u; char *s; int is_pm = 0; int i = 0; #ifndef SINGLE_BOT_ONLY BotInfo *bi; #endif if (action < 1) { return MOD_CONT; } if ((ac != 2) || (!(u = finduser(source))) || is_oper(u)) { /* non-user source */ return MOD_CONT; } if (*av[0] == '#' || !av[1]) { /* leave channel text */ return MOD_CONT; } s = strchr(av[0], '@'); if (s) { *s++ = 0; if (!stricmp(s, ServerName) == 0) { return MOD_CONT; } } #ifndef SINGLE_BOT_ONLY /* Cycle through all BotServ Bots */ for (i = 0; i < 256; i++) { for (bi = botlists[i]; bi; bi = bi->next) { if (stricmp(av[0], bi->nick)==0) { /* Message was PM'd to one */ is_pm = 1; break; } } if (is_pm) { break; } } #else if (stricmp(av[0], SINGLE_BOT_ONLY)==0) { is_pm = 1; } #endif if (is_pm) { char *tempbuf = NULL; char *buf = NULL; if ((tempbuf = moduleGetLastBuffer())) { (char *) buf = smalloc(strlen(tempbuf) + strlen(av[1]) + 5); sprintf(buf, "%s %s", av[1], tempbuf); } else { (char *) buf = smalloc(strlen(av[1]) + 5); buf = av[1]; } tempbuf = (char *) StripControlCodes(buf); MyFree(buf); for (i = 0; i < NUM_OF_WORDS; i++) { char wordb[512] = { '\0' }; snprintf(wordb, sizeof(wordb), "*%s*", words[i]); if (match_wild_nocase(wordb,tempbuf)) { if (logging > 1 || action == 1) { alog("[bs_antispam] %s triggered the spamchecker with message: %s", u->nick, tempbuf); } if (action > 1) { (void) kill_spammy_luser(u); } return MOD_CONT; } } } return MOD_CONT; } void kill_spammy_luser(User *u) { #ifdef ACTION_NOTICE1 notice(s_BotServ, u->nick, ACTION_NOTICE1); #endif #ifdef ACTION_NOTICE2 notice(s_BotServ, u->nick, ACTION_NOTICE2); #endif #ifdef ACTION_NOTICE3 notice(s_BotServ, u->nick, ACTION_NOTICE3); #endif #ifdef ACTION_NOTICE4 notice(s_BotServ, u->nick, ACTION_NOTICE4); #endif #ifdef ACTION_NOTICE5 notice(s_BotServ, u->nick, ACTION_NOTICE5); #endif #ifdef ACTION_NOTICE6 notice(s_BotServ, u->nick, ACTION_NOTICE6); #endif if (action < 1) { return; } else if (action == 4) { char mask[HOSTMAX + 2]; snprintf(mask, sizeof(mask), "*@%s", u->host); if (logging > 0) { alog("[bs_antispam] %s triggered the spamchecker: AKILLING [%s]", u->nick, mask); } add_akill(NULL, mask, s_BotServ, time(NULL)+AKILL_TIME_IN_SECS, AKILL_AND_KILL_REASON); MyFree(mask); if (!AkillOnAdd) { kill_user(s_BotServ, u->nick, AKILL_AND_KILL_REASON); } } else if (action == 3) { if (logging > 0) { alog("[bs_antispam] %s triggered the spamchecker: KILLING", u->nick); } kill_user(s_BotServ, u->nick, AKILL_AND_KILL_REASON); } else if (action == 2) { /* thanks to codemstr for the *next implementation * My original idea wasn't as efficient. */ struct u_chanlist *uc, *next; ChannelInfo *ci; char *av[3]; if (logging > 0) { alog("[bs_antispam] %s triggered the spamchecker: KICKING them from all channels.", u->nick); } for (uc = u->chans; uc; uc = next) { next = uc->next; av[0] = sstrdup(uc->chan->name); av[1] = sstrdup(u->nick); av[2] = sstrdup(KICK_REASON); if ((ci = uc->chan->ci)) { send_cmd(whosends(ci), "KICK %s %s :%s", av[0], av[1], av[2]); } else { send_cmd(s_BotServ, "KICK %s %s :%s", av[0], av[1], av[2]); } do_kick(s_BotServ, 3, av); free(av[2]); free(av[1]); free(av[0]); } } return; } /* This is ripped from UnrealIRCd - please see www.unrealircd.org for * authors and original credits etc. -- strips color, bold, underline, * and reverse codes from a string */ const char *StripControlCodes(unsigned char *text) { int nc = 0, col = 0, i = 0, len = strlen(text); static unsigned char new_str[530]; while (len > 0) { if (col && ((isdigit(*text) && nc < 2) || (*text == ',' && nc < 3))) { nc++; if (*text == ',') { nc = 0; } } else { if (col) { col = 0; } switch (*text) { case 3: /* color */ col = 1; nc = 0; break; case 2: /* bold */ break; case 31: /* underline */ break; case 22: /* reverse */ break; case 15: /* plain */ break; default: new_str[i] = *text; i++; break; } } text++; len--; } new_str[i] = 0; return new_str; }