]> git.netwichtig.de Git - user/henk/code/inspircd.git/blobdiff - src/modules/m_spanningtree.cpp
I hate typos
[user/henk/code/inspircd.git] / src / modules / m_spanningtree.cpp
index 6efae534af85b125f0577fc2f2690429bc8fd78a..e3aea5f0480d13fe5d3b255f620ed156b8fb69d5 100644 (file)
@@ -14,6 +14,8 @@
  * ---------------------------------------------------
  */
 
+/* $ModDesc: Povides a spanning tree server link protocol */
+
 using namespace std;
 
 #include <stdio.h>
@@ -35,6 +37,7 @@ using namespace std;
 #include "inspstring.h"
 #include "hashcomp.h"
 #include "message.h"
+#include "xline.h"
 
 #ifdef GCC3
 #define nspace __gnu_cxx
@@ -60,15 +63,27 @@ extern chan_hash chanlist;
 class TreeServer;
 class TreeSocket;
 
+TreeServer *TreeRoot;
+
+typedef nspace::hash_map<std::string, TreeServer*> server_hash;
+server_hash serverlist;
+
 bool DoOneToOne(std::string prefix, std::string command, std::deque<std::string> params, std::string target);
 bool DoOneToAllButSender(std::string prefix, std::string command, std::deque<std::string> params, std::string omit);
 bool DoOneToMany(std::string prefix, std::string command, std::deque<std::string> params);
 bool DoOneToAllButSenderRaw(std::string data,std::string omit, std::string prefix,std::string command,std::deque<std::string> params);
 void ReadConfiguration(bool rebind);
 
+extern std::vector<KLine> klines;
+extern std::vector<GLine> glines;
+extern std::vector<ZLine> zlines;
+extern std::vector<QLine> qlines;
+extern std::vector<ELine> elines;
+
 class TreeServer
 {
        TreeServer* Parent;
+       TreeServer* Route;
        std::vector<TreeServer*> Children;
        std::string ServerName;
        std::string ServerDesc;
@@ -76,6 +91,8 @@ class TreeServer
        int UserCount;
        int OperCount;
        TreeSocket* Socket;     // for directly connected servers this points at the socket object
+       time_t NextPing;
+       bool LastPingWasGood;
        
  public:
 
@@ -86,6 +103,7 @@ class TreeServer
                ServerDesc = "";
                VersionString = "";
                UserCount = OperCount = 0;
+               VersionString = GetVersionString();
        }
 
        TreeServer(std::string Name, std::string Desc) : ServerName(Name), ServerDesc(Desc)
@@ -93,12 +111,91 @@ class TreeServer
                Parent = NULL;
                VersionString = "";
                UserCount = OperCount = 0;
+               VersionString = GetVersionString();
+               Route = NULL;
+               AddHashEntry();
        }
 
        TreeServer(std::string Name, std::string Desc, TreeServer* Above, TreeSocket* Sock) : Parent(Above), ServerName(Name), ServerDesc(Desc), Socket(Sock)
        {
                VersionString = "";
                UserCount = OperCount = 0;
+               this->SetNextPingTime(time(NULL) + 60);
+               this->SetPingFlag();
+
+               /* find the 'route' for this server (e.g. the one directly connected
+                * to the local server, which we can use to reach it)
+                *
+                * In the following example, consider we have just added a TreeServer
+                * class for server G on our network, of which we are server A.
+                * To route traffic to G (marked with a *) we must send the data to
+                * B (marked with a +) so this algorithm initializes the 'Route'
+                * value to point at whichever server traffic must be routed through
+                * to get here. If we were to try this algorithm with server B,
+                * the Route pointer would point at its own object ('this').
+                *
+                *              A
+                *             / \
+                *          + B   C
+                *           / \   \
+                *          D   E   F
+                *         /         \
+                *      * G           H
+                *
+                * We only run this algorithm when a server is created, as
+                * the routes remain constant while ever the server exists, and
+                * do not need to be re-calculated.
+                */
+
+               Route = Above;
+               if (Route == TreeRoot)
+               {
+                       Route = this;
+               }
+               else
+               {
+                       while (this->Route->GetParent() != TreeRoot)
+                       {
+                               this->Route = Route->GetParent();
+                       }
+               }
+
+               /* Because recursive code is slow and takes a lot of resources,
+                * we store two representations of the server tree. The first
+                * is a recursive structure where each server references its
+                * children and its parent, which is used for netbursts and
+                * netsplits to dump the whole dataset to the other server,
+                * and the second is used for very fast lookups when routing
+                * messages and is instead a hash_map, where each item can
+                * be referenced by its server name. The AddHashEntry()
+                * call below automatically inserts each TreeServer class
+                * into the hash_map as it is created. There is a similar
+                * maintainance call in the destructor to tidy up deleted
+                * servers.
+                */
+
+               this->AddHashEntry();
+       }
+
+       void AddHashEntry()
+       {
+               server_hash::iterator iter;
+               iter = serverlist.find(this->ServerName);
+               if (iter == serverlist.end())
+                       serverlist[this->ServerName] = this;
+       }
+
+       void DelHashEntry()
+       {
+               server_hash::iterator iter;
+               iter = serverlist.find(this->ServerName);
+               if (iter != serverlist.end())
+                       serverlist.erase(iter);
+       }
+
+       TreeServer* GetRoute()
+       {
+               return Route;
        }
 
        std::string GetName()
@@ -116,6 +213,27 @@ class TreeServer
                return this->VersionString;
        }
 
+       void SetNextPingTime(time_t t)
+       {
+               this->NextPing = t;
+               LastPingWasGood = false;
+       }
+
+       time_t NextPingTime()
+       {
+               return this->NextPing;
+       }
+
+       bool AnsweredLastPing()
+       {
+               return LastPingWasGood;
+       }
+
+       void SetPingFlag()
+       {
+               LastPingWasGood = true;
+       }
+
        int GetUserCount()
        {
                return this->UserCount;
@@ -136,6 +254,11 @@ class TreeServer
                return this->Parent;
        }
 
