]> git.netwichtig.de Git - user/henk/code/inspircd.git/blob - src/xline.cpp
aa43ac43bc475edbac7324d384b61b53c9339b1e
[user/henk/code/inspircd.git] / src / xline.cpp
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 #include "inspircd.h"
15 #include "xline.h"
16 #include "bancache.h"
17
18 /** An XLineFactory specialized to generate GLine* pointers
19  */
20 class GLineFactory : public XLineFactory
21 {
22  public:
23         GLineFactory() : XLineFactory("G") { }
24
25         /** Generate a GLine
26          */
27         XLine* Generate(time_t set_time, long duration, std::string source, std::string reason, std::string xline_specific_mask)
28         {
29                 IdentHostPair ih = ServerInstance->XLines->IdentSplit(xline_specific_mask);
30                 return new GLine(set_time, duration, source, reason, ih.first, ih.second);
31         }
32 };
33
34 /** An XLineFactory specialized to generate ELine* pointers
35  */
36 class ELineFactory : public XLineFactory
37 {
38  public:
39         ELineFactory() : XLineFactory("E") { }
40
41         /** Generate an ELine
42          */
43         XLine* Generate(time_t set_time, long duration, std::string source, std::string reason, std::string xline_specific_mask)
44         {
45                 IdentHostPair ih = ServerInstance->XLines->IdentSplit(xline_specific_mask);
46                 return new ELine(set_time, duration, source, reason, ih.first, ih.second);
47         }
48 };
49
50 /** An XLineFactory specialized to generate KLine* pointers
51  */
52 class KLineFactory : public XLineFactory
53 {
54  public:
55         KLineFactory() : XLineFactory("K") { }
56
57         /** Generate a KLine
58          */
59         XLine* Generate(time_t set_time, long duration, std::string source, std::string reason, std::string xline_specific_mask)
60         {
61                 IdentHostPair ih = ServerInstance->XLines->IdentSplit(xline_specific_mask);
62                 return new KLine(set_time, duration, source, reason, ih.first, ih.second);
63         }
64 };
65
66 /** An XLineFactory specialized to generate QLine* pointers
67  */
68 class QLineFactory : public XLineFactory
69 {
70  public:
71         QLineFactory() : XLineFactory("Q") { }
72
73         /** Generate a QLine
74          */
75         XLine* Generate(time_t set_time, long duration, std::string source, std::string reason, std::string xline_specific_mask)
76         {
77                 return new QLine(set_time, duration, source, reason, xline_specific_mask);
78         }
79 };
80
81 /** An XLineFactory specialized to generate ZLine* pointers
82  */
83 class ZLineFactory : public XLineFactory
84 {
85  public:
86         ZLineFactory() : XLineFactory("Z") { }
87
88         /** Generate a ZLine
89          */
90         XLine* Generate(time_t set_time, long duration, std::string source, std::string reason, std::string xline_specific_mask)
91         {
92                 return new ZLine(set_time, duration, source, reason, xline_specific_mask);
93         }
94 };
95
96
97 /*
98  * This is now version 3 of the XLine subsystem, let's see if we can get it as nice and
99  * efficient as we can this time so we can close this file and never ever touch it again ..
100  *
101  * Background:
102  *  Version 1 stored all line types in one list (one for g, one for z, etc). This was fine,
103  *  but both version 1 and 2 suck at applying lines efficiently. That is, every time a new line
104  *  was added, it iterated every existing line for every existing user. Ow. Expiry was also
105  *  expensive, as the lists were NOT sorted.
106  *
107  *  Version 2 moved permanent lines into a seperate list from non-permanent to help optimize
108  *  matching speed, but matched in the same way.
109  *  Expiry was also sped up by sorting the list by expiry (meaning just remove the items at the
110  *  head of the list that are outdated.)
111  *
112  * This was fine and good, but it looked less than ideal in code, and matching was still slower
113  * than it could have been, something which we address here.
114  *
115  * VERSION 3:
116  *  All lines are (as in v1) stored together -- no seperation of perm and non-perm. They are stored in
117  *  a map of maps (first map is line type, second map is for quick lookup on add/delete/etc).
118  *
119  *  Expiry is *no longer* performed on a timer, and no longer uses a sorted list of any variety. This
120  *  is now done by only checking for expiry when a line is accessed, meaning that expiry is no longer
121  *  a resource intensive problem.
122  *
123  *  Application no longer tries to apply every single line on every single user - instead, now only lines
124  *  added since the previous application are applied. This keeps S2S ADDLINE during burst nice and fast,
125  *  while at the same time not slowing things the fuck down when we try adding a ban with lots of preexisting
126  *  bans. :)
127  */
128
129 bool XLine::Matches(User *u)
130 {
131         return false;
132 }
133
134 /*
135  * Checks what users match a given vector of ELines and sets their ban exempt flag accordingly.
136  */
137 void XLineManager::CheckELines()
138 {
139         ContainerIter n = lookup_lines.find("E");
140
141         if (n == lookup_lines.end())
142                 return;
143
144         XLineLookup& ELines = n->second;
145
146         if (ELines.empty())
147                 return;
148
149         for (std::vector<LocalUser*>::const_iterator u2 = ServerInstance->Users->local_users.begin(); u2 != ServerInstance->Users->local_users.end(); u2++)
150         {
151                 User* u = (User*)(*u2);
152
153                 /* This uses safe iteration to ensure that if a line expires here, it doenst trash the iterator */
154                 LookupIter safei;
155
156                 for (LookupIter i = ELines.begin(); i != ELines.end(); )
157                 {
158                         safei = i;
159                         safei++;
160
161                         XLine *e = i->second;
162                         u->exempt = e->Matches(u);
163
164                         i = safei;
165                 }
166         }
167 }
168
169
170 XLineLookup* XLineManager::GetAll(const std::string &type)
171 {
172         ContainerIter n = lookup_lines.find(type);
173
174         if (n == lookup_lines.end())
175                 return NULL;
176
177         LookupIter safei;
178         const time_t current = ServerInstance->Time();
179
180         /* Expire any dead ones, before sending */
181         for (LookupIter x = n->second.begin(); x != n->second.end(); )
182         {
183                 safei = x;
184                 safei++;
185                 if (x->second->duration && current > x->second->expiry)
186                 {
187                         ExpireLine(n, x);
188                 }
189                 x = safei;
190         }
191
192         return &(n->second);
193 }
194
195 void XLineManager::DelAll(const std::string &type)
196 {
197         ContainerIter n = lookup_lines.find(type);
198
199         if (n == lookup_lines.end())
200                 return;
201
202         LookupIter x;
203
204         /* Delete all of a given type (this should probably use DelLine, but oh well) */
205         while ((x = n->second.begin()) != n->second.end())
206         {
207                 ExpireLine(n, x);
208         }
209 }
210
211 std::vector<std::string> XLineManager::GetAllTypes()
212 {
213         std::vector<std::string> items;
214         for (ContainerIter x = lookup_lines.begin(); x != lookup_lines.end(); ++x)
215                 items.push_back(x->first);
216         return items;
217 }
218
219 IdentHostPair XLineManager::IdentSplit(const std::string &ident_and_host)
220 {
221         IdentHostPair n = std::make_pair<std::string,std::string>("*","*");
222         std::string::size_type x = ident_and_host.find('@');
223         if (x != std::string::npos)
224         {
225                 n.second = ident_and_host.substr(x + 1,ident_and_host.length());
226                 n.first = ident_and_host.substr(0, x);
227                 if (!n.first.length())
228                         n.first.assign("*");
229                 if (!n.second.length())
230                         n.second.assign("*");
231         }
232         else
233         {
234                 n.first = "";
235                 n.second = ident_and_host;
236         }
237
238         return n;
239 }
240
241 // adds a line
242
243 bool XLineManager::AddLine(XLine* line, User* user)
244 {
245         ServerInstance->BanCache->RemoveEntries(line->type, false); // XXX perhaps remove ELines here?
246
247         if (line->duration && ServerInstance->Time() > line->expiry)
248                 return false; // Don't apply expired XLines.
249
250         /* Don't apply duplicate xlines */
251         ContainerIter x = lookup_lines.find(line->type);
252         if (x != lookup_lines.end())
253         {
254                 LookupIter i = x->second.find(line->Displayable());
255                 if (i != x->second.end())
256                 {
257                         return false;
258                 }
259         }
260
261         /*ELine* item = new ELine(ServerInstance->Time(), duration, source, reason, ih.first.c_str(), ih.second.c_str());*/
262         XLineFactory* xlf = GetFactory(line->type);
263         if (!xlf)
264                 return false;
265
266         if (xlf->AutoApplyToUserList(line))
267                 pending_lines.push_back(line);
268
269         lookup_lines[line->type][line->Displayable()] = line;
270         line->OnAdd();
271
272         FOREACH_MOD(I_OnAddLine,OnAddLine(user, line));
273
274         return true;
275 }
276
277 // deletes a line, returns true if the line existed and was removed
278
279 bool XLineManager::DelLine(const char* hostmask, const std::string &type, User* user, bool simulate)
280 {
281         ContainerIter x = lookup_lines.find(type);
282
283         if (x == lookup_lines.end())
284                 return false;
285
286         LookupIter y = x->second.find(hostmask);
287
288         if (y == x->second.end())
289                 return false;
290
291         if (simulate)
292                 return true;
293
294         ServerInstance->BanCache->RemoveEntries(y->second->type, true);
295
296         FOREACH_MOD(I_OnDelLine,OnDelLine(user, y->second));
297
298         y->second->Unset();
299
300         std::vector<XLine*>::iterator pptr = std::find(pending_lines.begin(), pending_lines.end(), y->second);
301         if (pptr != pending_lines.end())
302                 pending_lines.erase(pptr);
303
304         delete y->second;
305         x->second.erase(y);
306
307         return true;
308 }
309
310
311 void ELine::Unset()
312 {
313         /* remove exempt from everyone and force recheck after deleting eline */
314         for (std::vector<LocalUser*>::const_iterator u2 = ServerInstance->Users->local_users.begin(); u2 != ServerInstance->Users->local_users.end(); u2++)
315         {
316                 User* u = (User*)(*u2);
317                 u->exempt = false;
318         }
319
320         ServerInstance->XLines->CheckELines();
321 }
322
323 // returns a pointer to the reason if a nickname matches a qline, NULL if it didnt match
324
325 XLine* XLineManager::MatchesLine(const std::string &type, User* user)
326 {
327         ContainerIter x = lookup_lines.find(type);
328
329         if (x == lookup_lines.end())
330                 return NULL;
331
332         const time_t current = ServerInstance->Time();
333
334         LookupIter safei;
335
336         for (LookupIter i = x->second.begin(); i != x->second.end(); )
337         {
338                 safei = i;
339                 safei++;
340
341                 if (i->second->duration && current > i->second->expiry)
342                 {
343                         /* Expire the line, proceed to next one */
344                         ExpireLine(x, i);
345                         i = safei;
346                         continue;
347                 }
348
349                 if (i->second->Matches(user))
350                 {
351                         return i->second;
352                 }
353
354                 i = safei;
355         }
356         return NULL;
357 }
358
359 XLine* XLineManager::MatchesLine(const std::string &type, const std::string &pattern)
360 {
361         ContainerIter x = lookup_lines.find(type);
362
363         if (x == lookup_lines.end())
364                 return NULL;
365
366         const time_t current = ServerInstance->Time();
367
368          LookupIter safei;
369
370         for (LookupIter i = x->second.begin(); i != x->second.end(); )
371         {
372                 safei = i;
373                 safei++;
374
375                 if (i->second->Matches(pattern))
376                 {
377                         if (i->second->duration && current > i->second->expiry)
378                         {
379                                 /* Expire the line, return nothing */
380                                 ExpireLine(x, i);
381                                 /* See above */
382                                 i = safei;
383                                 continue;
384                         }
385                         else
386                                 return i->second;
387                 }
388
389                 i = safei;
390         }
391         return NULL;
392 }
393
394 // removes lines that have expired
395 void XLineManager::ExpireLine(ContainerIter container, LookupIter item)
396 {
397         FOREACH_MOD(I_OnExpireLine, OnExpireLine(item->second));
398
399         item->second->DisplayExpiry();
400         item->second->Unset();
401
402         /* TODO: Can we skip this loop by having a 'pending' field in the XLine class, which is set when a line
403          * is pending, cleared when it is no longer pending, so we skip over this loop if its not pending?
404          * -- Brain
405          */
406         std::vector<XLine*>::iterator pptr = std::find(pending_lines.begin(), pending_lines.end(), item->second);
407         if (pptr != pending_lines.end())
408                 pending_lines.erase(pptr);
409
410         delete item->second;
411         container->second.erase(item);
412 }
413
414
415 // applies lines, removing clients and changing nicks etc as applicable
416 void XLineManager::ApplyLines()
417 {
418         std::vector<LocalUser*>::reverse_iterator u2 = ServerInstance->Users->local_users.rbegin();
419         while (u2 != ServerInstance->Users->local_users.rend())
420         {
421                 User* u = *u2++;
422
423                 // Don't ban people who are exempt.
424                 if (u->exempt)
425                         continue;
426
427                 for (std::vector<XLine *>::iterator i = pending_lines.begin(); i != pending_lines.end(); i++)
428                 {
429                         XLine *x = *i;
430                         if (x->Matches(u))
431                                 x->Apply(u);
432                 }
433         }
434
435         pending_lines.clear();
436 }
437
438 void XLineManager::InvokeStats(const std::string &type, int numeric, User* user, string_list &results)
439 {
440         std::string sn = ServerInstance->Config->ServerName;
441
442         ContainerIter n = lookup_lines.find(type);
443
444         time_t current = ServerInstance->Time();
445
446         LookupIter safei;
447
448         if (n != lookup_lines.end())
449         {
450                 XLineLookup& list = n->second;
451                 for (LookupIter i = list.begin(); i != list.end(); )
452                 {
453                         safei = i;
454                         safei++;
455
456                         if (i->second->duration && current > i->second->expiry)
457                         {
458                                 ExpireLine(n, i);
459                         }
460                         else
461                                 results.push_back(sn+" "+ConvToStr(numeric)+" "+user->nick+" :"+i->second->Displayable()+" "+
462                                         ConvToStr(i->second->set_time)+" "+ConvToStr(i->second->duration)+" "+std::string(i->second->source)+" :"+(i->second->reason));
463                         i = safei;
464                 }
465         }
466 }
467
468
469 XLineManager::XLineManager()
470 {
471         GLineFactory* GFact;
472         ELineFactory* EFact;
473         KLineFactory* KFact;
474         QLineFactory* QFact;
475         ZLineFactory* ZFact;
476
477
478         GFact = new GLineFactory;
479         EFact = new ELineFactory;
480         KFact = new KLineFactory;
481         QFact = new QLineFactory;
482         ZFact = new ZLineFactory;
483
484         RegisterFactory(GFact);
485         RegisterFactory(EFact);
486         RegisterFactory(KFact);
487         RegisterFactory(QFact);
488         RegisterFactory(ZFact);
489 }
490
491 XLineManager::~XLineManager()
492 {
493         const char gekqz[] = "GEKQZ";
494         for(unsigned int i=0; i < sizeof(gekqz); i++)
495         {
496                 XLineFactory* xlf = GetFactory(std::string(1, gekqz[i]));
497                 UnregisterFactory(xlf);
498                 delete xlf;
499         }
500
501         // Delete all existing XLines
502         for (XLineContainer::iterator i = lookup_lines.begin(); i != lookup_lines.end(); i++)
503         {
504                 for (XLineLookup::iterator j = i->second.begin(); j != i->second.end(); j++)
505                 {
506                         delete j->second;
507                 }
508                 i->second.clear();
509         }
510         lookup_lines.clear();
511
512 }
513
514 void XLine::Apply(User* u)
515 {
516 }
517
518 bool XLine::IsBurstable()
519 {
520         return true;
521 }
522
523 void XLine::DefaultApply(User* u, const std::string &line, bool bancache)
524 {
525         char sreason[MAXBUF];
526         snprintf(sreason, MAXBUF, "%s-Lined: %s", line.c_str(), this->reason.c_str());
527         if (!ServerInstance->Config->MoronBanner.empty())
528                 u->WriteServ("NOTICE %s :*** %s", u->nick.c_str(), ServerInstance->Config->MoronBanner.c_str());
529
530         if (ServerInstance->Config->HideBans)
531                 ServerInstance->Users->QuitUser(u, line + "-Lined", sreason);
532         else
533                 ServerInstance->Users->QuitUser(u, sreason);
534
535
536         if (bancache)
537         {
538                 ServerInstance->Logs->Log("BANCACHE", DEBUG, std::string("BanCache: Adding positive hit (") + line + ") for " + u->GetIPString());
539                 if (this->duration > 0)
540                         ServerInstance->BanCache->AddHit(u->GetIPString(), this->type, line + "-Lined: " + this->reason, this->duration);
541                 else
542                         ServerInstance->BanCache->AddHit(u->GetIPString(), this->type, line + "-Lined: " + this->reason);
543         }
544 }
545
546 bool KLine::Matches(User *u)
547 {
548         if (u->exempt)
549                 return false;
550
551         if (InspIRCd::Match(u->ident, this->identmask, ascii_case_insensitive_map))
552         {
553                 if (InspIRCd::MatchCIDR(u->host, this->hostmask, ascii_case_insensitive_map) ||
554                     InspIRCd::MatchCIDR(u->GetIPString(), this->hostmask, ascii_case_insensitive_map))
555                 {
556                         return true;
557                 }
558         }
559
560         return false;
561 }
562
563 void KLine::Apply(User* u)
564 {
565         DefaultApply(u, "K", (this->identmask ==  "*") ? true : false);
566 }
567
568 bool GLine::Matches(User *u)
569 {
570         if (u->exempt)
571                 return false;
572
573         if (InspIRCd::Match(u->ident, this->identmask, ascii_case_insensitive_map))
574         {
575                 if (InspIRCd::MatchCIDR(u->host, this->hostmask, ascii_case_insensitive_map) ||
576                     InspIRCd::MatchCIDR(u->GetIPString(), this->hostmask, ascii_case_insensitive_map))
577                 {
578                         return true;
579                 }
580         }
581
582         return false;
583 }
584
585 void GLine::Apply(User* u)
586 {
587         DefaultApply(u, "G", (this->identmask == "*") ? true : false);
588 }
589
590 bool ELine::Matches(User *u)
591 {
592         if (u->exempt)
593                 return false;
594
595         if (InspIRCd::Match(u->ident, this->identmask, ascii_case_insensitive_map))
596         {
597                 if (InspIRCd::MatchCIDR(u->host, this->hostmask, ascii_case_insensitive_map) ||
598                     InspIRCd::MatchCIDR(u->GetIPString(), this->hostmask, ascii_case_insensitive_map))
599                 {
600                         return true;
601                 }
602         }
603
604         return false;
605 }
606
607 bool ZLine::Matches(User *u)
608 {
609         if (u->exempt)
610                 return false;
611
612         if (InspIRCd::MatchCIDR(u->GetIPString(), this->ipaddr))
613                 return true;
614         else
615                 return false;
616 }
617
618 void ZLine::Apply(User* u)
619 {
620         DefaultApply(u, "Z", true);
621 }
622
623
624 bool QLine::Matches(User *u)
625 {
626         if (InspIRCd::Match(u->nick, this->nick))
627                 return true;
628
629         return false;
630 }
631
632 void QLine::Apply(User* u)
633 {
634         /* Force to uuid on apply of qline, no need to disconnect any more :) */
635         u->ForceNickChange(u->uuid.c_str());
636 }
637
638
639 bool ZLine::Matches(const std::string &str)
640 {
641         if (InspIRCd::MatchCIDR(str, this->ipaddr))
642                 return true;
643         else
644                 return false;
645 }
646
647 bool QLine::Matches(const std::string &str)
648 {
649         if (InspIRCd::Match(str, this->nick))
650                 return true;
651
652         return false;
653 }
654
655 bool ELine::Matches(const std::string &str)
656 {
657         return (InspIRCd::MatchCIDR(str, matchtext));
658 }
659
660 bool KLine::Matches(const std::string &str)
661 {
662         return (InspIRCd::MatchCIDR(str.c_str(), matchtext));
663 }
664
665 bool GLine::Matches(const std::string &str)
666 {
667         return (InspIRCd::MatchCIDR(str, matchtext));
668 }
669
670 void ELine::OnAdd()
671 {
672         /* When adding one eline, only check the one eline */
673         for (std::vector<LocalUser*>::const_iterator u2 = ServerInstance->Users->local_users.begin(); u2 != ServerInstance->Users->local_users.end(); u2++)
674         {
675                 User* u = (User*)(*u2);
676                 if (this->Matches(u))
677                         u->exempt = true;
678         }
679 }
680
681 void ELine::DisplayExpiry()
682 {
683         ServerInstance->SNO->WriteToSnoMask('x',"Removing expired E-Line %s@%s (set by %s %ld seconds ago)",
684                 identmask.c_str(),hostmask.c_str(),source.c_str(),(long)(ServerInstance->Time() - this->set_time));
685 }
686
687 void QLine::DisplayExpiry()
688 {
689         ServerInstance->SNO->WriteToSnoMask('x',"Removing expired Q-Line %s (set by %s %ld seconds ago)",
690                 nick.c_str(),source.c_str(),(long)(ServerInstance->Time() - this->set_time));
691 }
692
693 void ZLine::DisplayExpiry()
694 {
695         ServerInstance->SNO->WriteToSnoMask('x',"Removing expired Z-Line %s (set by %s %ld seconds ago)",
696                 ipaddr.c_str(),source.c_str(),(long)(ServerInstance->Time() - this->set_time));
697 }
698
699 void KLine::DisplayExpiry()
700 {
701         ServerInstance->SNO->WriteToSnoMask('x',"Removing expired K-Line %s@%s (set by %s %ld seconds ago)",
702                 identmask.c_str(),hostmask.c_str(),source.c_str(),(long)(ServerInstance->Time() - this->set_time));
703 }
704
705 void GLine::DisplayExpiry()
706 {
707         ServerInstance->SNO->WriteToSnoMask('x',"Removing expired G-Line %s@%s (set by %s %ld seconds ago)",
708                 identmask.c_str(),hostmask.c_str(),source.c_str(),(long)(ServerInstance->Time() - this->set_time));
709 }
710
711 const char* ELine::Displayable()
712 {
713         return matchtext.c_str();
714 }
715
716 const char* KLine::Displayable()
717 {
718         return matchtext.c_str();
719 }
720
721 const char* GLine::Displayable()
722 {
723         return matchtext.c_str();
724 }
725
726 const char* ZLine::Displayable()
727 {
728         return ipaddr.c_str();
729 }
730
731 const char* QLine::Displayable()
732 {
733         return nick.c_str();
734 }
735
736 bool KLine::IsBurstable()
737 {
738         return false;
739 }
740
741 bool XLineManager::RegisterFactory(XLineFactory* xlf)
742 {
743         XLineFactMap::iterator n = line_factory.find(xlf->GetType());
744
745         if (n != line_factory.end())
746                 return false;
747
748         line_factory[xlf->GetType()] = xlf;
749
750         return true;
751 }
752
753 bool XLineManager::UnregisterFactory(XLineFactory* xlf)
754 {
755         XLineFactMap::iterator n = line_factory.find(xlf->GetType());
756
757         if (n == line_factory.end())
758                 return false;
759
760         line_factory.erase(n);
761
762         return true;
763 }
764
765 XLineFactory* XLineManager::GetFactory(const std::string &type)
766 {
767         XLineFactMap::iterator n = line_factory.find(type);
768
769         if (n == line_factory.end())
770                 return NULL;
771
772         return n->second;
773 }