summaryrefslogtreecommitdiff
path: root/src/modules/m_spanningtree
diff options
context:
space:
mode:
authorattilamolnar <attilamolnar@hush.com>2013-04-12 16:40:16 +0200
committerattilamolnar <attilamolnar@hush.com>2013-04-13 16:22:03 +0200
commit1626437cbf963b16ce44ef6cc829957fd3a7e2ff (patch)
tree1ea562260984c5c049bb5ea3f5b9ec8215a603ac /src/modules/m_spanningtree
parentdebe102133f478e2fbff597f6da92215bb8c2524 (diff)
m_spanningtree Introduce IJOIN and RESYNC
When a local user joins an existing channel, instead of an FJOIN, send an IJOIN with the channel name being the first parameter. If the joining user received prefix modes, append the channel TS and the prefix mode letters as the second and third parameters. When receiving an IJOIN, first check if the target channel exists. If it does not exist, ignore the join (that is, do not create the channel) and send a RESYNC back to the source. If the channel does exist then join the user, and in case any prefix modes were sent (found in the 3rd parameter), compare the TS of the channel to the TS in the IJOIN (2nd parameter). If the timestamps match, set the modes on the user, otherwise ignore the modes. Outgoing IJOINs to 1202 protocol servers are converted to FJOINs, but the channel mode parameter is left empty ("+").
Diffstat (limited to 'src/modules/m_spanningtree')
-rw-r--r--src/modules/m_spanningtree/commands.h17
-rw-r--r--src/modules/m_spanningtree/compat.cpp52
-rw-r--r--src/modules/m_spanningtree/ijoin.cpp93
-rw-r--r--src/modules/m_spanningtree/main.cpp31
4 files changed, 183 insertions, 10 deletions
diff --git a/src/modules/m_spanningtree/commands.h b/src/modules/m_spanningtree/commands.h
index c7dc08b59..7e171aa99 100644
--- a/src/modules/m_spanningtree/commands.h
+++ b/src/modules/m_spanningtree/commands.h
@@ -133,6 +133,21 @@ class CommandFName : public Command
RouteDescriptor GetRouting(User* user, const std::vector<std::string>& parameters) { return ROUTE_BROADCAST; }
};
+class CommandIJoin : public SplitCommand
+{
+ public:
+ CommandIJoin(Module* Creator) : SplitCommand(Creator, "IJOIN", 1) { flags_needed = FLAG_SERVERONLY; }
+ CmdResult HandleRemote(const std::vector<std::string>& parameters, RemoteUser* user);
+ RouteDescriptor GetRouting(User* user, const std::vector<std::string>& parameters) { return ROUTE_BROADCAST; }
+};
+
+class CommandResync : public SplitCommand
+{
+ public:
+ CommandResync(Module* Creator) : SplitCommand(Creator, "RESYNC", 1) { flags_needed = FLAG_SERVERONLY; }
+ CmdResult HandleServer(const std::vector<std::string>& parameters, FakeUser* user);
+};
+
class SpanningTreeCommands
{
public:
@@ -145,6 +160,8 @@ class SpanningTreeCommands
CommandUID uid;
CommandOpertype opertype;
CommandFJoin fjoin;
+ CommandIJoin ijoin;
+ CommandResync resync;
CommandFMode fmode;
CommandFTopic ftopic;
CommandFHost fhost;
diff --git a/src/modules/m_spanningtree/compat.cpp b/src/modules/m_spanningtree/compat.cpp
index 32bdb3df8..bfc2d1560 100644
--- a/src/modules/m_spanningtree/compat.cpp
+++ b/src/modules/m_spanningtree/compat.cpp
@@ -39,6 +39,58 @@ void TreeSocket::WriteLine(std::string line)
std::string command = line.substr(a + 1, b-a-1);
// now try to find a translation entry
// TODO a more efficient lookup method will be needed later
+ if (proto_version < 1205)
+ {
+ if (command == "IJOIN")
+ {
+ // Convert
+ // :<uid> IJOIN <chan> [<ts> [<flags>]]
+ // to
+ // :<sid> FJOIN <chan> <ts> + [<flags>],<uuid>
+ std::string::size_type c = line.find(' ', b + 1);
+ if (c == std::string::npos)
+ {
+ // No TS or modes in the command
+ // :22DAAAAAB IJOIN #chan
+ const std::string channame = line.substr(b+1, c-b-1);
+ Channel* chan = ServerInstance->FindChan(channame);
+ if (!chan)
+ return;
+
+ line.push_back(' ');
+ line.append(ConvToStr(chan->age));
+ line.append(" + ,");
+ }
+ else
+ {
+ std::string::size_type d = line.find(' ', c + 1);
+ if (d == std::string::npos)
+ {
+ // TS present, no modes
+ // :22DAAAAAC IJOIN #chan 12345
+ line.append(" + ,");
+ }
+ else
+ {
+ // Both TS and modes are present
+ // :22DAAAAAC IJOIN #chan 12345 ov
+ std::string::size_type e = line.find(' ', d + 1);
+ if (e != std::string::npos)
+ line.erase(e);
+
+ line.insert(d, " +");
+ line.push_back(',');
+ }
+ }
+
+ // Move the uuid to the end and replace the I with an F
+ line.append(line.substr(1, 9));
+ line.erase(4, 6);
+ line[5] = 'F';
+ }
+ else if (command == "RESYNC")
+ return;
+ }
}
}
diff --git a/src/modules/m_spanningtree/ijoin.cpp b/src/modules/m_spanningtree/ijoin.cpp
new file mode 100644
index 000000000..2f71ffe27
--- /dev/null
+++ b/src/modules/m_spanningtree/ijoin.cpp
@@ -0,0 +1,93 @@
+/*
+ * InspIRCd -- Internet Relay Chat Daemon
+ *
+ * Copyright (C) 2012-2013 Attila Molnar <attilamolnar@hush.com>
+ *
+ * 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"
+#include "utils.h"
+#include "treeserver.h"
+#include "treesocket.h"
+
+CmdResult CommandIJoin::HandleRemote(const std::vector<std::string>& params, RemoteUser* user)
+{
+ Channel* chan = ServerInstance->FindChan(params[0]);
+ if (!chan)
+ {
+ // Desync detected, recover
+ // Ignore the join and send RESYNC, this will result in the remote server sending all channel data to us
+ ServerInstance->Logs->Log("m_spanningtree", LOG_DEBUG, "Received IJOIN for non-existant channel: " + params[0]);
+
+ parameterlist p;
+ p.push_back(params[0]);
+ SpanningTreeUtilities* Utils = ((ModuleSpanningTree*)(Module*)creator)->Utils;
+ Utils->DoOneToOne(ServerInstance->Config->GetSID(), "RESYNC", p, user->server);
+
+ return CMD_FAILURE;
+ }
+
+ bool apply_modes;
+ if (params.size() > 1)
+ {
+ time_t RemoteTS = ConvToInt(params[1]);
+ if (!RemoteTS)
+ {
+ ServerInstance->Logs->Log("m_spanningtree", LOG_DEFAULT, "Invalid TS in IJOIN: " + params[1]);
+ return CMD_INVALID;
+ }
+
+ if (RemoteTS < chan->age)
+ {
+ ServerInstance->Logs->Log("m_spanningtree", LOG_DEFAULT, "Attempted to lower TS via IJOIN. Channel=" + params[0] + " RemoteTS=" + params[1] + " LocalTS=" + ConvToStr(chan->age));
+ return CMD_INVALID;
+ }
+ apply_modes = ((params.size() > 2) && (RemoteTS == chan->age));
+ }
+ else
+ apply_modes = false;
+
+ chan->ForceJoin(user, apply_modes ? &params[2] : NULL);
+ return CMD_SUCCESS;
+}
+
+CmdResult CommandResync::HandleServer(const std::vector<std::string>& params, FakeUser* user)
+{
+ ServerInstance->Logs->Log("m_spanningtree", LOG_DEBUG, "Resyncing " + params[0]);
+ Channel* chan = ServerInstance->FindChan(params[0]);
+ if (!chan)
+ {
+ // This can happen for a number of reasons, safe to ignore
+ ServerInstance->Logs->Log("m_spanningtree", LOG_DEBUG, "Channel does not exist");
+ return CMD_FAILURE;
+ }
+
+ SpanningTreeUtilities* Utils = ((ModuleSpanningTree*)(Module*)creator)->Utils;
+ TreeServer* server = Utils->FindServer(user->server);
+ if (!server)
+ return CMD_FAILURE;
+
+ TreeSocket* socket = server->GetSocket();
+ if (!socket)
+ {
+ ServerInstance->Logs->Log("m_spanningtree", LOG_DEFAULT, "Received RESYNC with a source that is not directly connected: " + user->uuid);
+ return CMD_INVALID;
+ }
+
+ // Send all known information about the channel
+ socket->SyncChannel(chan);
+ return CMD_SUCCESS;
+}
diff --git a/src/modules/m_spanningtree/main.cpp b/src/modules/m_spanningtree/main.cpp
index 77ef6a0fd..7282baac9 100644
--- a/src/modules/m_spanningtree/main.cpp
+++ b/src/modules/m_spanningtree/main.cpp
@@ -47,8 +47,8 @@ ModuleSpanningTree::ModuleSpanningTree()
SpanningTreeCommands::SpanningTreeCommands(ModuleSpanningTree* module)
: rconnect(module, module->Utils), rsquit(module, module->Utils),
svsjoin(module), svspart(module), svsnick(module), metadata(module),
- uid(module), opertype(module), fjoin(module), fmode(module), ftopic(module),
- fhost(module), fident(module), fname(module)
+ uid(module), opertype(module), fjoin(module), ijoin(module), resync(module),
+ fmode(module), ftopic(module), fhost(module), fident(module), fname(module)
{
}
@@ -63,6 +63,8 @@ void ModuleSpanningTree::init()
ServerInstance->Modules->AddService(commands->uid);
ServerInstance->Modules->AddService(commands->opertype);
ServerInstance->Modules->AddService(commands->fjoin);
+ ServerInstance->Modules->AddService(commands->ijoin);
+ ServerInstance->Modules->AddService(commands->resync);
ServerInstance->Modules->AddService(commands->fmode);
ServerInstance->Modules->AddService(commands->ftopic);
ServerInstance->Modules->AddService(commands->fhost);
@@ -551,20 +553,29 @@ void ModuleSpanningTree::OnUserConnect(LocalUser* user)
Utils->TreeRoot->UserCount++;
}
-void ModuleSpanningTree::OnUserJoin(Membership* memb, bool sync, bool created, CUList& excepts)
+void ModuleSpanningTree::OnUserJoin(Membership* memb, bool sync, bool created_by_local, CUList& excepts)
{
// Only do this for local users
if (IS_LOCAL(memb->user))
{
parameterlist params;
- // set up their permissions and the channel TS with FJOIN.
- // All users are FJOINed now, because a module may specify
- // new joining permissions for the user.
params.push_back(memb->chan->name);
- params.push_back(ConvToStr(memb->chan->age));
- params.push_back(std::string("+") + memb->chan->ChanModes(true));
- params.push_back(memb->modes+","+memb->user->uuid);
- Utils->DoOneToMany(ServerInstance->Config->GetSID(),"FJOIN",params);
+ if (created_by_local)
+ {
+ params.push_back(ConvToStr(memb->chan->age));
+ params.push_back(std::string("+") + memb->chan->ChanModes(true));
+ params.push_back(memb->modes+","+memb->user->uuid);
+ Utils->DoOneToMany(ServerInstance->Config->GetSID(),"FJOIN",params);
+ }
+ else
+ {
+ if (!memb->modes.empty())
+ {
+ params.push_back(ConvToStr(memb->chan->age));
+ params.push_back(memb->modes);
+ }
+ Utils->DoOneToMany(memb->user->uuid, "IJOIN", params);
+ }
}
}