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