]> git.netwichtig.de Git - user/henk/code/inspircd.git/blob - include/u_listmode.h
88b5fb3eea17c66a4643903147af900f1a9da925
[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://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 /** 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                         std::deque<std::string> stackresult;
204                         std::vector<std::string> mode_junk;
205                         mode_junk.push_back(channel->name);
206
207                         for (modelist::iterator it = el->begin(); it != el->end(); it++)
208                         {
209                                 if (stack)
210                                         stack->Push(this->GetModeChar(), it->mask);
211                                 else
212                                         modestack.Push(this->GetModeChar(), it->mask);
213                         }
214
215                         if (stack)
216                                 return;
217
218                         while (modestack.GetStackedLine(stackresult))
219                         {
220                                 for (size_t j = 0; j < stackresult.size(); j++)
221                                 {
222                                         mode_junk.push_back(stackresult[j]);
223                                 }
224
225                                 ServerInstance->SendMode(mode_junk, ServerInstance->FakeClient);
226                         }
227                 }
228         }
229
230         /** See mode.h
231          */
232         virtual void RemoveMode(User*, irc::modestacker* stack)
233         {
234                 /* Listmodes dont get set on users */
235         }
236
237         /** Perform a rehash of this mode's configuration data
238          */
239         virtual void DoRehash()
240         {
241                 ConfigReader Conf(ServerInstance);
242
243                 chanlimits.clear();
244
245                 for (int i = 0; i < Conf.Enumerate(configtag); i++)
246                 {
247                         // For each <banlist> tag
248                         ListLimit limit;
249                         limit.mask = Conf.ReadValue(configtag, "chan", i);
250                         limit.limit = Conf.ReadInteger(configtag, "limit", i, true);
251
252                         if (limit.mask.size() && limit.limit > 0)
253                                 chanlimits.push_back(limit);
254                 }
255                 if (chanlimits.size() == 0)
256                 {
257                         ListLimit limit;
258                         limit.mask = "*";
259                         limit.limit = 64;
260                         chanlimits.push_back(limit);
261                 }
262         }
263
264         /** Populate the Implements list with the correct events for a List Mode
265          */
266         virtual void DoImplements(Module* m)
267         {
268                 Implementation eventlist[] = { I_OnChannelDelete, I_OnSyncChannel, I_OnCleanup, I_OnRehash, I_OnRequest };
269                 ServerInstance->Modules->Attach(eventlist, m, 5);
270         }
271
272         /** Handle the list mode.
273          * See mode.h
274          */
275         virtual ModeAction OnModeChange(User* source, User*, Channel* channel, std::string &parameter, bool adding, bool servermode)
276         {
277                 // Try and grab the list
278                 modelist* el;
279                 channel->GetExt(infokey, el);
280
281                 if (adding)
282                 {
283                         // If there was no list
284                         if (!el)
285                         {
286                                 // Make one
287                                 el = new modelist;
288                                 channel->Extend(infokey, el);
289                         }
290
291                         // Clean the mask up
292                         if (this->tidy)
293                                 ModeParser::CleanMask(parameter);
294
295                         // Check if the item already exists in the list
296                         for (modelist::iterator it = el->begin(); it != el->end(); it++)
297                         {
298                                 if (parameter == it->mask)
299                                 {
300                                         /* Give a subclass a chance to error about this */
301                                         TellAlreadyOnList(source, channel, parameter);
302
303                                         // it does, deny the change
304                                         return MODEACTION_DENY;
305                                 }
306                         }
307
308                         unsigned int maxsize = 0;
309
310                         for (limitlist::iterator it = chanlimits.begin(); it != chanlimits.end(); it++)
311                         {
312                                 if (InspIRCd::Match(channel->name, it->mask))
313                                 {
314                                         // We have a pattern matching the channel...
315                                         maxsize = el->size();
316                                         if (IS_LOCAL(source) || (maxsize < it->limit))
317                                         {
318                                                 /* Ok, it *could* be allowed, now give someone subclassing us
319                                                  * a chance to validate the parameter.
320                                                  * The param is passed by reference, so they can both modify it
321                                                  * and tell us if we allow it or not.
322                                                  *
323                                                  * eg, the subclass could:
324                                                  * 1) allow
325                                                  * 2) 'fix' parameter and then allow
326                                                  * 3) deny
327                                                  */
328                                                 if (ValidateParam(source, channel, parameter))
329                                                 {
330                                                         // And now add the mask onto the list...
331                                                         ListItem e;
332                                                         e.mask = parameter;
333                                                         e.nick = servermode ? ServerInstance->Config->ServerName : source->nick;
334                                                         e.time = stringtime(ServerInstance);
335
336                                                         el->push_back(e);
337                                                         return MODEACTION_ALLOW;
338                                                 }
339                                                 else
340                                                 {
341                                                         /* If they deny it they have the job of giving an error message */
342                                                         return MODEACTION_DENY;
343                                                 }
344                                         }
345                                 }
346                         }
347
348                         /* List is full, give subclass a chance to send a custom message */
349                         if (!TellListTooLong(source, channel, parameter))
350                         {
351                                 source->WriteNumeric(478, "%s %s %s :Channel ban/ignore list is full", source->nick.c_str(), channel->name.c_str(), parameter.c_str());
352                         }
353
354                         parameter = "";
355                         return MODEACTION_DENY;
356                 }
357                 else
358                 {
359                         // We're taking the mode off
360                         if (el)
361                         {
362                                 for (modelist::iterator it = el->begin(); it != el->end(); it++)
363                                 {
364                                         if (parameter == it->mask)
365                                         {
366                                                 el->erase(it);
367                                                 if (el->size() == 0)
368                                                 {
369                                                         channel->Shrink(infokey);
370                                                         delete el;
371                                                 }
372                                                 return MODEACTION_ALLOW;
373                                         }
374                                 }
375                                 /* Tried to remove something that wasn't set */
376                                 TellNotSet(source, channel, parameter);
377                                 parameter = "";
378                                 return MODEACTION_DENY;
379                         }
380                         else
381                         {
382                                 /* Hmm, taking an exception off a non-existant list, DIE */
383                                 TellNotSet(source, channel, parameter);
384                                 parameter = "";
385                                 return MODEACTION_DENY;
386                         }
387                 }
388                 return MODEACTION_DENY;
389         }
390
391         /** Get Extensible key for this mode
392          */
393         virtual std::string& GetInfoKey()
394         {
395                 return infokey;
396         }
397
398         /** Handle channel deletion.
399          * See modules.h.
400          * @param chan Channel being deleted
401          */
402         virtual void DoChannelDelete(Channel* chan)
403         {
404                 modelist* mlist;
405                 chan->GetExt(infokey, mlist);
406
407                 if (mlist)
408                 {
409                         chan->Shrink(infokey);
410                         delete mlist;
411                 }
412         }
413
414         /** Syncronize channel item list with another server.
415          * See modules.h
416          * @param chan Channel to syncronize
417          * @param proto Protocol module pointer
418          * @param opaque Opaque connection handle
419          */
420         virtual void DoSyncChannel(Channel* chan, Module* proto, void* opaque)
421         {
422                 modelist* mlist;
423                 chan->GetExt(infokey, mlist);
424                 irc::modestacker modestack(ServerInstance, true);
425                 std::deque<std::string> stackresult;
426                 std::vector<TranslateType> types;
427                 types.push_back(TR_TEXT);
428                 if (mlist)
429                 {
430                         for (modelist::iterator it = mlist->begin(); it != mlist->end(); it++)
431                         {
432                                 modestack.Push(std::string(1, mode)[0], it->mask);
433                                 types.push_back(this->GetTranslateType());
434                         }
435                 }
436                 while (modestack.GetStackedLine(stackresult))
437                 {
438                         irc::stringjoiner mode_join(" ", stackresult, 0, stackresult.size() - 1);
439                         std::string line = mode_join.GetJoined();
440                         proto->ProtoSendMode(opaque, TYPE_CHANNEL, chan, line, types);
441                 }
442         }
443
444         /** Clean up module on unload
445          * @param target_type Type of target to clean
446          * @param item Item to clean
447          */
448         virtual void DoCleanup(int, void*)
449         {
450         }
451
452         /** Validate parameters.
453          * Overridden by implementing module.
454          * @param source Source user adding the parameter
455          * @param channel Channel the parameter is being added to
456          * @param parameter The actual parameter being added
457          * @return true if the parameter is valid
458          */
459         virtual bool ValidateParam(User*, Channel*, std::string&)
460         {
461                 return true;
462         }
463
464         /** Tell the user the list is too long.
465          * Overridden by implementing module.
466          * @param source Source user adding the parameter
467          * @param channel Channel the parameter is being added to
468          * @param parameter The actual parameter being added
469          * @return Ignored
470          */
471         virtual bool TellListTooLong(User*, Channel*, std::string&)
472         {
473                 return false;
474         }
475
476         /** Tell the user an item is already on the list.
477          * Overridden by implementing module.
478          * @param source Source user adding the parameter
479          * @param channel Channel the parameter is being added to
480          * @param parameter The actual parameter being added
481          */
482         virtual void TellAlreadyOnList(User*, Channel*, std::string&)
483         {
484         }
485
486         /** Tell the user that the parameter is not in the list.
487          * Overridden by implementing module.
488          * @param source Source user removing the parameter
489          * @param channel Channel the parameter is being removed from
490          * @param parameter The actual parameter being removed
491          */
492         virtual void TellNotSet(User*, Channel*, std::string&)
493         {
494         }
495
496         virtual const char* DoOnRequest(Request* request)
497         {
498                 ListModeRequest* LM = (ListModeRequest*)request;
499                 if (strcmp("LM_CHECKLIST", request->GetId()) == 0)
500                 {
501                         modelist* mlist;
502                         LM->chan->GetExt(GetInfoKey(), mlist);
503                         if (mlist)
504                         {
505                                 std::string mask = LM->user->nick + "!" + LM->user->ident + "@" + LM->user->GetIPString();
506                                 for (modelist::iterator it = mlist->begin(); it != mlist->end(); ++it)
507                                 {
508                                         if (InspIRCd::Match(LM->user->GetFullRealHost(), it->mask) || InspIRCd::Match(LM->user->GetFullHost(), it->mask) || (InspIRCd::MatchCIDR(mask, it->mask)))
509                                                 return it->mask.c_str();
510                                 }
511                                 return NULL;
512                         }
513                 }
514                 else if (strcmp("LM_CHECKLIST_EX", request->GetId()) == 0)
515                 {
516                         modelist* mlist;
517                         LM->chan->GetExt(GetInfoKey(), mlist);
518
519                         if (mlist)
520                         {
521                                 if (LM->user)
522                                 {
523                                         LM->literal = LM->user->nick + "!" + LM->user->ident + "@" + LM->user->GetIPString();
524                                 }
525
526                                 for (modelist::iterator it = mlist->begin(); it != mlist->end(); it++)
527                                 {
528                                         if (LM->extban && it->mask.length() > 1 && it->mask[0] == LM->extban && it->mask[1] == ':')
529                                         {
530                                                 std::string ext = it->mask.substr(2);
531                                                 if (LM->user)
532                                                 {
533                                                         if (InspIRCd::Match(LM->user->GetFullRealHost(), ext) || InspIRCd::Match(LM->user->GetFullHost(), ext) || (InspIRCd::MatchCIDR(LM->literal, ext)))
534                                                         {
535                                                                 return it->mask.c_str();
536                                                         }
537                                                 }
538                                                 else if (InspIRCd::Match(LM->literal, ext))
539                                                 {
540                                                         return it->mask.c_str();
541                                                 }
542                                         }
543                                         else
544                                         {
545                                                 if (LM->user)
546                                                 {
547                                                         if (InspIRCd::Match(LM->user->GetFullRealHost(), it->mask) || InspIRCd::Match(LM->user->GetFullHost(), it->mask) || (InspIRCd::MatchCIDR(LM->literal, it->mask)))
548                                                         {
549                                                                 return it->mask.c_str();
550                                                         }
551                                                 }
552                                                 else if (InspIRCd::Match(LM->literal, it->mask))
553                                                 {
554                                                         return it->mask.c_str();
555                                                 }
556                                         }
557                                 }
558                         }
559                 }
560                 return NULL;
561         }
562
563 };
564
565 #endif