Add RESYNC command to allow automatic recovery from a detected desync

This commit is contained in:
Daniel De Graaf 2010-03-04 10:39:47 -06:00
parent 48ed1fb39b
commit 4351678065
7 changed files with 48 additions and 28 deletions

View File

@ -143,6 +143,11 @@ void TreeSocket::WriteLine(std::string line)
line.erase(b,c-b);
}
}
else if (proto_version < 1203 && command == "RESYNC")
{
// drop the command. 2.0 and earlier cannot automatically recover from desync
return;
}
else if (proto_version < 1202 && command == "ENCAP")
{
// :src ENCAP target command [args...]

View File

@ -56,6 +56,7 @@ CmdResult CommandFJoin::Handle(const std::vector<std::string>& params, User *src
irc::tokenstream users((params.size() > 3) ? params[params.size() - 1] : ""); /* users from the user list */
bool apply_other_sides_modes = true; /* True if we are accepting the other side's modes */
Channel* chan = ServerInstance->FindChan(channel); /* The channel we're sending joins to */
bool incremental = (params[2] == "*");
bool created = !chan; /* True if the channel doesnt exist here yet */
std::string item; /* One item in the list of nicks */
@ -71,7 +72,13 @@ CmdResult CommandFJoin::Handle(const std::vector<std::string>& params, User *src
if (created)
{
chan = new Channel(channel, TS);
ServerInstance->SNO->WriteToSnoMask('d', "Creation FJOIN recieved for %s, timestamp: %lu", chan->name.c_str(), (unsigned long)TS);
if (incremental)
{
ServerInstance->SNO->WriteToSnoMask('d', "Incremental creation FJOIN recieved for %s, timestamp: %lu", chan->name.c_str(), (unsigned long)TS);
parameterlist resync;
resync.push_back(channel);
Utils->DoOneToOne(ServerInstance->Config->GetSID().c_str(), "RESYNC", resync, srcuser->uuid);
}
}
else
{
@ -97,12 +104,19 @@ CmdResult CommandFJoin::Handle(const std::vector<std::string>& params, User *src
chan->age = TS;
param_list.push_back(channel);
this->RemoveStatus(ServerInstance->FakeClient, param_list);
if (incremental)
{
ServerInstance->SNO->WriteToSnoMask('d', "Incremental merge FJOIN recieved for %s, timestamp: %lu", chan->name.c_str(), (unsigned long)TS);
parameterlist resync;
resync.push_back(channel);
Utils->DoOneToOne(ServerInstance->Config->GetSID().c_str(), "RESYNC", resync, srcuser->uuid);
}
}
// The silent case here is ourTS == TS, we don't need to remove modes here, just to merge them later on.
}
/* First up, apply their modes if they won the TS war */
if (apply_other_sides_modes)
if (apply_other_sides_modes && !incremental)
{
unsigned int idx = 2;
std::vector<std::string> modelist;

View File

@ -611,9 +611,7 @@ void ModuleSpanningTree::OnUserJoin(Membership* memb, bool sync, bool created, C
// new joining permissions for the user.
params.push_back(memb->chan->name);
params.push_back(ConvToStr(memb->chan->age));
irc::modestacker ms;
memb->chan->ChanModes(ms, MODELIST_SHORT);
params.push_back(ms.popModeLine(FORMAT_NETWORK, 400, INT_MAX));
params.push_back("*");
params.push_back(memb->modes+","+std::string(memb->user->uuid));
Utils->DoOneToMany(ServerInstance->Config->GetSID(),"FJOIN",params);
}

View File

@ -26,7 +26,7 @@
* Failure to document your protocol changes will result in a painfully
* painful death by pain. You have been warned.
*/
const long ProtocolVersion = 1202;
const long ProtocolVersion = 1203;
const long MinCompatProtocol = 1201;
/** Forward declarations

View File

@ -81,7 +81,7 @@ void TreeSocket::SendServers(TreeServer* Current, TreeServer* s, int hops)
* If the length of a single line is more than 480-NICKMAX
* in length, it is split over multiple lines.
*/
void TreeSocket::SendFJoins(TreeServer* Current, Channel* c)
void TreeSocket::SendFJoins(Channel* c)
{
char list[MAXBUF];
@ -138,6 +138,22 @@ void TreeSocket::SendFJoins(TreeServer* Current, Channel* c)
snprintf(list, MAXBUF, ":%s FMODE %s %ld ", ServerInstance->Config->GetSID().c_str(), c->name.c_str(), (unsigned long)c->age);
while (!fmodes.empty())
WriteLine(list + fmodes.popModeLine(FORMAT_NETWORK));
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());
WriteLine(list);
}
for(Extensible::ExtensibleStore::const_iterator i = c->GetExtList().begin(); i != c->GetExtList().end(); i++)
{
ExtensionItem* item = i->first;
std::string value = item->serialize(FORMAT_NETWORK, c, i->second);
if (!value.empty())
sync.SendMetaData(c, item->name, value);
}
FOREACH_MOD(I_OnSyncChannel,OnSyncChannel(c, &sync));
}
/** Send G, Q, Z and E lines */
@ -183,28 +199,9 @@ void TreeSocket::SendXLines(TreeServer* Current)
/** Send channel modes and topics */
void TreeSocket::SendChannelModes(TreeServer* Current)
{
char data[MAXBUF];
std::deque<std::string> list;
std::string n = ServerInstance->Config->GetSID();
const char* sn = n.c_str();
for (chan_hash::iterator c = ServerInstance->chanlist->begin(); c != ServerInstance->chanlist->end(); c++)
{
SendFJoins(Current, c->second);
if (!c->second->topic.empty())
{
snprintf(data,MAXBUF,":%s FTOPIC %s %lu %s :%s", sn, c->second->name.c_str(), (unsigned long)c->second->topicset, c->second->setby.c_str(), c->second->topic.c_str());
this->WriteLine(data);
}
for(Extensible::ExtensibleStore::const_iterator i = c->second->GetExtList().begin(); i != c->second->GetExtList().end(); i++)
{
ExtensionItem* item = i->first;
std::string value = item->serialize(FORMAT_NETWORK, c->second, i->second);
if (!value.empty())
sync.SendMetaData(c->second, item->name, value);
}
FOREACH_MOD(I_OnSyncChannel,OnSyncChannel(c->second,&sync));
SendFJoins(c->second);
}
}

View File

@ -218,7 +218,7 @@ class TreeSocket : public BufferedSocket
* If the length of a single line is more than 480-NICKMAX
* in length, it is split over multiple lines.
*/
void SendFJoins(TreeServer* Current, Channel* c);
void SendFJoins(Channel* c);
/** Send G, Q, Z and E lines */
void SendXLines(TreeServer* Current);

View File

@ -289,6 +289,12 @@ void TreeSocket::ProcessConnectedLine(std::string& prefix, std::string& command,
{
this->Away(prefix,params);
}
else if (command == "RESYNC" && !params.empty())
{
Channel* chan = ServerInstance->FindChan(params[0]);
if (chan)
SendFJoins(chan);
}
else if (command == "PING")
{
this->LocalPing(prefix,params);