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