]> git.netwichtig.de Git - user/henk/code/inspircd.git/blob - src/modules/m_remove.cpp
Fix for parameters which contain a colon (which is not the first char in the string)
[user/henk/code/inspircd.git] / src / modules / m_remove.cpp
1 /*       +------------------------------------+
2  *       | Inspire Internet Relay Chat Daemon |
3  *       +------------------------------------+
4  *
5  *  InspIRCd is copyright (C) 2002-2006 ChatSpike-Dev.
6  *                     E-mail:
7  *              <brain@chatspike.net>
8  *              <Craig@chatspike.net>
9  *
10  * Written by Craig Edwards, Craig McLure, and others.
11  * This program is free but copyrighted software; see
12  * the file COPYING for details.
13  *
14  * ---------------------------------------------------
15  */
16
17 /* Support for a dancer-style /remove command, an alternative to /kick to try and avoid auto-rejoin-on-kick scripts */
18 /* Written by Om, 25-03-05 */
19
20 #include <sstream>
21 #include "users.h"
22 #include "channels.h"
23 #include "modules.h"
24 #include "configreader.h"
25 #include "inspircd.h"
26
27 /* $ModDesc: Provides a /remove command, this is mostly an alternative to /kick, except makes users appear to have parted the channel */
28
29 /*      
30  * This module supports the use of the +q and +a usermodes, but should work without them too.
31  * Usage of the command is restricted to +hoaq, and you cannot remove a user with a "higher" level than yourself.
32  * eg: +h can remove +hv and users with no modes. +a can remove +aohv and users with no modes.
33 */
34
35 class RemoveBase
36 {
37  private: 
38         bool& supportnokicks;
39         InspIRCd* ServerInstance;
40  
41  protected:
42         RemoveBase(InspIRCd* Instance, bool& snk) : supportnokicks(snk), ServerInstance(Instance)
43         {
44         }               
45  
46         enum ModeLevel { PEON = 0, HALFOP = 1, OP = 2, ADMIN = 3, OWNER = 4, ULINE = 5 };        
47  
48         /* This little function just converts a chanmode character (U ~ & @ & +) into an integer (5 4 3 2 1 0) */
49         /* XXX - this could be handy in the core, so it can be used elsewhere */
50         ModeLevel chartolevel(const std::string &privs)
51         {
52                 if(privs.empty())
53                 {
54                         return PEON;
55                 }
56         
57                 switch (privs[0])
58                 {
59                         case 'U':
60                                 /* Ulined */
61                                 return ULINE;
62                         case '~':
63                                 /* Owner */
64                                 return OWNER;
65                         case '&':
66                                 /* Admin */
67                                 return ADMIN;
68                         case '@':
69                                 /* Operator */
70                                 return OP;
71                         case '%':
72                                 /* Halfop */
73                                 return HALFOP;
74                         default:
75                                 /* Peon */
76                                 return PEON;
77                 }
78         }
79         
80         void Handle (const char** parameters, int pcnt, userrec *user, bool neworder)
81         {
82                 const char* channame;
83                 const char* username;
84                 userrec* target;
85                 chanrec* channel;
86                 ModeLevel tlevel;
87                 ModeLevel ulevel;
88                 std::ostringstream reason;
89                 std::string protectkey;
90                 std::string founderkey;
91                 bool hasnokicks;
92                 
93                 /* Set these to the parameters needed, the new version of this module switches it's parameters around
94                  * supplying a new command with the new order while keeping the old /remove with the older order.
95                  * /remove <nick> <channel> [reason ...]
96                  * /fpart <channel> <nick> [reason ...]
97                  */
98                 channame = parameters[ neworder ? 0 : 1];
99                 username = parameters[ neworder ? 1 : 0];
100                 
101                 /* Look up the user we're meant to be removing from the channel */
102                 target = ServerInstance->FindNick(username);
103                 
104                 /* And the channel we're meant to be removing them from */
105                 channel = ServerInstance->FindChan(channame);
106
107                 /* Fix by brain - someone needs to learn to validate their input! */
108                 if (!target || !channel)
109                 {
110                         user->WriteServ("401 %s %s :No such nick/channel", user->nick, !target ? username : channame);
111                         return;
112                 }
113
114                 if (!channel->HasUser(target))
115                 {
116                         user->WriteServ( "NOTICE %s :*** The user %s is not on channel %s", user->nick, target->nick, channel->name);
117                         return;
118                 }       
119                 
120                 /* This is adding support for the +q and +a channel modes, basically if they are enabled, and the remover has them set.
121                  * Then we change the @|%|+ to & if they are +a, or ~ if they are +q */
122                 protectkey = "cm_protect_" + std::string(channel->name);
123                 founderkey = "cm_founder_" + std::string(channel->name);
124                 
125                 if (ServerInstance->ULine(user->server) || ServerInstance->ULine(user->nick))
126                 {
127                         ServerInstance->Log(DEBUG, "Setting ulevel to U");
128                         ulevel = chartolevel("U");
129                 }
130                 if (user->GetExt(founderkey))
131                 {
132                         ServerInstance->Log(DEBUG, "Setting ulevel to ~");
133                         ulevel = chartolevel("~");
134                 }
135                 else if (user->GetExt(protectkey))
136                 {
137                         ServerInstance->Log(DEBUG, "Setting ulevel to &");
138                         ulevel = chartolevel("&");
139                 }
140                 else
141                 {
142                         ServerInstance->Log(DEBUG, "Setting ulevel to %s", channel->GetPrefixChar(user));
143                         ulevel = chartolevel(channel->GetPrefixChar(user));
144                 }
145                         
146                 /* 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 */
147                 if (ServerInstance->ULine(target->server) || ServerInstance->ULine(target->nick))
148                 {
149                         ServerInstance->Log(DEBUG, "Setting tlevel to U");
150                         tlevel = chartolevel("U");
151                 }
152                 else if (target->GetExt(founderkey))
153                 {
154                         ServerInstance->Log(DEBUG, "Setting tlevel to ~");
155                         tlevel = chartolevel("~");
156                 }
157                 else if (target->GetExt(protectkey))
158                 {
159                         ServerInstance->Log(DEBUG, "Setting tlevel to &");
160                         tlevel = chartolevel("&");
161                 }
162                 else
163                 {
164                         ServerInstance->Log(DEBUG, "Setting tlevel to %s", channel->GetPrefixChar(target));
165                         tlevel = chartolevel(channel->GetPrefixChar(target));
166                 }
167                 
168                 hasnokicks = (ServerInstance->FindModule("m_nokicks.so") && channel->IsModeSet('Q'));
169                 
170                 /* 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 */
171                 if(!supportnokicks || !hasnokicks || (ulevel == ULINE))
172                 {
173                         /* We'll let everyone remove their level and below, eg:
174                          * ops can remove ops, halfops, voices, and those with no mode (no moders actually are set to 1)
175                          * a ulined target will get a higher level than it's possible for a /remover to get..so they're safe.
176                          * Nobody may remove a founder.
177                          */
178                         if ((ulevel > PEON) && (ulevel >= tlevel) && (tlevel != OWNER))
179                         {
180                                 std::string reasonparam;
181                                 
182                                 /* If a reason is given, use it */
183                                 if(pcnt > 2)
184                                 {
185                                          reason <<  ":";
186                                         
187                                         /* Use all the remaining parameters as the reason */
188                                         for(int i = 2; i < pcnt; i++)
189                                         {
190                                                 reason << " " << parameters[i];
191                                         }
192                                         
193                                         reasonparam = reason.str();
194                                         reason.clear();
195                                 }
196
197                                 /* Build up the part reason string. */
198                                 reason << "Removed by " << user->nick << reasonparam;
199
200                                 channel->WriteChannelWithServ(ServerInstance->Config->ServerName, "NOTICE %s :%s removed %s from the channel", channel->name, user->nick, target->nick);
201                                 target->WriteServ("NOTICE %s :*** %s removed you from %s with the message: %s", target->nick, user->nick, channel->name, reasonparam.c_str());
202
203                                 if (!channel->PartUser(target, reason.str().c_str()))
204                                         delete channel;
205                         }
206                         else
207                         {
208                                 user->WriteServ( "NOTICE %s :*** You do not have access to /remove %s from %s", user->nick, target->nick, channel->name);
209                         }
210                 }
211                 else
212                 {
213                         /* m_nokicks.so was loaded and +Q was set, block! */
214                         user->WriteServ( "484 %s %s :Can't remove user %s from channel (+Q set)", user->nick, channel->name, target->nick);
215                 }
216         }
217 };
218
219 class cmd_remove : public command_t, public RemoveBase
220 {
221  public:
222         cmd_remove(InspIRCd* Instance, bool& snk) : command_t(Instance, "REMOVE", 0, 2), RemoveBase(Instance, snk)
223         {
224                 this->source = "m_remove.so";
225                 syntax = "<nick> <channel> [<reason>]";
226         }
227         
228         void Handle (const char** parameters, int pcnt, userrec *user)
229         {
230                 RemoveBase::Handle(parameters, pcnt, user, false);
231         }
232 };
233
234 class cmd_fpart : public command_t, public RemoveBase
235 {
236  public:
237         cmd_fpart(InspIRCd* Instance, bool& snk) : command_t(Instance, "FPART", 0, 2), RemoveBase(Instance, snk)
238         {
239                 this->source = "m_remove.so";
240                 syntax = "<channel> <nick> [<reason>]";
241         }
242
243         void Handle (const char** parameters, int pcnt, userrec *user)
244         {
245                 RemoveBase::Handle(parameters, pcnt, user, true);
246         }
247 };
248
249 class ModuleRemove : public Module
250 {
251         cmd_remove* mycommand;
252         cmd_fpart* mycommand2;
253         bool supportnokicks;
254         
255         
256  public:
257         ModuleRemove(InspIRCd* Me)
258         : Module::Module(Me)
259         {
260                 mycommand = new cmd_remove(ServerInstance, supportnokicks);
261                 mycommand2 = new cmd_fpart(ServerInstance, supportnokicks);
262                 ServerInstance->AddCommand(mycommand);
263                 ServerInstance->AddCommand(mycommand2);
264                 OnRehash("");
265         }
266
267         void Implements(char* List)
268         {
269                 List[I_On005Numeric] = List[I_OnRehash] = 1;
270         }
271
272         virtual void On005Numeric(std::string &output)
273         {
274                 output.append(" REMOVE");
275         }
276         
277         virtual void OnRehash(const std::string&)
278         {
279                 ConfigReader conf(ServerInstance);
280                 supportnokicks = conf.ReadFlag("remove", "supportnokicks", 0);
281         }
282         
283         virtual ~ModuleRemove()
284         {
285                 delete mycommand;
286                 delete mycommand2;
287         }
288         
289         virtual Version GetVersion()
290         {
291                 return Version(1,0,1,0,VF_VENDOR);
292         }
293         
294 };
295
296 // stuff down here is the module-factory stuff. For basic modules you can ignore this.
297
298 class ModuleRemoveFactory : public ModuleFactory
299 {
300  public:
301         ModuleRemoveFactory()
302         {
303         }
304         
305         ~ModuleRemoveFactory()
306         {
307         }
308         
309         virtual Module * CreateModule(InspIRCd* Me)
310         {
311                 return new ModuleRemove(Me);
312         }
313         
314 };
315
316
317 extern "C" void * init_module( void )
318 {
319         return new ModuleRemoveFactory;
320 }