]> git.netwichtig.de Git - user/henk/code/inspircd.git/blob - include/u_listmode.h
379bcc342a43e9d70bc5dd9725e0598957d6bbc5
[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(Module* m)
242         {
243                 Implementation eventlist[] = { I_OnChannelDelete, I_OnSyncChannel, I_OnCleanup, I_OnRehash };
244                 ServerInstance->Modules->Attach(eventlist, m, 4);
245         }
246
247         /** Handle the list mode.
248          * See mode.h
249          */
250         virtual ModeAction OnModeChange(User* source, User*, Channel* channel, std::string &parameter, bool adding)
251         {
252                 // Try and grab the list
253                 modelist* el;
254                 channel->GetExt(infokey, el);
255
256                 if (adding)
257                 {
258                         // If there was no list
259                         if (!el)
260                         {
261                                 // Make one
262                                 el = new modelist;
263                                 channel->Extend(infokey, el);
264                         }
265
266                         // Clean the mask up
267                         if (this->tidy)
268                                 ModeParser::CleanMask(parameter);
269
270                         // Check if the item already exists in the list
271                         for (modelist::iterator it = el->begin(); it != el->end(); it++)
272                         {
273                                 if (parameter == it->mask)
274                                 {
275                                         /* Give a subclass a chance to error about this */
276                                         TellAlreadyOnList(source, channel, parameter);
277                                         
278                                         // it does, deny the change
279                                         return MODEACTION_DENY;
280                                 }
281                         }
282
283                         unsigned int maxsize = 0;
284
285                         for (limitlist::iterator it = chanlimits.begin(); it != chanlimits.end(); it++)
286                         {
287                                 if (match(channel->name, it->mask.c_str()))
288                                 {
289                                         // We have a pattern matching the channel...
290                                         maxsize = el->size();
291                                         if (maxsize < it->limit)
292                                         {
293                                                 /* Ok, it *could* be allowed, now give someone subclassing us
294                                                  * a chance to validate the parameter.
295                                                  * The param is passed by reference, so they can both modify it
296                                                  * and tell us if we allow it or not.
297                                                  *
298                                                  * eg, the subclass could:
299                                                  * 1) allow
300                                                  * 2) 'fix' parameter and then allow
301                                                  * 3) deny
302                                                  */
303                                                 if (ValidateParam(source, channel, parameter))
304                                                 {
305                                                         // And now add the mask onto the list...
306                                                         ListItem e;
307                                                         e.mask = assign(parameter);
308                                                         e.nick = source->nick;
309                                                         e.time = stringtime();
310
311                                                         el->push_back(e);
312                                                         return MODEACTION_ALLOW;
313                                                 }
314                                                 else
315                                                 {
316                                                         /* If they deny it they have the job of giving an error message */
317                                                         return MODEACTION_DENY;
318                                                 }
319                                         }
320                                 }
321                         }
322
323                         /* List is full, give subclass a chance to send a custom message */
324                         if (!TellListTooLong(source, channel, parameter))
325                         {
326                                 source->WriteServ("478 %s %s %s :Channel ban/ignore list is full", source->nick, channel->name, parameter.c_str());
327                         }
328                         
329                         parameter = "";
330                         return MODEACTION_DENY; 
331                 }
332                 else
333                 {
334                         // We're taking the mode off
335                         if (el)
336                         {
337                                 for (modelist::iterator it = el->begin(); it != el->end(); it++)
338                                 {
339                                         if (parameter == it->mask)
340                                         {
341                                                 el->erase(it);
342                                                 if (el->size() == 0)
343                                                 {
344                                                         channel->Shrink(infokey);
345                                                         delete el;
346                                                 }
347                                                 return MODEACTION_ALLOW;
348                                         }
349                                 }
350                                 /* Tried to remove something that wasn't set */
351                                 TellNotSet(source, channel, parameter);
352                                 parameter = "";
353                                 return MODEACTION_DENY;
354                         }
355                         else
356                         {
357                                 /* Hmm, taking an exception off a non-existant list, DIE */
358                                 TellNotSet(source, channel, parameter);
359                                 parameter = "";
360                                 return MODEACTION_DENY;
361                         }
362                 }
363                 return MODEACTION_DENY;
364         }
365
366         /** Get Extensible key for this mode
367          */
368         virtual std::string& GetInfoKey()
369         {
370                 return infokey;
371         }
372
373         /** Handle channel deletion.
374          * See modules.h.
375          * @param chan Channel being deleted
376          */
377         virtual void DoChannelDelete(Channel* chan)
378         {
379                 modelist* list;
380                 chan->GetExt(infokey, list);
381
382                 if (list)
383                 {
384                         chan->Shrink(infokey);
385                         delete list;
386                 }
387         }
388
389         /** Syncronize channel item list with another server.
390          * See modules.h
391          * @param chan Channel to syncronize
392          * @param proto Protocol module pointer
393          * @param opaque Opaque connection handle
394          */
395         virtual void DoSyncChannel(Channel* chan, Module* proto, void* opaque)
396         {
397                 modelist* list;
398                 chan->GetExt(infokey, list);
399                 irc::modestacker modestack(true);
400                 std::deque<std::string> stackresult;
401                 if (list)
402                 {
403                         for (modelist::iterator it = list->begin(); it != list->end(); it++)
404                         {
405                                 modestack.Push(std::string(1, mode)[0], assign(it->mask));
406                         }
407                 }
408                 while (modestack.GetStackedLine(stackresult))
409                 {
410                         irc::stringjoiner mode_join(" ", stackresult, 0, stackresult.size() - 1);
411                         std::string line = mode_join.GetJoined();
412                         proto->ProtoSendMode(opaque, TYPE_CHANNEL, chan, line);
413                 }
414         }
415
416         /** Clean up module on unload
417          * @param target_type Type of target to clean
418          * @param item Item to clean
419          */
420         virtual void DoCleanup(int, void*)
421         {
422         }
423         
424         /** Validate parameters.
425          * Overridden by implementing module.
426          * @param source Source user adding the parameter
427          * @param channel Channel the parameter is being added to
428          * @param parameter The actual parameter being added
429          * @return true if the parameter is valid
430          */
431         virtual bool ValidateParam(User*, Channel*, std::string&)
432         {
433                 return true;
434         }
435         
436         /** Tell the user the list is too long.
437          * Overridden by implementing module.
438          * @param source Source user adding the parameter
439          * @param channel Channel the parameter is being added to
440          * @param parameter The actual parameter being added
441          * @return Ignored
442          */
443         virtual bool TellListTooLong(User*, Channel*, std::string&)
444         {
445                 return false;
446         }
447         
448         /** Tell the user an item is already on the list.
449          * Overridden by implementing module.
450          * @param source Source user adding the parameter
451          * @param channel Channel the parameter is being added to
452          * @param parameter The actual parameter being added
453          */
454         virtual void TellAlreadyOnList(User*, Channel*, std::string&)
455         {
456         }
457         
458         /** Tell the user that the parameter is not in the list.
459          * Overridden by implementing module.
460          * @param source Source user removing the parameter
461          * @param channel Channel the parameter is being removed from
462          * @param parameter The actual parameter being removed
463          */
464         virtual void TellNotSet(User*, Channel*, std::string&)
465         {
466         }
467 };
468
469 #endif
470