]> git.netwichtig.de Git - user/henk/code/inspircd.git/blob - src/xline.cpp
3c1259eac6473cba0e78dd281ba7889be117f6a9
[user/henk/code/inspircd.git] / src / xline.cpp
1 /*       +------------------------------------+
2  *       | Inspire Internet Relay Chat Daemon |
3  *       +------------------------------------+
4  *
5  *  InspIRCd: (C) 2002-2007 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 /* $Core: libIRCDxline */
15
16 #include "inspircd.h"
17 #include "wildcard.h"
18 #include "xline.h"
19
20 /*
21  * This is now version 3 of the XLine subsystem, let's see if we can get it as nice and 
22  * efficient as we can this time so we can close this file and never ever touch it again ..
23  *
24  * Background:
25  *  Version 1 stored all line types in one list (one for g, one for z, etc). This was fine,
26  *  but both version 1 and 2 suck at applying lines efficiently. That is, every time a new line
27  *  was added, it iterated every existing line for every existing user. Ow. Expiry was also
28  *  expensive, as the lists were NOT sorted.
29  *
30  *  Version 2 moved permanent lines into a seperate list from non-permanent to help optimize
31  *  matching speed, but matched in the same way.
32  *  Expiry was also sped up by sorting the list by expiry (meaning just remove the items at the
33  *  head of the list that are outdated.)
34  *
35  * This was fine and good, but it looked less than ideal in code, and matching was still slower
36  * than it could have been, something which we address here.
37  *
38  * VERSION 3:
39  *  All lines are (as in v1) stored together -- no seperation of perm and non-perm. Expiry will
40  *  still use a sorted list, and we'll just ignore anything permanent.
41  *
42  *  Application will be by a list of lines 'pending' application, meaning only the newly added lines
43  *  will be gone over. Much faster.
44  *
45  * More of course is to come.
46  */
47
48 /* Version two, now with optimized expiry!
49  *
50  * Because the old way was horrendously slow, the new way of expiring xlines is very
51  * very efficient. I have improved the efficiency of the algorithm in two ways:
52  *
53  * (1) There are now two lists of items for each linetype. One list holds temporary
54  *     items, and the other list holds permanent items (ones which will expire).
55  *     Items which are on the permanent list are NEVER checked at all by the
56  *     expire_lines() function.
57  * (2) The temporary xline lists are always kept in strict numerical order, keyed by
58  *     current time + duration. This means that the line which is due to expire the
59  *     soonest is always pointed at by vector::begin(), so a simple while loop can
60  *     very efficiently, very quickly and above all SAFELY pick off the first few
61  *     items in the vector which need zapping.
62  *
63  *     -- Brain
64  */
65
66 bool XLine::Matches(User *u)
67 {
68         return false;
69 }
70
71 /*
72  * Checks what users match a given vector of ELines and sets their ban exempt flag accordingly.
73  */
74 void XLineManager::CheckELines(std::map<std::string, XLine *> &ELines)
75 {
76         if (ELines.empty())
77                 return;
78
79         for (std::vector<User*>::const_iterator u2 = ServerInstance->local_users.begin(); u2 != ServerInstance->local_users.end(); u2++)
80         {
81                 User* u = (User*)(*u2);
82
83                 for (std::map<std::string, XLine *>::iterator i = ELines.begin(); i != ELines.end(); i++)
84                 {
85                         XLine *e = i->second;
86                         u->exempt = e->Matches(u);
87                 }
88         }
89 }
90
91 // this should probably be moved to configreader, but atm it relies on CheckELines above.
92 bool DoneELine(ServerConfig* conf, const char* tag)
93 {
94         for (std::vector<User*>::const_iterator u2 = conf->GetInstance()->local_users.begin(); u2 != conf->GetInstance()->local_users.end(); u2++)
95         {
96                 User* u = (User*)(*u2);
97                 u->exempt = false;
98         }
99
100         conf->GetInstance()->XLines->CheckELines(conf->GetInstance()->XLines->lookup_lines['E']);
101         return true;
102 }
103
104
105 IdentHostPair XLineManager::IdentSplit(const std::string &ident_and_host)
106 {
107         IdentHostPair n = std::make_pair<std::string,std::string>("*","*");
108         std::string::size_type x = ident_and_host.find('@');
109         if (x != std::string::npos)
110         {
111                 n.second = ident_and_host.substr(x + 1,ident_and_host.length());
112                 n.first = ident_and_host.substr(0, x);
113                 if (!n.first.length())
114                         n.first.assign("*");
115                 if (!n.second.length())
116                         n.second.assign("*");
117         }
118         else
119         {
120                 n.second = ident_and_host;
121         }
122
123         return n;
124 }
125
126 // adds a g:line
127
128 /*bool XLineManager::AddELine(long duration, const char* source, const char* reason, const char* hostmask)*/
129 bool XLineManager::AddLine(XLine* line, User* user)
130 {
131         /*IdentHostPair ih = IdentSplit(hostmask);*/
132
133         if (DelLine(line->Displayable(), line->type, user, true))
134                 return false;
135
136         /*ELine* item = new ELine(ServerInstance, ServerInstance->Time(), duration, source, reason, ih.first.c_str(), ih.second.c_str());*/
137
138         active_lines.push_back(line);
139         sort(active_lines.begin(), active_lines.end(), XLineManager::XSortComparison);
140         pending_lines.push_back(line);
141         lookup_lines[line->type][line->Displayable()] = line;
142         line->OnAdd();
143
144         FOREACH_MOD(I_OnAddLine,OnAddLine(user, line)); 
145
146         return true;
147 }
148
149 /*bool XLineManager::AddZLine(long duration, const char* source, const char* reason, const char* ipaddr)
150 {
151         if (strchr(ipaddr,'@'))
152         {
153                 while (*ipaddr != '@')
154                         ipaddr++;
155                 ipaddr++;
156         }*/
157
158 // deletes a g:line, returns true if the line existed and was removed
159
160 bool XLineManager::DelLine(const char* hostmask, char type, User* user, bool simulate)
161 {
162         IdentHostPair ih = IdentSplit(hostmask);
163         for (std::vector<XLine*>::iterator i = active_lines.begin(); i != active_lines.end(); i++)
164         {
165                 if ((*i)->type == type)
166                 {
167                         if ((*i)->MatchesLiteral(hostmask))
168                         {
169                                 if (!simulate)
170                                 {
171                                         (*i)->Unset();
172                                         active_lines.erase(i);
173                                         if (lookup_lines.find(type) != lookup_lines.end())
174                                                 lookup_lines[type].erase(hostmask);
175
176                                         FOREACH_MOD(I_OnDelLine,OnDelLine(user, *i));
177
178                                         std::vector<XLine*>::iterator pptr = std::find(pending_lines.begin(), pending_lines.end(), *i);                                 
179                                         if (pptr != pending_lines.end())
180                                                 pending_lines.erase(pptr);
181
182                                         delete *i;
183                                 }
184                                 return true;
185                         }
186                 }
187         }
188
189         return false;
190 }
191
192
193 void ELine::Unset()
194 {
195         /* remove exempt from everyone and force recheck after deleting eline */
196         for (std::vector<User*>::const_iterator u2 = ServerInstance->local_users.begin(); u2 != ServerInstance->local_users.end(); u2++)
197         {
198                 User* u = (User*)(*u2);
199                 u->exempt = false;
200         }
201
202         if (ServerInstance->XLines->lookup_lines.find('E') != ServerInstance->XLines->lookup_lines.end())
203                 ServerInstance->XLines->CheckELines(ServerInstance->XLines->lookup_lines['E']);
204 }
205
206 // returns a pointer to the reason if a nickname matches a qline, NULL if it didnt match
207
208 QLine* XLineManager::matches_qline(const char* nick)
209 {
210         if (lookup_lines.find('Q') == lookup_lines.end())
211                 return NULL;
212
213         if (lookup_lines.find('Q') != lookup_lines.end() && lookup_lines['Q'].empty())
214                 return NULL;
215
216         for (std::vector<XLine*>::iterator i = active_lines.begin(); i != active_lines.end(); i++)
217                 if ((*i)->type == 'Q' && (*i)->Matches(nick))
218                         return (QLine*)(*i);
219         return NULL;
220 }
221
222 // returns a pointer to the reason if a host matches a gline, NULL if it didnt match
223
224 GLine* XLineManager::matches_gline(User* user)
225 {
226         if (lookup_lines.find('G') == lookup_lines.end())
227                 return NULL;
228
229         if (lookup_lines.find('G') != lookup_lines.end() && lookup_lines['G'].empty())
230                 return NULL;
231
232         for (std::vector<XLine*>::iterator i = active_lines.begin(); i != active_lines.end(); i++)
233                 if ((*i)->type == 'G' && (*i)->Matches(user))
234                         return (GLine*)(*i);
235
236         return NULL;
237 }
238
239 ELine* XLineManager::matches_exception(User* user)
240 {
241         if (lookup_lines.find('E') == lookup_lines.end())
242                 return NULL;
243
244         if (lookup_lines.find('E') != lookup_lines.end() && lookup_lines['E'].empty())
245                 return NULL;
246
247         for (std::vector<XLine*>::iterator i = active_lines.begin(); i != active_lines.end(); i++)
248         {
249                 if ((*i)->type == 'E' && (*i)->Matches(user))
250                         return (ELine*)(*i);
251         }
252         return NULL;
253 }
254
255
256 void XLineManager::gline_set_creation_time(const char* host, time_t create_time)
257 {
258         /*for (std::vector<XLine*>::iterator i = glines.begin(); i != glines.end(); i++)
259         {
260                 if (!strcasecmp(host,(*i)->hostmask))
261                 {
262                         (*i)->set_time = create_time;
263                         (*i)->expiry = create_time + (*i)->duration;
264                         return;
265                 }
266         }*/
267
268         return ;
269 }
270
271 void XLineManager::eline_set_creation_time(const char* host, time_t create_time)
272 {
273         /*for (std::vector<ELine*>::iterator i = elines.begin(); i != elines.end(); i++)
274         {
275                 if (!strcasecmp(host,(*i)->hostmask))
276                 {
277                         (*i)->set_time = create_time;
278                         (*i)->expiry = create_time + (*i)->duration;
279                         return;
280                 }
281         }*/
282
283         return;
284 }
285
286 void XLineManager::qline_set_creation_time(const char* nick, time_t create_time)
287 {
288         /*for (std::vector<QLine*>::iterator i = qlines.begin(); i != qlines.end(); i++)
289         {
290                 if (!strcasecmp(nick,(*i)->nick))
291                 {
292                         (*i)->set_time = create_time;
293                         (*i)->expiry = create_time + (*i)->duration;
294                         return;
295                 }
296         }*/
297
298         return;
299 }
300
301 void XLineManager::zline_set_creation_time(const char* ip, time_t create_time)
302 {
303         /*for (std::vector<ZLine*>::iterator i = zlines.begin(); i != zlines.end(); i++)
304         {
305                 if (!strcasecmp(ip,(*i)->ipaddr))
306                 {
307                         (*i)->set_time = create_time;
308                         (*i)->expiry = create_time + (*i)->duration;
309                         return;
310                 }
311         }*/
312
313         return;
314 }
315
316 // returns a pointer to the reason if an ip address matches a zline, NULL if it didnt match
317
318 ZLine* XLineManager::matches_zline(User *u)
319 {
320         if (lookup_lines.find('Z') == lookup_lines.end())
321                 return NULL;
322
323         if (lookup_lines.find('Z') != lookup_lines.end() && lookup_lines['Z'].empty())
324                 return NULL;
325
326         for (std::vector<XLine*>::iterator i = active_lines.begin(); i != active_lines.end(); i++)
327                 if ((*i)->type == 'Z' && (*i)->Matches(u))
328                         return (ZLine*)(*i);
329         return NULL;
330 }
331
332 // returns a pointer to the reason if a host matches a kline, NULL if it didnt match
333
334 KLine* XLineManager::matches_kline(User* user)
335 {
336         if (lookup_lines.find('K') == lookup_lines.end())
337                 return NULL;
338
339         if (lookup_lines.find('K') != lookup_lines.end() && lookup_lines['K'].empty())
340                 return NULL;
341
342         for (std::vector<XLine*>::iterator i = active_lines.begin(); i != active_lines.end(); i++)
343                 if ((*i)->Matches(user))
344                         return (KLine*)(*i);
345
346         return NULL;
347 }
348
349 bool XLineManager::XSortComparison(const XLine *one, const XLine *two)
350 {
351         // account for permanent lines
352         if (one->expiry == 0)
353         {
354                 return false;
355         }
356         return (one->expiry) < (two->expiry);
357 }
358
359 // removes lines that have expired
360 void XLineManager::expire_lines()
361 {
362         time_t current = ServerInstance->Time();
363
364         /* Because we now store all our XLines in sorted order using ((*i)->duration + (*i)->set_time) as a key, this
365          * means that to expire the XLines we just need to do a while, picking off the top few until there are
366          * none left at the head of the queue that are after the current time.
367          */
368
369         while ((active_lines.size()) && (current > (*active_lines.begin())->expiry) && ((*active_lines.begin())->duration != 0))
370         {
371                 std::vector<XLine*>::iterator i = active_lines.begin();
372                 (*i)->DisplayExpiry();
373                 (*i)->Unset();
374
375                 active_lines.erase(i);
376                 if (lookup_lines.find((*i)->type) != lookup_lines.end())
377                         lookup_lines[(*i)->type].erase((*i)->Displayable());
378
379                 std::vector<XLine*>::iterator pptr = std::find(pending_lines.begin(), pending_lines.end(), *i);
380                 if (pptr != pending_lines.end())
381                         pending_lines.erase(pptr);
382
383                 delete *i;
384         }
385 }
386
387 // applies lines, removing clients and changing nicks etc as applicable
388 void XLineManager::ApplyLines()
389 {
390         for (std::vector<User*>::const_iterator u2 = ServerInstance->local_users.begin(); u2 != ServerInstance->local_users.end(); u2++)
391         {
392                 User* u = (User*)(*u2);
393
394                 for (std::vector<XLine *>::iterator i = pending_lines.begin(); i != pending_lines.end(); i++)
395                 {
396                         XLine *x = *i;
397                         if (x->Matches(u))
398                                 x->Apply(u);
399                 }
400         }
401
402         pending_lines.clear();
403 }
404
405 void XLineManager::stats_k(User* user, string_list &results)
406 {
407         /*std::string sn = ServerInstance->Config->ServerName;
408         for (std::vector<KLine*>::iterator i = klines.begin(); i != klines.end(); i++)
409                 results.push_back(sn+" 216 "+user->nick+" :"+(*i)->identmask+"@"+(*i)->hostmask+" "+ConvToStr((*i)->set_time)+" "+ConvToStr((*i)->duration)+" "+(*i)->source+" :"+(*i)->reason);*/
410 }
411
412 void XLineManager::stats_g(User* user, string_list &results)
413 {
414         /*std::string sn = ServerInstance->Config->ServerName;
415         for (std::vector<GLine*>::iterator i = glines.begin(); i != glines.end(); i++)
416                 results.push_back(sn+" 223 "+user->nick+" :"+(*i)->identmask+"@"+(*i)->hostmask+" "+ConvToStr((*i)->set_time)+" "+ConvToStr((*i)->duration)+" "+(*i)->source+" :"+(*i)->reason);*/
417 }
418
419 void XLineManager::stats_q(User* user, string_list &results)
420 {
421         /*std::string sn = ServerInstance->Config->ServerName;
422         for (std::vector<QLine*>::iterator i = qlines.begin(); i != qlines.end(); i++)
423                 results.push_back(sn+" 217 "+user->nick+" :"+(*i)->nick+" "+ConvToStr((*i)->set_time)+" "+ConvToStr((*i)->duration)+" "+(*i)->source+" :"+(*i)->reason);*/
424 }
425
426 void XLineManager::stats_z(User* user, string_list &results)
427 {
428         /*std::string sn = ServerInstance->Config->ServerName;
429         for (std::vector<ZLine*>::iterator i = zlines.begin(); i != zlines.end(); i++)
430                 results.push_back(sn+" 223 "+user->nick+" :"+(*i)->ipaddr+" "+ConvToStr((*i)->set_time)+" "+ConvToStr((*i)->duration)+" "+(*i)->source+" :"+(*i)->reason);*/
431 }
432
433 void XLineManager::stats_e(User* user, string_list &results)
434 {
435         /*std::string sn = ServerInstance->Config->ServerName;
436         for (std::vector<ELine*>::iterator i = elines.begin(); i != elines.end(); i++)
437                 results.push_back(sn+" 223 "+user->nick+" :"+(*i)->identmask+"@"+(*i)->hostmask+" "+ConvToStr((*i)->set_time)+" "+ConvToStr((*i)->duration)+" "+(*i)->source+" :"+(*i)->reason);*/
438 }
439
440 XLineManager::XLineManager(InspIRCd* Instance) : ServerInstance(Instance)
441 {
442         GFact = new GLineFactory(Instance);
443         EFact = new ELineFactory(Instance);
444         KFact = new KLineFactory(Instance);
445         QFact = new QLineFactory(Instance);
446         ZFact = new ZLineFactory(Instance);
447
448         RegisterFactory(GFact);
449         RegisterFactory(EFact);
450         RegisterFactory(KFact);
451         RegisterFactory(QFact);
452         RegisterFactory(ZFact);
453 }
454
455 XLineManager::~XLineManager()
456 {
457         UnregisterFactory(GFact);
458         UnregisterFactory(EFact);
459         UnregisterFactory(KFact);
460         UnregisterFactory(QFact);
461         UnregisterFactory(ZFact);
462
463         delete GFact;
464         delete EFact;
465         delete KFact;
466         delete QFact;
467         delete ZFact;
468 }
469
470 void XLine::Apply(User* u)
471 {
472 }
473
474 void XLine::DefaultApply(User* u, char line)
475 {
476         char reason[MAXBUF];
477         snprintf(reason, MAXBUF, "%c-Lined: %s", line, this->reason);
478         if (*ServerInstance->Config->MoronBanner)
479                 u->WriteServ("NOTICE %s :*** %s", u->nick, ServerInstance->Config->MoronBanner);
480         if (ServerInstance->Config->HideBans)
481                 User::QuitUser(ServerInstance, u, line + std::string("-Lined"), reason);
482         else
483                 User::QuitUser(ServerInstance, u, reason);
484 }
485
486 bool KLine::Matches(User *u)
487 {
488         if (u->exempt)
489                 return false;
490
491         if ((match(u->ident, this->identmask)))
492         {
493                 if ((match(u->host, this->hostmask, true)) || (match(u->GetIPString(), this->hostmask, true)))
494                 {
495                         return true;
496                 }
497         }
498
499         return false;
500 }
501
502 void KLine::Apply(User* u)
503 {
504         DefaultApply(u, 'K');
505 }
506
507 bool GLine::Matches(User *u)
508 {
509         if (u->exempt)
510                 return false;
511
512         if ((match(u->ident, this->identmask)))
513         {
514                 if ((match(u->host, this->hostmask, true)) || (match(u->GetIPString(), this->hostmask, true)))
515                 {
516                         return true;
517                 }
518         }
519
520         return false;
521 }
522
523 void GLine::Apply(User* u)
524 {       
525         DefaultApply(u, 'G');
526 }
527
528 bool ELine::Matches(User *u)
529 {
530         if (u->exempt)
531                 return false;
532
533         if ((match(u->ident, this->identmask)))
534         {
535                 if ((match(u->host, this->hostmask, true)) || (match(u->GetIPString(), this->hostmask, true)))
536                 {
537                         return true;
538                 }
539         }
540
541         return false;
542 }
543
544 bool ZLine::Matches(User *u)
545 {
546         if (u->exempt)
547                 return false;
548
549         if (match(u->GetIPString(), this->ipaddr, true))
550                 return true;
551         else
552                 return false;
553 }
554
555 void ZLine::Apply(User* u)
556 {       
557         DefaultApply(u, 'Z');
558 }
559
560
561 bool QLine::Matches(User *u)
562 {
563         if (u->exempt)
564                 return false;
565
566         if (match(u->nick, this->nick))
567                 return true;
568
569         return false;
570 }
571
572 void QLine::Apply(User* u)
573 {       
574         /* Can we force the user to their uid here instead? */
575         DefaultApply(u, 'Q');
576 }
577
578
579 bool ZLine::Matches(const std::string &str)
580 {
581         if (match(str.c_str(), this->ipaddr, true))
582                 return true;
583         else
584                 return false;
585 }
586
587 bool QLine::Matches(const std::string &str)
588 {
589         if (match(str.c_str(), this->nick))
590                 return true;
591
592         return false;
593 }
594
595 bool ELine::Matches(const std::string &str)
596 {
597         return ((match(str.c_str(), matchtext.c_str(), true)));
598 }
599
600 bool KLine::Matches(const std::string &str)
601 {
602         return ((match(str.c_str(), matchtext.c_str(), true)));
603 }
604
605 bool GLine::Matches(const std::string &str)
606 {
607         return ((match(str.c_str(), matchtext.c_str(), true)));
608 }
609
610 bool ELine::MatchesLiteral(const std::string &str)
611 {
612         return (assign(str) == matchtext);
613 }
614
615 bool ZLine::MatchesLiteral(const std::string &str)
616 {       
617         return (assign(str) == this->ipaddr);
618 }
619
620 bool GLine::MatchesLiteral(const std::string &str)
621 {       
622         return (assign(str) == matchtext);
623 }
624
625 bool KLine::MatchesLiteral(const std::string &str)
626 {       
627         return (assign(str) == matchtext);
628 }
629
630 bool QLine::MatchesLiteral(const std::string &str)
631 {       
632         return (assign(str) == this->nick);
633 }
634
635 void ELine::OnAdd()
636 {
637         /* When adding one eline, only check the one eline */
638         for (std::vector<User*>::const_iterator u2 = ServerInstance->local_users.begin(); u2 != ServerInstance->local_users.end(); u2++)
639         {
640                 User* u = (User*)(*u2);
641                 if (this->Matches(u))
642                         u->exempt = true;
643         }
644 }
645
646 void ELine::DisplayExpiry()
647 {
648         ServerInstance->SNO->WriteToSnoMask('x',"Expiring timed E-Line %s@%s (set by %s %d seconds ago)",this->identmask,this->hostmask,this->source,this->duration);
649 }
650
651 void QLine::DisplayExpiry()
652 {
653         ServerInstance->SNO->WriteToSnoMask('x',"Expiring timed Q-Line %s (set by %s %d seconds ago)",this->nick,this->source,this->duration);
654 }
655
656 void ZLine::DisplayExpiry()
657 {
658         ServerInstance->SNO->WriteToSnoMask('x',"Expiring timed Z-Line %s (set by %s %d seconds ago)",this->ipaddr,this->source,this->duration);
659 }
660
661 void KLine::DisplayExpiry()
662 {
663         ServerInstance->SNO->WriteToSnoMask('x',"Expiring timed K-Line %s@%s (set by %s %d seconds ago)",this->identmask,this->hostmask,this->source,this->duration);
664 }
665
666 void GLine::DisplayExpiry()
667 {
668         ServerInstance->SNO->WriteToSnoMask('x',"Expiring timed G-Line %s@%s (set by %s %d seconds ago)",this->identmask,this->hostmask,this->source,this->duration);
669 }
670
671 const char* ELine::Displayable()
672 {
673         return matchtext.c_str();
674 }
675
676 const char* KLine::Displayable()
677 {
678         return matchtext.c_str();
679 }
680
681 const char* GLine::Displayable()
682 {
683         return matchtext.c_str();
684 }
685
686 const char* ZLine::Displayable()
687 {
688         return ipaddr;
689 }
690
691 const char* QLine::Displayable()
692 {
693         return nick;
694 }
695
696 bool XLineManager::RegisterFactory(XLineFactory* xlf)
697 {
698         std::map<char, XLineFactory*>::iterator n = line_factory.find(xlf->GetType());
699
700         if (n != line_factory.end())
701                 return false;
702
703         line_factory[xlf->GetType()] = xlf;
704
705         return true;
706 }
707
708 bool XLineManager::UnregisterFactory(XLineFactory* xlf)
709 {
710         std::map<char, XLineFactory*>::iterator n = line_factory.find(xlf->GetType());
711
712         if (n == line_factory.end())
713                 return false;
714
715         line_factory.erase(n);
716
717         return true;
718 }
719
720 XLineFactory* XLineManager::GetFactory(const char type)
721 {
722         std::map<char, XLineFactory*>::iterator n = line_factory.find(type);
723
724         if (n != line_factory.end())
725                 return NULL;
726
727         return n->second;
728 }
729