]> git.netwichtig.de Git - user/henk/code/inspircd.git/blobdiff - src/modules/m_spanningtree.cpp
Fix for rehash not reading new link blocks
[user/henk/code/inspircd.git] / src / modules / m_spanningtree.cpp
index f5da4c51c622e37babd6311df59fbb413a6d78ab..e566fe210e6b542e406cd9dde1c273d8e2b713ee 100644 (file)
@@ -45,6 +45,27 @@ using namespace std;
 #define nspace std
 #endif
 
+/*
+ * The server list in InspIRCd is maintained as two structures
+ * which hold the data in different ways. Most of the time, we
+ * want to very quicky obtain three pieces of information:
+ *
+ * (1) The information on a server
+ * (2) The information on the server we must send data through
+ *     to actually REACH the server we're after
+ * (3) Potentially, the child/parent objects of this server
+ *
+ * The InspIRCd spanning protocol provides easy access to these
+ * by storing the data firstly in a recursive structure, where
+ * each item references its parent item, and a dynamic list
+ * of child items, and another structure which stores the items
+ * hashed, linearly. This means that if we want to find a server
+ * by name quickly, we can look it up in the hash, avoiding
+ * any O(n) lookups. If however, during a split or sync, we want
+ * to apply an operation to a server, and any of its child objects
+ * we can resort to recursion to walk the tree structure.
+ */
+
 class ModuleSpanningTree;
 static ModuleSpanningTree* TreeProtocolModule;
 
@@ -52,50 +73,93 @@ extern std::vector<Module*> modules;
 extern std::vector<ircd_module*> factory;
 extern int MODCOUNT;
 
+/* Any socket can have one of five states at any one time.
+ * The LISTENER state indicates a socket which is listening
+ * for connections. It cannot receive data itself, only incoming
+ * sockets.
+ * The CONNECTING state indicates an outbound socket which is
+ * waiting to be writeable.
+ * The WAIT_AUTH_1 state indicates the socket is outbound and
+ * has successfully connected, but has not yet sent and received
+ * SERVER strings.
+ * The WAIT_AUTH_2 state indicates that the socket is inbound
+ * (allocated by a LISTENER) but has not yet sent and received
+ * SERVER strings.
+ * The CONNECTED state represents a fully authorized, fully
+ * connected server.
+ */
 enum ServerState { LISTENER, CONNECTING, WAIT_AUTH_1, WAIT_AUTH_2, CONNECTED };
 
+/* We need to import these from the core for use in netbursts */
 typedef nspace::hash_map<std::string, userrec*, nspace::hash<string>, irc::StrHashComp> user_hash;
 typedef nspace::hash_map<std::string, chanrec*, nspace::hash<string>, irc::StrHashComp> chan_hash;
-
 extern user_hash clientlist;
 extern chan_hash chanlist;
 
+/* Foward declarations */
 class TreeServer;
 class TreeSocket;
 
+/* This variable represents the root of the server tree
+ * (for all intents and purposes, it's us)
+ */
 TreeServer *TreeRoot;
 
+/* This hash_map holds the hash equivalent of the server
+ * tree, used for rapid linear lookups.
+ */
 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);
+/* More forward declarations */
+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);
 
+/* Imported from xline.cpp for use during netburst */
 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;
 
