From 319a523b4fa79a965e39b066e7805abd9ece2d97 Mon Sep 17 00:00:00 2001 From: Sadie Powell Date: Thu, 20 Nov 2025 15:03:29 +0000 Subject: [PATCH] Add support for loading forbids from a file. Closes #365. --- data/operserv.example.conf | 29 +++++++++++- include/modules/operserv/forbid.h | 1 + language/anope.en_US.po | 8 +++- modules/operserv/os_forbid.cpp | 77 +++++++++++++++++++++++++++++-- 4 files changed, 108 insertions(+), 7 deletions(-) diff --git a/data/operserv.example.conf b/data/operserv.example.conf index 882176e4a..fc7d08dd3 100644 --- a/data/operserv.example.conf +++ b/data/operserv.example.conf @@ -351,7 +351,34 @@ command { service = "OperServ"; name = "CONFIG"; command = "operserv/config"; pe * * Used to forbid specific nicks, channels, emails, etc. from being used. */ -module { name = "os_forbid" } +module +{ + name = "os_forbid" + + /* + * Allows loading forbids from a file. + */ + #file + { + /* + * The type of forbid to add. Can be set to "chan", "email", "nick", + * "password", or "register". + */ + type = "email" + + /* + * The file to read forbids from. Each forbid should be placed on a new + * line. Surrounding whitespace will be ignored. + */ + file = "temp-emails.txt" + + /** + * The reason why entries from this file are forbidden. + */ + reason = "Temporary email" + } + +} command { service = "OperServ"; name = "FORBID"; command = "operserv/forbid"; permission = "operserv/forbid"; } /* diff --git a/include/modules/operserv/forbid.h b/include/modules/operserv/forbid.h index 73520ef3f..ed060ae3e 100644 --- a/include/modules/operserv/forbid.h +++ b/include/modules/operserv/forbid.h @@ -31,6 +31,7 @@ struct ForbidData Anope::string reason; time_t created = 0; time_t expires = 0; + bool immutable = false; ForbidType type; virtual ~ForbidData() = default; diff --git a/language/anope.en_US.po b/language/anope.en_US.po index 999267c1c..4b189971b 100644 --- a/language/anope.en_US.po +++ b/language/anope.en_US.po @@ -16,8 +16,8 @@ msgid "" msgstr "" "Project-Id-Version: Anope\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2025-11-20 12:43+0000\n" -"PO-Revision-Date: 2025-11-20 12:43+0000\n" +"POT-Creation-Date: 2025-11-20 15:05+0000\n" +"PO-Revision-Date: 2025-11-20 15:05+0000\n" "Last-Translator: Sadie Powell \n" "Language-Team: English\n" "Language: en_US\n" @@ -3068,6 +3068,10 @@ msgstr "" msgid "Forbid list:" msgstr "" +#, c-format +msgid "Forbid on %s can not be removed as it is from a file." +msgstr "" + #, c-format msgid "Forbid on %s was not found." msgstr "" diff --git a/modules/operserv/os_forbid.cpp b/modules/operserv/os_forbid.cpp index f12395a68..622fc6947 100644 --- a/modules/operserv/os_forbid.cpp +++ b/modules/operserv/os_forbid.cpp @@ -62,6 +62,20 @@ struct ForbidDataImpl final ForbidDataImpl() : Serializable("ForbidData") { } }; +struct ForbidDataFile final + : ForbidData +{ + ForbidDataFile(ForbidType t, const Anope::string &c, const Anope::string &m, const Anope::string &r) + { + created = Anope::CurTime; + creator = c; + immutable = true; + mask = m; + reason = r; + type = t; + } +}; + struct ForbidDataTypeImpl final : Serialize::Type { @@ -392,10 +406,14 @@ public: { if (Anope::ReadOnly) source.Reply(READ_ONLY_MODE); - - Log(LOG_ADMIN, source, this) << "to remove forbid on " << d->mask << " of type " << subcommand; - source.Reply(_("%s deleted from the %s forbid list."), d->mask.c_str(), subcommand.c_str()); - this->fs->RemoveForbid(d); + else if (d->immutable) + source.Reply(_("Forbid on %s can not be removed as it is from a file."), entry.c_str()); + else + { + Log(LOG_ADMIN, source, this) << "to remove forbid on " << d->mask << " of type " << subcommand; + source.Reply(_("%s deleted from the %s forbid list."), d->mask.c_str(), subcommand.c_str()); + this->fs->RemoveForbid(d); + } } else source.Reply(_("Forbid on %s was not found."), entry.c_str()); @@ -483,9 +501,11 @@ public: class OSForbid final : public Module { +private: MyForbidService forbidService; ForbidDataTypeImpl forbiddata_type; CommandOSForbid commandosforbid; + std::vector fileforbids; public: OSForbid(const Anope::string &modname, const Anope::string &creator) @@ -495,6 +515,55 @@ public: { } + void OnReload(Configuration::Conf &conf) override + { + for (auto* fileforbid : fileforbids) + forbidService.RemoveForbid(fileforbid); + fileforbids.clear(); + + const auto &modconf = conf.GetModule(this); + for (auto i = 0; i < modconf.CountBlock("file"); ++i) + { + const auto &fileblock = modconf.GetBlock("file", i); + + const auto reasonstr = fileblock.Get("reason"); + + const auto typestr = fileblock.Get("type"); + auto type = StringToType(typestr); + if (type == FT_SIZE) + { + Log(this) << "Unknown forbid file type: " << typestr << ", ignoring."; + continue; + } + + const auto filestr = Anope::ExpandConfig(fileblock.Get("file")); + std::ifstream file(filestr.str()); + if (!file.is_open()) + { + Log(this) << "Unable to read " << filestr << ", ignoring."; + continue; + } + for (Anope::string forbidstr; std::getline(file, forbidstr.str()); ) + { + forbidstr = forbidstr.trim(); + if (forbidstr.empty()) + continue; + + auto *forbid = forbidService.FindForbidExact(forbidstr, type); + if (forbid != nullptr) + { + Log(this) << "Forbid on " << forbidstr << " already exists, ignoring."; + continue; + } + + forbid = new ForbidDataFile(type, filestr, forbidstr, reasonstr); + forbidService.AddForbid(forbid); + fileforbids.push_back(forbid); + Log(LOG_DEBUG) << "Added a file forbid on " << forbidstr << ": " << reasonstr; + } + } + } + void OnUserConnect(User *u, bool &exempt) override { if (u->Quitting() || exempt)