]> git.netwichtig.de Git - user/henk/code/inspircd.git/blob - src/modules/m_spanningtree/main.cpp
Remove space indents
[user/henk/code/inspircd.git] / src / modules / m_spanningtree / main.cpp
1 /*       +------------------------------------+
2  *       | Inspire Internet Relay Chat Daemon |
3  *       +------------------------------------+
4  *
5  *  InspIRCd: (C) 2002-2007 InspIRCd Development Team
6  * See: http://www.inspircd.org/wiki/index.php/Credits
7  *
8  * This program is free but copyrighted software; see
9  *          the file COPYING for details.
10  *
11  * ---------------------------------------------------
12  */
13
14 /* $ModDesc: Provides a spanning tree server link protocol */
15
16 #include "configreader.h"
17 #include "users.h"
18 #include "channels.h"
19 #include "modules.h"
20 #include "commands/cmd_whois.h"
21 #include "commands/cmd_stats.h"
22 #include "socket.h"
23 #include "inspircd.h"
24 #include "wildcard.h"
25 #include "xline.h"
26 #include "transport.h"
27
28 #include "m_spanningtree/main.h"
29 #include "m_spanningtree/utils.h"
30 #include "m_spanningtree/treeserver.h"
31 #include "m_spanningtree/link.h"
32 #include "m_spanningtree/treesocket.h"
33
34 /** Handle /RCONNECT
35  */
36 class cmd_rconnect : public command_t
37 {
38         Module* Creator;
39         SpanningTreeUtilities* Utils;
40  public:
41         cmd_rconnect (InspIRCd* Instance, Module* Callback, SpanningTreeUtilities* Util) : command_t(Instance, "RCONNECT", 'o', 2), Creator(Callback), Utils(Util)
42         {
43                 this->source = "m_spanningtree.so";
44                 syntax = "<remote-server-mask> <target-server-mask>";
45         }
46
47         CmdResult Handle (const char** parameters, int pcnt, userrec *user)
48         {
49                 if (IS_LOCAL(user))
50                 {
51                         if (!Utils->FindServer(parameters[0]))
52                         {
53                                 user->WriteServ("NOTICE %s :*** RCONNECT: Server \002%s\002 isn't connected to the network!", user->nick, parameters[0]);
54                                 return CMD_FAILURE;
55                         }
56                         
57                         user->WriteServ("NOTICE %s :*** RCONNECT: Sending remote connect to \002%s\002 to connect server \002%s\002.",user->nick,parameters[0],parameters[1]);
58                 }
59                 
60                 /* Is this aimed at our server? */
61                 if (ServerInstance->MatchText(ServerInstance->Config->ServerName,parameters[0]))
62                 {
63                         /* Yes, initiate the given connect */
64                         ServerInstance->SNO->WriteToSnoMask('l',"Remote CONNECT from %s matching \002%s\002, connecting server \002%s\002",user->nick,parameters[0],parameters[1]);
65                         const char* para[1];
66                         para[0] = parameters[1];
67                         std::string original_command = std::string("CONNECT ") + parameters[1];
68                         Creator->OnPreCommand("CONNECT", para, 1, user, true, original_command);
69                 }
70                 
71                 return CMD_SUCCESS;
72         }
73 };
74  
75 /** This class is used to resolve server hostnames during /connect and autoconnect.
76  * As of 1.1, the resolver system is seperated out from InspSocket, so we must do this
77  * resolver step first ourselves if we need it. This is totally nonblocking, and will
78  * callback to OnLookupComplete or OnError when completed. Once it has completed we
79  * will have an IP address which we can then use to continue our connection.
80  */
81 class ServernameResolver : public Resolver
82 {       
83  private:
84         /** A copy of the Link tag info for what we're connecting to.
85          * We take a copy, rather than using a pointer, just in case the
86          * admin takes the tag away and rehashes while the domain is resolving.
87          */
88         Link MyLink;
89         SpanningTreeUtilities* Utils;
90  public: 
91         ServernameResolver(Module* me, SpanningTreeUtilities* Util, InspIRCd* Instance, const std::string &hostname, Link x, bool &cached) : Resolver(Instance, hostname, DNS_QUERY_FORWARD, cached, me), MyLink(x), Utils(Util)
92         {
93                 /* Nothing in here, folks */
94         }
95
96         void OnLookupComplete(const std::string &result, unsigned int ttl, bool cached)
97         {
98                 /* Initiate the connection, now that we have an IP to use.
99                  * Passing a hostname directly to InspSocket causes it to
100                  * just bail and set its FD to -1.
101                  */
102                 TreeServer* CheckDupe = Utils->FindServer(MyLink.Name.c_str());
103                 if (!CheckDupe) /* Check that nobody tried to connect it successfully while we were resolving */
104                 {
105
106                         if ((!MyLink.Hook.empty()) && (Utils->hooks.find(MyLink.Hook.c_str()) ==  Utils->hooks.end()))
107                                 return;
108
109                         TreeSocket* newsocket = new TreeSocket(this->Utils, ServerInstance, result,MyLink.Port,false,MyLink.Timeout ? MyLink.Timeout : 10,MyLink.Name.c_str(),
110                                         MyLink.Hook.empty() ? NULL : Utils->hooks[MyLink.Hook.c_str()]);
111                         if (newsocket->GetFd() > -1)
112                         {
113                                 /* We're all OK */
114                         }
115                         else
116                         {
117                                 /* Something barfed, show the opers */
118                                 ServerInstance->SNO->WriteToSnoMask('l',"CONNECT: Error connecting \002%s\002: %s.",MyLink.Name.c_str(),strerror(errno));
119                                 delete newsocket;
120                                 Utils->DoFailOver(&MyLink);
121                         }
122                 }
123         }
124
125         void OnError(ResolverError e, const std::string &errormessage)
126         {
127                 /* Ooops! */
128                 ServerInstance->SNO->WriteToSnoMask('l',"CONNECT: Error connecting \002%s\002: Unable to resolve hostname - %s",MyLink.Name.c_str(),errormessage.c_str());
129                 Utils->DoFailOver(&MyLink);
130         }
131 };
132
133 /** Create a timer which recurs every second, we inherit from InspTimer.
134  * InspTimer is only one-shot however, so at the end of each Tick() we simply
135  * insert another of ourselves into the pending queue :)
136  */
137 class TimeSyncTimer : public InspTimer
138 {
139  private:
140         InspIRCd *Instance;
141         ModuleSpanningTree *Module;
142  public:
143         TimeSyncTimer(InspIRCd *Instance, ModuleSpanningTree *Mod);
144         virtual void Tick(time_t TIME);
145 };
146
147
148 ModuleSpanningTree::ModuleSpanningTree(InspIRCd* Me)
149         : Module::Module(Me), max_local(0), max_global(0)
150 {
151         ServerInstance->UseInterface("InspSocketHook");
152         Utils = new SpanningTreeUtilities(Me, this);
153         command_rconnect = new cmd_rconnect(ServerInstance, this, Utils);
154         ServerInstance->AddCommand(command_rconnect);
155         if (Utils->EnableTimeSync)
156         {
157                 SyncTimer = new TimeSyncTimer(ServerInstance, this);
158                 ServerInstance->Timers->AddTimer(SyncTimer);
159         }
160         else
161                 SyncTimer = NULL;
162 }
163
164 void ModuleSpanningTree::ShowLinks(TreeServer* Current, userrec* user, int hops)
165 {
166         std::string Parent = Utils->TreeRoot->GetName();
167         if (Current->GetParent())
168         {
169                 Parent = Current->GetParent()->GetName();
170         }
171         for (unsigned int q = 0; q < Current->ChildCount(); q++)
172         {
173                 if ((Utils->HideULines) && (ServerInstance->ULine(Current->GetChild(q)->GetName().c_str())))
174                 {
175                         if (*user->oper)
176                         {
177                                  ShowLinks(Current->GetChild(q),user,hops+1);
178                         }
179                 }
180                 else
181                 {
182                         ShowLinks(Current->GetChild(q),user,hops+1);
183                 }
184         }
185         /* Don't display the line if its a uline, hide ulines is on, and the user isnt an oper */
186         if ((Utils->HideULines) && (ServerInstance->ULine(Current->GetName().c_str())) && (!*user->oper))
187                 return;
188         user->WriteServ("364 %s %s %s :%d %s",user->nick,Current->GetName().c_str(),(Utils->FlatLinks && (!*user->oper)) ? ServerInstance->Config->ServerName : Parent.c_str(),(Utils->FlatLinks && (!*user->oper)) ? 0 : hops,Current->GetDesc().c_str());
189 }
190
191 int ModuleSpanningTree::CountLocalServs()
192 {
193         return Utils->TreeRoot->ChildCount();
194 }
195
196 int ModuleSpanningTree::CountServs()
197 {
198         return Utils->serverlist.size();
199 }
200
201 void ModuleSpanningTree::HandleLinks(const char** parameters, int pcnt, userrec* user)
202 {
203         ShowLinks(Utils->TreeRoot,user,0);
204         user->WriteServ("365 %s * :End of /LINKS list.",user->nick);
205         return;
206 }
207
208 void ModuleSpanningTree::HandleLusers(const char** parameters, int pcnt, userrec* user)
209 {
210         unsigned int n_users = ServerInstance->UserCount();
211
212         /* Only update these when someone wants to see them, more efficient */
213         if ((unsigned int)ServerInstance->LocalUserCount() > max_local)
214                 max_local = ServerInstance->LocalUserCount();
215         if (n_users > max_global)
216                 max_global = n_users;
217
218         unsigned int ulined_count = 0;
219         unsigned int ulined_local_count = 0;
220
221         /* If ulined are hidden and we're not an oper, count the number of ulined servers hidden,
222          * locally and globally (locally means directly connected to us)
223          */
224         if ((Utils->HideULines) && (!*user->oper))
225         {
226                 for (server_hash::iterator q = Utils->serverlist.begin(); q != Utils->serverlist.end(); q++)
227                 {
228                         if (ServerInstance->ULine(q->second->GetName().c_str()))
229                         {
230                                 ulined_count++;
231                                 if (q->second->GetParent() == Utils->TreeRoot)
232                                         ulined_local_count++;
233                         }
234                 }
235         }
236         user->WriteServ("251 %s :There are %d users and %d invisible on %d servers",user->nick,n_users-ServerInstance->InvisibleUserCount(),ServerInstance->InvisibleUserCount(),ulined_count ? this->CountServs() - ulined_count : this->CountServs());
237         if (ServerInstance->OperCount())
238                 user->WriteServ("252 %s %d :operator(s) online",user->nick,ServerInstance->OperCount());
239         if (ServerInstance->UnregisteredUserCount())
240                 user->WriteServ("253 %s %d :unknown connections",user->nick,ServerInstance->UnregisteredUserCount());
241         if (ServerInstance->ChannelCount())
242                 user->WriteServ("254 %s %d :channels formed",user->nick,ServerInstance->ChannelCount());
243         user->WriteServ("254 %s :I have %d clients and %d servers",user->nick,ServerInstance->LocalUserCount(),ulined_local_count ? this->CountLocalServs() - ulined_local_count : this->CountLocalServs());
244         user->WriteServ("265 %s :Current Local Users: %d  Max: %d",user->nick,ServerInstance->LocalUserCount(),max_local);
245         user->WriteServ("266 %s :Current Global Users: %d  Max: %d",user->nick,n_users,max_global);
246         return;
247 }
248
249 // WARNING: NOT THREAD SAFE - DONT GET ANY SMART IDEAS.
250 void ModuleSpanningTree::ShowMap(TreeServer* Current, userrec* user, int depth, char matrix[128][80], float &totusers, float &totservers)
251 {
252         if (line < 128)
253         {
254                 for (int t = 0; t < depth; t++)
255                 {
256                         matrix[line][t] = ' ';
257                 }
258                 // For Aligning, we need to work out exactly how deep this thing is, and produce
259                 // a 'Spacer' String to compensate.
260                 char spacer[40];
261                 memset(spacer,' ',40);
262                 if ((40 - Current->GetName().length() - depth) > 1) {
263                         spacer[40 - Current->GetName().length() - depth] = '\0';
264                 }
265                 else
266                 {
267                         spacer[5] = '\0';
268                 }
269                 float percent;
270                 char text[80];
271                 if (ServerInstance->clientlist->size() == 0) {
272                         // If there are no users, WHO THE HELL DID THE /MAP?!?!?!
273                         percent = 0;
274                 }
275                 else
276                 {
277                         percent = ((float)Current->GetUserCount() / (float)ServerInstance->clientlist->size()) * 100;
278                 }
279                 snprintf(text, 80, "%s %s%5d [%5.2f%%]", Current->GetName().c_str(), spacer, Current->GetUserCount(), percent);
280                 totusers += Current->GetUserCount();
281                 totservers++;
282                 strlcpy(&matrix[line][depth],text,80);
283                 line++;
284                 for (unsigned int q = 0; q < Current->ChildCount(); q++)
285                 {
286                         if ((Utils->HideULines) && (ServerInstance->ULine(Current->GetChild(q)->GetName().c_str())))
287                         {
288                                 if (*user->oper)
289                                 {
290                                         ShowMap(Current->GetChild(q),user,(Utils->FlatLinks && (!*user->oper)) ? depth : depth+2,matrix,totusers,totservers);
291                                 }
292                         }
293                         else
294                         {
295                                 ShowMap(Current->GetChild(q),user,(Utils->FlatLinks && (!*user->oper)) ? depth : depth+2,matrix,totusers,totservers);
296                         }
297                 }
298         }
299 }
300
301 int ModuleSpanningTree::HandleMotd(const char** parameters, int pcnt, userrec* user)
302 {
303         if (pcnt > 0)
304         {
305                 /* Remote MOTD, the server is within the 1st parameter */
306                 std::deque<std::string> params;
307                 params.push_back(parameters[0]);
308                 /* Send it out remotely, generate no reply yet */
309                 TreeServer* s = Utils->FindServerMask(parameters[0]);
310                 if (s)
311                 {
312                         Utils->DoOneToOne(user->nick, "MOTD", params, s->GetName());
313                 }
314                 else
315                 {
316                         user->WriteServ( "402 %s %s :No such server", user->nick, parameters[0]);
317                 }
318                 return 1;
319         }
320         return 0;
321 }
322
323 int ModuleSpanningTree::HandleAdmin(const char** parameters, int pcnt, userrec* user)
324 {
325         if (pcnt > 0)
326         {
327                 /* Remote ADMIN, the server is within the 1st parameter */
328                 std::deque<std::string> params;
329                 params.push_back(parameters[0]);
330                 /* Send it out remotely, generate no reply yet */
331                 TreeServer* s = Utils->FindServerMask(parameters[0]);
332                 if (s)
333                 {
334                         Utils->DoOneToOne(user->nick, "ADMIN", params, s->GetName());
335                 }
336                 else
337                 {
338                         user->WriteServ( "402 %s %s :No such server", user->nick, parameters[0]);
339                 }
340                 return 1;
341         }
342         return 0;
343 }
344
345 int ModuleSpanningTree::HandleStats(const char** parameters, int pcnt, userrec* user)
346 {
347         if (pcnt > 1)
348         {
349                 /* Remote STATS, the server is within the 2nd parameter */
350                 std::deque<std::string> params;
351                 params.push_back(parameters[0]);
352                 params.push_back(parameters[1]);
353                 /* Send it out remotely, generate no reply yet */
354                 TreeServer* s = Utils->FindServerMask(parameters[1]);
355                 if (s)
356                 {
357                         params[1] = s->GetName();
358                         Utils->DoOneToOne(user->nick, "STATS", params, s->GetName());
359                 }
360                 else
361                 {
362                         user->WriteServ( "402 %s %s :No such server", user->nick, parameters[0]);
363                 }
364                 return 1;
365         }
366         return 0;
367 }
368
369 // Ok, prepare to be confused.
370 // After much mulling over how to approach this, it struck me that
371 // the 'usual' way of doing a /MAP isnt the best way. Instead of
372 // keeping track of a ton of ascii characters, and line by line
373 // under recursion working out where to place them using multiplications
374 // and divisons, we instead render the map onto a backplane of characters
375 // (a character matrix), then draw the branches as a series of "L" shapes
376 // from the nodes. This is not only friendlier on CPU it uses less stack.
377 void ModuleSpanningTree::HandleMap(const char** parameters, int pcnt, userrec* user)
378 {
379         // This array represents a virtual screen which we will
380         // "scratch" draw to, as the console device of an irc
381         // client does not provide for a proper terminal.
382         float totusers = 0;
383         float totservers = 0;
384         char matrix[128][80];
385         for (unsigned int t = 0; t < 128; t++)
386         {
387                 matrix[t][0] = '\0';
388         }
389         line = 0;
390         // The only recursive bit is called here.
391         ShowMap(Utils->TreeRoot,user,0,matrix,totusers,totservers);
392         // Process each line one by one. The algorithm has a limit of
393         // 128 servers (which is far more than a spanning tree should have
394         // anyway, so we're ok). This limit can be raised simply by making
395         // the character matrix deeper, 128 rows taking 10k of memory.
396         for (int l = 1; l < line; l++)
397         {
398                 // scan across the line looking for the start of the
399                 // servername (the recursive part of the algorithm has placed
400                 // the servers at indented positions depending on what they
401                 // are related to)
402                 int first_nonspace = 0;
403                 while (matrix[l][first_nonspace] == ' ')
404                 {
405                         first_nonspace++;
406                 }
407                 first_nonspace--;
408                 // Draw the `- (corner) section: this may be overwritten by
409                 // another L shape passing along the same vertical pane, becoming
410                 // a |- (branch) section instead.
411                 matrix[l][first_nonspace] = '-';
412                 matrix[l][first_nonspace-1] = '`';
413                 int l2 = l - 1;
414                 // Draw upwards until we hit the parent server, causing possibly
415                 // other corners (`-) to become branches (|-)
416                 while ((matrix[l2][first_nonspace-1] == ' ') || (matrix[l2][first_nonspace-1] == '`'))
417                 {
418                         matrix[l2][first_nonspace-1] = '|';
419                         l2--;
420                 }
421         }
422         // dump the whole lot to the user. This is the easy bit, honest.
423         for (int t = 0; t < line; t++)
424         {
425                 user->WriteServ("006 %s :%s",user->nick,&matrix[t][0]);
426         }
427         float avg_users = totusers / totservers;
428         user->WriteServ("270 %s :%.0f server%s and %.0f user%s, average %.2f users per server",user->nick,totservers,(totservers > 1 ? "s" : ""),totusers,(totusers > 1 ? "s" : ""),avg_users);
429         user->WriteServ("007 %s :End of /MAP",user->nick);
430         return;
431 }
432
433 int ModuleSpanningTree::HandleSquit(const char** parameters, int pcnt, userrec* user)
434 {
435         TreeServer* s = Utils->FindServerMask(parameters[0]);
436         if (s)
437         {
438                 if (s == Utils->TreeRoot)
439                 {
440                         user->WriteServ("NOTICE %s :*** SQUIT: Foolish mortal, you cannot make a server SQUIT itself! (%s matches local server name)",user->nick,parameters[0]);
441                         return 1;
442                 }
443                 TreeSocket* sock = s->GetSocket();
444                 if (sock)
445                 {
446                         ServerInstance->SNO->WriteToSnoMask('l',"SQUIT: Server \002%s\002 removed from network by %s",parameters[0],user->nick);
447                         sock->Squit(s,std::string("Server quit by ") + user->GetFullRealHost());
448                         ServerInstance->SE->DelFd(sock);
449                         sock->Close();
450                         delete sock;
451                 }
452                 else
453                 {
454                         /* route it */
455                         std::deque<std::string> params;
456                         params.push_back(parameters[0]);
457                         params.push_back(std::string(":Server quit by ") + user->GetFullRealHost());
458                         Utils->DoOneToOne(user->nick, "RSQUIT", params, parameters[0]);
459                 }
460         }
461         else
462         {
463                  user->WriteServ("NOTICE %s :*** SQUIT: The server \002%s\002 does not exist on the network.",user->nick,parameters[0]);
464         }
465         return 1;
466 }
467
468 int ModuleSpanningTree::HandleTime(const char** parameters, int pcnt, userrec* user)
469 {
470         if ((IS_LOCAL(user)) && (pcnt))
471         {
472                 TreeServer* found = Utils->FindServerMask(parameters[0]);
473                 if (found)
474                 {
475                         // we dont' override for local server
476                         if (found == Utils->TreeRoot)
477                                 return 0;
478                         
479                         std::deque<std::string> params;
480                         params.push_back(found->GetName());
481                         params.push_back(user->nick);
482                         Utils->DoOneToOne(ServerInstance->Config->ServerName,"TIME",params,found->GetName());
483                 }
484                 else
485                 {
486                         user->WriteServ("402 %s %s :No such server",user->nick,parameters[0]);
487                 }
488         }
489         return 1;
490 }
491
492 int ModuleSpanningTree::HandleRemoteWhois(const char** parameters, int pcnt, userrec* user)
493 {
494         if ((IS_LOCAL(user)) && (pcnt > 1))
495         {
496                 userrec* remote = ServerInstance->FindNick(parameters[1]);
497                 if ((remote) && (remote->GetFd() < 0))
498                 {
499                         std::deque<std::string> params;
500                         params.push_back(parameters[1]);
501                         Utils->DoOneToOne(user->nick,"IDLE",params,remote->server);
502                         return 1;
503                 }
504                 else if (!remote)
505                 {
506                         user->WriteServ("401 %s %s :No such nick/channel",user->nick, parameters[1]);
507                         user->WriteServ("318 %s %s :End of /WHOIS list.",user->nick, parameters[1]);
508                         return 1;
509                 }
510         }
511         return 0;
512 }
513
514 void ModuleSpanningTree::DoPingChecks(time_t curtime)
515 {
516         for (unsigned int j = 0; j < Utils->TreeRoot->ChildCount(); j++)
517         {
518                 TreeServer* serv = Utils->TreeRoot->GetChild(j);
519                 TreeSocket* sock = serv->GetSocket();
520                 if (sock)
521                 {
522                         if (curtime >= serv->NextPingTime())
523                         {
524                                 if (serv->AnsweredLastPing())
525                                 {
526                                         sock->WriteLine(std::string(":")+ServerInstance->Config->ServerName+" PING "+serv->GetName());
527                                         serv->SetNextPingTime(curtime + 60);
528                                 }
529                                 else
530                                 {
531                                         // they didnt answer, boot them
532                                         ServerInstance->SNO->WriteToSnoMask('l',"Server \002%s\002 pinged out",serv->GetName().c_str());
533                                         sock->Squit(serv,"Ping timeout");
534                                         ServerInstance->SE->DelFd(sock);
535                                         sock->Close();
536                                         delete sock;
537                                         return;
538                                 }
539                         }
540                 }
541         }
542 }
543
544 void ModuleSpanningTree::ConnectServer(Link* x)
545 {
546         insp_inaddr binip;
547         /* Do we already have an IP? If so, no need to resolve it. */
548         if (insp_aton(x->IPAddr.c_str(), &binip) > 0)
549         {
550                 /* Gave a hook, but it wasnt one we know */
551                 if ((!x->Hook.empty()) && (Utils->hooks.find(x->Hook.c_str()) == Utils->hooks.end()))
552                         return;
553                 TreeSocket* newsocket = new TreeSocket(Utils, ServerInstance, x->IPAddr,x->Port,false,x->Timeout ? x->Timeout : 10,x->Name.c_str(), x->Hook.empty() ? NULL : Utils->hooks[x->Hook.c_str()]);
554                 if (newsocket->GetFd() > -1)
555                 {
556                         /* Handled automatically on success */
557                 }
558                 else
559                 {
560                         ServerInstance->SNO->WriteToSnoMask('l',"CONNECT: Error connecting \002%s\002: %s.",x->Name.c_str(),strerror(errno));
561                         delete newsocket;
562                         Utils->DoFailOver(x);
563                 }
564         }
565         else
566         {
567                 try
568                 {
569                         bool cached;
570                         ServernameResolver* snr = new ServernameResolver((Module*)this, Utils, ServerInstance,x->IPAddr, *x, cached);
571                         ServerInstance->AddResolver(snr, cached);
572                 }
573                 catch (ModuleException& e)
574                 {
575                         ServerInstance->SNO->WriteToSnoMask('l',"CONNECT: Error connecting \002%s\002: %s.",x->Name.c_str(), e.GetReason());
576                         Utils->DoFailOver(x);
577                 }
578         }
579 }
580
581 void ModuleSpanningTree::AutoConnectServers(time_t curtime)
582 {
583         for (std::vector<Link>::iterator x = Utils->LinkBlocks.begin(); x < Utils->LinkBlocks.end(); x++)
584         {
585                 if ((x->AutoConnect) && (curtime >= x->NextConnectTime))
586                 {
587                         x->NextConnectTime = curtime + x->AutoConnect;
588                         TreeServer* CheckDupe = Utils->FindServer(x->Name.c_str());
589                         if (x->FailOver.length())
590                         {
591                                 TreeServer* CheckFailOver = Utils->FindServer(x->FailOver.c_str());
592                                 if (CheckFailOver)
593                                 {
594                                         /* The failover for this server is currently a member of the network.
595                                          * The failover probably succeeded, where the main link did not.
596                                          * Don't try the main link until the failover is gone again.
597                                          */
598                                         continue;
599                                 }
600                         }
601                         if (!CheckDupe)
602                         {
603                                 // an autoconnected server is not connected. Check if its time to connect it
604                                 ServerInstance->SNO->WriteToSnoMask('l',"AUTOCONNECT: Auto-connecting server \002%s\002 (%lu seconds until next attempt)",x->Name.c_str(),x->AutoConnect);
605                                 this->ConnectServer(&(*x));
606                         }
607                 }
608         }
609 }
610
611 int ModuleSpanningTree::HandleVersion(const char** parameters, int pcnt, userrec* user)
612 {
613         // we've already checked if pcnt > 0, so this is safe
614         TreeServer* found = Utils->FindServerMask(parameters[0]);
615         if (found)
616         {
617                 std::string Version = found->GetVersion();
618                 user->WriteServ("351 %s :%s",user->nick,Version.c_str());
619                 if (found == Utils->TreeRoot)
620                 {
621                         ServerInstance->Config->Send005(user);
622                 }
623         }
624         else
625         {
626                 user->WriteServ("402 %s %s :No such server",user->nick,parameters[0]);
627         }
628         return 1;
629 }
630         
631 int ModuleSpanningTree::HandleConnect(const char** parameters, int pcnt, userrec* user)
632 {
633         for (std::vector<Link>::iterator x = Utils->LinkBlocks.begin(); x < Utils->LinkBlocks.end(); x++)
634         {
635                 if (ServerInstance->MatchText(x->Name.c_str(),parameters[0]))
636                 {
637                         TreeServer* CheckDupe = Utils->FindServer(x->Name.c_str());
638                         if (!CheckDupe)
639                         {
640                                 user->WriteServ("NOTICE %s :*** CONNECT: Connecting to server: \002%s\002 (%s:%d)",user->nick,x->Name.c_str(),(x->HiddenFromStats ? "<hidden>" : x->IPAddr.c_str()),x->Port);
641                                 ConnectServer(&(*x));
642                                 return 1;
643                         }
644                         else
645                         {
646                                 user->WriteServ("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());
647                                 return 1;
648                         }
649                 }
650         }
651         user->WriteServ("NOTICE %s :*** CONNECT: No server matching \002%s\002 could be found in the config file.",user->nick,parameters[0]);
652         return 1;
653 }
654
655 void ModuleSpanningTree::BroadcastTimeSync()
656 {
657         std::deque<std::string> params;
658         params.push_back(ConvToStr(ServerInstance->Time(true)));
659         Utils->DoOneToMany(Utils->TreeRoot->GetName(), "TIMESET", params);
660 }
661
662 int ModuleSpanningTree::OnStats(char statschar, userrec* user, string_list &results)
663 {
664         if ((statschar == 'c') || (statschar == 'n'))
665         {
666                 for (unsigned int i = 0; i < Utils->LinkBlocks.size(); i++)
667                 {
668                         results.push_back(std::string(ServerInstance->Config->ServerName)+" 213 "+user->nick+" "+statschar+" *@"+(Utils->LinkBlocks[i].HiddenFromStats ? "<hidden>" : Utils->LinkBlocks[i].IPAddr)+" * "+Utils->LinkBlocks[i].Name.c_str()+" "+ConvToStr(Utils->LinkBlocks[i].Port)+" "+(Utils->LinkBlocks[i].Hook.empty() ? "plaintext" : Utils->LinkBlocks[i].Hook)+" "+(Utils->LinkBlocks[i].AutoConnect ? 'a' : '-')+'s');
669                         if (statschar == 'c')
670                                 results.push_back(std::string(ServerInstance->Config->ServerName)+" 244 "+user->nick+" H * * "+Utils->LinkBlocks[i].Name.c_str());
671                 }
672                 results.push_back(std::string(ServerInstance->Config->ServerName)+" 219 "+user->nick+" "+statschar+" :End of /STATS report");
673                 ServerInstance->SNO->WriteToSnoMask('t',"Notice: %s '%c' requested by %s (%s@%s)",(!strcmp(user->server,ServerInstance->Config->ServerName) ? "Stats" : "Remote stats"),statschar,user->nick,user->ident,user->host);
674                 return 1;
675         }
676         return 0;
677 }
678
679 int ModuleSpanningTree::OnPreCommand(const std::string &command, const char** parameters, int pcnt, userrec *user, bool validated, const std::string &original_line)
680 {
681         /* If the command doesnt appear to be valid, we dont want to mess with it. */
682         if (!validated)
683                 return 0;
684         if (command == "CONNECT")
685         {
686                 return this->HandleConnect(parameters,pcnt,user);
687         }
688         else if (command == "STATS")
689         {
690                 return this->HandleStats(parameters,pcnt,user);
691         }
692         else if (command == "MOTD")
693         {
694                 return this->HandleMotd(parameters,pcnt,user);
695         }
696         else if (command == "ADMIN")
697         {
698                 return this->HandleAdmin(parameters,pcnt,user);
699         }
700         else if (command == "SQUIT")
701         {
702                 return this->HandleSquit(parameters,pcnt,user);
703         }
704         else if (command == "MAP")
705         {
706                 this->HandleMap(parameters,pcnt,user);
707                 return 1;
708         }
709         else if ((command == "TIME") && (pcnt > 0))
710         {
711                 return this->HandleTime(parameters,pcnt,user);
712         }
713         else if (command == "LUSERS")
714         {
715                 this->HandleLusers(parameters,pcnt,user);
716                 return 1;
717         }
718         else if (command == "LINKS")
719         {
720                 this->HandleLinks(parameters,pcnt,user);
721                 return 1;
722         }
723         else if (command == "WHOIS")
724         {
725                 if (pcnt > 1)
726                 {
727                         // remote whois
728                         return this->HandleRemoteWhois(parameters,pcnt,user);
729                 }
730         }
731         else if ((command == "VERSION") && (pcnt > 0))
732         {
733                 this->HandleVersion(parameters,pcnt,user);
734                 return 1;
735         }
736         return 0;
737 }
738
739 void ModuleSpanningTree::OnPostCommand(const std::string &command, const char** parameters, int pcnt, userrec *user, CmdResult result, const std::string &original_line)
740 {
741         if ((result == CMD_SUCCESS) && (ServerInstance->IsValidModuleCommand(command, pcnt, user)))
742         {
743                 // this bit of code cleverly routes all module commands
744                 // to all remote severs *automatically* so that modules
745                 // can just handle commands locally, without having
746                 // to have any special provision in place for remote
747                 // commands and linking protocols.
748                 std::deque<std::string> params;
749                 params.clear();
750                 for (int j = 0; j < pcnt; j++)
751                 {
752                         if (strchr(parameters[j],' '))
753                         {
754                                 params.push_back(":" + std::string(parameters[j]));
755                         }
756                         else
757                         {
758                                 params.push_back(std::string(parameters[j]));
759                         }
760                 }
761                 Utils->DoOneToMany(user->nick,command,params);
762         }
763 }
764
765 void ModuleSpanningTree::OnGetServerDescription(const std::string &servername,std::string &description)
766 {
767         TreeServer* s = Utils->FindServer(servername);
768         if (s)
769         {
770                 description = s->GetDesc();
771         }
772 }
773
774 void ModuleSpanningTree::OnUserInvite(userrec* source,userrec* dest,chanrec* channel)
775 {
776         if (IS_LOCAL(source))
777         {
778                 std::deque<std::string> params;
779                 params.push_back(dest->nick);
780                 params.push_back(channel->name);
781                 Utils->DoOneToMany(source->nick,"INVITE",params);
782         }
783 }
784
785 void ModuleSpanningTree::OnPostLocalTopicChange(userrec* user, chanrec* chan, const std::string &topic)
786 {
787         std::deque<std::string> params;
788         params.push_back(chan->name);
789         params.push_back(":"+topic);
790         Utils->DoOneToMany(user->nick,"TOPIC",params);
791 }
792
793 void ModuleSpanningTree::OnWallops(userrec* user, const std::string &text)
794 {
795         if (IS_LOCAL(user))
796         {
797                 std::deque<std::string> params;
798                 params.push_back(":"+text);
799                 Utils->DoOneToMany(user->nick,"WALLOPS",params);
800         }
801 }
802
803 void ModuleSpanningTree::OnUserNotice(userrec* user, void* dest, int target_type, const std::string &text, char status, const CUList &exempt_list)
804 {
805         if (target_type == TYPE_USER)
806         {
807                 userrec* d = (userrec*)dest;
808                 if ((d->GetFd() < 0) && (IS_LOCAL(user)))
809                 {
810                         std::deque<std::string> params;
811                         params.clear();
812                         params.push_back(d->nick);
813                         params.push_back(":"+text);
814                         Utils->DoOneToOne(user->nick,"NOTICE",params,d->server);
815                 }
816         }
817         else if (target_type == TYPE_CHANNEL)
818         {
819                 if (IS_LOCAL(user))
820                 {
821                         chanrec *c = (chanrec*)dest;
822                         if (c)
823                         {
824                                 std::string cname = c->name;
825                                 if (status)
826                                         cname = status + cname;
827                                 TreeServerList list;
828                                 Utils->GetListOfServersForChannel(c,list,status,exempt_list);
829                                 for (TreeServerList::iterator i = list.begin(); i != list.end(); i++)
830                                 {
831                                         TreeSocket* Sock = i->second->GetSocket();
832                                         if (Sock)
833                                                 Sock->WriteLine(":"+std::string(user->nick)+" NOTICE "+cname+" :"+text);
834                                 }
835                         }
836                 }
837         }
838         else if (target_type == TYPE_SERVER)
839         {
840                 if (IS_LOCAL(user))
841                 {
842                         char* target = (char*)dest;
843                         std::deque<std::string> par;
844                         par.push_back(target);
845                         par.push_back(":"+text);
846                         Utils->DoOneToMany(user->nick,"NOTICE",par);
847                 }
848         }
849 }
850
851 void ModuleSpanningTree::OnUserMessage(userrec* user, void* dest, int target_type, const std::string &text, char status, const CUList &exempt_list)
852 {
853         if (target_type == TYPE_USER)
854         {
855                 // route private messages which are targetted at clients only to the server
856                 // which needs to receive them
857                 userrec* d = (userrec*)dest;
858                 if ((d->GetFd() < 0) && (IS_LOCAL(user)))
859                 {
860                         std::deque<std::string> params;
861                         params.clear();
862                         params.push_back(d->nick);
863                         params.push_back(":"+text);
864                         Utils->DoOneToOne(user->nick,"PRIVMSG",params,d->server);
865                 }
866         }
867         else if (target_type == TYPE_CHANNEL)
868         {
869                 if (IS_LOCAL(user))
870                 {
871                         chanrec *c = (chanrec*)dest;
872                         if (c)
873                         {
874                                 std::string cname = c->name;
875                                 if (status)
876                                         cname = status + cname;
877                                 TreeServerList list;
878                                 Utils->GetListOfServersForChannel(c,list,status,exempt_list);
879                                 for (TreeServerList::iterator i = list.begin(); i != list.end(); i++)
880                                 {
881                                         TreeSocket* Sock = i->second->GetSocket();
882                                         if (Sock)
883                                                 Sock->WriteLine(":"+std::string(user->nick)+" PRIVMSG "+cname+" :"+text);
884                                 }
885                         }
886                 }
887         }
888         else if (target_type == TYPE_SERVER)
889         {
890                 if (IS_LOCAL(user))
891                 {
892                         char* target = (char*)dest;
893                         std::deque<std::string> par;
894                         par.push_back(target);
895                         par.push_back(":"+text);
896                         Utils->DoOneToMany(user->nick,"PRIVMSG",par);
897                 }
898         }
899 }
900
901 void ModuleSpanningTree::OnBackgroundTimer(time_t curtime)
902 {
903         AutoConnectServers(curtime);
904         DoPingChecks(curtime);
905 }
906
907 void ModuleSpanningTree::OnUserJoin(userrec* user, chanrec* channel)
908 {
909         // Only do this for local users
910         if (IS_LOCAL(user))
911         {
912                 if (channel->GetUserCounter() == 1)
913                 {
914                         std::deque<std::string> params;
915                         // set up their permissions and the channel TS with FJOIN.
916                         // All users are FJOINed now, because a module may specify
917                         // new joining permissions for the user.
918                         params.push_back(channel->name);
919                         params.push_back(ConvToStr(channel->age));
920                         params.push_back(std::string(channel->GetAllPrefixChars(user))+","+std::string(user->nick));
921                         Utils->DoOneToMany(ServerInstance->Config->ServerName,"FJOIN",params);
922                         /* First user in, sync the modes for the channel */
923                         params.pop_back();
924                         /* This is safe, all inspircd servers default to +nt */
925                         params.push_back("+nt");
926                         Utils->DoOneToMany(ServerInstance->Config->ServerName,"FMODE",params);
927                 }
928                 else
929                 {
930                         std::deque<std::string> params;
931                         params.push_back(channel->name);
932                         params.push_back(ConvToStr(channel->age));
933                         Utils->DoOneToMany(user->nick,"JOIN",params);
934                 }
935         }
936 }
937
938 void ModuleSpanningTree::OnChangeHost(userrec* user, const std::string &newhost)
939 {
940         // only occurs for local clients
941         if (user->registered != REG_ALL)
942                 return;
943         std::deque<std::string> params;
944         params.push_back(newhost);
945         Utils->DoOneToMany(user->nick,"FHOST",params);
946 }
947
948 void ModuleSpanningTree::OnChangeName(userrec* user, const std::string &gecos)
949 {
950         // only occurs for local clients
951         if (user->registered != REG_ALL)
952                 return;
953         std::deque<std::string> params;
954         params.push_back(gecos);
955         Utils->DoOneToMany(user->nick,"FNAME",params);
956 }
957
958 void ModuleSpanningTree::OnUserPart(userrec* user, chanrec* channel, const std::string &partmessage)
959 {
960         if (IS_LOCAL(user))
961         {
962                 std::deque<std::string> params;
963                 params.push_back(channel->name);
964                 if (partmessage != "")
965                         params.push_back(":"+partmessage);
966                 Utils->DoOneToMany(user->nick,"PART",params);
967         }
968 }
969
970 void ModuleSpanningTree::OnUserConnect(userrec* user)
971 {
972         char agestr[MAXBUF];
973         if (IS_LOCAL(user))
974         {
975                 std::deque<std::string> params;
976                 snprintf(agestr,MAXBUF,"%lu",(unsigned long)user->age);
977                 params.push_back(agestr);
978                 params.push_back(user->nick);
979                 params.push_back(user->host);
980                 params.push_back(user->dhost);
981                 params.push_back(user->ident);
982                 params.push_back("+"+std::string(user->FormatModes()));
983                 params.push_back(user->GetIPString());
984                 params.push_back(":"+std::string(user->fullname));
985                 Utils->DoOneToMany(ServerInstance->Config->ServerName,"NICK",params);
986                 // User is Local, change needs to be reflected!
987                 TreeServer* SourceServer = Utils->FindServer(user->server);
988                 if (SourceServer)
989                 {
990                         SourceServer->AddUserCount();
991                 }
992         }
993 }
994
995 void ModuleSpanningTree::OnUserQuit(userrec* user, const std::string &reason)
996 {
997         if ((IS_LOCAL(user)) && (user->registered == REG_ALL))
998         {
999                 std::deque<std::string> params;
1000                 params.push_back(":"+reason);
1001                 Utils->DoOneToMany(user->nick,"QUIT",params);
1002         }
1003         // Regardless, We need to modify the user Counts..
1004         TreeServer* SourceServer = Utils->FindServer(user->server);
1005         if (SourceServer)
1006         {
1007                 SourceServer->DelUserCount();
1008         }
1009 }
1010
1011 void ModuleSpanningTree::OnUserPostNick(userrec* user, const std::string &oldnick)
1012 {
1013         if (IS_LOCAL(user))
1014         {
1015                 std::deque<std::string> params;
1016                 params.push_back(user->nick);
1017                 Utils->DoOneToMany(oldnick,"NICK",params);
1018         }
1019 }
1020
1021 void ModuleSpanningTree::OnUserKick(userrec* source, userrec* user, chanrec* chan, const std::string &reason)
1022 {
1023         if ((source) && (IS_LOCAL(source)))
1024         {
1025                 std::deque<std::string> params;
1026                 params.push_back(chan->name);
1027                 params.push_back(user->nick);
1028                 params.push_back(":"+reason);
1029                 Utils->DoOneToMany(source->nick,"KICK",params);
1030         }
1031         else if (!source)
1032         {
1033                 std::deque<std::string> params;
1034                 params.push_back(chan->name);
1035                 params.push_back(user->nick);
1036                 params.push_back(":"+reason);
1037                 Utils->DoOneToMany(ServerInstance->Config->ServerName,"KICK",params);
1038         }
1039 }
1040
1041 void ModuleSpanningTree::OnRemoteKill(userrec* source, userrec* dest, const std::string &reason)
1042 {
1043         std::deque<std::string> params;
1044         params.push_back(dest->nick);
1045         params.push_back(":"+reason);
1046         Utils->DoOneToMany(source->nick,"KILL",params);
1047 }
1048
1049 void ModuleSpanningTree::OnRehash(userrec* user, const std::string &parameter)
1050 {
1051         if (parameter != "")
1052         {
1053                 std::deque<std::string> params;
1054                 params.push_back(parameter);
1055                 Utils->DoOneToMany(user ? user->nick : ServerInstance->Config->ServerName, "REHASH", params);
1056                 // check for self
1057                 if (ServerInstance->MatchText(ServerInstance->Config->ServerName,parameter))
1058                 {
1059                         ServerInstance->WriteOpers("*** Remote rehash initiated locally by \002%s\002", user ? user->nick : ServerInstance->Config->ServerName);
1060                         ServerInstance->RehashServer();
1061                 }
1062         }
1063         Utils->ReadConfiguration(false);
1064         InitializeDisabledCommands(ServerInstance->Config->DisabledCommands, ServerInstance);
1065 }
1066
1067 // note: the protocol does not allow direct umode +o except
1068 // via NICK with 8 params. sending OPERTYPE infers +o modechange
1069 // locally.
1070 void ModuleSpanningTree::OnOper(userrec* user, const std::string &opertype)
1071 {
1072         if (IS_LOCAL(user))
1073         {
1074                 std::deque<std::string> params;
1075                 params.push_back(opertype);
1076                 Utils->DoOneToMany(user->nick,"OPERTYPE",params);
1077         }
1078 }
1079
1080 void ModuleSpanningTree::OnLine(userrec* source, const std::string &host, bool adding, char linetype, long duration, const std::string &reason)
1081 {
1082         if (!source)
1083         {
1084                 /* Server-set lines */
1085                 char data[MAXBUF];
1086                 snprintf(data,MAXBUF,"%c %s %s %lu %lu :%s", linetype, host.c_str(), ServerInstance->Config->ServerName, (unsigned long)ServerInstance->Time(false),
1087                                 (unsigned long)duration, reason.c_str());
1088                 std::deque<std::string> params;
1089                 params.push_back(data);
1090                 Utils->DoOneToMany(ServerInstance->Config->ServerName, "ADDLINE", params);
1091         }
1092         else
1093         {
1094                 if (IS_LOCAL(source))
1095                 {
1096                         char type[8];
1097                         snprintf(type,8,"%cLINE",linetype);
1098                         std::string stype = type;
1099                         if (adding)
1100                         {
1101                                 char sduration[MAXBUF];
1102                                 snprintf(sduration,MAXBUF,"%ld",duration);
1103                                 std::deque<std::string> params;
1104                                 params.push_back(host);
1105                                 params.push_back(sduration);
1106                                 params.push_back(":"+reason);
1107                                 Utils->DoOneToMany(source->nick,stype,params);
1108                         }
1109                         else
1110                         {
1111                                 std::deque<std::string> params;
1112                                 params.push_back(host);
1113                                 Utils->DoOneToMany(source->nick,stype,params);
1114                         }
1115                 }
1116         }
1117 }
1118
1119 void ModuleSpanningTree::OnAddGLine(long duration, userrec* source, const std::string &reason, const std::string &hostmask)
1120 {
1121         OnLine(source,hostmask,true,'G',duration,reason);
1122 }
1123         
1124 void ModuleSpanningTree::OnAddZLine(long duration, userrec* source, const std::string &reason, const std::string &ipmask)
1125 {
1126         OnLine(source,ipmask,true,'Z',duration,reason);
1127 }
1128
1129 void ModuleSpanningTree::OnAddQLine(long duration, userrec* source, const std::string &reason, const std::string &nickmask)
1130 {
1131         OnLine(source,nickmask,true,'Q',duration,reason);
1132 }
1133
1134 void ModuleSpanningTree::OnAddELine(long duration, userrec* source, const std::string &reason, const std::string &hostmask)
1135 {
1136         OnLine(source,hostmask,true,'E',duration,reason);
1137 }
1138
1139 void ModuleSpanningTree::OnDelGLine(userrec* source, const std::string &hostmask)
1140 {
1141         OnLine(source,hostmask,false,'G',0,"");
1142 }
1143
1144 void ModuleSpanningTree::OnDelZLine(userrec* source, const std::string &ipmask)
1145 {
1146         OnLine(source,ipmask,false,'Z',0,"");
1147 }
1148
1149 void ModuleSpanningTree::OnDelQLine(userrec* source, const std::string &nickmask)
1150 {
1151         OnLine(source,nickmask,false,'Q',0,"");
1152 }
1153
1154 void ModuleSpanningTree::OnDelELine(userrec* source, const std::string &hostmask)
1155 {
1156         OnLine(source,hostmask,false,'E',0,"");
1157 }
1158
1159 void ModuleSpanningTree::OnMode(userrec* user, void* dest, int target_type, const std::string &text)
1160 {
1161         if ((IS_LOCAL(user)) && (user->registered == REG_ALL))
1162         {
1163                 if (target_type == TYPE_USER)
1164                 {
1165                         userrec* u = (userrec*)dest;
1166                         std::deque<std::string> params;
1167                         params.push_back(u->nick);
1168                         params.push_back(text);
1169                         Utils->DoOneToMany(user->nick,"MODE",params);
1170                 }
1171                 else
1172                 {
1173                         chanrec* c = (chanrec*)dest;
1174                         std::deque<std::string> params;
1175                         params.push_back(c->name);
1176                         params.push_back(text);
1177                         Utils->DoOneToMany(user->nick,"MODE",params);
1178                 }
1179         }
1180 }
1181
1182 void ModuleSpanningTree::OnSetAway(userrec* user)
1183 {
1184         if (IS_LOCAL(user))
1185         {
1186                 std::deque<std::string> params;
1187                 params.push_back(":"+std::string(user->awaymsg));
1188                 Utils->DoOneToMany(user->nick,"AWAY",params);
1189         }
1190 }
1191
1192 void ModuleSpanningTree::OnCancelAway(userrec* user)
1193 {
1194         if (IS_LOCAL(user))
1195         {
1196                 std::deque<std::string> params;
1197                 params.clear();
1198                 Utils->DoOneToMany(user->nick,"AWAY",params);
1199         }
1200 }
1201
1202 void ModuleSpanningTree::ProtoSendMode(void* opaque, int target_type, void* target, const std::string &modeline)
1203 {
1204         TreeSocket* s = (TreeSocket*)opaque;
1205         if (target)
1206         {
1207                 if (target_type == TYPE_USER)
1208                 {
1209                         userrec* u = (userrec*)target;
1210                         s->WriteLine(std::string(":")+ServerInstance->Config->ServerName+" FMODE "+u->nick+" "+ConvToStr(u->age)+" "+modeline);
1211                 }
1212                 else
1213                 {
1214                         chanrec* c = (chanrec*)target;
1215                         s->WriteLine(std::string(":")+ServerInstance->Config->ServerName+" FMODE "+c->name+" "+ConvToStr(c->age)+" "+modeline);
1216                 }
1217         }
1218 }
1219
1220 void ModuleSpanningTree::ProtoSendMetaData(void* opaque, int target_type, void* target, const std::string &extname, const std::string &extdata)
1221 {
1222         TreeSocket* s = (TreeSocket*)opaque;
1223         if (target)
1224         {
1225                 if (target_type == TYPE_USER)
1226                 {
1227                         userrec* u = (userrec*)target;
1228                         s->WriteLine(std::string(":")+ServerInstance->Config->ServerName+" METADATA "+u->nick+" "+extname+" :"+extdata);
1229                 }
1230                 else if (target_type == TYPE_CHANNEL)
1231                 {
1232                         chanrec* c = (chanrec*)target;
1233                         s->WriteLine(std::string(":")+ServerInstance->Config->ServerName+" METADATA "+c->name+" "+extname+" :"+extdata);
1234                 }
1235         }
1236         if (target_type == TYPE_OTHER)
1237         {
1238                 s->WriteLine(std::string(":")+ServerInstance->Config->ServerName+" METADATA * "+extname+" :"+extdata);
1239         }
1240 }
1241
1242 void ModuleSpanningTree::OnEvent(Event* event)
1243 {
1244         std::deque<std::string>* params = (std::deque<std::string>*)event->GetData();
1245         if (event->GetEventID() == "send_metadata")
1246         {
1247                 if (params->size() < 3)
1248                         return;
1249                 (*params)[2] = ":" + (*params)[2];
1250                 Utils->DoOneToMany(ServerInstance->Config->ServerName,"METADATA",*params);
1251         }
1252         else if (event->GetEventID() == "send_topic")
1253         {
1254                 if (params->size() < 2)
1255                         return;
1256                 (*params)[1] = ":" + (*params)[1];
1257                 params->insert(params->begin() + 1,ServerInstance->Config->ServerName);
1258                 params->insert(params->begin() + 1,ConvToStr(ServerInstance->Time(true)));
1259                 Utils->DoOneToMany(ServerInstance->Config->ServerName,"FTOPIC",*params);
1260         }
1261         else if (event->GetEventID() == "send_mode")
1262         {
1263                 if (params->size() < 2)
1264                         return;
1265                 // Insert the TS value of the object, either userrec or chanrec
1266                 time_t ourTS = 0;
1267                 userrec* a = ServerInstance->FindNick((*params)[0]);
1268                 if (a)
1269                 {
1270                         ourTS = a->age;
1271                 }
1272                 else
1273                 {
1274                         chanrec* a = ServerInstance->FindChan((*params)[0]);
1275                         if (a)
1276                         {
1277                                 ourTS = a->age;
1278                         }
1279                 }
1280                 params->insert(params->begin() + 1,ConvToStr(ourTS));
1281                 Utils->DoOneToMany(ServerInstance->Config->ServerName,"FMODE",*params);
1282         }
1283         else if (event->GetEventID() == "send_mode_explicit")
1284         {
1285                 if (params->size() < 2)
1286                         return;
1287                 Utils->DoOneToMany(ServerInstance->Config->ServerName,"MODE",*params);
1288         }
1289         else if (event->GetEventID() == "send_opers")
1290         {
1291                 if (params->size() < 1)
1292                         return;
1293                 (*params)[0] = ":" + (*params)[0];
1294                 Utils->DoOneToMany(ServerInstance->Config->ServerName,"OPERNOTICE",*params);
1295         }
1296         else if (event->GetEventID() == "send_modeset")
1297         {
1298                 if (params->size() < 2)
1299                         return;
1300                 (*params)[1] = ":" + (*params)[1];
1301                 Utils->DoOneToMany(ServerInstance->Config->ServerName,"MODENOTICE",*params);
1302         }
1303         else if (event->GetEventID() == "send_snoset")
1304         {
1305                 if (params->size() < 2)
1306                         return;
1307                 (*params)[1] = ":" + (*params)[1];
1308                 Utils->DoOneToMany(ServerInstance->Config->ServerName,"SNONOTICE",*params);
1309         }
1310         else if (event->GetEventID() == "send_push")
1311         {
1312                 if (params->size() < 2)
1313                         return;
1314                         
1315                 userrec *a = ServerInstance->FindNick((*params)[0]);
1316                         
1317                 if (!a)
1318                         return;
1319                         
1320                 (*params)[1] = ":" + (*params)[1];
1321                 Utils->DoOneToOne(ServerInstance->Config->ServerName, "PUSH", *params, a->server);
1322         }
1323 }
1324
1325 ModuleSpanningTree::~ModuleSpanningTree()
1326 {
1327         /* This will also free the listeners */
1328         delete Utils;
1329         if (SyncTimer)
1330                 ServerInstance->Timers->DelTimer(SyncTimer);
1331
1332         ServerInstance->DoneWithInterface("InspSocketHook");
1333 }
1334
1335 Version ModuleSpanningTree::GetVersion()
1336 {
1337         return Version(1,1,0,2,VF_VENDOR,API_VERSION);
1338 }
1339
1340 void ModuleSpanningTree::Implements(char* List)
1341 {
1342         List[I_OnPreCommand] = List[I_OnGetServerDescription] = List[I_OnUserInvite] = List[I_OnPostLocalTopicChange] = 1;
1343         List[I_OnWallops] = List[I_OnUserNotice] = List[I_OnUserMessage] = List[I_OnBackgroundTimer] = 1;
1344         List[I_OnUserJoin] = List[I_OnChangeHost] = List[I_OnChangeName] = List[I_OnUserPart] = List[I_OnUserConnect] = 1;
1345         List[I_OnUserQuit] = List[I_OnUserPostNick] = List[I_OnUserKick] = List[I_OnRemoteKill] = List[I_OnRehash] = 1;
1346         List[I_OnOper] = List[I_OnAddGLine] = List[I_OnAddZLine] = List[I_OnAddQLine] = List[I_OnAddELine] = 1;
1347         List[I_OnDelGLine] = List[I_OnDelZLine] = List[I_OnDelQLine] = List[I_OnDelELine] = List[I_ProtoSendMode] = List[I_OnMode] = 1;
1348         List[I_OnStats] = List[I_ProtoSendMetaData] = List[I_OnEvent] = List[I_OnSetAway] = List[I_OnCancelAway] = List[I_OnPostCommand] = 1;
1349 }
1350
1351 /* It is IMPORTANT that m_spanningtree is the last module in the chain
1352  * so that any activity it sees is FINAL, e.g. we arent going to send out
1353  * a NICK message before m_cloaking has finished putting the +x on the user,
1354  * etc etc.
1355  * Therefore, we return PRIORITY_LAST to make sure we end up at the END of
1356  * the module call queue.
1357  */
1358 Priority ModuleSpanningTree::Prioritize()
1359 {
1360         return PRIORITY_LAST;
1361 }
1362
1363 TimeSyncTimer::TimeSyncTimer(InspIRCd *Inst, ModuleSpanningTree *Mod) : InspTimer(43200, Inst->Time(), true), Instance(Inst), Module(Mod)
1364 {
1365 }
1366
1367 void TimeSyncTimer::Tick(time_t TIME)
1368 {
1369         Module->BroadcastTimeSync();
1370 }
1371
1372
1373 class ModuleSpanningTreeFactory : public ModuleFactory
1374 {
1375  public:
1376         ModuleSpanningTreeFactory()
1377         {
1378         }
1379         
1380         ~ModuleSpanningTreeFactory()
1381         {
1382         }
1383         
1384         virtual Module * CreateModule(InspIRCd* Me)
1385         {
1386                 return new ModuleSpanningTree(Me);
1387         }
1388         
1389 };
1390
1391
1392 extern "C" void * init_module( void )
1393 {
1394         return new ModuleSpanningTreeFactory;
1395 }