+       void SetVersion(std::string Version)
+       {
+               VersionString = Version;
+       }
+
        unsigned int ChildCount()
        {
                return Children.size();
@@ -171,7 +294,10 @@ class TreeServer
                return false;
        }
 
-       // removes child nodes of this node, and of that node, etc etc
+       /* Removes child nodes of this node, and of that node, etc etc.
+        * This is used during netsplits to automatically tidy up the
+        * server tree. It is slow, we don't use it for much else.
+        */
        bool Tidy()
        {
                bool stillchildren = true;
@@ -190,6 +316,11 @@ class TreeServer
                }
                return true;
        }
+
+       ~TreeServer()
+       {
+               this->DelHashEntry();
+       }
 };
 
 class Link
@@ -200,76 +331,56 @@ class Link
         int Port;
         std::string SendPass;
         std::string RecvPass;
+        unsigned long AutoConnect;
+        time_t NextConnectTime;
 };
 
-/* $ModDesc: Povides a spanning tree server link protocol */
-
 Server *Srv;
 ConfigReader *Conf;
-TreeServer *TreeRoot;
 std::vector<Link> LinkBlocks;
 
-TreeServer* RouteEnumerate(TreeServer* Current, std::string ServerName)
+TreeServer* FindServer(std::string ServerName)
 {
-       if (Current->GetName() == ServerName)
-               return Current;
-       for (unsigned int q = 0; q < Current->ChildCount(); q++)
+       server_hash::iterator iter;
+       iter = serverlist.find(ServerName);
+       if (iter != serverlist.end())
        {
-               TreeServer* found = RouteEnumerate(Current->GetChild(q),ServerName);
-               if (found)
-               {
-                       return found;
-               }
+               return iter->second;
+       }
+       else
+       {
+               return NULL;
        }
-       return NULL;
 }
 
-// Returns the locally connected server we must route a
-// message through to reach server 'ServerName'. This
-// only applies to one-to-one and not one-to-many routing.
+/* Returns the locally connected server we must route a
+ * message through to reach server 'ServerName'. This
+ * only applies to one-to-one and not one-to-many routing.
+ * See the comments for the constructor of TreeServer
+ * for more details.
+ */
 TreeServer* BestRouteTo(std::string ServerName)
 {
        if (ServerName.c_str() == TreeRoot->GetName())
-       {
                return NULL;
-       }
-       // first, find the server by recursively walking the tree
-       TreeServer* Found = RouteEnumerate(TreeRoot,ServerName);
-       // did we find it? If not, they did something wrong, abort.
-       if (!Found)
+       TreeServer* Found = FindServer(ServerName);
+       if (Found)
        {
-               return NULL;
+               return Found->GetRoute();
        }
        else
        {
-               // The server exists, follow its parent nodes until
-               // the parent of the current is 'TreeRoot', we know
-               // then that this is a directly-connected server.
-               while ((Found) && (Found->GetParent() != TreeRoot))
-               {
-                       Found = Found->GetParent();
-               }
-               return Found;
-       }
-}
-
-bool LookForServer(TreeServer* Current, std::string ServerName)
-{
-       if (ServerName == Current->GetName())
-               return true;
-       for (unsigned int q = 0; q < Current->ChildCount(); q++)
-       {
-               if (LookForServer(Current->GetChild(q),ServerName))
-                       return true;
+               return NULL;
        }
-       return false;
 }
 
 TreeServer* Found;
 
-void RFindServer(TreeServer* Current, std::string ServerName)
+/* TODO: These need optimizing to use an iterator of serverlist
+ */
+void RFindServerMask(TreeServer* Current, std::string ServerName)
 {
-       if ((ServerName == Current->GetName()) && (!Found))
+       if (Srv->MatchText(Current->GetName(),ServerName) && (!Found))
        {
                Found = Current;
                return;
@@ -279,22 +390,21 @@ void RFindServer(TreeServer* Current, std::string ServerName)
                for (unsigned int q = 0; q < Current->ChildCount(); q++)
                {
                        if (!Found)
-                               RFindServer(Current->GetChild(q),ServerName);
+                               RFindServerMask(Current->GetChild(q),ServerName);
                }
        }
-       return;
 }
 
-TreeServer* FindServer(std::string ServerName)
+TreeServer* FindServerMask(std::string ServerName)
 {
        Found = NULL;
-       RFindServer(TreeRoot,ServerName);
+       RFindServerMask(TreeRoot,ServerName);
        return Found;
 }
 
 bool IsServer(std::string ServerName)
 {
-       return LookForServer(TreeRoot,ServerName);
+       return (FindServer(ServerName) != NULL);
 }
 
 class TreeSocket : public InspSocket
@@ -306,6 +416,8 @@ class TreeSocket : public InspSocket
        std::string InboundDescription;
        int num_lost_users;
        int num_lost_servers;
+       time_t NextPing;
+       bool LastPingWasGood;
        
  public:
 
@@ -371,6 +483,7 @@ class TreeSocket : public InspSocket
                                // :source.server SERVER server.name hops :Description
                                snprintf(command,1024,":%s SERVER %s * %d :%s",Current->GetName().c_str(),recursive_server->GetName().c_str(),hops,recursive_server->GetDesc().c_str());
                                this->WriteLine(command);
+                               this->WriteLine(":"+recursive_server->GetName()+" VERSION :"+recursive_server->GetVersion());
                                // down to next level
                                this->SendServers(recursive_server, s, hops+1);
                        }
@@ -466,10 +579,15 @@ class TreeSocket : public InspSocket
                {
                        if ((ts >= c->topicset) || (!*c->topic))
                        {
+                               std::string oldtopic = c->topic;
                                strlcpy(c->topic,topic.c_str(),MAXTOPIC);
                                strlcpy(c->setby,setby.c_str(),NICKMAX);
                                c->topicset = ts;
-                               WriteChannelWithServ((char*)source.c_str(), c, "TOPIC %s :%s", c->name, c->topic);
+                               // 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 != topic)
+                                       WriteChannelWithServ((char*)source.c_str(), c, "TOPIC %s :%s", c->name, c->topic);
                        }
                        
                }
