]> git.netwichtig.de Git - user/henk/code/inspircd.git/blob - include/u_listmode.h
Replace std::deque with std::vector in spanningtree and related modules
[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         /** Storage key
111          */
112         std::string infokey;
113         /** Numeric to use when outputting the list
114          */
115         unsigned int listnumeric;
116         /** Numeric to indicate end of list
117          */
118         unsigned int endoflistnumeric;
119         /** String to send for end of list
120          */
121         std::string endofliststring;
122         /** Automatically tidy up entries
123          */
124         bool tidy;
125         /** Config tag to check for max items per channel
126          */
127         std::string configtag;
128         /** Limits on a per-channel basis read from the tag
129          * specified in ListModeBase::configtag
130          */
131         limitlist chanlimits;
132
133  public:
134         /** Constructor.
135          * @param Instance The creator of this class
136          * @param modechar Mode character
137          * @param eolstr End of list string
138          * @pram lnum List numeric
139          * @param eolnum End of list numeric
140          * @param autotidy Automatically tidy list entries on add
141          * @param ctag Configuration tag to get limits from
142          */
143         ListModeBase(InspIRCd* Instance, char modechar, const std::string &eolstr, unsigned int lnum, unsigned int eolnum, bool autotidy, const std::string &ctag = "banlist")
144         : ModeHandler(Instance, modechar, 1, 1, true, MODETYPE_CHANNEL, false), listnumeric(lnum), endoflistnumeric(eolnum), endofliststring(eolstr), tidy(autotidy), configtag(ctag)
145         {
146                 this->DoRehash();
147                 infokey = "listbase_mode_" + std::string(1, mode) + "_list";
148         }
149
150         /** See mode.h
151          */
152         std::pair<bool,std::string> ModeSet(User*, User*, Channel* channel, const std::string &parameter)
153         {
154                 modelist* el;
155                 channel->GetExt(infokey, el);
156                 if (el)
157                 {
158                         for (modelist::iterator it = el->begin(); it != el->end(); it++)
159                         {
160                                 if(parameter == it->mask)
161                                 {
162                                         return std::make_pair(true, parameter);
163                                 }
164                         }
165                 }
166                 return std::make_pair(false, parameter);
167         }
168
169         /** Display the list for this mode
170          * @param user The user to send the list to
171          * @param channel The channel the user is requesting the list for
172          */
173         virtual void DisplayList(User* user, Channel* channel)
174         {
175                 modelist* el;
176                 channel->GetExt(infokey, el);
177                 if (el)
178                 {
179                         for (modelist::reverse_iterator it = el->rbegin(); it != el->rend(); ++it)
180                         {
181                                 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());
182                         }
183                 }
184                 user->WriteNumeric(endoflistnumeric, "%s %s :%s", user->nick.c_str(), channel->name.c_str(), endofliststring.c_str());
185         }
186
187         virtual void DisplayEmptyList(User* user, Channel* channel)
188         {
189                 user->WriteNumeric(endoflistnumeric, "%s %s :%s", user->nick.c_str(), channel->name.c_str(), endofliststring.c_str());
190         }
191
192         /** Remove all instances of the mode from a channel.
193          * See mode.h
194          * @param channel The channel to remove all instances of the mode from
195          */
196         virtual void RemoveMode(Channel* channel, irc::modestacker* stack)
197         {
198                 modelist* el;
199                 channel->GetExt(infokey, el);
200                 if (el)
201                 {
202                         irc::modestacker modestack(ServerInstance, false);
203
204                         for (modelist::iterator it = el->begin(); it != el->end(); it++)
205                         {
206                                 if (stack)
207                                         stack->Push(this->GetModeChar(), it->mask);
208                                 else
209                                         modestack.Push(this->GetModeChar(), it->mask);
210                         }
211
212                         if (stack)
213                                 return;
214
215                         std::vector<std::string> stackresult;
216                         stackresult.push_back(channel->name);
217                         while (modestack.GetStackedLine(stackresult))
218                         {
219                                 ServerInstance->SendMode(stackresult, ServerInstance->FakeClient);
220                                 stackresult.clear();
221                                 stackresult.push_back(channel->name);
222                         }
223                 }
224         }
225
226         /** See mode.h
227          */
228         virtual void RemoveMode(User*, irc::modestacker* stack)
229         {
230                 /* Listmodes dont get set on users */
231         }
232
233         /** Perform a rehash of this mode's configuration data
234          */
235         virtual void DoRehash()
236         {
237                 ConfigReader Conf(ServerInstance);
238
239                 chanlimits.clear();
240
241                 for (int i = 0; i < Conf.Enumerate(configtag); i++)
242                 {
243                         // For each <banlist> tag
244                         ListLimit limit;
245                         limit.mask = Conf.ReadValue(configtag, "chan", i);
246                         limit.limit = Conf.ReadInteger(configtag, "limit", i, true);
247
248                         if (limit.mask.size() && limit.limit > 0)
249                                 chanlimits.push_back(limit);
250                 }
251                 if (chanlimits.size() == 0)
252                 {
253                         ListLimit limit;
254                         limit.mask = "*";
255                         limit.limit = 64;
256                         chanlimits.push_back(limit);
257                 }
258         }
259
260         /** Populate the Implements list with the correct events for a List Mode
261          */
262         virtual void DoImplements(Module* m)
263         {
264                 Implementation eventlist[] = { I_OnChannelDelete, I_OnSyncChannel, I_OnCleanup, I_OnRehash, I_OnRequest };
265                 ServerInstance->Modules->Attach(eventlist, m, 5);
266         }
267
268         /** Handle the list mode.
269          * See mode.h
270          */
271         virtual ModeAction OnModeChange(User* source, User*, Channel* channel, std::string &parameter, bool adding, bool servermode)
272         {
273                 // Try and grab the list
274                 modelist* el;
275                 channel->GetExt(infokey, el);
276
277                 if (adding)
278                 {
279                         // If there was no list
280                         if (!el)
281                         {
282                                 // Make one
283                                 el = new modelist;
284                                 channel->Extend(infokey, 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 = servermode ? ServerInstance->Config->ServerName : 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                                                         channel->Shrink(infokey);
366                                                         delete el;
367                                                 }
368                                                 return MODEACTION_ALLOW;
369                                         }
370                                 }
371                                 /* Tried to remove something that wasn't set */
372                                 TellNotSet(source, channel, parameter);
373                                 parameter = "";
374                                 return MODEACTION_DENY;
375                         }
376                         else
377                         {
378                                 /* Hmm, taking an exception off a non-existant list, DIE */
379                                 TellNotSet(source, channel, parameter);
380                                 parameter = "";
381                                 return MODEACTION_DENY;
382                         }
383                 }
384                 return MODEACTION_DENY;
385         }
386
387         /** Get Extensible key for this mode
388          */
389         virtual std::string& GetInfoKey()
390         {
391                 return infokey;
392         }
393
394         /** Handle channel deletion.
395          * See modules.h.
396          * @param chan Channel being deleted
397          */
398         virtual void DoChannelDelete(Channel* chan)
399         {
400                 modelist* mlist;
401                 chan->GetExt(infokey, mlist);
402
403                 if (mlist)
404                 {
405                         chan->Shrink(infokey);
406                         delete mlist;
407                 }
408         }
409
410         /** Syncronize channel item list with another server.
411          * See modules.h
412          * @param chan Channel to syncronize
413          * @param proto Protocol module pointer
414          * @param opaque Opaque connection handle
415          */
416         virtual void DoSyncChannel(Channel* chan, Module* proto, void* opaque)
417         {
418                 modelist* mlist;
419                 chan->GetExt(infokey, mlist);
420                 irc::modestacker modestack(ServerInstance, true);
421                 std::vector<std::string> stackresult;
422                 std::vector<TranslateType> types;
423                 types.push_back(TR_TEXT);
424                 if (mlist)
425                 {
426                         for (modelist::iterator it = mlist->begin(); it != mlist->end(); it++)
427                         {
428                                 modestack.Push(std::string(1, mode)[0], it->mask);
429                         }
430                 }
431                 while (modestack.GetStackedLine(stackresult))
432                 {
433                         types.assign(stackresult.size(), this->GetTranslateType());
434                         proto->ProtoSendMode(opaque, TYPE_CHANNEL, chan, stackresult, types);
435                         stackresult.clear();
436                 }
437         }
438
439         /** Clean up module on unload
440          * @param target_type Type of target to clean
441          * @param item Item to clean
442          */
443         virtual void DoCleanup(int, void*)
444         {
445         }
446
447         /** Validate parameters.
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          * @return true if the parameter is valid
453          */
454         virtual bool ValidateParam(User*, Channel*, std::string&)
455         {
456                 return true;
457         }
458
459         /** Tell the user the list is too long.
460          * Overridden by implementing module.
461          * @param source Source user adding the parameter
462          * @param channel Channel the parameter is being added to
463          * @param parameter The actual parameter being added
464          * @return Ignored
465          */
466         virtual bool TellListTooLong(User*, Channel*, std::string&)
467         {
468                 return false;
469         }
470
471         /** Tell the user an item is already on the list.
472          * Overridden by implementing module.
473          * @param source Source user adding the parameter
474          * @param channel Channel the parameter is being added to
475          * @param parameter The actual parameter being added
476          */
477         virtual void TellAlreadyOnList(User*, Channel*, std::string&)
478         {
479         }
480
481         /** Tell the user that the parameter is not in the list.
482          * Overridden by implementing module.
483          * @param source Source user removing the parameter
484          * @param channel Channel the parameter is being removed from
485          * @param parameter The actual parameter being removed
486          */
487         virtual void TellNotSet(User*, Channel*, std::string&)
488         {
489         }
490
491         virtual const char* DoOnRequest(Request* request)
492         {
493                 ListModeRequest* LM = (ListModeRequest*)request;
494                 if (strcmp("LM_CHECKLIST", request->GetId()) == 0)
495                 {
496                         modelist* mlist;
497                         LM->chan->GetExt(GetInfoKey(), mlist);
498                         if (mlist)
499                         {
500                                 std::string mask = LM->user->nick + "!" + LM->user->ident + "@" + LM->user->GetIPString();
501                                 for (modelist::iterator it = mlist->begin(); it != mlist->end(); ++it)
502                                 {
503                                         if (InspIRCd::Match(LM->user->GetFullRealHost(), it->mask) || InspIRCd::Match(LM->user->GetFullHost(), it->mask) || (InspIRCd::MatchCIDR(mask, it->mask)))
504                                                 return it->mask.c_str();
505                                 }
506                                 return NULL;
507                         }
508                 }
509                 else if (strcmp("LM_CHECKLIST_EX", request->GetId()) == 0)
510                 {
511                         modelist* mlist;
512                         LM->chan->GetExt(GetInfoKey(), mlist);
513
514                         if (mlist)
515                         {
516                                 if (LM->user)
517                                 {
518                                         LM->literal = LM->user->nick + "!" + LM->user->ident + "@" + LM->user->GetIPString();
519                                 }
520
521                                 for (modelist::iterator it = mlist->begin(); it != mlist->end(); it++)
522                                 {
523                                         if (LM->extban && it->mask.length() > 1 && it->mask[0] == LM->extban && it->mask[1] == ':')
524                                         {
525                                                 std::string ext = it->mask.substr(2);
526                                                 if (LM->user)
527                                                 {
528                                                         if (InspIRCd::Match(LM->user->GetFullRealHost(), ext) || InspIRCd::Match(LM->user->GetFullHost(), ext) || (InspIRCd::MatchCIDR(LM->literal, ext)))
529                                                         {
530                                                                 return it->mask.c_str();
531                                                         }
532                                                 }
533                                                 else if (InspIRCd::Match(LM->literal, ext))
534                                                 {
535                                                         return it->mask.c_str();
536                                                 }
537                                         }
538                                         else
539                                         {
540                                                 if (LM->user)
541                                                 {
542                                                         if (InspIRCd::Match(LM->user->GetFullRealHost(), it->mask) || InspIRCd::Match(LM->user->GetFullHost(), it->mask) || (InspIRCd::MatchCIDR(LM->literal, it->mask)))
543                                                         {
544                                                                 return it->mask.c_str();
545                                                         }
546                                                 }
547                                                 else if (InspIRCd::Match(LM->literal, it->mask))
548                                                 {
549                                                         return it->mask.c_str();
550                                                 }
551                                         }
552                                 }
553                         }
554                 }
555                 return NULL;
556         }
557
558 };
559
560 #endif