/* XLine functions. * * (C) 2003-2022 Anope Team * Contact us at team@anope.org * * Please read COPYING and README for further details. * * Based on the original code of Epona by Lara. * Based on the original code of Services by Andy Church. */ #include "services.h" #include "modules.h" #include "xline.h" #include "users.h" #include "sockets.h" #include "regexpr.h" #include "config.h" #include "commands.h" #include "servers.h" /* List of XLine managers we check users against in XLineManager::CheckAll */ std::list XLineManager::XLineManagers; Serialize::Checker > XLineManager::XLinesByUID("XLine"); void XLine::Init() { if (this->mask.length() >= 2 && this->mask[0] == '/' && this->mask[this->mask.length() - 1] == '/' && !Config->GetBlock("options")->Get("regexengine").empty()) { Anope::string stripped_mask = this->mask.substr(1, this->mask.length() - 2); ServiceReference provider("Regex", Config->GetBlock("options")->Get("regexengine")); if (provider) { try { this->regex = provider->Compile(stripped_mask); } catch (const RegexException &ex) { Log(LOG_DEBUG) << ex.GetReason(); } } } size_t nick_t = this->mask.find('!'); if (nick_t != Anope::string::npos) nick = this->mask.substr(0, nick_t); size_t user_t = this->mask.find('!'), host_t = this->mask.find('@'); if (host_t != Anope::string::npos) { if (user_t != Anope::string::npos && host_t > user_t) user = this->mask.substr(user_t + 1, host_t - user_t - 1); else user = this->mask.substr(0, host_t); } size_t real_t = this->mask.find('#'); if (host_t != Anope::string::npos) { if (real_t != Anope::string::npos && real_t > host_t) host = this->mask.substr(host_t + 1, real_t - host_t - 1); else host = this->mask.substr(host_t + 1); } else { if (real_t != Anope::string::npos) host = this->mask.substr(0, real_t); else host = this->mask; } if (real_t != Anope::string::npos) real = this->mask.substr(real_t + 1); if (host.find('/') != Anope::string::npos) { c = new cidr(host); if (!c->valid()) { delete c; c = NULL; } } } 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) { regex = NULL; manager = NULL; c = NULL; this->Init(); } 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) { regex = NULL; manager = NULL; c = NULL; this->Init(); } XLine::~XLine() { if (manager) manager->RemoveXLine(this); delete regex; delete c; } const Anope::string &XLine::GetNick() const { return nick; } const Anope::string &XLine::GetUser() const { return user; } const Anope::string &XLine::GetHost() const { return host; } const Anope::string &XLine::GetReal() const { return real; } Anope::string XLine::GetReason() const { Anope::string r = this->reason; if (!this->id.empty()) r += " (ID: " + this->id + ")"; return r; } bool XLine::HasNickOrReal() const { return !this->GetNick().empty() || !this->GetReal().empty(); } bool XLine::IsRegex() const { return !this->mask.empty() && this->mask[0] == '/' && this->mask[this->mask.length() - 1] == '/'; } void XLine::Serialize(Serialize::Data &data) const { data["mask"] << this->mask; data["by"] << this->by; data["created"] << this->created; data["expires"] << this->expires; data["reason"] << this->reason; data["uid"] << this->id; if (this->manager) data["manager"] << this->manager->name; } Serializable* XLine::Unserialize(Serializable *obj, Serialize::Data &data) { Anope::string smanager; data["manager"] >> smanager; ServiceReference xlm("XLineManager", smanager); if (!xlm) return NULL; XLine *xl; if (obj) { xl = anope_dynamic_static_cast(obj); data["mask"] >> xl->mask; data["by"] >> xl->by; data["reason"] >> xl->reason; data["uid"] >> xl->id; if (xlm != xl->manager) { xl->manager->DelXLine(xl); xlm->AddXLine(xl); } } else { Anope::string smask, sby, sreason, suid; time_t expires; data["mask"] >> smask; data["by"] >> sby; data["reason"] >> sreason; data["uid"] >> suid; data["expires"] >> expires; xl = new XLine(smask, sby, expires, sreason, suid); xlm->AddXLine(xl); } data["created"] >> xl->created; xl->manager = xlm; return xl; } void XLineManager::RegisterXLineManager(XLineManager *xlm) { XLineManagers.push_back(xlm); } void XLineManager::UnregisterXLineManager(XLineManager *xlm) { std::list::iterator it = std::find(XLineManagers.begin(), XLineManagers.end(), xlm); if (it != XLineManagers.end()) XLineManagers.erase(it); } void XLineManager::CheckAll(User *u) { for (std::list::iterator it = XLineManagers.begin(), it_end = XLineManagers.end(); it != it_end; ++it) { XLineManager *xlm = *it; if (xlm->CheckAllXLines(u)) break; } } Anope::string XLineManager::GenerateUID() { Anope::string id; int count = 0; do { id.clear(); if (++count > 10) { Log(LOG_DEBUG) << "Unable to generate XLine UID"; break; } for (int i = 0; i < 10; ++i) { char c; do c = (rand() % 75) + 48; while (!isupper(c) && !isdigit(c)); id += c; } } while (XLinesByUID->count(id) > 0); return id; } XLineManager::XLineManager(Module *creator, const Anope::string &xname, char t) : Service(creator, "XLineManager", xname), type(t), xlines("XLine") { } XLineManager::~XLineManager() { this->Clear(); } const char &XLineManager::Type() { return this->type; } size_t XLineManager::GetCount() const { return this->xlines->size(); } const std::vector &XLineManager::GetList() const { return this->xlines; } void XLineManager::AddXLine(XLine *x) { if (!x->id.empty()) XLinesByUID->insert(std::make_pair(x->id, x)); this->xlines->push_back(x); x->manager = this; } void XLineManager::RemoveXLine(XLine *x) { /* called from the destructor */ std::vector::iterator it = std::find(this->xlines->begin(), this->xlines->end(), x); if (!x->id.empty()) { std::multimap::iterator it2 = XLinesByUID->find(x->id), it3 = XLinesByUID->upper_bound(x->id); for (; it2 != XLinesByUID->end() && it2 != it3; ++it2) if (it2->second == x) { XLinesByUID->erase(it2); break; } } if (it != this->xlines->end()) { this->SendDel(x); this->xlines->erase(it); } } bool XLineManager::DelXLine(XLine *x) { std::vector::iterator it = std::find(this->xlines->begin(), this->xlines->end(), x); if (!x->id.empty()) { std::multimap::iterator it2 = XLinesByUID->find(x->id), it3 = XLinesByUID->upper_bound(x->id); for (; it2 != XLinesByUID->end() && it2 != it3; ++it2) if (it2->second == x) { XLinesByUID->erase(it2); break; } } if (it != this->xlines->end()) { this->SendDel(x); x->manager = NULL; // Don't call remove delete x; this->xlines->erase(it); return true; } return false; } XLine* XLineManager::GetEntry(unsigned index) { if (index >= this->xlines->size()) return NULL; XLine *x = this->xlines->at(index); x->QueueUpdate(); return x; } void XLineManager::Clear() { std::vector xl; this->xlines->swap(xl); for (unsigned i = 0; i < xl.size(); ++i) { XLine *x = xl[i]; if (!x->id.empty()) XLinesByUID->erase(x->id); delete x; } } bool XLineManager::CanAdd(CommandSource &source, const Anope::string &mask, time_t expires, const Anope::string &reason) { for (unsigned i = this->GetCount(); i > 0; --i) { XLine *x = this->GetEntry(i - 1); if (x->mask.equals_ci(mask)) { if (!x->expires || x->expires >= expires) { if (x->reason != reason) { x->reason = reason; source.Reply(_("Reason for %s updated."), x->mask.c_str()); } else source.Reply(_("%s already exists."), mask.c_str()); } else { x->expires = expires; if (x->reason != reason) { x->reason = reason; source.Reply(_("Expiry and reason updated for %s."), x->mask.c_str()); } else source.Reply(_("Expiry for %s updated."), x->mask.c_str()); } return false; } else if (Anope::Match(mask, x->mask) && (!x->expires || x->expires >= expires)) { source.Reply(_("%s is already covered by %s."), mask.c_str(), x->mask.c_str()); return false; } else if (Anope::Match(x->mask, mask) && (!expires || x->expires <= expires)) { source.Reply(_("Removing %s because %s covers it."), x->mask.c_str(), mask.c_str()); this->DelXLine(x); } } return true; } XLine* XLineManager::HasEntry(const Anope::string &mask) { std::multimap::iterator it = XLinesByUID->find(mask); if (it != XLinesByUID->end()) for (std::multimap::iterator it2 = XLinesByUID->upper_bound(mask); it != it2; ++it) if (it->second->manager == NULL || it->second->manager == this) { it->second->QueueUpdate(); return it->second; } for (unsigned i = 0, end = this->xlines->size(); i < end; ++i) { XLine *x = this->xlines->at(i); if (x->mask.equals_ci(mask)) { x->QueueUpdate(); return x; } } return NULL; } XLine *XLineManager::CheckAllXLines(User *u) { for (unsigned i = this->xlines->size(); i > 0; --i) { XLine *x = this->xlines->at(i - 1); if (x->expires && x->expires < Anope::CurTime) { this->OnExpire(x); this->DelXLine(x); continue; } if (this->Check(u, x)) { this->OnMatch(u, x); return x; } } return NULL; } void XLineManager::OnExpire(const XLine *x) { }