summaryrefslogtreecommitdiff
path: root/modules
diff options
context:
space:
mode:
authorSadie Powell <sadie@witchery.services>2025-03-14 20:36:03 +0000
committerSadie Powell <sadie@witchery.services>2025-03-14 23:21:42 +0000
commit0ebc43f0dc277b307f671757005e218a502cc8a4 (patch)
tree9ddc6d2d3fbac56b131df4703f810f0904d1bc08 /modules
parent5ff86ea2c5361401de5f0deed0250ad4ae7dc853 (diff)
Refactor the InspIRCd METADATA handler to actually be readable.
Diffstat (limited to 'modules')
-rw-r--r--modules/protocol/inspircd.cpp379
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> &params, 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> &params, 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);
}
}
};