]> git.netwichtig.de Git - user/henk/code/inspircd.git/blob - include/u_listmode.h
Add counter system for umodes to get rid of some O(n)
[user/henk/code/inspircd.git] / include / u_listmode.h
1 /*       +------------------------------------+
2  *       | Inspire Internet Relay Chat Daemon |
3  *       +------------------------------------+
4  *
5  *  InspIRCd: (C) 2002-2007 InspIRCd Development Team
6  * See: http://www.inspircd.org/wiki/index.php/Credits
7  *
8  * This program is free but copyrighted software; see
9  *            the file COPYING for details.
10  *
11  * ---------------------------------------------------
12  */
13
14 #ifndef INSPIRCD_LISTMODE_PROVIDER
15 #define INSPIRCD_LISTMODE_PROVIDER
16
17 #include <stdio.h>
18 #include <string>
19 #include <sstream>
20 #include <vector>
21 #include "users.h"
22 #include "channels.h"
23 #include "modules.h"
24 #include "wildcard.h"
25 #include "inspircd.h"
26
27 /* $ModDesc: Provides support for easily creating listmodes, stores the time set, the user, and a parameter. */
28
29 /* Updated to use the <banlist> config tag if it exists */
30 /* Written by Om <omster@gmail.com>, December 2005. */
31 /* Based on code previously written by Om - April 2005 */
32 /* Updated to new API July 8th 2006 by Brain */
33 /* Originally based on m_chanprotect and m_silence */
34
35 inline std::string stringtime()
36 {
37         std::ostringstream TIME;
38         TIME << time(NULL); 
39         return TIME.str();
40 }
41
42 /** An item in a listmode's list
43  */
44 class ListItem : public classbase
45 {
46 public:
47         std::string nick;
48         std::string mask;
49         std::string time;
50 };
51
52 /** The number of items a listmode's list may contain
53  */
54 class ListLimit : public classbase
55 {
56 public:
57         std::string mask;
58         unsigned int limit;
59 };
60
61 // Just defining the type we use for the exception list here...
62 typedef std::vector<ListItem> modelist;
63 typedef std::vector<ListLimit> limitlist;
64
65 /** The base class for listmodes defined by u_listmode.h
66  */
67 class ListModeBase : public ModeHandler
68 {
69  protected:
70         std::string infokey;
71         std::string listnumeric;
72         std::string endoflistnumeric;
73         std::string endofliststring;
74         bool tidy;
75         std::string configtag;
76         limitlist chanlimits;
77  
78  public:
79         ListModeBase(InspIRCd* Instance, char modechar, const std::string &eolstr, const std::string &lnum, const std::string &eolnum, bool autotidy, const std::string &ctag = "banlist")
80         : ModeHandler(Instance, modechar, 1, 1, true, MODETYPE_CHANNEL, false), listnumeric(lnum), endoflistnumeric(eolnum), endofliststring(eolstr), tidy(autotidy), configtag(ctag)
81         {
82                 this->DoRehash();
83                 infokey = "listbase_mode_" + std::string(1, mode) + "_list";
84         }
85
86         std::pair<bool,std::string> ModeSet(userrec* source, userrec* dest, chanrec* channel, const std::string &parameter)
87         {
88                 modelist* el;
89                 channel->GetExt(infokey, el);
90                 if (el)
91                 {
92                         for (modelist::iterator it = el->begin(); it != el->end(); it++)
93                         {
94                                 if(parameter == it->mask)
95                                 {
96                                         return std::make_pair(true, parameter);
97                                 }
98                         }
99                 }
100                 return std::make_pair(false, parameter);
101         }
102
103         virtual void DisplayList(userrec* user, chanrec* channel)
104         {
105                 modelist* el;
106                 channel->GetExt(infokey, el);
107                 if (el)
108                 {
109                         for(modelist::iterator it = el->begin(); it != el->end(); it++)
110                         {
111                                 user->WriteServ("%s %s %s %s %s %s", listnumeric.c_str(), user->nick, channel->name, it->mask.c_str(), it->nick.c_str(), it->time.c_str());
112                         }
113                 }
114                 user->WriteServ("%s %s %s %s", endoflistnumeric.c_str(), user->nick, channel->name, endofliststring.c_str());
115         }
116
117         virtual void RemoveMode(chanrec* channel)
118         {
119                 ServerInstance->Log(DEBUG,"Removing listmode base from %s %s",channel->name,infokey.c_str());
120                 modelist* el;
121                 channel->GetExt(infokey, el);
122                 if (el)
123                 {
124                         ServerInstance->Log(DEBUG,"Channel is extended with a list");
125                         irc::modestacker modestack(false);
126                         std::deque<std::string> stackresult;
127                         const char* mode_junk[MAXMODES+1];
128                         mode_junk[0] = channel->name;
129                         userrec* n = new userrec(ServerInstance);
130                         n->SetFd(FD_MAGIC_NUMBER);
131                         for(modelist::iterator it = el->begin(); it != el->end(); it++)
132                         {
133                                 modestack.Push(this->GetModeChar(), it->mask);
134                         }
135                         while (modestack.GetStackedLine(stackresult))
136                         {
137                                 for (size_t j = 0; j < stackresult.size(); j++)
138                                 {
139                                         mode_junk[j+1] = stackresult[j].c_str();
140                                 }
141                                 ServerInstance->SendMode(mode_junk, stackresult.size() + 1, n);         
142                         }
143
144                         delete n;
145                 }
146         }
147
148         virtual void RemoveMode(userrec* user)
149         {
150                 /* Listmodes dont get set on users */
151         }
152
153         virtual void DoRehash()
154         {
155                 ConfigReader Conf(ServerInstance);
156
157                 chanlimits.clear();
158
159                 for(int i = 0; i < Conf.Enumerate(configtag); i++)
160                 {
161                         // For each <banlist> tag
162                         ListLimit limit;
163                         limit.mask = Conf.ReadValue(configtag, "chan", i);
164                         limit.limit = Conf.ReadInteger(configtag, "limit", i, true);
165
166                         if(limit.mask.size() && limit.limit > 0)
167                                 chanlimits.push_back(limit);
168                 }
169                 if(chanlimits.size() == 0)
170                 {
171                         ListLimit limit;
172                         limit.mask = "*";
173                         limit.limit = 64;
174                         chanlimits.push_back(limit);
175                 }
176         }
177
178         virtual void DoImplements(char* List)
179         {
180                 List[I_OnChannelDelete] = List[I_OnSyncChannel] = List[I_OnCleanup] = List[I_OnRehash] = 1;
181         }
182
183         virtual ModeAction OnModeChange(userrec* source, userrec* dest, chanrec* channel, std::string &parameter, bool adding)
184         {
185                 // Try and grab the list
186                 modelist* el;
187                 channel->GetExt(infokey, el);
188
189                 if (adding)
190                 {
191                         // If there was no list
192                         if (!el)
193                         {
194                                 // Make one
195                                 el = new modelist;
196                                 channel->Extend(infokey, el);
197                         }
198
199                         // Clean the mask up
200                         if (this->tidy)
201                                 ModeParser::CleanMask(parameter);
202
203                         // Check if the item already exists in the list
204                         for (modelist::iterator it = el->begin(); it != el->end(); it++)
205                         {
206                                 if(parameter == it->mask)
207                                 {
208                                         /* Give a subclass a chance to error about this */
209                                         TellAlreadyOnList(source, channel, parameter);
210                                         
211                                         // it does, deny the change
212                                         return MODEACTION_DENY;
213                                 }
214                         }
215
216                         unsigned int maxsize = 0;
217
218                         for (limitlist::iterator it = chanlimits.begin(); it != chanlimits.end(); it++)
219                         {
220                                 if (match(channel->name, it->mask.c_str()))
221                                 {
222                                         // We have a pattern matching the channel...
223                                         maxsize = el->size();
224                                         if (maxsize < it->limit)
225                                         {
226                                                 /* Ok, it *could* be allowed, now give someone subclassing us
227                                                  * a chance to validate the parameter.
228                                                  * The param is passed by reference, so they can both modify it
229                                                  * and tell us if we allow it or not.
230                                                  *
231                                                  * eg, the subclass could:
232                                                  * 1) allow
233                                                  * 2) 'fix' parameter and then allow
234                                                  * 3) deny
235                                                  */
236                                                 if(ValidateParam(source, channel, parameter))
237                                                 {
238                                                         // And now add the mask onto the list...
239                                                         ListItem e;
240                                                         e.mask = parameter;
241                                                         e.nick = source->nick;
242                                                         e.time = stringtime();
243
244                                                         el->push_back(e);
245                                                         return MODEACTION_ALLOW;
246                                                 }
247                                                 else
248                                                 {
249                                                         /* If they deny it they have the job of giving an error message */
250                                                         return MODEACTION_DENY;
251                                                 }
252                                         }
253                                 }
254                         }
255
256                         /* List is full, give subclass a chance to send a custom message */
257                         if(!TellListTooLong(source, channel, parameter))
258                         {
259                                 source->WriteServ("478 %s %s %s :Channel ban/ignore list is full", source->nick, channel->name, parameter.c_str());
260                         }
261                         
262                         parameter = "";
263                         return MODEACTION_DENY; 
264                 }
265                 else
266                 {
267                         // We're taking the mode off
268                         if (el)
269                         {
270                                 for (modelist::iterator it = el->begin(); it != el->end(); it++)
271                                 {
272                                         if(parameter == it->mask)
273                                         {
274                                                 el->erase(it);
275                                                 if(el->size() == 0)
276                                                 {
277                                                         channel->Shrink(infokey);
278                                                         delete el;
279                                                 }
280                                                 return MODEACTION_ALLOW;
281                                         }
282                                 }
283                                 /* Tried to remove something that wasn't set */
284                                 TellNotSet(source, channel, parameter);
285                                 parameter = "";
286                                 return MODEACTION_DENY;
287                         }
288                         else
289                         {
290                                 /* Hmm, taking an exception off a non-existant list, DIE */
291                                 TellNotSet(source, channel, parameter);
292                                 parameter = "";
293                                 return MODEACTION_DENY;
294                         }
295                 }
296                 return MODEACTION_DENY;
297         }
298
299         virtual std::string& GetInfoKey()
300         {
301                 return infokey;
302         }
303
304         virtual void DoChannelDelete(chanrec* chan)
305         {
306                 modelist* list;
307                 chan->GetExt(infokey, list);
308
309                 if (list)
310                 {
311                         chan->Shrink(infokey);
312                         delete list;
313                 }
314         }
315
316         virtual void DoSyncChannel(chanrec* chan, Module* proto, void* opaque)
317         {
318                 modelist* list;
319                 chan->GetExt(infokey, list);
320                 irc::modestacker modestack(true);
321                 std::deque<std::string> stackresult;
322                 if (list)
323                 {
324                         for (modelist::iterator it = list->begin(); it != list->end(); it++)
325                         {
326                                 modestack.Push(std::string(1, mode)[0], it->mask);
327                         }
328                 }
329                 while (modestack.GetStackedLine(stackresult))
330                 {
331                         irc::stringjoiner mode_join(" ", stackresult, 0, stackresult.size() - 1);
332                         std::string line = mode_join.GetJoined();
333                         proto->ProtoSendMode(opaque, TYPE_CHANNEL, chan, line);
334                 }
335         }
336
337         virtual void DoCleanup(int target_type, void* item)
338         {
339         }
340         
341         virtual bool ValidateParam(userrec* source, chanrec* channel, std::string &parameter)
342         {
343                 return true;
344         }
345         
346         virtual bool TellListTooLong(userrec* source, chanrec* channel, std::string &parameter)
347         {
348                 return false;
349         }
350         
351         virtual void TellAlreadyOnList(userrec* source, chanrec* channel, std::string &parameter)
352         {
353         }
354         
355         virtual void TellNotSet(userrec* source, chanrec* channel, std::string &parameter)
356         {
357                 
358         }
359 };
360
361 #endif