+
+/* Each server in the tree is represented by one class of
+ * type TreeServer. A locally connected TreeServer can
+ * have a class of type TreeSocket associated with it, for
+ * remote servers, the TreeSocket entry will be NULL.
+ * Each server also maintains a pointer to its parent
+ * (NULL if this server is ours, at the top of the tree)
+ * and a pointer to its "Route" (see the comments in the
+ * constructors below), and also a dynamic list of pointers
+ * to its children which can be iterated recursively
+ * if required. Creating or deleting objects of type
+ * TreeServer automatically maintains the hash_map of
+ * TreeServer items, deleting and inserting them as they
+ * are created and destroyed.
+ */
+
 class TreeServer
 {
-       TreeServer* Parent;
-       TreeServer* Route;
-       std::vector<TreeServer*> Children;
-       std::string ServerName;
-       std::string ServerDesc;
-       std::string VersionString;
-       int UserCount;
-       int OperCount;
-       TreeSocket* Socket;     // for directly connected servers this points at the socket object
-       time_t NextPing;
-       bool LastPingWasGood;
+       TreeServer* Parent;                     /* Parent entry */
+       TreeServer* Route;                      /* Route entry */
+       std::vector<TreeServer*> Children;      /* List of child objects */
+       std::string ServerName;                 /* Server's name */
+       std::string ServerDesc;                 /* Server's description */
+       std::string VersionString;              /* Version string or empty string */
+       int UserCount;                          /* Not used in this version */
+       int OperCount;                          /* Not used in this version */
+       TreeSocket* Socket;                     /* For directly connected servers this points at the socket object */
+       time_t NextPing;                        /* After this time, the server should be PINGed*/
+       bool LastPingWasGood;                   /* True if the server responded to the last PING with a PONG */
        
  public:
 
+       /* We don't use this constructor. Its a dummy, and won't cause any insertion
+        * of the TreeServer into the hash_map. See below for the two we DO use.
+        */
        TreeServer()
        {
                Parent = NULL;
@@ -106,6 +170,10 @@ class TreeServer
                VersionString = GetVersionString();
        }
 
+       /* We use this constructor only to create the 'root' item, TreeRoot, which
+        * represents our own server. Therefore, it has no route, no parent, and
+        * no socket associated with it. Its version string is our own local version.
+        */
        TreeServer(std::string Name, std::string Desc) : ServerName(Name), ServerDesc(Desc)
        {
                Parent = NULL;
@@ -116,6 +184,10 @@ class TreeServer
                AddHashEntry();
        }
 
+       /* When we create a new server, we call this constructor to initialize it.
+        * This constructor initializes the server's Route and Parent, and sets up
+        * its ping counters so that it will be pinged one minute from now.
+        */
        TreeServer(std::string Name, std::string Desc, TreeServer* Above, TreeSocket* Sock) : Parent(Above), ServerName(Name), ServerDesc(Desc), Socket(Sock)
        {
                VersionString = "";
@@ -177,6 +249,10 @@ class TreeServer
                this->AddHashEntry();
        }
 
+       /* This method is used to add the structure to the
+        * hash_map for linear searches. It is only called
+        * by the constructors.
+        */
        void AddHashEntry()
        {
                server_hash::iterator iter;
@@ -185,6 +261,10 @@ class TreeServer
                        serverlist[this->ServerName] = this;
        }
 
+       /* This method removes the reference to this object
+        * from the hash_map which is used for linear searches.
+        * It is only called by the default destructor.
+        */
        void DelHashEntry()
        {
                server_hash::iterator iter;
@@ -193,6 +273,10 @@ class TreeServer
                        serverlist.erase(iter);
        }
 
+       /* These accessors etc should be pretty self-
+        * explanitory.
+        */
+
        TreeServer* GetRoute()
        {
                return Route;
@@ -268,6 +352,11 @@ class TreeServer
        {
                if (n < Children.size())
                {
+                       /* Make sure they  cant request
+                        * an out-of-range object. After
+                        * all we know what these programmer
+                        * types are like *grin*.
+                        */
                        return Children[n];
                }
                else
@@ -319,10 +408,18 @@ class TreeServer
 
        ~TreeServer()
        {
+               /* We'd better tidy up after ourselves, eh? */
                this->DelHashEntry();
        }
 };
 
+/* The Link class might as well be a struct,
+ * but this is C++ and we don't believe in structs (!).
+ * It holds the entire information of one <link>
+ * tag from the main config file. We maintain a list
+ * of them, and populate the list on rehash/load.
+ */
+
 class Link
 {
  public:
@@ -335,10 +432,21 @@ class Link
         time_t NextConnectTime;
 };
 
+/* The usual stuff for inspircd modules,
+ * plus the vector of Link classes which we
+ * use to store the <link> tags from the config
+ * file.
+ */
 Server *Srv;
 ConfigReader *Conf;
 std::vector<Link> LinkBlocks;
 
+/* Yay for fast searches!
+ * This is hundreds of times faster than recursion
+ * or even scanning a linked list, especially when
+ * there are more than a few servers to deal with.
+ * (read as: lots).
+ */
 TreeServer* FindServer(std::string ServerName)
 {
        server_hash::iterator iter;
@@ -374,39 +482,40 @@ TreeServer* BestRouteTo(std::string ServerName)
        }
 }
 
-TreeServer* Found;
-
-/* TODO: These need optimizing to use an iterator of serverlist
+/* Find the first server matching a given glob mask.
+ * Theres no find-using-glob method of hash_map [awwww :-(]
+ * so instead, we iterate over the list using an iterator
+ * and match each one until we get a hit. Yes its slow,
+ * deal with it.
  */
-void RFindServerMask(TreeServer* Current, std::string ServerName)
+TreeServer* FindServerMask(std::string ServerName)
 {
-       if (Srv->MatchText(Current->GetName(),ServerName) && (!Found))
+       for (server_hash::iterator i = serverlist.begin(); i != serverlist.end(); i++)
        {
-               Found = Current;
-               return;
-       }
-       if (!Found)
-       {
-               for (unsigned int q = 0; q < Current->ChildCount(); q++)
-               {
-                       if (!Found)
-                               RFindServerMask(Current->GetChild(q),ServerName);
-               }
+               if (Srv->MatchText(i->first,ServerName))
+                       return i->second;
        }
+       return NULL;
 }
 
-TreeServer* FindServerMask(std::string ServerName)
-{
-       Found = NULL;
-       RFindServerMask(TreeRoot,ServerName);
-       return Found;
-}
-
+/* A convenient wrapper that returns true if a server exists */
 bool IsServer(std::string ServerName)
 {
        return (FindServer(ServerName) != NULL);
 }
 
+/* Every SERVER connection inbound or outbound is represented by
+ * an object of type TreeSocket.
+ * TreeSockets, being inherited from InspSocket, can be tied into
+ * the core socket engine, and we cn therefore receive activity events
+ * for them, just like activex objects on speed. (yes really, that
+ * is a technical term!) Each of these which relates to a locally
+ * connected server is assocated with it, by hooking it onto a
+ * TreeSocket class using its constructor. In this way, we can
+ * maintain a list of servers, some of which are directly connected,
+ * some of which are not.
+ */
+
 class TreeSocket : public InspSocket
 {
        std::string myhost;
@@ -421,6 +530,11 @@ class TreeSocket : public InspSocket
        
  public:
 
+       /* Because most of the I/O gubbins are encapsulated within
+        * InspSocket, we just call the superclass constructor for
+        * most of the action, and append a few of our own values
+        * to it.
+        */
        TreeSocket(std::string host, int port, bool listening, unsigned long maxtime)
                : InspSocket(host, port, listening, maxtime)
        {
@@ -435,43 +549,71 @@ class TreeSocket : public InspSocket
                this->LinkState = CONNECTING;
        }
 
+       /* When a listening socket gives us a new file descriptor,
+        * we must associate it with a socket without creating a new
+        * connection. This constructor is used for this purpose.
+        */
        TreeSocket(int newfd, char* ip)
                : InspSocket(newfd, ip)
        {
                this->LinkState = WAIT_AUTH_1;
        }
        
+       /* When an outbound connection finishes connecting, we receive
+        * this event, and must send our SERVER string to the other
+        * side. If the other side is happy, as outlined in the server
+        * to server docs on the inspircd.org site, the other side
+        * will then send back its own server string.
+        */
         virtual bool OnConnected()
        {
                if (this->LinkState == CONNECTING)
                {
                        Srv->SendOpers("*** Connection to "+myhost+"["+this->GetIP()+"] established.");
-                       // we should send our details here.
-                       // if the other side is satisfied, they send theirs.
-                       // we do not need to change state here.
+                       /* we do not need to change state here. */
                        for (std::vector<Link>::iterator x = LinkBlocks.begin(); x < LinkBlocks.end(); x++)
                        {
                                if (x->Name == this->myhost)
                                {
-                                       // found who we're supposed to be connecting to, send the neccessary gubbins.
+                                       /* found who we're supposed to be connecting to, send the neccessary gubbins. */
                                        this->WriteLine("SERVER "+Srv->GetServerName()+" "+x->SendPass+" 0 :"+Srv->GetServerDescription());
                                        return true;
                                }
                        }
                }
+               /* There is a (remote) chance that between the /CONNECT and the connection
+                * being accepted, some muppet has removed the <link> block and rehashed.
+                * If that happens the connection hangs here until it's closed. Unlikely
+                * and rather harmless.
+                */
                return true;
        }
        
         virtual void OnError(InspSocketError e)
        {
+               /* We don't handle this method, because all our
+                * dirty work is done in OnClose() (see below)
+                * which is still called on error conditions too.
+                */
        }
 
         virtual int OnDisconnect()
        {
+               /* For the same reason as above, we don't
+                * handle OnDisconnect()
+                */
                return true;
        }
 
-       // recursively send the server tree with distances as hops
+       /* Recursively send the server tree with distances as hops.
+        * This is used during network burst to inform the other server
+        * (and any of ITS servers too) of what servers we know about.
+        * If at any point any of these servers already exist on the other
+        * end, our connection may be terminated. The hopcounts given
+        * by this function are relative, this doesn't matter so long as
+        * they are all >1, as all the remote servers re-calculate them
+        * to be relative too, with themselves as hop 0.
+        */
        void SendServers(TreeServer* Current, TreeServer* s, int hops)
        {
                char command[1024];
@@ -480,29 +622,43 @@ class TreeSocket : public InspSocket
                        TreeServer* recursive_server = Current->GetChild(q);
                        if (recursive_server != s)
                        {
-                               // :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
+                               /* down to next level */
                                this->SendServers(recursive_server, s, hops+1);
                        }
                }
        }
 
+       /* 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
+        * is having a REAL bad hair day, this function shouldnt be called
+        * too many times a month ;-)
+        */
        void SquitServer(TreeServer* Current)
        {
-               // recursively squit the servers attached to 'Current'
+               /* recursively squit the servers attached to 'Current'.
+                * We're going backwards so we don't remove users
+                * while we still need them ;)
+                */
                for (unsigned int q = 0; q < Current->ChildCount(); q++)
                {
                        TreeServer* recursive_server = Current->GetChild(q);
                        this->SquitServer(recursive_server);
                }
-               // Now we've whacked the kids, whack self
+               /* Now we've whacked the kids, whack self */
                num_lost_servers++;
                bool quittingpeople = true;
                while (quittingpeople)
                {
+                       /* Yup i know, "ew". We cant continue to loop through the
+                        * iterator if we modify it, so whenever we modify it with a
+                        * QUIT we have to start alllll over again. If anyone knows
+                        * a better faster way of *safely* doing this, please let me
+                        * know!
+                        */
                        quittingpeople = false;
                        for (user_hash::iterator u = clientlist.begin(); u != clientlist.end(); u++)
                        {
@@ -517,6 +673,10 @@ class TreeSocket : public InspSocket
                }
        }
 
+       /* This is a wrapper function for SquitServer above, which
+        * does some validation first and passes on the SQUIT to all
+        * other remaining servers.
+        */
        void Squit(TreeServer* Current,std::string reason)
        {
                if (Current)
@@ -547,6 +707,7 @@ class TreeSocket : public InspSocket
                }
        }
 
+       /* FMODE command */
        bool ForceMode(std::string source, std::deque<std::string> params)
        {
                userrec* who = new userrec;
@@ -564,9 +725,9 @@ class TreeSocket : public InspSocket
                return true;
        }
 
+       /* FTOPIC command */
        bool ForceTopic(std::string source, std::deque<std::string> params)
        {
-               // FTOPIC %s %lu %s :%s
                if (params.size() != 4)
                        return true;
                std::string channel = params[0];
@@ -583,22 +744,24 @@ class TreeSocket : public InspSocket
                                strlcpy(c->topic,topic.c_str(),MAXTOPIC);
                                strlcpy(c->setby,setby.c_str(),NICKMAX);
                                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 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);
                        }
                        
                }
                
