]> git.netwichtig.de Git - user/henk/code/inspircd.git/blob - src/modules/m_chanprotect.cpp
Get rid of Server::GetUsers(chanrec) - a throwback to before chanrec could do this...
[user/henk/code/inspircd.git] / src / modules / m_chanprotect.cpp
1 /*       +------------------------------------+
2  *       | Inspire Internet Relay Chat Daemon |
3  *       +------------------------------------+
4  *
5  *  InspIRCd is copyright (C) 2002-2006 ChatSpike-Dev.
6  *                     E-mail:
7  *              <brain@chatspike.net>
8  *                <Craig@chatspike.net>
9  *     
10  * Written by Craig Edwards, Craig McLure, and others.
11  * This program is free but copyrighted software; see
12  *          the file COPYING for details.
13  *
14  * ---------------------------------------------------
15  */
16
17 #include "users.h"
18 #include "channels.h"
19 #include "modules.h"
20 #include "helperfuncs.h"
21
22 /* $ModDesc: Provides channel modes +a and +q */
23
24 const char* fakevalue = "on";
25
26 class ChanFounder : public ModeHandler
27 {
28         Server* Srv;
29         char* dummyptr;
30
31  public:
32         ChanFounder(Server* s) : ModeHandler('q', 1, 1, true, MODETYPE_CHANNEL, false), Srv(s) { }
33
34         ModePair ModeSet(userrec* source, userrec* dest, chanrec* channel, const std::string &parameter)
35         {
36                 userrec* x = Find(parameter);
37                 if (x)
38                 {
39                         if (!channel->HasUser(x))
40                         {
41                                 return std::make_pair(false, parameter);
42                         }
43                         else
44                         {
45                                 std::string founder = "cm_founder_"+std::string(channel->name);
46                                 if (x->GetExt(founder,dummyptr))
47                                 {
48                                         return std::make_pair(true, x->nick);
49                                 }
50                                 else
51                                 {
52                                         return std::make_pair(false, parameter);
53                                 }
54                         }
55                 }
56                 return std::make_pair(false, parameter);
57         }
58
59
60         ModeAction OnModeChange(userrec* source, userrec* dest, chanrec* channel, std::string &parameter, bool adding)
61         {
62                 userrec* theuser = Srv->FindNick(parameter);
63
64                 log(DEBUG,"ChanFounder::OnModeChange");
65
66                 // cant find the user given as the parameter, eat the mode change.
67                 if (!theuser)
68                 {
69                         log(DEBUG,"No such user in ChanFounder");
70                         parameter = "";
71                         return MODEACTION_DENY;
72                 }
73
74                 // given user isnt even on the channel, eat the mode change
75                 if (!channel->HasUser(theuser))
76                 {
77                         log(DEBUG,"Channel doesn't have user in ChanFounder");
78                         parameter = "";
79                         return MODEACTION_DENY;
80                 }
81
82                 std::string protect = "cm_protect_"+std::string(channel->name);
83                 std::string founder = "cm_founder_"+std::string(channel->name);
84
85                  // source is a server, or ulined, we'll let them +-q the user.
86                 if ((Srv->IsUlined(source->nick)) || (Srv->IsUlined(source->server)) || (!*source->server) || (!IS_LOCAL(source)))
87                 {
88                         log(DEBUG,"Allowing remote mode change in ChanFounder");
89                         if (adding)
90                         {
91                                 if (!theuser->GetExt(founder,dummyptr))
92                                 {
93                                         log(DEBUG,"Does not have the ext item in ChanFounder");
94                                         if (!theuser->Extend(founder,fakevalue))
95                                                 log(DEBUG,"COULD NOT EXTEND!!!");
96                                         // Tidy the nickname (make case match etc)
97                                         parameter = theuser->nick;
98                                         if (theuser->GetExt(founder, dummyptr))
99                                                 log(DEBUG,"Extended!");
100                                         else
101                                                 log(DEBUG,"Not extended :(");
102                                         return MODEACTION_ALLOW;
103                                 }
104                         }
105                         else
106                         {
107                                 if (theuser->GetExt(founder, dummyptr))
108                                 {
109                                         theuser->Shrink(founder);
110                                         // Tidy the nickname (make case match etc)
111                                         parameter = theuser->nick;
112                                         return MODEACTION_ALLOW;
113                                 }
114                         }
115                         return MODEACTION_DENY;
116                 }
117                 else
118                 {
119                         // whoops, someones being naughty!
120                         source->WriteServ("468 %s %s :Only servers may set channel mode +q",source->nick, channel->name);
121                         parameter = "";
122                         return MODEACTION_DENY;
123                 }
124         }
125
126         void DisplayList(userrec* user, chanrec* channel)
127         {
128                 CUList* cl = channel->GetUsers();
129                 std::string founder = "cm_founder_"+std::string(channel->name);
130                 for (CUList::iterator i = cl->begin(); i != cl->end(); i++)
131                 {
132                         if (i->second->GetExt(founder, dummyptr))
133                         {
134                                 user->WriteServ("386 %s %s %s",user->nick, channel->name,i->second->nick);
135                         }
136                 }
137                 user->WriteServ("387 %s %s :End of channel founder list",user->nick, channel->name);
138         }
139
140 };
141
142 class ChanProtect : public ModeHandler
143 {
144         Server* Srv;
145         char* dummyptr;
146  public:
147         ChanProtect(Server* s) : ModeHandler('a', 1, 1, true, MODETYPE_CHANNEL, false), Srv(s) { }
148
149         ModePair ModeSet(userrec* source, userrec* dest, chanrec* channel, const std::string &parameter)
150         {
151                 userrec* x = Find(parameter);
152                 if (x)
153                 {
154                         if (!channel->HasUser(x))
155                         {
156                                 return std::make_pair(false, parameter);
157                         }
158                         else
159                         {
160                                 std::string founder = "cm_protect_"+std::string(channel->name);
161                                 if (x->GetExt(founder,dummyptr))
162                                 {
163                                         return std::make_pair(true, x->nick);
164                                 }
165                                 else
166                                 {
167                                         return std::make_pair(false, parameter);
168                                 }
169                         }
170                 }
171                 return std::make_pair(false, parameter);
172         }
173
174         ModeAction OnModeChange(userrec* source, userrec* dest, chanrec* channel, std::string &parameter, bool adding)
175         {
176                 userrec* theuser = Srv->FindNick(parameter);
177
178                 // cant find the user given as the parameter, eat the mode change.
179                 if (!theuser)
180                 {
181                         parameter = "";
182                         return MODEACTION_DENY;
183                 }
184
185                 // given user isnt even on the channel, eat the mode change
186                 if (!channel->HasUser(theuser))
187                 {
188                         parameter = "";
189                         return MODEACTION_DENY;
190                 }
191
192                 std::string protect = "cm_protect_"+std::string(channel->name);
193                 std::string founder = "cm_founder_"+std::string(channel->name);
194
195                 // source has +q, is a server, or ulined, we'll let them +-a the user.
196                 if ((Srv->IsUlined(source->nick)) || (Srv->IsUlined(source->server)) || (!*source->server) || (source->GetExt(founder,dummyptr)) || (!IS_LOCAL(source)))
197                 {
198                         if (adding)
199                         {
200                                 if (!theuser->GetExt(protect,dummyptr))
201                                 {
202                                         theuser->Extend(protect,fakevalue);
203                                         // Tidy the nickname (make case match etc)
204                                         parameter = theuser->nick;
205                                         return MODEACTION_ALLOW;
206                                 }
207                         }
208                         else
209                         {
210                                 if (theuser->GetExt(protect,dummyptr))
211                                 {
212                                         theuser->Shrink(protect);
213                                         // Tidy the nickname (make case match etc)
214                                         parameter = theuser->nick;
215                                         return MODEACTION_ALLOW;
216                                 }
217                         }
218                         return MODEACTION_DENY;
219                 }
220                 else
221                 {
222                         // bzzzt, wrong answer!
223                         source->WriteServ("482 %s %s :You are not a channel founder",source->nick, channel->name);
224                         return MODEACTION_DENY;
225                 }
226         }
227
228         virtual void DisplayList(userrec* user, chanrec* channel)
229         {
230                 CUList* cl = channel->GetUsers();
231                 std::string protect = "cm_protect_"+std::string(channel->name);
232                 for (CUList::iterator i = cl->begin(); i != cl->end(); i++)
233                 {
234                         if (i->second->GetExt(protect,dummyptr))
235                         {
236                                 user->WriteServ("388 %s %s %s",user->nick, channel->name,i->second->nick);
237                         }
238                 }
239                 user->WriteServ("389 %s %s :End of channel protected user list",user->nick, channel->name);
240         }
241
242 };
243
244 class ModuleChanProtect : public Module
245 {
246         Server *Srv;
247         bool FirstInGetsFounder;
248         ChanProtect* cp;
249         ChanFounder* cf;
250         char* dummyptr;
251         
252  public:
253  
254         ModuleChanProtect(Server* Me) : Module::Module(Me), Srv(Me)
255         {       
256                 /* Initialise module variables */
257
258                 cp = new ChanProtect(Me);
259                 cf = new ChanFounder(Me);
260                 
261                 Srv->AddMode(cp, 'a');
262                 Srv->AddMode(cf, 'q');
263                 
264                 /* Load config stuff */
265                 OnRehash("");
266         }
267
268         void Implements(char* List)
269         {
270                 List[I_On005Numeric] = List[I_OnUserKick] = List[I_OnUserPart] = List[I_OnRehash] = List[I_OnUserJoin] = List[I_OnAccessCheck] = List[I_OnSyncChannel] = 1;
271         }
272         
273         virtual void On005Numeric(std::string &output)
274         {
275                 InsertMode(output,"qa",1);
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;
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 (Srv->CountUsers(channel) == 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                                         Srv->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                 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 ((Srv->IsUlined(source->nick)) || (Srv->IsUlined(source->server)) || (!strcmp(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                                 log(DEBUG,"OnAccessCheck AC_DEOP");
358                                 if (dest->GetExt(founder,dummyptr))
359                                 {
360                                         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                                         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                                 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(Server* 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 }