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