/* +------------------------------------+ * | Inspire Internet Relay Chat Daemon | * +------------------------------------+ * * InspIRCd: (C) 2002-2011 InspIRCd Development Team * See: http://wiki.inspircd.org/Credits * * This program is free but copyrighted software; see * the file COPYING for details. * * --------------------------------------------------- */ #include "inspircd.h" #include "protocol.h" /* $MODDESC: provides commands to clear a given list mode or kick all users */ class ClearBase : public Command { public: ClearBase (Module *me, const std::string &cmd, char flags) : Command (me, cmd, 2, 3) { flags_needed = flags; syntax = " {users [reason] || + [glob pattern] || [glob pattern]}"; } /* this access check returns -1 if user doesn't have access, 0 if he does and if further access checks must be skipped, 1 if user has access to the command but kick and mode accesses must be checked */ virtual int CheckAccess (User *who, Channel *chan) = 0; /* function to do things after the clear, like notifying ircops in saclear */ virtual void PostClear (User *user, Channel *chan, const std::string& type) { } /* make the user user clear the mode pointed to by mh on channel chan */ void ListClear (User *user, Channel *chan, ModeHandler *mh, std::string glob, bool accesscheck) { /* create the modestacker and fill it with mode changes to be made */ irc::modestacker ms; const modelist *list = mh->GetList (chan); const bool matchAll = glob.empty() || (glob == "*"); if (!list) return; for (modelist::const_iterator it = list->begin ( ); it != list->end ( ); it++) if(matchAll || InspIRCd::Match(it->mask, glob)) ms.push (irc::modechange (mh->id, it->mask, false)); /* commit all mode changes and process them, calling handlers and skipping acls, it will update lists */ ServerInstance->Modes->Process (user, chan, ms, false, !accesscheck); /* then, send information about all mode changes to users in the channel to make clients know it */ ServerInstance->Modes->Send (user, chan, ms); /* now, send all mode changes to remaining irc servers, that will make servers also process and send modes */ ServerInstance->PI->SendMode (user, chan, ms); /* write notice */ if (!mh->GetList (chan)) chan->WriteChannel (ServerInstance->FakeClient, "NOTICE %s :%s cleared the %s list mode", chan->name.c_str(), user->GetFullHost().c_str(), mh->name.c_str()); } /* command handler */ CmdResult Handle (const std::vector ¶ms, User *user) { std::string reason; if (params.size() == 3) reason.assign(params[2], 0, ServerInstance->Config->Limits.MaxKick); else reason = user->nick; std::string type = params[1]; Channel *chan = ServerInstance->FindChan (params[0]); std::string channame = params[0]; if (!chan) { user->WriteNumeric (ERR_NOSUCHCHANNEL, "%s %s :No such nick/channel", user->nick.c_str ( ), channame.c_str ( )); return CMD_FAILURE; } /* make the access check */ int acc = CheckAccess (user, chan); if (acc == -1) return CMD_FAILURE; /* this depends on what the user wanted to clear */ if (irc::string (type) == "USERS") { /* the given user tries to empty the channel */ /* send a notice to the whole channel */ chan->WriteChannel (ServerInstance->FakeClient, "NOTICE %s :This channel is being cleared by %s", channame.c_str(), user->GetFullHost().c_str()); /* get each channel user and kick him from the channel */ /* if access checks are to be skipped, use FakeClient as source */ User *fuser; if (acc == 0) fuser = ServerInstance->FakeClient; else fuser = user; /* kick them */ UserMembList memblist = *chan->GetUsers ( ); for (UserMembList::iterator it = memblist.begin ( ); it != memblist.end ( ); it++) { chan->KickUser (fuser, it->first, reason); } } else if (type[0] == '+') { /* it is if the user gave a mode letter */ if (type.size() < 2) { /* that is called when the user typed + and nothing more, invalid param */ user->WriteServ ("NOTICE %s :You must provide a mode letter for a list mode to clear", user->nick.c_str ( )); return CMD_FAILURE; } /* find the list mode */ ModeHandler *mh = ServerInstance->Modes->FindMode (type[1], MODETYPE_CHANNEL); if (!mh) { user->WriteNumeric (472, "%s %c :is unknown mode char to me", user->nick.c_str ( ), type[1]); return CMD_FAILURE; } else if (!mh->IsListMode ( )) { user->WriteServ ("NOTICE %s :the %s mode is not a list mode", user->nick.c_str ( ), mh->name.c_str ( )); return CMD_FAILURE; } /* clear the list mode */ ListClear (user, chan, mh, params.size() == 3 ? params[2] : "", acc == 1); } else { /* the mode was given as a name */ /* find it */ ModeHandler *mh = ServerInstance->Modes->FindMode (type); if (!mh) { user->WriteNumeric (472, "%s %s :is unknown mode string to me", user->nick.c_str ( ), type.c_str ( )); return CMD_FAILURE; } else if (!mh->IsListMode ( )) { user->WriteServ ("NOTICE %s :the %s mode is not a list mode", user->nick.c_str ( ), type.c_str ( )); return CMD_FAILURE; } /* clear the list mode */ ListClear (user, chan, mh, params.size() == 3 ? params[2] : "", acc == 1); } PostClear (user, chan, type); return CMD_SUCCESS; } }; class SaclearCommand : public ClearBase { public: SaclearCommand (Module *me) : ClearBase (me, "SACLEAR", 'o') { } /* access checks, really used for server noticing, not for real access checks */ int CheckAccess (User *user, Channel *chan) { /* the user has permissions to use this command and doesn't need to be on a channel, so */ /* make it to skip all access checks */ return 0; } /* post clear hook */ void PostClear (User *user, Channel *chan, const std::string& type) { /* send notice */ ServerInstance->SNO->WriteGlobalSno ('a', "%s used saclear %s on channel %s", user->nick.c_str ( ), type.c_str ( ), chan->name.c_str ( )); } }; class ClearCommand : public ClearBase { public: ClearCommand (Module *me) : ClearBase (me, "CLEAR", 0) { } int CheckAccess (User *user, Channel *chan) { if (!chan->HasUser (user)) { user->WriteNumeric (ERR_NOTONCHANNEL, "%s %s :you're not on that channel", user->nick.c_str ( ), chan->name.c_str ( )); return -1; } /* allow using the command with access checking */ return 1; } }; class ClearModule : public Module { SaclearCommand saclearcmd; ClearCommand clearcmd; public: ClearModule ( ) : saclearcmd (this), clearcmd (this) { } Version GetVersion ( ) { return Version("Provides commands to clear a given list mode or kick all users", VF_VENDOR); } void init ( ) { ServerInstance->Modules->AddService (saclearcmd); ServerInstance->Modules->AddService (clearcmd); } }; MODULE_INIT(ClearModule)