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