/* * ================================================================= * Filename: m_rmban.c * Description: Command /rmban, taken from DarkFire IRCd. * Author: AngryWolf * Documentation: m_rmban.txt (comes with the package) * ================================================================= */ #include "config.h" #include "struct.h" #include "common.h" #include "sys.h" #include "numeric.h" #include "msg.h" #include "channel.h" #include #include #include #include #include #ifdef _WIN32 #include #endif #include #include "h.h" #ifdef STRIPBADWORDS #include "badwords.h" #endif #ifdef _WIN32 #include "version.h" #endif extern void sendto_one(aClient *to, char *pattern, ...); extern int del_banid(aChannel *, char *); #ifdef EXBTYPE_BAN extern char *clean_ban_mask(char *mask, int what); #else extern char *trim_str(char *str, int len); extern char *clean_ban_mask(char *mask); #endif #define MSG_RMBAN "RMBAN" #define TOK_RMBAN "RB" #define DelCommand(x) if (x) CommandDel(x); x = NULL #define BadParam(x) ((parc < (x)+1) || BadPtr(parv[(x)])) static Command *AddCommand(Module *module, char *msg, char *token, iFP func); DLLFUNC int m_rmban(aClient *cptr, aClient *sptr, int parc, char *parv[]); Command *CmdRmban; enum MaskType { MT_Nick, MT_Nuh }; #ifndef HOOKTYPE_REHASH_COMPLETE static char modebuf[MAXMODEPARAMS*2+1], parabuf[504]; #endif static u_int modecount; ModuleHeader MOD_HEADER(m_rmban) = { "rmban", "$Id: m_rmban.c,v 3.1 2004/03/22 21:04:41 angrywolf Exp $", "command /rmban", "3.2-b8-1", NULL }; DLLFUNC int MOD_INIT(m_rmban)(ModuleInfo *modinfo) { CmdRmban = AddCommand(modinfo->handle, MSG_RMBAN, TOK_RMBAN, m_rmban); if (!CmdRmban) return MOD_FAILED; return MOD_SUCCESS; } DLLFUNC int MOD_LOAD(m_rmban)(int module_load) { return MOD_SUCCESS; } DLLFUNC int MOD_UNLOAD(m_rmban)(int module_unload) { DelCommand(CmdRmban); return MOD_SUCCESS; } static Command *AddCommand(Module *module, char *msg, char *token, iFP func) { Command *cmd; if (CommandExists(msg)) { config_error("Command %s already exists", msg); return NULL; } if (CommandExists(token)) { config_error("Token %s already exists", token); return NULL; } cmd = CommandAdd(module, msg, token, func, MAXPARA, 0); #ifndef STATIC_LINKING if (ModuleGetError(module) != MODERR_NOERROR || !cmd) #else if (!cmd) #endif { #ifndef STATIC_LINKING config_error("Error adding command %s: %s", msg, ModuleGetErrorStr(module)); #else config_error("Error adding command %s", msg); #endif return NULL; } return cmd; } /* * my_find_chasing * * a modified find_chasing(), originally comes from channel.c, * fitting the needs for /rmban. */ aClient *my_find_chasing(char *user) { aClient *who; if ((who = find_person(user, NULL))) return who; if (!(who = get_history(user, (long) KILLCHASETIMELIMIT))) return NULL; if (!IsServer(who)) return who; else return NULL; } static u_int match_ban(aClient *cptr, aChannel *chptr, Ban *ban) { char *s; static char realhost[NICKLEN + USERLEN + HOSTLEN + 6]; static char virthost[NICKLEN + USERLEN + HOSTLEN + 6]; static char nuip[NICKLEN + USERLEN + HOSTLEN + 6]; int dovirt = 0, mine = 0; #ifdef EXBTYPE_BAN Extban *extban; ban_realhost = realhost; ban_ip = ban_virthost = NULL; #endif s = make_nick_user_host(cptr->name, cptr->user->username, cptr->user->realhost); strlcpy(realhost, s, sizeof realhost); if (MyConnect(cptr)) { mine = 1; s = make_nick_user_host(cptr->name, cptr->user->username, Inet_ia2p(&cptr->ip)); strlcpy(nuip, s, sizeof nuip); #ifdef EXBTYPE_BAN ban_ip = nuip; #endif } if (cptr->user->virthost && strcmp(cptr->user->realhost, cptr->user->virthost)) { dovirt = 1; s = make_nick_user_host(cptr->name, cptr->user->username, cptr->user->virthost); strlcpy(virthost, s, sizeof virthost); #ifdef EXBTYPE_BAN ban_virthost = virthost; #endif } #ifdef EXBTYPE_BAN if (*ban->banstr == '~') { extban = findmod_by_bantype(ban->banstr[1]); if (!extban) return 0; if ( extban->is_banned(cptr, chptr, ban->banstr, BANCHK_JOIN) || extban->is_banned(cptr, chptr, ban->banstr, BANCHK_MSG) || extban->is_banned(cptr, chptr, ban->banstr, BANCHK_NICK) ) return 1; } else { #endif if ((match(ban->banstr, realhost) == 0) || (mine && (match(ban->banstr, nuip) == 0)) || (dovirt && (match(ban->banstr, virthost) == 0))) return 1; #ifdef EXBTYPE_BAN } #endif return 0; } static inline u_int match_type(u_int type, aClient *acptr, aChannel *chptr, Ban *ban, char *mask) { switch (type) { case MT_Nick: if (!match_ban(acptr, chptr, ban)) return 0; break; case MT_Nuh: if (match(mask, ban->banstr)) return 0; } return 1; } static inline void init_mode_str() { memset(&modebuf, 0, sizeof modebuf); memset(¶buf, 0, sizeof parabuf); *modebuf = '-'; modecount = 0; } static void flush_mode_str(aClient *sptr, aChannel *chptr) { if (modecount > 0) { sendto_serv_butone(&me, ":%s MODE %s %s %s 0", sptr->name, chptr->chname, modebuf, parabuf); sendto_channel_butserv(chptr, &me, ":%s MODE %s %s %s", sptr->name, chptr->chname, modebuf, parabuf); } init_mode_str(); } static void add_mode_to_str(aClient *sptr, aChannel *chptr, char *mode, char *param) { strcat(modebuf, mode); strcat(parabuf, param); strcat(parabuf, " "); if (++modecount == MAXMODEPARAMS) flush_mode_str(sptr, chptr); } /* * dump a NULL-terminated array of strings to user sptr using * the numeric rplnum, and then return 0 * (taken from DarkFire IRCd) */ static int dumpit(aClient *sptr, int rplnum, char **p) { for (; *p != NULL; p++) sendto_one(sptr, ":%s %03d %s :%s", me.name, rplnum, sptr->name, *p); /* let user take 8 seconds to read it! */ if (MyClient(sptr)) sptr->since += 8; return 0; } /* help for /rmban command */ static char *rmban_help[] = { "*** Help on /rmban *** ", "COMMAND - Removes bans from a channel with given specifications.", " /rmban #channel [mask [time [max]]]", "The #channel field specifies which channel to remove bans in.", "The mask field can be:", " - a mask", " /rmban #chan *!*@*.com [remove bans ending in .com in #chan]", " /rmban #chan * [remove all bans in #chan]", " - a nickname of someone online, to unban all bans affecting that user", " /rmban #chan JoeBob [remove all bans affecting JoeBob]", " - nothing: unban yourself", "The time field can be:", " - zero (0) or unspecified: no time restrictions on bans removed", " - ># : only bans older than # seconds. the '>' is optional.", " /rmban #chan * 600 [remove bans in #chan older than 10 mins]", " - <# : only bans newer than # seconds.", " /rmban #chan * <30 [remove bans less than 30 seconds old]", "The max field can be:", " - zero (0) or unspecified: no limit on number of bans to remove", " - not zero: remove only the 5 newest bans with that mask", " /rmban #chan * 0 10 [remove 10 most recent bans]", "NOTE: The fields can be combined in any way, but if you specify", "time, you may not skip any fields.", " /rmban #chan *!*@*.foobar.com 30 2", " [remove the 2 most recent bans older than 30 seconds for *.foobar.com]", "*** End of help ***", NULL }; /* * m_rmban * * parv[1] = channel (if none, send help screen) * parv[2] = mask (if none, self assumed) * parv[3] = time limit (+x older than x secs, -x younger than x, 0 default) * parv[4] = max count to delete (60 default) * * originally written by RedLightning and sedition * expanded/renamed/etc by binary * taken from DarkFire IRCd by AngryWolf * */ DLLFUNC int m_rmban(aClient *cptr, aClient *sptr, int parc, char *parv[]) { aClient *acptr = NULL; aChannel *chptr; Ban *ban, *next; char *mask, *tlim, *maxc; u_int type, max, count; int tlimit; TS tmin, tmax; if (!IsPerson(sptr) || !MyConnect(cptr)) return -1; if (BadParam(1)) return dumpit(sptr, RPL_TEXT, rmban_help); if (!(chptr = find_channel(parv[1], NULL))) { sendto_one(sptr, err_str(ERR_NOSUCHCHANNEL), me.name, sptr->name, parv[1]); return -1; } mask = BadParam(2) ? NULL : parv[2]; tlim = BadParam(3) ? NULL : parv[3]; maxc = BadParam(4) ? NULL : parv[4]; if (!is_chan_op(sptr, chptr)) { #ifndef NO_OPEROVERRIDE if (!IsOper(sptr) || !OPCanOverride(sptr)) { #endif sendto_one(sptr, err_str(ERR_CHANOPRIVSNEEDED), me.name, sptr->name, chptr->chname); return -1; #ifndef NO_OPEROVERRIDE } sendto_snomask(SNO_EYES, "*** OperOverride -- %s (%s@%s) RMBAN %s" "%s%s" "%s%s" "%s%s", sptr->name, sptr->user->username, sptr->user->realhost, /* parv[1] */ chptr->chname, /* parv[2] */ mask ? " " : "", mask ? mask : "", /* parv[3] */ tlim ? " " : "", tlim ? tlim : "", /* parv[4] */ maxc ? " " : "", maxc ? maxc : ""); #endif } if (!mask) { acptr = sptr; mask = sptr->name; type = MT_Nick; } else if (!strchr(mask, '@') && !strchr(mask, '!') && !strchr(mask, ':') && (acptr = my_find_chasing(mask))) { mask = acptr->name; type = MT_Nick; } else { #ifdef EXBTYPE_BAN mask = clean_ban_mask(mask, MODE_ADD); #else mask = clean_ban_mask(mask); #endif type = MT_Nuh; } if (tlim) { if (*tlim == '<') tlimit = -atoi(tlim+1); else if (*tlim == '>') tlimit = atoi(tlim+1); else tlimit = atoi(tlim); /* use +/- signs */ } else tlimit = 0; count = max = maxc ? atoi(maxc) : 0; tmin = 0; tmax = time(NULL); if (tlimit < 0) tmin = time(NULL) + tlimit; else tmax -= tlimit; init_mode_str(); for (ban = chptr->banlist; ban; ban = next) { next = ban->next; if (!match_type(type, acptr, chptr, ban, mask)) continue; if (ban->when < tmin || ban->when > tmax) continue; /* ban found */ add_mode_to_str(sptr, chptr, "b", ban->banstr); del_banid(chptr, ban->banstr); if (--count == 0) break; } flush_mode_str(sptr, chptr); sendto_one(sptr, ":%s NOTICE %s :Removed all bans on %s matching %s, %s %d secs, %d max", me.name, sptr->name, chptr->chname, mask, tlimit < 0 ? "newer than" : "older than", abs(tlimit), max); return 0; }