]> git.netwichtig.de Git - user/henk/code/inspircd.git/blob - src/modules/m_chanprotect.cpp
95bb06a13c23ee57a437b9d6bf3d9059da453a37
[user/henk/code/inspircd.git] / src / modules / m_chanprotect.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 #include "users.h"
18 #include "channels.h"
19 #include "modules.h"
20 #include "helperfuncs.h"
21
22 /* $ModDesc: Provides channel modes +a and +q */
23
24 const char* fakevalue = "on";
25
26 class ChanFounder : public ModeHandler
27 {
28         Server* Srv;
29         char* dummyptr;
30
31  public:
32         ChanFounder(Server* s) : ModeHandler('q', 1, 1, true, MODETYPE_CHANNEL, false), Srv(s) { }
33
34         ModePair ModeSet(userrec* source, userrec* dest, chanrec* channel, const std::string &parameter)
35         {
36                 userrec* x = Find(parameter);
37                 if (x)
38                 {
39                         if (!channel->HasUser(x))
40                         {
41                                 return std::make_pair(false, parameter);
42                         }
43                         else
44                         {
45                                 std::string founder = "cm_founder_"+std::string(channel->name);
46                                 if (x->GetExt(founder,dummyptr))
47                                 {
48                                         return std::make_pair(true, x->nick);
49                                 }
50                                 else
51                                 {
52                                         return std::make_pair(false, parameter);
53                                 }
54                         }
55                 }
56                 return std::make_pair(false, parameter);
57         }
58
59
60         ModeAction OnModeChange(userrec* source, userrec* dest, chanrec* channel, std::string &parameter, bool adding)
61         {
62                 userrec* theuser = Srv->FindNick(parameter);
63
64                 // cant find the user given as the parameter, eat the mode change.
65                 if (!theuser)
66                 {
67                         parameter = "";
68                         return MODEACTION_DENY;
69                 }
70
71                 // given user isnt even on the channel, eat the mode change
72                 if (!channel->HasUser(theuser))
73                 {
74                         parameter = "";
75                         return MODEACTION_DENY;
76                 }
77
78                 std::string protect = "cm_protect_"+std::string(channel->name);
79                 std::string founder = "cm_founder_"+std::string(channel->name);
80
81                  // source is a server, or ulined, we'll let them +-q the user.
82                 if ((Srv->IsUlined(source->nick)) || (Srv->IsUlined(source->server)) || (!*source->server))
83                 {
84                         if (adding)
85                         {
86                                 if (!theuser->GetExt(founder,dummyptr))
87                                 {
88                                         theuser->Extend(founder,fakevalue);
89                                         // Tidy the nickname (make case match etc)
90                                         parameter = theuser->nick;
91                                         return MODEACTION_ALLOW;
92                                 }
93                         }
94                         else
95                         {
96                                 if (theuser->GetExt(founder, dummyptr))
97                                 {
98                                         theuser->Shrink(founder);
99                                         // Tidy the nickname (make case match etc)
100                                         parameter = theuser->nick;
101                                         return MODEACTION_ALLOW;
102                                 }
103                         }
104                         return MODEACTION_DENY;
105                 }
106                 else
107                 {
108                         // whoops, someones being naughty!
109                         WriteServ(source->fd,"468 %s %s :Only servers may set channel mode +q",source->nick, channel->name);
110                         parameter = "";
111                         return MODEACTION_DENY;
112                 }
113         }
114
115         void DisplayList(userrec* user, chanrec* channel)
116         {
117                 chanuserlist cl = Srv->GetUsers(channel);
118                 std::string founder = "cm_founder_"+std::string(channel->name);
119                 for (unsigned int i = 0; i < cl.size(); i++)
120                 {
121                         if (cl[i]->GetExt(founder, dummyptr))
122                         {
123                                 WriteServ(user->fd,"386 %s %s %s",user->nick, channel->name,cl[i]->nick);
124                         }
125                 }
126                 WriteServ(user->fd,"387 %s %s :End of channel founder list",user->nick, channel->name);
127         }
128
129 };
130
131 class ChanProtect : public ModeHandler
132 {
133         Server* Srv;
134         char* dummyptr;
135  public:
136         ChanProtect(Server* s) : ModeHandler('a', 1, 1, true, MODETYPE_CHANNEL, false), Srv(s) { }
137
138         ModePair ModeSet(userrec* source, userrec* dest, chanrec* channel, const std::string &parameter)
139         {
140                 userrec* x = Find(parameter);
141                 if (x)
142                 {
143                         if (!channel->HasUser(x))
144                         {
145                                 return std::make_pair(false, parameter);
146                         }
147                         else
148                         {
149                                 std::string founder = "cm_protect_"+std::string(channel->name);
150                                 if (x->GetExt(founder,dummyptr))
151                                 {
152                                         return std::make_pair(true, x->nick);
153                                 }
154                                 else
155                                 {
156                                         return std::make_pair(false, parameter);
157                                 }
158                         }
159                 }
160                 return std::make_pair(false, parameter);
161         }
162
163         ModeAction OnModeChange(userrec* source, userrec* dest, chanrec* channel, std::string &parameter, bool adding)
164         {
165                 userrec* theuser = Srv->FindNick(parameter);
166
167                 // cant find the user given as the parameter, eat the mode change.
168                 if (!theuser)
169                 {
170                         parameter = "";
171                         return MODEACTION_DENY;
172                 }
173
174                 // given user isnt even on the channel, eat the mode change
175                 if (!channel->HasUser(theuser))
176                 {
177                         parameter = "";
178                         return MODEACTION_DENY;
179                 }
180
181                 std::string protect = "cm_protect_"+std::string(channel->name);
182                 std::string founder = "cm_founder_"+std::string(channel->name);
183
184                 // source has +q, is a server, or ulined, we'll let them +-a the user.
185                 if ((Srv->IsUlined(source->nick)) || (Srv->IsUlined(source->server)) || (!*source->server) || (source->GetExt(founder,dummyptr)))
186                 {
187                         if (adding)
188                         {
189                                 if (!theuser->GetExt(protect,dummyptr))
190                                 {
191                                         theuser->Extend(protect,fakevalue);
192                                         // Tidy the nickname (make case match etc)
193                                         parameter = theuser->nick;
194                                         return MODEACTION_ALLOW;
195                                 }
196                         }
197                         else
198                         {
199                                 if (theuser->GetExt(protect,dummyptr))
200                                 {
201                                         theuser->Shrink(protect);
202                                         // Tidy the nickname (make case match etc)
203                                         parameter = theuser->nick;
204                                         return MODEACTION_ALLOW;
205                                 }
206                         }
207                         return MODEACTION_DENY;
208                 }
209                 else
210                 {
211                         // bzzzt, wrong answer!
212                         WriteServ(source->fd,"482 %s %s :You are not a channel founder",source->nick, channel->name);
213                         return MODEACTION_DENY;
214                 }
215         }
216
217         virtual void DisplayList(userrec* user, chanrec* channel)
218         {
219                 chanuserlist cl = Srv->GetUsers(channel);
220                 std::string protect = "cm_protect_"+std::string(channel->name);
221                 for (unsigned int i = 0; i < cl.size(); i++)
222                 {
223                         if (cl[i]->GetExt(protect,dummyptr))
224                         {
225                                 WriteServ(user->fd,"388 %s %s %s",user->nick, channel->name,cl[i]->nick);
226                         }
227                 }
228                 WriteServ(user->fd,"389 %s %s :End of channel protected user list",user->nick, channel->name);
229         }
230
231 };
232
233 class ModuleChanProtect : public Module
234 {
235         Server *Srv;
236         bool FirstInGetsFounder;
237         ChanProtect* cp;
238         ChanFounder* cf;
239         char* dummyptr;
240         
241  public:
242  
243         ModuleChanProtect(Server* Me) : Module::Module(Me), Srv(Me)
244         {       
245                 /* Initialise module variables */
246
247                 cp = new ChanProtect(Me);
248                 cf = new ChanFounder(Me);
249                 
250                 Srv->AddMode(cp, 'a');
251                 Srv->AddMode(cf, 'q');
252                 
253                 /* Load config stuff */
254                 OnRehash("");
255         }
256
257         void Implements(char* List)
258         {
259                 List[I_On005Numeric] = List[I_OnUserKick] = List[I_OnUserPart] = List[I_OnRehash] = List[I_OnUserJoin] = List[I_OnAccessCheck] = List[I_OnSyncChannel] = 1;
260         }
261         
262         virtual void On005Numeric(std::string &output)
263         {
264                 InsertMode(output,"qa",1);
265         }
266
267         virtual void OnUserKick(userrec* source, userrec* user, chanrec* chan, const std::string &reason)
268         {
269                 // FIX: when someone gets kicked from a channel we must remove their Extensibles!
270                 user->Shrink("cm_founder_"+std::string(chan->name));
271                 user->Shrink("cm_protect_"+std::string(chan->name));
272         }
273
274         virtual void OnUserPart(userrec* user, chanrec* channel, const std::string &partreason)
275         {
276                 // FIX: when someone parts a channel we must remove their Extensibles!
277                 user->Shrink("cm_founder_"+std::string(channel->name));
278                 user->Shrink("cm_protect_"+std::string(channel->name));
279         }
280
281         virtual void OnRehash(const std::string &parameter)
282         {
283                 /* Create a configreader class and read our flag,
284                  * in old versions this was heap-allocated and the
285                  * object was kept between rehashes...now we just
286                  * stack-allocate it locally.
287                  */
288                 ConfigReader Conf;
289                 
290                 FirstInGetsFounder = Conf.ReadFlag("options","noservices",0);
291         }
292         
293         virtual void OnUserJoin(userrec* user, chanrec* channel)
294         {
295                 // if the user is the first user into the channel, mark them as the founder, but only if
296                 // the config option for it is set
297                 if (FirstInGetsFounder)
298                 {
299                         if (Srv->CountUsers(channel) == 1)
300                         {
301                                 // we're using Extensible::Extend to add data into user objects.
302                                 // this way is best as it adds data thats accessible to other modules
303                                 // (so long as you document your code properly) without breaking anything
304                                 // because its encapsulated neatly in a map.
305
306                                 // Change requested by katsklaw... when the first in is set to get founder,
307                                 // to make it clearer that +q has been given, send that one user the +q notice
308                                 // so that their client's syncronization and their sanity are left intact.
309                                 WriteServ(user->fd,"MODE %s +q %s",channel->name,user->nick);
310                                 if (user->Extend("cm_founder_"+std::string(channel->name),fakevalue))
311                                 {
312                                         Srv->Log(DEBUG,"Marked user "+std::string(user->nick)+" as founder for "+std::string(channel->name));
313                                 }
314                         }
315                 }
316         }
317         
318         virtual int OnAccessCheck(userrec* source,userrec* dest,chanrec* channel,int access_type)
319         {
320                 // here we perform access checks, this is the important bit that actually stops kicking/deopping
321                 // etc of protected users. There are many types of access check, we're going to handle
322                 // a relatively small number of them relevent to our module using a switch statement.
323         
324                 // don't allow action if:
325                 // (A) Theyre founder (no matter what)
326                 // (B) Theyre protected, and you're not
327                 // always allow the action if:
328                 // (A) The source is ulined
329                 
330                 
331                 // firstly, if a ulined nick, or a server, is setting the mode, then allow them to set the mode
332                 // without any access checks, we're not worthy :p
333                 if ((Srv->IsUlined(source->nick)) || (Srv->IsUlined(source->server)) || (!strcmp(source->server,"")))
334                 {
335                         return ACR_ALLOW;
336                 }
337
338                 std::string founder = "cm_founder_"+std::string(channel->name);
339                 std::string protect = "cm_protect_"+std::string(channel->name);
340
341                 switch (access_type)
342                 {
343                         // a user has been deopped. Do we let them? hmmm...
344                         case AC_DEOP:
345                                 if (dest->GetExt(founder,dummyptr))
346                                 {
347                                         Srv->SendServ(source->fd,"484 "+std::string(source->nick)+" "+std::string(channel->name)+" :Can't deop "+std::string(dest->nick)+" as they're a channel founder");
348                                         return ACR_DENY;
349                                 }
350                                 if ((dest->GetExt(protect,dummyptr)) && (!source->GetExt(protect,dummyptr)))
351                                 {
352                                         Srv->SendServ(source->fd,"484 "+std::string(source->nick)+" "+std::string(channel->name)+" :Can't deop "+std::string(dest->nick)+" as they're protected (+a)");
353                                         return ACR_DENY;
354                                 }
355                         break;
356
357                         // a user is being kicked. do we chop off the end of the army boot?
358                         case AC_KICK:
359                                 if (dest->GetExt(founder,dummyptr))
360                                 {
361                                         Srv->SendServ(source->fd,"484 "+std::string(source->nick)+" "+std::string(channel->name)+" :Can't kick "+std::string(dest->nick)+" as they're a channel founder");
362                                         return ACR_DENY;
363                                 }
364                                 if ((dest->GetExt(protect,dummyptr)) && (!source->GetExt(protect,dummyptr)))
365                                 {
366                                         Srv->SendServ(source->fd,"484 "+std::string(source->nick)+" "+std::string(channel->name)+" :Can't kick "+std::string(dest->nick)+" as they're protected (+a)");
367                                         return ACR_DENY;
368                                 }
369                         break;
370
371                         // a user is being dehalfopped. Yes, we do disallow -h of a +ha user
372                         case AC_DEHALFOP:
373                                 if (dest->GetExt(founder,dummyptr))
374                                 {
375                                         Srv->SendServ(source->fd,"484 "+std::string(source->nick)+" "+std::string(channel->name)+" :Can't de-halfop "+std::string(dest->nick)+" as they're a channel founder");
376                                         return ACR_DENY;
377                                 }
378                                 if ((dest->GetExt(protect,dummyptr)) && (!source->GetExt(protect,dummyptr)))
379                                 {
380                                         Srv->SendServ(source->fd,"484 "+std::string(source->nick)+" "+std::string(channel->name)+" :Can't de-halfop "+std::string(dest->nick)+" as they're protected (+a)");
381                                         return ACR_DENY;
382                                 }
383                         break;
384
385                         // same with devoice.
386                         case AC_DEVOICE:
387                                 if (dest->GetExt(founder,dummyptr))
388                                 {
389                                         Srv->SendServ(source->fd,"484 "+std::string(source->nick)+" "+std::string(channel->name)+" :Can't devoice "+std::string(dest->nick)+" as they're a channel founder");
390                                         return ACR_DENY;
391                                 }
392                                 if ((dest->GetExt(protect,dummyptr)) && (!source->GetExt(protect,dummyptr)))
393                                 {
394                                         Srv->SendServ(source->fd,"484 "+std::string(source->nick)+" "+std::string(channel->name)+" :Can't devoice "+std::string(dest->nick)+" as they're protected (+a)");
395                                         return ACR_DENY;
396                                 }
397                         break;
398                 }
399                 
400                 // we dont know what this access check is, or dont care. just carry on, nothing to see here.
401                 return ACR_DEFAULT;
402         }
403         
404         virtual ~ModuleChanProtect()
405         {
406                 DELETE(cp);
407                 DELETE(cf);
408         }
409         
410         virtual Version GetVersion()
411         {
412                 return Version(1,0,0,0,VF_STATIC|VF_VENDOR);
413         }
414         
415         virtual void OnSyncChannel(chanrec* chan, Module* proto, void* opaque)
416         {
417                 // this is called when the server is linking into a net and wants to sync channel data.
418                 // we should send our mode changes for the channel here to ensure that other servers
419                 // know whos +q/+a on the channel.
420                 chanuserlist cl = Srv->GetUsers(chan);
421                 string_list commands;
422                 std::string founder = "cm_founder_"+std::string(chan->name);
423                 std::string protect = "cm_protect_"+std::string(chan->name);
424                 for (unsigned int i = 0; i < cl.size(); i++)
425                 {
426                         if (cl[i]->GetExt(founder,dummyptr))
427                         {
428                                 proto->ProtoSendMode(opaque,TYPE_CHANNEL,chan,"+q "+std::string(cl[i]->nick));
429                         }
430                         if (cl[i]->GetExt(protect,dummyptr))
431                         {
432                                 proto->ProtoSendMode(opaque,TYPE_CHANNEL,chan,"+a "+std::string(cl[i]->nick));
433                         }
434                 }
435         }
436
437 };
438
439
440 class ModuleChanProtectFactory : public ModuleFactory
441 {
442  public:
443         ModuleChanProtectFactory()
444         {
445         }
446         
447         ~ModuleChanProtectFactory()
448         {
449         }
450         
451         virtual Module * CreateModule(Server* Me)
452         {
453                 return new ModuleChanProtect(Me);
454         }
455         
456 };
457
458
459 extern "C" void * init_module( void )
460 {
461         return new ModuleChanProtectFactory;
462 }