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