]> git.netwichtig.de Git - user/henk/code/inspircd.git/blob - include/u_listmode.h
eb3cf18b7e2694c4c9d3ecae59792a2735fad226
[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                         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                                 mode_junk.insert(mode_junk.end(), stackresult.begin(), stackresult.end());
221                                 ServerInstance->SendMode(mode_junk, ServerInstance->FakeClient);
222                                 mode_junk.erase(mode_junk.begin() + 1, mode_junk.end());
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, bool servermode)
273         {
274                 // Try and grab the list
275                 modelist* el;
276                 channel->GetExt(infokey, el);
277
278                 if (adding)
279                 {
280                         // If there was no list
281                         if (!el)
282                         {
283                                 // Make one
284                                 el = new modelist;
285                                 channel->Extend(infokey, el);
286                         }
287
288                         // Clean the mask up
289                         if (this->tidy)
290                                 ModeParser::CleanMask(parameter);
291
292                         // Check if the item already exists in the list
293                         for (modelist::iterator it = el->begin(); it != el->end(); it++)
294                         {
295                                 if (parameter == it->mask)
296                                 {
297                                         /* Give a subclass a chance to error about this */
298                                         TellAlreadyOnList(source, channel, parameter);
299
300                                         // it does, deny the change
301                                         return MODEACTION_DENY;
302                                 }
303                         }
304
305                         unsigned int maxsize = 0;
306
307                         for (limitlist::iterator it = chanlimits.begin(); it != chanlimits.end(); it++)
308                         {
309                                 if (InspIRCd::Match(channel->name, it->mask))
310                                 {
311                                         // We have a pattern matching the channel...
312                                         maxsize = el->size();
313                                         if (IS_LOCAL(source) || (maxsize < it->limit))
314                                         {
315                                                 /* Ok, it *could* be allowed, now give someone subclassing us
316                                                  * a chance to validate the parameter.
317                                                  * The param is passed by reference, so they can both modify it
318                                                  * and tell us if we allow it or not.
319                                                  *
320                                                  * eg, the subclass could:
321                                                  * 1) allow
322                                                  * 2) 'fix' parameter and then allow
323                                                  * 3) deny
324                                                  */
325                                                 if (ValidateParam(source, channel, parameter))
326                                                 {
327                                                         // And now add the mask onto the list...
328                                                         ListItem e;
329                                                         e.mask = parameter;
330                                                         e.nick = servermode ? ServerInstance->Config->ServerName : source->nick;
331                                                         e.time = stringtime(ServerInstance);
332
333                                                         el->push_back(e);
334                                                         return MODEACTION_ALLOW;
335                                                 }
336                                                 else
337                                                 {
338                                                         /* If they deny it they have the job of giving an error message */
339                                                         return MODEACTION_DENY;
340                                                 }
341                                         }
342                                 }
343                         }
344
345                         /* List is full, give subclass a chance to send a custom message */
346                         if (!TellListTooLong(source, channel, parameter))
347                         {
348                                 source->WriteNumeric(478, "%s %s %s :Channel ban/ignore list is full", source->nick.c_str(), channel->name.c_str(), parameter.c_str());
349                         }
350
351                         parameter = "";
352                         return MODEACTION_DENY;
353                 }
354                 else
355                 {
356                         // We're taking the mode off
357                         if (el)
358                         {
359                                 for (modelist::iterator it = el->begin(); it != el->end(); it++)
360                                 {
361                                         if (parameter == it->mask)
362                                         {
363                                                 el->erase(it);
364                                                 if (el->size() == 0)
365                                                 {
366                                                         channel->Shrink(infokey);
367                                                         delete el;
368                                                 }
369                                                 return MODEACTION_ALLOW;
370                                         }
371                                 }
372                                 /* Tried to remove something that wasn't set */
373                                 TellNotSet(source, channel, parameter);
374                                 parameter = "";
375                                 return MODEACTION_DENY;
376                         }
377                         else
378                         {
379                                 /* Hmm, taking an exception off a non-existant list, DIE */
380                                 TellNotSet(source, channel, parameter);
381                                 parameter = "";
382                                 return MODEACTION_DENY;
383                         }
384                 }
385                 return MODEACTION_DENY;
386         }
387
388         /** Get Extensible key for this mode
389          */
390         virtual std::string& GetInfoKey()
391         {
392                 return infokey;
393         }
394
395         /** Handle channel deletion.
396          * See modules.h.
397          * @param chan Channel being deleted
398          */
399         virtual void DoChannelDelete(Channel* chan)
400         {
401                 modelist* mlist;
402                 chan->GetExt(infokey, mlist);
403
404                 if (mlist)
405                 {
406                         chan->Shrink(infokey);
407                         delete mlist;
408                 }
409         }
410
411         /** Syncronize channel item list with another server.
412          * See modules.h
413          * @param chan Channel to syncronize
414          * @param proto Protocol module pointer
415          * @param opaque Opaque connection handle
416          */
417         virtual void DoSyncChannel(Channel* chan, Module* proto, void* opaque)
418         {
419                 modelist* mlist;
420                 chan->GetExt(infokey, mlist);
421                 irc::modestacker modestack(ServerInstance, true);
422                 std::deque<std::string> stackresult;
423                 std::deque<TranslateType> types;
424                 types.push_back(TR_TEXT);
425                 if (mlist)
426                 {
427                         for (modelist::iterator it = mlist->begin(); it != mlist->end(); it++)
428                         {
429                                 modestack.Push(std::string(1, mode)[0], it->mask);
430                         }
431                 }
432                 while (modestack.GetStackedLine(stackresult))
433                 {
434                         types.assign(stackresult.size(), this->GetTranslateType());
435                         proto->ProtoSendMode(opaque, TYPE_CHANNEL, chan, stackresult, types);
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