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