-               // all done, send it on its way
+               /* all done, send it on its way */
                params[3] = ":" + params[3];
                DoOneToAllButSender(source,"FTOPIC",params,source);
 
                return true;
        }
 
+       /* FJOIN, similar to unreal SJOIN */
        bool ForceJoin(std::string source, std::deque<std::string> params)
        {
                if (params.size() < 3)
@@ -624,8 +787,9 @@ class TreeSocket : public InspSocket
                }
                strlcpy(mode_users[0],channel.c_str(),MAXBUF);
 
-               // default is a high value, which if we dont have this
-               // channel will let the other side apply their modes.
+               /* 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)
@@ -635,13 +799,14 @@ class TreeSocket : public InspSocket
 
                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.
+               /* 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.
+                       /* process one channel at a time, applying modes. */
                        char* usr = (char*)params[usernum].c_str();
                        char permissions = *usr;
                        switch (permissions)
@@ -668,8 +833,9 @@ class TreeSocket : public InspSocket
                                Srv->JoinUserToChannel(who,channel,key);
                                if (modectr >= (MAXMODES-1))
                                {
-                                       // theres a mode for this user. push them onto the mode queue, and flush it
-                                       // if there are more than MAXMODES to go.
+                                       /* theres a mode for this user. push them onto the mode queue, and flush it
+                                        * if there are more than MAXMODES to go.
+                                        */
                                        if (ourTS >= TS)
                                        {
                                                log(DEBUG,"Our our channel newer than theirs, accepting their modes");
@@ -678,9 +844,9 @@ class TreeSocket : public InspSocket
                                        else
                                        {
                                                log(DEBUG,"Their channel newer than ours, bouncing their modes");
-                                               // bouncy bouncy!
+                                               /* bouncy bouncy! */
                                                std::deque<std::string> params;
-                                               // modes are now being UNSET...
+                                               /* modes are now being UNSET... */
                                                *mode_users[1] = '-';
                                                for (unsigned int x = 0; x < modectr; x++)
                                                {
@@ -694,8 +860,9 @@ class TreeSocket : public InspSocket
                                }
                        }
                }
-               // there werent enough modes built up to flush it during FJOIN,
-               // or, there are a number left over. flush them out.
+               /* there werent enough modes built up to flush it during FJOIN,
+                * or, there are a number left over. flush them out.
+                */
                if ((modectr > 2) && (who))
                {
                        if (ourTS >= TS)
@@ -718,6 +885,7 @@ class TreeSocket : public InspSocket
                return true;
        }
 
+       /* NICK command */
        bool IntroduceClient(std::string source, std::deque<std::string> params)
        {
                if (params.size() < 8)
@@ -773,6 +941,10 @@ class TreeSocket : public InspSocket
                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.
+        */
        void SendFJoins(TreeServer* Current, chanrec* c)
        {
                char list[MAXBUF];
@@ -797,10 +969,11 @@ class TreeSocket : public InspSocket
                }
        }
 
+       /* Send G, Q, Z and E lines */
        void SendXLines(TreeServer* Current)
        {
                char data[MAXBUF];
-               // for zlines and qlines, we should first check if theyre global...
+               /* Yes, these arent too nice looking, but they get the job done */
                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);
@@ -823,6 +996,7 @@ class TreeSocket : public InspSocket
                }
        }
 