@@ -483,7 +601,7 @@ class TreeSocket : public InspSocket
 
        bool ForceJoin(std::string source, std::deque<std::string> params)
        {
-               if (params.size() < 2)
+               if (params.size() < 3)
                        return true;
 
                char first[MAXBUF];
@@ -496,14 +614,32 @@ class TreeSocket : public InspSocket
                
                userrec* who = NULL;
                std::string channel = params[0];
+               time_t TS = atoi(params[1].c_str());
                char* key = "";
+               
                chanrec* chan = Srv->FindChannel(channel);
                if (chan)
                {
                        key = chan->key;
                }
                strlcpy(mode_users[0],channel.c_str(),MAXBUF);
-               for (unsigned int usernum = 1; usernum < params.size(); usernum++)
+
+               // default is a high value, which if we dont have this
+               // channel will let the other side apply their modes.
+               time_t ourTS = time(NULL)+600;
+               chanrec* us = Srv->FindChannel(channel);
+               if (us)
+               {
+                       ourTS = us->age;
+               }
+
+               log(DEBUG,"FJOIN detected, our TS=%lu, their TS=%lu",ourTS,TS);
+
+               // do this first, so our mode reversals are correctly received by other servers
+               // if there is a TS collision.
+               DoOneToAllButSender(source,"FJOIN",params,source);
+               
+               for (unsigned int usernum = 2; usernum < params.size(); usernum++)
                {
                        // process one channel at a time, applying modes.
                        char* usr = (char*)params[usernum].c_str();
@@ -534,7 +670,25 @@ class TreeSocket : public InspSocket
                                {
                                        // theres a mode for this user. push them onto the mode queue, and flush it
                                        // if there are more than MAXMODES to go.
-                                       Srv->SendMode(mode_users,modectr,who);
+                                       if (ourTS >= TS)
+                                       {
+                                               log(DEBUG,"Our our channel newer than theirs, accepting their modes");
+                                               Srv->SendMode(mode_users,modectr,who);
+                                       }
+                                       else
+                                       {
+                                               log(DEBUG,"Their channel newer than ours, bouncing their modes");
+                                               // bouncy bouncy!
+                                               std::deque<std::string> params;
+                                               // modes are now being UNSET...
+                                               *mode_users[1] = '-';
+                                               for (unsigned int x = 0; x < modectr; x++)
+                                               {
+                                                       params.push_back(mode_users[x]);
+                                               }
+                                               // tell everyone to bounce the modes. bad modes, bad!
+                                               DoOneToMany(Srv->GetServerName(),"FMODE",params);
+                                       }
                                        strcpy(mode_users[1],"+");
                                        modectr = 2;
                                }
@@ -544,9 +698,23 @@ class TreeSocket : public InspSocket
                // or, there are a number left over. flush them out.
                if ((modectr > 2) && (who))
                {
-                       Srv->SendMode(mode_users,modectr,who);
+                       if (ourTS >= TS)
+                       {
+                               log(DEBUG,"Our our channel newer than theirs, accepting their modes");
+                               Srv->SendMode(mode_users,modectr,who);
+                       }
+                       else
+                       {
+                               log(DEBUG,"Their channel newer than ours, bouncing their modes");
+                               std::deque<std::string> params;
+                               *mode_users[1] = '-';
+                               for (unsigned int x = 0; x < modectr; x++)
+                               {
+                                       params.push_back(mode_users[x]);
+                               }
+                               DoOneToMany(Srv->GetServerName(),"FMODE",params);
+                       }
                }
-               DoOneToAllButSender(source,"FJOIN",params,source);
                return true;
        }
 
@@ -562,7 +730,7 @@ class TreeSocket : public InspSocket
                std::string ident = params[4];
                time_t age = atoi(params[0].c_str());
                std::string modes = params[5];
-               if (*(modes.c_str()) == '+')
+               while (*(modes.c_str()) == '+')
                {
                        char* m = (char*)modes.c_str();
                        m++;
@@ -593,6 +761,7 @@ class TreeSocket : public InspSocket
                strlcpy(clientlist[tempnick]->fullname, gecos.c_str(),MAXGECOS);
                clientlist[tempnick]->registered = 7;
                clientlist[tempnick]->signon = age;
+               strlcpy(clientlist[tempnick]->modes, modes.c_str(),53);
                strlcpy(clientlist[tempnick]->ip,ip.c_str(),16);
                for (int i = 0; i < MAXCHANS; i++)
                {
@@ -607,7 +776,7 @@ class TreeSocket : public InspSocket
        void SendFJoins(TreeServer* Current, chanrec* c)
        {
                char list[MAXBUF];
-               snprintf(list,MAXBUF,":%s FJOIN %s",Srv->GetServerName().c_str(),c->name);
+               snprintf(list,MAXBUF,":%s FJOIN %s %lu",Srv->GetServerName().c_str(),c->name,(unsigned long)c->age);
                std::vector<char*> *ulist = c->GetUsers();
                for (unsigned int i = 0; i < ulist->size(); i++)
                {
@@ -619,7 +788,7 @@ class TreeSocket : public InspSocket
                        if (strlen(list)>(480-NICKMAX))
                        {
                                this->WriteLine(list);
-                               snprintf(list,MAXBUF,":%s FJOIN %s",Srv->GetServerName().c_str(),c->name);
+                               snprintf(list,MAXBUF,":%s FJOIN %s %lu",Srv->GetServerName().c_str(),c->name,(unsigned long)c->age);
                        }
                }
                if (list[strlen(list)-1] != ':')
@@ -628,6 +797,32 @@ class TreeSocket : public InspSocket
                }
        }
 
+       void SendXLines(TreeServer* Current)
+       {
+               char data[MAXBUF];
+               // for zlines and qlines, we should first check if theyre global...
+               for (std::vector<ZLine>::iterator i = zlines.begin(); i != zlines.end(); i++)
+               {
+                       snprintf(data,MAXBUF,":%s ADDLINE Z %s %s %lu %lu :%s",Srv->GetServerName().c_str(),i->ipaddr,i->source,(unsigned long)i->set_time,(unsigned long)i->duration,i->reason);
+                       this->WriteLine(data);
+               }
+               for (std::vector<QLine>::iterator i = qlines.begin(); i != qlines.end(); i++)
+               {
+                       snprintf(data,MAXBUF,":%s ADDLINE Q %s %s %lu %lu :%s",Srv->GetServerName().c_str(),i->nick,i->source,(unsigned long)i->set_time,(unsigned long)i->duration,i->reason);
+                       this->WriteLine(data);
+               }
+               for (std::vector<GLine>::iterator i = glines.begin(); i != glines.end(); i++)
+               {
+                       snprintf(data,MAXBUF,":%s ADDLINE G %s %s %lu %lu :%s",Srv->GetServerName().c_str(),i->hostmask,i->source,(unsigned long)i->set_time,(unsigned long)i->duration,i->reason);
+                       this->WriteLine(data);
+               }
+               for (std::vector<ELine>::iterator i = elines.begin(); i != elines.end(); i++)
+               {
+                       snprintf(data,MAXBUF,":%s ADDLINE E %s %s %lu %lu :%s",Srv->GetServerName().c_str(),i->hostmask,i->source,(unsigned long)i->set_time,(unsigned long)i->duration,i->reason);
+                       this->WriteLine(data);
+               }
+       }
+
        void SendChannelModes(TreeServer* Current)
        {
                char data[MAXBUF];
@@ -678,12 +873,15 @@ class TreeSocket : public InspSocket
        {
                Srv->SendOpers("*** Bursting to "+s->GetName()+".");
                this->WriteLine("BURST");
+               // send our version string
+               this->WriteLine(":"+Srv->GetServerName()+" VERSION :"+GetVersionString());
                // Send server tree
                this->SendServers(TreeRoot,s,1);
                // Send users and their channels
                this->SendUsers(s);
-               // TODO: Send everything else (channel modes etc)
+               // Send everything else (channel modes etc)
                this->SendChannelModes(s);
+               this->SendXLines(s);
                this->WriteLine("ENDBURST");
        }
 
@@ -747,7 +945,7 @@ class TreeSocket : public InspSocket
                        {
                                strcat(u->modes,"o");
                        }
-                       DoOneToAllButSender(u->server,"OPERTYPE",params,u->server);
+                       DoOneToAllButSender(u->nick,"OPERTYPE",params,u->server);
                }
                return true;
        }
@@ -789,6 +987,107 @@ class TreeSocket : public InspSocket
                return true;
        }
 
