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