diff options
| author | 2024-02-26 18:01:03 +0000 | |
|---|---|---|
| committer | 2024-02-27 13:15:08 +0000 | |
| commit | 81e84056170f4f25a8dd0647739486f28fb5edcd (patch) | |
| tree | 53259cc51942bb1db0149eb429a95780481b14e4 | |
| parent | Merge branch 'insp3' into master. (diff) | |
Move SVSJOIN/SVSNICK/SVSPART to the services module.
| -rw-r--r-- | src/modules/m_services.cpp | 164 | ||||
| -rw-r--r-- | src/modules/m_spanningtree/commands.h | 39 | ||||
| -rw-r--r-- | src/modules/m_spanningtree/compat.cpp | 17 | ||||
| -rw-r--r-- | src/modules/m_spanningtree/encap.cpp | 10 | ||||
| -rw-r--r-- | src/modules/m_spanningtree/main.cpp | 5 | ||||
| -rw-r--r-- | src/modules/m_spanningtree/svsjoin.cpp | 61 | ||||
| -rw-r--r-- | src/modules/m_spanningtree/svsnick.cpp | 78 | ||||
| -rw-r--r-- | src/modules/m_spanningtree/svspart.cpp | 50 | ||||
| -rw-r--r-- | src/modules/m_spanningtree/treesocket.h | 4 | ||||
| -rw-r--r-- | src/modules/m_spanningtree/treesocket2.cpp | 7 |
10 files changed, 193 insertions, 242 deletions
diff --git a/src/modules/m_services.cpp b/src/modules/m_services.cpp index e7ef487cf..d27d738c4 100644 --- a/src/modules/m_services.cpp +++ b/src/modules/m_services.cpp @@ -210,6 +210,164 @@ public: } }; +class CommandSVSJoin final + : public Command +{ +public: + CommandSVSJoin(Module* mod) + : Command(mod, "SVSJOIN", 2) + { + access_needed = CmdAccess::SERVER; + } + + CmdResult Handle(User* user, const Params& parameters) override + { + // The command can only be executed by remote services servers. + if (IS_LOCAL(user) || !user->server->IsService()) + return CmdResult::FAILURE; + + // Check for a valid channel name. + if (!ServerInstance->Channels.IsChannel(parameters[1])) + return CmdResult::FAILURE; + + // Check the target exists/ + auto* u = ServerInstance->Users.FindUUID(parameters[0]); + if (!u) + return CmdResult::FAILURE; + + /* only join if it's local, otherwise just pass it on! */ + auto* lu = IS_LOCAL(u); + if (lu) + { + bool override = false; + std::string key; + if (parameters.size() >= 3) + { + key = parameters[2]; + if (key.empty()) + override = true; + } + + Channel::JoinUser(lu, parameters[1], override, key); + } + + return CmdResult::SUCCESS; + } + + RouteDescriptor GetRouting(User* user, const Params& parameters) override + { + return ROUTE_UNICAST(parameters[0]); + } +}; + +class CommandSVSNick final + : public Command +{ +public: + CommandSVSNick(Module* mod) + : Command(mod, "SVSNICK", 3) + { + access_needed = CmdAccess::SERVER; + } + + CmdResult Handle(User* user, const Params& parameters) override + { + // The command can only be executed by remote services servers. + if (IS_LOCAL(user) || !user->server->IsService()) + return CmdResult::FAILURE; + + auto* u = ServerInstance->Users.Find(parameters[0]); + if (u && IS_LOCAL(u)) + { + // The 4th parameter is optional and it is the expected nick TS of the target user. If + // this parameter is present and it doesn't match the user's nick TS, the SVSNICK is not + // acted upon. + // + // This makes it possible to detect the case when services wants to change the nick of + // a user, but the user changes their nick before the SVSNICK arrives, making the + // SVSNICK nick change (usually to a guest nick) unnecessary. Consider the following for + // example: + // + // 1. test changes nick to Attila which is protected by services + // 2. Services SVSNICKs the user to Guest12345 + // 3. Attila changes nick to Attila_ which isn't protected by services + // 4. SVSNICK arrives + // 5. Attila_ gets his nick changed to Guest12345 unnecessarily + // + // In this case when the SVSNICK is processed the target has already changed their nick + // to something which isn't protected, so changing the nick again to a Guest nick is not + // desired. However, if the expected nick TS parameter is present in the SVSNICK then + // the nick change in step 5 won't happen because the timestamps won't match. + if (parameters.size() > 3) + { + time_t expectedts = ConvToNum<time_t>(parameters[3]); + if (!expectedts) + return CmdResult::INVALID; // Malformed message + + if (u->nickchanged != expectedts) + return CmdResult::FAILURE; // Ignore SVSNICK + } + + std::string nick = parameters[1]; + if (isdigit(nick[0])) + nick = u->uuid; + + time_t nickts = ConvToNum<time_t>(parameters[2]); + if (!nickts) + return CmdResult::INVALID; // Malformed message + + if (!u->ChangeNick(nick, nickts)) + { + // Changing to 'nick' failed (it may already be in use), change to the uuid + u->WriteNumeric(RPL_SAVENICK, u->uuid, "Your nickname is in use by an older user on a new server."); + u->ChangeNick(u->uuid); + } + } + return CmdResult::SUCCESS; + } + + RouteDescriptor GetRouting(User* user, const Params& parameters) override + { + return ROUTE_UNICAST(parameters[0]); + } +}; + +class CommandSVSPart final + : public Command +{ +public: + CommandSVSPart(Module* mod) + : Command(mod, "SVSPART", 2) + { + access_needed = CmdAccess::SERVER; + } + + CmdResult Handle(User* user, const Params& parameters) override + { + // The command can only be executed by remote services servers. + if (IS_LOCAL(user) || !user->server->IsService()) + return CmdResult::FAILURE; + + auto* u = ServerInstance->Users.FindUUID(parameters[0]); + if (!u) + return CmdResult::FAILURE; + + auto* c = ServerInstance->Channels.Find(parameters[1]); + if (!c) + return CmdResult::FAILURE; + + if (IS_LOCAL(u)) + c->PartUser(u, parameters.size() == 3 ? parameters[2] : "Services forced part"); + + return CmdResult::SUCCESS; + } + + RouteDescriptor GetRouting(User* user, const Params& parameters) override + { + return ROUTE_UNICAST(parameters[0]); + } +}; + class ModuleServices final : public Module , public Stats::EventListener @@ -222,6 +380,9 @@ private: ServProtect servprotectmode; SVSHoldFactory svsholdfactory; CommandSVSHold svsholdcmd; + CommandSVSJoin svsjoincmd; + CommandSVSNick svsnickcmd; + CommandSVSPart svspartcmd; bool accountoverrideshold; public: @@ -234,6 +395,9 @@ public: , servicetag(this) , servprotectmode(this) , svsholdcmd(this) + , svsjoincmd(this) + , svsnickcmd(this) + , svspartcmd(this) { } diff --git a/src/modules/m_spanningtree/commands.h b/src/modules/m_spanningtree/commands.h index 6ce65c29d..a34e774ce 100644 --- a/src/modules/m_spanningtree/commands.h +++ b/src/modules/m_spanningtree/commands.h @@ -69,42 +69,6 @@ public: RouteDescriptor GetRouting(User* user, const Params& parameters) override; }; -class CommandSVSJoin final - : public ServerCommand -{ -public: - CommandSVSJoin(Module* Creator) - : ServerCommand(Creator, "SVSJOIN", 2) - { - } - CmdResult Handle(User* user, Params& params) override; - RouteDescriptor GetRouting(User* user, const Params& parameters) override; -}; - -class CommandSVSPart final - : public ServerCommand -{ -public: - CommandSVSPart(Module* Creator) - : ServerCommand(Creator, "SVSPART", 2) - { - } - CmdResult Handle(User* user, Params& params) override; - RouteDescriptor GetRouting(User* user, const Params& parameters) override; -}; - -class CommandSVSNick final - : public ServerCommand -{ -public: - CommandSVSNick(Module* Creator) - : ServerCommand(Creator, "SVSNICK", 3) - { - } - CmdResult Handle(User* user, Params& params) override; - RouteDescriptor GetRouting(User* user, const Params& parameters) override; -}; - class CommandMetadata final : public ServerCommand { @@ -546,9 +510,6 @@ public: class SpanningTreeCommands final { public: - CommandSVSJoin svsjoin; - CommandSVSPart svspart; - CommandSVSNick svsnick; CommandMetadata metadata; CommandUID uid; CommandOpertype opertype; diff --git a/src/modules/m_spanningtree/compat.cpp b/src/modules/m_spanningtree/compat.cpp index 5d61272ea..d193d5e5a 100644 --- a/src/modules/m_spanningtree/compat.cpp +++ b/src/modules/m_spanningtree/compat.cpp @@ -109,3 +109,20 @@ void TreeSocket::WriteLine(const std::string& original_line) WriteLineInternal(line); } + +bool TreeSocket::PreProcessOldProtocolMessage(User*& who, std::string& cmd, CommandBase::Params& params) +{ + if (irc::equals(cmd, "SVSJOIN") || irc::equals(cmd, "SVSNICK") || irc::equals(cmd, "SVSPART")) + { + if (params.empty()) + return false; // Malformed. + + auto* target = ServerInstance->Users.FindUUID(params[0]); + if (!target) + return false; // User gone. + + params.insert(params.begin(), { cmd, target->uuid }); + cmd = "ENCAP"; + } + return true; +} diff --git a/src/modules/m_spanningtree/encap.cpp b/src/modules/m_spanningtree/encap.cpp index be4244ce6..0cd3b28dd 100644 --- a/src/modules/m_spanningtree/encap.cpp +++ b/src/modules/m_spanningtree/encap.cpp @@ -33,16 +33,6 @@ CmdResult CommandEncap::Handle(User* user, Params& params) if (ServerInstance->Config->ServerId == params[0] || InspIRCd::Match(ServerInstance->Config->ServerName, params[0])) { CommandBase::Params plist(params.begin() + 2, params.end()); - - // XXX: Workaround for SVS* commands provided by spanningtree not being registered in the core - if ((params[1] == "SVSNICK") || (params[1] == "SVSJOIN") || (params[1] == "SVSPART")) - { - ServerCommand* const scmd = Utils->Creator->CmdManager.GetHandler(params[1]); - if (scmd) - scmd->Handle(user, plist); - return CmdResult::SUCCESS; - } - Command* cmd = nullptr; ServerInstance->Parser.CallHandler(params[1], plist, user, &cmd); // Discard return value, ENCAP shall succeed even if the command does not exist diff --git a/src/modules/m_spanningtree/main.cpp b/src/modules/m_spanningtree/main.cpp index 718ff83ae..136001d91 100644 --- a/src/modules/m_spanningtree/main.cpp +++ b/src/modules/m_spanningtree/main.cpp @@ -63,10 +63,7 @@ ModuleSpanningTree::ModuleSpanningTree() } SpanningTreeCommands::SpanningTreeCommands(ModuleSpanningTree* module) - : svsjoin(module) - , svspart(module) - , svsnick(module) - , metadata(module) + : metadata(module) , uid(module) , opertype(module) , fjoin(module) diff --git a/src/modules/m_spanningtree/svsjoin.cpp b/src/modules/m_spanningtree/svsjoin.cpp deleted file mode 100644 index db55b4cb9..000000000 --- a/src/modules/m_spanningtree/svsjoin.cpp +++ /dev/null @@ -1,61 +0,0 @@ -/* - * InspIRCd -- Internet Relay Chat Daemon - * - * Copyright (C) 2013, 2018 Sadie Powell <sadie@witchery.services> - * Copyright (C) 2012-2014, 2016 Attila Molnar <attilamolnar@hush.com> - * Copyright (C) 2012 Robby <robby@chatbelgie.be> - * Copyright (C) 2010 Daniel De Graaf <danieldg@inspircd.org> - * Copyright (C) 2008-2009 Robin Burchell <robin+git@viroteck.net> - * - * 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 "commands.h" - -CmdResult CommandSVSJoin::Handle(User* user, Params& parameters) -{ - // Check for valid channel name - if (!ServerInstance->Channels.IsChannel(parameters[1])) - return CmdResult::FAILURE; - - // Check target exists - auto* u = ServerInstance->Users.FindUUID(parameters[0]); - if (!u) - return CmdResult::FAILURE; - - /* only join if it's local, otherwise just pass it on! */ - LocalUser* localuser = IS_LOCAL(u); - if (localuser) - { - bool override = false; - std::string key; - if (parameters.size() >= 3) - { - key = parameters[2]; - if (key.empty()) - override = true; - } - - Channel::JoinUser(localuser, parameters[1], override, key); - } - - return CmdResult::SUCCESS; -} - -RouteDescriptor CommandSVSJoin::GetRouting(User* user, const Params& parameters) -{ - return ROUTE_OPT_UCAST(parameters[0]); -} diff --git a/src/modules/m_spanningtree/svsnick.cpp b/src/modules/m_spanningtree/svsnick.cpp deleted file mode 100644 index 6423a8437..000000000 --- a/src/modules/m_spanningtree/svsnick.cpp +++ /dev/null @@ -1,78 +0,0 @@ -/* - * InspIRCd -- Internet Relay Chat Daemon - * - * Copyright (C) 2018-2020 Sadie Powell <sadie@witchery.services> - * Copyright (C) 2013-2016 Attila Molnar <attilamolnar@hush.com> - * Copyright (C) 2012 Robby <robby@chatbelgie.be> - * Copyright (C) 2009-2010 Daniel De Graaf <danieldg@inspircd.org> - * Copyright (C) 2009 Uli Schlachter <psychon@inspircd.org> - * Copyright (C) 2008 Robin Burchell <robin+git@viroteck.net> - * - * 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 "main.h" -#include "commands.h" - -CmdResult CommandSVSNick::Handle(User* user, Params& parameters) -{ - auto* u = ServerInstance->Users.Find(parameters[0]); - - if (u && IS_LOCAL(u)) - { - // The 4th parameter is optional and it is the expected nick TS of the target user. If this parameter is - // present and it doesn't match the user's nick TS, the SVSNICK is not acted upon. - // This makes it possible to detect the case when services wants to change the nick of a user, but the - // user changes their nick before the SVSNICK arrives, making the SVSNICK nick change (usually to a guest nick) - // unnecessary. Consider the following for example: - // - // 1. test changes nick to Attila which is protected by services - // 2. Services SVSNICKs the user to Guest12345 - // 3. Attila changes nick to Attila_ which isn't protected by services - // 4. SVSNICK arrives - // 5. Attila_ gets his nick changed to Guest12345 unnecessarily - // - // In this case when the SVSNICK is processed the target has already changed their nick to something - // which isn't protected, so changing the nick again to a Guest nick is not desired. - // However, if the expected nick TS parameter is present in the SVSNICK then the nick change in step 5 - // won't happen because the timestamps won't match. - if (parameters.size() > 3) - { - time_t ExpectedTS = ServerCommand::ExtractTS(parameters[3]); - if (u->nickchanged != ExpectedTS) - return CmdResult::FAILURE; // Ignore SVSNICK - } - - std::string nick = parameters[1]; - if (isdigit(nick[0])) - nick = u->uuid; - - time_t NickTS = ServerCommand::ExtractTS(parameters[2]); - if (!u->ChangeNick(nick, NickTS)) - { - // Changing to 'nick' failed (it may already be in use), change to the uuid - u->WriteNumeric(RPL_SAVENICK, u->uuid, "Your nickname is in use by an older user on a new server."); - u->ChangeNick(u->uuid); - } - } - - return CmdResult::SUCCESS; -} - -RouteDescriptor CommandSVSNick::GetRouting(User* user, const Params& parameters) -{ - return ROUTE_OPT_UCAST(parameters[0]); -} diff --git a/src/modules/m_spanningtree/svspart.cpp b/src/modules/m_spanningtree/svspart.cpp deleted file mode 100644 index cff40141a..000000000 --- a/src/modules/m_spanningtree/svspart.cpp +++ /dev/null @@ -1,50 +0,0 @@ -/* - * InspIRCd -- Internet Relay Chat Daemon - * - * Copyright (C) 2018 Sadie Powell <sadie@witchery.services> - * Copyright (C) 2012, 2016 Attila Molnar <attilamolnar@hush.com> - * Copyright (C) 2012 Robby <robby@chatbelgie.be> - * Copyright (C) 2009-2010 Daniel De Graaf <danieldg@inspircd.org> - * Copyright (C) 2009 Uli Schlachter <psychon@inspircd.org> - * Copyright (C) 2008 Robin Burchell <robin+git@viroteck.net> - * - * 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 "commands.h" - -CmdResult CommandSVSPart::Handle(User* user, Params& parameters) -{ - auto* u = ServerInstance->Users.FindUUID(parameters[0]); - if (!u) - return CmdResult::FAILURE; - - auto* c = ServerInstance->Channels.Find(parameters[1]); - if (!c) - return CmdResult::FAILURE; - - if (IS_LOCAL(u)) - { - std::string reason = (parameters.size() == 3) ? parameters[2] : "Services forced part"; - c->PartUser(u, reason); - } - return CmdResult::SUCCESS; -} - -RouteDescriptor CommandSVSPart::GetRouting(User* user, const Params& parameters) -{ - return ROUTE_OPT_UCAST(parameters[0]); -} diff --git a/src/modules/m_spanningtree/treesocket.h b/src/modules/m_spanningtree/treesocket.h index 497d0b46b..2d1b9e609 100644 --- a/src/modules/m_spanningtree/treesocket.h +++ b/src/modules/m_spanningtree/treesocket.h @@ -333,7 +333,11 @@ public: /** Handle socket timeout from connect() */ void OnTimeout() override; + /** Handle server quit on close */ void Close() override; + + /** Fixes messages coming from old servers so the new command handlers understand them. */ + bool PreProcessOldProtocolMessage(User*& who, std::string& cmd, CommandBase::Params& params); }; diff --git a/src/modules/m_spanningtree/treesocket2.cpp b/src/modules/m_spanningtree/treesocket2.cpp index 09bc27807..1398e323d 100644 --- a/src/modules/m_spanningtree/treesocket2.cpp +++ b/src/modules/m_spanningtree/treesocket2.cpp @@ -347,6 +347,13 @@ void TreeSocket::ProcessConnectedLine(std::string& taglist, std::string& prefix, return; } + // Translate commands coming from servers using an older protocol + if (proto_version < PROTO_NEWEST) + { + if (!PreProcessOldProtocolMessage(who, command, params)) + return; + } + ServerCommand* scmd = Utils->Creator->CmdManager.GetHandler(command); CommandBase* cmdbase = scmd; Command* cmd = nullptr; |
