summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorattilamolnar <attilamolnar@hush.com>2013-08-19 20:36:41 +0200
committerattilamolnar <attilamolnar@hush.com>2013-08-28 12:11:33 +0200
commit7432fea968127b606fc029ae462e91d3f30df8a4 (patch)
tree396250c0766372c87e83a73d00e2018ed61cc69a /src
parent7e7e773d4d43e68c18c19e2ec712f4250e5f756d (diff)
m_spanningtree Propagate topic changes via FTOPIC in order to prevent desync when two TOPIC messages cross
TOPIC is no longer accepted from servers using the new protocol
Diffstat (limited to 'src')
-rw-r--r--src/commands/cmd_topic.cpp19
-rw-r--r--src/modules/m_spanningtree/commands.h9
-rw-r--r--src/modules/m_spanningtree/compat.cpp24
-rw-r--r--src/modules/m_spanningtree/ftopic.cpp49
-rw-r--r--src/modules/m_spanningtree/main.cpp5
-rw-r--r--src/modules/m_spanningtree/netburst.cpp6
-rw-r--r--src/modules/m_spanningtree/protocolinterface.cpp10
7 files changed, 80 insertions, 42 deletions
diff --git a/src/commands/cmd_topic.cpp b/src/commands/cmd_topic.cpp
index 997fb6a91..8f5979865 100644
--- a/src/commands/cmd_topic.cpp
+++ b/src/commands/cmd_topic.cpp
@@ -27,7 +27,7 @@
* the same way, however, they can be fully unloaded, where these
* may not.
*/
-class CommandTopic : public Command
+class CommandTopic : public SplitCommand
{
ChanModeReference secretmode;
ChanModeReference topiclockmode;
@@ -36,7 +36,7 @@ class CommandTopic : public Command
/** Constructor for topic.
*/
CommandTopic(Module* parent)
- : Command(parent, "TOPIC", 1, 2)
+ : SplitCommand(parent, "TOPIC", 1, 2)
, secretmode(parent, "secret")
, topiclockmode(parent, "topiclock")
{
@@ -50,14 +50,10 @@ class CommandTopic : public Command
* @param user The user issuing the command
* @return A value from CmdResult to indicate command success or failure.
*/
- CmdResult Handle(const std::vector<std::string>& parameters, User *user);
- RouteDescriptor GetRouting(User* user, const std::vector<std::string>& parameters)
- {
- return (IS_LOCAL(user) ? ROUTE_LOCALONLY : ROUTE_BROADCAST);
- }
+ CmdResult HandleLocal(const std::vector<std::string>& parameters, LocalUser* user);
};
-CmdResult CommandTopic::Handle (const std::vector<std::string>& parameters, User *user)
+CmdResult CommandTopic::HandleLocal(const std::vector<std::string>& parameters, LocalUser* user)
{
Channel* c = ServerInstance->FindChan(parameters[0]);
if (!c)
@@ -89,13 +85,6 @@ CmdResult CommandTopic::Handle (const std::vector<std::string>& parameters, User
return CMD_SUCCESS;
}
- // Access checks are skipped for non-local users
- if (!IS_LOCAL(user))
- {
- c->SetTopic(user, parameters[1]);
- return CMD_SUCCESS;
- }
-
std::string t = parameters[1]; // needed, in case a module wants to change it
ModResult res;
FIRST_MOD_RESULT(OnPreTopicChange, res, (user,c,t));
diff --git a/src/modules/m_spanningtree/commands.h b/src/modules/m_spanningtree/commands.h
index 007c0f2b0..f15708a9a 100644
--- a/src/modules/m_spanningtree/commands.h
+++ b/src/modules/m_spanningtree/commands.h
@@ -145,8 +145,15 @@ class CommandFMode : public ServerCommand
class CommandFTopic : public ServerCommand
{
public:
- CommandFTopic(Module* Creator) : ServerCommand(Creator, "FTOPIC", 5) { }
+ CommandFTopic(Module* Creator) : ServerCommand(Creator, "FTOPIC", 4, 5) { }
CmdResult Handle(User* user, std::vector<std::string>& params);
+
+ class Builder : public CmdBuilder
+ {
+ public:
+ Builder(Channel* chan);
+ Builder(User* user, Channel* chan);
+ };
};
class CommandFHost : public UserOnlyServerCommand<CommandFHost>
diff --git a/src/modules/m_spanningtree/compat.cpp b/src/modules/m_spanningtree/compat.cpp
index a9e48a10c..02e11c849 100644
--- a/src/modules/m_spanningtree/compat.cpp
+++ b/src/modules/m_spanningtree/compat.cpp
@@ -123,8 +123,10 @@ void TreeSocket::WriteLine(const std::string& original_line)
else if (command == "FTOPIC")
{
// Drop channel TS for FTOPIC
- // :sid FTOPIC #target TS TopicTS ...
- // A B C D
+ // :sid FTOPIC #target TS TopicTS setter :newtopic
+ // A B C D E F
+ // :uid FTOPIC #target TS TopicTS :newtopic
+ // A B C D E
if (b == std::string::npos)
return;
@@ -136,7 +138,14 @@ void TreeSocket::WriteLine(const std::string& original_line)
if (d == std::string::npos)
return;
- line.erase(c, d-c);
+ std::string::size_type e = line.find(' ', d + 1);
+ if (line[e+1] == ':')
+ {
+ line.erase(c, e-c);
+ line.erase(a+1, 1);
+ }
+ else
+ line.erase(c, d-c);
}
else if ((command == "PING") || (command == "PONG"))
{
@@ -268,6 +277,15 @@ bool TreeSocket::PreProcessOldProtocolMessage(User*& who, std::string& cmd, std:
params.insert(params.begin()+1, "operquit");
who = MyRoot->ServerUser;
}
+ else if ((cmd == "TOPIC") && (params.size() >= 2))
+ {
+ // :20DAAAAAC TOPIC #chan :new topic
+ cmd = "FTOPIC";
+ if (!InsertCurrentChannelTS(params))
+ return false;
+
+ params.insert(params.begin()+2, ConvToStr(ServerInstance->Time()));
+ }
return true; // Passthru
}
diff --git a/src/modules/m_spanningtree/ftopic.cpp b/src/modules/m_spanningtree/ftopic.cpp
index 0a4a95f9e..bd15489a2 100644
--- a/src/modules/m_spanningtree/ftopic.cpp
+++ b/src/modules/m_spanningtree/ftopic.cpp
@@ -44,26 +44,65 @@ CmdResult CommandFTopic::Handle(User* user, std::vector<std::string>& params)
if (ts < c->topicset)
return CMD_FAILURE;
+ // The topic text is always the last parameter
+ const std::string& newtopic = params.back();
+
+ // If there is a setter in the message use that, otherwise use the message source
+ const std::string& setter = ((params.size() > 4) ? params[3] : (ServerInstance->Config->FullHostInTopic ? user->GetFullHost() : user->nick));
+
/*
* If the topics were updated at the exact same second, accept
* the remote only when it's "bigger" than ours as defined by
* string comparision, so non-empty topics always overridde
* empty topics if their timestamps are equal
+ *
+ * Similarly, if the topic texts are equal too, keep one topic
+ * setter and discard the other
*/
- if ((ts == c->topicset) && (c->topic > params[4]))
- return CMD_FAILURE; // Topics were set at the exact same time, keep our topic and setter
+ if (ts == c->topicset)
+ {
+ // Discard if their topic text is "smaller"
+ if (c->topic > newtopic)
+ return CMD_FAILURE;
- if (c->topic != params[4])
+ // If the texts are equal in addition to the timestamps, decide which setter to keep
+ if ((c->topic == newtopic) && (c->setby >= setter))
+ return CMD_FAILURE;
+ }
+
+ if (c->topic != newtopic)
{
// Update topic only when it differs from current topic
- c->topic.assign(params[4], 0, ServerInstance->Config->Limits.MaxTopic);
+ c->topic.assign(newtopic, 0, ServerInstance->Config->Limits.MaxTopic);
c->WriteChannel(user, "TOPIC %s :%s", c->name.c_str(), c->topic.c_str());
}
// Update setter and settime
- c->setby.assign(params[3], 0, 127);
+ c->setby.assign(setter, 0, 128);
c->topicset = ts;
+ FOREACH_MOD(OnPostTopicChange, (user, c, c->topic));
+
return CMD_SUCCESS;
}
+// Used when bursting and in reply to RESYNC, contains topic setter as the 4th parameter
+CommandFTopic::Builder::Builder(Channel* chan)
+ : CmdBuilder("FTOPIC")
+{
+ push(chan->name);
+ push_int(chan->age);
+ push_int(chan->topicset);
+ push(chan->setby);
+ push_last(chan->topic);
+}
+
+// Used when changing the topic, the setter is the message source
+CommandFTopic::Builder::Builder(User* user, Channel* chan)
+ : CmdBuilder(user, "FTOPIC")
+{
+ push(chan->name);
+ push_int(chan->age);
+ push_int(chan->topicset);
+ push_last(chan->topic);
+}
diff --git a/src/modules/m_spanningtree/main.cpp b/src/modules/m_spanningtree/main.cpp
index f458c2d2f..98f9a304b 100644
--- a/src/modules/m_spanningtree/main.cpp
+++ b/src/modules/m_spanningtree/main.cpp
@@ -422,10 +422,7 @@ void ModuleSpanningTree::OnPostTopicChange(User* user, Channel* chan, const std:
if (!IS_LOCAL(user))
return;
- CmdBuilder params(user->uuid, "TOPIC");
- params.push_back(chan->name);
- params.push_last(topic);
- params.Broadcast();
+ CommandFTopic::Builder(user, chan).Broadcast();
}
void ModuleSpanningTree::OnUserMessage(User* user, void* dest, int target_type, const std::string& text, char status, const CUList& exempt_list, MessageType msgtype)
diff --git a/src/modules/m_spanningtree/netburst.cpp b/src/modules/m_spanningtree/netburst.cpp
index 2cd107972..d4669442a 100644
--- a/src/modules/m_spanningtree/netburst.cpp
+++ b/src/modules/m_spanningtree/netburst.cpp
@@ -147,11 +147,7 @@ void TreeSocket::SyncChannel(Channel* chan)
// If the topic was ever set, send it, even if it's empty now
// because a new empty topic should override an old non-empty topic
if (chan->topicset != 0)
- {
- this->WriteLine(InspIRCd::Format(":%s FTOPIC %s %lu %lu %s :%s", ServerInstance->Config->GetSID().c_str(),
- chan->name.c_str(), (unsigned long)chan->age, (unsigned long)chan->topicset,
- chan->setby.c_str(), chan->topic.c_str()));
- }
+ this->WriteLine(CommandFTopic::Builder(chan));
for (Extensible::ExtensibleStore::const_iterator i = chan->GetExtList().begin(); i != chan->GetExtList().end(); i++)
{
diff --git a/src/modules/m_spanningtree/protocolinterface.cpp b/src/modules/m_spanningtree/protocolinterface.cpp
index afd463360..013dfac1b 100644
--- a/src/modules/m_spanningtree/protocolinterface.cpp
+++ b/src/modules/m_spanningtree/protocolinterface.cpp
@@ -70,15 +70,7 @@ void SpanningTreeProtocolInterface::SendMetaData(Extensible* target, const std::
void SpanningTreeProtocolInterface::SendTopic(Channel* channel, std::string &topic)
{
- CmdBuilder params("FTOPIC");
-
- params.push_back(channel->name);
- params.push_back(ConvToStr(channel->age));
- params.push_back(ConvToStr(ServerInstance->Time()));
- params.push_back(ServerInstance->Config->ServerName);
- params.push_last(topic);
-
- params.Broadcast();
+ CommandFTopic::Builder(ServerInstance->FakeClient, channel).Broadcast();
}
void SpanningTreeProtocolInterface::SendMode(User* source, User* u, Channel* c, const std::vector<std::string>& modedata, const std::vector<TranslateType>& translate)