diff options
author | Sadie Powell <sadie@witchery.services> | 2025-03-14 20:36:03 +0000 |
---|---|---|
committer | Sadie Powell <sadie@witchery.services> | 2025-03-14 23:21:42 +0000 |
commit | 0ebc43f0dc277b307f671757005e218a502cc8a4 (patch) | |
tree | 9ddc6d2d3fbac56b131df4703f810f0904d1bc08 /modules/protocol/inspircd.cpp | |
parent | 5ff86ea2c5361401de5f0deed0250ad4ae7dc853 (diff) |
Refactor the InspIRCd METADATA handler to actually be readable.
Diffstat (limited to 'modules/protocol/inspircd.cpp')
-rw-r--r-- | modules/protocol/inspircd.cpp | 379 |
1 files changed, 221 insertions, 158 deletions
diff --git a/modules/protocol/inspircd.cpp b/modules/protocol/inspircd.cpp index f59440e17..56f6e193e 100644 --- a/modules/protocol/inspircd.cpp +++ b/modules/protocol/inspircd.cpp @@ -1864,206 +1864,269 @@ private: ServiceReference<CertService> certs; PrimitiveExtensibleItem<ListLimits> &maxlist; - -public: - IRCDMessageMetadata(Module *creator, PrimitiveExtensibleItem<ListLimits> &listlimits) - : IRCDMessage(creator, "METADATA", 3) - , certs("CertService", "certs") - , maxlist(listlimits) + static void HandleAccountName(User *u, const Anope::string &value) { - SetFlag(FLAG_REQUIRE_SERVER); - SetFlag(FLAG_SOFT_LIMIT); + // :36D METADATA 9TSAAAAAA accountname + // :36D METADATA 9TSAAAAAA accountname Sadie + if (value.empty()) + { + // The user has been logged out by the IRC server. + u->Logout(); + } + else + { + // If we're bursting then then the user was probably logged in + // during a previous connection. + auto *nc = NickCore::Find(value); + if (nc) + u->Login(nc); + } } - void Run(MessageSource &source, const std::vector<Anope::string> ¶ms, const Anope::map<Anope::string> &tags) override + static void HandleCloakMethods(Server *s, const Anope::string &value) { - // We deliberately ignore non-bursting servers to avoid pseudoserver fights - // Channel METADATA has an additional parameter: the channel TS - // Received: :715 METADATA #chan 1572026333 mlock :nt - if ((params[0][0] == '#') && (params.size() > 3) && (!source.GetServer()->IsSynced())) + // :9TS METADATA * cloakmethods :foo bar=baz bax + + // We are only interested when it comes from our uplink. + if (s->GetUplink() != Me) + return; + + spacesepstream tokens(value); + for (Anope::string token; tokens.GetToken(token); ) { - Channel *c = Channel::Find(params[0]); - if (c) + const auto idx = token.find('='); + if (token.compare(0, idx, "custom", 6) == 0) { - if (c->ci && params[2] == "mlock") - { - ModeLocks *modelocks = c->ci->GetExt<ModeLocks>("modelocks"); - Anope::string modes; - if (modelocks) - modes = modelocks->GetMLockAsString(false).replace_all_cs("+", "").replace_all_cs("-", ""); - - // Mode lock string is not what we say it is? - if (modes != params[3]) - Uplink::Send("METADATA", c->name, c->creation_time, "mlock", modes); - } - else if (c->ci && params[2] == "topiclock") + if (!Servers::Capab.count("TOPICLOCK")) { - bool mystate = c->ci->HasExt("TOPICLOCK"); - bool serverstate = (params[3] == "1"); - if (mystate != serverstate) - Uplink::Send("METADATA", c->name, c->creation_time, "topiclock", !!mystate); - } - else if (params[2] == "maxlist") - { - ListLimits limits; - spacesepstream limitstream(params[3]); - Anope::string modechr, modelimit; - while (limitstream.GetToken(modechr) && limitstream.GetToken(modelimit)) - { - limits.emplace(modechr[0], Anope::Convert<unsigned>(modelimit, 0)); - } - maxlist.Set(c, limits); + Log() << "The remote server has the custom cloak method; this will be used for setting vhosts."; + Servers::Capab.insert("CLOAK"); } + return; } } - else if (isdigit(params[0][0])) + + if (Servers::Capab.count("TOPICLOCK")) { - auto *u = User::Find(params[0]); - if (!u) - return; + Log() << "The remote server does not have the custom cloak method; CHGIDENT and CHGHOST will be used until the module is loaded."; + Servers::Capab.erase("CLOAK"); + } + } - if (params[1].equals_cs("accountname")) - { - if (params[2].empty()) - { - // The user has been logged out by the IRC server. - u->Logout(); - } - else - { - // If we're bursting then then the user was probably logged - // in during a previous connection. - NickCore *nc = NickCore::Find(params[2]); - if (nc) - u->Login(nc); - } - } + void HandleMaxList(Channel *c, const Anope::string &value) + { + ListLimits limits; + spacesepstream limitstream(value); + Anope::string modechr, modelimit; + while (limitstream.GetToken(modechr) && limitstream.GetToken(modelimit)) + { + limits.emplace(modechr[0], Anope::Convert<unsigned>(modelimit, 0)); + } + maxlist.Set(c, limits); + } - /* - * possible incoming ssl_cert messages: - * Received: :409 METADATA 409AAAAAA ssl_cert :vTrSe c38070ce96e41cc144ed6590a68d45a6 <...> <...> - * Received: :409 METADATA 409AAAAAC ssl_cert :vTrSE Could not get peer certificate: error:00000000:lib(0):func(0):reason(0) - */ - else if (params[1].equals_cs("ssl_cert")) - { - u->Extend<bool>("ssl"); + static void HandleMLock(Channel *c, const Anope::string &value) + { + // :36D METADATA #chan 69 nts + if (!c->ci) + return; // Not registered. - Anope::string data; - spacesepstream tokens(params[2]); - if (!tokens.GetToken(data) || data.find('E') != Anope::string::npos || !tokens.GetToken(data)) - return; // Malformed or no client certificate. + Anope::string modes; + const auto *modelocks = c->ci->GetExt<ModeLocks>("modelocks"); + if (modelocks) + modes = modelocks->GetMLockAsString(false).replace_all_cs("+", "").replace_all_cs("-", ""); - commasepstream fingerprints(data); - if (!fingerprints.GetToken(data)) - return; // Should never happen? + // Mode lock string is not what we say it is? + if (!modes.equals_cs(value)) + Uplink::Send("METADATA", c->name, c->creation_time, "mlock", modes); + } - u->fingerprint = data; - FOREACH_MOD(OnFingerprint, (u)); + static void HandleModules(Server *s, const Anope::string &value) + { + // :20D METADATA * modules +cloak=custom + // :20D METADATA * modules -cloak - while (certs && fingerprints.GetToken(data)) - certs->ReplaceCert(data, u->fingerprint); - } - } - else if (params[0] == "*") + // We are only interested when it comes from our uplink. + if (s->GetUplink() != Me) + return; + + auto plus = (value[0] == '+'); + if (!plus && value[0] != '-') + return; // Malformed. + + bool required = false; + Anope::string capab, modname, moddata; + ParseModule(value.substr(1), modname, moddata); + + if (modname.equals_cs("account")) + required = true; + + else if (modname.equals_cs("cban")) { - // Wed Oct 3 15:40:27 2012: S[14] O :20D METADATA * modules :-m_svstopic.so + if (plus && (spanningtree_proto_ver >= 1206 || moddata == "glob")) + IRCD->CanSQLineChannel = true; + else + IRCD->CanSQLineChannel = false; + } - if (params[1].equals_cs("modules") && !params[2].empty()) - { - // only interested when it comes from our uplink - Server *server = source.GetServer(); - if (!server || server->GetUplink() != Me) - return; + else if (modname.equals_cs("cban") && spanningtree_proto_ver >= 1206) + IRCD->CanSQLineChannel = plus; - bool plus = (params[2][0] == '+'); - if (!plus && params[2][0] != '-') - return; + else if (modname.equals_cs("chghost")) + capab = "CHGHOST"; - bool required = false; - Anope::string capab, modname, moddata; - ParseModule(params[2].substr(1), modname, moddata); + else if (modname.equals_cs("chgident")) + capab = "CHGIDENT"; - if (modname.equals_cs("account")) - required = true; + else if (modname.equals_cs("globops")) + capab = "GLOBOPS"; - else if (modname.equals_cs("cban")) - { - if (plus && (spanningtree_proto_ver >= 1206 || moddata == "glob")) - IRCD->CanSQLineChannel = true; - else - IRCD->CanSQLineChannel = false; - } + else if (modname.equals_cs("hidechans")) + required = true; - else if (modname.equals_cs("cban") && spanningtree_proto_ver >= 1206) - IRCD->CanSQLineChannel = plus; + else if (modname.equals_cs("ircv3_ctctags")) + IRCD->CanTagMessage = plus; - else if (modname.equals_cs("chghost")) - capab = "CHGHOST"; + else if (modname.equals_cs("rline")) + capab = "RLINE"; - else if (modname.equals_cs("chgident")) - capab = "CHGIDENT"; + else if (modname.equals_cs("services")) + required = true; - else if (modname.equals_cs("globops")) - capab = "GLOBOPS"; + // Deprecated 1205 modules. + else if (modname.equals_cs("services_account")) + required = true; - else if (modname.equals_cs("hidechans")) - required = true; + else if (modname.equals_cs("svshold")) + IRCD->CanSVSHold = plus; - else if (modname.equals_cs("ircv3_ctctags")) - IRCD->CanTagMessage = plus; + else if (modname.equals_cs("topiclock")) + capab = "TOPICLOCK"; - else if (modname.equals_cs("rline")) - capab = "RLINE"; + else + return; - else if (modname.equals_cs("services")) - required = true; + if (required) + { + if (plus) + Log() << "Warning: InspIRCd unloaded the " << modname << " module. Anope won't function correctly without it."; + } + else + { + if (plus && !capab.empty()) + Servers::Capab.insert(capab); - // Deprecated 1205 modules. - else if (modname.equals_cs("services_account")) - required = true; + else if (!capab.empty()) + Servers::Capab.erase(capab); - else if (modname.equals_cs("svshold")) - IRCD->CanSVSHold = plus; + Log() << "InspIRCd " << (plus ? "loaded" : "unloaded") << " the " << modname << " module; adjusted functionality."; + } + } - else if (modname.equals_cs("topiclock")) - capab = "TOPICLOCK"; + void HandleSSLCert(User *u, const Anope::string &value) + { + // :409 METADATA 409AAAAAA ssl_cert :vTrSe c38070ce96e41cc144ed6590a68d45a6 <...> <...> + // :409 METADATA 409AAAAAC ssl_cert :vTrSE Could not get peer certificate: error:00000000:lib(0):func(0):reason(0) + u->Extend<bool>("ssl"); - else - return; + Anope::string data; + spacesepstream tokens(value); + if (!tokens.GetToken(data) || data.find('E') != Anope::string::npos || !tokens.GetToken(data)) + return; // Malformed or no client certificate. - if (required) - { - if (plus) - Log() << "Warning: InspIRCd unloaded the " << modname << " module. Anope won't function correctly without it."; - } - else - { - if (plus && !capab.empty()) - Servers::Capab.insert(capab); - else if (!capab.empty()) - Servers::Capab.erase(capab); + commasepstream fingerprints(data); + if (!fingerprints.GetToken(data)) + return; // Should never happen? - Log() << "InspIRCd " << (plus ? "loaded" : "unloaded") << " the " << modname << " module; adjusted functionality."; - } + // The first fingerprint is always the current one. + u->fingerprint = data; + FOREACH_MOD(OnFingerprint, (u)); + + // Any extra fingerprints are using backup algorithms. + while (certs && fingerprints.GetToken(data)) + certs->ReplaceCert(data, u->fingerprint); + } + + static void HandleTopicLock(Channel *c, const Anope::string &value) + { + // :36D METADATA #chan 69 1 + if (!c->ci) + return; // Not registered. + + auto localstate = c->ci->HasExt("TOPICLOCK"); + auto remotestate = Anope::Convert<bool>(value, false); + if (localstate != remotestate) + Uplink::Send("METADATA", c->name, c->creation_time, "topiclock", !!localstate); + } + +public: + IRCDMessageMetadata(Module *creator, PrimitiveExtensibleItem<ListLimits> &listlimits) + : IRCDMessage(creator, "METADATA", 2) + , certs("CertService", "certs") + , maxlist(listlimits) + { + SetFlag(FLAG_REQUIRE_SERVER); + SetFlag(FLAG_SOFT_LIMIT); + } + + void Run(MessageSource &source, const std::vector<Anope::string> ¶ms, const Anope::map<Anope::string> &tags) override + { + if (params[0][0] == '#') + { + // :<sid> METADATA <chan> <ts> <key> [<value>] + + // Channel METADATA has an additional parameter: the channel ts. + // We ignore non-bursting servers to avoid pseudoserver fights. + if (params.size() < 3 || source.GetServer()->IsSynced()) + return; + + auto *c = Channel::Find(params[0]); + if (!c) + { + Log(LOG_DEBUG) << "Received a METADATA " << params[2] << " message for unintroduced channel " << params[0]; + return; } - else if (params[1].equals_cs("cloakmethods")) + const auto &value = params.size() > 3 ? params[3] : ""; + if (params[1].equals_ci("maxlist")) + HandleMaxList(c, value); + + else if (params[1].equals_ci("mlock")) + HandleMLock(c, value); + } + else if (isdigit(params[0][0])) + { + // :<sid> METADATA <uuid> <key> [<value>] + auto *u = User::Find(params[0]); + if (!u) { - spacesepstream tokens(params[2]); - for (Anope::string token; tokens.GetToken(token); ) - { - const auto idx = token.find('='); - if (token.compare(0, idx, "custom", 6) == 0) - { - Log() << "The remote server has the custom cloak method; this will be used for setting vhosts."; - Servers::Capab.insert("CLOAK"); - return; - } - } + Log(LOG_DEBUG) << "Received a METADATA " << params[1] << " message for unintroduced user " << params[0]; + return; + } - Log() << "The remote server does not have the custom cloak method; CHGIDENT and CHGHOST will be used until the module is loaded."; - Servers::Capab.erase("CLOAK"); + const auto &value = params.size() > 2 ? params[2] : ""; + if (params[1].equals_ci("accountname")) + HandleAccountName(u, value); + + else if (params[1].equals_ci("ssl_cert")) + HandleSSLCert(u, value); + } + else if (params[0] == "*") + { + // :<sid> METADATA * <key> [<value>] + auto *s = source.GetServer(); + if (!s) + { + Log(LOG_DEBUG) << "Received a METADATA " << params[1] << " message from unintroduced server " << source.GetName(); + return; } + + const auto &value = params.size() > 2 ? params[2] : ""; + if (params[1].equals_ci("cloakmethods")) + HandleCloakMethods(s, value); + + else if (params[1].equals_ci("modules")) + HandleModules(s, value); } } }; |