+       /* Send channel modes and topics */
        void SendChannelModes(TreeServer* Current)
        {
                char data[MAXBUF];
@@ -845,7 +1019,7 @@ class TreeSocket : public InspSocket
                }
        }
 
-       // send all users and their channels
+       /* send all users and their oper state/modes */
        void SendUsers(TreeServer* Current)
        {
                char data[MAXBUF];
@@ -869,28 +1043,45 @@ class TreeSocket : public InspSocket
                }
        }
 
+       /* This function is called when we want to send a netburst to a local
+        * server. There is a set order we must do this, because for example
+        * users require their servers to exist, and channels require their
+        * users to exist. You get the idea.
+        */
        void DoBurst(TreeServer* s)
        {
-               Srv->SendOpers("*** Bursting to "+s->GetName()+".");
+               Srv->SendOpers("*** Bursting to \2"+s->GetName()+"\2.");
                this->WriteLine("BURST");
-               // send our version string
+               /* send our version string */
                this->WriteLine(":"+Srv->GetServerName()+" VERSION :"+GetVersionString());
-               // Send server tree
+               /* Send server tree */
                this->SendServers(TreeRoot,s,1);
-               // Send users and their channels
+               /* Send users and their oper status */
                this->SendUsers(s);
-               // Send everything else (channel modes etc)
+               /* Send everything else (channel modes, xlines etc) */
                this->SendChannelModes(s);
                this->SendXLines(s);
                this->WriteLine("ENDBURST");
+               Srv->SendOpers("*** Finished bursting to \2"+s->GetName()+"\2.");
        }
 
