Skip to content

Commit

Permalink
Merge pull request #167 from attilamolnar/insp21+spanningtree1204
Browse files Browse the repository at this point in the history
[2.1] Spanningtree protocol version 1204
  • Loading branch information
rburchell committed May 30, 2012
2 parents 3645845 + ef970fe commit ba1672b
Show file tree
Hide file tree
Showing 6 changed files with 163 additions and 19 deletions.
40 changes: 40 additions & 0 deletions src/modules/m_spanningtree/compat.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -207,6 +207,46 @@ void TreeSocket::WriteLine(std::string line)
}
}
}
else if (proto_version < 1204 && command == "METADATA")
{
// Drop TS for channel METADATA
// :sid METADATA #target TS extname ...
// A B C D
if (b == std::string::npos)
return;
std::string::size_type c = line.find(' ', b + 1);
if (c == std::string::npos)
return;

if (line[b + 1] == '#')
{
// We're sending a channel metadata indeed
std::string::size_type d = line.find(' ', c + 1);
if (d == std::string::npos)
return;

ServerInstance->Logs->Log("m_spanningtree", DEBUG, "Stripping channel TS in METADATA for pre-1204-protocol server");
line.erase(c, d-c);
}
}
else if (proto_version < 1204 && command == "FTOPIC")
{
// Drop channel TS for FTOPIC
// :sid FTOPIC #target TS TopicTS ...
// A B C D
if (b == std::string::npos)
return;
std::string::size_type c = line.find(' ', b + 1);
if (c == std::string::npos)
return;

std::string::size_type d = line.find(' ', c + 1);
if (d == std::string::npos)
return;

ServerInstance->Logs->Log("m_spanningtree", DEBUG, "Stripping channel TS in FTOPIC for pre-1204-protocol server");
line.erase(c, d-c);
}
}

ServerInstance->Logs->Log("m_spanningtree", RAWIO, "S[%d] O %s", this->GetFd(), line.c_str());
Expand Down
87 changes: 74 additions & 13 deletions src/modules/m_spanningtree/ftopic.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -28,24 +28,85 @@
/** FTOPIC command */
CmdResult CommandFTopic::Handle(const std::vector<std::string>& params, User *user)
{
time_t ts = atoi(params[1].c_str());
Channel* c = ServerInstance->FindChan(params[0]);
if (c)
if ((!c) || (!IS_SERVER(user)))
return CMD_FAILURE;

/**
* Here's how this works:
*
* In pre-1204 protocol version we had a syntax like
*
* FTOPIC #chan topicts setby :topic
*
* where topicts is the time when the topic was set, and setby is the n!u@h
* of the user who set it. If the topicts value was bigger (newer) than our topic
* TS (stored in Channel::topicset), we accepted the change, else we assumed
* we already had a more recent topic and did nothing.
*
* However this behavior meant that if the channel was recreated on a server
* during a netsplit, users there could set any topic and override ours with it
* at merge because their topic had a newer topic TS.
* Protocol version 1204 addresses this issue by adding the channel TS to FTOPIC:
*
* FTOPIC #chan chants topicts setby :topic
*
* so we notice when the channel has been recreated and discard the FTOPIC in that case.
* Apart from this the logic remains the same. For previous versions not supporting the
* new syntax we need to fall back to the old behavior.
*
*/

// Determine the protocol version of the sender
SpanningTreeUtilities* Utils = ((ModuleSpanningTree*) (Module*) creator)->Utils;
TreeServer* srcserver = Utils->FindServer(user->server);
bool has_chants = (srcserver->Socket->proto_version >= 1204);

// If we got a channel TS compare it with ours. If it's different, drop the command.
// Also drop the command if we are using 1204, but there aren't enough parameters.
if (has_chants)
{
if (params.size() < 5)
return CMD_FAILURE;

// Only proceed if the TSes are equal. If their TS is newer the channel
// got recreated on their side, if it's older that means things are messed up,
// because they haven't sent us an FJOIN earlier which could lower the chan TS.
if (ConvToInt(params[1]) != c->age)
return CMD_FAILURE;
}

// Now do things as usual but if required, apply an offset to the index when accessing params
unsigned int indexoffset = (has_chants ? 1 : 0);
time_t topicts = ConvToInt(params[1+indexoffset]);

// See if the topic they sent is newer than ours (or we don't have a topic at all)
if ((topicts >= c->topicset) || (c->topic.empty()))
{
if ((ts >= c->topicset) || (c->topic.empty()))
if (c->topic != params[3+indexoffset])
{
if (c->topic != params[3])
{
// Update topic only when it differs from current topic
c->topic.assign(params[3], 0, ServerInstance->Config->Limits.MaxTopic);
c->WriteChannel(user, "TOPIC %s :%s", c->name.c_str(), c->topic.c_str());
}

// Always update setter and settime.
c->setby.assign(params[2], 0, 127);
c->topicset = ts;
// Update topic only when it differs from current topic
c->topic.assign(params[3+indexoffset], 0, ServerInstance->Config->Limits.MaxTopic);
c->WriteChannel(user, "TOPIC %s :%s", c->name.c_str(), c->topic.c_str());
}

// Always update setter and settime.
c->setby.assign(params[2+indexoffset], 0, 127);
c->topicset = topicts;
}
else
{
// We got a newer topic than this one, keep ours and drop the command
return CMD_FAILURE;
}

// They haven't sent us a channel TS, add ours before passing the command on
if (!has_chants)
{
parameterlist& p = const_cast<parameterlist&>(params);
p.insert(p.begin()+1, ConvToStr(c->age));
}

return CMD_SUCCESS;
}

