The InspIRCd Project
Home | Developers | Wiki | Forums | Bug Tracker | SVN | Download
Main Page | Namespace List | Class Hierarchy | Alphabetical List | Class List | Directories | File List | Namespace Members | Class Members | File Members

m_remove.cpp

Go to the documentation of this file.
00001 /*       +------------------------------------+
00002  *       | Inspire Internet Relay Chat Daemon |
00003  *       +------------------------------------+
00004  *
00005  *  InspIRCd: (C) 2002-2008 InspIRCd Development Team
00006  * See: http://www.inspircd.org/wiki/index.php/Credits
00007  *
00008  * This program is free but copyrighted software; see
00009  *            the file COPYING for details.
00010  *
00011  * ---------------------------------------------------
00012  */
00013 
00014 #include "inspircd.h"
00015 
00016 /* $ModDesc: Provides a /remove command, this is mostly an alternative to /kick, except makes users appear to have parted the channel */
00017 
00018 /*
00019  * This module supports the use of the +q and +a usermodes, but should work without them too.
00020  * Usage of the command is restricted to +hoaq, and you cannot remove a user with a "higher" level than yourself.
00021  * eg: +h can remove +hv and users with no modes. +a can remove +aohv and users with no modes.
00022 */
00023 
00026 class RemoveBase
00027 {
00028  private:
00029         bool& supportnokicks;
00030         InspIRCd* ServerInstance;
00031 
00032  protected:
00033         RemoveBase(InspIRCd* Instance, bool& snk) : supportnokicks(snk), ServerInstance(Instance)
00034         {
00035         }
00036 
00037         enum ModeLevel { PEON = 0, HALFOP = 1, OP = 2, ADMIN = 3, OWNER = 4, ULINE = 5 };
00038 
00039         /* This little function just converts a chanmode character (U ~ & @ & +) into an integer (5 4 3 2 1 0) */
00040         /* XXX - We should probably use the new mode prefix rank stuff
00041          * for this instead now -- Brain */
00042         ModeLevel chartolevel(const std::string &privs)
00043         {
00044                 if(privs.empty())
00045                 {
00046                         return PEON;
00047                 }
00048 
00049                 switch (privs[0])
00050                 {
00051                         case 'U':
00052                                 /* Ulined */
00053                                 return ULINE;
00054                         case '~':
00055                                 /* Owner */
00056                                 return OWNER;
00057                         case '&':
00058                                 /* Admin */
00059                                 return ADMIN;
00060                         case '@':
00061                                 /* Operator */
00062                                 return OP;
00063                         case '%':
00064                                 /* Halfop */
00065                                 return HALFOP;
00066                         default:
00067                                 /* Peon */
00068                                 return PEON;
00069                 }
00070         }
00071 
00072         CmdResult Handle (const std::vector<std::string>& parameters, User *user, bool neworder)
00073         {
00074                 const char* channame;
00075                 const char* username;
00076                 User* target;
00077                 Channel* channel;
00078                 ModeLevel tlevel;
00079                 ModeLevel ulevel;
00080                 std::string reason;
00081                 std::string protectkey;
00082                 std::string founderkey;
00083                 bool hasnokicks;
00084 
00085                 /* Set these to the parameters needed, the new version of this module switches it's parameters around
00086                  * supplying a new command with the new order while keeping the old /remove with the older order.
00087                  * /remove <nick> <channel> [reason ...]
00088                  * /fpart <channel> <nick> [reason ...]
00089                  */
00090                 channame = parameters[ neworder ? 0 : 1].c_str();
00091                 username = parameters[ neworder ? 1 : 0].c_str();
00092 
00093                 /* Look up the user we're meant to be removing from the channel */
00094                 target = ServerInstance->FindNick(username);
00095 
00096                 /* And the channel we're meant to be removing them from */
00097                 channel = ServerInstance->FindChan(channame);
00098 
00099                 /* Fix by brain - someone needs to learn to validate their input! */
00100                 if (!target || !channel)
00101                 {
00102                         user->WriteNumeric(ERR_NOSUCHNICK, "%s %s :No such nick/channel", user->nick.c_str(), !target ? username : channame);
00103                         return CMD_FAILURE;
00104                 }
00105 
00106                 if (!channel->HasUser(target))
00107                 {
00108                         user->WriteServ( "NOTICE %s :*** The user %s is not on channel %s", user->nick.c_str(), target->nick.c_str(), channel->name.c_str());
00109                         return CMD_FAILURE;
00110                 }
00111 
00112                 /* This is adding support for the +q and +a channel modes, basically if they are enabled, and the remover has them set.
00113                  * Then we change the @|%|+ to & if they are +a, or ~ if they are +q */
00114                 protectkey = "cm_protect_" + std::string(channel->name);
00115                 founderkey = "cm_founder_" + std::string(channel->name);
00116 
00117                 if (ServerInstance->ULine(user->server) || ServerInstance->ULine(user->nick.c_str()))
00118                 {
00119                         ulevel = chartolevel("U");
00120                 }
00121                 if (user->GetExt(founderkey))
00122                 {
00123                         ulevel = chartolevel("~");
00124                 }
00125                 else if (user->GetExt(protectkey))
00126                 {
00127                         ulevel = chartolevel("&");
00128                 }
00129                 else
00130                 {
00131                         ulevel = chartolevel(channel->GetPrefixChar(user));
00132                 }
00133 
00134                 /* Now it's the same idea, except for the target. If they're ulined make sure they get a higher level than the sender can */
00135                 if (ServerInstance->ULine(target->server) || ServerInstance->ULine(target->nick.c_str()))
00136                 {
00137                         tlevel = chartolevel("U");
00138                 }
00139                 else if (target->GetExt(founderkey))
00140                 {
00141                         tlevel = chartolevel("~");
00142                 }
00143                 else if (target->GetExt(protectkey))
00144                 {
00145                         tlevel = chartolevel("&");
00146                 }
00147                 else
00148                 {
00149                         tlevel = chartolevel(channel->GetPrefixChar(target));
00150                 }
00151 
00152                 hasnokicks = (ServerInstance->Modules->Find("m_nokicks.so") && channel->IsModeSet('Q'));
00153 
00154                 /* 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 */
00155                 if ((!IS_LOCAL(user)) || (!supportnokicks || !hasnokicks || (ulevel == ULINE)))
00156                 {
00157                         /* We'll let everyone remove their level and below, eg:
00158                          * ops can remove ops, halfops, voices, and those with no mode (no moders actually are set to 1)
00159                          * a ulined target will get a higher level than it's possible for a /remover to get..so they're safe.
00160                          * Nobody may remove a founder.
00161                          */
00162                         if ((!IS_LOCAL(user)) || ((ulevel > PEON) && (ulevel >= tlevel) && (tlevel != OWNER)))
00163                         {
00164                                 // no you can't just go from a std::ostringstream to a std::string, Om. -nenolod
00165                                 // but you can do this, nenolod -brain
00166 
00167                                 std::string reasonparam("No reason given");
00168 
00169                                 /* If a reason is given, use it */
00170                                 if(parameters.size() > 2)
00171                                 {
00172                                         /* Join params 2 ... pcnt - 1 (inclusive) into one */
00173                                         irc::stringjoiner reason_join(" ", parameters, 2, parameters.size() - 1);
00174                                         reasonparam = reason_join.GetJoined();
00175                                 }
00176 
00177                                 /* Build up the part reason string. */
00178                                 reason = std::string("Removed by ") + user->nick + ": " + reasonparam;
00179 
00180                                 channel->WriteChannelWithServ(ServerInstance->Config->ServerName, "NOTICE %s :%s removed %s from the channel", channel->name.c_str(), user->nick.c_str(), target->nick.c_str());
00181                                 target->WriteServ("NOTICE %s :*** %s removed you from %s with the message: %s", target->nick.c_str(), user->nick.c_str(), channel->name.c_str(), reasonparam.c_str());
00182 
00183                                 if (!channel->PartUser(target, reason))
00184                                         delete channel;
00185                         }
00186                         else
00187                         {
00188                                 user->WriteServ( "NOTICE %s :*** You do not have access to /remove %s from %s", user->nick.c_str(), target->nick.c_str(), channel->name.c_str());
00189                                 return CMD_FAILURE;
00190                         }
00191                 }
00192                 else
00193                 {
00194                         /* m_nokicks.so was loaded and +Q was set, block! */
00195                         user->WriteServ( "484 %s %s :Can't remove user %s from channel (+Q set)", user->nick.c_str(), channel->name.c_str(), target->nick.c_str());
00196                         return CMD_FAILURE;
00197                 }
00198 
00199                 /* route me */
00200                 return CMD_SUCCESS;
00201         }
00202 };
00203 
00206 class CommandRemove : public Command, public RemoveBase
00207 {
00208  public:
00209         CommandRemove(InspIRCd* Instance, bool& snk) : Command(Instance, "REMOVE", 0, 2), RemoveBase(Instance, snk)
00210         {
00211                 this->source = "m_remove.so";
00212                 syntax = "<nick> <channel> [<reason>]";
00213                 TRANSLATE4(TR_NICK, TR_TEXT, TR_TEXT, TR_END);
00214         }
00215 
00216         CmdResult Handle (const std::vector<std::string>& parameters, User *user)
00217         {
00218                 return RemoveBase::Handle(parameters, user, false);
00219         }
00220 };
00221 
00224 class CommandFpart : public Command, public RemoveBase
00225 {
00226  public:
00227         CommandFpart(InspIRCd* Instance, bool& snk) : Command(Instance, "FPART", 0, 2), RemoveBase(Instance, snk)
00228         {
00229                 this->source = "m_remove.so";
00230                 syntax = "<channel> <nick> [<reason>]";
00231         }
00232 
00233         CmdResult Handle (const std::vector<std::string>& parameters, User *user)
00234         {
00235                 return RemoveBase::Handle(parameters, user, true);
00236         }
00237 };
00238 
00239 class ModuleRemove : public Module
00240 {
00241         CommandRemove* mycommand;
00242         CommandFpart* mycommand2;
00243         bool supportnokicks;
00244 
00245 
00246  public:
00247         ModuleRemove(InspIRCd* Me)
00248         : Module(Me)
00249         {
00250                 mycommand = new CommandRemove(ServerInstance, supportnokicks);
00251                 mycommand2 = new CommandFpart(ServerInstance, supportnokicks);
00252                 ServerInstance->AddCommand(mycommand);
00253                 ServerInstance->AddCommand(mycommand2);
00254                 OnRehash(NULL,"");
00255                 Implementation eventlist[] = { I_On005Numeric, I_OnRehash };
00256                 ServerInstance->Modules->Attach(eventlist, this, 2);
00257         }
00258 
00259 
00260         virtual void On005Numeric(std::string &output)
00261         {
00262                 output.append(" REMOVE");
00263         }
00264 
00265         virtual void OnRehash(User* user, const std::string&)
00266         {
00267                 ConfigReader conf(ServerInstance);
00268                 supportnokicks = conf.ReadFlag("remove", "supportnokicks", 0);
00269         }
00270 
00271         virtual ~ModuleRemove()
00272         {
00273         }
00274 
00275         virtual Version GetVersion()
00276         {
00277                 return Version("$Id: m_remove.cpp 10291 2008-08-25 20:35:51Z w00t $", VF_COMMON | VF_VENDOR, API_VERSION);
00278         }
00279 
00280 };
00281 
00282 MODULE_INIT(ModuleRemove)