]> git.netwichtig.de Git - user/henk/code/inspircd.git/blob - src/modules/m_remove.cpp
Use gmtime() not localtime() as RFC2616 says dates sent by a http server must be...
[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         CmdResult 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 CMD_FAILURE;
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 CMD_FAILURE;
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                                 return CMD_FAILURE;
210                         }
211                 }
212                 else
213                 {
214                         /* m_nokicks.so was loaded and +Q was set, block! */
215                         user->WriteServ( "484 %s %s :Can't remove user %s from channel (+Q set)", user->nick, channel->name, target->nick);
216                         return CMD_FAILURE;
217                 }
218
219                 return CMD_SUCCESS;
220         }
221 };
222
223 class cmd_remove : public command_t, public RemoveBase
224 {
225  public:
226         cmd_remove(InspIRCd* Instance, bool& snk) : command_t(Instance, "REMOVE", 0, 2), RemoveBase(Instance, snk)
227         {
228                 this->source = "m_remove.so";
229                 syntax = "<nick> <channel> [<reason>]";
230         }
231         
232         CmdResult Handle (const char** parameters, int pcnt, userrec *user)
233         {
234                 return RemoveBase::Handle(parameters, pcnt, user, false);
235         }
236 };
237
238 class cmd_fpart : public command_t, public RemoveBase
239 {
240  public:
241         cmd_fpart(InspIRCd* Instance, bool& snk) : command_t(Instance, "FPART", 0, 2), RemoveBase(Instance, snk)
242         {
243                 this->source = "m_remove.so";
244                 syntax = "<channel> <nick> [<reason>]";
245         }
246
247         CmdResult Handle (const char** parameters, int pcnt, userrec *user)
248         {
249                 return RemoveBase::Handle(parameters, pcnt, user, true);
250         }
251 };
252
253 class ModuleRemove : public Module
254 {
255         cmd_remove* mycommand;
256         cmd_fpart* mycommand2;
257         bool supportnokicks;
258         
259         
260  public:
261         ModuleRemove(InspIRCd* Me)
262         : Module::Module(Me)
263         {
264                 mycommand = new cmd_remove(ServerInstance, supportnokicks);
265                 mycommand2 = new cmd_fpart(ServerInstance, supportnokicks);
266                 ServerInstance->AddCommand(mycommand);
267                 ServerInstance->AddCommand(mycommand2);
268                 OnRehash("");
269         }
270
271         void Implements(char* List)
272         {
273                 List[I_On005Numeric] = List[I_OnRehash] = 1;
274         }
275
276         virtual void On005Numeric(std::string &output)
277         {
278                 output.append(" REMOVE");
279         }
280         
281         virtual void OnRehash(const std::string&)
282         {
283                 ConfigReader conf(ServerInstance);
284                 supportnokicks = conf.ReadFlag("remove", "supportnokicks", 0);
285         }
286         
287         virtual ~ModuleRemove()
288         {
289                 delete mycommand;
290                 delete mycommand2;
291         }
292         
293         virtual Version GetVersion()
294         {
295                 return Version(1,0,1,0,VF_VENDOR);
296         }
297         
298 };
299
300 // stuff down here is the module-factory stuff. For basic modules you can ignore this.
301
302 class ModuleRemoveFactory : public ModuleFactory
303 {
304  public:
305         ModuleRemoveFactory()
306         {
307         }
308         
309         ~ModuleRemoveFactory()
310         {
311         }
312         
313         virtual Module * CreateModule(InspIRCd* Me)
314         {
315                 return new ModuleRemove(Me);
316         }
317         
318 };
319
320
321 extern "C" void * init_module( void )
322 {
323         return new ModuleRemoveFactory;
324 }