/* * Anope IRC Services * * Copyright (C) 2003-2017 Anope Team * * This file is part of Anope. Anope is free software; you can * redistribute it and/or modify it under the terms of the GNU * General Public License as published by the Free Software * Foundation, version 2. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, see see . */ #include "module.h" #include "modules/operserv/ignore.h" class IgnoreImpl : public Ignore { friend class IgnoreType; Serialize::Storage mask, creator, reason; Serialize::Storage time; public: using Ignore::Ignore; Anope::string GetMask() override; void SetMask(const Anope::string &) override; Anope::string GetCreator() override; void SetCreator(const Anope::string &) override; Anope::string GetReason() override; void SetReason(const Anope::string &) override; time_t GetTime() override; void SetTime(const time_t &) override; }; class IgnoreType : public Serialize::Type { public: Serialize::Field mask, creator, reason; Serialize::Field time; IgnoreType(Module *me) : Serialize::Type(me) , mask(this, "mask", &IgnoreImpl::mask) , creator(this, "creator", &IgnoreImpl::creator) , reason(this, "reason", &IgnoreImpl::reason) , time(this, "time", &IgnoreImpl::time) { } }; Anope::string IgnoreImpl::GetMask() { return Get(&IgnoreType::mask); } void IgnoreImpl::SetMask(const Anope::string &m) { Set(&IgnoreType::mask, m); } Anope::string IgnoreImpl::GetCreator() { return Get(&IgnoreType::creator); } void IgnoreImpl::SetCreator(const Anope::string &c) { Set(&IgnoreType::creator, c); } Anope::string IgnoreImpl::GetReason() { return Get(&IgnoreType::reason); } void IgnoreImpl::SetReason(const Anope::string &r) { Set(&IgnoreType::reason, r); } time_t IgnoreImpl::GetTime() { return Get(&IgnoreType::time); } void IgnoreImpl::SetTime(const time_t &t) { Set(&IgnoreType::time, t); } class OSIgnoreService : public IgnoreService { public: OSIgnoreService(Module *o) : IgnoreService(o) { } Ignore *Find(const Anope::string &mask) override { User *u = User::Find(mask, true); std::vector ignores = Serialize::GetObjects(); std::vector::iterator ign = ignores.begin(), ign_end = ignores.end(); if (u) { for (; ign != ign_end; ++ign) { Entry ignore_mask("", (*ign)->GetMask()); if (ignore_mask.Matches(u, true)) break; } } else { size_t user, host; Anope::string tmp; /* We didn't get a user.. generate a valid mask. */ if ((host = mask.find('@')) != Anope::string::npos) { if ((user = mask.find('!')) != Anope::string::npos) { /* this should never happen */ if (user > host) return NULL; tmp = mask; } else /* We have user@host. Add nick wildcard. */ tmp = "*!" + mask; } /* We only got a nick.. */ else tmp = mask + "!*@*"; for (; ign != ign_end; ++ign) if (Anope::Match(tmp, (*ign)->GetMask(), false, true)) break; } /* Check whether the entry has timed out */ if (ign != ign_end) { Ignore *id = *ign; if (id->GetTime() && !Anope::NoExpire && id->GetTime() <= Anope::CurTime) { this->GetOwner()->logger.Bot("OperServ").Category("expire/ignore").Log(_("Expiring ignore for {0}"), id->GetMask()); id->Delete(); } else { return id; } } return NULL; } }; class CommandOSIgnore : public Command { ServiceReference ignore_service; private: Anope::string RealMask(const Anope::string &mask) { /* If it s an existing user, we ignore the hostmask. */ User *u = User::Find(mask, true); if (u) return "*!*@" + u->host; size_t host = mask.find('@'); /* Determine whether we get a nick or a mask. */ if (host != Anope::string::npos) { size_t user = mask.find('!'); /* Check whether we have a nick too.. */ if (user != Anope::string::npos) { if (user > host) /* this should never happen */ return ""; else return mask; } else { /* We have user@host. Add nick wildcard. */ return "*!" + mask; } } /* We only got a nick.. */ return mask + "!*@*"; } void DoAdd(CommandSource &source, const std::vector ¶ms) { const Anope::string &time = params.size() > 1 ? params[1] : ""; const Anope::string &nick = params.size() > 2 ? params[2] : ""; const Anope::string &reason = params.size() > 3 ? params[3] : ""; if (time.empty() || nick.empty()) { this->OnSyntaxError(source, "ADD"); return; } time_t t = Anope::DoTime(time); if (t <= -1) { source.Reply(_("Invalid expiry time \002{0}\002."), time); return; } Anope::string mask = RealMask(nick); if (mask.empty()) { source.Reply(_("Mask must be in the form \037user\037@\037host\037.")); return; } if (Anope::ReadOnly) source.Reply(_("Services are in read-only mode. Any changes made may not persist.")); Ignore *ign = Serialize::New(); ign->SetMask(mask); ign->SetCreator(source.GetNick()); ign->SetReason(reason); ign->SetTime(t ? Anope::CurTime + t : 0); if (!t) { source.Reply(_("\002{0}\002 will now permanently be ignored."), mask); logger.Admin(source, _("{source} used {command} to add a permanent ignore for {0}"), mask); } else { source.Reply(_("\002{0}\002 will now be ignored for \002{1}\002."), mask, Anope::Duration(t, source.GetAccount())); logger.Admin(source, _("{source} used {command} to add an ignore on {0} for {1}"), mask, Anope::Duration(t)); } } void DoList(CommandSource &source) { std::vector ignores = Serialize::GetObjects(); for (Ignore *id : ignores) { if (id->GetTime() && !Anope::NoExpire && id->GetTime() <= Anope::CurTime) { this->GetOwner()->logger.Bot("OperServ").Category("expire/ignore").Log(_("Expiring ignore entry {0}"), id->GetMask()); id->Delete(); } } ignores = Serialize::GetObjects(); if (ignores.empty()) { source.Reply(_("Ignore list is empty.")); return; } ListFormatter list(source.GetAccount()); list.AddColumn(_("Mask")).AddColumn(_("Creator")).AddColumn(_("Reason")).AddColumn(_("Expires")); for (Ignore *ignore : ignores) { ListFormatter::ListEntry entry; entry["Mask"] = ignore->GetMask(); entry["Creator"] = ignore->GetCreator(); entry["Reason"] = ignore->GetReason(); entry["Expires"] = Anope::Expires(ignore->GetTime(), source.GetAccount()); list.AddEntry(entry); } source.Reply(_("Services ignore list:")); std::vector replies; list.Process(replies); for (const Anope::string &r : replies) source.Reply(r); } void DoDel(CommandSource &source, const std::vector ¶ms) { const Anope::string nick = params.size() > 1 ? params[1] : ""; if (nick.empty()) { this->OnSyntaxError(source, "DEL"); return; } Anope::string mask = RealMask(nick); if (mask.empty()) { source.Reply(_("Mask must be in the form \037user\037@\037host\037.")); return; } Ignore *ign = ignore_service->Find(mask); if (!ign) { source.Reply(_("\002{0}\002 not found on ignore list."), mask); return; } if (Anope::ReadOnly) source.Reply(_("Services are in read-only mode. Any changes made may not persist.")); logger.Admin(source, _("{source} used {command} to remove an ignore on {0}"), mask); source.Reply(_("\002{0}\002 will no longer be ignored."), mask); ign->Delete(); } void DoClear(CommandSource &source) { if (Anope::ReadOnly) source.Reply(_("Services are in read-only mode. Any changes made may not persist.")); for (Ignore *ign : Serialize::GetObjects()) ign->Delete(); logger.Admin(source, _("{source} used {command} to CLEAR the ignore list")); source.Reply(_("Ignore list has been cleared.")); } public: CommandOSIgnore(Module *creator) : Command(creator, "operserv/ignore", 1, 4) { this->SetDesc(_("Modify the Services ignore list")); this->SetSyntax(_("ADD \037expiry\037 {\037nick\037|\037mask\037} [\037reason\037]")); this->SetSyntax(_("DEL {\037nick\037|\037mask\037}")); this->SetSyntax("LIST"); this->SetSyntax("CLEAR"); } void Execute(CommandSource &source, const std::vector ¶ms) override { const Anope::string &cmd = params[0]; if (cmd.equals_ci("ADD")) return this->DoAdd(source, params); else if (cmd.equals_ci("LIST")) return this->DoList(source); else if (cmd.equals_ci("DEL")) return this->DoDel(source, params); else if (cmd.equals_ci("CLEAR")) return this->DoClear(source); else this->OnSyntaxError(source, ""); } bool OnHelp(CommandSource &source, const Anope::string &subcommand) override { source.Reply(_("The \002{0}\002 command allows you to make services ignore a user or hostmask." " \037expiry\037 must be an integer and can be followed by one of \037d\037 (days), \037h\037 (hours), or \037m\037 (minutes)." " If a unit specifier is not included, the default is seconds." " To make services permanently ignore the user, use 0 as the expiry time." " When adding a \037mask\037, it should be in the format nick!user@host, everything else will be considered a nicknames. Wildcards are permitted."), source.GetCommand()); const Anope::string ®exengine = Config->GetBlock("options")->Get("regexengine"); if (!regexengine.empty()) { source.Reply(" "); source.Reply(_("Regex matches are also supported using the \002{0}\002 engine. Enclose your pattern in // if this is desired."), regexengine); } return true; } void OnSyntaxError(CommandSource &source, const Anope::string &subcommand) override { if (subcommand.equals_ci("ADD")) { SubcommandSyntaxError(source, subcommand, _("\037expiry\037 {\037nick\037|\037mask\037} [\037reason\037]")); } else if (subcommand.equals_ci("DEL")) { SubcommandSyntaxError(source, subcommand, _("{\037nick\037|\037mask\037}")); } else { Command::OnSyntaxError(source, subcommand); } } }; class OSIgnore : public Module , public EventHook { IgnoreType ignoretype; OSIgnoreService osignoreservice; CommandOSIgnore commandosignore; public: OSIgnore(const Anope::string &modname, const Anope::string &creator) : Module(modname, creator, VENDOR) , EventHook(this) , ignoretype(this) , osignoreservice(this) , commandosignore(this) { } EventReturn OnBotPrivmsg(User *u, ServiceBot *bi, Anope::string &message) override { if (!u->HasMode("OPER") && this->osignoreservice.Find(u->nick)) return EVENT_STOP; return EVENT_CONTINUE; } }; MODULE_INIT(OSIgnore)