]> git.netwichtig.de Git - user/henk/code/inspircd.git/blob - src/xline.cpp
Add the lookup_lines stuff. Sorry i havent documented this yet, i am tired.
[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 InitXLine(ServerConfig* conf, const char* tag)
67 {
68         return true;
69 }
70
71 bool DoneZLine(ServerConfig* conf, const char* tag)
72 {
73         // XXX we should really only call this once - after we've finished processing configuration all together
74         conf->GetInstance()->XLines->ApplyLines();
75         return true;
76 }
77
78 bool DoneQLine(ServerConfig* conf, const char* tag)
79 {
80         // XXX we should really only call this once - after we've finished processing configuration all together
81         conf->GetInstance()->XLines->ApplyLines();
82         return true;
83 }
84
85 bool DoneKLine(ServerConfig* conf, const char* tag)
86 {
87         // XXX we should really only call this once - after we've finished processing configuration all together
88         conf->GetInstance()->XLines->ApplyLines();
89         return true;
90 }
91
92 bool DoneELine(ServerConfig* conf, const char* tag)
93 {
94         // XXX we should really only call this once - after we've finished processing configuration all together
95         conf->GetInstance()->XLines->ApplyLines();
96         return true;
97 }
98
99 bool DoZLine(ServerConfig* conf, const char* tag, char** entries, ValueList &values, int* types)
100 {
101         const char* reason = values[0].GetString();
102         const char* ipmask = values[1].GetString();
103
104         conf->GetInstance()->XLines->AddZLine(0,"<Config>",reason,ipmask);
105         return true;
106 }
107
108 bool DoQLine(ServerConfig* conf, const char* tag, char** entries, ValueList &values, int* types)
109 {
110         const char* reason = values[0].GetString();
111         const char* nick = values[1].GetString();
112
113         conf->GetInstance()->XLines->AddQLine(0,"<Config>",reason,nick);
114         return true;
115 }
116
117 bool DoKLine(ServerConfig* conf, const char* tag, char** entries, ValueList &values, int* types)
118 {
119         const char* reason = values[0].GetString();
120         const char* host = values[1].GetString();
121
122         conf->GetInstance()->XLines->AddKLine(0,"<Config>",reason,host);
123         return true;
124 }
125
126 bool DoELine(ServerConfig* conf, const char* tag, char** entries, ValueList &values, int* types)
127 {
128         const char* reason = values[0].GetString();
129         const char* host = values[1].GetString();
130
131         conf->GetInstance()->XLines->AddELine(0,"<Config>",reason,host);
132         return true;
133 }
134
135 bool XLine::Matches(User *u)
136 {
137         return false;
138 }
139
140 //XXX perhaps move into xlinemanager
141 void CheckELines(InspIRCd *ServerInstance, std::map<std::string, XLine *> &ELines)
142 {
143         if (ELines.empty())
144                 return;
145
146         for (std::vector<User*>::const_iterator u2 = ServerInstance->local_users.begin(); u2 != ServerInstance->local_users.end(); u2++)
147         {
148                 User* u = (User*)(*u2);
149
150                 for (std::map<std::string, XLine *>::iterator i = ELines.begin(); i != ELines.end(); i++)
151                 {
152                         XLine *e = i->second;
153                         u->exempt = e->Matches(u);
154                 }
155         }
156 }
157
158
159 IdentHostPair XLineManager::IdentSplit(const std::string &ident_and_host)
160 {
161         IdentHostPair n = std::make_pair<std::string,std::string>("*","*");
162         std::string::size_type x = ident_and_host.find('@');
163         if (x != std::string::npos)
164         {
165                 n.second = ident_and_host.substr(x + 1,ident_and_host.length());
166                 n.first = ident_and_host.substr(0, x);
167                 if (!n.first.length())
168                         n.first.assign("*");
169                 if (!n.second.length())
170                         n.second.assign("*");
171         }
172         else
173         {
174                 n.second = ident_and_host;
175         }
176
177         return n;
178 }
179
180 // adds a g:line
181
182 bool XLineManager::AddGLine(long duration, const char* source,const char* reason,const char* hostmask)
183 {
184         IdentHostPair ih = IdentSplit(hostmask);
185
186         if (DelLine(hostmask, 'G', true))
187                 return false;
188
189         GLine* item = new GLine(ServerInstance, ServerInstance->Time(), duration, source, reason, ih.first.c_str(), ih.second.c_str());
190
191         active_lines.push_back(item);
192         sort(active_lines.begin(), active_lines.end(),XLineManager::XSortComparison);
193         pending_lines.push_back(item);
194         lookup_lines['G'][hostmask] = item;
195
196         return true;
197 }
198
199 // adds an e:line (exception to bans)
200
201 bool XLineManager::AddELine(long duration, const char* source, const char* reason, const char* hostmask)
202 {
203         IdentHostPair ih = IdentSplit(hostmask);
204
205         if (DelLine(hostmask, 'E', true))
206                 return false;
207
208         ELine* item = new ELine(ServerInstance, ServerInstance->Time(), duration, source, reason, ih.first.c_str(), ih.second.c_str());
209
210         active_lines.push_back(item);
211         sort(active_lines.begin(), active_lines.end(),XLineManager::XSortComparison);
212         lookup_lines['E'][hostmask] = item;
213
214         // XXX we really only need to check one line (the new one) - this is a bit wasteful!
215         CheckELines(ServerInstance, lookup_lines['E']);
216
217         return true;
218 }
219
220 // adds a q:line
221
222 bool XLineManager::AddQLine(long duration, const char* source, const char* reason, const char* nickname)
223 {
224         if (DelLine(nickname, 'Q', true))
225                 return false;
226
227         QLine* item = new QLine(ServerInstance, ServerInstance->Time(), duration, source, reason, nickname);
228
229         active_lines.push_back(item);
230         sort(active_lines.begin(), active_lines.end(), XLineManager::XSortComparison);
231         pending_lines.push_back(item);
232         lookup_lines['Q'][nickname] = item;
233
234         return true;
235 }
236
237 // adds a z:line
238
239 bool XLineManager::AddZLine(long duration, const char* source, const char* reason, const char* ipaddr)
240 {
241         if (strchr(ipaddr,'@'))
242         {
243                 while (*ipaddr != '@')
244                         ipaddr++;
245                 ipaddr++;
246         }
247
248         if (DelLine(ipaddr, 'Z', true))
249                 return false;
250
251         ZLine* item = new ZLine(ServerInstance, ServerInstance->Time(), duration, source, reason, ipaddr);
252
253         active_lines.push_back(item);
254         sort(active_lines.begin(), active_lines.end(),XLineManager::XSortComparison);
255         pending_lines.push_back(item);
256         lookup_lines['Z'][ipaddr] = item;
257
258         return true;
259 }
260
261 // adds a k:line
262
263 bool XLineManager::AddKLine(long duration, const char* source, const char* reason, const char* hostmask)
264 {
265         IdentHostPair ih = IdentSplit(hostmask);
266
267         if (DelLine(hostmask, 'K', true))
268                 return false;
269
270         KLine* item = new KLine(ServerInstance, ServerInstance->Time(), duration, source, reason, ih.first.c_str(), ih.second.c_str());
271
272         active_lines.push_back(item);
273         sort(active_lines.begin(), active_lines.end(),XLineManager::XSortComparison);
274         pending_lines.push_back(item);
275         lookup_lines['K'][hostmask] = item;
276
277         return true;
278 }
279
280 // deletes a g:line, returns true if the line existed and was removed
281
282 bool XLineManager::DelLine(const char* hostmask, char type, bool simulate)
283 {
284         IdentHostPair ih = IdentSplit(hostmask);
285         for (std::vector<XLine*>::iterator i = active_lines.begin(); i != active_lines.end(); i++)
286         {
287                 if ((*i)->type == type)
288                 {
289                         if ((*i)->Matches(hostmask))
290                         {
291                                 if (!simulate)
292                                 {
293                                         (*i)->Unset();
294                                         delete *i;
295                                         active_lines.erase(i);
296                                         if (lookup_lines.find(type) != lookup_lines.end())
297                                                 lookup_lines[type].erase(hostmask);
298                                         /* XXX: Should erase from pending lines here */
299                                 }
300                                 return true;
301                         }
302                 }
303         }
304
305         return false;
306 }
307
308
309 void ELine::Unset()
310 {
311         /* remove exempt from everyone and force recheck after deleting eline */
312         for (std::vector<User*>::const_iterator u2 = ServerInstance->local_users.begin(); u2 != ServerInstance->local_users.end(); u2++)
313         {
314                 User* u = (User*)(*u2);
315                 u->exempt = false;
316         }
317
318         if (ServerInstance->XLines->lookup_lines.find('E') != ServerInstance->XLines->lookup_lines.end())
319                 CheckELines(ServerInstance, ServerInstance->XLines->lookup_lines['E']);
320 }
321
322 // returns a pointer to the reason if a nickname matches a qline, NULL if it didnt match
323
324 QLine* XLineManager::matches_qline(const char* nick)
325 {
326         if (lookup_lines.find('Q') != lookup_lines.end() && lookup_lines['Q'].empty())
327                 return NULL;
328
329         for (std::vector<XLine*>::iterator i = active_lines.begin(); i != active_lines.end(); i++)
330                 if ((*i)->type == 'Q' && (*i)->Matches(nick))
331                         return (QLine*)(*i);
332         return NULL;
333 }
334
335 // returns a pointer to the reason if a host matches a gline, NULL if it didnt match
336
337 GLine* XLineManager::matches_gline(User* user)
338 {
339         if (lookup_lines.find('G') != lookup_lines.end() && lookup_lines['G'].empty())
340                 return NULL;
341
342         for (std::vector<XLine*>::iterator i = active_lines.begin(); i != active_lines.end(); i++)
343                 if ((*i)->type == 'G' && (*i)->Matches(user))
344                         return (GLine*)(*i);
345
346         return NULL;
347 }
348
349 ELine* XLineManager::matches_exception(User* user)
350 {
351         if (lookup_lines.find('E') != lookup_lines.end() && lookup_lines['E'].empty())
352                 return NULL;
353
354         for (std::vector<XLine*>::iterator i = active_lines.begin(); i != active_lines.end(); i++)
355         {
356                 if ((*i)->type == 'E' && (*i)->Matches(user))
357                         return (ELine*)(*i);
358         }
359         return NULL;
360 }
361
362
363 void XLineManager::gline_set_creation_time(const char* host, time_t create_time)
364 {
365         /*for (std::vector<XLine*>::iterator i = glines.begin(); i != glines.end(); i++)
366         {
367                 if (!strcasecmp(host,(*i)->hostmask))
368                 {
369                         (*i)->set_time = create_time;
370                         (*i)->expiry = create_time + (*i)->duration;
371                         return;
372                 }
373         }*/
374
375         return ;
376 }
377
378 void XLineManager::eline_set_creation_time(const char* host, time_t create_time)
379 {
380         /*for (std::vector<ELine*>::iterator i = elines.begin(); i != elines.end(); i++)
381         {
382                 if (!strcasecmp(host,(*i)->hostmask))
383                 {
384                         (*i)->set_time = create_time;
385                         (*i)->expiry = create_time + (*i)->duration;
386                         return;
387                 }
388         }*/
389
390         return;
391 }
392
393 void XLineManager::qline_set_creation_time(const char* nick, time_t create_time)
394 {
395         /*for (std::vector<QLine*>::iterator i = qlines.begin(); i != qlines.end(); i++)
396         {
397                 if (!strcasecmp(nick,(*i)->nick))
398                 {
399                         (*i)->set_time = create_time;
400                         (*i)->expiry = create_time + (*i)->duration;
401                         return;
402                 }
403         }*/
404
405         return;
406 }
407
408 void XLineManager::zline_set_creation_time(const char* ip, time_t create_time)
409 {
410         /*for (std::vector<ZLine*>::iterator i = zlines.begin(); i != zlines.end(); i++)
411         {
412                 if (!strcasecmp(ip,(*i)->ipaddr))
413                 {
414                         (*i)->set_time = create_time;
415                         (*i)->expiry = create_time + (*i)->duration;
416                         return;
417                 }
418         }*/
419
420         return;
421 }
422
423 // returns a pointer to the reason if an ip address matches a zline, NULL if it didnt match
424
425 ZLine* XLineManager::matches_zline(User *u)
426 {
427         if (lookup_lines.find('Z') != lookup_lines.end() && lookup_lines['Z'].empty())
428                 return NULL;
429
430         for (std::vector<XLine*>::iterator i = active_lines.begin(); i != active_lines.end(); i++)
431                 if ((*i)->type == 'Z' && (*i)->Matches(u))
432                         return (ZLine*)(*i);
433         return NULL;
434 }
435
436 // returns a pointer to the reason if a host matches a kline, NULL if it didnt match
437
438 KLine* XLineManager::matches_kline(User* user)
439 {
440         if (lookup_lines.find('K') != lookup_lines.end() && lookup_lines['K'].empty())
441                 return NULL;
442
443         for (std::vector<XLine*>::iterator i = active_lines.begin(); i != active_lines.end(); i++)
444                 if ((*i)->Matches(user))
445                         return (KLine*)(*i);
446
447         return NULL;
448 }
449
450 bool XLineManager::XSortComparison(const XLine *one, const XLine *two)
451 {
452         // account for permanent lines
453         if (one->expiry == 0)
454         {
455                 return false;
456         }
457         return (one->expiry) < (two->expiry);
458 }
459
460 // removes lines that have expired
461 void XLineManager::expire_lines()
462 {
463         time_t current = ServerInstance->Time();
464
465         /* Because we now store all our XLines in sorted order using ((*i)->duration + (*i)->set_time) as a key, this
466          * means that to expire the XLines we just need to do a while, picking off the top few until there are
467          * none left at the head of the queue that are after the current time.
468          */
469
470         while ((active_lines.size()) && (current > (*active_lines.begin())->expiry) && ((*active_lines.begin())->duration != 0))
471         {
472                 std::vector<XLine*>::iterator i = active_lines.begin();
473                 (*i)->DisplayExpiry();
474                 (*i)->Unset();
475                 active_lines.erase(i);
476                 delete *i;
477         }
478 }
479
480 // applies lines, removing clients and changing nicks etc as applicable
481 void XLineManager::ApplyLines()
482 {
483         for (std::vector<User*>::const_iterator u2 = ServerInstance->local_users.begin(); u2 != ServerInstance->local_users.end(); u2++)
484         {
485                 User* u = (User*)(*u2);
486
487                 for (std::vector<XLine *>::iterator i = pending_lines.begin(); i != pending_lines.end(); i++)
488                 {
489                         XLine *x = *i;
490                         if (x->Matches(u))
491                                 x->Apply(u);
492                 }
493         }
494
495         pending_lines.clear();
496 }
497
498 void XLineManager::stats_k(User* user, string_list &results)
499 {
500         /*std::string sn = ServerInstance->Config->ServerName;
501         for (std::vector<KLine*>::iterator i = klines.begin(); i != klines.end(); i++)
502                 results.push_back(sn+" 216 "+user->nick+" :"+(*i)->identmask+"@"+(*i)->hostmask+" "+ConvToStr((*i)->set_time)+" "+ConvToStr((*i)->duration)+" "+(*i)->source+" :"+(*i)->reason);*/
503 }
504
505 void XLineManager::stats_g(User* user, string_list &results)
506 {
507         /*std::string sn = ServerInstance->Config->ServerName;
508         for (std::vector<GLine*>::iterator i = glines.begin(); i != glines.end(); i++)
509                 results.push_back(sn+" 223 "+user->nick+" :"+(*i)->identmask+"@"+(*i)->hostmask+" "+ConvToStr((*i)->set_time)+" "+ConvToStr((*i)->duration)+" "+(*i)->source+" :"+(*i)->reason);*/
510 }
511
512 void XLineManager::stats_q(User* user, string_list &results)
513 {
514         /*std::string sn = ServerInstance->Config->ServerName;
515         for (std::vector<QLine*>::iterator i = qlines.begin(); i != qlines.end(); i++)
516                 results.push_back(sn+" 217 "+user->nick+" :"+(*i)->nick+" "+ConvToStr((*i)->set_time)+" "+ConvToStr((*i)->duration)+" "+(*i)->source+" :"+(*i)->reason);*/
517 }
518
519 void XLineManager::stats_z(User* user, string_list &results)
520 {
521         /*std::string sn = ServerInstance->Config->ServerName;
522         for (std::vector<ZLine*>::iterator i = zlines.begin(); i != zlines.end(); i++)
523                 results.push_back(sn+" 223 "+user->nick+" :"+(*i)->ipaddr+" "+ConvToStr((*i)->set_time)+" "+ConvToStr((*i)->duration)+" "+(*i)->source+" :"+(*i)->reason);*/
524 }
525
526 void XLineManager::stats_e(User* user, string_list &results)
527 {
528         /*std::string sn = ServerInstance->Config->ServerName;
529         for (std::vector<ELine*>::iterator i = elines.begin(); i != elines.end(); i++)
530                 results.push_back(sn+" 223 "+user->nick+" :"+(*i)->identmask+"@"+(*i)->hostmask+" "+ConvToStr((*i)->set_time)+" "+ConvToStr((*i)->duration)+" "+(*i)->source+" :"+(*i)->reason);*/
531 }
532
533 XLineManager::XLineManager(InspIRCd* Instance) : ServerInstance(Instance)
534 {
535 }
536
537 bool XLine::Matches(const std::string &str)
538 {
539         return false;
540 }
541
542 void XLine::Apply(User* u)
543 {
544 }
545
546 void XLine::DefaultApply(User* u, char line)
547 {
548         char reason[MAXBUF];
549         snprintf(reason, MAXBUF, "%c-Lined: %s", line, this->reason);
550         if (*ServerInstance->Config->MoronBanner)
551                 u->WriteServ("NOTICE %s :*** %s", u->nick, ServerInstance->Config->MoronBanner);
552         if (ServerInstance->Config->HideBans)
553                 User::QuitUser(ServerInstance, u, line + std::string("-Lined"), reason);
554         else
555                 User::QuitUser(ServerInstance, u, reason);
556 }
557
558 bool KLine::Matches(User *u)
559 {
560         if (u->exempt)
561                 return false;
562
563         if ((match(u->ident, this->identmask)))
564         {
565                 if ((match(u->host, this->hostmask, true)) || (match(u->GetIPString(), this->hostmask, true)))
566                 {
567                         return true;
568                 }
569         }
570
571         return false;
572 }
573
574 void KLine::Apply(User* u)
575 {
576         DefaultApply(u, 'K');
577 }
578
579 bool GLine::Matches(User *u)
580 {
581         if (u->exempt)
582                 return false;
583
584         if ((match(u->ident, this->identmask)))
585         {
586                 if ((match(u->host, this->hostmask, true)) || (match(u->GetIPString(), this->hostmask, true)))
587                 {
588                         return true;
589                 }
590         }
591
592         return false;
593 }
594
595 void GLine::Apply(User* u)
596 {       
597         DefaultApply(u, 'G');
598 }
599
600 bool ELine::Matches(User *u)
601 {
602         if (u->exempt)
603                 return false;
604
605         if ((match(u->ident, this->identmask)))
606         {
607                 if ((match(u->host, this->hostmask, true)) || (match(u->GetIPString(), this->hostmask, true)))
608                 {
609                         return true;
610                 }
611         }
612
613         return false;
614 }
615
616 bool ZLine::Matches(User *u)
617 {
618         if (u->exempt)
619                 return false;
620
621         if (match(u->GetIPString(), this->ipaddr, true))
622                 return true;
623         else
624                 return false;
625 }
626
627 void ZLine::Apply(User* u)
628 {       
629         DefaultApply(u, 'Z');
630 }
631
632
633 bool QLine::Matches(User *u)
634 {
635         if (u->exempt)
636                 return false;
637
638         if (match(u->nick, this->nick))
639                 return true;
640
641         return false;
642 }
643
644 void QLine::Apply(User* u)
645 {       
646         /* Can we force the user to their uid here instead? */
647         DefaultApply(u, 'Q');
648 }
649
650
651 bool ZLine::Matches(const std::string &str)
652 {
653         if (match(str.c_str(), this->ipaddr, true))
654                 return true;
655         else
656                 return false;
657 }
658
659 bool QLine::Matches(const std::string &str)
660 {
661         if (match(str.c_str(), this->nick))
662                 return true;
663
664         return false;
665 }
666
667 void ELine::DisplayExpiry()
668 {
669         ServerInstance->SNO->WriteToSnoMask('x',"Expiring timed E-Line %s@%s (set by %s %d seconds ago)",this->identmask,this->hostmask,this->source,this->duration);
670 }
671
672 void QLine::DisplayExpiry()
673 {
674         ServerInstance->SNO->WriteToSnoMask('x',"Expiring timed G-Line %s (set by %s %d seconds ago)",this->nick,this->source,this->duration);
675 }
676
677 void ZLine::DisplayExpiry()
678 {
679         ServerInstance->SNO->WriteToSnoMask('x',"Expiring timed Z-Line %s (set by %s %d seconds ago)",this->ipaddr,this->source,this->duration);
680 }
681
682 void KLine::DisplayExpiry()
683 {
684         ServerInstance->SNO->WriteToSnoMask('x',"Expiring timed K-Line %s@%s (set by %s %d seconds ago)",this->identmask,this->hostmask,this->source,this->duration);
685 }
686
687 void GLine::DisplayExpiry()
688 {
689         ServerInstance->SNO->WriteToSnoMask('x',"Expiring timed G-Line %s@%s (set by %s %d seconds ago)",this->identmask,this->hostmask,this->source,this->duration);
690 }
691