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