]> git.netwichtig.de Git - user/henk/code/inspircd.git/blobdiff - src/modules/m_remove.cpp
Fix a bunch of weird indentation and spacing issues.
[user/henk/code/inspircd.git] / src / modules / m_remove.cpp
index 5ec868554fc88fc9da4a1f45ed6c49deacf3b448..cd59f07bcf4f6191e9ebbbfb8f6e8ab318f88fb0 100644 (file)
-/* Support for a dancer-style /remove command, an alternative to /kick to try and avoid auto-rejoin-on-kick scripts */
-/* Written by Om, 25-03-05 */
-
-using namespace std;
-
-#include <stdio.h>
-#include <string>
-#include "users.h"
-#include "channels.h"
-#include "modules.h"
-
-/* $ModDesc: Provides a /remove command, this is mostly an alternative to /kick, except makes users appear to have parted the channel */
-
-/*     
+/*
+ * InspIRCd -- Internet Relay Chat Daemon
+ *
+ *   Copyright (C) 2017 B00mX0r <b00mx0r@aureus.pw>
+ *   Copyright (C) 2013, 2018, 2020 Sadie Powell <sadie@witchery.services>
+ *   Copyright (C) 2012-2014, 2016 Attila Molnar <attilamolnar@hush.com>
+ *   Copyright (C) 2012, 2019 Robby <robby@chatbelgie.be>
+ *   Copyright (C) 2012 Justin Crawford <Justasic@Gmail.com>
+ *   Copyright (C) 2009 Uli Schlachter <psychon@inspircd.org>
+ *   Copyright (C) 2009 Daniel De Graaf <danieldg@inspircd.org>
+ *   Copyright (C) 2008 Thomas Stagner <aquanight@inspircd.org>
+ *   Copyright (C) 2007 Robin Burchell <robin+git@viroteck.net>
+ *   Copyright (C) 2007 Dennis Friis <peavey@inspircd.org>
+ *   Copyright (C) 2006 Oliver Lupton <om@inspircd.org>
+ *   Copyright (C) 2005-2006, 2010 Craig Edwards <brain@inspircd.org>
+ *
+ * This file is part of InspIRCd.  InspIRCd is free software: you can
+ * redistribute it and/or modify it under the terms of the GNU General Public
+ * License as published by the Free Software Foundation, version 2.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+ * FOR A PARTICULAR PURPOSE.  See the GNU General Public License for more
+ * details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+
+#include "inspircd.h"
+
+/*
  * This module supports the use of the +q and +a usermodes, but should work without them too.
  * Usage of the command is restricted to +hoaq, and you cannot remove a user with a "higher" level than yourself.
  * eg: +h can remove +hv and users with no modes. +a can remove +aohv and users with no modes.
 */
 
-static Server *Srv;
-
-/* This little function just converts a chanmode character (~ & @ & +) into an integer (5 4 3 2 1) */
-/* XXX - this could be handy in the core, so it can be used elsewhere */
-int chartolevel(std::string &privs)
+/** Base class for /FPART and /REMOVE
+ */
+class RemoveBase : public Command
 {
-       const char* n = privs.c_str();
-
-       switch (*n)
-       {
-               case '~':
-                       return 5;
-               break;
-               case '&':
-                       return 4;
-               break;
-               case '@':
-                       return 3;
-               break;
-               case '%':
-                       return 2;
-               break;
-               default:
-                       return 1;
-               break;
-       }
-       return 1;
-}
+       bool& supportnokicks;
+       ChanModeReference& nokicksmode;
 