+       bool LocalPong(std::string prefix, std::deque<std::string> params)
+       {
+               if (params.size() < 1)
+                       return true;
+               TreeServer* ServerSource = FindServer(prefix);
+               if (ServerSource)
+               {
+                       ServerSource->SetPingFlag();
+               }
+               return true;
+       }
+
+       bool ServerVersion(std::string prefix, std::deque<std::string> params)
+       {
+               if (params.size() < 1)
+                       return true;
+               TreeServer* ServerSource = FindServer(prefix);
+               if (ServerSource)
+               {
+                       ServerSource->SetVersion(params[0]);
+               }
+               params[0] = ":" + params[0];
+               DoOneToAllButSender(prefix,"VERSION",params,prefix);
+               return true;
+       }
+
+       bool ChangeHost(std::string prefix, std::deque<std::string> params)
+       {
+               if (params.size() < 1)
+                       return true;
+               userrec* u = Srv->FindNick(prefix);
+               if (u)
+               {
+                       Srv->ChangeHost(u,params[0]);
+                       DoOneToAllButSender(prefix,"FHOST",params,u->server);
+               }
+               return true;
+       }
+
+       bool AddLine(std::string prefix, std::deque<std::string> params)
+       {
+               if (params.size() < 6)
+                       return true;
+               std::string linetype = params[0]; /* Z, Q, E, G, K */
+               std::string mask = params[1]; /* Line type dependent */
+               std::string source = params[2]; /* may not be online or may be a server */
+               std::string settime = params[3]; /* EPOCH time set */
+               std::string duration = params[4]; /* Duration secs */
+               std::string reason = params[5];
+
+               switch (*(linetype.c_str()))
+               {
+                       case 'Z':
+                               add_zline(atoi(duration.c_str()), source.c_str(), reason.c_str(), mask.c_str());
+                       break;
+                       case 'Q':
+                               add_qline(atoi(duration.c_str()), source.c_str(), reason.c_str(), mask.c_str());
+                       break;
+                       case 'E':
+                               add_eline(atoi(duration.c_str()), source.c_str(), reason.c_str(), mask.c_str());
+                       break;
+                       case 'G':
+                               add_gline(atoi(duration.c_str()), source.c_str(), reason.c_str(), mask.c_str());
+                       break;
+                       case 'K':
+                               add_kline(atoi(duration.c_str()), source.c_str(), reason.c_str(), mask.c_str());
+                       break;
+                       default:
+                               /* Just in case... */
+                               Srv->SendOpers("*** \2WARNING\2: Invalid xline type '"+linetype+"' sent by server "+prefix+", ignored!");
+                       break;
+               }
+               /* Send it on its way */
+               params[5] = ":" + params[5];
+               DoOneToAllButSender(prefix,"ADDLINE",params,prefix);
+               return true;
+       }
+
+       bool ChangeName(std::string prefix, std::deque<std::string> params)
+       {
+               if (params.size() < 1)
+                       return true;
+               userrec* u = Srv->FindNick(prefix);
+               if (u)
+               {
+                       Srv->ChangeGECOS(u,params[0]);
+                       params[0] = ":" + params[0];
+                       DoOneToAllButSender(prefix,"FNAME",params,u->server);
+               }
+               return true;
+       }
+       
+       bool LocalPing(std::string prefix, std::deque<std::string> params)
+       {
+               if (params.size() < 1)
+                       return true;
+               std::string stufftobounce = params[0];
+               this->WriteLine(":"+Srv->GetServerName()+" PONG "+stufftobounce);
+               return true;
+       }
+
        bool RemoteServer(std::string prefix, std::deque<std::string> params)
        {
                if (params.size() < 4)
@@ -803,6 +1102,12 @@ class TreeSocket : public InspSocket
                        this->WriteLine("ERROR :Protocol error - Introduced remote server from unknown server "+prefix);
                        return false;
                }
