]> git.netwichtig.de Git - user/henk/code/inspircd.git/blob - src/modules/m_spanningtree.cpp
Review and optimize
[user/henk/code/inspircd.git] / src / modules / m_spanningtree.cpp
1 /*       +------------------------------------+
2  *       | Inspire Internet Relay Chat Daemon |
3  *       +------------------------------------+
4  *
5  *  Inspire is copyright (C) 2002-2005 ChatSpike-Dev.
6  *                       E-mail:
7  *                <brain@chatspike.net>
8  *                <Craig@chatspike.net>
9  *     
10  * Written by Craig Edwards, Craig McLure, and others.
11  * This program is free but copyrighted software; see
12  *            the file COPYING for details.
13  *
14  * ---------------------------------------------------
15  */
16
17 /* $ModDesc: Povides a spanning tree server link protocol */
18
19 using namespace std;
20
21 #include <stdio.h>
22 #include <vector>
23 #include <deque>
24 #include "globals.h"
25 #include "inspircd_config.h"
26 #ifdef GCC3
27 #include <ext/hash_map>
28 #else
29 #include <hash_map>
30 #endif
31 #include "users.h"
32 #include "channels.h"
33 #include "modules.h"
34 #include "commands.h"
35 #include "socket.h"
36 #include "helperfuncs.h"
37 #include "inspircd.h"
38 #include "inspstring.h"
39 #include "hashcomp.h"
40 #include "message.h"
41 #include "xline.h"
42 #include "typedefs.h"
43 #include "cull_list.h"
44 #include "aes.h"
45
46 #ifdef GCC3
47 #define nspace __gnu_cxx
48 #else
49 #define nspace std
50 #endif
51
52 /*
53  * The server list in InspIRCd is maintained as two structures
54  * which hold the data in different ways. Most of the time, we
55  * want to very quicky obtain three pieces of information:
56  *
57  * (1) The information on a server
58  * (2) The information on the server we must send data through
59  *     to actually REACH the server we're after
60  * (3) Potentially, the child/parent objects of this server
61  *
62  * The InspIRCd spanning protocol provides easy access to these
63  * by storing the data firstly in a recursive structure, where
64  * each item references its parent item, and a dynamic list
65  * of child items, and another structure which stores the items
66  * hashed, linearly. This means that if we want to find a server
67  * by name quickly, we can look it up in the hash, avoiding
68  * any O(n) lookups. If however, during a split or sync, we want
69  * to apply an operation to a server, and any of its child objects
70  * we can resort to recursion to walk the tree structure.
71  */
72
73 class ModuleSpanningTree;
74 static ModuleSpanningTree* TreeProtocolModule;
75
76 extern std::vector<Module*> modules;
77 extern std::vector<ircd_module*> factory;
78 extern int MODCOUNT;
79
80 /* Any socket can have one of five states at any one time.
81  * The LISTENER state indicates a socket which is listening
82  * for connections. It cannot receive data itself, only incoming
83  * sockets.
84  * The CONNECTING state indicates an outbound socket which is
85  * waiting to be writeable.
86  * The WAIT_AUTH_1 state indicates the socket is outbound and
87  * has successfully connected, but has not yet sent and received
88  * SERVER strings.
89  * The WAIT_AUTH_2 state indicates that the socket is inbound
90  * (allocated by a LISTENER) but has not yet sent and received
91  * SERVER strings.
92  * The CONNECTED state represents a fully authorized, fully
93  * connected server.
94  */
95 enum ServerState { LISTENER, CONNECTING, WAIT_AUTH_1, WAIT_AUTH_2, CONNECTED };
96
97 /* We need to import these from the core for use in netbursts */
98 extern user_hash clientlist;
99 extern chan_hash chanlist;
100
101 /* Foward declarations */
102 class TreeServer;
103 class TreeSocket;
104
105 /* This variable represents the root of the server tree
106  * (for all intents and purposes, it's us)
107  */
108 TreeServer *TreeRoot;
109
110 Server* Srv;
111
112 /* This hash_map holds the hash equivalent of the server
113  * tree, used for rapid linear lookups.
114  */
115 typedef nspace::hash_map<std::string, TreeServer*> server_hash;
116 server_hash serverlist;
117
118 /* More forward declarations */
119 bool DoOneToOne(std::string prefix, std::string command, std::deque<std::string> &params, std::string target);
120 bool DoOneToAllButSender(std::string prefix, std::string command, std::deque<std::string> &params, std::string omit);
121 bool DoOneToMany(std::string prefix, std::string command, std::deque<std::string> &params);
122 bool DoOneToAllButSenderRaw(std::string data, std::string omit, std::string prefix, std::string command, std::deque<std::string> &params);
123 void ReadConfiguration(bool rebind);
124
125 /* Imported from xline.cpp for use during netburst */
126 extern std::vector<KLine> klines;
127 extern std::vector<GLine> glines;
128 extern std::vector<ZLine> zlines;
129 extern std::vector<QLine> qlines;
130 extern std::vector<ELine> elines;
131 extern std::vector<KLine> pklines;
132 extern std::vector<GLine> pglines;
133 extern std::vector<ZLine> pzlines;
134 extern std::vector<QLine> pqlines;
135 extern std::vector<ELine> pelines;
136
137 /* Each server in the tree is represented by one class of
138  * type TreeServer. A locally connected TreeServer can
139  * have a class of type TreeSocket associated with it, for
140  * remote servers, the TreeSocket entry will be NULL.
141  * Each server also maintains a pointer to its parent
142  * (NULL if this server is ours, at the top of the tree)
143  * and a pointer to its "Route" (see the comments in the
144  * constructors below), and also a dynamic list of pointers
145  * to its children which can be iterated recursively
146  * if required. Creating or deleting objects of type
147  * TreeServer automatically maintains the hash_map of
148  * TreeServer items, deleting and inserting them as they
149  * are created and destroyed.
150  */
151
152 class TreeServer
153 {
154         TreeServer* Parent;                     /* Parent entry */
155         TreeServer* Route;                      /* Route entry */
156         std::vector<TreeServer*> Children;      /* List of child objects */
157         std::string ServerName;                 /* Server's name */
158         std::string ServerDesc;                 /* Server's description */
159         std::string VersionString;              /* Version string or empty string */
160         int UserCount;                          /* Not used in this version */
161         int OperCount;                          /* Not used in this version */
162         TreeSocket* Socket;                     /* For directly connected servers this points at the socket object */
163         time_t NextPing;                        /* After this time, the server should be PINGed*/
164         bool LastPingWasGood;                   /* True if the server responded to the last PING with a PONG */
165         
166  public:
167
168         /* We don't use this constructor. Its a dummy, and won't cause any insertion
169          * of the TreeServer into the hash_map. See below for the two we DO use.
170          */
171         TreeServer()
172         {
173                 Parent = NULL;
174                 ServerName = "";
175                 ServerDesc = "";
176                 VersionString = "";
177                 UserCount = OperCount = 0;
178                 VersionString = Srv->GetVersion();
179         }
180
181         /* We use this constructor only to create the 'root' item, TreeRoot, which
182          * represents our own server. Therefore, it has no route, no parent, and
183          * no socket associated with it. Its version string is our own local version.
184          */
185         TreeServer(std::string Name, std::string Desc) : ServerName(Name), ServerDesc(Desc)
186         {
187                 Parent = NULL;
188                 VersionString = "";
189                 UserCount = OperCount = 0;
190                 VersionString = Srv->GetVersion();
191                 Route = NULL;
192                 Socket = NULL; /* Fix by brain */
193                 AddHashEntry();
194         }
195
196         /* When we create a new server, we call this constructor to initialize it.
197          * This constructor initializes the server's Route and Parent, and sets up
198          * its ping counters so that it will be pinged one minute from now.
199          */
200         TreeServer(std::string Name, std::string Desc, TreeServer* Above, TreeSocket* Sock) : Parent(Above), ServerName(Name), ServerDesc(Desc), Socket(Sock)
201         {
202                 VersionString = "";
203                 UserCount = OperCount = 0;
204                 this->SetNextPingTime(time(NULL) + 60);
205                 this->SetPingFlag();
206
207                 /* find the 'route' for this server (e.g. the one directly connected
208                  * to the local server, which we can use to reach it)
209                  *
210                  * In the following example, consider we have just added a TreeServer
211                  * class for server G on our network, of which we are server A.
212                  * To route traffic to G (marked with a *) we must send the data to
213                  * B (marked with a +) so this algorithm initializes the 'Route'
214                  * value to point at whichever server traffic must be routed through
215                  * to get here. If we were to try this algorithm with server B,
216                  * the Route pointer would point at its own object ('this').
217                  *
218                  *              A
219                  *             / \
220                  *          + B   C
221                  *           / \   \
222                  *          D   E   F
223                  *         /         \
224                  *      * G           H
225                  *
226                  * We only run this algorithm when a server is created, as
227                  * the routes remain constant while ever the server exists, and
228                  * do not need to be re-calculated.
229                  */
230
231                 Route = Above;
232                 if (Route == TreeRoot)
233                 {
234                         Route = this;
235                 }
236                 else
237                 {
238                         while (this->Route->GetParent() != TreeRoot)
239                         {
240                                 this->Route = Route->GetParent();
241                         }
242                 }
243
244                 /* Because recursive code is slow and takes a lot of resources,
245                  * we store two representations of the server tree. The first
246                  * is a recursive structure where each server references its
247                  * children and its parent, which is used for netbursts and
248                  * netsplits to dump the whole dataset to the other server,
249                  * and the second is used for very fast lookups when routing
250                  * messages and is instead a hash_map, where each item can
251                  * be referenced by its server name. The AddHashEntry()
252                  * call below automatically inserts each TreeServer class
253                  * into the hash_map as it is created. There is a similar
254                  * maintainance call in the destructor to tidy up deleted
255                  * servers.
256                  */
257
258                 this->AddHashEntry();
259         }
260
261         /* This method is used to add the structure to the
262          * hash_map for linear searches. It is only called
263          * by the constructors.
264          */
265         void AddHashEntry()
266         {
267                 server_hash::iterator iter;
268                 iter = serverlist.find(this->ServerName);
269                 if (iter == serverlist.end())
270                         serverlist[this->ServerName] = this;
271         }
272
273         /* This method removes the reference to this object
274          * from the hash_map which is used for linear searches.
275          * It is only called by the default destructor.
276          */
277         void DelHashEntry()
278         {
279                 server_hash::iterator iter;
280                 iter = serverlist.find(this->ServerName);
281                 if (iter != serverlist.end())
282                         serverlist.erase(iter);
283         }
284
285         /* These accessors etc should be pretty self-
286          * explanitory.
287          */
288
289         TreeServer* GetRoute()
290         {
291                 return Route;
292         }
293
294         std::string GetName()
295         {
296                 return ServerName;
297         }
298
299         std::string GetDesc()
300         {
301                 return ServerDesc;
302         }
303
304         std::string GetVersion()
305         {
306                 return VersionString;
307         }
308
309         void SetNextPingTime(time_t t)
310         {
311                 this->NextPing = t;
312                 LastPingWasGood = false;
313         }
314
315         time_t NextPingTime()
316         {
317                 return NextPing;
318         }
319
320         bool AnsweredLastPing()
321         {
322                 return LastPingWasGood;
323         }
324
325         void SetPingFlag()
326         {
327                 LastPingWasGood = true;
328         }
329
330         int GetUserCount()
331         {
332                 return UserCount;
333         }
334
335         int GetOperCount()
336         {
337                 return OperCount;
338         }
339
340         TreeSocket* GetSocket()
341         {
342                 return Socket;
343         }
344
345         TreeServer* GetParent()
346         {
347                 return Parent;
348         }
349
350         void SetVersion(std::string Version)
351         {
352                 VersionString = Version;
353         }
354
355         unsigned int ChildCount()
356         {
357                 return Children.size();
358         }
359
360         TreeServer* GetChild(unsigned int n)
361         {
362                 if (n < Children.size())
363                 {
364                         /* Make sure they  cant request
365                          * an out-of-range object. After
366                          * all we know what these programmer
367                          * types are like *grin*.
368                          */
369                         return Children[n];
370                 }
371                 else
372                 {
373                         return NULL;
374                 }
375         }
376
377         void AddChild(TreeServer* Child)
378         {
379                 Children.push_back(Child);
380         }
381
382         bool DelChild(TreeServer* Child)
383         {
384                 for (std::vector<TreeServer*>::iterator a = Children.begin(); a < Children.end(); a++)
385                 {
386                         if (*a == Child)
387                         {
388                                 Children.erase(a);
389                                 return true;
390                         }
391                 }
392                 return false;
393         }
394
395         /* Removes child nodes of this node, and of that node, etc etc.
396          * This is used during netsplits to automatically tidy up the
397          * server tree. It is slow, we don't use it for much else.
398          */
399         bool Tidy()
400         {
401                 bool stillchildren = true;
402                 while (stillchildren)
403                 {
404                         stillchildren = false;
405                         for (std::vector<TreeServer*>::iterator a = Children.begin(); a < Children.end(); a++)
406                         {
407                                 TreeServer* s = (TreeServer*)*a;
408                                 s->Tidy();
409                                 Children.erase(a);
410                                 delete s;
411                                 stillchildren = true;
412                                 break;
413                         }
414                 }
415                 return true;
416         }
417
418         ~TreeServer()
419         {
420                 /* We'd better tidy up after ourselves, eh? */
421                 this->DelHashEntry();
422         }
423 };
424
425 /* The Link class might as well be a struct,
426  * but this is C++ and we don't believe in structs (!).
427  * It holds the entire information of one <link>
428  * tag from the main config file. We maintain a list
429  * of them, and populate the list on rehash/load.
430  */
431
432 class Link
433 {
434  public:
435          std::string Name;
436          std::string IPAddr;
437          int Port;
438          std::string SendPass;
439          std::string RecvPass;
440          unsigned long AutoConnect;
441          time_t NextConnectTime;
442          std::string EncryptionKey;
443 };
444
445 /* The usual stuff for inspircd modules,
446  * plus the vector of Link classes which we
447  * use to store the <link> tags from the config
448  * file.
449  */
450 ConfigReader *Conf;
451 std::vector<Link> LinkBlocks;
452
453 /* Yay for fast searches!
454  * This is hundreds of times faster than recursion
455  * or even scanning a linked list, especially when
456  * there are more than a few servers to deal with.
457  * (read as: lots).
458  */
459 TreeServer* FindServer(std::string ServerName)
460 {
461         server_hash::iterator iter;
462         iter = serverlist.find(ServerName);
463         if (iter != serverlist.end())
464         {
465                 return iter->second;
466         }
467         else
468         {
469                 return NULL;
470         }
471 }
472
473 /* Returns the locally connected server we must route a
474  * message through to reach server 'ServerName'. This
475  * only applies to one-to-one and not one-to-many routing.
476  * See the comments for the constructor of TreeServer
477  * for more details.
478  */
479 TreeServer* BestRouteTo(std::string ServerName)
480 {
481         if (ServerName.c_str() == TreeRoot->GetName())
482                 return NULL;
483         TreeServer* Found = FindServer(ServerName);
484         if (Found)
485         {
486                 return Found->GetRoute();
487         }
488         else
489         {
490                 return NULL;
491         }
492 }
493
494 /* Find the first server matching a given glob mask.
495  * Theres no find-using-glob method of hash_map [awwww :-(]
496  * so instead, we iterate over the list using an iterator
497  * and match each one until we get a hit. Yes its slow,
498  * deal with it.
499  */
500 TreeServer* FindServerMask(std::string ServerName)
501 {
502         for (server_hash::iterator i = serverlist.begin(); i != serverlist.end(); i++)
503         {
504                 if (Srv->MatchText(i->first,ServerName))
505                         return i->second;
506         }
507         return NULL;
508 }
509
510 /* A convenient wrapper that returns true if a server exists */
511 bool IsServer(std::string ServerName)
512 {
513         return (FindServer(ServerName) != NULL);
514 }
515
516 /* Every SERVER connection inbound or outbound is represented by
517  * an object of type TreeSocket.
518  * TreeSockets, being inherited from InspSocket, can be tied into
519  * the core socket engine, and we cn therefore receive activity events
520  * for them, just like activex objects on speed. (yes really, that
521  * is a technical term!) Each of these which relates to a locally
522  * connected server is assocated with it, by hooking it onto a
523  * TreeSocket class using its constructor. In this way, we can
524  * maintain a list of servers, some of which are directly connected,
525  * some of which are not.
526  */
527
528 class TreeSocket : public InspSocket
529 {
530         std::string myhost;
531         std::string in_buffer;
532         ServerState LinkState;
533         std::string InboundServerName;
534         std::string InboundDescription;
535         int num_lost_users;
536         int num_lost_servers;
537         time_t NextPing;
538         bool LastPingWasGood;
539         bool bursting;
540         AES* ctx;
541         unsigned int keylength;
542         
543  public:
544
545         /* Because most of the I/O gubbins are encapsulated within
546          * InspSocket, we just call the superclass constructor for
547          * most of the action, and append a few of our own values
548          * to it.
549          */
550         TreeSocket(std::string host, int port, bool listening, unsigned long maxtime)
551                 : InspSocket(host, port, listening, maxtime)
552         {
553                 myhost = host;
554                 this->LinkState = LISTENER;
555                 this->ctx = NULL;
556         }
557
558         TreeSocket(std::string host, int port, bool listening, unsigned long maxtime, std::string ServerName)
559                 : InspSocket(host, port, listening, maxtime)
560         {
561                 myhost = ServerName;
562                 this->LinkState = CONNECTING;
563                 this->ctx = NULL;
564         }
565
566         /* When a listening socket gives us a new file descriptor,
567          * we must associate it with a socket without creating a new
568          * connection. This constructor is used for this purpose.
569          */
570         TreeSocket(int newfd, char* ip)
571                 : InspSocket(newfd, ip)
572         {
573                 this->LinkState = WAIT_AUTH_1;
574                 this->ctx = NULL;
575                 this->SendCapabilities();
576         }
577
578         ~TreeSocket()
579         {
580                 if (ctx)
581                         delete ctx;
582         }
583
584         void InitAES(std::string key,std::string SName)
585         {
586                 if (key == "")
587                         return;
588
589                 ctx = new AES();
590                 log(DEBUG,"Initialized AES key %s",key.c_str());
591                 // key must be 16, 24, 32 etc bytes (multiple of 8)
592                 keylength = key.length();
593                 if (!(keylength == 16 || keylength == 24 || keylength == 32))
594                 {
595                         WriteOpers("*** \2ERROR\2: Key length for encryptionkey is not 16, 24 or 32 bytes in length!");
596                         log(DEBUG,"Key length not 16, 24 or 32 characters!");
597                 }
598                 else
599                 {
600                         WriteOpers("*** \2AES\2: Initialized %d bit encryption to server %s",keylength*8,SName.c_str());
601                         ctx->MakeKey(key.c_str(), "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\
602                                 \0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0", keylength, keylength);
603                 }
604         }
605         
606         /* When an outbound connection finishes connecting, we receive
607          * this event, and must send our SERVER string to the other
608          * side. If the other side is happy, as outlined in the server
609          * to server docs on the inspircd.org site, the other side
610          * will then send back its own server string.
611          */
612         virtual bool OnConnected()
613         {
614                 if (this->LinkState == CONNECTING)
615                 {
616                         Srv->SendOpers("*** Connection to "+myhost+"["+this->GetIP()+"] established.");
617                         /* we do not need to change state here. */
618                         for (std::vector<Link>::iterator x = LinkBlocks.begin(); x < LinkBlocks.end(); x++)
619                         {
620                                 if (x->Name == this->myhost)
621                                 {
622                                         this->SendCapabilities();
623                                         if (x->EncryptionKey != "")
624                                         {
625                                                 if (!(x->EncryptionKey.length() == 16 || x->EncryptionKey.length() == 24 || x->EncryptionKey.length() == 32))
626                                                 {
627                                                         WriteOpers("\2WARNING\2: Your encryption key is NOT 16, 24 or 32 characters in length, encryption will \2NOT\2 be enabled.");
628                                                 }
629                                                 else
630                                                 {
631                                                         this->WriteLine("AES "+Srv->GetServerName());
632                                                         this->InitAES(x->EncryptionKey,x->Name);
633                                                 }
634                                         }
635                                         /* found who we're supposed to be connecting to, send the neccessary gubbins. */
636                                         this->WriteLine("SERVER "+Srv->GetServerName()+" "+x->SendPass+" 0 :"+Srv->GetServerDescription());
637                                         return true;
638                                 }
639                         }
640                 }
641                 /* There is a (remote) chance that between the /CONNECT and the connection
642                  * being accepted, some muppet has removed the <link> block and rehashed.
643                  * If that happens the connection hangs here until it's closed. Unlikely
644                  * and rather harmless.
645                  */
646                 return true;
647         }
648         
649         virtual void OnError(InspSocketError e)
650         {
651                 /* We don't handle this method, because all our
652                  * dirty work is done in OnClose() (see below)
653                  * which is still called on error conditions too.
654                  */
655         }
656
657         virtual int OnDisconnect()
658         {
659                 /* For the same reason as above, we don't
660                  * handle OnDisconnect()
661                  */
662                 return true;
663         }
664
665         /* Recursively send the server tree with distances as hops.
666          * This is used during network burst to inform the other server
667          * (and any of ITS servers too) of what servers we know about.
668          * If at any point any of these servers already exist on the other
669          * end, our connection may be terminated. The hopcounts given
670          * by this function are relative, this doesn't matter so long as
671          * they are all >1, as all the remote servers re-calculate them
672          * to be relative too, with themselves as hop 0.
673          */
674         void SendServers(TreeServer* Current, TreeServer* s, int hops)
675         {
676                 char command[1024];
677                 for (unsigned int q = 0; q < Current->ChildCount(); q++)
678                 {
679                         TreeServer* recursive_server = Current->GetChild(q);
680                         if (recursive_server != s)
681                         {
682                                 snprintf(command,1024,":%s SERVER %s * %d :%s",Current->GetName().c_str(),recursive_server->GetName().c_str(),hops,recursive_server->GetDesc().c_str());
683                                 this->WriteLine(command);
684                                 this->WriteLine(":"+recursive_server->GetName()+" VERSION :"+recursive_server->GetVersion());
685                                 /* down to next level */
686                                 this->SendServers(recursive_server, s, hops+1);
687                         }
688                 }
689         }
690
691         std::string MyCapabilities()
692         {
693                 ServerConfig* Config = Srv->GetConfig();
694                 std::vector<std::string> modlist;
695                 std::string capabilities = "";
696
697                 for (int i = 0; i <= MODCOUNT; i++)
698                 {
699                         if ((modules[i]->GetVersion().Flags & VF_STATIC) || (modules[i]->GetVersion().Flags & VF_COMMON))
700                                 modlist.push_back(Config->module_names[i]);
701                 }
702                 sort(modlist.begin(),modlist.end());
703                 for (unsigned int i = 0; i < modlist.size(); i++)
704                 {
705                         if (i)
706                                 capabilities = capabilities + ",";
707                         capabilities = capabilities + modlist[i];
708                 }
709                 return capabilities;
710         }
711         
712         void SendCapabilities()
713         {
714                 this->WriteLine("CAPAB "+MyCapabilities());
715         }
716
717         bool Capab(std::deque<std::string> params)
718         {
719                 if (params.size() != 1)
720                 {
721                         this->WriteLine("ERROR :Invalid number of parameters for CAPAB");
722                         return false;
723                 }
724                 if (params[0] != this->MyCapabilities())
725                 {
726                         std::string quitserver = this->myhost;
727                         if (this->InboundServerName != "")
728                         {
729                                 quitserver = this->InboundServerName;
730                         }
731                         WriteOpers("*** \2ERROR\2: Server '%s' does not have the same set of modules loaded, cannot link!",quitserver.c_str());
732                         WriteOpers("*** Our networked module set is: '%s'",this->MyCapabilities().c_str());
733                         WriteOpers("*** Other server's networked module set is: '%s'",params[0].c_str());
734                         WriteOpers("*** These lists must match exactly on both servers. Please correct these errors, and try again.");
735                         this->WriteLine("ERROR :CAPAB mismatch; My capabilities: '"+this->MyCapabilities()+"'");
736                         return false;
737                 }
738                 return true;
739         }
740
741         /* This function forces this server to quit, removing this server
742          * and any users on it (and servers and users below that, etc etc).
743          * It's very slow and pretty clunky, but luckily unless your network
744          * is having a REAL bad hair day, this function shouldnt be called
745          * too many times a month ;-)
746          */
747         void SquitServer(TreeServer* Current, CullList* Goners)
748         {
749                 /* recursively squit the servers attached to 'Current'.
750                  * We're going backwards so we don't remove users
751                  * while we still need them ;)
752                  */
753                 for (unsigned int q = 0; q < Current->ChildCount(); q++)
754                 {
755                         TreeServer* recursive_server = Current->GetChild(q);
756                         this->SquitServer(recursive_server,Goners);
757                 }
758                 /* Now we've whacked the kids, whack self */
759                 num_lost_servers++;
760                 for (user_hash::iterator u = clientlist.begin(); u != clientlist.end(); u++)
761                 {
762                         if (!strcasecmp(u->second->server,Current->GetName().c_str()))
763                         {
764                                 std::string qreason = Current->GetName()+" "+std::string(Srv->GetServerName());
765                                 Goners->AddItem(u->second,qreason);
766                                 num_lost_users++;
767                         }
768                 }
769         }
770
771         /* This is a wrapper function for SquitServer above, which
772          * does some validation first and passes on the SQUIT to all
773          * other remaining servers.
774          */
775         void Squit(TreeServer* Current,std::string reason)
776         {
777                 if ((Current) && (Current != TreeRoot))
778                 {
779                         std::deque<std::string> params;
780                         params.push_back(Current->GetName());
781                         params.push_back(":"+reason);
782                         DoOneToAllButSender(Current->GetParent()->GetName(),"SQUIT",params,Current->GetName());
783                         if (Current->GetParent() == TreeRoot)
784                         {
785                                 Srv->SendOpers("Server \002"+Current->GetName()+"\002 split: "+reason);
786                         }
787                         else
788                         {
789                                 Srv->SendOpers("Server \002"+Current->GetName()+"\002 split from server \002"+Current->GetParent()->GetName()+"\002 with reason: "+reason);
790                         }
791                         num_lost_servers = 0;
792                         num_lost_users = 0;
793                         CullList* Goners = new CullList();
794                         SquitServer(Current, Goners);
795                         Goners->Apply();
796                         Current->Tidy();
797                         Current->GetParent()->DelChild(Current);
798                         delete Current;
799                         delete Goners;
800                         WriteOpers("Netsplit complete, lost \002%d\002 users on \002%d\002 servers.", num_lost_users, num_lost_servers);
801                 }
802                 else
803                 {
804                         log(DEFAULT,"Squit from unknown server");
805                 }
806         }
807
808         /* FMODE command */
809         bool ForceMode(std::string source, std::deque<std::string> params)
810         {
811                 userrec* who = new userrec;
812                 who->fd = FD_MAGIC_NUMBER;
813                 if (params.size() < 2)
814                         return true;
815                 char* modelist[255];
816                 for (unsigned int q = 0; q < params.size(); q++)
817                 {
818                         modelist[q] = (char*)params[q].c_str();
819                 }
820                 Srv->SendMode(modelist,params.size(),who);
821                 DoOneToAllButSender(source,"FMODE",params,source);
822                 delete who;
823                 return true;
824         }
825
826         /* FTOPIC command */
827         bool ForceTopic(std::string source, std::deque<std::string> params)
828         {
829                 if (params.size() != 4)
830                         return true;
831                 std::string channel = params[0];
832                 time_t ts = atoi(params[1].c_str());
833                 std::string setby = params[2];
834                 std::string topic = params[3];
835
836                 chanrec* c = Srv->FindChannel(channel);
837                 if (c)
838                 {
839                         if ((ts >= c->topicset) || (!*c->topic))
840                         {
841                                 std::string oldtopic = c->topic;
842                                 strlcpy(c->topic,topic.c_str(),MAXTOPIC);
843                                 strlcpy(c->setby,setby.c_str(),NICKMAX);
844                                 c->topicset = ts;
845                                 /* if the topic text is the same as the current topic,
846                                  * dont bother to send the TOPIC command out, just silently
847                                  * update the set time and set nick.
848                                  */
849                                 if (oldtopic != topic)
850                                         WriteChannelWithServ((char*)source.c_str(), c, "TOPIC %s :%s", c->name, c->topic);
851                         }
852                         
853                 }
854                 
855                 /* all done, send it on its way */
856                 params[3] = ":" + params[3];
857                 DoOneToAllButSender(source,"FTOPIC",params,source);
858
859                 return true;
860         }
861
862         /* FJOIN, similar to unreal SJOIN */
863         bool ForceJoin(std::string source, std::deque<std::string> params)
864         {
865                 if (params.size() < 3)
866                         return true;
867
868                 char first[MAXBUF];
869                 char modestring[MAXBUF];
870                 char* mode_users[127];
871                 mode_users[0] = first;
872                 mode_users[1] = modestring;
873                 strcpy(mode_users[1],"+");
874                 unsigned int modectr = 2;
875                 
876                 userrec* who = NULL;
877                 std::string channel = params[0];
878                 time_t TS = atoi(params[1].c_str());
879                 char* key = "";
880                 
881                 chanrec* chan = Srv->FindChannel(channel);
882                 if (chan)
883                 {
884                         key = chan->key;
885                 }
886                 strlcpy(mode_users[0],channel.c_str(),MAXBUF);
887
888                 /* default is a high value, which if we dont have this
889                  * channel will let the other side apply their modes.
890                  */
891                 time_t ourTS = time(NULL)+600;
892                 chanrec* us = Srv->FindChannel(channel);
893                 if (us)
894                 {
895                         ourTS = us->age;
896                 }
897
898                 log(DEBUG,"FJOIN detected, our TS=%lu, their TS=%lu",ourTS,TS);
899
900                 /* do this first, so our mode reversals are correctly received by other servers
901                  * if there is a TS collision.
902                  */
903                 DoOneToAllButSender(source,"FJOIN",params,source);
904                 
905                 for (unsigned int usernum = 2; usernum < params.size(); usernum++)
906                 {
907                         /* process one channel at a time, applying modes. */
908                         char* usr = (char*)params[usernum].c_str();
909                         char permissions = *usr;
910                         switch (permissions)
911                         {
912                                 case '@':
913                                         usr++;
914                                         mode_users[modectr++] = usr;
915                                         strlcat(modestring,"o",MAXBUF);
916                                 break;
917                                 case '%':
918                                         usr++;
919                                         mode_users[modectr++] = usr;
920                                         strlcat(modestring,"h",MAXBUF);
921                                 break;
922                                 case '+':
923                                         usr++;
924                                         mode_users[modectr++] = usr;
925                                         strlcat(modestring,"v",MAXBUF);
926                                 break;
927                         }
928                         who = Srv->FindNick(usr);
929                         if (who)
930                         {
931                                 Srv->JoinUserToChannel(who,channel,key);
932                                 if (modectr >= (MAXMODES-1))
933                                 {
934                                         /* theres a mode for this user. push them onto the mode queue, and flush it
935                                          * if there are more than MAXMODES to go.
936                                          */
937                                         if ((ourTS >= TS) || (Srv->IsUlined(who->server)))
938                                         {
939                                                 /* We also always let u-lined clients win, no matter what the TS value */
940                                                 log(DEBUG,"Our our channel newer than theirs, accepting their modes");
941                                                 Srv->SendMode(mode_users,modectr,who);
942                                         }
943                                         else
944                                         {
945                                                 log(DEBUG,"Their channel newer than ours, bouncing their modes");
946                                                 /* bouncy bouncy! */
947                                                 std::deque<std::string> params;
948                                                 /* modes are now being UNSET... */
949                                                 *mode_users[1] = '-';
950                                                 for (unsigned int x = 0; x < modectr; x++)
951                                                 {
952                                                         params.push_back(mode_users[x]);
953                                                 }
954                                                 // tell everyone to bounce the modes. bad modes, bad!
955                                                 DoOneToMany(Srv->GetServerName(),"FMODE",params);
956                                         }
957                                         strcpy(mode_users[1],"+");
958                                         modectr = 2;
959                                 }
960                         }
961                 }
962                 /* there werent enough modes built up to flush it during FJOIN,
963                  * or, there are a number left over. flush them out.
964                  */
965                 if ((modectr > 2) && (who))
966                 {
967                         if (ourTS >= TS)
968                         {
969                                 log(DEBUG,"Our our channel newer than theirs, accepting their modes");
970                                 Srv->SendMode(mode_users,modectr,who);
971                         }
972                         else
973                         {
974                                 log(DEBUG,"Their channel newer than ours, bouncing their modes");
975                                 std::deque<std::string> params;
976                                 *mode_users[1] = '-';
977                                 for (unsigned int x = 0; x < modectr; x++)
978                                 {
979                                         params.push_back(mode_users[x]);
980                                 }
981                                 DoOneToMany(Srv->GetServerName(),"FMODE",params);
982                         }
983                 }
984                 return true;
985         }
986
987         /* NICK command */
988         bool IntroduceClient(std::string source, std::deque<std::string> params)
989         {
990                 if (params.size() < 8)
991                         return true;
992                 // NICK age nick host dhost ident +modes ip :gecos
993                 //       0   1    2    3      4     5    6   7
994                 std::string nick = params[1];
995                 std::string host = params[2];
996                 std::string dhost = params[3];
997                 std::string ident = params[4];
998                 time_t age = atoi(params[0].c_str());
999                 std::string modes = params[5];
1000                 while (*(modes.c_str()) == '+')
1001                 {
1002                         char* m = (char*)modes.c_str();
1003                         m++;
1004                         modes = m;
1005                 }
1006                 std::string ip = params[6];
1007                 std::string gecos = params[7];
1008                 char* tempnick = (char*)nick.c_str();
1009                 log(DEBUG,"Introduce client %s!%s@%s",tempnick,ident.c_str(),host.c_str());
1010                 
1011                 user_hash::iterator iter;
1012                 iter = clientlist.find(tempnick);
1013                 if (iter != clientlist.end())
1014                 {
1015                         // nick collision
1016                         log(DEBUG,"Nick collision on %s!%s@%s: %lu %lu",tempnick,ident.c_str(),host.c_str(),(unsigned long)age,(unsigned long)iter->second->age);
1017                         this->WriteLine(":"+Srv->GetServerName()+" KILL "+tempnick+" :Nickname collision");
1018                         return true;
1019                 }
1020
1021                 clientlist[tempnick] = new userrec();
1022                 clientlist[tempnick]->fd = FD_MAGIC_NUMBER;
1023                 strlcpy(clientlist[tempnick]->nick, tempnick,NICKMAX);
1024                 strlcpy(clientlist[tempnick]->host, host.c_str(),160);
1025                 strlcpy(clientlist[tempnick]->dhost, dhost.c_str(),160);
1026                 clientlist[tempnick]->server = (char*)FindServerNamePtr(source.c_str());
1027                 strlcpy(clientlist[tempnick]->ident, ident.c_str(),IDENTMAX);
1028                 strlcpy(clientlist[tempnick]->fullname, gecos.c_str(),MAXGECOS);
1029                 clientlist[tempnick]->registered = 7;
1030                 clientlist[tempnick]->signon = age;
1031                 strlcpy(clientlist[tempnick]->modes, modes.c_str(),53);
1032                 strlcpy(clientlist[tempnick]->ip,ip.c_str(),16);
1033
1034                 ucrec a;
1035                 a.channel = NULL;
1036                 a.uc_modes = 0;
1037                 for (int i = 0; i < MAXCHANS; i++)
1038                         clientlist[tempnick]->chans.push_back(a);
1039
1040                 if (!this->bursting)
1041                 {
1042                         WriteOpers("*** Client connecting at %s: %s!%s@%s [%s]",clientlist[tempnick]->server,clientlist[tempnick]->nick,clientlist[tempnick]->ident,clientlist[tempnick]->host,clientlist[tempnick]->ip);
1043                 }
1044                 params[7] = ":" + params[7];
1045                 DoOneToAllButSender(source,"NICK",params,source);
1046                 return true;
1047         }
1048
1049         /* Send one or more FJOINs for a channel of users.
1050          * If the length of a single line is more than 480-NICKMAX
1051          * in length, it is split over multiple lines.
1052          */
1053         void SendFJoins(TreeServer* Current, chanrec* c)
1054         {
1055                 log(DEBUG,"Sending FJOINs to other server for %s",c->name);
1056                 char list[MAXBUF];
1057                 snprintf(list,MAXBUF,":%s FJOIN %s %lu",Srv->GetServerName().c_str(),c->name,(unsigned long)c->age);
1058                 std::vector<char*> *ulist = c->GetUsers();
1059                 for (unsigned int i = 0; i < ulist->size(); i++)
1060                 {
1061                         char* o = (*ulist)[i];
1062                         userrec* otheruser = (userrec*)o;
1063                         strlcat(list," ",MAXBUF);
1064                         strlcat(list,cmode(otheruser,c),MAXBUF);
1065                         strlcat(list,otheruser->nick,MAXBUF);
1066                         if (strlen(list)>(480-NICKMAX))
1067                         {
1068                                 log(DEBUG,"FJOIN line wrapped");
1069                                 this->WriteLine(list);
1070                                 snprintf(list,MAXBUF,":%s FJOIN %s %lu",Srv->GetServerName().c_str(),c->name,(unsigned long)c->age);
1071                         }
1072                 }
1073                 if (list[strlen(list)-1] != ':')
1074                 {
1075                         log(DEBUG,"Final FJOIN line");
1076                         this->WriteLine(list);
1077                 }
1078         }
1079
1080         /* Send G, Q, Z and E lines */
1081         void SendXLines(TreeServer* Current)
1082         {
1083                 char data[MAXBUF];
1084                 /* Yes, these arent too nice looking, but they get the job done */
1085                 for (std::vector<ZLine>::iterator i = zlines.begin(); i != zlines.end(); i++)
1086                 {
1087                         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);
1088                         this->WriteLine(data);
1089                 }
1090                 for (std::vector<QLine>::iterator i = qlines.begin(); i != qlines.end(); i++)
1091                 {
1092                         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);
1093                         this->WriteLine(data);
1094                 }
1095                 for (std::vector<GLine>::iterator i = glines.begin(); i != glines.end(); i++)
1096                 {
1097                         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);
1098                         this->WriteLine(data);
1099                 }
1100                 for (std::vector<ELine>::iterator i = elines.begin(); i != elines.end(); i++)
1101                 {
1102                         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);
1103                         this->WriteLine(data);
1104                 }
1105                 for (std::vector<ZLine>::iterator i = pzlines.begin(); i != pzlines.end(); i++)
1106                 {
1107                         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);
1108                         this->WriteLine(data);
1109                 }
1110                 for (std::vector<QLine>::iterator i = pqlines.begin(); i != pqlines.end(); i++)
1111                 {
1112                         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);
1113                         this->WriteLine(data);
1114                 }
1115                 for (std::vector<GLine>::iterator i = pglines.begin(); i != pglines.end(); i++)
1116                 {
1117                         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);
1118                         this->WriteLine(data);
1119                 }
1120                 for (std::vector<ELine>::iterator i = pelines.begin(); i != pelines.end(); i++)
1121                 {
1122                         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);
1123                         this->WriteLine(data);
1124                 }
1125         }
1126
1127         /* Send channel modes and topics */
1128         void SendChannelModes(TreeServer* Current)
1129         {
1130                 char data[MAXBUF];
1131                 std::deque<std::string> list;
1132                 for (chan_hash::iterator c = chanlist.begin(); c != chanlist.end(); c++)
1133                 {
1134                         SendFJoins(Current, c->second);
1135                         snprintf(data,MAXBUF,":%s FMODE %s +%s",Srv->GetServerName().c_str(),c->second->name,chanmodes(c->second));
1136                         this->WriteLine(data);
1137                         if (*c->second->topic)
1138                         {
1139                                 snprintf(data,MAXBUF,":%s FTOPIC %s %lu %s :%s",Srv->GetServerName().c_str(),c->second->name,(unsigned long)c->second->topicset,c->second->setby,c->second->topic);
1140                                 this->WriteLine(data);
1141                         }
1142                         for (BanList::iterator b = c->second->bans.begin(); b != c->second->bans.end(); b++)
1143                         {
1144                                 snprintf(data,MAXBUF,":%s FMODE %s +b %s",Srv->GetServerName().c_str(),c->second->name,b->data);
1145                                 this->WriteLine(data);
1146                         }
1147                         FOREACH_MOD OnSyncChannel(c->second,(Module*)TreeProtocolModule,(void*)this);
1148                         list.clear();
1149                         c->second->GetExtList(list);
1150                         for (unsigned int j = 0; j < list.size(); j++)
1151                         {
1152                                 FOREACH_MOD OnSyncChannelMetaData(c->second,(Module*)TreeProtocolModule,(void*)this,list[j]);
1153                         }
1154                 }
1155         }
1156
1157         /* send all users and their oper state/modes */
1158         void SendUsers(TreeServer* Current)
1159         {
1160                 char data[MAXBUF];
1161                 std::deque<std::string> list;
1162                 for (user_hash::iterator u = clientlist.begin(); u != clientlist.end(); u++)
1163                 {
1164                         if (u->second->registered == 7)
1165                         {
1166                                 snprintf(data,MAXBUF,":%s NICK %lu %s %s %s %s +%s %s :%s",u->second->server,(unsigned long)u->second->age,u->second->nick,u->second->host,u->second->dhost,u->second->ident,u->second->modes,u->second->ip,u->second->fullname);
1167                                 this->WriteLine(data);
1168                                 if (strchr(u->second->modes,'o'))
1169                                 {
1170                                         this->WriteLine(":"+std::string(u->second->nick)+" OPERTYPE "+std::string(u->second->oper));
1171                                 }
1172                                 FOREACH_MOD OnSyncUser(u->second,(Module*)TreeProtocolModule,(void*)this);
1173                                 list.clear();
1174                                 u->second->GetExtList(list);
1175                                 for (unsigned int j = 0; j < list.size(); j++)
1176                                 {
1177                                         FOREACH_MOD OnSyncUserMetaData(u->second,(Module*)TreeProtocolModule,(void*)this,list[j]);
1178                                 }
1179                         }
1180                 }
1181         }
1182
1183         /* This function is called when we want to send a netburst to a local
1184          * server. There is a set order we must do this, because for example
1185          * users require their servers to exist, and channels require their
1186          * users to exist. You get the idea.
1187          */
1188         void DoBurst(TreeServer* s)
1189         {
1190                 Srv->SendOpers("*** Bursting to \2"+s->GetName()+"\2.");
1191                 this->WriteLine("BURST");
1192                 /* send our version string */
1193                 this->WriteLine(":"+Srv->GetServerName()+" VERSION :"+Srv->GetVersion());
1194                 /* Send server tree */
1195                 this->SendServers(TreeRoot,s,1);
1196                 /* Send users and their oper status */
1197                 this->SendUsers(s);
1198                 /* Send everything else (channel modes, xlines etc) */
1199                 this->SendChannelModes(s);
1200                 this->SendXLines(s);
1201                 this->WriteLine("ENDBURST");
1202                 Srv->SendOpers("*** Finished bursting to \2"+s->GetName()+"\2.");
1203         }
1204
1205         /* This function is called when we receive data from a remote
1206          * server. We buffer the data in a std::string (it doesnt stay
1207          * there for long), reading using InspSocket::Read() which can
1208          * read up to 16 kilobytes in one operation.
1209          *
1210          * IF THIS FUNCTION RETURNS FALSE, THE CORE CLOSES AND DELETES
1211          * THE SOCKET OBJECT FOR US.
1212          */
1213         virtual bool OnDataReady()
1214         {
1215                 char* data = this->Read();
1216                 if (data)
1217                 {
1218                         this->in_buffer += data;
1219                         /* While there is at least one new line in the buffer,
1220                          * do something useful (we hope!) with it.
1221                          */
1222                         while (in_buffer.find("\n") != std::string::npos)
1223                         {
1224                                 char* line = (char*)in_buffer.c_str();
1225                                 std::string ret = "";
1226                                 while ((*line != '\n') && (strlen(line)))
1227                                 {
1228                                         ret = ret + *line;
1229                                         line++;
1230                                 }
1231                                 if ((*line == '\n') || (*line == '\r'))
1232                                         line++;
1233                                 in_buffer = line;
1234                                 /* Process this one, abort if it
1235                                  * didnt return true.
1236                                  */
1237                                 if (this->ctx)
1238                                 {
1239                                         char out[1024];
1240                                         char result[1024];
1241                                         log(DEBUG,"Original string '%s'",ret.c_str());
1242                                         /* ERROR + CAPAB is still allowed unencryped */
1243                                         if ((ret.substr(0,7) != "ERROR :") && (ret.substr(0,6) != "CAPAB "))
1244                                         {
1245                                                 int nbytes = from64tobits(out, ret.c_str(), 1024);
1246                                                 log(DEBUG,"m_spanningtree: decrypt %d bytes",nbytes);
1247                                                 ctx->Decrypt(out, result, nbytes, 0);
1248                                                 for (int t = 0; t < nbytes; t++)
1249                                                         if (result[t] == '\7') result[t] = 0;
1250                                                 ret = result;
1251                                         }
1252                                 }
1253                                 if (!this->ProcessLine(ret))
1254                                 {
1255                                         return false;
1256                                 }
1257                         }
1258                 }
1259                 return (data != NULL);
1260         }
1261
1262         int WriteLine(std::string line)
1263         {
1264                 log(DEBUG,"OUT: %s",line.c_str());
1265                 if (this->ctx)
1266                 {
1267                         log(DEBUG,"AES context");
1268                         char result[10240];
1269                         char result64[10240];
1270                         if (this->keylength)
1271                         {
1272                                 while (line.length() % this->keylength != 0)
1273                                 {
1274                                         // pad it to be a multiple of the key length
1275                                         line = line + "\7";
1276                                 }
1277                         }
1278                         unsigned int ll = line.length();
1279                         log(DEBUG,"Plaintext line with padding = %d chars",ll);
1280                         ctx->Encrypt(line.c_str(), result, ll, 0);
1281                         log(DEBUG,"Encrypted.");
1282                         to64frombits((unsigned char*)result64,
1283                                         (unsigned char*)result,
1284                                         ll);
1285                         line = result64;
1286                         log(DEBUG,"Encrypted: %s",line.c_str());
1287                         //int from64tobits(char *out, const char *in, int maxlen);
1288                 }
1289                 return this->Write(line + "\r\n");
1290         }
1291
1292         /* Handle ERROR command */
1293         bool Error(std::deque<std::string> params)
1294         {
1295                 if (params.size() < 1)
1296                         return false;
1297                 std::string Errmsg = params[0];
1298                 std::string SName = myhost;
1299                 if (InboundServerName != "")
1300                 {
1301                         SName = InboundServerName;
1302                 }
1303                 Srv->SendOpers("*** ERROR from "+SName+": "+Errmsg);
1304                 /* we will return false to cause the socket to close.
1305                  */
1306                 return false;
1307         }
1308
1309         /* Because the core won't let users or even SERVERS set +o,
1310          * we use the OPERTYPE command to do this.
1311          */
1312         bool OperType(std::string prefix, std::deque<std::string> &params)
1313         {
1314                 if (params.size() != 1)
1315                         return true;
1316                 std::string opertype = params[0];
1317                 userrec* u = Srv->FindNick(prefix);
1318                 if (u)
1319                 {
1320                         strlcpy(u->oper,opertype.c_str(),NICKMAX);
1321                         if (!strchr(u->modes,'o'))
1322                         {
1323                                 strcat(u->modes,"o");
1324                         }
1325                         DoOneToAllButSender(u->nick,"OPERTYPE",params,u->server);
1326                 }
1327                 return true;
1328         }
1329
1330         /* Because Andy insists that services-compatible servers must
1331          * implement SVSNICK and SVSJOIN, that's exactly what we do :p
1332          */
1333         bool ForceNick(std::string prefix, std::deque<std::string> &params)
1334         {
1335                 if (params.size() < 3)
1336                         return true;
1337                 userrec* u = Srv->FindNick(params[0]);
1338                 if (u)
1339                 {
1340                         Srv->ChangeUserNick(u,params[1]);
1341                         u->age = atoi(params[2].c_str());
1342                         DoOneToAllButSender(prefix,"SVSNICK",params,prefix);
1343                 }
1344                 return true;
1345         }
1346
1347         bool ServiceJoin(std::string prefix, std::deque<std::string> &params)
1348         {
1349                 if (params.size() < 2)
1350                         return true;
1351                 userrec* u = Srv->FindNick(params[0]);
1352                 if (u)
1353                 {
1354                         Srv->JoinUserToChannel(u,params[1],"");
1355                         DoOneToAllButSender(prefix,"SVSJOIN",params,prefix);
1356                 }
1357                 return true;
1358         }
1359
1360         bool RemoteRehash(std::string prefix, std::deque<std::string> &params)
1361         {
1362                 if (params.size() < 1)
1363                         return false;
1364                 std::string servermask = params[0];
1365                 if (Srv->MatchText(Srv->GetServerName(),servermask))
1366                 {
1367                         Srv->SendOpers("*** Remote rehash initiated from server \002"+prefix+"\002.");
1368                         Srv->RehashServer();
1369                         ReadConfiguration(false);
1370                 }
1371                 DoOneToAllButSender(prefix,"REHASH",params,prefix);
1372                 return true;
1373         }
1374
1375         bool RemoteKill(std::string prefix, std::deque<std::string> &params)
1376         {
1377                 if (params.size() != 2)
1378                         return true;
1379                 std::string nick = params[0];
1380                 userrec* u = Srv->FindNick(prefix);
1381                 userrec* who = Srv->FindNick(nick);
1382                 if (who)
1383                 {
1384                         /* Prepend kill source, if we don't have one */
1385                         std::string sourceserv = prefix;
1386                         if (u)
1387                         {
1388                                 sourceserv = u->server;
1389                         }
1390                         if (*(params[1].c_str()) != '[')
1391                         {
1392                                 params[1] = "[" + sourceserv + "] Killed (" + params[1] +")";
1393                         }
1394                         std::string reason = params[1];
1395                         params[1] = ":" + params[1];
1396                         DoOneToAllButSender(prefix,"KILL",params,sourceserv);
1397                         Srv->QuitUser(who,reason);
1398                 }
1399                 return true;
1400         }
1401
1402         bool LocalPong(std::string prefix, std::deque<std::string> &params)
1403         {
1404                 if (params.size() < 1)
1405                         return true;
1406                 TreeServer* ServerSource = FindServer(prefix);
1407                 if (ServerSource)
1408                 {
1409                         ServerSource->SetPingFlag();
1410                 }
1411                 return true;
1412         }
1413         
1414         bool MetaData(std::string prefix, std::deque<std::string> &params)
1415         {
1416                 if (params.size() < 3)
1417                         return true;
1418                 TreeServer* ServerSource = FindServer(prefix);
1419                 if (ServerSource)
1420                 {
1421                         if (*(params[0].c_str()) == '#')
1422                         {
1423                                 chanrec* c = Srv->FindChannel(params[0]);
1424                                 if (c)
1425                                 {
1426                                         FOREACH_MOD OnDecodeMetaData(TYPE_CHANNEL,c,params[1],params[2]);
1427                                 }
1428                         }
1429                         else
1430                         {
1431                                 userrec* u = Srv->FindNick(params[0]);
1432                                 if (u)
1433                                 {
1434                                         FOREACH_MOD OnDecodeMetaData(TYPE_USER,u,params[1],params[2]);
1435                                 }
1436                         }
1437                 }
1438                 params[2] = ":" + params[2];
1439                 DoOneToAllButSender(prefix,"METADATA",params,prefix);
1440                 return true;
1441         }
1442
1443         bool ServerVersion(std::string prefix, std::deque<std::string> &params)
1444         {
1445                 if (params.size() < 1)
1446                         return true;
1447                 TreeServer* ServerSource = FindServer(prefix);
1448                 if (ServerSource)
1449                 {
1450                         ServerSource->SetVersion(params[0]);
1451                 }
1452                 params[0] = ":" + params[0];
1453                 DoOneToAllButSender(prefix,"VERSION",params,prefix);
1454                 return true;
1455         }
1456
1457         bool ChangeHost(std::string prefix, std::deque<std::string> &params)
1458         {
1459                 if (params.size() < 1)
1460                         return true;
1461                 userrec* u = Srv->FindNick(prefix);
1462                 if (u)
1463                 {
1464                         Srv->ChangeHost(u,params[0]);
1465                         DoOneToAllButSender(prefix,"FHOST",params,u->server);
1466                 }
1467                 return true;
1468         }
1469
1470         bool AddLine(std::string prefix, std::deque<std::string> &params)
1471         {
1472                 if (params.size() < 6)
1473                         return true;
1474                 std::string linetype = params[0]; /* Z, Q, E, G, K */
1475                 std::string mask = params[1]; /* Line type dependent */
1476                 std::string source = params[2]; /* may not be online or may be a server */
1477                 std::string settime = params[3]; /* EPOCH time set */
1478                 std::string duration = params[4]; /* Duration secs */
1479                 std::string reason = params[5];
1480
1481                 switch (*(linetype.c_str()))
1482                 {
1483                         case 'Z':
1484                                 add_zline(atoi(duration.c_str()), source.c_str(), reason.c_str(), mask.c_str());
1485                                 zline_set_creation_time((char*)mask.c_str(), atoi(settime.c_str()));
1486                         break;
1487                         case 'Q':
1488                                 add_qline(atoi(duration.c_str()), source.c_str(), reason.c_str(), mask.c_str());
1489                                 qline_set_creation_time((char*)mask.c_str(), atoi(settime.c_str()));
1490                         break;
1491                         case 'E':
1492                                 add_eline(atoi(duration.c_str()), source.c_str(), reason.c_str(), mask.c_str());
1493                                 eline_set_creation_time((char*)mask.c_str(), atoi(settime.c_str()));
1494                         break;
1495                         case 'G':
1496                                 add_gline(atoi(duration.c_str()), source.c_str(), reason.c_str(), mask.c_str());
1497                                 gline_set_creation_time((char*)mask.c_str(), atoi(settime.c_str()));
1498                         break;
1499                         case 'K':
1500                                 add_kline(atoi(duration.c_str()), source.c_str(), reason.c_str(), mask.c_str());
1501                         break;
1502                         default:
1503                                 /* Just in case... */
1504                                 Srv->SendOpers("*** \2WARNING\2: Invalid xline type '"+linetype+"' sent by server "+prefix+", ignored!");
1505                         break;
1506                 }
1507                 /* Send it on its way */
1508                 params[5] = ":" + params[5];
1509                 DoOneToAllButSender(prefix,"ADDLINE",params,prefix);
1510                 return true;
1511         }
1512
1513         bool ChangeName(std::string prefix, std::deque<std::string> &params)
1514         {
1515                 if (params.size() < 1)
1516                         return true;
1517                 userrec* u = Srv->FindNick(prefix);
1518                 if (u)
1519                 {
1520                         Srv->ChangeGECOS(u,params[0]);
1521                         params[0] = ":" + params[0];
1522                         DoOneToAllButSender(prefix,"FNAME",params,u->server);
1523                 }
1524                 return true;
1525         }
1526
1527         bool Whois(std::string prefix, std::deque<std::string> &params)
1528         {
1529                 if (params.size() < 1)
1530                         return true;
1531                 log(DEBUG,"In IDLE command");
1532                 userrec* u = Srv->FindNick(prefix);
1533                 if (u)
1534                 {
1535                         log(DEBUG,"USER EXISTS: %s",u->nick);
1536                         // an incoming request
1537                         if (params.size() == 1)
1538                         {
1539                                 userrec* x = Srv->FindNick(params[0]);
1540                                 if (x->fd > -1)
1541                                 {
1542                                         userrec* x = Srv->FindNick(params[0]);
1543                                         log(DEBUG,"Got IDLE");
1544                                         char signon[MAXBUF];
1545                                         char idle[MAXBUF];
1546                                         log(DEBUG,"Sending back IDLE 3");
1547                                         snprintf(signon,MAXBUF,"%lu",(unsigned long)x->signon);
1548                                         snprintf(idle,MAXBUF,"%lu",(unsigned long)abs((x->idle_lastmsg)-time(NULL)));
1549                                         std::deque<std::string> par;
1550                                         par.push_back(prefix);
1551                                         par.push_back(signon);
1552                                         par.push_back(idle);
1553                                         // ours, we're done, pass it BACK
1554                                         DoOneToOne(params[0],"IDLE",par,u->server);
1555                                 }
1556                                 else
1557                                 {
1558                                         // not ours pass it on
1559                                         DoOneToOne(prefix,"IDLE",params,x->server);
1560                                 }
1561                         }
1562                         else if (params.size() == 3)
1563                         {
1564                                 std::string who_did_the_whois = params[0];
1565                                 userrec* who_to_send_to = Srv->FindNick(who_did_the_whois);
1566                                 if (who_to_send_to->fd > -1)
1567                                 {
1568                                         log(DEBUG,"Got final IDLE");
1569                                         // an incoming reply to a whois we sent out
1570                                         std::string nick_whoised = prefix;
1571                                         unsigned long signon = atoi(params[1].c_str());
1572                                         unsigned long idle = atoi(params[2].c_str());
1573                                         if ((who_to_send_to) && (who_to_send_to->fd > -1))
1574                                                 do_whois(who_to_send_to,u,signon,idle,(char*)nick_whoised.c_str());
1575                                 }
1576                                 else
1577                                 {
1578                                         // not ours, pass it on
1579                                         DoOneToOne(prefix,"IDLE",params,who_to_send_to->server);
1580                                 }
1581                         }
1582                 }
1583                 return true;
1584         }
1585         
1586         bool LocalPing(std::string prefix, std::deque<std::string> &params)
1587         {
1588                 if (params.size() < 1)
1589                         return true;
1590                 std::string stufftobounce = params[0];
1591                 this->WriteLine(":"+Srv->GetServerName()+" PONG "+stufftobounce);
1592                 return true;
1593         }
1594
1595         bool RemoteServer(std::string prefix, std::deque<std::string> &params)
1596         {
1597                 if (params.size() < 4)
1598                         return false;
1599                 std::string servername = params[0];
1600                 std::string password = params[1];
1601                 // hopcount is not used for a remote server, we calculate this ourselves
1602                 std::string description = params[3];
1603                 TreeServer* ParentOfThis = FindServer(prefix);
1604                 if (!ParentOfThis)
1605                 {
1606                         this->WriteLine("ERROR :Protocol error - Introduced remote server from unknown server "+prefix);
1607                         return false;
1608                 }
1609                 TreeServer* CheckDupe = FindServer(servername);
1610                 if (CheckDupe)
1611                 {
1612                         this->WriteLine("ERROR :Server "+servername+" already exists on server "+CheckDupe->GetParent()->GetName()+"!");
1613                         Srv->SendOpers("*** Server connection from \2"+servername+"\2 denied, already exists on server "+CheckDupe->GetParent()->GetName());
1614                         return false;
1615                 }
1616                 TreeServer* Node = new TreeServer(servername,description,ParentOfThis,NULL);
1617                 ParentOfThis->AddChild(Node);
1618                 params[3] = ":" + params[3];
1619                 DoOneToAllButSender(prefix,"SERVER",params,prefix);
1620                 Srv->SendOpers("*** Server \002"+prefix+"\002 introduced server \002"+servername+"\002 ("+description+")");
1621                 return true;
1622         }
1623
1624         bool Outbound_Reply_Server(std::deque<std::string> &params)
1625         {
1626                 if (params.size() < 4)
1627                         return false;
1628                 std::string servername = params[0];
1629                 std::string password = params[1];
1630                 int hops = atoi(params[2].c_str());
1631                 if (hops)
1632                 {
1633                         this->WriteLine("ERROR :Server too far away for authentication");
1634                         Srv->SendOpers("*** Server connection from \2"+servername+"\2 denied, server is too far away for authentication");
1635                         return false;
1636                 }
1637                 std::string description = params[3];
1638                 for (std::vector<Link>::iterator x = LinkBlocks.begin(); x < LinkBlocks.end(); x++)
1639                 {
1640                         if ((x->Name == servername) && (x->RecvPass == password))
1641                         {
1642                                 TreeServer* CheckDupe = FindServer(servername);
1643                                 if (CheckDupe)
1644                                 {
1645                                         this->WriteLine("ERROR :Server "+servername+" already exists on server "+CheckDupe->GetParent()->GetName()+"!");
1646                                         Srv->SendOpers("*** Server connection from \2"+servername+"\2 denied, already exists on server "+CheckDupe->GetParent()->GetName());
1647                                         return false;
1648                                 }
1649                                 // Begin the sync here. this kickstarts the
1650                                 // other side, waiting in WAIT_AUTH_2 state,
1651                                 // into starting their burst, as it shows
1652                                 // that we're happy.
1653                                 this->LinkState = CONNECTED;
1654                                 // we should add the details of this server now
1655                                 // to the servers tree, as a child of the root
1656                                 // node.
1657                                 TreeServer* Node = new TreeServer(servername,description,TreeRoot,this);
1658                                 TreeRoot->AddChild(Node);
1659                                 params[3] = ":" + params[3];
1660                                 DoOneToAllButSender(TreeRoot->GetName(),"SERVER",params,servername);
1661                                 this->bursting = true;
1662                                 this->DoBurst(Node);
1663                                 return true;
1664                         }
1665                 }
1666                 this->WriteLine("ERROR :Invalid credentials");
1667                 Srv->SendOpers("*** Server connection from \2"+servername+"\2 denied, invalid link credentials");
1668                 return false;
1669         }
1670
1671         bool Inbound_Server(std::deque<std::string> &params)
1672         {
1673                 if (params.size() < 4)
1674                         return false;
1675                 std::string servername = params[0];
1676                 std::string password = params[1];
1677                 int hops = atoi(params[2].c_str());
1678                 if (hops)
1679                 {
1680                         this->WriteLine("ERROR :Server too far away for authentication");
1681                         Srv->SendOpers("*** Server connection from \2"+servername+"\2 denied, server is too far away for authentication");
1682                         return false;
1683                 }
1684                 std::string description = params[3];
1685                 for (std::vector<Link>::iterator x = LinkBlocks.begin(); x < LinkBlocks.end(); x++)
1686                 {
1687                         if ((x->Name == servername) && (x->RecvPass == password))
1688                         {
1689                                 TreeServer* CheckDupe = FindServer(servername);
1690                                 if (CheckDupe)
1691                                 {
1692                                         this->WriteLine("ERROR :Server "+servername+" already exists on server "+CheckDupe->GetParent()->GetName()+"!");
1693                                         Srv->SendOpers("*** Server connection from \2"+servername+"\2 denied, already exists on server "+CheckDupe->GetParent()->GetName());
1694                                         return false;
1695                                 }
1696                                 /* If the config says this link is encrypted, but the remote side
1697                                  * hasnt bothered to send the AES command before SERVER, then we
1698                                  * boot them off as we MUST have this connection encrypted.
1699                                  */
1700                                 if ((x->EncryptionKey != "") && (!this->ctx))
1701                                 {
1702                                         this->WriteLine("ERROR :This link requires AES encryption to be enabled. Plaintext connection refused.");
1703                                         Srv->SendOpers("*** Server connection from \2"+servername+"\2 denied, remote server did not enable AES.");
1704                                         return false;
1705                                 }
1706                                 Srv->SendOpers("*** Verified incoming server connection from \002"+servername+"\002["+this->GetIP()+"] ("+description+")");
1707                                 this->InboundServerName = servername;
1708                                 this->InboundDescription = description;
1709                                 // this is good. Send our details: Our server name and description and hopcount of 0,
1710                                 // along with the sendpass from this block.
1711                                 this->WriteLine("SERVER "+Srv->GetServerName()+" "+x->SendPass+" 0 :"+Srv->GetServerDescription());
1712                                 // move to the next state, we are now waiting for THEM.
1713                                 this->LinkState = WAIT_AUTH_2;
1714                                 return true;
1715                         }
1716                 }
1717                 this->WriteLine("ERROR :Invalid credentials");
1718                 Srv->SendOpers("*** Server connection from \2"+servername+"\2 denied, invalid link credentials");
1719                 return false;
1720         }
1721
1722         void Split(std::string line, bool stripcolon, std::deque<std::string> &n)
1723         {
1724                 if (!strchr(line.c_str(),' '))
1725                 {
1726                         n.push_back(line);
1727                         return;
1728                 }
1729                 std::stringstream s(line);
1730                 std::string param = "";
1731                 n.clear();
1732                 int item = 0;
1733                 while (!s.eof())
1734                 {
1735                         char c;
1736                         s.get(c);
1737                         if (c == ' ')
1738                         {
1739                                 n.push_back(param);
1740                                 param = "";
1741                                 item++;
1742                         }
1743                         else
1744                         {
1745                                 if (!s.eof())
1746                                 {
1747                                         param = param + c;
1748                                 }
1749                                 if ((param == ":") && (item > 0))
1750                                 {
1751                                         param = "";
1752                                         while (!s.eof())
1753                                         {
1754                                                 s.get(c);
1755                                                 if (!s.eof())
1756                                                 {
1757                                                         param = param + c;
1758                                                 }
1759                                         }
1760                                         n.push_back(param);
1761                                         param = "";
1762                                 }
1763                         }
1764                 }
1765                 if (param != "")
1766                 {
1767                         n.push_back(param);
1768                 }
1769                 return;
1770         }
1771
1772         bool ProcessLine(std::string line)
1773         {
1774                 char* l = (char*)line.c_str();
1775                 while ((strlen(l)) && (l[strlen(l)-1] == '\r') || (l[strlen(l)-1] == '\n'))
1776                         l[strlen(l)-1] = '\0';
1777                 line = l;
1778                 if (line == "")
1779                         return true;
1780                 Srv->Log(DEBUG,"IN: "+line);
1781                 std::deque<std::string> params;
1782                 this->Split(line,true,params);
1783                 std::string command = "";
1784                 std::string prefix = "";
1785                 if (((params[0].c_str())[0] == ':') && (params.size() > 1))
1786                 {
1787                         prefix = params[0];
1788                         command = params[1];
1789                         char* pref = (char*)prefix.c_str();
1790                         prefix = ++pref;
1791                         params.pop_front();
1792                         params.pop_front();
1793                 }
1794                 else
1795                 {
1796                         prefix = "";
1797                         command = params[0];
1798                         params.pop_front();
1799                 }
1800
1801                 if ((!this->ctx) && (command == "AES"))
1802                 {
1803                         std::string sserv = params[0];
1804                         for (std::vector<Link>::iterator x = LinkBlocks.begin(); x < LinkBlocks.end(); x++)
1805                         {
1806                                 if ((x->EncryptionKey != "") && (x->Name == sserv))
1807                                 {
1808                                         this->InitAES(x->EncryptionKey,sserv);
1809                                 }
1810                         }
1811                         return true;
1812                 }
1813                 else if ((this->ctx) && (command == "AES"))
1814                 {
1815                         WriteOpers("*** \2AES\2: Encryption already enabled on this connection yet %s is trying to enable it twice!",params[0].c_str());
1816                 }
1817
1818                 switch (this->LinkState)
1819                 {
1820                         TreeServer* Node;
1821                         
1822                         case WAIT_AUTH_1:
1823                                 // Waiting for SERVER command from remote server. Server initiating
1824                                 // the connection sends the first SERVER command, listening server
1825                                 // replies with theirs if its happy, then if the initiator is happy,
1826                                 // it starts to send its net sync, which starts the merge, otherwise
1827                                 // it sends an ERROR.
1828                                 if (command == "PASS")
1829                                 {
1830                                         /* Silently ignored */
1831                                 }
1832                                 else if (command == "SERVER")
1833                                 {
1834                                         return this->Inbound_Server(params);
1835                                 }
1836                                 else if (command == "ERROR")
1837                                 {
1838                                         return this->Error(params);
1839                                 }
1840                                 else if (command == "USER")
1841                                 {
1842                                         this->WriteLine("ERROR :Client connections to this port are prohibited.");
1843                                         return false;
1844                                 }
1845                                 else if (command == "CAPAB")
1846                                 {
1847                                         return this->Capab(params);
1848                                 }
1849                                 else
1850                                 {
1851                                         this->WriteLine("ERROR :Invalid command in negotiation phase.");
1852                                         return false;
1853                                 }
1854                         break;
1855                         case WAIT_AUTH_2:
1856                                 // Waiting for start of other side's netmerge to say they liked our
1857                                 // password.
1858                                 if (command == "SERVER")
1859                                 {
1860                                         // cant do this, they sent it to us in the WAIT_AUTH_1 state!
1861                                         // silently ignore.
1862                                         return true;
1863                                 }
1864                                 else if (command == "BURST")
1865                                 {
1866                                         this->LinkState = CONNECTED;
1867                                         Node = new TreeServer(InboundServerName,InboundDescription,TreeRoot,this);
1868                                         TreeRoot->AddChild(Node);
1869                                         params.clear();
1870                                         params.push_back(InboundServerName);
1871                                         params.push_back("*");
1872                                         params.push_back("1");
1873                                         params.push_back(":"+InboundDescription);
1874                                         DoOneToAllButSender(TreeRoot->GetName(),"SERVER",params,InboundServerName);
1875                                         this->bursting = true;
1876                                         this->DoBurst(Node);
1877                                 }
1878                                 else if (command == "ERROR")
1879                                 {
1880                                         return this->Error(params);
1881                                 }
1882                                 else if (command == "CAPAB")
1883                                 {
1884                                         return this->Capab(params);
1885                                 }
1886                                 
1887                         break;
1888                         case LISTENER:
1889                                 this->WriteLine("ERROR :Internal error -- listening socket accepted its own descriptor!!!");
1890                                 return false;
1891                         break;
1892                         case CONNECTING:
1893                                 if (command == "SERVER")
1894                                 {
1895                                         // another server we connected to, which was in WAIT_AUTH_1 state,
1896                                         // has just sent us their credentials. If we get this far, theyre
1897                                         // happy with OUR credentials, and they are now in WAIT_AUTH_2 state.
1898                                         // if we're happy with this, we should send our netburst which
1899                                         // kickstarts the merge.
1900                                         return this->Outbound_Reply_Server(params);
1901                                 }
1902                                 else if (command == "ERROR")
1903                                 {
1904                                         return this->Error(params);
1905                                 }
1906                         break;
1907                         case CONNECTED:
1908                                 // This is the 'authenticated' state, when all passwords
1909                                 // have been exchanged and anything past this point is taken
1910                                 // as gospel.
1911                                 
1912                                 if (prefix != "")
1913                                 {
1914                                         std::string direction = prefix;
1915                                         userrec* t = Srv->FindNick(prefix);
1916                                         if (t)
1917                                         {
1918                                                 direction = t->server;
1919                                         }
1920                                         TreeServer* route_back_again = BestRouteTo(direction);
1921                                         if ((!route_back_again) || (route_back_again->GetSocket() != this))
1922                                         {
1923                                                 if (route_back_again)
1924                                                 {
1925                                                         WriteOpers("*** Protocol violation: Fake direction in command '%s' from connection '%s'",line.c_str(),this->GetName().c_str());
1926                                                 }
1927                                                 else
1928                                                 {
1929                                                         WriteOpers("*** Protocol violation: Invalid source '%s' in command '%s' from connection '%s'",direction.c_str(),line.c_str(),this->GetName().c_str());
1930                                                 }
1931                                                 
1932                                                 return true;
1933                                         }
1934                                 }
1935                                 
1936                                 if (command == "SVSMODE")
1937                                 {
1938                                         /* Services expects us to implement
1939                                          * SVSMODE. In inspircd its the same as
1940                                          * MODE anyway.
1941                                          */
1942                                         command = "MODE";
1943                                 }
1944                                 std::string target = "";
1945                                 /* Yes, know, this is a mess. Its reasonably fast though as we're
1946                                  * working with std::string here.
1947                                  */
1948                                 if ((command == "NICK") && (params.size() > 1))
1949                                 {
1950                                         return this->IntroduceClient(prefix,params);
1951                                 }
1952                                 else if (command == "FJOIN")
1953                                 {
1954                                         return this->ForceJoin(prefix,params);
1955                                 }
1956                                 else if (command == "SERVER")
1957                                 {
1958                                         return this->RemoteServer(prefix,params);
1959                                 }
1960                                 else if (command == "ERROR")
1961                                 {
1962                                         return this->Error(params);
1963                                 }
1964                                 else if (command == "OPERTYPE")
1965                                 {
1966                                         return this->OperType(prefix,params);
1967                                 }
1968                                 else if (command == "FMODE")
1969                                 {
1970                                         return this->ForceMode(prefix,params);
1971                                 }
1972                                 else if (command == "KILL")
1973                                 {
1974                                         return this->RemoteKill(prefix,params);
1975                                 }
1976                                 else if (command == "FTOPIC")
1977                                 {
1978                                         return this->ForceTopic(prefix,params);
1979                                 }
1980                                 else if (command == "REHASH")
1981                                 {
1982                                         return this->RemoteRehash(prefix,params);
1983                                 }
1984                                 else if (command == "METADATA")
1985                                 {
1986                                         return this->MetaData(prefix,params);
1987                                 }
1988                                 else if (command == "PING")
1989                                 {
1990                                         return this->LocalPing(prefix,params);
1991                                 }
1992                                 else if (command == "PONG")
1993                                 {
1994                                         return this->LocalPong(prefix,params);
1995                                 }
1996                                 else if (command == "VERSION")
1997                                 {
1998                                         return this->ServerVersion(prefix,params);
1999                                 }
2000                                 else if (command == "FHOST")
2001                                 {
2002                                         return this->ChangeHost(prefix,params);
2003                                 }
2004                                 else if (command == "FNAME")
2005                                 {
2006                                         return this->ChangeName(prefix,params);
2007                                 }
2008                                 else if (command == "ADDLINE")
2009                                 {
2010                                         return this->AddLine(prefix,params);
2011                                 }
2012                                 else if (command == "SVSNICK")
2013                                 {
2014                                         if (prefix == "")
2015                                         {
2016                                                 prefix = this->GetName();
2017                                         }
2018                                         return this->ForceNick(prefix,params);
2019                                 }
2020                                 else if (command == "IDLE")
2021                                 {
2022                                         return this->Whois(prefix,params);
2023                                 }
2024                                 else if (command == "SVSJOIN")
2025                                 {
2026                                         if (prefix == "")
2027                                         {
2028                                                 prefix = this->GetName();
2029                                         }
2030                                         return this->ServiceJoin(prefix,params);
2031                                 }
2032                                 else if (command == "SQUIT")
2033                                 {
2034                                         if (params.size() == 2)
2035                                         {
2036                                                 this->Squit(FindServer(params[0]),params[1]);
2037                                         }
2038                                         return true;
2039                                 }
2040                                 else if (command == "ENDBURST")
2041                                 {
2042                                         this->bursting = false;
2043                                         return true;
2044                                 }
2045                                 else
2046                                 {
2047                                         // not a special inter-server command.
2048                                         // Emulate the actual user doing the command,
2049                                         // this saves us having a huge ugly parser.
2050                                         userrec* who = Srv->FindNick(prefix);
2051                                         std::string sourceserv = this->myhost;
2052                                         if (this->InboundServerName != "")
2053                                         {
2054                                                 sourceserv = this->InboundServerName;
2055                                         }
2056                                         if (who)
2057                                         {
2058                                                 // its a user
2059                                                 target = who->server;
2060                                                 char* strparams[127];
2061                                                 for (unsigned int q = 0; q < params.size(); q++)
2062                                                 {
2063                                                         strparams[q] = (char*)params[q].c_str();
2064                                                 }
2065                                                 Srv->CallCommandHandler(command, strparams, params.size(), who);
2066                                         }
2067                                         else
2068                                         {
2069                                                 // its not a user. Its either a server, or somethings screwed up.
2070                                                 if (IsServer(prefix))
2071                                                 {
2072                                                         target = Srv->GetServerName();
2073                                                 }
2074                                                 else
2075                                                 {
2076                                                         log(DEBUG,"Command with unknown origin '%s'",prefix.c_str());
2077                                                         return true;
2078                                                 }
2079                                         }
2080                                         return DoOneToAllButSenderRaw(line,sourceserv,prefix,command,params);
2081
2082                                 }
2083                                 return true;
2084                         break;
2085                 }
2086                 return true;
2087         }
2088
2089         virtual std::string GetName()
2090         {
2091                 std::string sourceserv = this->myhost;
2092                 if (this->InboundServerName != "")
2093                 {
2094                         sourceserv = this->InboundServerName;
2095                 }
2096                 return sourceserv;
2097         }
2098
2099         virtual void OnTimeout()
2100         {
2101                 if (this->LinkState == CONNECTING)
2102                 {
2103                         Srv->SendOpers("*** CONNECT: Connection to \002"+myhost+"\002 timed out.");
2104                 }
2105         }
2106
2107         virtual void OnClose()
2108         {
2109                 // Connection closed.
2110                 // If the connection is fully up (state CONNECTED)
2111                 // then propogate a netsplit to all peers.
2112                 std::string quitserver = this->myhost;
2113                 if (this->InboundServerName != "")
2114                 {
2115                         quitserver = this->InboundServerName;
2116                 }
2117                 TreeServer* s = FindServer(quitserver);
2118                 if (s)
2119                 {
2120                         Squit(s,"Remote host closed the connection");
2121                 }
2122                 WriteOpers("Server '\2%s\2[%s]' closed the connection.",quitserver.c_str(),this->GetIP().c_str());
2123         }
2124
2125         virtual int OnIncomingConnection(int newsock, char* ip)
2126         {
2127                 TreeSocket* s = new TreeSocket(newsock, ip);
2128                 Srv->AddSocket(s);
2129                 return true;
2130         }
2131 };
2132
2133 void AddThisServer(TreeServer* server, std::deque<TreeServer*> &list)
2134 {
2135         for (unsigned int c = 0; c < list.size(); c++)
2136         {
2137                 if (list[c] == server)
2138                 {
2139                         return;
2140                 }
2141         }
2142         list.push_back(server);
2143 }
2144
2145 // returns a list of DIRECT servernames for a specific channel
2146 void GetListOfServersForChannel(chanrec* c, std::deque<TreeServer*> &list)
2147 {
2148         std::vector<char*> *ulist = c->GetUsers();
2149         unsigned int ucount = ulist->size();
2150         for (unsigned int i = 0; i < ucount; i++)
2151         {
2152                 char* o = (*ulist)[i];
2153                 userrec* otheruser = (userrec*)o;
2154                 if (otheruser->fd < 0)
2155                 {
2156                         TreeServer* best = BestRouteTo(otheruser->server);
2157                         if (best)
2158                                 AddThisServer(best,list);
2159                 }
2160         }
2161         return;
2162 }
2163
2164 bool DoOneToAllButSenderRaw(std::string data, std::string omit, std::string prefix, std::string command, std::deque<std::string> &params)
2165 {
2166         TreeServer* omitroute = BestRouteTo(omit);
2167         if ((command == "NOTICE") || (command == "PRIVMSG"))
2168         {
2169                 if ((params.size() >= 2) && (*(params[0].c_str()) != '$'))
2170                 {
2171                         if (*(params[0].c_str()) != '#')
2172                         {
2173                                 // special routing for private messages/notices
2174                                 userrec* d = Srv->FindNick(params[0]);
2175                                 if (d)
2176                                 {
2177                                         std::deque<std::string> par;
2178                                         par.push_back(params[0]);
2179                                         par.push_back(":"+params[1]);
2180                                         DoOneToOne(prefix,command,par,d->server);
2181                                         return true;
2182                                 }
2183                         }
2184                         else
2185                         {
2186                                 log(DEBUG,"Channel privmsg going to chan %s",params[0].c_str());
2187                                 chanrec* c = Srv->FindChannel(params[0]);
2188                                 if (c)
2189                                 {
2190                                         std::deque<TreeServer*> list;
2191                                         GetListOfServersForChannel(c,list);
2192                                         log(DEBUG,"Got a list of %d servers",list.size());
2193                                         unsigned int lsize = list.size();
2194                                         for (unsigned int i = 0; i < lsize; i++)
2195                                         {
2196                                                 TreeSocket* Sock = list[i]->GetSocket();
2197                                                 if ((Sock) && (list[i]->GetName() != omit) && (omitroute != list[i]))
2198                                                 {
2199                                                         log(DEBUG,"Writing privmsg to server %s",list[i]->GetName().c_str());
2200                                                         Sock->WriteLine(data);
2201                                                 }
2202                                         }
2203                                         return true;
2204                                 }
2205                         }
2206                 }
2207         }
2208         unsigned int items = TreeRoot->ChildCount();
2209         for (unsigned int x = 0; x < items; x++)
2210         {
2211                 TreeServer* Route = TreeRoot->GetChild(x);
2212                 if ((Route->GetSocket()) && (Route->GetName() != omit) && (omitroute != Route))
2213                 {
2214                         TreeSocket* Sock = Route->GetSocket();
2215                         Sock->WriteLine(data);
2216                 }
2217         }
2218         return true;
2219 }
2220
2221 bool DoOneToAllButSender(std::string prefix, std::string command, std::deque<std::string> &params, std::string omit)
2222 {
2223         TreeServer* omitroute = BestRouteTo(omit);
2224         std::string FullLine = ":" + prefix + " " + command;
2225         unsigned int words = params.size();
2226         for (unsigned int x = 0; x < words; x++)
2227         {
2228                 FullLine = FullLine + " " + params[x];
2229         }
2230         unsigned int items = TreeRoot->ChildCount();
2231         for (unsigned int x = 0; x < items; x++)
2232         {
2233                 TreeServer* Route = TreeRoot->GetChild(x);
2234                 // Send the line IF:
2235                 // The route has a socket (its a direct connection)
2236                 // The route isnt the one to be omitted
2237                 // The route isnt the path to the one to be omitted
2238                 if ((Route->GetSocket()) && (Route->GetName() != omit) && (omitroute != Route))
2239                 {
2240                         TreeSocket* Sock = Route->GetSocket();
2241                         Sock->WriteLine(FullLine);
2242                 }
2243         }
2244         return true;
2245 }
2246
2247 bool DoOneToMany(std::string prefix, std::string command, std::deque<std::string> &params)
2248 {
2249         std::string FullLine = ":" + prefix + " " + command;
2250         unsigned int words = params.size();
2251         for (unsigned int x = 0; x < words; x++)
2252         {
2253                 FullLine = FullLine + " " + params[x];
2254         }
2255         unsigned int items = TreeRoot->ChildCount();
2256         for (unsigned int x = 0; x < items; x++)
2257         {
2258                 TreeServer* Route = TreeRoot->GetChild(x);
2259                 if (Route->GetSocket())
2260                 {
2261                         TreeSocket* Sock = Route->GetSocket();
2262                         Sock->WriteLine(FullLine);
2263                 }
2264         }
2265         return true;
2266 }
2267
2268 bool DoOneToOne(std::string prefix, std::string command, std::deque<std::string> &params, std::string target)
2269 {
2270         TreeServer* Route = BestRouteTo(target);
2271         if (Route)
2272         {
2273                 std::string FullLine = ":" + prefix + " " + command;
2274                 unsigned int words = params.size();
2275                 for (unsigned int x = 0; x < words; x++)
2276                 {
2277                         FullLine = FullLine + " " + params[x];
2278                 }
2279                 if (Route->GetSocket())
2280                 {
2281                         TreeSocket* Sock = Route->GetSocket();
2282                         Sock->WriteLine(FullLine);
2283                 }
2284                 return true;
2285         }
2286         else
2287         {
2288                 return true;
2289         }
2290 }
2291
2292 std::vector<TreeSocket*> Bindings;
2293
2294 void ReadConfiguration(bool rebind)
2295 {
2296         Conf = new ConfigReader;
2297         if (rebind)
2298         {
2299                 for (int j =0; j < Conf->Enumerate("bind"); j++)
2300                 {
2301                         std::string Type = Conf->ReadValue("bind","type",j);
2302                         std::string IP = Conf->ReadValue("bind","address",j);
2303                         long Port = Conf->ReadInteger("bind","port",j,true);
2304                         if (Type == "servers")
2305                         {
2306                                 if (IP == "*")
2307                                 {
2308                                         IP = "";
2309                                 }
2310                                 TreeSocket* listener = new TreeSocket(IP.c_str(),Port,true,10);
2311                                 if (listener->GetState() == I_LISTENING)
2312                                 {
2313                                         Srv->AddSocket(listener);
2314                                         Bindings.push_back(listener);
2315                                 }
2316                                 else
2317                                 {
2318                                         log(DEFAULT,"m_spanningtree: Warning: Failed to bind server port %d",Port);
2319                                         listener->Close();
2320                                         delete listener;
2321                                 }
2322                         }
2323                 }
2324         }
2325         LinkBlocks.clear();
2326         for (int j =0; j < Conf->Enumerate("link"); j++)
2327         {
2328                 Link L;
2329                 L.Name = Conf->ReadValue("link","name",j);
2330                 L.IPAddr = Conf->ReadValue("link","ipaddr",j);
2331                 L.Port = Conf->ReadInteger("link","port",j,true);
2332                 L.SendPass = Conf->ReadValue("link","sendpass",j);
2333                 L.RecvPass = Conf->ReadValue("link","recvpass",j);
2334                 L.AutoConnect = Conf->ReadInteger("link","autoconnect",j,true);
2335                 L.EncryptionKey =  Conf->ReadValue("link","encryptionkey",j);
2336                 L.NextConnectTime = time(NULL) + L.AutoConnect;
2337                 /* Bugfix by brain, do not allow people to enter bad configurations */
2338                 if ((L.RecvPass != "") && (L.SendPass != "") && (L.Name != "") && (L.Port))
2339                 {
2340                         LinkBlocks.push_back(L);
2341                         log(DEBUG,"m_spanningtree: Read server %s with host %s:%d",L.Name.c_str(),L.IPAddr.c_str(),L.Port);
2342                 }
2343                 else
2344                 {
2345                         log(DEFAULT,"m_spanningtree: Invalid configuration for server '%s', ignored!",L.Name.c_str());
2346                 }
2347         }
2348         delete Conf;
2349 }
2350
2351
2352 class ModuleSpanningTree : public Module
2353 {
2354         std::vector<TreeSocket*> Bindings;
2355         int line;
2356         int NumServers;
2357
2358  public:
2359
2360         ModuleSpanningTree(Server* Me)
2361                 : Module::Module(Me)
2362         {
2363                 Srv = Me;
2364                 Bindings.clear();
2365
2366                 // Create the root of the tree
2367                 TreeRoot = new TreeServer(Srv->GetServerName(),Srv->GetServerDescription());
2368
2369                 ReadConfiguration(true);
2370         }
2371
2372         void ShowLinks(TreeServer* Current, userrec* user, int hops)
2373         {
2374                 std::string Parent = TreeRoot->GetName();
2375                 if (Current->GetParent())
2376                 {
2377                         Parent = Current->GetParent()->GetName();
2378                 }
2379                 for (unsigned int q = 0; q < Current->ChildCount(); q++)
2380                 {
2381                         ShowLinks(Current->GetChild(q),user,hops+1);
2382                 }
2383                 WriteServ(user->fd,"364 %s %s %s :%d %s",user->nick,Current->GetName().c_str(),Parent.c_str(),hops,Current->GetDesc().c_str());
2384         }
2385
2386         int CountLocalServs()
2387         {
2388                 return TreeRoot->ChildCount();
2389         }
2390
2391         int CountServs()
2392         {
2393                 return serverlist.size();
2394         }
2395
2396         void HandleLinks(char** parameters, int pcnt, userrec* user)
2397         {
2398                 ShowLinks(TreeRoot,user,0);
2399                 WriteServ(user->fd,"365 %s * :End of /LINKS list.",user->nick);
2400                 return;
2401         }
2402
2403         void HandleLusers(char** parameters, int pcnt, userrec* user)
2404         {
2405                 WriteServ(user->fd,"251 %s :There are %d users and %d invisible on %d servers",user->nick,usercnt()-usercount_invisible(),usercount_invisible(),this->CountServs());
2406                 WriteServ(user->fd,"252 %s %d :operator(s) online",user->nick,usercount_opers());
2407                 WriteServ(user->fd,"253 %s %d :unknown connections",user->nick,usercount_unknown());
2408                 WriteServ(user->fd,"254 %s %d :channels formed",user->nick,chancount());
2409                 WriteServ(user->fd,"254 %s :I have %d clients and %d servers",user->nick,local_count(),this->CountLocalServs());
2410                 return;
2411         }
2412
2413         // WARNING: NOT THREAD SAFE - DONT GET ANY SMART IDEAS.
2414
2415         void ShowMap(TreeServer* Current, userrec* user, int depth, char matrix[128][80])
2416         {
2417                 if (line < 128)
2418                 {
2419                         for (int t = 0; t < depth; t++)
2420                         {
2421                                 matrix[line][t] = ' ';
2422                         }
2423                         strlcpy(&matrix[line][depth],Current->GetName().c_str(),80);
2424                         line++;
2425                         for (unsigned int q = 0; q < Current->ChildCount(); q++)
2426                         {
2427                                 ShowMap(Current->GetChild(q),user,depth+2,matrix);
2428                         }
2429                 }
2430         }
2431
2432         // Ok, prepare to be confused.
2433         // After much mulling over how to approach this, it struck me that
2434         // the 'usual' way of doing a /MAP isnt the best way. Instead of
2435         // keeping track of a ton of ascii characters, and line by line
2436         // under recursion working out where to place them using multiplications
2437         // and divisons, we instead render the map onto a backplane of characters
2438         // (a character matrix), then draw the branches as a series of "L" shapes
2439         // from the nodes. This is not only friendlier on CPU it uses less stack.
2440
2441         void HandleMap(char** parameters, int pcnt, userrec* user)
2442         {
2443                 // This array represents a virtual screen which we will
2444                 // "scratch" draw to, as the console device of an irc
2445                 // client does not provide for a proper terminal.
2446                 char matrix[128][80];
2447                 for (unsigned int t = 0; t < 128; t++)
2448                 {
2449                         matrix[t][0] = '\0';
2450                 }
2451                 line = 0;
2452                 // The only recursive bit is called here.
2453                 ShowMap(TreeRoot,user,0,matrix);
2454                 // Process each line one by one. The algorithm has a limit of
2455                 // 128 servers (which is far more than a spanning tree should have
2456                 // anyway, so we're ok). This limit can be raised simply by making
2457                 // the character matrix deeper, 128 rows taking 10k of memory.
2458                 for (int l = 1; l < line; l++)
2459                 {
2460                         // scan across the line looking for the start of the
2461                         // servername (the recursive part of the algorithm has placed
2462                         // the servers at indented positions depending on what they
2463                         // are related to)
2464                         int first_nonspace = 0;
2465                         while (matrix[l][first_nonspace] == ' ')
2466                         {
2467                                 first_nonspace++;
2468                         }
2469                         first_nonspace--;
2470                         // Draw the `- (corner) section: this may be overwritten by
2471                         // another L shape passing along the same vertical pane, becoming
2472                         // a |- (branch) section instead.
2473                         matrix[l][first_nonspace] = '-';
2474                         matrix[l][first_nonspace-1] = '`';
2475                         int l2 = l - 1;
2476                         // Draw upwards until we hit the parent server, causing possibly
2477                         // other corners (`-) to become branches (|-)
2478                         while ((matrix[l2][first_nonspace-1] == ' ') || (matrix[l2][first_nonspace-1] == '`'))
2479                         {
2480                                 matrix[l2][first_nonspace-1] = '|';
2481                                 l2--;
2482                         }
2483                 }
2484                 // dump the whole lot to the user. This is the easy bit, honest.
2485                 for (int t = 0; t < line; t++)
2486                 {
2487                         WriteServ(user->fd,"006 %s :%s",user->nick,&matrix[t][0]);
2488                 }
2489                 WriteServ(user->fd,"007 %s :End of /MAP",user->nick);
2490                 return;
2491         }
2492
2493         int HandleSquit(char** parameters, int pcnt, userrec* user)
2494         {
2495                 TreeServer* s = FindServerMask(parameters[0]);
2496                 if (s)
2497                 {
2498                         if (s == TreeRoot)
2499                         {
2500                                  WriteServ(user->fd,"NOTICE %s :*** SQUIT: Foolish mortal, you cannot make a server SQUIT itself! (%s matches local server name)",user->nick,parameters[0]);
2501                                 return 1;
2502                         }
2503                         TreeSocket* sock = s->GetSocket();
2504                         if (sock)
2505                         {
2506                                 log(DEBUG,"Splitting server %s",s->GetName().c_str());
2507                                 WriteOpers("*** SQUIT: Server \002%s\002 removed from network by %s",parameters[0],user->nick);
2508                                 sock->Squit(s,"Server quit by "+std::string(user->nick)+"!"+std::string(user->ident)+"@"+std::string(user->host));
2509                                 sock->Close();
2510                         }
2511                         else
2512                         {
2513                                 WriteServ(user->fd,"NOTICE %s :*** SQUIT: The server \002%s\002 is not directly connected.",user->nick,parameters[0]);
2514                         }
2515                 }
2516                 else
2517                 {
2518                          WriteServ(user->fd,"NOTICE %s :*** SQUIT: The server \002%s\002 does not exist on the network.",user->nick,parameters[0]);
2519                 }
2520                 return 1;
2521         }
2522
2523         int HandleRemoteWhois(char** parameters, int pcnt, userrec* user)
2524         {
2525                 if ((user->fd > -1) && (pcnt > 1))
2526                 {
2527                         userrec* remote = Srv->FindNick(parameters[1]);
2528                         if ((remote) && (remote->fd < 0))
2529                         {
2530                                 std::deque<std::string> params;
2531                                 params.push_back(parameters[1]);
2532                                 DoOneToOne(user->nick,"IDLE",params,remote->server);
2533                                 return 1;
2534                         }
2535                         else if (!remote)
2536                         {
2537                                 WriteServ(user->fd,"401 %s %s :No such nick/channel",user->nick, parameters[1]);
2538                                 WriteServ(user->fd,"318 %s %s :End of /WHOIS list.",user->nick, parameters[1]);
2539                                 return 1;
2540                         }
2541                 }
2542                 return 0;
2543         }
2544
2545         void DoPingChecks(time_t curtime)
2546         {
2547                 for (unsigned int j = 0; j < TreeRoot->ChildCount(); j++)
2548                 {
2549                         TreeServer* serv = TreeRoot->GetChild(j);
2550                         TreeSocket* sock = serv->GetSocket();
2551                         if (sock)
2552                         {
2553                                 if (curtime >= serv->NextPingTime())
2554                                 {
2555                                         if (serv->AnsweredLastPing())
2556                                         {
2557                                                 sock->WriteLine(":"+Srv->GetServerName()+" PING "+serv->GetName());
2558                                                 serv->SetNextPingTime(curtime + 60);
2559                                         }
2560                                         else
2561                                         {
2562                                                 // they didnt answer, boot them
2563                                                 WriteOpers("*** Server \002%s\002 pinged out",serv->GetName().c_str());
2564                                                 sock->Squit(serv,"Ping timeout");
2565                                                 sock->Close();
2566                                                 return;
2567                                         }
2568                                 }
2569                         }
2570                 }
2571         }
2572
2573         void AutoConnectServers(time_t curtime)
2574         {
2575                 for (std::vector<Link>::iterator x = LinkBlocks.begin(); x < LinkBlocks.end(); x++)
2576                 {
2577                         if ((x->AutoConnect) && (curtime >= x->NextConnectTime))
2578                         {
2579                                 log(DEBUG,"Auto-Connecting %s",x->Name.c_str());
2580                                 x->NextConnectTime = curtime + x->AutoConnect;
2581                                 TreeServer* CheckDupe = FindServer(x->Name);
2582                                 if (!CheckDupe)
2583                                 {
2584                                         // an autoconnected server is not connected. Check if its time to connect it
2585                                         WriteOpers("*** AUTOCONNECT: Auto-connecting server \002%s\002 (%lu seconds until next attempt)",x->Name.c_str(),x->AutoConnect);
2586                                         TreeSocket* newsocket = new TreeSocket(x->IPAddr,x->Port,false,10,x->Name);
2587                                         Srv->AddSocket(newsocket);
2588                                 }
2589                         }
2590                 }
2591         }
2592
2593         int HandleVersion(char** parameters, int pcnt, userrec* user)
2594         {
2595                 // we've already checked if pcnt > 0, so this is safe
2596                 TreeServer* found = FindServerMask(parameters[0]);
2597                 if (found)
2598                 {
2599                         std::string Version = found->GetVersion();
2600                         WriteServ(user->fd,"351 %s :%s",user->nick,Version.c_str());
2601                 }
2602                 else
2603                 {
2604                         WriteServ(user->fd,"402 %s %s :No such server",user->nick,parameters[0]);
2605                 }
2606                 return 1;
2607         }
2608         
2609         int HandleConnect(char** parameters, int pcnt, userrec* user)
2610         {
2611                 for (std::vector<Link>::iterator x = LinkBlocks.begin(); x < LinkBlocks.end(); x++)
2612                 {
2613                         if (Srv->MatchText(x->Name.c_str(),parameters[0]))
2614                         {
2615                                 TreeServer* CheckDupe = FindServer(x->Name);
2616                                 if (!CheckDupe)
2617                                 {
2618                                         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);
2619                                         TreeSocket* newsocket = new TreeSocket(x->IPAddr,x->Port,false,10,x->Name);
2620                                         Srv->AddSocket(newsocket);
2621                                         return 1;
2622                                 }
2623                                 else
2624                                 {
2625                                         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());
2626                                         return 1;
2627                                 }
2628                         }
2629                 }
2630                 WriteServ(user->fd,"NOTICE %s :*** CONNECT: No server matching \002%s\002 could be found in the config file.",user->nick,parameters[0]);
2631                 return 1;
2632         }
2633
2634         virtual bool HandleStats(char ** parameters, int pcnt, userrec* user)
2635         {
2636                 if (*parameters[0] == 'c')
2637                 {
2638                         for (unsigned int i = 0; i < LinkBlocks.size(); i++)
2639                         {
2640                                 WriteServ(user->fd,"213 %s C *@%s * %s %d 0 %s",user->nick,LinkBlocks[i].IPAddr.c_str(),LinkBlocks[i].Name.c_str(),LinkBlocks[i].Port,(LinkBlocks[i].EncryptionKey != "" ? "es" : " s"));
2641                                 WriteServ(user->fd,"244 %s H * * %s",user->nick,LinkBlocks[i].Name.c_str());
2642                         }
2643                         WriteServ(user->fd,"219 %s %s :End of /STATS report",user->nick,parameters[0]);
2644                         WriteOpers("*** Notice: Stats '%s' requested by %s (%s@%s)",parameters[0],user->nick,user->ident,user->host);
2645                         return true;
2646                 }
2647                 return false;
2648         }
2649
2650         virtual int OnPreCommand(std::string command, char **parameters, int pcnt, userrec *user, bool validated)
2651         {
2652                 /* If the command doesnt appear to be valid, we dont want to mess with it. */
2653                 if (!validated)
2654                         return 0;
2655
2656                 if (command == "CONNECT")
2657                 {
2658                         return this->HandleConnect(parameters,pcnt,user);
2659                 }
2660                 else if (command == "SQUIT")
2661                 {
2662                         return this->HandleSquit(parameters,pcnt,user);
2663                 }
2664                 else if (command == "STATS")
2665                 {
2666                         return this->HandleStats(parameters,pcnt,user);
2667                 }
2668                 else if (command == "MAP")
2669                 {
2670                         this->HandleMap(parameters,pcnt,user);
2671                         return 1;
2672                 }
2673                 else if (command == "LUSERS")
2674                 {
2675                         this->HandleLusers(parameters,pcnt,user);
2676                         return 1;
2677                 }
2678                 else if (command == "LINKS")
2679                 {
2680                         this->HandleLinks(parameters,pcnt,user);
2681                         return 1;
2682                 }
2683                 else if (command == "WHOIS")
2684                 {
2685                         if (pcnt > 1)
2686                         {
2687                                 // remote whois
2688                                 return this->HandleRemoteWhois(parameters,pcnt,user);
2689                         }
2690                 }
2691                 else if ((command == "VERSION") && (pcnt > 0))
2692                 {
2693                         this->HandleVersion(parameters,pcnt,user);
2694                         return 1;
2695                 }
2696                 else if (Srv->IsValidModuleCommand(command, pcnt, user))
2697                 {
2698                         // this bit of code cleverly routes all module commands
2699                         // to all remote severs *automatically* so that modules
2700                         // can just handle commands locally, without having
2701                         // to have any special provision in place for remote
2702                         // commands and linking protocols.
2703                         std::deque<std::string> params;
2704                         params.clear();
2705                         for (int j = 0; j < pcnt; j++)
2706                         {
2707                                 if (strchr(parameters[j],' '))
2708                                 {
2709                                         params.push_back(":" + std::string(parameters[j]));
2710                                 }
2711                                 else
2712                                 {
2713                                         params.push_back(std::string(parameters[j]));
2714                                 }
2715                         }
2716                         DoOneToMany(user->nick,command,params);
2717                 }
2718                 return 0;
2719         }
2720
2721         virtual void OnGetServerDescription(std::string servername,std::string &description)
2722         {
2723                 TreeServer* s = FindServer(servername);
2724                 if (s)
2725                 {
2726                         description = s->GetDesc();
2727                 }
2728         }
2729
2730         virtual void OnUserInvite(userrec* source,userrec* dest,chanrec* channel)
2731         {
2732                 if (source->fd > -1)
2733                 {
2734                         std::deque<std::string> params;
2735                         params.push_back(dest->nick);
2736                         params.push_back(channel->name);
2737                         DoOneToMany(source->nick,"INVITE",params);
2738                 }
2739         }
2740
2741         virtual void OnPostLocalTopicChange(userrec* user, chanrec* chan, std::string topic)
2742         {
2743                 std::deque<std::string> params;
2744                 params.push_back(chan->name);
2745                 params.push_back(":"+topic);
2746                 DoOneToMany(user->nick,"TOPIC",params);
2747         }
2748
2749         virtual void OnWallops(userrec* user, std::string text)
2750         {
2751                 if (user->fd > -1)
2752                 {
2753                         std::deque<std::string> params;
2754                         params.push_back(":"+text);
2755                         DoOneToMany(user->nick,"WALLOPS",params);
2756                 }
2757         }
2758
2759         virtual void OnUserNotice(userrec* user, void* dest, int target_type, std::string text)
2760         {
2761                 if (target_type == TYPE_USER)
2762                 {
2763                         userrec* d = (userrec*)dest;
2764                         if ((d->fd < 0) && (user->fd > -1))
2765                         {
2766                                 std::deque<std::string> params;
2767                                 params.clear();
2768                                 params.push_back(d->nick);
2769                                 params.push_back(":"+text);
2770                                 DoOneToOne(user->nick,"NOTICE",params,d->server);
2771                         }
2772                 }
2773                 else
2774                 {
2775                         if (user->fd > -1)
2776                         {
2777                                 chanrec *c = (chanrec*)dest;
2778                                 std::deque<TreeServer*> list;
2779                                 GetListOfServersForChannel(c,list);
2780                                 unsigned int ucount = list.size();
2781                                 for (unsigned int i = 0; i < ucount; i++)
2782                                 {
2783                                         TreeSocket* Sock = list[i]->GetSocket();
2784                                         if (Sock)
2785                                                 Sock->WriteLine(":"+std::string(user->nick)+" NOTICE "+std::string(c->name)+" :"+text);
2786                                 }
2787                         }
2788                 }
2789         }
2790
2791         virtual void OnUserMessage(userrec* user, void* dest, int target_type, std::string text)
2792         {
2793                 if (target_type == TYPE_USER)
2794                 {
2795                         // route private messages which are targetted at clients only to the server
2796                         // which needs to receive them
2797                         userrec* d = (userrec*)dest;
2798                         if ((d->fd < 0) && (user->fd > -1))
2799                         {
2800                                 std::deque<std::string> params;
2801                                 params.clear();
2802                                 params.push_back(d->nick);
2803                                 params.push_back(":"+text);
2804                                 DoOneToOne(user->nick,"PRIVMSG",params,d->server);
2805                         }
2806                 }
2807                 else
2808                 {
2809                         if (user->fd > -1)
2810                         {
2811                                 chanrec *c = (chanrec*)dest;
2812                                 std::deque<TreeServer*> list;
2813                                 GetListOfServersForChannel(c,list);
2814                                 unsigned int ucount = list.size();
2815                                 for (unsigned int i = 0; i < ucount; i++)
2816                                 {
2817                                         TreeSocket* Sock = list[i]->GetSocket();
2818                                         if (Sock)
2819                                                 Sock->WriteLine(":"+std::string(user->nick)+" PRIVMSG "+std::string(c->name)+" :"+text);
2820                                 }
2821                         }
2822                 }
2823         }
2824
2825         virtual void OnBackgroundTimer(time_t curtime)
2826         {
2827                 AutoConnectServers(curtime);
2828                 DoPingChecks(curtime);
2829         }
2830
2831         virtual void OnUserJoin(userrec* user, chanrec* channel)
2832         {
2833                 // Only do this for local users
2834                 if (user->fd > -1)
2835                 {
2836                         std::deque<std::string> params;
2837                         params.clear();
2838                         params.push_back(channel->name);
2839                         if (*channel->key)
2840                         {
2841                                 // if the channel has a key, force the join by emulating the key.
2842                                 params.push_back(channel->key);
2843                         }
2844                         if (channel->GetUserCounter() > 1)
2845                         {
2846                                 // not the first in the channel
2847                                 DoOneToMany(user->nick,"JOIN",params);
2848                         }
2849                         else
2850                         {
2851                                 // first in the channel, set up their permissions
2852                                 // and the channel TS with FJOIN.
2853                                 char ts[24];
2854                                 snprintf(ts,24,"%lu",(unsigned long)channel->age);
2855                                 params.clear();
2856                                 params.push_back(channel->name);
2857                                 params.push_back(ts);
2858                                 params.push_back("@"+std::string(user->nick));
2859                                 DoOneToMany(Srv->GetServerName(),"FJOIN",params);
2860                         }
2861                 }
2862         }
2863
2864         virtual void OnChangeHost(userrec* user, std::string newhost)
2865         {
2866                 // only occurs for local clients
2867                 if (user->registered != 7)
2868                         return;
2869                 std::deque<std::string> params;
2870                 params.push_back(newhost);
2871                 DoOneToMany(user->nick,"FHOST",params);
2872         }
2873
2874         virtual void OnChangeName(userrec* user, std::string gecos)
2875         {
2876                 // only occurs for local clients
2877                 if (user->registered != 7)
2878                         return;
2879                 std::deque<std::string> params;
2880                 params.push_back(gecos);
2881                 DoOneToMany(user->nick,"FNAME",params);
2882         }
2883
2884         virtual void OnUserPart(userrec* user, chanrec* channel)
2885         {
2886                 if (user->fd > -1)
2887                 {
2888                         std::deque<std::string> params;
2889                         params.push_back(channel->name);
2890                         DoOneToMany(user->nick,"PART",params);
2891                 }
2892         }
2893
2894         virtual void OnUserConnect(userrec* user)
2895         {
2896                 char agestr[MAXBUF];
2897                 if (user->fd > -1)
2898                 {
2899                         std::deque<std::string> params;
2900                         snprintf(agestr,MAXBUF,"%lu",(unsigned long)user->age);
2901                         params.push_back(agestr);
2902                         params.push_back(user->nick);
2903                         params.push_back(user->host);
2904                         params.push_back(user->dhost);
2905                         params.push_back(user->ident);
2906                         params.push_back("+"+std::string(user->modes));
2907                         params.push_back(user->ip);
2908                         params.push_back(":"+std::string(user->fullname));
2909                         DoOneToMany(Srv->GetServerName(),"NICK",params);
2910                 }
2911         }
2912
2913         virtual void OnUserQuit(userrec* user, std::string reason)
2914         {
2915                 if ((user->fd > -1) && (user->registered == 7))
2916                 {
2917                         std::deque<std::string> params;
2918                         params.push_back(":"+reason);
2919                         DoOneToMany(user->nick,"QUIT",params);
2920                 }
2921         }
2922
2923         virtual void OnUserPostNick(userrec* user, std::string oldnick)
2924         {
2925                 if (user->fd > -1)
2926                 {
2927                         std::deque<std::string> params;
2928                         params.push_back(user->nick);
2929                         DoOneToMany(oldnick,"NICK",params);
2930                 }
2931         }
2932
2933         virtual void OnUserKick(userrec* source, userrec* user, chanrec* chan, std::string reason)
2934         {
2935                 if (source->fd > -1)
2936                 {
2937                         std::deque<std::string> params;
2938                         params.push_back(chan->name);
2939                         params.push_back(user->nick);
2940                         params.push_back(":"+reason);
2941                         DoOneToMany(source->nick,"KICK",params);
2942                 }
2943         }
2944
2945         virtual void OnRemoteKill(userrec* source, userrec* dest, std::string reason)
2946         {
2947                 std::deque<std::string> params;
2948                 params.push_back(dest->nick);
2949                 params.push_back(":"+reason);
2950                 DoOneToMany(source->nick,"KILL",params);
2951         }
2952
2953         virtual void OnRehash(std::string parameter)
2954         {
2955                 if (parameter != "")
2956                 {
2957                         std::deque<std::string> params;
2958                         params.push_back(parameter);
2959                         DoOneToMany(Srv->GetServerName(),"REHASH",params);
2960                         // check for self
2961                         if (Srv->MatchText(Srv->GetServerName(),parameter))
2962                         {
2963                                 Srv->SendOpers("*** Remote rehash initiated from server \002"+Srv->GetServerName()+"\002.");
2964                                 Srv->RehashServer();
2965                         }
2966                 }
2967                 ReadConfiguration(false);
2968         }
2969
2970         // note: the protocol does not allow direct umode +o except
2971         // via NICK with 8 params. sending OPERTYPE infers +o modechange
2972         // locally.
2973         virtual void OnOper(userrec* user, std::string opertype)
2974         {
2975                 if (user->fd > -1)
2976                 {
2977                         std::deque<std::string> params;
2978                         params.push_back(opertype);
2979                         DoOneToMany(user->nick,"OPERTYPE",params);
2980                 }
2981         }
2982
2983         void OnLine(userrec* source, std::string host, bool adding, char linetype, long duration, std::string reason)
2984         {
2985                 if (source->fd > -1)
2986                 {
2987                         char type[8];
2988                         snprintf(type,8,"%cLINE",linetype);
2989                         std::string stype = type;
2990                         if (adding)
2991                         {
2992                                 char sduration[MAXBUF];
2993                                 snprintf(sduration,MAXBUF,"%ld",duration);
2994                                 std::deque<std::string> params;
2995                                 params.push_back(host);
2996                                 params.push_back(sduration);
2997                                 params.push_back(":"+reason);
2998                                 DoOneToMany(source->nick,stype,params);
2999                         }
3000                         else
3001                         {
3002                                 std::deque<std::string> params;
3003                                 params.push_back(host);
3004                                 DoOneToMany(source->nick,stype,params);
3005                         }
3006                 }
3007         }
3008
3009         virtual void OnAddGLine(long duration, userrec* source, std::string reason, std::string hostmask)
3010         {
3011                 OnLine(source,hostmask,true,'G',duration,reason);
3012         }
3013         
3014         virtual void OnAddZLine(long duration, userrec* source, std::string reason, std::string ipmask)
3015         {
3016                 OnLine(source,ipmask,true,'Z',duration,reason);
3017         }
3018
3019         virtual void OnAddQLine(long duration, userrec* source, std::string reason, std::string nickmask)
3020         {
3021                 OnLine(source,nickmask,true,'Q',duration,reason);
3022         }
3023
3024         virtual void OnAddELine(long duration, userrec* source, std::string reason, std::string hostmask)
3025         {
3026                 OnLine(source,hostmask,true,'E',duration,reason);
3027         }
3028
3029         virtual void OnDelGLine(userrec* source, std::string hostmask)
3030         {
3031                 OnLine(source,hostmask,false,'G',0,"");
3032         }
3033
3034         virtual void OnDelZLine(userrec* source, std::string ipmask)
3035         {
3036                 OnLine(source,ipmask,false,'Z',0,"");
3037         }
3038
3039         virtual void OnDelQLine(userrec* source, std::string nickmask)
3040         {
3041                 OnLine(source,nickmask,false,'Q',0,"");
3042         }
3043
3044         virtual void OnDelELine(userrec* source, std::string hostmask)
3045         {
3046                 OnLine(source,hostmask,false,'E',0,"");
3047         }
3048
3049         virtual void OnMode(userrec* user, void* dest, int target_type, std::string text)
3050         {
3051                 if ((user->fd > -1) && (user->registered == 7))
3052                 {
3053                         if (target_type == TYPE_USER)
3054                         {
3055                                 userrec* u = (userrec*)dest;
3056                                 std::deque<std::string> params;
3057                                 params.push_back(u->nick);
3058                                 params.push_back(text);
3059                                 DoOneToMany(user->nick,"MODE",params);
3060                         }
3061                         else
3062                         {
3063                                 chanrec* c = (chanrec*)dest;
3064                                 std::deque<std::string> params;
3065                                 params.push_back(c->name);
3066                                 params.push_back(text);
3067                                 DoOneToMany(user->nick,"MODE",params);
3068                         }
3069                 }
3070         }
3071
3072         virtual void ProtoSendMode(void* opaque, int target_type, void* target, std::string modeline)
3073         {
3074                 TreeSocket* s = (TreeSocket*)opaque;
3075                 if (target)
3076                 {
3077                         if (target_type == TYPE_USER)
3078                         {
3079                                 userrec* u = (userrec*)target;
3080                                 s->WriteLine(":"+Srv->GetServerName()+" FMODE "+u->nick+" "+modeline);
3081                         }
3082                         else
3083                         {
3084                                 chanrec* c = (chanrec*)target;
3085                                 s->WriteLine(":"+Srv->GetServerName()+" FMODE "+c->name+" "+modeline);
3086                         }
3087                 }
3088         }
3089
3090         virtual void ProtoSendMetaData(void* opaque, int target_type, void* target, std::string extname, std::string extdata)
3091         {
3092                 TreeSocket* s = (TreeSocket*)opaque;
3093                 if (target)
3094                 {
3095                         if (target_type == TYPE_USER)
3096                         {
3097                                 userrec* u = (userrec*)target;
3098                                 s->WriteLine(":"+Srv->GetServerName()+" METADATA "+u->nick+" "+extname+" :"+extdata);
3099                         }
3100                         else
3101                         {
3102                                 chanrec* c = (chanrec*)target;
3103                                 s->WriteLine(":"+Srv->GetServerName()+" METADATA "+c->name+" "+extname+" :"+extdata);
3104                         }
3105                 }
3106         }
3107
3108         virtual ~ModuleSpanningTree()
3109         {
3110         }
3111
3112         virtual Version GetVersion()
3113         {
3114                 return Version(1,0,0,0,VF_STATIC|VF_VENDOR);
3115         }
3116 };
3117
3118
3119 class ModuleSpanningTreeFactory : public ModuleFactory
3120 {
3121  public:
3122         ModuleSpanningTreeFactory()
3123         {
3124         }
3125         
3126         ~ModuleSpanningTreeFactory()
3127         {
3128         }
3129         
3130         virtual Module * CreateModule(Server* Me)
3131         {
3132                 TreeProtocolModule = new ModuleSpanningTree(Me);
3133                 return TreeProtocolModule;
3134         }
3135         
3136 };
3137
3138
3139 extern "C" void * init_module( void )
3140 {
3141         return new ModuleSpanningTreeFactory;
3142 }