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