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