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