- if ((Current) && (Current != Utils->TreeRoot))
- {
- Event rmode((char*)Current->GetName().c_str(), (Module*)Utils->Creator, "lost_server");
- rmode.Send(Instance);
-
- std::deque<std::string> params;
- params.push_back(Current->GetName());
- params.push_back(":"+reason);
- Utils->DoOneToAllButSender(Current->GetParent()->GetName(),"SQUIT",params,Current->GetName());
- if (Current->GetParent() == Utils->TreeRoot)
- {
- this->Instance->SNO->WriteToSnoMask('l',"Server \002"+Current->GetName()+"\002 split: "+reason);
- }
- else
- {
- this->Instance->SNO->WriteToSnoMask('l',"Server \002"+Current->GetName()+"\002 split from server \002"+Current->GetParent()->GetName()+"\002 with reason: "+reason);
- }
- num_lost_servers = 0;
- num_lost_users = 0;
- std::string from = Current->GetParent()->GetName()+" "+Current->GetName();
- SquitServer(from, Current);
- Current->Tidy();
- Current->GetParent()->DelChild(Current);
- DELETE(Current);
- this->Instance->SNO->WriteToSnoMask('l',"Netsplit complete, lost \002%d\002 users on \002%d\002 servers.", num_lost_users, num_lost_servers);
- }
- else
- Instance->Log(DEFAULT,"Squit from unknown server");
-}
-
-/** FMODE command - server mode with timestamp checks */
-bool TreeSocket::ForceMode(const std::string &source, std::deque<std::string> ¶ms)
-{
- /* Chances are this is a 1.0 FMODE without TS */
- if (params.size() < 3)
- {
- /* No modes were in the command, probably a channel with no modes set on it */
- return true;
- }
-
- bool smode = false;
- std::string sourceserv;
- /* Are we dealing with an FMODE from a user, or from a server? */
- userrec* who = this->Instance->FindNick(source);
- if (who)
- {
- /* FMODE from a user, set sourceserv to the users server name */
- sourceserv = who->server;
- }
- else
- {
- /* FMODE from a server, use a fake user to receive mode feedback */
- who = this->Instance->FakeClient;
- smode = true; /* Setting this flag tells us we should free the userrec later */
- sourceserv = source; /* Set sourceserv to the actual source string */
- }
- const char* modelist[64];
- time_t TS = 0;
- int n = 0;
- memset(&modelist,0,sizeof(modelist));
- for (unsigned int q = 0; (q < params.size()) && (q < 64); q++)
- {
- if (q == 1)
- {
- /* The timestamp is in this position.
- * We don't want to pass that up to the
- * server->client protocol!
- */
- TS = atoi(params[q].c_str());
- }
- else
- {
- /* Everything else is fine to append to the modelist */
- modelist[n++] = params[q].c_str();
- }
-
- }
- /* Extract the TS value of the object, either userrec or chanrec */
- userrec* dst = this->Instance->FindNick(params[0]);
- chanrec* chan = NULL;
- time_t ourTS = 0;
- if (dst)
- {
- ourTS = dst->age;
- }
- else
- {
- chan = this->Instance->FindChan(params[0]);
- if (chan)
- {
- ourTS = chan->age;
- }
- else
- /* Oops, channel doesnt exist! */
- return true;
- }
-
- if (!TS)
- {
- Instance->Log(DEFAULT,"*** BUG? *** TS of 0 sent to FMODE. Are some services authors smoking craq, or is it 1970 again?. Dropped.");
- Instance->SNO->WriteToSnoMask('d', "WARNING: The server %s is sending FMODE with a TS of zero. Total craq. Mode was dropped.", sourceserv.c_str());
- return true;
- }
-
- /* TS is equal or less: Merge the mode changes into ours and pass on.
- */
- if (TS <= ourTS)
- {
- if ((TS < ourTS) && (!dst))
- Instance->Log(DEFAULT,"*** BUG *** Channel TS sent in FMODE to %s is %lu which is not equal to %lu!", params[0].c_str(), TS, ourTS);
-
- if (smode)
- {
- this->Instance->SendMode(modelist, n, who);
- }
- else
- {
- this->Instance->CallCommandHandler("MODE", modelist, n, who);
- }
- /* HOT POTATO! PASS IT ON! */
- Utils->DoOneToAllButSender(source,"FMODE",params,sourceserv);
- }
- /* If the TS is greater than ours, we drop the mode and dont pass it anywhere.
- */
- return true;
-}
-
-/** FTOPIC command */
-bool TreeSocket::ForceTopic(const std::string &source, std::deque<std::string> ¶ms)
-{
- if (params.size() != 4)
- return true;
- time_t ts = atoi(params[1].c_str());
- std::string nsource = source;
- chanrec* c = this->Instance->FindChan(params[0]);
- if (c)
- {
- if ((ts >= c->topicset) || (!*c->topic))
- {
- std::string oldtopic = c->topic;
- strlcpy(c->topic,params[3].c_str(),MAXTOPIC);
- strlcpy(c->setby,params[2].c_str(),127);
- c->topicset = ts;
- /* if the topic text is the same as the current topic,
- * dont bother to send the TOPIC command out, just silently
- * update the set time and set nick.
- */
- if (oldtopic != params[3])
- {
- userrec* user = this->Instance->FindNick(source);
- if (!user)
- {
- c->WriteChannelWithServ(Instance->Config->ServerName, "TOPIC %s :%s", c->name, c->topic);
- }
- else
- {
- c->WriteChannel(user, "TOPIC %s :%s", c->name, c->topic);
- nsource = user->server;
- }
- /* all done, send it on its way */
- params[3] = ":" + params[3];
- Utils->DoOneToAllButSender(source,"FTOPIC",params,nsource);
- }
- }
-
- }
- return true;
-}
-
-/** FJOIN, similar to TS6 SJOIN, but not quite. */
-bool TreeSocket::ForceJoin(const std::string &source, std::deque<std::string> ¶ms)
-{
- /* 1.1 FJOIN works as follows:
- *
- * Each FJOIN is sent along with a timestamp, and the side with the lowest
- * timestamp 'wins'. From this point on we will refer to this side as the
- * winner. The side with the higher timestamp loses, from this point on we
- * will call this side the loser or losing side. This should be familiar to
- * anyone who's dealt with dreamforge or TS6 before.
- *
- * When two sides of a split heal and this occurs, the following things
- * will happen:
- *
- * If the timestamps are exactly equal, both sides merge their privilages
- * and users, as in InspIRCd 1.0 and ircd2.8. The channels have not been
- * re-created during a split, this is safe to do.
- *
- * If the timestamps are NOT equal, the losing side removes all of its
- * modes from the channel, before introducing new users into the channel
- * which are listed in the FJOIN command's parameters. The losing side then
- * LOWERS its timestamp value of the channel to match that of the winning
- * side, and the modes of the users of the winning side are merged in with
- * the losing side.
- *
- * The winning side on the other hand will ignore all user modes from the
- * losing side, so only its own modes get applied. Life is simple for those
- * who succeed at internets. :-)
- *
- * NOTE: Unlike TS6 and dreamforge and other protocols which have SJOIN,
- * FJOIN does not contain the simple-modes such as +iklmnsp. Why not,
- * you ask? Well, quite simply because we don't need to. They'll be sent
- * after the FJOIN by FMODE, and FMODE is timestamped, so in the event
- * the losing side sends any modes for the channel which shouldnt win,
- * they wont as their timestamp will be too high :-)
- */
-
- if (params.size() < 3)
- return true;
-
- irc::modestacker modestack(true); /* Modes to apply from the users in the user list */
- userrec* who = NULL; /* User we are currently checking */
- std::string channel = params[0]; /* Channel name, as a string */
- time_t TS = atoi(params[1].c_str()); /* Timestamp given to us for remote side */
- irc::tokenstream users(params[2]); /* Users from the user list */
- bool apply_other_sides_modes = true; /* True if we are accepting the other side's modes */
- chanrec* chan = this->Instance->FindChan(channel); /* The channel we're sending joins to */
- time_t ourTS = chan ? chan->age : Instance->Time(true)+600; /* The TS of our side of the link */
- bool created = !chan; /* True if the channel doesnt exist here yet */
- std::string item; /* One item in the list of nicks */
-
- params[2] = ":" + params[2];
- Utils->DoOneToAllButSender(source,"FJOIN",params,source);