]> git.netwichtig.de Git - user/henk/code/inspircd.git/blob - include/u_listmode.h
Proper fix for end-of-list numerics on restricted lists in bug #386, rather than...
[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 /* Updated to use the <banlist> config tag if it exists
28  * Written by Om <omster@gmail.com>, December 2005.
29  * Based on code previously written by Om - April 2005
30  * Updated to new API July 8th 2006 by Brain
31  * Originally based on m_chanprotect and m_silence
32  */
33
34 /** Get the time as a string
35  */
36 inline std::string stringtime()
37 {
38         std::ostringstream TIME;
39         TIME << time(NULL); 
40         return TIME.str();
41 }
42
43 /** An item in a listmode's list
44  */
45 class ListItem : public classbase
46 {
47 public:
48         std::string nick;
49         irc::string mask;
50         std::string time;
51 };
52
53 /** The number of items a listmode's list may contain
54  */
55 class ListLimit : public classbase
56 {
57 public:
58         std::string mask;
59         unsigned int limit;
60 };
61
62 /** Items stored in the channel's list
63  */
64 typedef std::vector<ListItem> modelist;
65 /** Max items per channel by name
66  */
67 typedef std::vector<ListLimit> limitlist;
68
69 /** A request used to check if a user is on a channel's list or not
70  */
71 class ListModeRequest : public Request
72 {
73  public:
74         userrec* user;
75         chanrec* chan;
76
77         /** Check if a user is on a channel's list.
78          * The Event::Send() event returns true if the user is on the channel's list.
79          * @param sender Sending module
80          * @param target Target module
81          * @param u User to check against
82          * @param c Channel to check against
83          */
84         ListModeRequest(Module* sender, Module* target, userrec* u, chanrec* c) : Request(sender, target, "LM_CHECKLIST"), user(u), chan(c)
85         {
86         }
87
88         /** Destructor
89          */
90         ~ListModeRequest()
91         {
92         }
93 };
94
95 /** The base class for list modes, should be inherited.
96  */
97 class ListModeBase : public ModeHandler
98 {
99  protected:
100         /** Storage key
101          */
102         std::string infokey;
103         /** Numeric to use when outputting the list
104          */
105         std::string listnumeric;
106         /** Numeric to indicate end of list
107          */
108         std::string endoflistnumeric;
109         /** String to send for end of list
110          */
111         std::string endofliststring;
112         /** Automatically tidy up entries
113          */
114         bool tidy;
115         /** Config tag to check for max items per channel
116          */
117         std::string configtag;
118         /** Limits on a per-channel basis read from the tag
119          * specified in ListModeBase::configtag
120          */
121         limitlist chanlimits;
122  
123  public:
124         /** Constructor.
125          * @param Instance The creator of this class
126          * @param modechar Mode character
127          * @param eolstr End of list string
128          * @pram lnum List numeric
129          * @param eolnum End of list numeric
130          * @param autotidy Automatically tidy list entries on add
131          * @param ctag Configuration tag to get limits from
132          */
133         ListModeBase(InspIRCd* Instance, char modechar, const std::string &eolstr, const std::string &lnum, const std::string &eolnum, bool autotidy, const std::string &ctag = "banlist")
134         : ModeHandler(Instance, modechar, 1, 1, true, MODETYPE_CHANNEL, false), listnumeric(lnum), endoflistnumeric(eolnum), endofliststring(eolstr), tidy(autotidy), configtag(ctag)
135         {
136                 this->DoRehash();
137                 infokey = "listbase_mode_" + std::string(1, mode) + "_list";
138         }
139
140         /** See mode.h 
141          */
142         std::pair<bool,std::string> ModeSet(userrec* source, userrec* dest, chanrec* channel, const std::string &parameter)
143         {
144                 modelist* el;
145                 channel->GetExt(infokey, el);
146                 if (el)
147                 {
148                         for (modelist::iterator it = el->begin(); it != el->end(); it++)
149                         {
150                                 if(parameter == it->mask)
151                                 {
152                                         return std::make_pair(true, parameter);
153                                 }
154                         }
155                 }
156                 return std::make_pair(false, parameter);
157         }
158
159         /** Display the list for this mode
160          * @param user The user to send the list to
161          * @param channel The channel the user is requesting the list for
162          */
163         virtual void DisplayList(userrec* user, chanrec* channel)
164         {
165                 modelist* el;
166                 channel->GetExt(infokey, el);
167                 if (el)
168                 {
169                         for (modelist::reverse_iterator it = el->rbegin(); it != el->rend(); ++it)
170                         {
171                                 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());
172                         }
173                 }
174                 user->WriteServ("%s %s %s :%s", endoflistnumeric.c_str(), user->nick, channel->name, endofliststring.c_str());
175         }
176
177         virtual void DisplayEmptyList(userrec* user, chanrec* channel)
178         {
179                 user->WriteServ("%s %s %s :%s", endoflistnumeric.c_str(), user->nick, channel->name, endofliststring.c_str());
180         }
181
182         /** Remove all instances of the mode from a channel.
183          * See mode.h
184          * @param channel The channel to remove all instances of the mode from
185          */
186         virtual void RemoveMode(chanrec* channel)
187         {
188                 modelist* el;
189                 channel->GetExt(infokey, el);
190                 if (el)
191                 {
192                         irc::modestacker modestack(false);
193                         std::deque<std::string> stackresult;
194                         const char* mode_junk[MAXMODES+2];
195                         mode_junk[0] = channel->name;
196                         userrec* n = new userrec(ServerInstance);
197                         n->SetFd(FD_MAGIC_NUMBER);
198                         for (modelist::iterator it = el->begin(); it != el->end(); it++)
199                         {
200                                 modestack.Push(this->GetModeChar(), assign(it->mask));
201                         }
202                         while (modestack.GetStackedLine(stackresult))
203                         {
204                                 for (size_t j = 0; j < stackresult.size(); j++)
205                                 {
206                                         mode_junk[j+1] = stackresult[j].c_str();
207                                 }
208                                 ServerInstance->SendMode(mode_junk, stackresult.size() + 1, n);         
209                         }
210
211                         delete n;
212                 }
213         }
214
215         /** See mode.h
216          */
217         virtual void RemoveMode(userrec* user)
218         {
219                 /* Listmodes dont get set on users */
220         }
221
222         /** Perform a rehash of this mode's configuration data
223          */
224         virtual void DoRehash()
225         {
226                 ConfigReader Conf(ServerInstance);
227
228                 chanlimits.clear();
229
230                 for (int i = 0; i < Conf.Enumerate(configtag); i++)
231                 {
232                         // For each <banlist> tag
233                         ListLimit limit;
234                         limit.mask = Conf.ReadValue(configtag, "chan", i);
235                         limit.limit = Conf.ReadInteger(configtag, "limit", i, true);
236
237                         if (limit.mask.size() && limit.limit > 0)
238                                 chanlimits.push_back(limit);
239                 }
240                 if (chanlimits.size() == 0)
241                 {
242                         ListLimit limit;
243                         limit.mask = "*";
244                         limit.limit = 64;
245                         chanlimits.push_back(limit);
246                 }
247         }
248
249         /** Populate the Implements list with the correct events for a List Mode
250          */
251         virtual void DoImplements(char* List)
252         {
253                 List[I_OnChannelDelete] = List[I_OnSyncChannel] = List[I_OnCleanup] = List[I_OnRehash] = 1;
254         }
255
256         /** Handle the list mode.
257          * See mode.h
258          */
259         virtual ModeAction OnModeChange(userrec* source, userrec* dest, chanrec* channel, std::string &parameter, bool adding)
260         {
261                 // Try and grab the list
262                 modelist* el;
263                 channel->GetExt(infokey, el);
264
265                 if (adding)
266                 {
267                         // If there was no list
268                         if (!el)
269                         {
270                                 // Make one
271                                 el = new modelist;
272                                 channel->Extend(infokey, el);
273                         }
274
275                         // Clean the mask up
276                         if (this->tidy)
277                                 ModeParser::CleanMask(parameter);
278
279                         // Check if the item already exists in the list
280                         for (modelist::iterator it = el->begin(); it != el->end(); it++)
281                         {
282                                 if (parameter == it->mask)
283                                 {
284                                         /* Give a subclass a chance to error about this */
285                                         TellAlreadyOnList(source, channel, parameter);
286                                         
287                                         // it does, deny the change
288                                         return MODEACTION_DENY;
289                                 }
290                         }
291
292                         unsigned int maxsize = 0;
293
294                         for (limitlist::iterator it = chanlimits.begin(); it != chanlimits.end(); it++)
295                         {
296                                 if (match(channel->name, it->mask.c_str()))
297                                 {
298                                         // We have a pattern matching the channel...
299                                         maxsize = el->size();
300                                         if (maxsize < it->limit)
301                                         {
302                                                 /* Ok, it *could* be allowed, now give someone subclassing us
303                                                  * a chance to validate the parameter.
304                                                  * The param is passed by reference, so they can both modify it
305                                                  * and tell us if we allow it or not.
306                                                  *
307                                                  * eg, the subclass could:
308                                                  * 1) allow
309                                                  * 2) 'fix' parameter and then allow
310                                                  * 3) deny
311                                                  */
312                                                 if (ValidateParam(source, channel, parameter))
313                                                 {
314                                                         // And now add the mask onto the list...
315                                                         ListItem e;
316                                                         e.mask = assign(parameter);
317                                                         e.nick = source->nick;
318                                                         e.time = stringtime();
319
320                                                         el->push_back(e);
321                                                         return MODEACTION_ALLOW;
322                                                 }
323                                                 else
324                                                 {
325                                                         /* If they deny it they have the job of giving an error message */
326                                                         return MODEACTION_DENY;
327                                                 }
328                                         }
329                                 }
330                         }
331
332                         /* List is full, give subclass a chance to send a custom message */
333                         if (!TellListTooLong(source, channel, parameter))
334                         {
335                                 source->WriteServ("478 %s %s %s :Channel ban/ignore list is full", source->nick, channel->name, parameter.c_str());
336                         }
337                         
338                         parameter = "";
339                         return MODEACTION_DENY; 
340                 }
341                 else
342                 {
343                         // We're taking the mode off
344                         if (el)
345                         {
346                                 for (modelist::iterator it = el->begin(); it != el->end(); it++)
347                                 {
348                                         if (parameter == it->mask)
349                                         {
350                                                 el->erase(it);
351                                                 if (el->size() == 0)
352                                                 {
353                                                         channel->Shrink(infokey);
354                                                         delete el;
355                                                 }
356                                                 return MODEACTION_ALLOW;
357                                         }
358                                 }
359                                 /* Tried to remove something that wasn't set */
360                                 TellNotSet(source, channel, parameter);
361                                 parameter = "";
362                                 return MODEACTION_DENY;
363                         }
364                         else
365                         {
366                                 /* Hmm, taking an exception off a non-existant list, DIE */
367                                 TellNotSet(source, channel, parameter);
368                                 parameter = "";
369                                 return MODEACTION_DENY;
370                         }
371                 }
372                 return MODEACTION_DENY;
373         }
374
375         /** Get Extensible key for this mode
376          */
377         virtual std::string& GetInfoKey()
378         {
379                 return infokey;
380         }
381
382         /** Handle channel deletion.
383          * See modules.h.
384          * @param chan Channel being deleted
385          */
386         virtual void DoChannelDelete(chanrec* chan)
387         {
388                 modelist* list;
389                 chan->GetExt(infokey, list);
390
391                 if (list)
392                 {
393                         chan->Shrink(infokey);
394                         delete list;
395                 }
396         }
397
398         /** Syncronize channel item list with another server.
399          * See modules.h
400          * @param chan Channel to syncronize
401          * @param proto Protocol module pointer
402          * @param opaque Opaque connection handle
403          */
404         virtual void DoSyncChannel(chanrec* chan, Module* proto, void* opaque)
405         {
406                 modelist* list;
407                 chan->GetExt(infokey, list);
408                 irc::modestacker modestack(true);
409                 std::deque<std::string> stackresult;
410                 if (list)
411                 {
412                         for (modelist::iterator it = list->begin(); it != list->end(); it++)
413                         {
414                                 modestack.Push(std::string(1, mode)[0], assign(it->mask));
415                         }
416                 }
417                 while (modestack.GetStackedLine(stackresult))
418                 {
419                         irc::stringjoiner mode_join(" ", stackresult, 0, stackresult.size() - 1);
420                         std::string line = mode_join.GetJoined();
421                         proto->ProtoSendMode(opaque, TYPE_CHANNEL, chan, line);
422                 }
423         }
424
425         /** Clean up module on unload
426          * @param target_type Type of target to clean
427          * @param item Item to clean
428          */
429         virtual void DoCleanup(int target_type, void* item)
430         {
431         }
432         
433         /** Validate parameters.
434          * Overridden by implementing module.
435          * @param source Source user adding the parameter
436          * @param channel Channel the parameter is being added to
437          * @param parameter The actual parameter being added
438          * @return true if the parameter is valid
439          */
440         virtual bool ValidateParam(userrec* source, chanrec* channel, std::string &parameter)
441         {
442                 return true;
443         }
444         
445         /** Tell the user the list is too long.
446          * Overridden by implementing module.
447          * @param source Source user adding the parameter
448          * @param channel Channel the parameter is being added to
449          * @param parameter The actual parameter being added
450          * @return Ignored
451          */
452         virtual bool TellListTooLong(userrec* source, chanrec* channel, std::string &parameter)
453         {
454                 return false;
455         }
456         
457         /** Tell the user an item is already on the list.
458          * Overridden by implementing module.
459          * @param source Source user adding the parameter
460          * @param channel Channel the parameter is being added to
461          * @param parameter The actual parameter being added
462          */
463         virtual void TellAlreadyOnList(userrec* source, chanrec* channel, std::string &parameter)
464         {
465         }
466         
467         /** Tell the user that the parameter is not in the list.
468          * Overridden by implementing module.
469          * @param source Source user removing the parameter
470          * @param channel Channel the parameter is being removed from
471          * @param parameter The actual parameter being removed
472          */
473         virtual void TellNotSet(userrec* source, chanrec* channel, std::string &parameter)
474         {
475         }
476 };
477
478 #endif
479