+               TreeServer* CheckDupe = FindServer(servername);
+               if (CheckDupe)
+               {
+                       this->WriteLine("ERROR :Server "+servername+" already exists on server "+CheckDupe->GetParent()->GetName()+"!");
+                       return false;
+               }
                TreeServer* Node = new TreeServer(servername,description,ParentOfThis,NULL);
                ParentOfThis->AddChild(Node);
                params[3] = ":" + params[3];
@@ -828,6 +1133,12 @@ class TreeSocket : public InspSocket
                {
                        if ((x->Name == servername) && (x->RecvPass == password))
                        {
+                               TreeServer* CheckDupe = FindServer(servername);
+                               if (CheckDupe)
+                               {
+                                       this->WriteLine("ERROR :Server "+servername+" already exists on server "+CheckDupe->GetParent()->GetName()+"!");
+                                       return false;
+                               }
                                // Begin the sync here. this kickstarts the
                                // other side, waiting in WAIT_AUTH_2 state,
                                // into starting their burst, as it shows
@@ -865,6 +1176,12 @@ class TreeSocket : public InspSocket
                {
                        if ((x->Name == servername) && (x->RecvPass == password))
                        {
+                               TreeServer* CheckDupe = FindServer(servername);
+                               if (CheckDupe)
+                               {
+                                       this->WriteLine("ERROR :Server "+servername+" already exists on server "+CheckDupe->GetParent()->GetName()+"!");
+                                       return false;
+                               }
                                Srv->SendOpers("*** Verified incoming server connection from \002"+servername+"\002["+this->GetIP()+"] ("+description+")");
                                this->InboundServerName = servername;
                                this->InboundDescription = description;
@@ -1066,6 +1383,30 @@ class TreeSocket : public InspSocket
                                {
                                        return this->RemoteRehash(prefix,params);
                                }
+                               else if (command == "PING")
+                               {
+                                       return this->LocalPing(prefix,params);
+                               }
+                               else if (command == "PONG")
+                               {
+                                       return this->LocalPong(prefix,params);
+                               }
+                               else if (command == "VERSION")
+                               {
+                                       return this->ServerVersion(prefix,params);
+                               }
+                               else if (command == "FHOST")
+                               {
+                                       return this->ChangeHost(prefix,params);
+                               }
+                               else if (command == "FNAME")
+                               {
+                                       return this->ChangeName(prefix,params);
+                               }
+                               else if (command == "ADDLINE")
+                               {
+                                       return this->AddLine(prefix,params);
+                               }
                                else if (command == "SQUIT")
                                {
                                        if (params.size() == 2)
@@ -1174,11 +1515,11 @@ void AddThisServer(TreeServer* server, std::deque<TreeServer*> &list)
 }
 
 // returns a list of DIRECT servernames for a specific channel
-std::deque<TreeServer*> GetListOfServersForChannel(chanrec* c)
+void GetListOfServersForChannel(chanrec* c, std::deque<TreeServer*> &list)
 {
-       std::deque<TreeServer*> list;
        std::vector<char*> *ulist = c->GetUsers();
-       for (unsigned int i = 0; i < ulist->size(); i++)
+       unsigned int ucount = ulist->size();
+       for (unsigned int i = 0; i < ucount; i++)
        {
                char* o = (*ulist)[i];
                userrec* otheruser = (userrec*)o;
@@ -1189,7 +1530,7 @@ std::deque<TreeServer*> GetListOfServersForChannel(chanrec* c)
                                AddThisServer(best,list);
                }
        }
-       return list;
+       return;
 }
 
 bool DoOneToAllButSenderRaw(std::string data,std::string omit,std::string prefix,std::string command,std::deque<std::string> params)
@@ -1197,7 +1538,7 @@ bool DoOneToAllButSenderRaw(std::string data,std::string omit,std::string prefix
        TreeServer* omitroute = BestRouteTo(omit);
        if ((command == "NOTICE") || (command == "PRIVMSG"))
        {
-               if (params.size() >= 2)
+               if ((params.size() >= 2) && (*(params[0].c_str()) != '$'))
                {
                        if (*(params[0].c_str()) != '#')
                        {
@@ -1206,7 +1547,6 @@ bool DoOneToAllButSenderRaw(std::string data,std::string omit,std::string prefix
                                if (d)
                                {
                                        std::deque<std::string> par;
-                                       par.clear();
                                        par.push_back(params[0]);
                                        par.push_back(":"+params[1]);
                                        DoOneToOne(prefix,command,par,d->server);
@@ -1219,9 +1559,11 @@ bool DoOneToAllButSenderRaw(std::string data,std::string omit,std::string prefix
                                chanrec* c = Srv->FindChannel(params[0]);
                                if (c)
                                {
-                                       std::deque<TreeServer*> list = GetListOfServersForChannel(c);
+                                       std::deque<TreeServer*> list;
+                                       GetListOfServersForChannel(c,list);
                                        log(DEBUG,"Got a list of %d servers",list.size());
-                                       for (unsigned int i = 0; i < list.size(); i++)
+                                       unsigned int lsize = list.size();
+                                       for (unsigned int i = 0; i < lsize; i++)
                                        {
                                                TreeSocket* Sock = list[i]->GetSocket();
                                                if ((Sock) && (list[i]->GetName() != omit) && (omitroute != list[i]))
@@ -1235,7 +1577,8 @@ bool DoOneToAllButSenderRaw(std::string data,std::string omit,std::string prefix
                        }
                }
        }
-       for (unsigned int x = 0; x < TreeRoot->ChildCount(); x++)
+       unsigned int items = TreeRoot->ChildCount();
+       for (unsigned int x = 0; x < items; x++)
        {
                TreeServer* Route = TreeRoot->GetChild(x);
                if ((Route->GetSocket()) && (Route->GetName() != omit) && (omitroute != Route))
@@ -1251,11 +1594,13 @@ bool DoOneToAllButSender(std::string prefix, std::string command, std::deque<std
 {
        TreeServer* omitroute = BestRouteTo(omit);
        std::string FullLine = ":" + prefix + " " + command;
-       for (unsigned int x = 0; x < params.size(); x++)
+       unsigned int words = params.size();
+       for (unsigned int x = 0; x < words; x++)
        {
                FullLine = FullLine + " " + params[x];
        }
-       for (unsigned int x = 0; x < TreeRoot->ChildCount(); x++)
+       unsigned int items = TreeRoot->ChildCount();
+       for (unsigned int x = 0; x < items; x++)
        {
                TreeServer* Route = TreeRoot->GetChild(x);
                // Send the line IF:
@@ -1274,11 +1619,13 @@ bool DoOneToAllButSender(std::string prefix, std::string command, std::deque<std
 bool DoOneToMany(std::string prefix, std::string command, std::deque<std::string> params)
 {
        std::string FullLine = ":" + prefix + " " + command;
-       for (unsigned int x = 0; x < params.size(); x++)
+       unsigned int words = params.size();
+       for (unsigned int x = 0; x < words; x++)
        {
                FullLine = FullLine + " " + params[x];
        }
-       for (unsigned int x = 0; x < TreeRoot->ChildCount(); x++)
+       unsigned int items = TreeRoot->ChildCount();
+       for (unsigned int x = 0; x < items; x++)
        {
                TreeServer* Route = TreeRoot->GetChild(x);
                if (Route->GetSocket())
@@ -1296,7 +1643,8 @@ bool DoOneToOne(std::string prefix, std::string command, std::deque<std::string>
        if (Route)
        {
                std::string FullLine = ":" + prefix + " " + command;
-               for (unsigned int x = 0; x < params.size(); x++)
+               unsigned int words = params.size();
+               for (unsigned int x = 0; x < words; x++)
                {
                        FullLine = FullLine + " " + params[x];
                }
@@ -1354,16 +1702,19 @@ void ReadConfiguration(bool rebind)
                L.Port = Conf->ReadInteger("link","port",j,true);
                L.SendPass = Conf->ReadValue("link","sendpass",j);
                L.RecvPass = Conf->ReadValue("link","recvpass",j);
+               L.AutoConnect = Conf->ReadInteger("link","autoconnect",j,true);
+               L.NextConnectTime = time(NULL) + L.AutoConnect;
                LinkBlocks.push_back(L);
                log(DEBUG,"m_spanningtree: Read server %s with host %s:%d",L.Name.c_str(),L.IPAddr.c_str(),L.Port);
        }
 }
 
-       
+
 class ModuleSpanningTree : public Module
 {
        std::vector<TreeSocket*> Bindings;
        int line;
+       int NumServers;
 
  public:
 
@@ -1393,6 +1744,27 @@ class ModuleSpanningTree : public Module
                WriteServ(user->fd,"364 %s %s %s :%d %s",user->nick,Current->GetName().c_str(),Parent.c_str(),hops,Current->GetDesc().c_str());
        }
 
+       int CountLocalServs()
+       {
+               return TreeRoot->ChildCount();
+       }
+
+       void CountServsRecursive(TreeServer* Current)
+       {
+               NumServers++;
+               for (unsigned int q = 0; q < Current->ChildCount(); q++)
+               {
+                       CountServsRecursive(Current->GetChild(q));
+               }
+       }
+       
+       int CountServs()
+       {
+               NumServers = 0;
+               CountServsRecursive(TreeRoot);
+               return NumServers;
+       }
+
        void HandleLinks(char** parameters, int pcnt, userrec* user)
        {
                ShowLinks(TreeRoot,user,0);
@@ -1402,6 +1774,11 @@ class ModuleSpanningTree : public Module
 
        void HandleLusers(char** parameters, int pcnt, userrec* user)
        {
+               WriteServ(user->fd,"251 %s :There are %d users and %d invisible on %d servers",user->nick,usercnt()-usercount_invisible(),usercount_invisible(),this->CountServs());
+               WriteServ(user->fd,"252 %s %d :operator(s) online",user->nick,usercount_opers());
+               WriteServ(user->fd,"253 %s %d :unknown connections",user->nick,usercount_unknown());
+               WriteServ(user->fd,"254 %s %d :channels formed",user->nick,chancount());
+               WriteServ(user->fd,"254 %s :I have %d clients and %d servers",user->nick,local_count(),this->CountLocalServs());
                return;
        }
 
@@ -1487,22 +1864,114 @@ class ModuleSpanningTree : public Module
 
        int HandleSquit(char** parameters, int pcnt, userrec* user)
        {
+               TreeServer* s = FindServerMask(parameters[0]);
+               if (s)
+               {
+                       TreeSocket* sock = s->GetSocket();
+                       if (sock)
+                       {
+                               WriteOpers("*** SQUIT: Server \002%s\002 removed from network by %s",parameters[0],user->nick);
+                               sock->Squit(s,"Server quit by "+std::string(user->nick)+"!"+std::string(user->ident)+"@"+std::string(user->host));
+                               sock->Close();
+                       }
+                       else
+                       {
+                               WriteServ(user->fd,"NOTICE %s :*** SQUIT: The server \002%s\002 is not directly connected.",user->nick,parameters[0]);
+                       }
+               }
+               else
+               {
+                        WriteServ(user->fd,"NOTICE %s :*** SQUIT: The server \002%s\002 does not exist on the network.",user->nick,parameters[0]);
+               }
                return 1;
        }
 
+       void DoPingChecks(time_t curtime)
+       {
+               for (unsigned int j = 0; j < TreeRoot->ChildCount(); j++)
+               {
+                       TreeServer* serv = TreeRoot->GetChild(j);
+                       TreeSocket* sock = serv->GetSocket();
+                       if (sock)
+                       {
+                               if (curtime >= serv->NextPingTime())
+                               {
+                                       if (serv->AnsweredLastPing())
+                                       {
+                                               sock->WriteLine(":"+Srv->GetServerName()+" PING "+serv->GetName());
+                                               serv->SetNextPingTime(curtime + 60);
+                                       }
+                                       else
+                                       {
+                                               // they didnt answer, boot them
+                                               WriteOpers("*** Server \002%s\002 pinged out",serv->GetName().c_str());
+                                               sock->Squit(serv,"Ping timeout");
+                                               sock->Close();
+                                               return;
+                                       }
+                               }
+                       }
+               }
+       }
+
+       void AutoConnectServers(time_t curtime)
+       {
+               for (std::vector<Link>::iterator x = LinkBlocks.begin(); x < LinkBlocks.end(); x++)
+               {
+                       if ((x->AutoConnect) && (curtime >= x->NextConnectTime))
+                       {
+                               log(DEBUG,"Auto-Connecting %s",x->Name.c_str());
+                               x->NextConnectTime = curtime + x->AutoConnect;
+                               TreeServer* CheckDupe = FindServer(x->Name);
+                               if (!CheckDupe)
+                               {
+                                       // an autoconnected server is not connected. Check if its time to connect it
+                                       WriteOpers("*** AUTOCONNECT: Auto-connecting server \002%s\002 (%lu seconds until next attempt)",x->Name.c_str(),x->AutoConnect);
+                                       TreeSocket* newsocket = new TreeSocket(x->IPAddr,x->Port,false,10,x->Name);
+                                       Srv->AddSocket(newsocket);
+                               }
+                       }
+               }
+       }
+
+       int HandleVersion(char** parameters, int pcnt, userrec* user)
+       {
+               // we've already checked if pcnt > 0, so this is safe
+               TreeServer* found = FindServerMask(parameters[0]);
+               if (found)
+               {
+                       std::string Version = found->GetVersion();
+                       WriteServ(user->fd,"351 %s :%s",user->nick,Version.c_str());
+               }
+               else
+               {
+                       WriteServ(user->fd,"402 %s %s :No such server",user->nick,parameters[0]);
+               }
+               return 1;
+       }
+       
        int HandleConnect(char** parameters, int pcnt, userrec* user)
        {
                for (std::vector<Link>::iterator x = LinkBlocks.begin(); x < LinkBlocks.end(); x++)
                {
                        if (Srv->MatchText(x->Name.c_str(),parameters[0]))
                        {
-                               WriteServ(user->fd,"NOTICE %s :*** CONNECT: Connecting to server: %s (%s:%d)",user->nick,x->Name.c_str(),x->IPAddr.c_str(),x->Port);
-                               TreeSocket* newsocket = new TreeSocket(x->IPAddr,x->Port,false,10,x->Name);
-                               Srv->AddSocket(newsocket);
-                               return 1;
+                               TreeServer* CheckDupe = FindServer(x->Name);
+                               if (!CheckDupe)
+                               {
+                                       WriteServ(user->fd,"NOTICE %s :*** CONNECT: Connecting to server: \002%s\002 (%s:%d)",user->nick,x->Name.c_str(),x->IPAddr.c_str(),x->Port);
+                                       TreeSocket* newsocket = new TreeSocket(x->IPAddr,x->Port,false,10,x->Name);
+                                       Srv->AddSocket(newsocket);
+                                       return 1;
+                               }
+                               else
+                               {
+                                       WriteServ(user->fd,"NOTICE %s :*** CONNECT: Server \002%s\002 already exists on the network and is connected via \002%s\002",user->nick,x->Name.c_str(),CheckDupe->GetParent()->GetName().c_str());
+                                       return 1;
+                               }
                        }
                }
-               WriteServ(user->fd,"NOTICE %s :*** CONNECT: No matching server could be found in the config file.",user->nick);
+               WriteServ(user->fd,"NOTICE %s :*** CONNECT: No server matching \002%s\002 could be found in the config file.",user->nick,parameters[0]);
                return 1;
        }
 
@@ -1531,6 +2000,11 @@ class ModuleSpanningTree : public Module
                        this->HandleLinks(parameters,pcnt,user);
                        return 1;
                }
+               else if ((command == "VERSION") && (pcnt > 0))
+               {
+                       this->HandleVersion(parameters,pcnt,user);
+                       return 1;
+               }
                else if (Srv->IsValidModuleCommand(command, pcnt, user))
                {
                        // this bit of code cleverly routes all module commands
@@ -1584,6 +2058,16 @@ class ModuleSpanningTree : public Module
                DoOneToMany(user->nick,"TOPIC",params);
        }
 
+       virtual void OnWallops(userrec* user, std::string text)
+       {
+               if (std::string(user->server) == Srv->GetServerName())
+               {
+                       std::deque<std::string> params;
+                       params.push_back(":"+text);
+                       DoOneToMany(user->nick,"WALLOPS",params);
+               }
+       }
+
        virtual void OnUserNotice(userrec* user, void* dest, int target_type, std::string text)
        {
                if (target_type == TYPE_USER)
@@ -1603,8 +2087,10 @@ class ModuleSpanningTree : public Module
                        if (std::string(user->server) == Srv->GetServerName())
                        {
                                chanrec *c = (chanrec*)dest;
-                               std::deque<TreeServer*> list = GetListOfServersForChannel(c);
-                               for (unsigned int i = 0; i < list.size(); i++)
+                               std::deque<TreeServer*> list;
+                               GetListOfServersForChannel(c,list);
+                               unsigned int ucount = list.size();
+                               for (unsigned int i = 0; i < ucount; i++)
                                {
                                        TreeSocket* Sock = list[i]->GetSocket();
                                        if (Sock)
@@ -1635,8 +2121,10 @@ class ModuleSpanningTree : public Module
                        if (std::string(user->server) == Srv->GetServerName())
                        {
                                chanrec *c = (chanrec*)dest;
-                               std::deque<TreeServer*> list = GetListOfServersForChannel(c);
-                               for (unsigned int i = 0; i < list.size(); i++)
+                               std::deque<TreeServer*> list;
+                               GetListOfServersForChannel(c,list);
+                               unsigned int ucount = list.size();
+                               for (unsigned int i = 0; i < ucount; i++)
                                {
                                        TreeSocket* Sock = list[i]->GetSocket();
                                        if (Sock)
@@ -1646,6 +2134,12 @@ class ModuleSpanningTree : public Module
                }
        }
 
+       virtual void OnBackgroundTimer(time_t curtime)
+       {
+               AutoConnectServers(curtime);
+               DoPingChecks(curtime);
+       }
+
        virtual void OnUserJoin(userrec* user, chanrec* channel)
        {
                // Only do this for local users
@@ -1659,16 +2153,47 @@ class ModuleSpanningTree : public Module
                                // if the channel has a key, force the join by emulating the key.
                                params.push_back(channel->key);
                        }
-                       DoOneToMany(user->nick,"JOIN",params);
+                       if (channel->GetUserCounter() > 1)
+                       {
+                               // not the first in the channel
+                               DoOneToMany(user->nick,"JOIN",params);
+                       }
+                       else
+                       {
+                               // first in the channel, set up their permissions
+                               // and the channel TS with FJOIN.
+                               char ts[24];
+                               snprintf(ts,24,"%lu",(unsigned long)channel->age);
+                               params.clear();
+                               params.push_back(channel->name);
+                               params.push_back(ts);
+                               params.push_back("@"+std::string(user->nick));
+                               DoOneToMany(Srv->GetServerName(),"FJOIN",params);
+                       }
                }
        }
 
+       virtual void OnChangeHost(userrec* user, std::string newhost)
+       {
+               // only occurs for local clients
+               std::deque<std::string> params;
+               params.push_back(newhost);
+               DoOneToMany(user->nick,"FHOST",params);
+       }
+
+       virtual void OnChangeName(userrec* user, std::string gecos)
+       {
+               // only occurs for local clients
+               std::deque<std::string> params;
+               params.push_back(gecos);
+               DoOneToMany(user->nick,"FNAME",params);
+       }
+
        virtual void OnUserPart(userrec* user, chanrec* channel)
        {
                if (std::string(user->server) == Srv->GetServerName())
                {
                        std::deque<std::string> params;
-                       params.clear();
                        params.push_back(channel->name);
                        DoOneToMany(user->nick,"PART",params);
                }
@@ -1681,7 +2206,6 @@ class ModuleSpanningTree : public Module
                {
                        std::deque<std::string> params;
                        snprintf(agestr,MAXBUF,"%lu",(unsigned long)user->age);
-                       params.clear();
                        params.push_back(agestr);
                        params.push_back(user->nick);
                        params.push_back(user->host);
@@ -1764,6 +2288,72 @@ class ModuleSpanningTree : public Module
                }
        }
 
+       void OnLine(userrec* source, std::string host, bool adding, char linetype, long duration, std::string reason)
+       {
+               if (std::string(source->server) == Srv->GetServerName())
+               {
+                       char type[8];
+                       snprintf(type,8,"%cLINE",linetype);
+                       std::string stype = type;
+                       if (adding)
+                       {
+                               char sduration[MAXBUF];
+                               snprintf(sduration,MAXBUF,"%ld",duration);
+                               std::deque<std::string> params;
+                               params.push_back(host);
+                               params.push_back(sduration);
+                               params.push_back(":"+reason);
+                               DoOneToMany(source->nick,stype,params);
+                       }
+                       else
+                       {
+                               std::deque<std::string> params;
+                               params.push_back(host);
+                               DoOneToMany(source->nick,stype,params);
+                       }
+               }
+       }
+
+       virtual void OnAddGLine(long duration, userrec* source, std::string reason, std::string hostmask)
+       {
+               OnLine(source,hostmask,true,'G',duration,reason);
+       }
+       
+       virtual void OnAddZLine(long duration, userrec* source, std::string reason, std::string ipmask)
+       {
+               OnLine(source,ipmask,true,'Z',duration,reason);
+       }
+
+       virtual void OnAddQLine(long duration, userrec* source, std::string reason, std::string nickmask)
+       {
+               OnLine(source,nickmask,true,'Q',duration,reason);
+       }
+
+       virtual void OnAddELine(long duration, userrec* source, std::string reason, std::string hostmask)
+       {
+               OnLine(source,hostmask,true,'E',duration,reason);
+       }
+
+       virtual void OnDelGLine(userrec* source, std::string hostmask)
+       {
+               OnLine(source,hostmask,false,'G',0,"");
+       }
+
+       virtual void OnDelZLine(userrec* source, std::string ipmask)
+       {
+               OnLine(source,ipmask,false,'Z',0,"");
+       }
+
+       virtual void OnDelQLine(userrec* source, std::string nickmask)
+       {
+               OnLine(source,nickmask,false,'Q',0,"");
+       }
+
+       virtual void OnDelELine(userrec* source, std::string hostmask)
+       {
+               OnLine(source,hostmask,false,'E',0,"");
+       }
+
        virtual void OnMode(userrec* user, void* dest, int target_type, std::string text)
        {
                if (std::string(user->server) == Srv->GetServerName())
@@ -1841,4 +2431,3 @@ extern "C" void * init_module( void )
 {
        return new ModuleSpanningTreeFactory;
 }
-