aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorGravatar Sadie Powell2024-02-26 18:01:03 +0000
committerGravatar Sadie Powell2024-02-27 13:15:08 +0000
commit81e84056170f4f25a8dd0647739486f28fb5edcd (patch)
tree53259cc51942bb1db0149eb429a95780481b14e4
parentMerge branch 'insp3' into master. (diff)
Move SVSJOIN/SVSNICK/SVSPART to the services module.
-rw-r--r--src/modules/m_services.cpp164
-rw-r--r--src/modules/m_spanningtree/commands.h39
-rw-r--r--src/modules/m_spanningtree/compat.cpp17
-rw-r--r--src/modules/m_spanningtree/encap.cpp10
-rw-r--r--src/modules/m_spanningtree/main.cpp5
-rw-r--r--src/modules/m_spanningtree/svsjoin.cpp61
-rw-r--r--src/modules/m_spanningtree/svsnick.cpp78
-rw-r--r--src/modules/m_spanningtree/svspart.cpp50
-rw-r--r--src/modules/m_spanningtree/treesocket.h4
-rw-r--r--src/modules/m_spanningtree/treesocket2.cpp7
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;