+       /* This function is called when we receive data from a remote
+        * server. We buffer the data in a std::string (it doesnt stay
+        * there for long), reading using InspSocket::Read() which can
+        * read up to 16 kilobytes in one operation.
+        *
+        * IF THIS FUNCTION RETURNS FALSE, THE CORE CLOSES AND DELETES
+        * THE SOCKET OBJECT FOR US.
+        */
         virtual bool OnDataReady()
        {
                char* data = this->Read();
                if (data)
                {
                        this->in_buffer += data;
+                       /* While there is at least one new line in the buffer,
+                        * do something useful (we hope!) with it.
+                        */
                        while (in_buffer.find("\n") != std::string::npos)
                        {
                                char* line = (char*)in_buffer.c_str();
@@ -903,6 +1094,9 @@ class TreeSocket : public InspSocket
                                if ((*line == '\n') || (*line == '\r'))
                                        line++;
                                in_buffer = line;
+                               /* Process this one, abort if it
+                                * didnt return true.
+                                */
                                if (!this->ProcessLine(ret))
                                {
                                        return false;
@@ -917,6 +1111,7 @@ class TreeSocket : public InspSocket
                return this->Write(line + "\r\n");
        }
 
+       /* Handle ERROR command */
        bool Error(std::deque<std::string> params)
        {
                if (params.size() < 1)
@@ -928,11 +1123,15 @@ class TreeSocket : public InspSocket
                        SName = InboundServerName;
                }
                Srv->SendOpers("*** ERROR from "+SName+": "+Errmsg);
-               // we will return false to cause the socket to close.
+               /* we will return false to cause the socket to close.
+                */
                return false;
        }
 
-       bool OperType(std::string prefix, std::deque<std::string> params)
+       /* Because the core won't let users or even SERVERS set +o,
+        * we use the OPERTYPE command to do this.
+        */
+       bool OperType(std::string prefix, std::deque<std::string> &params)
        {
                if (params.size() != 1)
                        return true;
@@ -950,7 +1149,10 @@ class TreeSocket : public InspSocket
                return true;
        }
 
-       bool ForceNick(std::string prefix, std::deque<std::string> params)
+       /* Because Andy insists that services-compatible servers must
+        * implement SVSNICK and SVSJOIN, that's exactly what we do :p
+        */
+       bool ForceNick(std::string prefix, std::deque<std::string> &params)
        {
                if (params.size() < 3)
                        return true;
@@ -964,7 +1166,7 @@ class TreeSocket : public InspSocket
                return true;
        }
 
-       bool ServiceJoin(std::string prefix, std::deque<std::string> params)
+       bool ServiceJoin(std::string prefix, std::deque<std::string> &params)
        {
                if (params.size() < 2)
                        return true;
@@ -977,10 +1179,10 @@ class TreeSocket : public InspSocket
                return true;
        }
 
-       bool RemoteRehash(std::string prefix, std::deque<std::string> params)
+       bool RemoteRehash(std::string prefix, std::deque<std::string> &params)
        {
                if (params.size() < 1)
-                       return true;
+                       return false;
                std::string servermask = params[0];
                if (Srv->MatchText(Srv->GetServerName(),servermask))
                {
@@ -992,21 +1194,26 @@ class TreeSocket : public InspSocket
                return true;
        }
 
-       bool RemoteKill(std::string prefix, std::deque<std::string> params)
+       bool RemoteKill(std::string prefix, std::deque<std::string> &params)
        {
                if (params.size() != 2)
                        return true;
                std::string nick = params[0];
-               std::string reason = params[1];
                userrec* u = Srv->FindNick(prefix);
                userrec* who = Srv->FindNick(nick);
                if (who)
                {
+                       /* Prepend kill source, if we don't have one */
                        std::string sourceserv = prefix;
                        if (u)
                        {
                                sourceserv = u->server;
                        }
+                       if (*(params[1].c_str()) != '[')
+                       {
+                               params[1] = "[" + sourceserv + "] Killed (" + params[1] +")";
+                       }
+                       std::string reason = params[1];
                        params[1] = ":" + params[1];
                        DoOneToAllButSender(prefix,"KILL",params,sourceserv);
                        Srv->QuitUser(who,reason);
@@ -1014,7 +1221,7 @@ class TreeSocket : public InspSocket
                return true;
        }
 
-       bool LocalPong(std::string prefix, std::deque<std::string> params)
+       bool LocalPong(std::string prefix, std::deque<std::string> &params)
        {
                if (params.size() < 1)
                        return true;
@@ -1026,7 +1233,7 @@ class TreeSocket : public InspSocket
                return true;
        }
 
-       bool ServerVersion(std::string prefix, std::deque<std::string> params)
+       bool ServerVersion(std::string prefix, std::deque<std::string> &params)
        {
                if (params.size() < 1)
                        return true;
@@ -1040,7 +1247,7 @@ class TreeSocket : public InspSocket
                return true;
        }
 
-       bool ChangeHost(std::string prefix, std::deque<std::string> params)
+       bool ChangeHost(std::string prefix, std::deque<std::string> &params)
        {
                if (params.size() < 1)
                        return true;
@@ -1053,7 +1260,7 @@ class TreeSocket : public InspSocket
                return true;
        }
 
-       bool AddLine(std::string prefix, std::deque<std::string> params)
+       bool AddLine(std::string prefix, std::deque<std::string> &params)
        {
                if (params.size() < 6)
                        return true;
@@ -1092,7 +1299,7 @@ class TreeSocket : public InspSocket
                return true;
        }
 
-       bool ChangeName(std::string prefix, std::deque<std::string> params)
+       bool ChangeName(std::string prefix, std::deque<std::string> &params)
        {
                if (params.size() < 1)
                        return true;
@@ -1106,7 +1313,7 @@ class TreeSocket : public InspSocket
                return true;
        }
        
-       bool LocalPing(std::string prefix, std::deque<std::string> params)
+       bool LocalPing(std::string prefix, std::deque<std::string> &params)
        {
                if (params.size() < 1)
                        return true;
@@ -1115,7 +1322,7 @@ class TreeSocket : public InspSocket
                return true;
        }
 
-       bool RemoteServer(std::string prefix, std::deque<std::string> params)
+       bool RemoteServer(std::string prefix, std::deque<std::string> &params)
        {
                if (params.size() < 4)
                        return false;
@@ -1143,7 +1350,7 @@ class TreeSocket : public InspSocket
                return true;
        }
 
-       bool Outbound_Reply_Server(std::deque<std::string> params)
+       bool Outbound_Reply_Server(std::deque<std::string> &params)
        {
                if (params.size() < 4)
                        return false;
@@ -1186,7 +1393,7 @@ class TreeSocket : public InspSocket
                return false;
        }
 
-       bool Inbound_Server(std::deque<std::string> params)
+       bool Inbound_Server(std::deque<std::string> &params)
        {
                if (params.size() < 4)
                        return false;
@@ -1224,9 +1431,8 @@ class TreeSocket : public InspSocket
                return false;
        }
 
-       std::deque<std::string> Split(std::string line, bool stripcolon)
+       void Split(std::string line, bool stripcolon, std::deque<std::string> &n)
        {
-               std::deque<std::string> n;
                if (!strchr(line.c_str(),' '))
                {
                        n.push_back(line);
@@ -1284,7 +1490,8 @@ class TreeSocket : public InspSocket
                if (line == "")
                        return true;
                Srv->Log(DEBUG,"IN: '"+line+"'");
-               std::deque<std::string> params = this->Split(line,true);
+               std::deque<std::string> params;
+               this->Split(line,true,params);
                std::string command = "";
                std::string prefix = "";
                if (((params[0].c_str())[0] == ':') && (params.size() > 1))
@@ -1373,7 +1580,18 @@ class TreeSocket : public InspSocket
                                // This is the 'authenticated' state, when all passwords
                                // have been exchanged and anything past this point is taken
                                // as gospel.
+                               if (command == "SVSMODE")
+                               {
+                                       /* Services expects us to implement
+                                        * SVSMODE. In inspircd its the same as
+                                        * MODE anyway.
+                                        */
+                                       command = "MODE";
+                               }
                                std::string target = "";
+                               /* Yes, know, this is a mess. Its reasonably fast though as we're
+                                * working with std::string here.
+                                */
                                if ((command == "NICK") && (params.size() > 1))
                                {
                                        return this->IntroduceClient(prefix,params);
@@ -1576,7 +1794,7 @@ void GetListOfServersForChannel(chanrec* c, std::deque<TreeServer*> &list)
        return;
 }
 
-bool DoOneToAllButSenderRaw(std::string data,std::string omit,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)
 {
        TreeServer* omitroute = BestRouteTo(omit);
        if ((command == "NOTICE") || (command == "PRIVMSG"))
@@ -1633,7 +1851,7 @@ bool DoOneToAllButSenderRaw(std::string data,std::string omit,std::string prefix
        return true;
 }
 
-bool DoOneToAllButSender(std::string prefix, std::string command, std::deque<std::string> params, std::string omit)
+bool DoOneToAllButSender(std::string prefix, std::string command, std::deque<std::string> &params, std::string omit)
 {
        TreeServer* omitroute = BestRouteTo(omit);
        std::string FullLine = ":" + prefix + " " + command;
@@ -1659,7 +1877,7 @@ bool DoOneToAllButSender(std::string prefix, std::string command, std::deque<std
        return true;
 }
 
-bool DoOneToMany(std::string prefix, std::string command, std::deque<std::string> params)
+bool DoOneToMany(std::string prefix, std::string command, std::deque<std::string> &params)
 {
        std::string FullLine = ":" + prefix + " " + command;
        unsigned int words = params.size();
@@ -1680,7 +1898,7 @@ bool DoOneToMany(std::string prefix, std::string command, std::deque<std::string
        return true;
 }
 
-bool DoOneToOne(std::string prefix, std::string command, std::deque<std::string> params, std::string target)
+bool DoOneToOne(std::string prefix, std::string command, std::deque<std::string> &params, std::string target)
 {
        TreeServer* Route = BestRouteTo(target);
        if (Route)
@@ -1708,6 +1926,7 @@ std::vector<TreeSocket*> Bindings;
 
 void ReadConfiguration(bool rebind)
 {
+       Conf = new ConfigReader;
        if (rebind)
        {
                for (int j =0; j < Conf->Enumerate("bind"); j++)
@@ -1750,6 +1969,7 @@ void ReadConfiguration(bool rebind)
                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);
        }
+       delete Conf;
 }
 
 
@@ -1764,7 +1984,6 @@ class ModuleSpanningTree : public Module
        ModuleSpanningTree()
        {
                Srv = new Server;
-               Conf = new ConfigReader;
                Bindings.clear();
 
                // Create the root of the tree
@@ -1792,20 +2011,9 @@ class ModuleSpanningTree : public Module
                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;
+               return serverlist.size();
        }
 
        void HandleLinks(char** parameters, int pcnt, userrec* user)
@@ -2018,6 +2226,20 @@ class ModuleSpanningTree : public Module
                return 1;
        }
 
+       virtual bool HandleStats(char ** parameters, int pcnt, userrec* user)
+       {
+               if (*parameters[0] == 'c')
+               {
+                       for (int i = 0; i < LinkBlocks.size(); i++)
+                       {
+                               WriteServ(user->fd,"213 %s C *@%s * %s %d 0 M",user->nick,LinkBlocks[i].IPAddr,LinkBlocks[i].ServerName,LinkBlocks[i].Port);
+                               WriteServ(user->fd,"244 %s H * * %s",user->nick,LinkBlocks[i].ServerName);
+                       }
+                       return true;
+               }
+               return false;
+       }
+
        virtual int OnPreCommand(std::string command, char **parameters, int pcnt, userrec *user)
        {
                if (command == "CONNECT")
@@ -2028,6 +2250,10 @@ class ModuleSpanningTree : public Module
                {
                        return this->HandleSquit(parameters,pcnt,user);
                }
+               else if (command == "STATS")
+               {
+                       return this->HandleStats(parameters,pcnt,user);
+               }
                else if (command == "MAP")
                {
                        this->HandleMap(parameters,pcnt,user);