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