diff options
| author | 2023-06-20 15:29:17 +0100 | |
|---|---|---|
| committer | 2023-06-20 15:36:54 +0100 | |
| commit | 7f6ffa0bdae7603dbcc97f7c670a8d1f736bd6cb (patch) | |
| tree | 1b0ec4c44530306351fbf178feb6142e448c18b2 /src/modules/m_cloak_user.cpp | |
| parent | Fix building cloak_sha256 against libpsl on Windows. (diff) | |
Merge the cloak_account and cloak_nick modules.
These modules are 90% identical and were only separate because
before commit 78cd3898f5 we could not conditionally regenerate
cloaks.
Diffstat (limited to 'src/modules/m_cloak_user.cpp')
| -rw-r--r-- | src/modules/m_cloak_user.cpp | 232 |
1 files changed, 232 insertions, 0 deletions
diff --git a/src/modules/m_cloak_user.cpp b/src/modules/m_cloak_user.cpp new file mode 100644 index 000000000..b01159b2a --- /dev/null +++ b/src/modules/m_cloak_user.cpp @@ -0,0 +1,232 @@ +/* + * InspIRCd -- Internet Relay Chat Daemon + * + * Copyright (C) 2023 Sadie Powell <sadie@witchery.services> + * + * This file is part of InspIRCd. InspIRCd 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 <http://www.gnu.org/licenses/>. + */ + +#include "inspircd.h" +#include "modules/account.h" +#include "modules/cloak.h" + +class UserMethod + : public Cloak::Method +{ +protected: + // The characters which are valid in a hostname. + const CharState& hostmap; + + // The prefix for cloaks (e.g. users.). + const std::string prefix; + + // Whether to strip non-host characters from the cloak. + const bool sanitize; + + // The suffix for IP cloaks (e.g. .example.org). + const std::string suffix; + + UserMethod(const Cloak::Engine* engine, const std::shared_ptr<ConfigTag>& tag, const CharState& hm) ATTR_NOT_NULL(2) + : Cloak::Method(engine, tag) + , hostmap(hm) + , prefix(tag->getString("prefix")) + , sanitize(tag->getBool("sanitize", true)) + , suffix(tag->getString("suffix")) + { + } + + // Retrieves the middle segment of the cloak. + virtual std::string GetMiddle(LocalUser* user) = 0; + +public: + std::string Generate(LocalUser* user) override ATTR_NOT_NULL(2) + { + if (!MatchesUser(user)) + return {}; // We shouldn't cloak this user. + + const std::string middle = GetMiddle(user); + if (middle.empty()) + return {}; // No middle cloak. + + std::string safemiddle; + safemiddle.reserve(middle.length()); + for (const auto chr : middle) + { + if (!hostmap.test(static_cast<unsigned char>(chr))) + { + if (!sanitize) + return {}; // Contains invalid characters. + + continue; + } + + safemiddle.push_back(chr); + } + + if (safemiddle.empty()) + return {}; // No cloak. + + return prefix + safemiddle + suffix; + } + + std::string Generate(const std::string& hostip) override + { + // We can't generate account cloaks without a user. + return {}; + } + + void GetLinkData(Module::LinkData& data, std::string& compatdata) override + { + data["prefix"] = prefix; + data["sanitize"] = sanitize ? "yes" : "no"; + data["suffix"] = suffix; + } +}; + +class AccountMethod final + : public UserMethod +{ +private: + // Dynamic reference to the account api. + Account::API accountapi; + + // Retrieves the middle segment of the cloak. + std::string GetMiddle(LocalUser* user) override + { + const std::string* accountname = accountapi ? accountapi->GetAccountName(user) : nullptr; + return accountname ? *accountname : ""; + } + +public: + AccountMethod(const Cloak::Engine* engine, const std::shared_ptr<ConfigTag>& tag, const CharState& hm) ATTR_NOT_NULL(2) + : UserMethod(engine, tag, hm) + , accountapi(engine->creator) + { + } +}; + +class AccountIdMethod final + : public UserMethod +{ +private: + // Dynamic reference to the account api. + Account::API accountapi; + + // Retrieves the middle segment of the cloak. + std::string GetMiddle(LocalUser* user) override + { + const std::string* accountid = accountapi ? accountapi->GetAccountId(user) : nullptr; + return accountid ? *accountid : ""; + } + +public: + AccountIdMethod(const Cloak::Engine* engine, const std::shared_ptr<ConfigTag>& tag, const CharState& hm) ATTR_NOT_NULL(2) + : UserMethod(engine, tag, hm) + , accountapi(engine->creator) + { + } +}; + +class NickMethod final + : public UserMethod +{ +private: + // Retrieves the middle segment of the cloak. + std::string GetMiddle(LocalUser* user) override + { + return user->nick; + } + +public: + NickMethod(const Cloak::Engine* engine, const std::shared_ptr<ConfigTag>& tag, const CharState& hm) ATTR_NOT_NULL(2) + : UserMethod(engine, tag, hm) + { + } +}; + +template <typename Method> +class UserEngine final + : public Cloak::Engine +{ +private: + // The characters which are valid in a hostname. + const CharState& hostmap; + +public: + UserEngine(Module* Creator, const std::string& Name, const CharState& hm) + : Cloak::Engine(Creator, Name) + , hostmap(hm) + { + } + + Cloak::MethodPtr Create(const std::shared_ptr<ConfigTag>& tag, bool primary) override + { + return std::make_shared<Method>(this, tag, hostmap); + } +}; + +class ModuleCloakUser final + : public Module + , public Account::EventListener +{ +private: + UserEngine<AccountMethod> accountcloak; + UserEngine<AccountIdMethod> accountidcloak; + UserEngine<NickMethod> nickcloak; + Cloak::API cloakapi; + CharState hostmap; + +public: + ModuleCloakUser() + : Module(VF_VENDOR, "Adds the account, account-id, nick cloaking methods for use with the cloak module.") + , Account::EventListener(this) + , accountcloak(this, "account", hostmap) + , accountidcloak(this, "account-id", hostmap) + , nickcloak(this, "nick", hostmap) + , cloakapi(this) + { + } + + void ReadConfig(ConfigStatus& status) override + { + CharState newhostmap; + const auto& tag = ServerInstance->Config->ConfValue("hostname"); + for (const auto chr : tag->getString("charmap", "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz.-_/0123456789", 1)) + { + // A hostname can not contain NUL, LF, CR, or SPACE. + if (chr == 0x00 || chr == 0x0A || chr == 0x0D || chr == 0x20) + throw ModuleException(this, INSP_FORMAT("<hostname:charmap> can not contain character 0x{:02X} ({})", chr, chr)); + newhostmap.set(static_cast<unsigned char>(chr)); + } + std::swap(newhostmap, hostmap); + } + + void OnAccountChange(User* user, const std::string& newaccount) override + { + LocalUser* luser = IS_LOCAL(user); + if (!luser || !cloakapi) + return; + + if (cloakapi->IsActiveCloak(accountcloak) || cloakapi->IsActiveCloak(accountidcloak)) + cloakapi->ResetCloaks(luser, true); + } + + void OnUserPostNick(User* user, const std::string& oldnick) override + { + LocalUser* luser = IS_LOCAL(user); + if (luser && cloakapi && cloakapi->IsActiveCloak(nickcloak)) + cloakapi->ResetCloaks(luser, true); + } +}; + +MODULE_INIT(ModuleCloakUser) |