2 changes: 1 addition & 1 deletion src/modules/m_spanningtree/main.h
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@
* Failure to document your protocol changes will result in a painfully
* painful death by pain. You have been warned.
*/
const long ProtocolVersion = 1203;
const long ProtocolVersion = 1204;
const long MinCompatProtocol = 1201;

/** Forward declarations
Expand Down
45 changes: 42 additions & 3 deletions src/modules/m_spanningtree/metadata.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -27,27 +27,66 @@

CmdResult CommandMetadata::Handle(const std::vector<std::string>& params, User *srcuser)
{
std::string value = params.size() < 3 ? "" : params[2];
ExtensionItem* item = ServerInstance->Extensions.GetItem(params[1]);
if (params[0] == "*")
{
std::string value = params.size() < 3 ? "" : params[2];
FOREACH_MOD(I_OnDecodeMetaData,OnDecodeMetaData(NULL,params[1],value));
}
else if (*(params[0].c_str()) == '#')
{
Channel* c = ServerInstance->FindChan(params[0]);
if (c)
{
/*
* Since protocol version 1204 we have the channel TS in METADATA concerning channels and
* we only accept them if the timestamps match. Previous versions don't send a timestamp
* so we need to fall back to the old behaviour and accept what they sent.
*
* Pre-1204: :sid METADATA #chan metaname :value
* 1204 and later: :sid METADATA #chan chants metaname :value
*
*/

// Determine the protocol version of the sender
SpanningTreeUtilities* Utils = ((ModuleSpanningTree*) (Module*) creator)->Utils;
TreeServer* srcserver = Utils->FindServer(srcuser->server);
if (!srcserver)
{
// Illegal prefix in METADATA
return CMD_FAILURE;
}

bool has_ts = (srcserver->Socket->proto_version >= 1204);

// If we got a channel TS compare it with ours. If it's different, drop the command.
// Also drop the command if we are using 1204, but there aren't enough parameters.
if ((has_ts) && ((ConvToInt(params[1]) != c->age) || params.size() < 3))
return CMD_FAILURE;

// Now do things as usual but if required, apply an offset to the index when accessing params
unsigned int indexoffset = has_ts ? 1 : 0;

std::string value = params.size() < (3+indexoffset) ? "" : params[2+indexoffset];
ExtensionItem* item = ServerInstance->Extensions.GetItem(params[1+indexoffset]);
if (item)
item->unserialize(FORMAT_NETWORK, c, value);
FOREACH_MOD(I_OnDecodeMetaData,OnDecodeMetaData(c,params[1],value));
FOREACH_MOD(I_OnDecodeMetaData,OnDecodeMetaData(c,params[1+indexoffset],value));

// Finally, if they haven't sent us a channel TS add ours before passing the command on
if (!has_ts)
{
parameterlist& p = const_cast<parameterlist&>(params);
p.insert(p.begin()+1, ConvToStr(c->age));
}
}
}
else if (*(params[0].c_str()) != '#')
{
User* u = ServerInstance->FindNick(params[0]);
if (u)
{
std::string value = params.size() < 3 ? "" : params[2];
ExtensionItem* item = ServerInstance->Extensions.GetItem(params[1]);
if (item)
item->unserialize(FORMAT_NETWORK, u, value);
FOREACH_MOD(I_OnDecodeMetaData,OnDecodeMetaData(u,params[1],value));
Expand Down
2 changes: 1 addition & 1 deletion src/modules/m_spanningtree/netburst.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -156,7 +156,7 @@ void TreeSocket::SendFJoins(Channel* c)

if (!c->topic.empty())
{
snprintf(list,MAXBUF,":%s FTOPIC %s %lu %s :%s", ServerInstance->Config->GetSID().c_str(), c->name.c_str(), (unsigned long)c->topicset, c->setby.c_str(), c->topic.c_str());
snprintf(list,MAXBUF,":%s FTOPIC %s %lu %lu %s :%s", ServerInstance->Config->GetSID().c_str(), c->name.c_str(), (unsigned long) c->age, (unsigned long) c->topicset, c->setby.c_str(), c->topic.c_str());
WriteLine(list);
}

Expand Down
6 changes: 5 additions & 1 deletion src/modules/m_spanningtree/protocolinterface.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@ void SpanningTreeSyncTarget::SendMetaData(Extensible* target, const std::string
if (u)
ts.WriteLine(std::string(":")+ServerInstance->Config->GetSID()+" METADATA "+u->uuid+" "+extname+" :"+extdata);
else if (c)
ts.WriteLine(std::string(":")+ServerInstance->Config->GetSID()+" METADATA "+c->name+" "+extname+" :"+extdata);
ts.WriteLine(std::string(":")+ServerInstance->Config->GetSID()+" METADATA "+c->name+" "+ConvToStr(c->age)+" "+extname+" :"+extdata);
else if (!target)
ts.WriteLine(std::string(":")+ServerInstance->Config->GetSID()+" METADATA * "+extname+" :"+extdata);
}
Expand Down Expand Up @@ -94,7 +94,10 @@ void SpanningTreeProtocolInterface::SendMetaData(Extensible* target, const std::
if (u)
params.push_back(u->uuid);
else if (c)
{
params.push_back(c->name);
params.push_back(ConvToStr(c->age));
}
else
params.push_back("*");

Expand All @@ -109,6 +112,7 @@ void SpanningTreeProtocolInterface::SendTopic(Channel* channel, std::string &top
parameterlist params;

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_back(":" + topic);
Expand Down

0 comments on commit ba1672b

Please sign in to comment.