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