1 /* XLine functions.
2 *
3 * (C) 2003-2020 Anope Team
4 * Contact us at team@anope.org
5 *
6 * Please read COPYING and README for further details.
7 *
8 * Based on the original code of Epona by Lara.
9 * Based on the original code of Services by Andy Church.
10 */
11
12 #include "services.h"
13 #include "modules.h"
14 #include "xline.h"
15 #include "users.h"
16 #include "sockets.h"
17 #include "regexpr.h"
18 #include "config.h"
19 #include "commands.h"
20 #include "servers.h"
21
22 /* List of XLine managers we check users against in XLineManager::CheckAll */
23 std::list<XLineManager *> XLineManager::XLineManagers;
24 Serialize::Checker<std::multimap<Anope::string, XLine *, ci::less> > XLineManager::XLinesByUID("XLine");
25
Init()26 void XLine::Init()
27 {
28 if (this->mask.length() >= 2 && this->mask[0] == '/' && this->mask[this->mask.length() - 1] == '/' && !Config->GetBlock("options")->Get<const Anope::string>("regexengine").empty())
29 {
30 Anope::string stripped_mask = this->mask.substr(1, this->mask.length() - 2);
31
32 ServiceReference<RegexProvider> provider("Regex", Config->GetBlock("options")->Get<const Anope::string>("regexengine"));
33 if (provider)
34 {
35 try
36 {
37 this->regex = provider->Compile(stripped_mask);
38 }
39 catch (const RegexException &ex)
40 {
41 Log(LOG_DEBUG) << ex.GetReason();
42 }
43 }
44 }
45
46 size_t nick_t = this->mask.find('!');
47 if (nick_t != Anope::string::npos)
48 nick = this->mask.substr(0, nick_t);
49
50 size_t user_t = this->mask.find('!'), host_t = this->mask.find('@');
51 if (host_t != Anope::string::npos)
52 {
53 if (user_t != Anope::string::npos && host_t > user_t)
54 user = this->mask.substr(user_t + 1, host_t - user_t - 1);
55 else
56 user = this->mask.substr(0, host_t);
57 }
58
59 size_t real_t = this->mask.find('#');
60 if (host_t != Anope::string::npos)
61 {
62 if (real_t != Anope::string::npos && real_t > host_t)
63 host = this->mask.substr(host_t + 1, real_t - host_t - 1);
64 else
65 host = this->mask.substr(host_t + 1);
66 }
67 else
68 {
69 if (real_t != Anope::string::npos)
70 host = this->mask.substr(0, real_t);
71 else
72 host = this->mask;
73 }
74
75 if (real_t != Anope::string::npos)
76 real = this->mask.substr(real_t + 1);
77
78 if (host.find('/') != Anope::string::npos)
79 {
80 c = new cidr(host);
81 if (!c->valid())
82 {
83 delete c;
84 c = NULL;
85 }
86 }
87 }
88
XLine(const Anope::string & ma,const Anope::string & r,const Anope::string & uid)89 XLine::XLine(const Anope::string &ma, const Anope::string &r, const Anope::string &uid) : Serializable("XLine"), mask(ma), by(Me->GetName()), created(0), expires(0), reason(r), id(uid)
90 {
91 regex = NULL;
92 manager = NULL;
93 c = NULL;
94
95 this->Init();
96 }
97
XLine(const Anope::string & ma,const Anope::string & b,const time_t ex,const Anope::string & r,const Anope::string & uid)98 XLine::XLine(const Anope::string &ma, const Anope::string &b, const time_t ex, const Anope::string &r, const Anope::string &uid) : Serializable("XLine"), mask(ma), by(b), created(Anope::CurTime), expires(ex), reason(r), id(uid)
99 {
100 regex = NULL;
101 manager = NULL;
102 c = NULL;
103
104 this->Init();
105 }
106
~XLine()107 XLine::~XLine()
108 {
109 if (manager)
110 manager->RemoveXLine(this);
111
112 delete regex;
113 delete c;
114 }
115
GetNick() const116 const Anope::string &XLine::GetNick() const
117 {
118 return nick;
119 }
120
GetUser() const121 const Anope::string &XLine::GetUser() const
122 {
123 return user;
124 }
125
GetHost() const126 const Anope::string &XLine::GetHost() const
127 {
128 return host;
129 }
130
GetReal() const131 const Anope::string &XLine::GetReal() const
132 {
133 return real;
134 }
135
GetReason() const136 Anope::string XLine::GetReason() const
137 {
138 Anope::string r = this->reason;
139 if (!this->id.empty())
140 r += " (ID: " + this->id + ")";
141 return r;
142 }
143
HasNickOrReal() const144 bool XLine::HasNickOrReal() const
145 {
146 return !this->GetNick().empty() || !this->GetReal().empty();
147 }
148
IsRegex() const149 bool XLine::IsRegex() const
150 {
151 return !this->mask.empty() && this->mask[0] == '/' && this->mask[this->mask.length() - 1] == '/';
152 }
153
Serialize(Serialize::Data & data) const154 void XLine::Serialize(Serialize::Data &data) const
155 {
156 data["mask"] << this->mask;
157 data["by"] << this->by;
158 data["created"] << this->created;
159 data["expires"] << this->expires;
160 data["reason"] << this->reason;
161 data["uid"] << this->id;
162 if (this->manager)
163 data["manager"] << this->manager->name;
164 }
165
Unserialize(Serializable * obj,Serialize::Data & data)166 Serializable* XLine::Unserialize(Serializable *obj, Serialize::Data &data)
167 {
168 Anope::string smanager;
169
170 data["manager"] >> smanager;
171
172 ServiceReference<XLineManager> xlm("XLineManager", smanager);
173 if (!xlm)
174 return NULL;
175
176 XLine *xl;
177 if (obj)
178 {
179 xl = anope_dynamic_static_cast<XLine *>(obj);
180 data["mask"] >> xl->mask;
181 data["by"] >> xl->by;
182 data["reason"] >> xl->reason;
183 data["uid"] >> xl->id;
184
185 if (xlm != xl->manager)
186 {
187 xl->manager->DelXLine(xl);
188 xlm->AddXLine(xl);
189 }
190 }
191 else
192 {
193 Anope::string smask, sby, sreason, suid;
194 time_t expires;
195
196 data["mask"] >> smask;
197 data["by"] >> sby;
198 data["reason"] >> sreason;
199 data["uid"] >> suid;
200 data["expires"] >> expires;
201
202 xl = new XLine(smask, sby, expires, sreason, suid);
203 xlm->AddXLine(xl);
204 }
205
206 data["created"] >> xl->created;
207 xl->manager = xlm;
208
209 return xl;
210 }
211
RegisterXLineManager(XLineManager * xlm)212 void XLineManager::RegisterXLineManager(XLineManager *xlm)
213 {
214 XLineManagers.push_back(xlm);
215 }
216
UnregisterXLineManager(XLineManager * xlm)217 void XLineManager::UnregisterXLineManager(XLineManager *xlm)
218 {
219 std::list<XLineManager *>::iterator it = std::find(XLineManagers.begin(), XLineManagers.end(), xlm);
220
221 if (it != XLineManagers.end())
222 XLineManagers.erase(it);
223 }
224
CheckAll(User * u)225 void XLineManager::CheckAll(User *u)
226 {
227 for (std::list<XLineManager *>::iterator it = XLineManagers.begin(), it_end = XLineManagers.end(); it != it_end; ++it)
228 {
229 XLineManager *xlm = *it;
230
231 if (xlm->CheckAllXLines(u))
232 break;
233 }
234 }
235
GenerateUID()236 Anope::string XLineManager::GenerateUID()
237 {
238 Anope::string id;
239 int count = 0;
240 do
241 {
242 id.clear();
243
244 if (++count > 10)
245 {
246 Log(LOG_DEBUG) << "Unable to generate XLine UID";
247 break;
248 }
249
250 for (int i = 0; i < 10; ++i)
251 {
252 char c;
253 do
254 c = (rand() % 75) + 48;
255 while (!isupper(c) && !isdigit(c));
256 id += c;
257 }
258 }
259 while (XLinesByUID->count(id) > 0);
260
261 return id;
262 }
263
XLineManager(Module * creator,const Anope::string & xname,char t)264 XLineManager::XLineManager(Module *creator, const Anope::string &xname, char t) : Service(creator, "XLineManager", xname), type(t), xlines("XLine")
265 {
266 }
267
~XLineManager()268 XLineManager::~XLineManager()
269 {
270 this->Clear();
271 }
272
Type()273 const char &XLineManager::Type()
274 {
275 return this->type;
276 }
277
GetCount() const278 size_t XLineManager::GetCount() const
279 {
280 return this->xlines->size();
281 }
282
GetList() const283 const std::vector<XLine *> &XLineManager::GetList() const
284 {
285 return this->xlines;
286 }
287
AddXLine(XLine * x)288 void XLineManager::AddXLine(XLine *x)
289 {
290 if (!x->id.empty())
291 XLinesByUID->insert(std::make_pair(x->id, x));
292 this->xlines->push_back(x);
293 x->manager = this;
294 }
295
RemoveXLine(XLine * x)296 void XLineManager::RemoveXLine(XLine *x)
297 {
298 /* called from the destructor */
299
300 std::vector<XLine *>::iterator it = std::find(this->xlines->begin(), this->xlines->end(), x);
301
302 if (!x->id.empty())
303 {
304 std::multimap<Anope::string, XLine *, ci::less>::iterator it2 = XLinesByUID->find(x->id), it3 = XLinesByUID->upper_bound(x->id);
305 for (; it2 != XLinesByUID->end() && it2 != it3; ++it2)
306 if (it2->second == x)
307 {
308 XLinesByUID->erase(it2);
309 break;
310 }
311 }
312
313 if (it != this->xlines->end())
314 {
315 this->SendDel(x);
316 this->xlines->erase(it);
317 }
318 }
319
DelXLine(XLine * x)320 bool XLineManager::DelXLine(XLine *x)
321 {
322 std::vector<XLine *>::iterator it = std::find(this->xlines->begin(), this->xlines->end(), x);
323
324 if (!x->id.empty())
325 {
326 std::multimap<Anope::string, XLine *, ci::less>::iterator it2 = XLinesByUID->find(x->id), it3 = XLinesByUID->upper_bound(x->id);
327 for (; it2 != XLinesByUID->end() && it2 != it3; ++it2)
328 if (it2->second == x)
329 {
330 XLinesByUID->erase(it2);
331 break;
332 }
333 }
334
335 if (it != this->xlines->end())
336 {
337 this->SendDel(x);
338
339 x->manager = NULL; // Don't call remove
340 delete x;
341 this->xlines->erase(it);
342
343 return true;
344 }
345
346 return false;
347 }
348
GetEntry(unsigned index)349 XLine* XLineManager::GetEntry(unsigned index)
350 {
351 if (index >= this->xlines->size())
352 return NULL;
353
354 XLine *x = this->xlines->at(index);
355 x->QueueUpdate();
356 return x;
357 }
358
Clear()359 void XLineManager::Clear()
360 {
361 std::vector<XLine *> xl;
362 this->xlines->swap(xl);
363
364 for (unsigned i = 0; i < xl.size(); ++i)
365 {
366 XLine *x = xl[i];
367 if (!x->id.empty())
368 XLinesByUID->erase(x->id);
369 delete x;
370 }
371 }
372
CanAdd(CommandSource & source,const Anope::string & mask,time_t expires,const Anope::string & reason)373 bool XLineManager::CanAdd(CommandSource &source, const Anope::string &mask, time_t expires, const Anope::string &reason)
374 {
375 for (unsigned i = this->GetCount(); i > 0; --i)
376 {
377 XLine *x = this->GetEntry(i - 1);
378
379 if (x->mask.equals_ci(mask))
380 {
381 if (!x->expires || x->expires >= expires)
382 {
383 if (x->reason != reason)
384 {
385 x->reason = reason;
386 source.Reply(_("Reason for %s updated."), x->mask.c_str());
387 }
388 else
389 source.Reply(_("%s already exists."), mask.c_str());
390 }
391 else
392 {
393 x->expires = expires;
394 if (x->reason != reason)
395 {
396 x->reason = reason;
397 source.Reply(_("Expiry and reason updated for %s."), x->mask.c_str());
398 }
399 else
400 source.Reply(_("Expiry for %s updated."), x->mask.c_str());
401 }
402
403 return false;
404 }
405 else if (Anope::Match(mask, x->mask) && (!x->expires || x->expires >= expires))
406 {
407 source.Reply(_("%s is already covered by %s."), mask.c_str(), x->mask.c_str());
408 return false;
409 }
410 else if (Anope::Match(x->mask, mask) && (!expires || x->expires <= expires))
411 {
412 source.Reply(_("Removing %s because %s covers it."), x->mask.c_str(), mask.c_str());
413 this->DelXLine(x);
414 }
415 }
416
417 return true;
418 }
419
HasEntry(const Anope::string & mask)420 XLine* XLineManager::HasEntry(const Anope::string &mask)
421 {
422 std::multimap<Anope::string, XLine *, ci::less>::iterator it = XLinesByUID->find(mask);
423 if (it != XLinesByUID->end())
424 for (std::multimap<Anope::string, XLine *, ci::less>::iterator it2 = XLinesByUID->upper_bound(mask); it != it2; ++it)
425 if (it->second->manager == NULL || it->second->manager == this)
426 {
427 it->second->QueueUpdate();
428 return it->second;
429 }
430 for (unsigned i = 0, end = this->xlines->size(); i < end; ++i)
431 {
432 XLine *x = this->xlines->at(i);
433
434 if (x->mask.equals_ci(mask))
435 {
436 x->QueueUpdate();
437 return x;
438 }
439 }
440
441 return NULL;
442 }
443
CheckAllXLines(User * u)444 XLine *XLineManager::CheckAllXLines(User *u)
445 {
446 for (unsigned i = this->xlines->size(); i > 0; --i)
447 {
448 XLine *x = this->xlines->at(i - 1);
449
450 if (x->expires && x->expires < Anope::CurTime)
451 {
452 this->OnExpire(x);
453 this->DelXLine(x);
454 continue;
455 }
456
457 if (this->Check(u, x))
458 {
459 this->OnMatch(u, x);
460 return x;
461 }
462 }
463
464 return NULL;
465 }
466
OnExpire(const XLine * x)467 void XLineManager::OnExpire(const XLine *x)
468 {
469 }
470