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