aboutsummaryrefslogtreecommitdiff
/*       +------------------------------------+
 *       | 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 = "<channel> {users [reason] || +<modeletter> [glob pattern] || <modename> [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<std::string> &params, 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)