summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--src/modules/m_spanningtree/capab.cpp262
-rw-r--r--src/modules/m_spanningtree/fjoin.cpp186
-rw-r--r--src/modules/m_spanningtree/fmode.cpp131
-rw-r--r--src/modules/m_spanningtree/ftopic.cpp75
-rw-r--r--src/modules/m_spanningtree/nickcollide.cpp137
-rw-r--r--src/modules/m_spanningtree/treesocket1.cpp759
-rw-r--r--src/modules/m_spanningtree/uid.cpp167
7 files changed, 958 insertions, 759 deletions
diff --git a/src/modules/m_spanningtree/capab.cpp b/src/modules/m_spanningtree/capab.cpp
new file mode 100644
index 000000000..744e827bf
--- /dev/null
+++ b/src/modules/m_spanningtree/capab.cpp
@@ -0,0 +1,262 @@
+/* +------------------------------------+
+ * | Inspire Internet Relay Chat Daemon |
+ * +------------------------------------+
+ *
+ * InspIRCd: (C) 2002-2008 InspIRCd Development Team
+ * See: http://www.inspircd.org/wiki/index.php/Credits
+ *
+ * This program is free but copyrighted software; see
+ * the file COPYING for details.
+ *
+ * ---------------------------------------------------
+ */
+
+#include "inspircd.h"
+#include "commands/cmd_whois.h"
+#include "commands/cmd_stats.h"
+#include "socket.h"
+#include "wildcard.h"
+#include "xline.h"
+#include "transport.h"
+#include "m_hash.h"
+#include "socketengine.h"
+
+#include "m_spanningtree/main.h"
+#include "m_spanningtree/utils.h"
+#include "m_spanningtree/treeserver.h"
+#include "m_spanningtree/link.h"
+#include "m_spanningtree/treesocket.h"
+#include "m_spanningtree/resolvers.h"
+#include "m_spanningtree/handshaketimer.h"
+
+/* $ModDep: m_spanningtree/timesynctimer.h m_spanningtree/resolvers.h m_spanningtree/main.h m_spanningtree/utils.h m_spanningtree/treeserver.h m_spanningtree/link.h m_spanningtree/treesocket.h m_hash.h */
+
+std::string TreeSocket::MyCapabilities()
+{
+ std::vector<std::string> modlist = this->Instance->Modules->GetAllModuleNames(VF_COMMON);
+ std::string capabilities;
+ sort(modlist.begin(),modlist.end());
+ for (unsigned int i = 0; i < modlist.size(); i++)
+ {
+ if (i)
+ capabilities = capabilities + ",";
+ capabilities = capabilities + modlist[i];
+ }
+ return capabilities;
+}
+
+void TreeSocket::SendCapabilities()
+{
+ if (sentcapab)
+ return;
+
+ sentcapab = true;
+ irc::commasepstream modulelist(MyCapabilities());
+ this->WriteLine("CAPAB START");
+
+ /* Send module names, split at 509 length */
+ std::string item;
+ std::string line = "CAPAB MODULES ";
+ while (modulelist.GetToken(item))
+ {
+ if (line.length() + item.length() + 1 > 509)
+ {
+ this->WriteLine(line);
+ line = "CAPAB MODULES ";
+ }
+
+ if (line != "CAPAB MODULES ")
+ line.append(",");
+
+ line.append(item);
+ }
+ if (line != "CAPAB MODULES ")
+ this->WriteLine(line);
+
+ int ip6 = 0;
+ int ip6support = 0;
+#ifdef IPV6
+ ip6 = 1;
+#endif
+#ifdef SUPPORT_IP6LINKS
+ ip6support = 1;
+#endif
+ std::string extra;
+ /* Do we have sha256 available? If so, we send a challenge */
+ if (Utils->ChallengeResponse && (Instance->Modules->Find("m_sha256.so")))
+ {
+ this->SetOurChallenge(RandString(20));
+ extra = " CHALLENGE=" + this->GetOurChallenge();
+ }
+
+ this->WriteLine("CAPAB CAPABILITIES :NICKMAX="+ConvToStr(NICKMAX)+" HALFOP="+ConvToStr(this->Instance->Config->AllowHalfop)+" CHANMAX="+ConvToStr(CHANMAX)+" MAXMODES="+ConvToStr(MAXMODES)+" IDENTMAX="+ConvToStr(IDENTMAX)+" MAXQUIT="+ConvToStr(MAXQUIT)+" MAXTOPIC="+ConvToStr(MAXTOPIC)+" MAXKICK="+ConvToStr(MAXKICK)+" MAXGECOS="+ConvToStr(MAXGECOS)+" MAXAWAY="+ConvToStr(MAXAWAY)+" IP6NATIVE="+ConvToStr(ip6)+" IP6SUPPORT="+ConvToStr(ip6support)+" PROTOCOL="+ConvToStr(ProtocolVersion)+extra+" PREFIX="+Instance->Modes->BuildPrefixes()+" CHANMODES="+Instance->Modes->ChanModes()+" SVSPART=1");
+
+ this->WriteLine("CAPAB END");
+}
+
+/* Check a comma seperated list for an item */
+bool TreeSocket::HasItem(const std::string &list, const std::string &item)
+{
+ irc::commasepstream seplist(list);
+ std::string item2;
+
+ while (seplist.GetToken(item2))
+ {
+ if (item2 == item)
+ return true;
+ }
+ return false;
+}
+
+/* Isolate and return the elements that are different between two comma seperated lists */
+std::string TreeSocket::ListDifference(const std::string &one, const std::string &two)
+{
+ irc::commasepstream list_one(one);
+ std::string item;
+ std::string result;
+ while (list_one.GetToken(item))
+ {
+ if (!HasItem(two, item))
+ {
+ result.append(" ");
+ result.append(item);
+ }
+ }
+ return result;
+}
+
+bool TreeSocket::Capab(const std::deque<std::string> &params)
+{
+ if (params.size() < 1)
+ {
+ this->SendError("Invalid number of parameters for CAPAB - Mismatched version");
+ return false;
+ }
+ if (params[0] == "START")
+ {
+ this->ModuleList.clear();
+ this->CapKeys.clear();
+ }
+ else if (params[0] == "END")
+ {
+ std::string reason;
+ int ip6support = 0;
+#ifdef SUPPORT_IP6LINKS
+ ip6support = 1;
+#endif
+ /* Compare ModuleList and check CapKeys...
+ * Maybe this could be tidier? -- Brain
+ */
+ if ((this->ModuleList != this->MyCapabilities()) && (this->ModuleList.length()))
+ {
+ std::string diff = ListDifference(this->ModuleList, this->MyCapabilities());
+ if (!diff.length())
+ {
+ diff = "your server:" + ListDifference(this->MyCapabilities(), this->ModuleList);
+ }
+ else
+ {
+ diff = "this server:" + diff;
+ }
+ if (diff.length() == 12)
+ reason = "Module list in CAPAB is not alphabetically ordered, cannot compare lists.";
+ else
+ reason = "Modules loaded on these servers are not correctly matched, these modules are not loaded on " + diff;
+ }
+
+ cap_validation valid_capab[] = {
+ {"Maximum nickname lengths differ or remote nickname length not specified", "NICKMAX", NICKMAX},
+ {"Maximum ident lengths differ or remote ident length not specified", "IDENTMAX", IDENTMAX},
+ {"Maximum channel lengths differ or remote channel length not specified", "CHANMAX", CHANMAX},
+ {"Maximum modes per line differ or remote modes per line not specified", "MAXMODES", MAXMODES},
+ {"Maximum quit lengths differ or remote quit length not specified", "MAXQUIT", MAXQUIT},
+ {"Maximum topic lengths differ or remote topic length not specified", "MAXTOPIC", MAXTOPIC},
+ {"Maximum kick lengths differ or remote kick length not specified", "MAXKICK", MAXKICK},
+ {"Maximum GECOS (fullname) lengths differ or remote GECOS length not specified", "MAXGECOS", MAXGECOS},
+ {"Maximum awaymessage lengths differ or remote awaymessage length not specified", "MAXAWAY", MAXAWAY},
+ {"", "", 0}
+ };
+
+ if (((this->CapKeys.find("IP6SUPPORT") == this->CapKeys.end()) && (ip6support)) || ((this->CapKeys.find("IP6SUPPORT") != this->CapKeys.end()) && (this->CapKeys.find("IP6SUPPORT")->second != ConvToStr(ip6support))))
+ reason = "We don't both support linking to IPV6 servers";
+ if (((this->CapKeys.find("IP6NATIVE") != this->CapKeys.end()) && (this->CapKeys.find("IP6NATIVE")->second == "1")) && (!ip6support))
+ reason = "The remote server is IPV6 native, and we don't support linking to IPV6 servers";
+ if (((this->CapKeys.find("PROTOCOL") == this->CapKeys.end()) || ((this->CapKeys.find("PROTOCOL") != this->CapKeys.end()) && (this->CapKeys.find("PROTOCOL")->second != ConvToStr(ProtocolVersion)))))
+ {
+ if (this->CapKeys.find("PROTOCOL") != this->CapKeys.end())
+ reason = "Mismatched protocol versions "+this->CapKeys.find("PROTOCOL")->second+" and "+ConvToStr(ProtocolVersion);
+ else
+ reason = "Protocol version not specified";
+ }
+
+ if(this->CapKeys.find("PREFIX") != this->CapKeys.end() && this->CapKeys.find("PREFIX")->second != this->Instance->Modes->BuildPrefixes())
+ reason = "One or more of the prefixes on the remote server are invalid on this server.";
+
+ if (((this->CapKeys.find("HALFOP") == this->CapKeys.end()) && (Instance->Config->AllowHalfop)) || ((this->CapKeys.find("HALFOP") != this->CapKeys.end()) && (this->CapKeys.find("HALFOP")->second != ConvToStr(Instance->Config->AllowHalfop))))
+ reason = "We don't both have halfop support enabled/disabled identically";
+
+ for (int x = 0; valid_capab[x].size; ++x)
+ {
+ if (((this->CapKeys.find(valid_capab[x].key) == this->CapKeys.end()) || ((this->CapKeys.find(valid_capab[x].key) != this->CapKeys.end()) &&
+ (this->CapKeys.find(valid_capab[x].key)->second != ConvToStr(valid_capab[x].size)))))
+ reason = valid_capab[x].reason;
+ }
+
+ /* Challenge response, store their challenge for our password */
+ std::map<std::string,std::string>::iterator n = this->CapKeys.find("CHALLENGE");
+ if (Utils->ChallengeResponse && (n != this->CapKeys.end()) && (Instance->Modules->Find("m_sha256.so")))
+ {
+ /* Challenge-response is on now */
+ this->SetTheirChallenge(n->second);
+ if (!this->GetTheirChallenge().empty() && (this->LinkState == CONNECTING))
+ {
+ this->WriteLine(std::string("SERVER ")+this->Instance->Config->ServerName+" "+this->MakePass(OutboundPass, this->GetTheirChallenge())+" 0 "+
+ Instance->Config->GetSID()+" :"+this->Instance->Config->ServerDesc);
+ }
+ }
+ else
+ {
+ /* They didnt specify a challenge or we don't have m_sha256.so, we use plaintext */
+ if (this->LinkState == CONNECTING)
+ this->WriteLine(std::string("SERVER ")+this->Instance->Config->ServerName+" "+OutboundPass+" 0 "+Instance->Config->GetSID()+" :"+this->Instance->Config->ServerDesc);
+ }
+
+ if (reason.length())
+ {
+ this->SendError("CAPAB negotiation failed: "+reason);
+ return false;
+ }
+ }
+ else if ((params[0] == "MODULES") && (params.size() == 2))
+ {
+ if (!this->ModuleList.length())
+ {
+ this->ModuleList.append(params[1]);
+ }
+ else
+ {
+ this->ModuleList.append(",");
+ this->ModuleList.append(params[1]);
+ }
+ }
+
+ else if ((params[0] == "CAPABILITIES") && (params.size() == 2))
+ {
+ irc::tokenstream capabs(params[1]);
+ std::string item;
+ bool more = true;
+ while ((more = capabs.GetToken(item)))
+ {
+ /* Process each key/value pair */
+ std::string::size_type equals = item.rfind('=');
+ if (equals != std::string::npos)
+ {
+ std::string var = item.substr(0, equals);
+ std::string value = item.substr(equals+1, item.length());
+ CapKeys[var] = value;
+ }
+ }
+ }
+ return true;
+}
+
diff --git a/src/modules/m_spanningtree/fjoin.cpp b/src/modules/m_spanningtree/fjoin.cpp
new file mode 100644
index 000000000..eacd9ee04
--- /dev/null
+++ b/src/modules/m_spanningtree/fjoin.cpp
@@ -0,0 +1,186 @@
+/* +------------------------------------+
+ * | Inspire Internet Relay Chat Daemon |
+ * +------------------------------------+
+ *
+ * InspIRCd: (C) 2002-2008 InspIRCd Development Team
+ * See: http://www.inspircd.org/wiki/index.php/Credits
+ *
+ * This program is free but copyrighted software; see
+ * the file COPYING for details.
+ *
+ * ---------------------------------------------------
+ */
+
+#include "inspircd.h"
+#include "commands/cmd_whois.h"
+#include "commands/cmd_stats.h"
+#include "socket.h"
+#include "wildcard.h"
+#include "xline.h"
+#include "transport.h"
+#include "m_hash.h"
+#include "socketengine.h"
+
+#include "m_spanningtree/main.h"
+#include "m_spanningtree/utils.h"
+#include "m_spanningtree/treeserver.h"
+#include "m_spanningtree/link.h"
+#include "m_spanningtree/treesocket.h"
+#include "m_spanningtree/resolvers.h"
+#include "m_spanningtree/handshaketimer.h"
+
+/* $ModDep: m_spanningtree/timesynctimer.h m_spanningtree/resolvers.h m_spanningtree/main.h m_spanningtree/utils.h m_spanningtree/treeserver.h m_spanningtree/link.h m_spanningtree/treesocket.h m_hash.h */
+
+/** FJOIN, similar to TS6 SJOIN, but not quite. */
+bool TreeSocket::ForceJoin(const std::string &source, std::deque<std::string> &params)
+{
+ /* 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() < 2)
+ return true;
+
+ irc::modestacker modestack(true); /* Modes to apply from the users in the user list */
+ User* 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.size() > 2) ? params[2] : ""); /* users from the user list */
+ bool apply_other_sides_modes = true; /* True if we are accepting the other side's modes */
+ Channel* 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 */
+
+ if (params.size() > 2)
+ params[2] = ":" + params[2];
+
+ Utils->DoOneToAllButSender(source,"FJOIN",params,source);
+
+ if (!TS)
+ {
+ Instance->Log(DEFAULT,"*** BUG? *** TS of 0 sent to FJOIN. Are some services authors smoking craq, or is it 1970 again?. Dropped.");
+ Instance->SNO->WriteToSnoMask('d', "WARNING: The server %s is sending FJOIN with a TS of zero. Total craq. Command was dropped.", source.c_str());
+ return true;
+ }
+
+ if (created)
+ chan = new Channel(Instance, channel, ourTS);
+
+ /* If our TS is less than theirs, we dont accept their modes */
+ if (ourTS < TS)
+ apply_other_sides_modes = false;
+
+ /* Our TS greater than theirs, clear all our modes from the channel, accept theirs. */
+ if (ourTS > TS)
+ {
+ std::deque<std::string> param_list;
+ if (Utils->AnnounceTSChange && chan)
+ chan->WriteChannelWithServ(Instance->Config->ServerName, "NOTICE %s :TS for %s changed from %lu to %lu", chan->name, chan->name, ourTS, TS);
+ ourTS = TS;
+ if (!created)
+ {
+ chan->age = TS;
+ param_list.push_back(channel);
+ this->RemoveStatus(Instance->Config->GetSID(), param_list);
+ }
+ }
+
+ /* Now, process every 'prefixes,nick' pair */
+ while (users.GetToken(item))
+ {
+ const char* usr = item.c_str();
+ if (usr && *usr)
+ {
+ const char* permissions = usr;
+ /* Iterate through all the prefix values, convert them from prefixes to mode letters */
+ std::string modes;
+ while ((*permissions) && (*permissions != ','))
+ {
+ ModeHandler* mh = Instance->Modes->FindPrefix(*permissions);
+ if (mh)
+ modes = modes + mh->GetModeChar();
+ else
+ {
+ this->SendError(std::string("Invalid prefix '")+(*permissions)+"' in FJOIN");
+ return false;
+ }
+ usr++;
+ permissions++;
+ }
+ /* Advance past the comma, to the nick */
+ usr++;
+
+ /* Check the user actually exists */
+ who = this->Instance->FindUUID(usr);
+ if (who)
+ {
+ /* Check that the user's 'direction' is correct */
+ TreeServer* route_back_again = Utils->BestRouteTo(who->server);
+ if ((!route_back_again) || (route_back_again->GetSocket() != this))
+ continue;
+
+ /* Add any permissions this user had to the mode stack */
+ for (std::string::iterator x = modes.begin(); x != modes.end(); ++x)
+ modestack.Push(*x, who->nick);
+
+ Channel::JoinUser(this->Instance, who, channel.c_str(), true, "", true, TS);
+ }
+ else
+ {
+ Instance->Log(SPARSE,"Warning! Invalid user %s in FJOIN to channel %s IGNORED", usr, channel.c_str());
+ continue;
+ }
+ }
+ }
+
+ /* Flush mode stacker if we lost the FJOIN or had equal TS */
+ if (apply_other_sides_modes)
+ {
+ std::deque<std::string> stackresult;
+ const char* mode_junk[MAXMODES+2];
+ mode_junk[0] = channel.c_str();
+
+ while (modestack.GetStackedLine(stackresult))
+ {
+ for (size_t j = 0; j < stackresult.size(); j++)
+ {
+ mode_junk[j+1] = stackresult[j].c_str();
+ }
+ Instance->SendMode(mode_junk, stackresult.size() + 1, Instance->FakeClient);
+ }
+ }
+
+ return true;
+}
+
diff --git a/src/modules/m_spanningtree/fmode.cpp b/src/modules/m_spanningtree/fmode.cpp
new file mode 100644
index 000000000..137cb256a
--- /dev/null
+++ b/src/modules/m_spanningtree/fmode.cpp
@@ -0,0 +1,131 @@
+/* +------------------------------------+
+ * | Inspire Internet Relay Chat Daemon |
+ * +------------------------------------+
+ *
+ * InspIRCd: (C) 2002-2008 InspIRCd Development Team
+ * See: http://www.inspircd.org/wiki/index.php/Credits
+ *
+ * This program is free but copyrighted software; see
+ * the file COPYING for details.
+ *
+ * ---------------------------------------------------
+ */
+
+#include "inspircd.h"
+#include "commands/cmd_whois.h"
+#include "commands/cmd_stats.h"
+#include "socket.h"
+#include "wildcard.h"
+#include "xline.h"
+#include "transport.h"
+#include "m_hash.h"
+#include "socketengine.h"
+
+#include "m_spanningtree/main.h"
+#include "m_spanningtree/utils.h"
+#include "m_spanningtree/treeserver.h"
+#include "m_spanningtree/link.h"
+#include "m_spanningtree/treesocket.h"
+#include "m_spanningtree/resolvers.h"
+#include "m_spanningtree/handshaketimer.h"
+
+/* $ModDep: m_spanningtree/timesynctimer.h m_spanningtree/resolvers.h m_spanningtree/main.h m_spanningtree/utils.h m_spanningtree/treeserver.h m_spanningtree/link.h m_spanningtree/treesocket.h m_hash.h */
+
+/** FMODE command - server mode with timestamp checks */
+bool TreeSocket::ForceMode(const std::string &source, std::deque<std::string> &params)
+{
+ /* 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? */
+ User* 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 User 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 User or Channel */
+ User* dst = this->Instance->FindNick(params[0]);
+ Channel* 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;
+}
+
+
diff --git a/src/modules/m_spanningtree/ftopic.cpp b/src/modules/m_spanningtree/ftopic.cpp
new file mode 100644
index 000000000..de91c216d
--- /dev/null
+++ b/src/modules/m_spanningtree/ftopic.cpp
@@ -0,0 +1,75 @@
+/* +------------------------------------+
+ * | Inspire Internet Relay Chat Daemon |
+ * +------------------------------------+
+ *
+ * InspIRCd: (C) 2002-2008 InspIRCd Development Team
+ * See: http://www.inspircd.org/wiki/index.php/Credits
+ *
+ * This program is free but copyrighted software; see
+ * the file COPYING for details.
+ *
+ * ---------------------------------------------------
+ */
+
+#include "inspircd.h"
+#include "commands/cmd_whois.h"
+#include "commands/cmd_stats.h"
+#include "socket.h"
+#include "wildcard.h"
+#include "xline.h"
+#include "transport.h"
+#include "m_hash.h"
+#include "socketengine.h"
+
+#include "m_spanningtree/main.h"
+#include "m_spanningtree/utils.h"
+#include "m_spanningtree/treeserver.h"
+#include "m_spanningtree/link.h"
+#include "m_spanningtree/treesocket.h"
+#include "m_spanningtree/resolvers.h"
+#include "m_spanningtree/handshaketimer.h"
+
+/* $ModDep: m_spanningtree/timesynctimer.h m_spanningtree/resolvers.h m_spanningtree/main.h m_spanningtree/utils.h m_spanningtree/treeserver.h m_spanningtree/link.h m_spanningtree/treesocket.h m_hash.h */
+
+/** FTOPIC command */
+bool TreeSocket::ForceTopic(const std::string &source, std::deque<std::string> &params)
+{
+ if (params.size() != 4)
+ return true;
+ time_t ts = atoi(params[1].c_str());
+ std::string nsource = source;
+ Channel* 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])
+ {
+ User* 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;
+}
+
diff --git a/src/modules/m_spanningtree/nickcollide.cpp b/src/modules/m_spanningtree/nickcollide.cpp
new file mode 100644
index 000000000..5d36bc46f
--- /dev/null
+++ b/src/modules/m_spanningtree/nickcollide.cpp
@@ -0,0 +1,137 @@
+/* +------------------------------------+
+ * | Inspire Internet Relay Chat Daemon |
+ * +------------------------------------+
+ *
+ * InspIRCd: (C) 2002-2008 InspIRCd Development Team
+ * See: http://www.inspircd.org/wiki/index.php/Credits
+ *
+ * This program is free but copyrighted software; see
+ * the file COPYING for details.
+ *
+ * ---------------------------------------------------
+ */
+
+#include "inspircd.h"
+#include "commands/cmd_whois.h"
+#include "commands/cmd_stats.h"
+#include "socket.h"
+#include "wildcard.h"
+#include "xline.h"
+#include "transport.h"
+#include "m_hash.h"
+#include "socketengine.h"
+
+#include "m_spanningtree/main.h"
+#include "m_spanningtree/utils.h"
+#include "m_spanningtree/treeserver.h"
+#include "m_spanningtree/link.h"
+#include "m_spanningtree/treesocket.h"
+#include "m_spanningtree/resolvers.h"
+#include "m_spanningtree/handshaketimer.h"
+
+/* $ModDep: m_spanningtree/timesynctimer.h m_spanningtree/resolvers.h m_spanningtree/main.h m_spanningtree/utils.h m_spanningtree/treeserver.h m_spanningtree/link.h m_spanningtree/treesocket.h m_hash.h */
+
+/*
+ * Yes, this function looks a little ugly.
+ * However, in some circumstances we may not have a User, so we need to do things this way.
+ * Returns 1 if colliding local client, 2 if colliding remote, 3 if colliding both.
+ * Sends SVSNICKs as appropriate and forces nickchanges too.
+ */
+int TreeSocket::DoCollision(User *u, time_t remotets, const char *remoteident, const char *remoteip, const char *remoteuid)
+{
+ /*
+ * Under old protocol rules, we would have had to kill both clients.
+ * Really, this sucks.
+ * These days, we have UID. And, so what we do is, force nick change client(s)
+ * involved according to timestamp rules.
+ *
+ * RULES:
+ * user@ip equal:
+ * Force nick change on OLDER timestamped client
+ * user@ip differ:
+ * Force nick change on NEWER timestamped client
+ * TS EQUAL:
+ * FNC both.
+ *
+ * This stops abusive use of collisions, simplifies problems with loops, and so on.
+ * -- w00t
+ */
+ bool bChangeLocal = true;
+ bool bChangeRemote = true;
+
+ /* for brevity, don't use the User */
+ time_t localts = u->age;
+ const char *localident = u->ident;
+ const char *localip = u->GetIPString();
+
+ /* mmk. let's do this again. */
+ if (remotets == localts)
+ {
+ /* equal. fuck them both! do nada, let the handler at the bottom figure this out. */
+ }
+ else
+ {
+ /* fuck. now it gets complex. */
+
+ /* first, let's see if ident@host matches. */
+ bool SamePerson = !strcmp(localident, remoteident)
+ && !strcmp(localip, remoteip);
+
+ /*
+ * if ident@ip is equal, and theirs is newer, or
+ * ident@ip differ, and ours is newer
+ */
+ if((SamePerson && remotets < localts) ||
+ (!SamePerson && remotets > localts))
+ {
+ /* remote needs to change */
+ bChangeLocal = false;
+ }
+ else
+ {
+ /* ours needs to change */
+ bChangeRemote = false;
+ }
+ }
+
+
+ if (bChangeLocal)
+ {
+ u->ForceNickChange(u->uuid);
+
+ if (!bChangeRemote)
+ return 1;
+ }
+ if (bChangeRemote)
+ {
+ /*
+ * Cheat a little here. Instead of a dedicated command to change UID,
+ * use SVSNICK and accept their client with it's UID (as we know the SVSNICK will
+ * not fail under any circumstances -- UIDs are netwide exclusive).
+ *
+ * This means that each side of a collide will generate one extra NICK back to where
+ * they have just linked (and where it got the SVSNICK from), however, it will
+ * be dropped harmlessly as it will come in as :928AAAB NICK 928AAAB, and we already
+ * have 928AAAB's nick set to that.
+ * -- w00t
+ */
+ User *remote = this->Instance->FindUUID(remoteuid);
+
+ if (remote)
+ {
+ /* buh.. nick change collide. force change their nick. */
+ remote->ForceNickChange(remote->uuid);
+ }
+ else
+ {
+ /* user has not been introduced yet, just inform their server */
+ this->WriteLine(std::string(":")+this->Instance->Config->GetSID()+" SVSNICK "+remoteuid+" " + remoteuid + " " + ConvToStr(remotets));
+ }
+
+ if (!bChangeLocal)
+ return 2;
+ }
+
+ return 3;
+}
+
diff --git a/src/modules/m_spanningtree/treesocket1.cpp b/src/modules/m_spanningtree/treesocket1.cpp
index 743b34c8d..2a50b50e5 100644
--- a/src/modules/m_spanningtree/treesocket1.cpp
+++ b/src/modules/m_spanningtree/treesocket1.cpp
@@ -273,20 +273,6 @@ void TreeSocket::SendServers(TreeServer* Current, TreeServer* s, int hops)
}
}
-std::string TreeSocket::MyCapabilities()
-{
- std::vector<std::string> modlist = this->Instance->Modules->GetAllModuleNames(VF_COMMON);
- std::string capabilities;
- sort(modlist.begin(),modlist.end());
- for (unsigned int i = 0; i < modlist.size(); i++)
- {
- if (i)
- capabilities = capabilities + ",";
- capabilities = capabilities + modlist[i];
- }
- return capabilities;
-}
-
std::string TreeSocket::RandString(unsigned int length)
{
char* randombuf = new char[length+1];
@@ -320,86 +306,6 @@ std::string TreeSocket::RandString(unsigned int length)
return out;
}
-void TreeSocket::SendCapabilities()
-{
- if (sentcapab)
- return;
-
- sentcapab = true;
- irc::commasepstream modulelist(MyCapabilities());
- this->WriteLine("CAPAB START");
-
- /* Send module names, split at 509 length */
- std::string item;
- std::string line = "CAPAB MODULES ";
- while (modulelist.GetToken(item))
- {
- if (line.length() + item.length() + 1 > 509)
- {
- this->WriteLine(line);
- line = "CAPAB MODULES ";
- }
-
- if (line != "CAPAB MODULES ")
- line.append(",");
-
- line.append(item);
- }
- if (line != "CAPAB MODULES ")
- this->WriteLine(line);
-
- int ip6 = 0;
- int ip6support = 0;
-#ifdef IPV6
- ip6 = 1;
-#endif
-#ifdef SUPPORT_IP6LINKS
- ip6support = 1;
-#endif
- std::string extra;
- /* Do we have sha256 available? If so, we send a challenge */
- if (Utils->ChallengeResponse && (Instance->Modules->Find("m_sha256.so")))
- {
- this->SetOurChallenge(RandString(20));
- extra = " CHALLENGE=" + this->GetOurChallenge();
- }
-
- this->WriteLine("CAPAB CAPABILITIES :NICKMAX="+ConvToStr(NICKMAX)+" HALFOP="+ConvToStr(this->Instance->Config->AllowHalfop)+" CHANMAX="+ConvToStr(CHANMAX)+" MAXMODES="+ConvToStr(MAXMODES)+" IDENTMAX="+ConvToStr(IDENTMAX)+" MAXQUIT="+ConvToStr(MAXQUIT)+" MAXTOPIC="+ConvToStr(MAXTOPIC)+" MAXKICK="+ConvToStr(MAXKICK)+" MAXGECOS="+ConvToStr(MAXGECOS)+" MAXAWAY="+ConvToStr(MAXAWAY)+" IP6NATIVE="+ConvToStr(ip6)+" IP6SUPPORT="+ConvToStr(ip6support)+" PROTOCOL="+ConvToStr(ProtocolVersion)+extra+" PREFIX="+Instance->Modes->BuildPrefixes()+" CHANMODES="+Instance->Modes->ChanModes()+" SVSPART=1");
-
- this->WriteLine("CAPAB END");
-}
-
-/* Check a comma seperated list for an item */
-bool TreeSocket::HasItem(const std::string &list, const std::string &item)
-{
- irc::commasepstream seplist(list);
- std::string item2;
-
- while (seplist.GetToken(item2))
- {
- if (item2 == item)
- return true;
- }
- return false;
-}
-
-/* Isolate and return the elements that are different between two comma seperated lists */
-std::string TreeSocket::ListDifference(const std::string &one, const std::string &two)
-{
- irc::commasepstream list_one(one);
- std::string item;
- std::string result;
- while (list_one.GetToken(item))
- {
- if (!HasItem(two, item))
- {
- result.append(" ");
- result.append(item);
- }
- }
- return result;
-}
-
void TreeSocket::SendError(const std::string &errormessage)
{
/* Display the error locally as well as sending it remotely */
@@ -409,141 +315,6 @@ void TreeSocket::SendError(const std::string &errormessage)
this->FlushWriteBuffer();
}
-bool TreeSocket::Capab(const std::deque<std::string> &params)
-{
- if (params.size() < 1)
- {
- this->SendError("Invalid number of parameters for CAPAB - Mismatched version");
- return false;
- }
- if (params[0] == "START")
- {
- this->ModuleList.clear();
- this->CapKeys.clear();
- }
- else if (params[0] == "END")
- {
- std::string reason;
- int ip6support = 0;
-#ifdef SUPPORT_IP6LINKS
- ip6support = 1;
-#endif
- /* Compare ModuleList and check CapKeys...
- * Maybe this could be tidier? -- Brain
- */
- if ((this->ModuleList != this->MyCapabilities()) && (this->ModuleList.length()))
- {
- std::string diff = ListDifference(this->ModuleList, this->MyCapabilities());
- if (!diff.length())
- {
- diff = "your server:" + ListDifference(this->MyCapabilities(), this->ModuleList);
- }
- else
- {
- diff = "this server:" + diff;
- }
- if (diff.length() == 12)
- reason = "Module list in CAPAB is not alphabetically ordered, cannot compare lists.";
- else
- reason = "Modules loaded on these servers are not correctly matched, these modules are not loaded on " + diff;
- }
-
- cap_validation valid_capab[] = {
- {"Maximum nickname lengths differ or remote nickname length not specified", "NICKMAX", NICKMAX},
- {"Maximum ident lengths differ or remote ident length not specified", "IDENTMAX", IDENTMAX},
- {"Maximum channel lengths differ or remote channel length not specified", "CHANMAX", CHANMAX},
- {"Maximum modes per line differ or remote modes per line not specified", "MAXMODES", MAXMODES},
- {"Maximum quit lengths differ or remote quit length not specified", "MAXQUIT", MAXQUIT},
- {"Maximum topic lengths differ or remote topic length not specified", "MAXTOPIC", MAXTOPIC},
- {"Maximum kick lengths differ or remote kick length not specified", "MAXKICK", MAXKICK},
- {"Maximum GECOS (fullname) lengths differ or remote GECOS length not specified", "MAXGECOS", MAXGECOS},
- {"Maximum awaymessage lengths differ or remote awaymessage length not specified", "MAXAWAY", MAXAWAY},
- {"", "", 0}
- };
-
- if (((this->CapKeys.find("IP6SUPPORT") == this->CapKeys.end()) && (ip6support)) || ((this->CapKeys.find("IP6SUPPORT") != this->CapKeys.end()) && (this->CapKeys.find("IP6SUPPORT")->second != ConvToStr(ip6support))))
- reason = "We don't both support linking to IPV6 servers";
- if (((this->CapKeys.find("IP6NATIVE") != this->CapKeys.end()) && (this->CapKeys.find("IP6NATIVE")->second == "1")) && (!ip6support))
- reason = "The remote server is IPV6 native, and we don't support linking to IPV6 servers";
- if (((this->CapKeys.find("PROTOCOL") == this->CapKeys.end()) || ((this->CapKeys.find("PROTOCOL") != this->CapKeys.end()) && (this->CapKeys.find("PROTOCOL")->second != ConvToStr(ProtocolVersion)))))
- {
- if (this->CapKeys.find("PROTOCOL") != this->CapKeys.end())
- reason = "Mismatched protocol versions "+this->CapKeys.find("PROTOCOL")->second+" and "+ConvToStr(ProtocolVersion);
- else
- reason = "Protocol version not specified";
- }
-
- if(this->CapKeys.find("PREFIX") != this->CapKeys.end() && this->CapKeys.find("PREFIX")->second != this->Instance->Modes->BuildPrefixes())
- reason = "One or more of the prefixes on the remote server are invalid on this server.";
-
- if (((this->CapKeys.find("HALFOP") == this->CapKeys.end()) && (Instance->Config->AllowHalfop)) || ((this->CapKeys.find("HALFOP") != this->CapKeys.end()) && (this->CapKeys.find("HALFOP")->second != ConvToStr(Instance->Config->AllowHalfop))))
- reason = "We don't both have halfop support enabled/disabled identically";
-
- for (int x = 0; valid_capab[x].size; ++x)
- {
- if (((this->CapKeys.find(valid_capab[x].key) == this->CapKeys.end()) || ((this->CapKeys.find(valid_capab[x].key) != this->CapKeys.end()) &&
- (this->CapKeys.find(valid_capab[x].key)->second != ConvToStr(valid_capab[x].size)))))
- reason = valid_capab[x].reason;
- }
-
- /* Challenge response, store their challenge for our password */
- std::map<std::string,std::string>::iterator n = this->CapKeys.find("CHALLENGE");
- if (Utils->ChallengeResponse && (n != this->CapKeys.end()) && (Instance->Modules->Find("m_sha256.so")))
- {
- /* Challenge-response is on now */
- this->SetTheirChallenge(n->second);
- if (!this->GetTheirChallenge().empty() && (this->LinkState == CONNECTING))
- {
- this->WriteLine(std::string("SERVER ")+this->Instance->Config->ServerName+" "+this->MakePass(OutboundPass, this->GetTheirChallenge())+" 0 "+
- Instance->Config->GetSID()+" :"+this->Instance->Config->ServerDesc);
- }
- }
- else
- {
- /* They didnt specify a challenge or we don't have m_sha256.so, we use plaintext */
- if (this->LinkState == CONNECTING)
- this->WriteLine(std::string("SERVER ")+this->Instance->Config->ServerName+" "+OutboundPass+" 0 "+Instance->Config->GetSID()+" :"+this->Instance->Config->ServerDesc);
- }
-
- if (reason.length())
- {
- this->SendError("CAPAB negotiation failed: "+reason);
- return false;
- }
- }
- else if ((params[0] == "MODULES") && (params.size() == 2))
- {
- if (!this->ModuleList.length())
- {
- this->ModuleList.append(params[1]);
- }
- else
- {
- this->ModuleList.append(",");
- this->ModuleList.append(params[1]);
- }
- }
-
- else if ((params[0] == "CAPABILITIES") && (params.size() == 2))
- {
- irc::tokenstream capabs(params[1]);
- std::string item;
- bool more = true;
- while ((more = capabs.GetToken(item)))
- {
- /* Process each key/value pair */
- std::string::size_type equals = item.rfind('=');
- if (equals != std::string::npos)
- {
- std::string var = item.substr(0, equals);
- std::string value = item.substr(equals+1, item.length());
- CapKeys[var] = value;
- }
- }
- }
- return true;
-}
-
/** This function forces this server to quit, removing this server
* and any users on it (and servers and users below that, etc etc).
* It's very slow and pretty clunky, but luckily unless your network
@@ -602,536 +373,6 @@ void TreeSocket::Squit(TreeServer* Current, const std::string &reason)
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> &params)
-{
- /* 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? */
- User* 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 User 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 User or Channel */
- User* dst = this->Instance->FindNick(params[0]);
- Channel* 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> &params)
-{
- if (params.size() != 4)
- return true;
- time_t ts = atoi(params[1].c_str());
- std::string nsource = source;
- Channel* 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])
- {
- User* 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> &params)
-{
- /* 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() < 2)
- return true;
-
- irc::modestacker modestack(true); /* Modes to apply from the users in the user list */
- User* 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.size() > 2) ? params[2] : ""); /* users from the user list */
- bool apply_other_sides_modes = true; /* True if we are accepting the other side's modes */
- Channel* 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 */
-
- if (params.size() > 2)
- params[2] = ":" + params[2];
-
- Utils->DoOneToAllButSender(source,"FJOIN",params,source);
-
- if (!TS)
- {
- Instance->Log(DEFAULT,"*** BUG? *** TS of 0 sent to FJOIN. Are some services authors smoking craq, or is it 1970 again?. Dropped.");
- Instance->SNO->WriteToSnoMask('d', "WARNING: The server %s is sending FJOIN with a TS of zero. Total craq. Command was dropped.", source.c_str());
- return true;
- }
-
- if (created)
- chan = new Channel(Instance, channel, ourTS);
-
- /* If our TS is less than theirs, we dont accept their modes */
- if (ourTS < TS)
- apply_other_sides_modes = false;
-
- /* Our TS greater than theirs, clear all our modes from the channel, accept theirs. */
- if (ourTS > TS)
- {
- std::deque<std::string> param_list;
- if (Utils->AnnounceTSChange && chan)
- chan->WriteChannelWithServ(Instance->Config->ServerName, "NOTICE %s :TS for %s changed from %lu to %lu", chan->name, chan->name, ourTS, TS);
- ourTS = TS;
- if (!created)
- {
- chan->age = TS;
- param_list.push_back(channel);
- this->RemoveStatus(Instance->Config->GetSID(), param_list);
- }
- }
-
- /* Now, process every 'prefixes,nick' pair */
- while (users.GetToken(item))
- {
- const char* usr = item.c_str();
- if (usr && *usr)
- {
- const char* permissions = usr;
- /* Iterate through all the prefix values, convert them from prefixes to mode letters */
- std::string modes;
- while ((*permissions) && (*permissions != ','))
- {
- ModeHandler* mh = Instance->Modes->FindPrefix(*permissions);
- if (mh)
- modes = modes + mh->GetModeChar();
- else
- {
- this->SendError(std::string("Invalid prefix '")+(*permissions)+"' in FJOIN");
- return false;
- }
- usr++;
- permissions++;
- }
- /* Advance past the comma, to the nick */
- usr++;
-
- /* Check the user actually exists */
- who = this->Instance->FindUUID(usr);
- if (who)
- {
- /* Check that the user's 'direction' is correct */
- TreeServer* route_back_again = Utils->BestRouteTo(who->server);
- if ((!route_back_again) || (route_back_again->GetSocket() != this))
- continue;
-
- /* Add any permissions this user had to the mode stack */
- for (std::string::iterator x = modes.begin(); x != modes.end(); ++x)
- modestack.Push(*x, who->nick);
-
- Channel::JoinUser(this->Instance, who, channel.c_str(), true, "", true, TS);
- }
- else
- {
- Instance->Log(SPARSE,"Warning! Invalid user %s in FJOIN to channel %s IGNORED", usr, channel.c_str());
- continue;
- }
- }
- }
-
- /* Flush mode stacker if we lost the FJOIN or had equal TS */
- if (apply_other_sides_modes)
- {
- std::deque<std::string> stackresult;
- const char* mode_junk[MAXMODES+2];
- mode_junk[0] = channel.c_str();
-
- while (modestack.GetStackedLine(stackresult))
- {
- for (size_t j = 0; j < stackresult.size(); j++)
- {
- mode_junk[j+1] = stackresult[j].c_str();
- }
- Instance->SendMode(mode_junk, stackresult.size() + 1, Instance->FakeClient);
- }
- }
-
- return true;
-}
-
-/*
- * Yes, this function looks a little ugly.
- * However, in some circumstances we may not have a User, so we need to do things this way.
- * Returns 1 if colliding local client, 2 if colliding remote, 3 if colliding both.
- * Sends SVSNICKs as appropriate and forces nickchanges too.
- */
-int TreeSocket::DoCollision(User *u, time_t remotets, const char *remoteident, const char *remoteip, const char *remoteuid)
-{
- /*
- * Under old protocol rules, we would have had to kill both clients.
- * Really, this sucks.
- * These days, we have UID. And, so what we do is, force nick change client(s)
- * involved according to timestamp rules.
- *
- * RULES:
- * user@ip equal:
- * Force nick change on OLDER timestamped client
- * user@ip differ:
- * Force nick change on NEWER timestamped client
- * TS EQUAL:
- * FNC both.
- *
- * This stops abusive use of collisions, simplifies problems with loops, and so on.
- * -- w00t
- */
- bool bChangeLocal = true;
- bool bChangeRemote = true;
-
- /* for brevity, don't use the User */
- time_t localts = u->age;
- const char *localident = u->ident;
- const char *localip = u->GetIPString();
-
- /* mmk. let's do this again. */
- if (remotets == localts)
- {
- /* equal. fuck them both! do nada, let the handler at the bottom figure this out. */
- }
- else
- {
- /* fuck. now it gets complex. */
-
- /* first, let's see if ident@host matches. */
- bool SamePerson = !strcmp(localident, remoteident)
- && !strcmp(localip, remoteip);
-
- /*
- * if ident@ip is equal, and theirs is newer, or
- * ident@ip differ, and ours is newer
- */
- if((SamePerson && remotets < localts) ||
- (!SamePerson && remotets > localts))
- {
- /* remote needs to change */
- bChangeLocal = false;
- }
- else
- {
- /* ours needs to change */
- bChangeRemote = false;
- }
- }
-
-
- if (bChangeLocal)
- {
- u->ForceNickChange(u->uuid);
-
- if (!bChangeRemote)
- return 1;
- }
- if (bChangeRemote)
- {
- /*
- * Cheat a little here. Instead of a dedicated command to change UID,
- * use SVSNICK and accept their client with it's UID (as we know the SVSNICK will
- * not fail under any circumstances -- UIDs are netwide exclusive).
- *
- * This means that each side of a collide will generate one extra NICK back to where
- * they have just linked (and where it got the SVSNICK from), however, it will
- * be dropped harmlessly as it will come in as :928AAAB NICK 928AAAB, and we already
- * have 928AAAB's nick set to that.
- * -- w00t
- */
- User *remote = this->Instance->FindUUID(remoteuid);
-
- if (remote)
- {
- /* buh.. nick change collide. force change their nick. */
- remote->ForceNickChange(remote->uuid);
- }
- else
- {
- /* user has not been introduced yet, just inform their server */
- this->WriteLine(std::string(":")+this->Instance->Config->GetSID()+" SVSNICK "+remoteuid+" " + remoteuid + " " + ConvToStr(remotets));
- }
-
- if (!bChangeLocal)
- return 2;
- }
-
- return 3;
-}
-
-bool TreeSocket::ParseUID(const std::string &source, std::deque<std::string> &params)
-{
- /** Do we have enough parameters:
- * UID uuid age nick host dhost ident +modestr ip.string :gecos
- */
- if (params.size() != 10)
- {
- this->WriteLine(std::string(":")+this->Instance->Config->GetSID()+" KILL "+params[0]+" :Invalid client introduction ("+params[0]+" with only "+
- ConvToStr(params.size())+" of 10 parameters?)");
- return true;
- }
-
- time_t age = ConvToInt(params[1]);
- time_t signon = ConvToInt(params[8]);
- const char* tempnick = params[2].c_str();
- std::string empty;
-
- /* XXX probably validate UID length too -- w00t */
- cmd_validation valid[] = { {"Nickname", 2, NICKMAX}, {"Hostname", 3, 64}, {"Displayed hostname", 4, 64}, {"Ident", 5, IDENTMAX}, {"GECOS", 9, MAXGECOS}, {"", 0, 0} };
-
- TreeServer* remoteserver = Utils->FindServer(source);
-
- if (!remoteserver)
- {
- this->WriteLine(std::string(":")+this->Instance->Config->GetSID()+" KILL "+params[0]+" :Invalid client introduction (Unknown server "+source+")");
- return true;
- }
-
- /* Check parameters for validity before introducing the client, discovered by dmb */
- if (!age)
- {
- this->WriteLine(std::string(":")+this->Instance->Config->GetSID()+" KILL "+params[0]+" :Invalid client introduction (Invalid TS?)");
- return true;
- }
-
- for (size_t x = 0; valid[x].length; ++x)
- {
- if (params[valid[x].param].length() > valid[x].length)
- {
- this->WriteLine(std::string(":")+this->Instance->Config->GetSID()+" KILL "+params[0]+" :Invalid client introduction (" + valid[x].item + " > " + ConvToStr(valid[x].length) + ")");
- return true;
- }
- }
-
-
- /* check for collision */
- user_hash::iterator iter = this->Instance->Users->clientlist->find(tempnick);
-
- if (iter != this->Instance->Users->clientlist->end())
- {
- /*
- * Nick collision.
- */
- Instance->Log(DEBUG,"*** Collision on %s", tempnick);
- int collide = this->DoCollision(iter->second, age, params[5].c_str(), params[7].c_str(), params[0].c_str());
-
- if (collide == 2)
- {
- /* remote client changed, make sure we change their nick for the hash too */
- tempnick = params[0].c_str();
- }
- }
-
- /* IMPORTANT NOTE: For remote users, we pass the UUID in the constructor. This automatically
- * sets it up in the UUID hash for us.
- */
- User* _new = NULL;
- try
- {
- _new = new User(this->Instance, params[0]);
- }
- catch (...)
- {
- SendError("Protocol violation - Duplicate UUID '" + params[0] + "' on introduction of new user");
- return false;
- }
- (*(this->Instance->Users->clientlist))[tempnick] = _new;
- _new->SetFd(FD_MAGIC_NUMBER);
- strlcpy(_new->nick, tempnick, NICKMAX - 1);
- strlcpy(_new->host, params[3].c_str(),64);
- strlcpy(_new->dhost, params[4].c_str(),64);
- _new->server = this->Instance->FindServerNamePtr(remoteserver->GetName().c_str());
- strlcpy(_new->ident, params[5].c_str(),IDENTMAX);
- strlcpy(_new->fullname, params[9].c_str(),MAXGECOS);
- _new->registered = REG_ALL;
- _new->signon = signon;
- _new->age = age;
-
- /* we need to remove the + from the modestring, so we can do our stuff */
- std::string::size_type pos_after_plus = params[6].find_first_not_of('+');
- if (pos_after_plus != std::string::npos)
- params[6] = params[6].substr(pos_after_plus);
-
- for (std::string::iterator v = params[6].begin(); v != params[6].end(); v++)
- {
- /* For each mode thats set, increase counter */
- ModeHandler* mh = Instance->Modes->FindMode(*v, MODETYPE_USER);
-
- if (mh)
- {
- mh->OnModeChange(_new, _new, NULL, empty, true);
- _new->SetMode(*v, true);
- mh->ChangeCount(1);
- }
- }
-
- /* now we've done with modes processing, put the + back for remote servers */
- params[6] = "+" + params[6];
-
-#ifdef SUPPORT_IP6LINKS
- if (params[7].find_first_of(":") != std::string::npos)
- _new->SetSockAddr(AF_INET6, params[7].c_str(), 0);
- else
-#endif
- _new->SetSockAddr(AF_INET, params[7].c_str(), 0);
-
- Instance->Users->AddGlobalClone(_new);
-
- bool dosend = true;
-
- if ((this->Utils->quiet_bursts && remoteserver->bursting) || this->Instance->SilentULine(_new->server))
- dosend = false;
-
- if (dosend)
- this->Instance->SNO->WriteToSnoMask('C',"Client connecting at %s: %s!%s@%s [%s] [%s]",_new->server,_new->nick,_new->ident,_new->host, _new->GetIPString(), _new->fullname);
-
- params[9] = ":" + params[9];
- Utils->DoOneToAllButSender(source, "UID", params, source);
-
- FOREACH_MOD_I(Instance,I_OnPostConnect,OnPostConnect(_new));
-
- return true;
-}
-
/** Send one or more FJOINs for a channel of users.
* If the length of a single line is more than 480-NICKMAX
* in length, it is split over multiple lines.
diff --git a/src/modules/m_spanningtree/uid.cpp b/src/modules/m_spanningtree/uid.cpp
new file mode 100644
index 000000000..4e81bf2af
--- /dev/null
+++ b/src/modules/m_spanningtree/uid.cpp
@@ -0,0 +1,167 @@
+/* +------------------------------------+
+ * | Inspire Internet Relay Chat Daemon |
+ * +------------------------------------+
+ *
+ * InspIRCd: (C) 2002-2008 InspIRCd Development Team
+ * See: http://www.inspircd.org/wiki/index.php/Credits
+ *
+ * This program is free but copyrighted software; see
+ * the file COPYING for details.
+ *
+ * ---------------------------------------------------
+ */
+
+#include "inspircd.h"
+#include "commands/cmd_whois.h"
+#include "commands/cmd_stats.h"
+#include "socket.h"
+#include "wildcard.h"
+#include "xline.h"
+#include "transport.h"
+#include "m_hash.h"
+#include "socketengine.h"
+
+#include "m_spanningtree/main.h"
+#include "m_spanningtree/utils.h"
+#include "m_spanningtree/treeserver.h"
+#include "m_spanningtree/link.h"
+#include "m_spanningtree/treesocket.h"
+#include "m_spanningtree/resolvers.h"
+#include "m_spanningtree/handshaketimer.h"
+
+/* $ModDep: m_spanningtree/timesynctimer.h m_spanningtree/resolvers.h m_spanningtree/main.h m_spanningtree/utils.h m_spanningtree/treeserver.h m_spanningtree/link.h m_spanningtree/treesocket.h m_hash.h */
+
+bool TreeSocket::ParseUID(const std::string &source, std::deque<std::string> &params)
+{
+ /** Do we have enough parameters:
+ * UID uuid age nick host dhost ident +modestr ip.string :gecos
+ */
+ if (params.size() != 10)
+ {
+ this->WriteLine(std::string(":")+this->Instance->Config->GetSID()+" KILL "+params[0]+" :Invalid client introduction ("+params[0]+" with only "+
+ ConvToStr(params.size())+" of 10 parameters?)");
+ return true;
+ }
+
+ time_t age = ConvToInt(params[1]);
+ time_t signon = ConvToInt(params[8]);
+ const char* tempnick = params[2].c_str();
+ std::string empty;
+
+ /* XXX probably validate UID length too -- w00t */
+ cmd_validation valid[] = { {"Nickname", 2, NICKMAX}, {"Hostname", 3, 64}, {"Displayed hostname", 4, 64}, {"Ident", 5, IDENTMAX}, {"GECOS", 9, MAXGECOS}, {"", 0, 0} };
+
+ TreeServer* remoteserver = Utils->FindServer(source);
+
+ if (!remoteserver)
+ {
+ this->WriteLine(std::string(":")+this->Instance->Config->GetSID()+" KILL "+params[0]+" :Invalid client introduction (Unknown server "+source+")");
+ return true;
+ }
+
+ /* Check parameters for validity before introducing the client, discovered by dmb */
+ if (!age)
+ {
+ this->WriteLine(std::string(":")+this->Instance->Config->GetSID()+" KILL "+params[0]+" :Invalid client introduction (Invalid TS?)");
+ return true;
+ }
+
+ for (size_t x = 0; valid[x].length; ++x)
+ {
+ if (params[valid[x].param].length() > valid[x].length)
+ {
+ this->WriteLine(std::string(":")+this->Instance->Config->GetSID()+" KILL "+params[0]+" :Invalid client introduction (" + valid[x].item + " > " + ConvToStr(valid[x].length) + ")");
+ return true;
+ }
+ }
+
+
+ /* check for collision */
+ user_hash::iterator iter = this->Instance->Users->clientlist->find(tempnick);
+
+ if (iter != this->Instance->Users->clientlist->end())
+ {
+ /*
+ * Nick collision.
+ */
+ Instance->Log(DEBUG,"*** Collision on %s", tempnick);
+ int collide = this->DoCollision(iter->second, age, params[5].c_str(), params[7].c_str(), params[0].c_str());
+
+ if (collide == 2)
+ {
+ /* remote client changed, make sure we change their nick for the hash too */
+ tempnick = params[0].c_str();
+ }
+ }
+
+ /* IMPORTANT NOTE: For remote users, we pass the UUID in the constructor. This automatically
+ * sets it up in the UUID hash for us.
+ */
+ User* _new = NULL;
+ try
+ {
+ _new = new User(this->Instance, params[0]);
+ }
+ catch (...)
+ {
+ SendError("Protocol violation - Duplicate UUID '" + params[0] + "' on introduction of new user");
+ return false;
+ }
+ (*(this->Instance->Users->clientlist))[tempnick] = _new;
+ _new->SetFd(FD_MAGIC_NUMBER);
+ strlcpy(_new->nick, tempnick, NICKMAX - 1);
+ strlcpy(_new->host, params[3].c_str(),64);
+ strlcpy(_new->dhost, params[4].c_str(),64);
+ _new->server = this->Instance->FindServerNamePtr(remoteserver->GetName().c_str());
+ strlcpy(_new->ident, params[5].c_str(),IDENTMAX);
+ strlcpy(_new->fullname, params[9].c_str(),MAXGECOS);
+ _new->registered = REG_ALL;
+ _new->signon = signon;
+ _new->age = age;
+
+ /* we need to remove the + from the modestring, so we can do our stuff */
+ std::string::size_type pos_after_plus = params[6].find_first_not_of('+');
+ if (pos_after_plus != std::string::npos)
+ params[6] = params[6].substr(pos_after_plus);
+
+ for (std::string::iterator v = params[6].begin(); v != params[6].end(); v++)
+ {
+ /* For each mode thats set, increase counter */
+ ModeHandler* mh = Instance->Modes->FindMode(*v, MODETYPE_USER);
+
+ if (mh)
+ {
+ mh->OnModeChange(_new, _new, NULL, empty, true);
+ _new->SetMode(*v, true);
+ mh->ChangeCount(1);
+ }
+ }
+
+ /* now we've done with modes processing, put the + back for remote servers */
+ params[6] = "+" + params[6];
+
+#ifdef SUPPORT_IP6LINKS
+ if (params[7].find_first_of(":") != std::string::npos)
+ _new->SetSockAddr(AF_INET6, params[7].c_str(), 0);
+ else
+#endif
+ _new->SetSockAddr(AF_INET, params[7].c_str(), 0);
+
+ Instance->Users->AddGlobalClone(_new);
+
+ bool dosend = true;
+
+ if ((this->Utils->quiet_bursts && remoteserver->bursting) || this->Instance->SilentULine(_new->server))
+ dosend = false;
+
+ if (dosend)
+ this->Instance->SNO->WriteToSnoMask('C',"Client connecting at %s: %s!%s@%s [%s] [%s]",_new->server,_new->nick,_new->ident,_new->host, _new->GetIPString(), _new->fullname);
+
+ params[9] = ":" + params[9];
+ Utils->DoOneToAllButSender(source, "UID", params, source);
+
+ FOREACH_MOD_I(Instance,I_OnPostConnect,OnPostConnect(_new));
+
+ return true;
+}
+