-class cmd_remove : public command_t
-{
  public:
-       cmd_remove () : command_t("REMOVE", 0, 2)
+       unsigned int protectedrank;
+
+       RemoveBase(Module* Creator, bool& snk, ChanModeReference& nkm, const char* cmdn)
+               : Command(Creator, cmdn, 2, 3)
+               , supportnokicks(snk)
+               , nokicksmode(nkm)
        {
-               this->source = "m_remove.so";
        }
 
-       void Handle (char **parameters, int pcnt, userrec *user)
+       CmdResult HandleRMB(User* user, const CommandBase::Params& parameters,  bool fpart)
        {
+               User* target;
+               Channel* channel;
+               std::string reason;
+
+               // If the command is a /REMOVE then detect the parameter order
+               bool neworder = ((fpart) || (parameters[0][0] == '#'));
+
+               /* Set these to the parameters needed, the new version of this module switches it's parameters around
+                * supplying a new command with the new order while keeping the old /remove with the older order.
+                * /remove <nick> <channel> [reason ...]
+                * /fpart <channel> <nick> [reason ...]
+                */
+               const std::string& channame = parameters[neworder ? 0 : 1];
+               const std::string& username = parameters[neworder ? 1 : 0];
+
                /* Look up the user we're meant to be removing from the channel */
-               userrec* target = Srv->FindNick(std::string(parameters[0]));
+               if (IS_LOCAL(user))
+                       target = ServerInstance->FindNickOnly(username);
+               else
+                       target = ServerInstance->FindNick(username);
+
                /* And the channel we're meant to be removing them from */
-               chanrec* channel = Srv->FindChannel(std::string(parameters[1]));
+               channel = ServerInstance->FindChan(channame);
 
                /* Fix by brain - someone needs to learn to validate their input! */
-               if (!target || !channel)
+               if (!channel)
+               {
+                       user->WriteNumeric(Numerics::NoSuchChannel(channame));
+                       return CMD_FAILURE;
+               }
+               if ((!target) || (target->registered != REG_ALL))
                {
-                       WriteServ(user->fd,"401 %s %s :No such nick/channel",user->nick, !target ? parameters[0] : parameters[1]);
-                       return;
+                       user->WriteNumeric(Numerics::NoSuchNick(username));
+                       return CMD_FAILURE;
                }
 
-               /* And see if the person calling the command has access to use it on the channel */
-               std::string privs = Srv->ChanMode(user, channel);
-               /* Check what privs the person being removed has */
-               std::string targetprivs = Srv->ChanMode(target, channel);
-
-               int tlevel;
-               int ulevel;
-               int n = 2;
-               std::string result;
-               
-               /* This turns all the parameters after the first two into a single string, so the part reason can be multi-word */
-               while (n < pcnt)
+               if (!channel->HasUser(target))
                {
-                       result=result + std::string(" ") + std::string(parameters[n]);
-                       n++;
+                       user->WriteNotice(InspIRCd::Format("*** User %s is not on channel %s", target->nick.c_str(), channel->name.c_str()));
+                       return CMD_FAILURE;
                }
-               
-               /* If the target nick exists... */
-               if (target && channel)
+
+               if (target->server->IsULine())
                {
-                       for (unsigned int x = 0; x < strlen(parameters[1]); x++)
-                       {
-                                       if ((parameters[1][0] != '#') || (parameters[1][x] == ' ') || (parameters[1][x] == ','))
-                                       {
-                                               Srv->SendTo(NULL,user,"NOTICE "+std::string(user->nick)+" :*** Invalid characters in channel name");
-                                               return;
-                                       }
-                       }
-                       
-                       /* This is adding support for the +q and +a channel modes, basically if they are enabled, and the remover has them set. */
-                       /* Then we change the @|%|+ to & if they are +a, or ~ if they are +q */
-                       if (user->GetExt("cm_protect_"+std::string(channel->name)))
-                               privs = std::string("&");
-                       if (user->GetExt("cm_founder_"+std::string(channel->name)))
-                               privs = std::string("~");
-                               
-                       /* Now it's the same idea, except for the target */
-                       if (target->GetExt("cm_protect_"+std::string(channel->name)))
-                               targetprivs = std::string("&");
-                       if (target->GetExt("cm_founder_"+std::string(channel->name)))
-                               targetprivs = std::string("~");
-                               
-                       tlevel = chartolevel(targetprivs);
-                       ulevel = chartolevel(privs);
-                       
-                       /* If the user calling the command is either an admin, owner, operator or a half-operator on the channel */
-                       if(ulevel > 1)
+                       user->WriteNumeric(ERR_CHANOPRIVSNEEDED, channame, "Only a U-line may remove a U-line from a channel.");
+                       return CMD_FAILURE;
+               }
+
+               /* We support the +Q channel mode via. the m_nokicks module, if the module is loaded and the mode is set then disallow the /remove */
+               if ((!IS_LOCAL(user)) || (!supportnokicks) || (!channel->IsModeSet(nokicksmode)))
+               {
+                       /* We'll let everyone remove their level and below, eg:
+                        * ops can remove ops, halfops, voices, and those with no mode (no moders actually are set to 1)
+                        * a ulined target will get a higher level than it's possible for a /remover to get..so they're safe.
+                        * Nobody may remove people with >= protectedrank rank.
+                        */
+                       unsigned int ulevel = channel->GetPrefixValue(user);
+                       unsigned int tlevel = channel->GetPrefixValue(target);
+                       if ((!IS_LOCAL(user)) || ((ulevel > VOICE_VALUE) && (ulevel >= tlevel) && ((protectedrank == 0) || (tlevel < protectedrank))))
                        {
-                               /* For now, we'll let everyone remove their level and below, eg ops can remove ops, halfops, voices, and those with no mode (no moders actually are set to 1) */
-                               if(ulevel >= tlevel)
+                               // REMOVE will be sent to the target's server and it will reply with a PART (or do nothing if it doesn't understand the command)
+                               if (!IS_LOCAL(target))
                                {
-                                       Srv->PartUserFromChannel(target,std::string(parameters[1]), "Removed by "+std::string(user->nick)+":"+result);
-                                       Srv->SendTo(NULL,user,"NOTICE "+std::string(channel->name)+" : "+std::string(user->nick)+" removed "+std::string(target->nick)+ " from the channel");
-                                       Srv->SendTo(NULL,target,"NOTICE "+std::string(target->nick)+" :*** "+std::string(user->nick)+" removed you from "+std::string(channel->name)+" with the message:"+std::string(result));
+                                       // Send an ENCAP REMOVE with parameters being in the old <user> <chan> order which is
+                                       // compatible with both 2.0 and 3.0. This also turns FPART into REMOVE.
+                                       CommandBase::Params p;
+                                       p.push_back(target->uuid);
+                                       p.push_back(channel->name);
+                                       if (parameters.size() > 2)
+                                               p.push_back(":" + parameters[2]);
+                                       ServerInstance->PI->SendEncapsulatedData(target->server->GetName(), "REMOVE", p, user);
+
+                                       return CMD_SUCCESS;
                                }
+
+                               std::string reasonparam;
+
+                               /* If a reason is given, use it */
+                               if(parameters.size() > 2)
+                                       reasonparam = parameters[2];
                                else
-                               {
-                                       Srv->SendTo(NULL,user,"NOTICE "+std::string(user->nick)+" :*** You do not have access to remove "+std::string(target->nick)+" from the "+std::string(channel->name));
-                               }
+                                       reasonparam = "No reason given";
+
+                               /* Build up the part reason string. */
+                               reason = "Removed by " + user->nick + ": " + reasonparam;
+
+                               channel->WriteRemoteNotice(InspIRCd::Format("%s removed %s from the channel", user->nick.c_str(), target->nick.c_str()));
+                               target->WriteNotice("*** " + user->nick + " removed you from " + channel->name + " with the message: " + reasonparam);
+
+                               channel->PartUser(target, reason);
                        }
                        else
                        {
-                               Srv->SendTo(NULL,user,"NOTICE "+std::string(user->nick)+" :*** You do not have access to use /remove on "+std::string(channel->name));
+                               user->WriteNotice(InspIRCd::Format("*** You do not have access to /REMOVE %s from %s", target->nick.c_str(), channel->name.c_str()));
+                               return CMD_FAILURE;
                        }
                }
+               else
+               {
+                       /* m_nokicks.so was loaded and +Q was set, block! */
+                       user->WriteNumeric(ERR_RESTRICTED, channel->name, InspIRCd::Format("Can't remove user %s from channel (+Q is set)", target->nick.c_str()));
+                       return CMD_FAILURE;
+               }
+
+               return CMD_SUCCESS;
        }
 };
 
