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