]> git.netwichtig.de Git - user/henk/code/inspircd.git/blob - src/modules/m_spanningtree.cpp
Fixed routing optimization #1
[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 using namespace std;
18
19 #include <stdio.h>
20 #include <vector>
21 #include <deque>
22 #include "globals.h"
23 #include "inspircd_config.h"
24 #ifdef GCC3
25 #include <ext/hash_map>
26 #else
27 #include <hash_map>
28 #endif
29 #include "users.h"
30 #include "channels.h"
31 #include "modules.h"
32 #include "socket.h"
33 #include "helperfuncs.h"
34 #include "inspircd.h"
35 #include "inspstring.h"
36 #include "hashcomp.h"
37 #include "message.h"
38
39 #ifdef GCC3
40 #define nspace __gnu_cxx
41 #else
42 #define nspace std
43 #endif
44
45 class ModuleSpanningTree;
46 static ModuleSpanningTree* TreeProtocolModule;
47
48 extern std::vector<Module*> modules;
49 extern std::vector<ircd_module*> factory;
50 extern int MODCOUNT;
51
52 enum ServerState { LISTENER, CONNECTING, WAIT_AUTH_1, WAIT_AUTH_2, CONNECTED };
53
54 typedef nspace::hash_map<std::string, userrec*, nspace::hash<string>, irc::StrHashComp> user_hash;
55 typedef nspace::hash_map<std::string, chanrec*, nspace::hash<string>, irc::StrHashComp> chan_hash;
56
57 extern user_hash clientlist;
58 extern chan_hash chanlist;
59
60 class TreeServer;
61 class TreeSocket;
62
63 TreeServer *TreeRoot;
64
65 bool DoOneToOne(std::string prefix, std::string command, std::deque<std::string> params, std::string target);
66 bool DoOneToAllButSender(std::string prefix, std::string command, std::deque<std::string> params, std::string omit);
67 bool DoOneToMany(std::string prefix, std::string command, std::deque<std::string> params);
68 bool DoOneToAllButSenderRaw(std::string data,std::string omit, std::string prefix,std::string command,std::deque<std::string> params);
69 void ReadConfiguration(bool rebind);
70
71 class TreeServer
72 {
73         TreeServer* Parent;
74         TreeServer* Route;
75         std::vector<TreeServer*> Children;
76         std::string ServerName;
77         std::string ServerDesc;
78         std::string VersionString;
79         int UserCount;
80         int OperCount;
81         TreeSocket* Socket;     // for directly connected servers this points at the socket object
82         time_t NextPing;
83         bool LastPingWasGood;
84         
85  public:
86
87         TreeServer()
88         {
89                 Parent = NULL;
90                 ServerName = "";
91                 ServerDesc = "";
92                 VersionString = "";
93                 UserCount = OperCount = 0;
94                 VersionString = GetVersionString();
95         }
96
97         TreeServer(std::string Name, std::string Desc) : ServerName(Name), ServerDesc(Desc)
98         {
99                 Parent = NULL;
100                 VersionString = "";
101                 UserCount = OperCount = 0;
102                 VersionString = GetVersionString();
103                 Route = NULL;
104         }
105
106         TreeServer(std::string Name, std::string Desc, TreeServer* Above, TreeSocket* Sock) : Parent(Above), ServerName(Name), ServerDesc(Desc), Socket(Sock)
107         {
108                 VersionString = "";
109                 UserCount = OperCount = 0;
110                 this->SetNextPingTime(time(NULL) + 60);
111                 this->SetPingFlag();
112
113                 log(DEBUG,"*** CREATE NEW SERVER %s ABOVE IS %s",Name.c_str(),Above->GetName().c_str());
114
115                 // find the 'route' for this server (e.g. the one directly connected
116                 // to the local server, which we can use to reach it)
117                 Route = Above;
118                 if (Route == TreeRoot)
119                 {
120                         log(DEBUG,"(0) Route is %s",this->GetName().c_str());
121                         Route = this;
122                 }
123                 else
124                 {
125                         log(DEBUG,"(1) Route is %s",Route->GetName().c_str());
126                         while (Route->GetParent() != TreeRoot)
127                         {
128                                 Route = Route->GetParent();
129                                 log(DEBUG,"(2) Route is %s",Route->GetName().c_str());
130                         }
131                 }
132
133                 log(DEBUG,"    ROUTE FOR %s is %s",Name.c_str(),Route->GetName().c_str()); 
134         }
135
136         TreeServer* GetRoute()
137         {
138                 return Route;
139         }
140
141         std::string GetName()
142         {
143                 return this->ServerName;
144         }
145
146         std::string GetDesc()
147         {
148                 return this->ServerDesc;
149         }
150
151         std::string GetVersion()
152         {
153                 return this->VersionString;
154         }
155
156         void SetNextPingTime(time_t t)
157         {
158                 this->NextPing = t;
159                 LastPingWasGood = false;
160         }
161
162         time_t NextPingTime()
163         {
164                 return this->NextPing;
165         }
166
167         bool AnsweredLastPing()
168         {
169                 return LastPingWasGood;
170         }
171
172         void SetPingFlag()
173         {
174                 LastPingWasGood = true;
175         }
176
177         int GetUserCount()
178         {
179                 return this->UserCount;
180         }
181
182         int GetOperCount()
183         {
184                 return this->OperCount;
185         }
186
187         TreeSocket* GetSocket()
188         {
189                 return this->Socket;
190         }
191
192         TreeServer* GetParent()
193         {
194                 return this->Parent;
195         }
196
197         void SetVersion(std::string Version)
198         {
199                 VersionString = Version;
200         }
201
202         unsigned int ChildCount()
203         {
204                 return Children.size();
205         }
206
207         TreeServer* GetChild(unsigned int n)
208         {
209                 if (n < Children.size())
210                 {
211                         return Children[n];
212                 }
213                 else
214                 {
215                         return NULL;
216                 }
217         }
218
219         void AddChild(TreeServer* Child)
220         {
221                 Children.push_back(Child);
222         }
223
224         bool DelChild(TreeServer* Child)
225         {
226                 for (std::vector<TreeServer*>::iterator a = Children.begin(); a < Children.end(); a++)
227                 {
228                         if (*a == Child)
229                         {
230                                 Children.erase(a);
231                                 return true;
232                         }
233                 }
234                 return false;
235         }
236
237         // removes child nodes of this node, and of that node, etc etc
238         bool Tidy()
239         {
240                 bool stillchildren = true;
241                 while (stillchildren)
242                 {
243                         stillchildren = false;
244                         for (std::vector<TreeServer*>::iterator a = Children.begin(); a < Children.end(); a++)
245                         {
246                                 TreeServer* s = (TreeServer*)*a;
247                                 s->Tidy();
248                                 Children.erase(a);
249                                 delete s;
250                                 stillchildren = true;
251                                 break;
252                         }
253                 }
254                 return true;
255         }
256 };
257
258 class Link
259 {
260  public:
261          std::string Name;
262          std::string IPAddr;
263          int Port;
264          std::string SendPass;
265          std::string RecvPass;
266          unsigned long AutoConnect;
267          time_t NextConnectTime;
268 };
269
270 /* $ModDesc: Povides a spanning tree server link protocol */
271
272 Server *Srv;
273 ConfigReader *Conf;
274 std::vector<Link> LinkBlocks;
275
276 TreeServer* RouteEnumerate(TreeServer* Current, std::string ServerName)
277 {
278         if (Current->GetName() == ServerName)
279                 return Current->GetRoute();
280         for (unsigned int q = 0; q < Current->ChildCount(); q++)
281         {
282                 TreeServer* found = RouteEnumerate(Current->GetChild(q),ServerName);
283                 if (found)
284                         return found->GetRoute();
285         }
286         return NULL;
287 }
288
289 // Returns the locally connected server we must route a
290 // message through to reach server 'ServerName'. This
291 // only applies to one-to-one and not one-to-many routing.
292 TreeServer* BestRouteTo(std::string ServerName)
293 {
294         if (ServerName.c_str() == TreeRoot->GetName())
295         {
296                 return NULL;
297         }
298         // first, find the server by recursively walking the tree
299         TreeServer* Found = RouteEnumerate(TreeRoot,ServerName);
300         return Found;
301 }
302
303 bool LookForServer(TreeServer* Current, std::string ServerName)
304 {
305         if (ServerName == Current->GetName())
306                 return true;
307         for (unsigned int q = 0; q < Current->ChildCount(); q++)
308         {
309                 if (LookForServer(Current->GetChild(q),ServerName))
310                         return true;
311         }
312         return false;
313 }
314
315 TreeServer* Found;
316
317 void RFindServer(TreeServer* Current, std::string ServerName)
318 {
319         if ((ServerName == Current->GetName()) && (!Found))
320         {
321                 Found = Current;
322                 return;
323         }
324         if (!Found)
325         {
326                 for (unsigned int q = 0; q < Current->ChildCount(); q++)
327                 {
328                         if (!Found)
329                                 RFindServer(Current->GetChild(q),ServerName);
330                 }
331         }
332         return;
333 }
334
335 void RFindServerMask(TreeServer* Current, std::string ServerName)
336 {
337         if (Srv->MatchText(Current->GetName(),ServerName) && (!Found))
338         {
339                 Found = Current;
340                 return;
341         }
342         if (!Found)
343         {
344                 for (unsigned int q = 0; q < Current->ChildCount(); q++)
345                 {
346                         if (!Found)
347                                 RFindServerMask(Current->GetChild(q),ServerName);
348                 }
349         }
350 }
351
352 TreeServer* FindServer(std::string ServerName)
353 {
354         Found = NULL;
355         RFindServer(TreeRoot,ServerName);
356         return Found;
357 }
358
359 TreeServer* FindServerMask(std::string ServerName)
360 {
361         Found = NULL;
362         RFindServerMask(TreeRoot,ServerName);
363         return Found;
364 }
365
366 bool IsServer(std::string ServerName)
367 {
368         return LookForServer(TreeRoot,ServerName);
369 }
370
371 class TreeSocket : public InspSocket
372 {
373         std::string myhost;
374         std::string in_buffer;
375         ServerState LinkState;
376         std::string InboundServerName;
377         std::string InboundDescription;
378         int num_lost_users;
379         int num_lost_servers;
380         time_t NextPing;
381         bool LastPingWasGood;
382         
383  public:
384
385         TreeSocket(std::string host, int port, bool listening, unsigned long maxtime)
386                 : InspSocket(host, port, listening, maxtime)
387         {
388                 myhost = host;
389                 this->LinkState = LISTENER;
390         }
391
392         TreeSocket(std::string host, int port, bool listening, unsigned long maxtime, std::string ServerName)
393                 : InspSocket(host, port, listening, maxtime)
394         {
395                 myhost = ServerName;
396                 this->LinkState = CONNECTING;
397         }
398
399         TreeSocket(int newfd, char* ip)
400                 : InspSocket(newfd, ip)
401         {
402                 this->LinkState = WAIT_AUTH_1;
403         }
404         
405         virtual bool OnConnected()
406         {
407                 if (this->LinkState == CONNECTING)
408                 {
409                         Srv->SendOpers("*** Connection to "+myhost+"["+this->GetIP()+"] established.");
410                         // we should send our details here.
411                         // if the other side is satisfied, they send theirs.
412                         // we do not need to change state here.
413                         for (std::vector<Link>::iterator x = LinkBlocks.begin(); x < LinkBlocks.end(); x++)
414                         {
415                                 if (x->Name == this->myhost)
416                                 {
417                                         // found who we're supposed to be connecting to, send the neccessary gubbins.
418                                         this->WriteLine("SERVER "+Srv->GetServerName()+" "+x->SendPass+" 0 :"+Srv->GetServerDescription());
419                                         return true;
420                                 }
421                         }
422                 }
423                 return true;
424         }
425         
426         virtual void OnError(InspSocketError e)
427         {
428         }
429
430         virtual int OnDisconnect()
431         {
432                 return true;
433         }
434
435         // recursively send the server tree with distances as hops
436         void SendServers(TreeServer* Current, TreeServer* s, int hops)
437         {
438                 char command[1024];
439                 for (unsigned int q = 0; q < Current->ChildCount(); q++)
440                 {
441                         TreeServer* recursive_server = Current->GetChild(q);
442                         if (recursive_server != s)
443                         {
444                                 // :source.server SERVER server.name hops :Description
445                                 snprintf(command,1024,":%s SERVER %s * %d :%s",Current->GetName().c_str(),recursive_server->GetName().c_str(),hops,recursive_server->GetDesc().c_str());
446                                 this->WriteLine(command);
447                                 this->WriteLine(":"+recursive_server->GetName()+" VERSION :"+recursive_server->GetVersion());
448                                 // down to next level
449                                 this->SendServers(recursive_server, s, hops+1);
450                         }
451                 }
452         }
453
454         void SquitServer(TreeServer* Current)
455         {
456                 // recursively squit the servers attached to 'Current'
457                 for (unsigned int q = 0; q < Current->ChildCount(); q++)
458                 {
459                         TreeServer* recursive_server = Current->GetChild(q);
460                         this->SquitServer(recursive_server);
461                 }
462                 // Now we've whacked the kids, whack self
463                 num_lost_servers++;
464                 bool quittingpeople = true;
465                 while (quittingpeople)
466                 {
467                         quittingpeople = false;
468                         for (user_hash::iterator u = clientlist.begin(); u != clientlist.end(); u++)
469                         {
470                                 if (!strcasecmp(u->second->server,Current->GetName().c_str()))
471                                 {
472                                         Srv->QuitUser(u->second,Current->GetName()+" "+std::string(Srv->GetServerName()));
473                                         num_lost_users++;
474                                         quittingpeople = true;
475                                         break;
476                                 }
477                         }
478                 }
479         }
480
481         void Squit(TreeServer* Current,std::string reason)
482         {
483                 if (Current)
484                 {
485                         std::deque<std::string> params;
486                         params.push_back(Current->GetName());
487                         params.push_back(":"+reason);
488                         DoOneToAllButSender(Current->GetParent()->GetName(),"SQUIT",params,Current->GetName());
489                         if (Current->GetParent() == TreeRoot)
490                         {
491                                 Srv->SendOpers("Server \002"+Current->GetName()+"\002 split: "+reason);
492                         }
493                         else
494                         {
495                                 Srv->SendOpers("Server \002"+Current->GetName()+"\002 split from server \002"+Current->GetParent()->GetName()+"\002 with reason: "+reason);
496                         }
497                         num_lost_servers = 0;
498                         num_lost_users = 0;
499                         SquitServer(Current);
500                         Current->Tidy();
501                         Current->GetParent()->DelChild(Current);
502                         delete Current;
503                         WriteOpers("Netsplit complete, lost \002%d\002 users on \002%d\002 servers.", num_lost_users, num_lost_servers);
504                 }
505                 else
506                 {
507                         log(DEFAULT,"Squit from unknown server");
508                 }
509         }
510
511         bool ForceMode(std::string source, std::deque<std::string> params)
512         {
513                 userrec* who = new userrec;
514                 who->fd = FD_MAGIC_NUMBER;
515                 if (params.size() < 2)
516                         return true;
517                 char* modelist[255];
518                 for (unsigned int q = 0; q < params.size(); q++)
519                 {
520                         modelist[q] = (char*)params[q].c_str();
521                 }
522                 Srv->SendMode(modelist,params.size(),who);
523                 DoOneToAllButSender(source,"FMODE",params,source);
524                 delete who;
525                 return true;
526         }
527
528         bool ForceTopic(std::string source, std::deque<std::string> params)
529         {
530                 // FTOPIC %s %lu %s :%s
531                 if (params.size() != 4)
532                         return true;
533                 std::string channel = params[0];
534                 time_t ts = atoi(params[1].c_str());
535                 std::string setby = params[2];
536                 std::string topic = params[3];
537
538                 chanrec* c = Srv->FindChannel(channel);
539                 if (c)
540                 {
541                         if ((ts >= c->topicset) || (!*c->topic))
542                         {
543                                 std::string oldtopic = c->topic;
544                                 strlcpy(c->topic,topic.c_str(),MAXTOPIC);
545                                 strlcpy(c->setby,setby.c_str(),NICKMAX);
546                                 c->topicset = ts;
547                                 // if the topic text is the same as the current topic,
548                                 // dont bother to send the TOPIC command out, just silently
549                                 // update the set time and set nick.
550                                 if (oldtopic != topic)
551                                         WriteChannelWithServ((char*)source.c_str(), c, "TOPIC %s :%s", c->name, c->topic);
552                         }
553                         
554                 }
555                 
556                 // all done, send it on its way
557                 params[3] = ":" + params[3];
558                 DoOneToAllButSender(source,"FTOPIC",params,source);
559
560                 return true;
561         }
562
563         bool ForceJoin(std::string source, std::deque<std::string> params)
564         {
565                 if (params.size() < 3)
566                         return true;
567
568                 char first[MAXBUF];
569                 char modestring[MAXBUF];
570                 char* mode_users[127];
571                 mode_users[0] = first;
572                 mode_users[1] = modestring;
573                 strcpy(mode_users[1],"+");
574                 unsigned int modectr = 2;
575                 
576                 userrec* who = NULL;
577                 std::string channel = params[0];
578                 time_t TS = atoi(params[1].c_str());
579                 char* key = "";
580                 
581                 chanrec* chan = Srv->FindChannel(channel);
582                 if (chan)
583                 {
584                         key = chan->key;
585                 }
586                 strlcpy(mode_users[0],channel.c_str(),MAXBUF);
587
588                 // default is a high value, which if we dont have this
589                 // channel will let the other side apply their modes.
590                 time_t ourTS = time(NULL)+600;
591                 chanrec* us = Srv->FindChannel(channel);
592                 if (us)
593                 {
594                         ourTS = us->age;
595                 }
596
597                 log(DEBUG,"FJOIN detected, our TS=%lu, their TS=%lu",ourTS,TS);
598
599                 // do this first, so our mode reversals are correctly received by other servers
600                 // if there is a TS collision.
601                 DoOneToAllButSender(source,"FJOIN",params,source);
602                 
603                 for (unsigned int usernum = 2; usernum < params.size(); usernum++)
604                 {
605                         // process one channel at a time, applying modes.
606                         char* usr = (char*)params[usernum].c_str();
607                         char permissions = *usr;
608                         switch (permissions)
609                         {
610                                 case '@':
611                                         usr++;
612                                         mode_users[modectr++] = usr;
613                                         strlcat(modestring,"o",MAXBUF);
614                                 break;
615                                 case '%':
616                                         usr++;
617                                         mode_users[modectr++] = usr;
618                                         strlcat(modestring,"h",MAXBUF);
619                                 break;
620                                 case '+':
621                                         usr++;
622                                         mode_users[modectr++] = usr;
623                                         strlcat(modestring,"v",MAXBUF);
624                                 break;
625                         }
626                         who = Srv->FindNick(usr);
627                         if (who)
628                         {
629                                 Srv->JoinUserToChannel(who,channel,key);
630                                 if (modectr >= (MAXMODES-1))
631                                 {
632                                         // theres a mode for this user. push them onto the mode queue, and flush it
633                                         // if there are more than MAXMODES to go.
634                                         if (ourTS >= TS)
635                                         {
636                                                 log(DEBUG,"Our our channel newer than theirs, accepting their modes");
637                                                 Srv->SendMode(mode_users,modectr,who);
638                                         }
639                                         else
640                                         {
641                                                 log(DEBUG,"Their channel newer than ours, bouncing their modes");
642                                                 // bouncy bouncy!
643                                                 std::deque<std::string> params;
644                                                 // modes are now being UNSET...
645                                                 *mode_users[1] = '-';
646                                                 for (unsigned int x = 0; x < modectr; x++)
647                                                 {
648                                                         params.push_back(mode_users[x]);
649                                                 }
650                                                 // tell everyone to bounce the modes. bad modes, bad!
651                                                 DoOneToMany(Srv->GetServerName(),"FMODE",params);
652                                         }
653                                         strcpy(mode_users[1],"+");
654                                         modectr = 2;
655                                 }
656                         }
657                 }
658                 // there werent enough modes built up to flush it during FJOIN,
659                 // or, there are a number left over. flush them out.
660                 if ((modectr > 2) && (who))
661                 {
662                         if (ourTS >= TS)
663                         {
664                                 log(DEBUG,"Our our channel newer than theirs, accepting their modes");
665                                 Srv->SendMode(mode_users,modectr,who);
666                         }
667                         else
668                         {
669                                 log(DEBUG,"Their channel newer than ours, bouncing their modes");
670                                 std::deque<std::string> params;
671                                 *mode_users[1] = '-';
672                                 for (unsigned int x = 0; x < modectr; x++)
673                                 {
674                                         params.push_back(mode_users[x]);
675                                 }
676                                 DoOneToMany(Srv->GetServerName(),"FMODE",params);
677                         }
678                 }
679                 return true;
680         }
681
682         bool IntroduceClient(std::string source, std::deque<std::string> params)
683         {
684                 if (params.size() < 8)
685                         return true;
686                 // NICK age nick host dhost ident +modes ip :gecos
687                 //       0   1    2    3      4     5    6   7
688                 std::string nick = params[1];
689                 std::string host = params[2];
690                 std::string dhost = params[3];
691                 std::string ident = params[4];
692                 time_t age = atoi(params[0].c_str());
693                 std::string modes = params[5];
694                 while (*(modes.c_str()) == '+')
695                 {
696                         char* m = (char*)modes.c_str();
697                         m++;
698                         modes = m;
699                 }
700                 std::string ip = params[6];
701                 std::string gecos = params[7];
702                 char* tempnick = (char*)nick.c_str();
703                 log(DEBUG,"Introduce client %s!%s@%s",tempnick,ident.c_str(),host.c_str());
704                 
705                 user_hash::iterator iter;
706                 iter = clientlist.find(tempnick);
707                 if (iter != clientlist.end())
708                 {
709                         // nick collision
710                         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);
711                         this->WriteLine(":"+Srv->GetServerName()+" KILL "+tempnick+" :Nickname collision");
712                         return true;
713                 }
714
715                 clientlist[tempnick] = new userrec();
716                 clientlist[tempnick]->fd = FD_MAGIC_NUMBER;
717                 strlcpy(clientlist[tempnick]->nick, tempnick,NICKMAX);
718                 strlcpy(clientlist[tempnick]->host, host.c_str(),160);
719                 strlcpy(clientlist[tempnick]->dhost, dhost.c_str(),160);
720                 clientlist[tempnick]->server = (char*)FindServerNamePtr(source.c_str());
721                 strlcpy(clientlist[tempnick]->ident, ident.c_str(),IDENTMAX);
722                 strlcpy(clientlist[tempnick]->fullname, gecos.c_str(),MAXGECOS);
723                 clientlist[tempnick]->registered = 7;
724                 clientlist[tempnick]->signon = age;
725                 strlcpy(clientlist[tempnick]->modes, modes.c_str(),53);
726                 strlcpy(clientlist[tempnick]->ip,ip.c_str(),16);
727                 for (int i = 0; i < MAXCHANS; i++)
728                 {
729                         clientlist[tempnick]->chans[i].channel = NULL;
730                         clientlist[tempnick]->chans[i].uc_modes = 0;
731                 }
732                 params[7] = ":" + params[7];
733                 DoOneToAllButSender(source,"NICK",params,source);
734                 return true;
735         }
736
737         void SendFJoins(TreeServer* Current, chanrec* c)
738         {
739                 char list[MAXBUF];
740                 snprintf(list,MAXBUF,":%s FJOIN %s %lu",Srv->GetServerName().c_str(),c->name,(unsigned long)c->age);
741                 std::vector<char*> *ulist = c->GetUsers();
742                 for (unsigned int i = 0; i < ulist->size(); i++)
743                 {
744                         char* o = (*ulist)[i];
745                         userrec* otheruser = (userrec*)o;
746                         strlcat(list," ",MAXBUF);
747                         strlcat(list,cmode(otheruser,c),MAXBUF);
748                         strlcat(list,otheruser->nick,MAXBUF);
749                         if (strlen(list)>(480-NICKMAX))
750                         {
751                                 this->WriteLine(list);
752                                 snprintf(list,MAXBUF,":%s FJOIN %s %lu",Srv->GetServerName().c_str(),c->name,(unsigned long)c->age);
753                         }
754                 }
755                 if (list[strlen(list)-1] != ':')
756                 {
757                         this->WriteLine(list);
758                 }
759         }
760
761         void SendChannelModes(TreeServer* Current)
762         {
763                 char data[MAXBUF];
764                 for (chan_hash::iterator c = chanlist.begin(); c != chanlist.end(); c++)
765                 {
766                         SendFJoins(Current, c->second);
767                         snprintf(data,MAXBUF,":%s FMODE %s +%s",Srv->GetServerName().c_str(),c->second->name,chanmodes(c->second));
768                         this->WriteLine(data);
769                         if (*c->second->topic)
770                         {
771                                 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);
772                                 this->WriteLine(data);
773                         }
774                         for (BanList::iterator b = c->second->bans.begin(); b != c->second->bans.end(); b++)
775                         {
776                                 snprintf(data,MAXBUF,":%s FMODE %s +b %s",Srv->GetServerName().c_str(),c->second->name,b->data);
777                                 this->WriteLine(data);
778                         }
779                         FOREACH_MOD OnSyncChannel(c->second,(Module*)TreeProtocolModule,(void*)this);
780                 }
781         }
782
783         // send all users and their channels
784         void SendUsers(TreeServer* Current)
785         {
786                 char data[MAXBUF];
787                 for (user_hash::iterator u = clientlist.begin(); u != clientlist.end(); u++)
788                 {
789                         if (u->second->registered == 7)
790                         {
791                                 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);
792                                 this->WriteLine(data);
793                                 if (strchr(u->second->modes,'o'))
794                                 {
795                                         this->WriteLine(":"+std::string(u->second->nick)+" OPERTYPE "+std::string(u->second->oper));
796                                 }
797                                 //char* chl = chlist(u->second,u->second);
798                                 //if (*chl)
799                                 //{
800                                 //      this->WriteLine(":"+std::string(u->second->nick)+" FJOIN "+std::string(chl));
801                                 //}
802                                 FOREACH_MOD OnSyncUser(u->second,(Module*)TreeProtocolModule,(void*)this);
803                         }
804                 }
805         }
806
807         void DoBurst(TreeServer* s)
808         {
809                 Srv->SendOpers("*** Bursting to "+s->GetName()+".");
810                 this->WriteLine("BURST");
811                 // send our version string
812                 this->WriteLine(":"+Srv->GetServerName()+" VERSION :"+GetVersionString());
813                 // Send server tree
814                 this->SendServers(TreeRoot,s,1);
815                 // Send users and their channels
816                 this->SendUsers(s);
817                 // Send everything else (channel modes etc)
818                 this->SendChannelModes(s);
819                 this->WriteLine("ENDBURST");
820         }
821
822         virtual bool OnDataReady()
823         {
824                 char* data = this->Read();
825                 if (data)
826                 {
827                         this->in_buffer += data;
828                         while (in_buffer.find("\n") != std::string::npos)
829                         {
830                                 char* line = (char*)in_buffer.c_str();
831                                 std::string ret = "";
832                                 while ((*line != '\n') && (strlen(line)))
833                                 {
834                                         ret = ret + *line;
835                                         line++;
836                                 }
837                                 if ((*line == '\n') || (*line == '\r'))
838                                         line++;
839                                 in_buffer = line;
840                                 if (!this->ProcessLine(ret))
841                                 {
842                                         return false;
843                                 }
844                         }
845                 }
846                 return (data != NULL);
847         }
848
849         int WriteLine(std::string line)
850         {
851                 return this->Write(line + "\r\n");
852         }
853
854         bool Error(std::deque<std::string> params)
855         {
856                 if (params.size() < 1)
857                         return false;
858                 std::string Errmsg = params[0];
859                 std::string SName = myhost;
860                 if (InboundServerName != "")
861                 {
862                         SName = InboundServerName;
863                 }
864                 Srv->SendOpers("*** ERROR from "+SName+": "+Errmsg);
865                 // we will return false to cause the socket to close.
866                 return false;
867         }
868
869         bool OperType(std::string prefix, std::deque<std::string> params)
870         {
871                 if (params.size() != 1)
872                         return true;
873                 std::string opertype = params[0];
874                 userrec* u = Srv->FindNick(prefix);
875                 if (u)
876                 {
877                         strlcpy(u->oper,opertype.c_str(),NICKMAX);
878                         if (!strchr(u->modes,'o'))
879                         {
880                                 strcat(u->modes,"o");
881                         }
882                         DoOneToAllButSender(u->nick,"OPERTYPE",params,u->server);
883                 }
884                 return true;
885         }
886
887         bool RemoteRehash(std::string prefix, std::deque<std::string> params)
888         {
889                 if (params.size() < 1)
890                         return true;
891                 std::string servermask = params[0];
892                 if (Srv->MatchText(Srv->GetServerName(),servermask))
893                 {
894                         Srv->SendOpers("*** Remote rehash initiated from server \002"+prefix+"\002.");
895                         Srv->RehashServer();
896                         ReadConfiguration(false);
897                 }
898                 DoOneToAllButSender(prefix,"REHASH",params,prefix);
899                 return true;
900         }
901
902         bool RemoteKill(std::string prefix, std::deque<std::string> params)
903         {
904                 if (params.size() != 2)
905                         return true;
906                 std::string nick = params[0];
907                 std::string reason = params[1];
908                 userrec* u = Srv->FindNick(prefix);
909                 userrec* who = Srv->FindNick(nick);
910                 if (who)
911                 {
912                         std::string sourceserv = prefix;
913                         if (u)
914                         {
915                                 sourceserv = u->server;
916                         }
917                         params[1] = ":" + params[1];
918                         DoOneToAllButSender(prefix,"KILL",params,sourceserv);
919                         Srv->QuitUser(who,reason);
920                 }
921                 return true;
922         }
923
924         bool LocalPong(std::string prefix, std::deque<std::string> params)
925         {
926                 if (params.size() < 1)
927                         return true;
928                 TreeServer* ServerSource = FindServer(prefix);
929                 if (ServerSource)
930                 {
931                         ServerSource->SetPingFlag();
932                 }
933                 return true;
934         }
935
936         bool ServerVersion(std::string prefix, std::deque<std::string> params)
937         {
938                 if (params.size() < 1)
939                         return true;
940                 TreeServer* ServerSource = FindServer(prefix);
941                 if (ServerSource)
942                 {
943                         ServerSource->SetVersion(params[0]);
944                 }
945                 params[0] = ":" + params[0];
946                 DoOneToAllButSender(prefix,"VERSION",params,prefix);
947                 return true;
948         }
949
950         bool ChangeHost(std::string prefix, std::deque<std::string> params)
951         {
952                 if (params.size() < 1)
953                         return true;
954                 userrec* u = Srv->FindNick(prefix);
955                 if (u)
956                 {
957                         Srv->ChangeHost(u,params[0]);
958                         DoOneToAllButSender(prefix,"FHOST",params,u->server);
959                 }
960                 return true;
961         }
962
963         bool ChangeName(std::string prefix, std::deque<std::string> params)
964         {
965                 if (params.size() < 1)
966                         return true;
967                 userrec* u = Srv->FindNick(prefix);
968                 if (u)
969                 {
970                         Srv->ChangeGECOS(u,params[0]);
971                         params[0] = ":" + params[0];
972                         DoOneToAllButSender(prefix,"FNAME",params,u->server);
973                 }
974                 return true;
975         }
976         
977         bool LocalPing(std::string prefix, std::deque<std::string> params)
978         {
979                 if (params.size() < 1)
980                         return true;
981                 std::string stufftobounce = params[0];
982                 this->WriteLine(":"+Srv->GetServerName()+" PONG "+stufftobounce);
983                 return true;
984         }
985
986         bool RemoteServer(std::string prefix, std::deque<std::string> params)
987         {
988                 if (params.size() < 4)
989                         return false;
990                 std::string servername = params[0];
991                 std::string password = params[1];
992                 // hopcount is not used for a remote server, we calculate this ourselves
993                 std::string description = params[3];
994                 TreeServer* ParentOfThis = FindServer(prefix);
995                 if (!ParentOfThis)
996                 {
997                         this->WriteLine("ERROR :Protocol error - Introduced remote server from unknown server "+prefix);
998                         return false;
999                 }
1000                 TreeServer* CheckDupe = FindServer(servername);
1001                 if (CheckDupe)
1002                 {
1003                         this->WriteLine("ERROR :Server "+servername+" already exists on server "+CheckDupe->GetParent()->GetName()+"!");
1004                         return false;
1005                 }
1006                 TreeServer* Node = new TreeServer(servername,description,ParentOfThis,NULL);
1007                 ParentOfThis->AddChild(Node);
1008                 params[3] = ":" + params[3];
1009                 DoOneToAllButSender(prefix,"SERVER",params,prefix);
1010                 Srv->SendOpers("*** Server \002"+prefix+"\002 introduced server \002"+servername+"\002 ("+description+")");
1011                 return true;
1012         }
1013
1014         bool Outbound_Reply_Server(std::deque<std::string> params)
1015         {
1016                 if (params.size() < 4)
1017                         return false;
1018                 std::string servername = params[0];
1019                 std::string password = params[1];
1020                 int hops = atoi(params[2].c_str());
1021                 if (hops)
1022                 {
1023                         this->WriteLine("ERROR :Server too far away for authentication");
1024                         return false;
1025                 }
1026                 std::string description = params[3];
1027                 for (std::vector<Link>::iterator x = LinkBlocks.begin(); x < LinkBlocks.end(); x++)
1028                 {
1029                         if ((x->Name == servername) && (x->RecvPass == password))
1030                         {
1031                                 TreeServer* CheckDupe = FindServer(servername);
1032                                 if (CheckDupe)
1033                                 {
1034                                         this->WriteLine("ERROR :Server "+servername+" already exists on server "+CheckDupe->GetParent()->GetName()+"!");
1035                                         return false;
1036                                 }
1037                                 // Begin the sync here. this kickstarts the
1038                                 // other side, waiting in WAIT_AUTH_2 state,
1039                                 // into starting their burst, as it shows
1040                                 // that we're happy.
1041                                 this->LinkState = CONNECTED;
1042                                 // we should add the details of this server now
1043                                 // to the servers tree, as a child of the root
1044                                 // node.
1045                                 TreeServer* Node = new TreeServer(servername,description,TreeRoot,this);
1046                                 TreeRoot->AddChild(Node);
1047                                 params[3] = ":" + params[3];
1048                                 DoOneToAllButSender(TreeRoot->GetName(),"SERVER",params,servername);
1049                                 this->DoBurst(Node);
1050                                 return true;
1051                         }
1052                 }
1053                 this->WriteLine("ERROR :Invalid credentials");
1054                 return false;
1055         }
1056
1057         bool Inbound_Server(std::deque<std::string> params)
1058         {
1059                 if (params.size() < 4)
1060                         return false;
1061                 std::string servername = params[0];
1062                 std::string password = params[1];
1063                 int hops = atoi(params[2].c_str());
1064                 if (hops)
1065                 {
1066                         this->WriteLine("ERROR :Server too far away for authentication");
1067                         return false;
1068                 }
1069                 std::string description = params[3];
1070                 for (std::vector<Link>::iterator x = LinkBlocks.begin(); x < LinkBlocks.end(); x++)
1071                 {
1072                         if ((x->Name == servername) && (x->RecvPass == password))
1073                         {
1074                                 TreeServer* CheckDupe = FindServer(servername);
1075                                 if (CheckDupe)
1076                                 {
1077                                         this->WriteLine("ERROR :Server "+servername+" already exists on server "+CheckDupe->GetParent()->GetName()+"!");
1078                                         return false;
1079                                 }
1080                                 Srv->SendOpers("*** Verified incoming server connection from \002"+servername+"\002["+this->GetIP()+"] ("+description+")");
1081                                 this->InboundServerName = servername;
1082                                 this->InboundDescription = description;
1083                                 // this is good. Send our details: Our server name and description and hopcount of 0,
1084                                 // along with the sendpass from this block.
1085                                 this->WriteLine("SERVER "+Srv->GetServerName()+" "+x->SendPass+" 0 :"+Srv->GetServerDescription());
1086                                 // move to the next state, we are now waiting for THEM.
1087                                 this->LinkState = WAIT_AUTH_2;
1088                                 return true;
1089                         }
1090                 }
1091                 this->WriteLine("ERROR :Invalid credentials");
1092                 return false;
1093         }
1094
1095         std::deque<std::string> Split(std::string line, bool stripcolon)
1096         {
1097                 std::deque<std::string> n;
1098                 if (!strchr(line.c_str(),' '))
1099                 {
1100                         n.push_back(line);
1101                         return n;
1102                 }
1103                 std::stringstream s(line);
1104                 std::string param = "";
1105                 n.clear();
1106                 int item = 0;
1107                 while (!s.eof())
1108                 {
1109                         char c;
1110                         s.get(c);
1111                         if (c == ' ')
1112                         {
1113                                 n.push_back(param);
1114                                 param = "";
1115                                 item++;
1116                         }
1117                         else
1118                         {
1119                                 if (!s.eof())
1120                                 {
1121                                         param = param + c;
1122                                 }
1123                                 if ((param == ":") && (item > 0))
1124                                 {
1125                                         param = "";
1126                                         while (!s.eof())
1127                                         {
1128                                                 s.get(c);
1129                                                 if (!s.eof())
1130                                                 {
1131                                                         param = param + c;
1132                                                 }
1133                                         }
1134                                         n.push_back(param);
1135                                         param = "";
1136                                 }
1137                         }
1138                 }
1139                 if (param != "")
1140                 {
1141                         n.push_back(param);
1142                 }
1143                 return n;
1144         }
1145
1146         bool ProcessLine(std::string line)
1147         {
1148                 char* l = (char*)line.c_str();
1149                 while ((strlen(l)) && (l[strlen(l)-1] == '\r') || (l[strlen(l)-1] == '\n'))
1150                         l[strlen(l)-1] = '\0';
1151                 line = l;
1152                 if (line == "")
1153                         return true;
1154                 Srv->Log(DEBUG,"IN: '"+line+"'");
1155                 std::deque<std::string> params = this->Split(line,true);
1156                 std::string command = "";
1157                 std::string prefix = "";
1158                 if (((params[0].c_str())[0] == ':') && (params.size() > 1))
1159                 {
1160                         prefix = params[0];
1161                         command = params[1];
1162                         char* pref = (char*)prefix.c_str();
1163                         prefix = ++pref;
1164                         params.pop_front();
1165                         params.pop_front();
1166                 }
1167                 else
1168                 {
1169                         prefix = "";
1170                         command = params[0];
1171                         params.pop_front();
1172                 }
1173                 
1174                 switch (this->LinkState)
1175                 {
1176                         TreeServer* Node;
1177                         
1178                         case WAIT_AUTH_1:
1179                                 // Waiting for SERVER command from remote server. Server initiating
1180                                 // the connection sends the first SERVER command, listening server
1181                                 // replies with theirs if its happy, then if the initiator is happy,
1182                                 // it starts to send its net sync, which starts the merge, otherwise
1183                                 // it sends an ERROR.
1184                                 if (command == "SERVER")
1185                                 {
1186                                         return this->Inbound_Server(params);
1187                                 }
1188                                 else if (command == "ERROR")
1189                                 {
1190                                         return this->Error(params);
1191                                 }
1192                         break;
1193                         case WAIT_AUTH_2:
1194                                 // Waiting for start of other side's netmerge to say they liked our
1195                                 // password.
1196                                 if (command == "SERVER")
1197                                 {
1198                                         // cant do this, they sent it to us in the WAIT_AUTH_1 state!
1199                                         // silently ignore.
1200                                         return true;
1201                                 }
1202                                 else if (command == "BURST")
1203                                 {
1204                                         this->LinkState = CONNECTED;
1205                                         Node = new TreeServer(InboundServerName,InboundDescription,TreeRoot,this);
1206                                         TreeRoot->AddChild(Node);
1207                                         params.clear();
1208                                         params.push_back(InboundServerName);
1209                                         params.push_back("*");
1210                                         params.push_back("1");
1211                                         params.push_back(":"+InboundDescription);
1212                                         DoOneToAllButSender(TreeRoot->GetName(),"SERVER",params,InboundServerName);
1213                                         this->DoBurst(Node);
1214                                 }
1215                                 else if (command == "ERROR")
1216                                 {
1217                                         return this->Error(params);
1218                                 }
1219                                 
1220                         break;
1221                         case LISTENER:
1222                                 this->WriteLine("ERROR :Internal error -- listening socket accepted its own descriptor!!!");
1223                                 return false;
1224                         break;
1225                         case CONNECTING:
1226                                 if (command == "SERVER")
1227                                 {
1228                                         // another server we connected to, which was in WAIT_AUTH_1 state,
1229                                         // has just sent us their credentials. If we get this far, theyre
1230                                         // happy with OUR credentials, and they are now in WAIT_AUTH_2 state.
1231                                         // if we're happy with this, we should send our netburst which
1232                                         // kickstarts the merge.
1233                                         return this->Outbound_Reply_Server(params);
1234                                 }
1235                                 else if (command == "ERROR")
1236                                 {
1237                                         return this->Error(params);
1238                                 }
1239                         break;
1240                         case CONNECTED:
1241                                 // This is the 'authenticated' state, when all passwords
1242                                 // have been exchanged and anything past this point is taken
1243                                 // as gospel.
1244                                 std::string target = "";
1245                                 if ((command == "NICK") && (params.size() > 1))
1246                                 {
1247                                         return this->IntroduceClient(prefix,params);
1248                                 }
1249                                 else if (command == "FJOIN")
1250                                 {
1251                                         return this->ForceJoin(prefix,params);
1252                                 }
1253                                 else if (command == "SERVER")
1254                                 {
1255                                         return this->RemoteServer(prefix,params);
1256                                 }
1257                                 else if (command == "ERROR")
1258                                 {
1259                                         return this->Error(params);
1260                                 }
1261                                 else if (command == "OPERTYPE")
1262                                 {
1263                                         return this->OperType(prefix,params);
1264                                 }
1265                                 else if (command == "FMODE")
1266                                 {
1267                                         return this->ForceMode(prefix,params);
1268                                 }
1269                                 else if (command == "KILL")
1270                                 {
1271                                         return this->RemoteKill(prefix,params);
1272                                 }
1273                                 else if (command == "FTOPIC")
1274                                 {
1275                                         return this->ForceTopic(prefix,params);
1276                                 }
1277                                 else if (command == "REHASH")
1278                                 {
1279                                         return this->RemoteRehash(prefix,params);
1280                                 }
1281                                 else if (command == "PING")
1282                                 {
1283                                         return this->LocalPing(prefix,params);
1284                                 }
1285                                 else if (command == "PONG")
1286                                 {
1287                                         return this->LocalPong(prefix,params);
1288                                 }
1289                                 else if (command == "VERSION")
1290                                 {
1291                                         return this->ServerVersion(prefix,params);
1292                                 }
1293                                 else if (command == "FHOST")
1294                                 {
1295                                         return this->ChangeHost(prefix,params);
1296                                 }
1297                                 else if (command == "FNAME")
1298                                 {
1299                                         return this->ChangeName(prefix,params);
1300                                 }
1301                                 else if (command == "SQUIT")
1302                                 {
1303                                         if (params.size() == 2)
1304                                         {
1305                                                 this->Squit(FindServer(params[0]),params[1]);
1306                                         }
1307                                         return true;
1308                                 }
1309                                 else
1310                                 {
1311                                         // not a special inter-server command.
1312                                         // Emulate the actual user doing the command,
1313                                         // this saves us having a huge ugly parser.
1314                                         userrec* who = Srv->FindNick(prefix);
1315                                         std::string sourceserv = this->myhost;
1316                                         if (this->InboundServerName != "")
1317                                         {
1318                                                 sourceserv = this->InboundServerName;
1319                                         }
1320                                         if (who)
1321                                         {
1322                                                 // its a user
1323                                                 target = who->server;
1324                                                 char* strparams[127];
1325                                                 for (unsigned int q = 0; q < params.size(); q++)
1326                                                 {
1327                                                         strparams[q] = (char*)params[q].c_str();
1328                                                 }
1329                                                 Srv->CallCommandHandler(command, strparams, params.size(), who);
1330                                         }
1331                                         else
1332                                         {
1333                                                 // its not a user. Its either a server, or somethings screwed up.
1334                                                 if (IsServer(prefix))
1335                                                 {
1336                                                         target = Srv->GetServerName();
1337                                                 }
1338                                                 else
1339                                                 {
1340                                                         log(DEBUG,"Command with unknown origin '%s'",prefix.c_str());
1341                                                         return true;
1342                                                 }
1343                                         }
1344                                         return DoOneToAllButSenderRaw(line,sourceserv,prefix,command,params);
1345
1346                                 }
1347                                 return true;
1348                         break;
1349                 }
1350                 return true;
1351         }
1352
1353         virtual std::string GetName()
1354         {
1355                 std::string sourceserv = this->myhost;
1356                 if (this->InboundServerName != "")
1357                 {
1358                         sourceserv = this->InboundServerName;
1359                 }
1360                 return sourceserv;
1361         }
1362
1363         virtual void OnTimeout()
1364         {
1365                 if (this->LinkState == CONNECTING)
1366                 {
1367                         Srv->SendOpers("*** CONNECT: Connection to \002"+myhost+"\002 timed out.");
1368                 }
1369         }
1370
1371         virtual void OnClose()
1372         {
1373                 // Connection closed.
1374                 // If the connection is fully up (state CONNECTED)
1375                 // then propogate a netsplit to all peers.
1376                 std::string quitserver = this->myhost;
1377                 if (this->InboundServerName != "")
1378                 {
1379                         quitserver = this->InboundServerName;
1380                 }
1381                 TreeServer* s = FindServer(quitserver);
1382                 if (s)
1383                 {
1384                         Squit(s,"Remote host closed the connection");
1385                 }
1386         }
1387
1388         virtual int OnIncomingConnection(int newsock, char* ip)
1389         {
1390                 TreeSocket* s = new TreeSocket(newsock, ip);
1391                 Srv->AddSocket(s);
1392                 return true;
1393         }
1394 };
1395
1396 void AddThisServer(TreeServer* server, std::deque<TreeServer*> &list)
1397 {
1398         for (unsigned int c = 0; c < list.size(); c++)
1399         {
1400                 if (list[c] == server)
1401                 {
1402                         return;
1403                 }
1404         }
1405         list.push_back(server);
1406 }
1407
1408 // returns a list of DIRECT servernames for a specific channel
1409 std::deque<TreeServer*> GetListOfServersForChannel(chanrec* c)
1410 {
1411         std::deque<TreeServer*> list;
1412         std::vector<char*> *ulist = c->GetUsers();
1413         for (unsigned int i = 0; i < ulist->size(); i++)
1414         {
1415                 char* o = (*ulist)[i];
1416                 userrec* otheruser = (userrec*)o;
1417                 if (std::string(otheruser->server) != Srv->GetServerName())
1418                 {
1419                         TreeServer* best = BestRouteTo(otheruser->server);
1420                         if (best)
1421                                 AddThisServer(best,list);
1422                 }
1423         }
1424         return list;
1425 }
1426
1427 bool DoOneToAllButSenderRaw(std::string data,std::string omit,std::string prefix,std::string command,std::deque<std::string> params)
1428 {
1429         TreeServer* omitroute = BestRouteTo(omit);
1430         if ((command == "NOTICE") || (command == "PRIVMSG"))
1431         {
1432                 if ((params.size() >= 2) && (*(params[0].c_str()) != '$'))
1433                 {
1434                         if (*(params[0].c_str()) != '#')
1435                         {
1436                                 // special routing for private messages/notices
1437                                 userrec* d = Srv->FindNick(params[0]);
1438                                 if (d)
1439                                 {
1440                                         std::deque<std::string> par;
1441                                         par.clear();
1442                                         par.push_back(params[0]);
1443                                         par.push_back(":"+params[1]);
1444                                         DoOneToOne(prefix,command,par,d->server);
1445                                         return true;
1446                                 }
1447                         }
1448                         else
1449                         {
1450                                 log(DEBUG,"Channel privmsg going to chan %s",params[0].c_str());
1451                                 chanrec* c = Srv->FindChannel(params[0]);
1452                                 if (c)
1453                                 {
1454                                         std::deque<TreeServer*> list = GetListOfServersForChannel(c);
1455                                         log(DEBUG,"Got a list of %d servers",list.size());
1456                                         for (unsigned int i = 0; i < list.size(); i++)
1457                                         {
1458                                                 TreeSocket* Sock = list[i]->GetSocket();
1459                                                 if ((Sock) && (list[i]->GetName() != omit) && (omitroute != list[i]))
1460                                                 {
1461                                                         log(DEBUG,"Writing privmsg to server %s",list[i]->GetName().c_str());
1462                                                         Sock->WriteLine(data);
1463                                                 }
1464                                         }
1465                                         return true;
1466                                 }
1467                         }
1468                 }
1469         }
1470         for (unsigned int x = 0; x < TreeRoot->ChildCount(); x++)
1471         {
1472                 TreeServer* Route = TreeRoot->GetChild(x);
1473                 if ((Route->GetSocket()) && (Route->GetName() != omit) && (omitroute != Route))
1474                 {
1475                         TreeSocket* Sock = Route->GetSocket();
1476                         Sock->WriteLine(data);
1477                 }
1478         }
1479         return true;
1480 }
1481
1482 bool DoOneToAllButSender(std::string prefix, std::string command, std::deque<std::string> params, std::string omit)
1483 {
1484         TreeServer* omitroute = BestRouteTo(omit);
1485         std::string FullLine = ":" + prefix + " " + command;
1486         for (unsigned int x = 0; x < params.size(); x++)
1487         {
1488                 FullLine = FullLine + " " + params[x];
1489         }
1490         for (unsigned int x = 0; x < TreeRoot->ChildCount(); x++)
1491         {
1492                 TreeServer* Route = TreeRoot->GetChild(x);
1493                 // Send the line IF:
1494                 // The route has a socket (its a direct connection)
1495                 // The route isnt the one to be omitted
1496                 // The route isnt the path to the one to be omitted
1497                 if ((Route->GetSocket()) && (Route->GetName() != omit) && (omitroute != Route))
1498                 {
1499                         TreeSocket* Sock = Route->GetSocket();
1500                         Sock->WriteLine(FullLine);
1501                 }
1502         }
1503         return true;
1504 }
1505
1506 bool DoOneToMany(std::string prefix, std::string command, std::deque<std::string> params)
1507 {
1508         std::string FullLine = ":" + prefix + " " + command;
1509         for (unsigned int x = 0; x < params.size(); x++)
1510         {
1511                 FullLine = FullLine + " " + params[x];
1512         }
1513         for (unsigned int x = 0; x < TreeRoot->ChildCount(); x++)
1514         {
1515                 TreeServer* Route = TreeRoot->GetChild(x);
1516                 if (Route->GetSocket())
1517                 {
1518                         TreeSocket* Sock = Route->GetSocket();
1519                         Sock->WriteLine(FullLine);
1520                 }
1521         }
1522         return true;
1523 }
1524
1525 bool DoOneToOne(std::string prefix, std::string command, std::deque<std::string> params, std::string target)
1526 {
1527         TreeServer* Route = BestRouteTo(target);
1528         if (Route)
1529         {
1530                 std::string FullLine = ":" + prefix + " " + command;
1531                 for (unsigned int x = 0; x < params.size(); x++)
1532                 {
1533                         FullLine = FullLine + " " + params[x];
1534                 }
1535                 if (Route->GetSocket())
1536                 {
1537                         TreeSocket* Sock = Route->GetSocket();
1538                         Sock->WriteLine(FullLine);
1539                 }
1540                 return true;
1541         }
1542         else
1543         {
1544                 return true;
1545         }
1546 }
1547
1548 std::vector<TreeSocket*> Bindings;
1549
1550 void ReadConfiguration(bool rebind)
1551 {
1552         if (rebind)
1553         {
1554                 for (int j =0; j < Conf->Enumerate("bind"); j++)
1555                 {
1556                         std::string Type = Conf->ReadValue("bind","type",j);
1557                         std::string IP = Conf->ReadValue("bind","address",j);
1558                         long Port = Conf->ReadInteger("bind","port",j,true);
1559                         if (Type == "servers")
1560                         {
1561                                 if (IP == "*")
1562                                 {
1563                                         IP = "";
1564                                 }
1565                                 TreeSocket* listener = new TreeSocket(IP.c_str(),Port,true,10);
1566                                 if (listener->GetState() == I_LISTENING)
1567                                 {
1568                                         Srv->AddSocket(listener);
1569                                         Bindings.push_back(listener);
1570                                 }
1571                                 else
1572                                 {
1573                                         log(DEFAULT,"m_spanningtree: Warning: Failed to bind server port %d",Port);
1574                                         listener->Close();
1575                                         delete listener;
1576                                 }
1577                         }
1578                 }
1579         }
1580         LinkBlocks.clear();
1581         for (int j =0; j < Conf->Enumerate("link"); j++)
1582         {
1583                 Link L;
1584                 L.Name = Conf->ReadValue("link","name",j);
1585                 L.IPAddr = Conf->ReadValue("link","ipaddr",j);
1586                 L.Port = Conf->ReadInteger("link","port",j,true);
1587                 L.SendPass = Conf->ReadValue("link","sendpass",j);
1588                 L.RecvPass = Conf->ReadValue("link","recvpass",j);
1589                 L.AutoConnect = Conf->ReadInteger("link","autoconnect",j,true);
1590                 L.NextConnectTime = time(NULL) + L.AutoConnect;
1591                 LinkBlocks.push_back(L);
1592                 log(DEBUG,"m_spanningtree: Read server %s with host %s:%d",L.Name.c_str(),L.IPAddr.c_str(),L.Port);
1593         }
1594 }
1595
1596
1597 class ModuleSpanningTree : public Module
1598 {
1599         std::vector<TreeSocket*> Bindings;
1600         int line;
1601         int NumServers;
1602
1603  public:
1604
1605         ModuleSpanningTree()
1606         {
1607                 Srv = new Server;
1608                 Conf = new ConfigReader;
1609                 Bindings.clear();
1610
1611                 // Create the root of the tree
1612                 TreeRoot = new TreeServer(Srv->GetServerName(),Srv->GetServerDescription());
1613
1614                 ReadConfiguration(true);
1615         }
1616
1617         void ShowLinks(TreeServer* Current, userrec* user, int hops)
1618         {
1619                 std::string Parent = TreeRoot->GetName();
1620                 if (Current->GetParent())
1621                 {
1622                         Parent = Current->GetParent()->GetName();
1623                 }
1624                 for (unsigned int q = 0; q < Current->ChildCount(); q++)
1625                 {
1626                         ShowLinks(Current->GetChild(q),user,hops+1);
1627                 }
1628                 WriteServ(user->fd,"364 %s %s %s :%d %s",user->nick,Current->GetName().c_str(),Parent.c_str(),hops,Current->GetDesc().c_str());
1629         }
1630
1631         int CountLocalServs()
1632         {
1633                 return TreeRoot->ChildCount();
1634         }
1635
1636         void CountServsRecursive(TreeServer* Current)
1637         {
1638                 NumServers++;
1639                 for (unsigned int q = 0; q < Current->ChildCount(); q++)
1640                 {
1641                         CountServsRecursive(Current->GetChild(q));
1642                 }
1643         }
1644         
1645         int CountServs()
1646         {
1647                 NumServers = 0;
1648                 CountServsRecursive(TreeRoot);
1649                 return NumServers;
1650         }
1651
1652         void HandleLinks(char** parameters, int pcnt, userrec* user)
1653         {
1654                 ShowLinks(TreeRoot,user,0);
1655                 WriteServ(user->fd,"365 %s * :End of /LINKS list.",user->nick);
1656                 return;
1657         }
1658
1659         void HandleLusers(char** parameters, int pcnt, userrec* user)
1660         {
1661                 WriteServ(user->fd,"251 %s :There are %d users and %d invisible on %d servers",user->nick,usercnt()-usercount_invisible(),usercount_invisible(),this->CountServs());
1662                 WriteServ(user->fd,"252 %s %d :operator(s) online",user->nick,usercount_opers());
1663                 WriteServ(user->fd,"253 %s %d :unknown connections",user->nick,usercount_unknown());
1664                 WriteServ(user->fd,"254 %s %d :channels formed",user->nick,chancount());
1665                 WriteServ(user->fd,"254 %s :I have %d clients and %d servers",user->nick,local_count(),this->CountLocalServs());
1666                 return;
1667         }
1668
1669         // WARNING: NOT THREAD SAFE - DONT GET ANY SMART IDEAS.
1670
1671         void ShowMap(TreeServer* Current, userrec* user, int depth, char matrix[128][80])
1672         {
1673                 if (line < 128)
1674                 {
1675                         for (int t = 0; t < depth; t++)
1676                         {
1677                                 matrix[line][t] = ' ';
1678                         }
1679                         strlcpy(&matrix[line][depth],Current->GetName().c_str(),80);
1680                         line++;
1681                         for (unsigned int q = 0; q < Current->ChildCount(); q++)
1682                         {
1683                                 ShowMap(Current->GetChild(q),user,depth+2,matrix);
1684                         }
1685                 }
1686         }
1687
1688         // Ok, prepare to be confused.
1689         // After much mulling over how to approach this, it struck me that
1690         // the 'usual' way of doing a /MAP isnt the best way. Instead of
1691         // keeping track of a ton of ascii characters, and line by line
1692         // under recursion working out where to place them using multiplications
1693         // and divisons, we instead render the map onto a backplane of characters
1694         // (a character matrix), then draw the branches as a series of "L" shapes
1695         // from the nodes. This is not only friendlier on CPU it uses less stack.
1696
1697         void HandleMap(char** parameters, int pcnt, userrec* user)
1698         {
1699                 // This array represents a virtual screen which we will
1700                 // "scratch" draw to, as the console device of an irc
1701                 // client does not provide for a proper terminal.
1702                 char matrix[128][80];
1703                 for (unsigned int t = 0; t < 128; t++)
1704                 {
1705                         matrix[t][0] = '\0';
1706                 }
1707                 line = 0;
1708                 // The only recursive bit is called here.
1709                 ShowMap(TreeRoot,user,0,matrix);
1710                 // Process each line one by one. The algorithm has a limit of
1711                 // 128 servers (which is far more than a spanning tree should have
1712                 // anyway, so we're ok). This limit can be raised simply by making
1713                 // the character matrix deeper, 128 rows taking 10k of memory.
1714                 for (int l = 1; l < line; l++)
1715                 {
1716                         // scan across the line looking for the start of the
1717                         // servername (the recursive part of the algorithm has placed
1718                         // the servers at indented positions depending on what they
1719                         // are related to)
1720                         int first_nonspace = 0;
1721                         while (matrix[l][first_nonspace] == ' ')
1722                         {
1723                                 first_nonspace++;
1724                         }
1725                         first_nonspace--;
1726                         // Draw the `- (corner) section: this may be overwritten by
1727                         // another L shape passing along the same vertical pane, becoming
1728                         // a |- (branch) section instead.
1729                         matrix[l][first_nonspace] = '-';
1730                         matrix[l][first_nonspace-1] = '`';
1731                         int l2 = l - 1;
1732                         // Draw upwards until we hit the parent server, causing possibly
1733                         // other corners (`-) to become branches (|-)
1734                         while ((matrix[l2][first_nonspace-1] == ' ') || (matrix[l2][first_nonspace-1] == '`'))
1735                         {
1736                                 matrix[l2][first_nonspace-1] = '|';
1737                                 l2--;
1738                         }
1739                 }
1740                 // dump the whole lot to the user. This is the easy bit, honest.
1741                 for (int t = 0; t < line; t++)
1742                 {
1743                         WriteServ(user->fd,"006 %s :%s",user->nick,&matrix[t][0]);
1744                 }
1745                 WriteServ(user->fd,"007 %s :End of /MAP",user->nick);
1746                 return;
1747         }
1748
1749         int HandleSquit(char** parameters, int pcnt, userrec* user)
1750         {
1751                 TreeServer* s = FindServerMask(parameters[0]);
1752                 if (s)
1753                 {
1754                         TreeSocket* sock = s->GetSocket();
1755                         if (sock)
1756                         {
1757                                 WriteOpers("*** SQUIT: Server \002%s\002 removed from network by %s",parameters[0],user->nick);
1758                                 sock->Squit(s,"Server quit by "+std::string(user->nick)+"!"+std::string(user->ident)+"@"+std::string(user->host));
1759                                 sock->Close();
1760                         }
1761                         else
1762                         {
1763                                 WriteServ(user->fd,"NOTICE %s :*** SQUIT: The server \002%s\002 is not directly connected.",user->nick,parameters[0]);
1764                         }
1765                 }
1766                 else
1767                 {
1768                          WriteServ(user->fd,"NOTICE %s :*** SQUIT: The server \002%s\002 does not exist on the network.",user->nick,parameters[0]);
1769                 }
1770                 return 1;
1771         }
1772
1773         void DoPingChecks(time_t curtime)
1774         {
1775                 for (unsigned int j = 0; j < TreeRoot->ChildCount(); j++)
1776                 {
1777                         TreeServer* serv = TreeRoot->GetChild(j);
1778                         TreeSocket* sock = serv->GetSocket();
1779                         if (sock)
1780                         {
1781                                 if (curtime >= serv->NextPingTime())
1782                                 {
1783                                         if (serv->AnsweredLastPing())
1784                                         {
1785                                                 sock->WriteLine(":"+Srv->GetServerName()+" PING "+serv->GetName());
1786                                                 serv->SetNextPingTime(curtime + 60);
1787                                         }
1788                                         else
1789                                         {
1790                                                 // they didnt answer, boot them
1791                                                 WriteOpers("*** Server \002%s\002 pinged out",serv->GetName().c_str());
1792                                                 sock->Squit(serv,"Ping timeout");
1793                                                 sock->Close();
1794                                                 return;
1795                                         }
1796                                 }
1797                         }
1798                 }
1799         }
1800
1801         void AutoConnectServers(time_t curtime)
1802         {
1803                 for (std::vector<Link>::iterator x = LinkBlocks.begin(); x < LinkBlocks.end(); x++)
1804                 {
1805                         if ((x->AutoConnect) && (curtime >= x->NextConnectTime))
1806                         {
1807                                 log(DEBUG,"Auto-Connecting %s",x->Name.c_str());
1808                                 x->NextConnectTime = curtime + x->AutoConnect;
1809                                 TreeServer* CheckDupe = FindServer(x->Name);
1810                                 if (!CheckDupe)
1811                                 {
1812                                         // an autoconnected server is not connected. Check if its time to connect it
1813                                         WriteOpers("*** AUTOCONNECT: Auto-connecting server \002%s\002 (%lu seconds until next attempt)",x->Name.c_str(),x->AutoConnect);
1814                                         TreeSocket* newsocket = new TreeSocket(x->IPAddr,x->Port,false,10,x->Name);
1815                                         Srv->AddSocket(newsocket);
1816                                 }
1817                         }
1818                 }
1819         }
1820
1821         int HandleVersion(char** parameters, int pcnt, userrec* user)
1822         {
1823                 // we've already checked if pcnt > 0, so this is safe
1824                 TreeServer* found = FindServerMask(parameters[0]);
1825                 if (found)
1826                 {
1827                         std::string Version = found->GetVersion();
1828                         WriteServ(user->fd,"351 %s :%s",user->nick,Version.c_str());
1829                 }
1830                 else
1831                 {
1832                         WriteServ(user->fd,"402 %s %s :No such server",user->nick,parameters[0]);
1833                 }
1834                 return 1;
1835         }
1836         
1837         int HandleConnect(char** parameters, int pcnt, userrec* user)
1838         {
1839                 for (std::vector<Link>::iterator x = LinkBlocks.begin(); x < LinkBlocks.end(); x++)
1840                 {
1841                         if (Srv->MatchText(x->Name.c_str(),parameters[0]))
1842                         {
1843                                 TreeServer* CheckDupe = FindServer(x->Name);
1844                                 if (!CheckDupe)
1845                                 {
1846                                         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);
1847                                         TreeSocket* newsocket = new TreeSocket(x->IPAddr,x->Port,false,10,x->Name);
1848                                         Srv->AddSocket(newsocket);
1849                                         return 1;
1850                                 }
1851                                 else
1852                                 {
1853                                         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());
1854                                         return 1;
1855                                 }
1856                         }
1857                 }
1858                 WriteServ(user->fd,"NOTICE %s :*** CONNECT: No server matching \002%s\002 could be found in the config file.",user->nick,parameters[0]);
1859                 return 1;
1860         }
1861
1862         virtual int OnPreCommand(std::string command, char **parameters, int pcnt, userrec *user)
1863         {
1864                 if (command == "CONNECT")
1865                 {
1866                         return this->HandleConnect(parameters,pcnt,user);
1867                 }
1868                 else if (command == "SQUIT")
1869                 {
1870                         return this->HandleSquit(parameters,pcnt,user);
1871                 }
1872                 else if (command == "MAP")
1873                 {
1874                         this->HandleMap(parameters,pcnt,user);
1875                         return 1;
1876                 }
1877                 else if (command == "LUSERS")
1878                 {
1879                         this->HandleLusers(parameters,pcnt,user);
1880                         return 1;
1881                 }
1882                 else if (command == "LINKS")
1883                 {
1884                         this->HandleLinks(parameters,pcnt,user);
1885                         return 1;
1886                 }
1887                 else if ((command == "VERSION") && (pcnt > 0))
1888                 {
1889                         this->HandleVersion(parameters,pcnt,user);
1890                         return 1;
1891                 }
1892                 else if (Srv->IsValidModuleCommand(command, pcnt, user))
1893                 {
1894                         // this bit of code cleverly routes all module commands
1895                         // to all remote severs *automatically* so that modules
1896                         // can just handle commands locally, without having
1897                         // to have any special provision in place for remote
1898                         // commands and linking protocols.
1899                         std::deque<std::string> params;
1900                         params.clear();
1901                         for (int j = 0; j < pcnt; j++)
1902                         {
1903                                 if (strchr(parameters[j],' '))
1904                                 {
1905                                         params.push_back(":" + std::string(parameters[j]));
1906                                 }
1907                                 else
1908                                 {
1909                                         params.push_back(std::string(parameters[j]));
1910                                 }
1911                         }
1912                         DoOneToMany(user->nick,command,params);
1913                 }
1914                 return 0;
1915         }
1916
1917         virtual void OnGetServerDescription(std::string servername,std::string &description)
1918         {
1919                 TreeServer* s = FindServer(servername);
1920                 if (s)
1921                 {
1922                         description = s->GetDesc();
1923                 }
1924         }
1925
1926         virtual void OnUserInvite(userrec* source,userrec* dest,chanrec* channel)
1927         {
1928                 if (std::string(source->server) == Srv->GetServerName())
1929                 {
1930                         std::deque<std::string> params;
1931                         params.push_back(dest->nick);
1932                         params.push_back(channel->name);
1933                         DoOneToMany(source->nick,"INVITE",params);
1934                 }
1935         }
1936
1937         virtual void OnPostLocalTopicChange(userrec* user, chanrec* chan, std::string topic)
1938         {
1939                 std::deque<std::string> params;
1940                 params.push_back(chan->name);
1941                 params.push_back(":"+topic);
1942                 DoOneToMany(user->nick,"TOPIC",params);
1943         }
1944
1945         virtual void OnWallops(userrec* user, std::string text)
1946         {
1947                 if (std::string(user->server) == Srv->GetServerName())
1948                 {
1949                         std::deque<std::string> params;
1950                         params.push_back(":"+text);
1951                         DoOneToMany(user->nick,"WALLOPS",params);
1952                 }
1953         }
1954
1955         virtual void OnUserNotice(userrec* user, void* dest, int target_type, std::string text)
1956         {
1957                 if (target_type == TYPE_USER)
1958                 {
1959                         userrec* d = (userrec*)dest;
1960                         if ((std::string(d->server) != Srv->GetServerName()) && (std::string(user->server) == Srv->GetServerName()))
1961                         {
1962                                 std::deque<std::string> params;
1963                                 params.clear();
1964                                 params.push_back(d->nick);
1965                                 params.push_back(":"+text);
1966                                 DoOneToOne(user->nick,"NOTICE",params,d->server);
1967                         }
1968                 }
1969                 else
1970                 {
1971                         if (std::string(user->server) == Srv->GetServerName())
1972                         {
1973                                 chanrec *c = (chanrec*)dest;
1974                                 std::deque<TreeServer*> list = GetListOfServersForChannel(c);
1975                                 for (unsigned int i = 0; i < list.size(); i++)
1976                                 {
1977                                         TreeSocket* Sock = list[i]->GetSocket();
1978                                         if (Sock)
1979                                                 Sock->WriteLine(":"+std::string(user->nick)+" NOTICE "+std::string(c->name)+" :"+text);
1980                                 }
1981                         }
1982                 }
1983         }
1984
1985         virtual void OnUserMessage(userrec* user, void* dest, int target_type, std::string text)
1986         {
1987                 if (target_type == TYPE_USER)
1988                 {
1989                         // route private messages which are targetted at clients only to the server
1990                         // which needs to receive them
1991                         userrec* d = (userrec*)dest;
1992                         if ((std::string(d->server) != Srv->GetServerName()) && (std::string(user->server) == Srv->GetServerName()))
1993                         {
1994                                 std::deque<std::string> params;
1995                                 params.clear();
1996                                 params.push_back(d->nick);
1997                                 params.push_back(":"+text);
1998                                 DoOneToOne(user->nick,"PRIVMSG",params,d->server);
1999                         }
2000                 }
2001                 else
2002                 {
2003                         if (std::string(user->server) == Srv->GetServerName())
2004                         {
2005                                 chanrec *c = (chanrec*)dest;
2006                                 std::deque<TreeServer*> list = GetListOfServersForChannel(c);
2007                                 for (unsigned int i = 0; i < list.size(); i++)
2008                                 {
2009                                         TreeSocket* Sock = list[i]->GetSocket();
2010                                         if (Sock)
2011                                                 Sock->WriteLine(":"+std::string(user->nick)+" PRIVMSG "+std::string(c->name)+" :"+text);
2012                                 }
2013                         }
2014                 }
2015         }
2016
2017         virtual void OnBackgroundTimer(time_t curtime)
2018         {
2019                 AutoConnectServers(curtime);
2020                 DoPingChecks(curtime);
2021         }
2022
2023         virtual void OnUserJoin(userrec* user, chanrec* channel)
2024         {
2025                 // Only do this for local users
2026                 if (std::string(user->server) == Srv->GetServerName())
2027                 {
2028                         std::deque<std::string> params;
2029                         params.clear();
2030                         params.push_back(channel->name);
2031                         if (*channel->key)
2032                         {
2033                                 // if the channel has a key, force the join by emulating the key.
2034                                 params.push_back(channel->key);
2035                         }
2036                         if (channel->GetUserCounter() > 1)
2037                         {
2038                                 // not the first in the channel
2039                                 DoOneToMany(user->nick,"JOIN",params);
2040                         }
2041                         else
2042                         {
2043                                 // first in the channel, set up their permissions
2044                                 // and the channel TS with FJOIN.
2045                                 char ts[24];
2046                                 snprintf(ts,24,"%lu",(unsigned long)channel->age);
2047                                 params.clear();
2048                                 params.push_back(channel->name);
2049                                 params.push_back(ts);
2050                                 params.push_back("@"+std::string(user->nick));
2051                                 DoOneToMany(Srv->GetServerName(),"FJOIN",params);
2052                         }
2053                 }
2054         }
2055
2056         virtual void OnChangeHost(userrec* user, std::string newhost)
2057         {
2058                 // only occurs for local clients
2059                 std::deque<std::string> params;
2060                 params.push_back(newhost);
2061                 DoOneToMany(user->nick,"FHOST",params);
2062         }
2063
2064         virtual void OnChangeName(userrec* user, std::string gecos)
2065         {
2066                 // only occurs for local clients
2067                 std::deque<std::string> params;
2068                 params.push_back(gecos);
2069                 DoOneToMany(user->nick,"FNAME",params);
2070         }
2071
2072         virtual void OnUserPart(userrec* user, chanrec* channel)
2073         {
2074                 if (std::string(user->server) == Srv->GetServerName())
2075                 {
2076                         std::deque<std::string> params;
2077                         params.clear();
2078                         params.push_back(channel->name);
2079                         DoOneToMany(user->nick,"PART",params);
2080                 }
2081         }
2082
2083         virtual void OnUserConnect(userrec* user)
2084         {
2085                 char agestr[MAXBUF];
2086                 if (std::string(user->server) == Srv->GetServerName())
2087                 {
2088                         std::deque<std::string> params;
2089                         snprintf(agestr,MAXBUF,"%lu",(unsigned long)user->age);
2090                         params.clear();
2091                         params.push_back(agestr);
2092                         params.push_back(user->nick);
2093                         params.push_back(user->host);
2094                         params.push_back(user->dhost);
2095                         params.push_back(user->ident);
2096                         params.push_back("+"+std::string(user->modes));
2097                         params.push_back(user->ip);
2098                         params.push_back(":"+std::string(user->fullname));
2099                         DoOneToMany(Srv->GetServerName(),"NICK",params);
2100                 }
2101         }
2102
2103         virtual void OnUserQuit(userrec* user, std::string reason)
2104         {
2105                 if (std::string(user->server) == Srv->GetServerName())
2106                 {
2107                         std::deque<std::string> params;
2108                         params.push_back(":"+reason);
2109                         DoOneToMany(user->nick,"QUIT",params);
2110                 }
2111         }
2112
2113         virtual void OnUserPostNick(userrec* user, std::string oldnick)
2114         {
2115                 if (std::string(user->server) == Srv->GetServerName())
2116                 {
2117                         std::deque<std::string> params;
2118                         params.push_back(user->nick);
2119                         DoOneToMany(oldnick,"NICK",params);
2120                 }
2121         }
2122
2123         virtual void OnUserKick(userrec* source, userrec* user, chanrec* chan, std::string reason)
2124         {
2125                 if (std::string(source->server) == Srv->GetServerName())
2126                 {
2127                         std::deque<std::string> params;
2128                         params.push_back(chan->name);
2129                         params.push_back(user->nick);
2130                         params.push_back(":"+reason);
2131                         DoOneToMany(source->nick,"KICK",params);
2132                 }
2133         }
2134
2135         virtual void OnRemoteKill(userrec* source, userrec* dest, std::string reason)
2136         {
2137                 std::deque<std::string> params;
2138                 params.push_back(dest->nick);
2139                 params.push_back(":"+reason);
2140                 DoOneToMany(source->nick,"KILL",params);
2141         }
2142
2143         virtual void OnRehash(std::string parameter)
2144         {
2145                 if (parameter != "")
2146                 {
2147                         std::deque<std::string> params;
2148                         params.push_back(parameter);
2149                         DoOneToMany(Srv->GetServerName(),"REHASH",params);
2150                         // check for self
2151                         if (Srv->MatchText(Srv->GetServerName(),parameter))
2152                         {
2153                                 Srv->SendOpers("*** Remote rehash initiated from server \002"+Srv->GetServerName()+"\002.");
2154                                 Srv->RehashServer();
2155                         }
2156                 }
2157                 ReadConfiguration(false);
2158         }
2159
2160         // note: the protocol does not allow direct umode +o except
2161         // via NICK with 8 params. sending OPERTYPE infers +o modechange
2162         // locally.
2163         virtual void OnOper(userrec* user, std::string opertype)
2164         {
2165                 if (std::string(user->server) == Srv->GetServerName())
2166                 {
2167                         std::deque<std::string> params;
2168                         params.push_back(opertype);
2169                         DoOneToMany(user->nick,"OPERTYPE",params);
2170                 }
2171         }
2172
2173         virtual void OnMode(userrec* user, void* dest, int target_type, std::string text)
2174         {
2175                 if (std::string(user->server) == Srv->GetServerName())
2176                 {
2177                         if (target_type == TYPE_USER)
2178                         {
2179                                 userrec* u = (userrec*)dest;
2180                                 std::deque<std::string> params;
2181                                 params.push_back(u->nick);
2182                                 params.push_back(text);
2183                                 DoOneToMany(user->nick,"MODE",params);
2184                         }
2185                         else
2186                         {
2187                                 chanrec* c = (chanrec*)dest;
2188                                 std::deque<std::string> params;
2189                                 params.push_back(c->name);
2190                                 params.push_back(text);
2191                                 DoOneToMany(user->nick,"MODE",params);
2192                         }
2193                 }
2194         }
2195
2196         virtual void ProtoSendMode(void* opaque, int target_type, void* target, std::string modeline)
2197         {
2198                 TreeSocket* s = (TreeSocket*)opaque;
2199                 if (target)
2200                 {
2201                         if (target_type == TYPE_USER)
2202                         {
2203                                 userrec* u = (userrec*)target;
2204                                 s->WriteLine(":"+Srv->GetServerName()+" FMODE "+u->nick+" "+modeline);
2205                         }
2206                         else
2207                         {
2208                                 chanrec* c = (chanrec*)target;
2209                                 s->WriteLine(":"+Srv->GetServerName()+" FMODE "+c->name+" "+modeline);
2210                         }
2211                 }
2212         }
2213
2214         virtual ~ModuleSpanningTree()
2215         {
2216                 delete Srv;
2217         }
2218
2219         virtual Version GetVersion()
2220         {
2221                 return Version(1,0,0,0,VF_STATIC|VF_VENDOR);
2222         }
2223 };
2224
2225
2226 class ModuleSpanningTreeFactory : public ModuleFactory
2227 {
2228  public:
2229         ModuleSpanningTreeFactory()
2230         {
2231         }
2232         
2233         ~ModuleSpanningTreeFactory()
2234         {
2235         }
2236         
2237         virtual Module * CreateModule()
2238         {
2239                 TreeProtocolModule = new ModuleSpanningTree;
2240                 return TreeProtocolModule;
2241         }
2242         
2243 };
2244
2245
2246 extern "C" void * init_module( void )
2247 {
2248         return new ModuleSpanningTreeFactory;
2249 }