-class ModuleRemove : public Module
+/** Handle /REMOVE
+ */
+class CommandRemove : public RemoveBase
 {
-       cmd_remove* mycommand;
  public:
-       ModuleRemove(Server* Me)
-               : Module::Module(Me)
+       CommandRemove(Module* Creator, bool& snk, ChanModeReference& nkm)
+               : RemoveBase(Creator, snk, nkm, "REMOVE")
        {
-               Srv = Me;
-               mycommand = new cmd_remove();
-               Srv->AddCommand(mycommand);
+               syntax = "<channel> <nick> [:<reason>]";
+               TRANSLATE3(TR_NICK, TR_TEXT, TR_TEXT);
        }
 
-       void Implements(char* List)
+       CmdResult Handle(User* user, const Params& parameters) CXX11_OVERRIDE
        {
-               List[I_On005Numeric] = 1;
+               return HandleRMB(user, parameters, false);
        }
+};
 
-        virtual void On005Numeric(std::string &output)
-        {
-                output = output + std::string(" REMOVE");
-        }
-       
-       virtual ~ModuleRemove()
+/** Handle /FPART
+ */
+class CommandFpart : public RemoveBase
+{
+ public:
+       CommandFpart(Module* Creator, bool& snk, ChanModeReference& nkm)
+               : RemoveBase(Creator, snk, nkm, "FPART")
        {
+               syntax = "<channel> <nick> [:<reason>]";
+               TRANSLATE3(TR_TEXT, TR_NICK, TR_TEXT);
        }
-       
-       virtual Version GetVersion()
+
+       CmdResult Handle(User* user, const Params& parameters) CXX11_OVERRIDE
        {
-               return Version(1,0,0,1,VF_VENDOR);
+               return HandleRMB(user, parameters, true);
        }
-       
 };
 
