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