-// stuff down here is the module-factory stuff. For basic modules you can ignore this.
-
-class ModuleRemoveFactory : public ModuleFactory
+class ModuleRemove : public Module
 {
+       ChanModeReference nokicksmode;
+       CommandRemove cmd1;
+       CommandFpart cmd2;
+       bool supportnokicks;
+
  public:
-       ModuleRemoveFactory()
+       ModuleRemove()
+               : nokicksmode(this, "nokick")
+               , cmd1(this, supportnokicks, nokicksmode)
+               , cmd2(this, supportnokicks, nokicksmode)
+       {
+       }
+
+       void On005Numeric(std::map<std::string, std::string>& tokens) CXX11_OVERRIDE
        {
+               tokens["REMOVE"];
        }
-       
-       ~ModuleRemoveFactory()
+
+       void ReadConfig(ConfigStatus& status) CXX11_OVERRIDE
        {
+               ConfigTag* tag = ServerInstance->Config->ConfValue("remove");
+               supportnokicks = tag->getBool("supportnokicks");
+               cmd1.protectedrank = cmd2.protectedrank = tag->getUInt("protectedrank", 50000);
        }
-       
-       virtual Module * CreateModule(Server* Me)
+
+       Version GetVersion() CXX11_OVERRIDE
        {
-               return new ModuleRemove(Me);
+               return Version("Adds the /FPART and /REMOVE commands which allows channel operators to force part users from a channel.", VF_OPTCOMMON | VF_VENDOR);
        }
-       
 };
 
-
-extern "C" void * init_module( void )
-{
-       return new ModuleRemoveFactory;
-}
-
+MODULE_INIT(ModuleRemove)