]> git.netwichtig.de Git - user/henk/code/inspircd.git/blob - src/modules/m_spanningtree/main.cpp
9a7cddec3ce6e395ef48625641ac23dd77d67ea6
[user/henk/code/inspircd.git] / src / modules / m_spanningtree / main.cpp
1 /*
2  * InspIRCd -- Internet Relay Chat Daemon
3  *
4  *   Copyright (C) 2009-2010 Daniel De Graaf <danieldg@inspircd.org>
5  *   Copyright (C) 2007-2009 Craig Edwards <craigedwards@brainbox.cc>
6  *   Copyright (C) 2007-2008 Robin Burchell <robin+git@viroteck.net>
7  *   Copyright (C) 2008 Thomas Stagner <aquanight@inspircd.org>
8  *   Copyright (C) 2007 Dennis Friis <peavey@inspircd.org>
9  *
10  * This file is part of InspIRCd.  InspIRCd is free software: you can
11  * redistribute it and/or modify it under the terms of the GNU General Public
12  * License as published by the Free Software Foundation, version 2.
13  *
14  * This program is distributed in the hope that it will be useful, but WITHOUT
15  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
16  * FOR A PARTICULAR PURPOSE.  See the GNU General Public License for more
17  * details.
18  *
19  * You should have received a copy of the GNU General Public License
20  * along with this program.  If not, see <http://www.gnu.org/licenses/>.
21  */
22
23
24 #include "inspircd.h"
25 #include "socket.h"
26 #include "xline.h"
27 #include "iohook.h"
28
29 #include "resolvers.h"
30 #include "main.h"
31 #include "utils.h"
32 #include "treeserver.h"
33 #include "link.h"
34 #include "treesocket.h"
35 #include "commands.h"
36 #include "protocolinterface.h"
37
38 ModuleSpanningTree::ModuleSpanningTree()
39         : rconnect(this), rsquit(this), map(this)
40         , commands(NULL), DNS(this, "DNS")
41 {
42 }
43
44 SpanningTreeCommands::SpanningTreeCommands(ModuleSpanningTree* module)
45         : svsjoin(module), svspart(module), svsnick(module), metadata(module),
46         uid(module), opertype(module), fjoin(module), ijoin(module), resync(module),
47         fmode(module), ftopic(module), fhost(module), fident(module), fname(module),
48         away(module), addline(module), delline(module), encap(module), idle(module),
49         nick(module), ping(module), pong(module), push(module), save(module),
50         server(module), squit(module), snonotice(module), version(module),
51         burst(module), endburst(module)
52 {
53 }
54
55 namespace
56 {
57         void SetLocalUsersServer(Server* newserver)
58         {
59                 ServerInstance->FakeClient->server = newserver;
60                 const LocalUserList& list = ServerInstance->Users->local_users;
61                 for (LocalUserList::const_iterator i = list.begin(); i != list.end(); ++i)
62                         (*i)->server = newserver;
63         }
64 }
65
66 void ModuleSpanningTree::init()
67 {
68         ServerInstance->SNO->EnableSnomask('l', "LINK");
69
70         Utils = new SpanningTreeUtilities(this);
71         Utils->TreeRoot = new TreeServer;
72         commands = new SpanningTreeCommands(this);
73
74         delete ServerInstance->PI;
75         ServerInstance->PI = new SpanningTreeProtocolInterface;
76
77         delete ServerInstance->FakeClient->server;
78         SetLocalUsersServer(Utils->TreeRoot);
79
80         loopCall = false;
81         SplitInProgress = false;
82
83         // update our local user count
84         Utils->TreeRoot->UserCount = ServerInstance->Users->local_users.size();
85 }
86
87 void ModuleSpanningTree::ShowLinks(TreeServer* Current, User* user, int hops)
88 {
89         std::string Parent = Utils->TreeRoot->GetName();
90         if (Current->GetParent())
91         {
92                 Parent = Current->GetParent()->GetName();
93         }
94
95         const TreeServer::ChildServers& children = Current->GetChildren();
96         for (TreeServer::ChildServers::const_iterator i = children.begin(); i != children.end(); ++i)
97         {
98                 TreeServer* server = *i;
99                 if ((server->Hidden) || ((Utils->HideULines) && (server->IsULine())))
100                 {
101                         if (user->IsOper())
102                         {
103                                  ShowLinks(server, user, hops+1);
104                         }
105                 }
106                 else
107                 {
108                         ShowLinks(server, user, hops+1);
109                 }
110         }
111         /* Don't display the line if its a uline, hide ulines is on, and the user isnt an oper */
112         if ((Utils->HideULines) && (Current->IsULine()) && (!user->IsOper()))
113                 return;
114         /* Or if the server is hidden and they're not an oper */
115         else if ((Current->Hidden) && (!user->IsOper()))
116                 return;
117
118         user->WriteNumeric(RPL_LINKS, "%s %s :%d %s",   Current->GetName().c_str(),
119                         (Utils->FlatLinks && (!user->IsOper())) ? ServerInstance->Config->ServerName.c_str() : Parent.c_str(),
120                         (Utils->FlatLinks && (!user->IsOper())) ? 0 : hops,
121                         Current->GetDesc().c_str());
122 }
123
124 void ModuleSpanningTree::HandleLinks(const std::vector<std::string>& parameters, User* user)
125 {
126         ShowLinks(Utils->TreeRoot,user,0);
127         user->WriteNumeric(RPL_ENDOFLINKS, "* :End of /LINKS list.");
128 }
129
130 std::string ModuleSpanningTree::TimeToStr(time_t secs)
131 {
132         time_t mins_up = secs / 60;
133         time_t hours_up = mins_up / 60;
134         time_t days_up = hours_up / 24;
135         secs = secs % 60;
136         mins_up = mins_up % 60;
137         hours_up = hours_up % 24;
138         return ((days_up ? (ConvToStr(days_up) + "d") : "")
139                         + (hours_up ? (ConvToStr(hours_up) + "h") : "")
140                         + (mins_up ? (ConvToStr(mins_up) + "m") : "")
141                         + ConvToStr(secs) + "s");
142 }
143
144 void ModuleSpanningTree::DoPingChecks(time_t curtime)
145 {
146         /*
147          * Cancel remote burst mode on any servers which still have it enabled due to latency/lack of data.
148          * This prevents lost REMOTECONNECT notices
149          */
150         long ts = ServerInstance->Time() * 1000 + (ServerInstance->Time_ns() / 1000000);
151
152 restart:
153         for (server_hash::iterator i = Utils->serverlist.begin(); i != Utils->serverlist.end(); i++)
154         {
155                 TreeServer *s = i->second;
156
157                 // Skip myself
158                 if (s->IsRoot())
159                         continue;
160
161                 if (s->GetSocket()->GetLinkState() == DYING)
162                 {
163                         s->GetSocket()->Close();
164                         goto restart;
165                 }
166
167                 // Do not ping servers that are not fully connected yet!
168                 // Servers which are connected to us have IsLocal() == true and if they're fully connected
169                 // then Socket->LinkState == CONNECTED. Servers that are linked to another server are always fully connected.
170                 if (s->IsLocal() && s->GetSocket()->GetLinkState() != CONNECTED)
171                         continue;
172
173                 // Now do PING checks on all servers
174                 // Only ping if this server needs one
175                 if (curtime >= s->NextPingTime())
176                 {
177                         // And if they answered the last
178                         if (s->AnsweredLastPing())
179                         {
180                                 // They did, send a ping to them
181                                 s->SetNextPingTime(curtime + Utils->PingFreq);
182                                 s->GetSocket()->WriteLine(":" + ServerInstance->Config->GetSID() + " PING " + s->GetID());
183                                 s->LastPingMsec = ts;
184                         }
185                         else
186                         {
187                                 // They didn't answer the last ping, if they are locally connected, get rid of them.
188                                 if (s->IsLocal())
189                                 {
190                                         TreeSocket* sock = s->GetSocket();
191                                         sock->SendError("Ping timeout");
192                                         sock->Close();
193                                         goto restart;
194                                 }
195                         }
196                 }
197
198                 // If warn on ping enabled and not warned and the difference is sufficient and they didn't answer the last ping...
199                 if ((Utils->PingWarnTime) && (!s->Warned) && (curtime >= s->NextPingTime() - (Utils->PingFreq - Utils->PingWarnTime)) && (!s->AnsweredLastPing()))
200                 {
201                         /* The server hasnt responded, send a warning to opers */
202                         ServerInstance->SNO->WriteToSnoMask('l',"Server \002%s\002 has not responded to PING for %d seconds, high latency.", s->GetName().c_str(), Utils->PingWarnTime);
203                         s->Warned = true;
204                 }
205         }
206 }
207
208 void ModuleSpanningTree::ConnectServer(Autoconnect* a, bool on_timer)
209 {
210         if (!a)
211                 return;
212         for(unsigned int j=0; j < a->servers.size(); j++)
213         {
214                 if (Utils->FindServer(a->servers[j]))
215                 {
216                         // found something in this block. Should the server fail,
217                         // we want to start at the start of the list, not in the
218                         // middle where we left off
219                         a->position = -1;
220                         return;
221                 }
222         }
223         if (on_timer && a->position >= 0)
224                 return;
225         if (!on_timer && a->position < 0)
226                 return;
227
228         a->position++;
229         while (a->position < (int)a->servers.size())
230         {
231                 Link* x = Utils->FindLink(a->servers[a->position]);
232                 if (x)
233                 {
234                         ServerInstance->SNO->WriteToSnoMask('l', "AUTOCONNECT: Auto-connecting server \002%s\002", x->Name.c_str());
235                         ConnectServer(x, a);
236                         return;
237                 }
238                 a->position++;
239         }
240         // Autoconnect chain has been fully iterated; start at the beginning on the
241         // next AutoConnectServers run
242         a->position = -1;
243 }
244
245 void ModuleSpanningTree::ConnectServer(Link* x, Autoconnect* y)
246 {
247         bool ipvalid = true;
248
249         if (InspIRCd::Match(ServerInstance->Config->ServerName, assign(x->Name)))
250         {
251                 ServerInstance->SNO->WriteToSnoMask('l', "CONNECT: Not connecting to myself.");
252                 return;
253         }
254
255         DNS::QueryType start_type = DNS::QUERY_AAAA;
256         if (strchr(x->IPAddr.c_str(),':'))
257         {
258                 in6_addr n;
259                 if (inet_pton(AF_INET6, x->IPAddr.c_str(), &n) < 1)
260                         ipvalid = false;
261         }
262         else
263         {
264                 in_addr n;
265                 if (inet_aton(x->IPAddr.c_str(),&n) < 1)
266                         ipvalid = false;
267         }
268
269         /* Do we already have an IP? If so, no need to resolve it. */
270         if (ipvalid)
271         {
272                 /* Gave a hook, but it wasnt one we know */
273                 TreeSocket* newsocket = new TreeSocket(x, y, x->IPAddr);
274                 if (newsocket->GetFd() > -1)
275                 {
276                         /* Handled automatically on success */
277                 }
278                 else
279                 {
280                         ServerInstance->SNO->WriteToSnoMask('l', "CONNECT: Error connecting \002%s\002: %s.",
281                                 x->Name.c_str(), newsocket->getError().c_str());
282                         ServerInstance->GlobalCulls.AddItem(newsocket);
283                 }
284         }
285         else if (!DNS)
286         {
287                 ServerInstance->SNO->WriteToSnoMask('l', "CONNECT: Error connecting \002%s\002: Hostname given and m_dns.so is not loaded, unable to resolve.", x->Name.c_str());
288         }
289         else
290         {
291                 ServernameResolver* snr = new ServernameResolver(*DNS, x->IPAddr, x, start_type, y);
292                 try
293                 {
294                         DNS->Process(snr);
295                 }
296                 catch (DNS::Exception& e)
297                 {
298                         delete snr;
299                         ServerInstance->SNO->WriteToSnoMask('l', "CONNECT: Error connecting \002%s\002: %s.",x->Name.c_str(), e.GetReason().c_str());
300                         ConnectServer(y, false);
301                 }
302         }
303 }
304
305 void ModuleSpanningTree::AutoConnectServers(time_t curtime)
306 {
307         for (std::vector<reference<Autoconnect> >::iterator i = Utils->AutoconnectBlocks.begin(); i < Utils->AutoconnectBlocks.end(); ++i)
308         {
309                 Autoconnect* x = *i;
310                 if (curtime >= x->NextConnectTime)
311                 {
312                         x->NextConnectTime = curtime + x->Period;
313                         ConnectServer(x, true);
314                 }
315         }
316 }
317
318 void ModuleSpanningTree::DoConnectTimeout(time_t curtime)
319 {
320         std::map<TreeSocket*, std::pair<std::string, int> >::iterator i = Utils->timeoutlist.begin();
321         while (i != Utils->timeoutlist.end())
322         {
323                 TreeSocket* s = i->first;
324                 std::pair<std::string, int> p = i->second;
325                 std::map<TreeSocket*, std::pair<std::string, int> >::iterator me = i;
326                 i++;
327                 if (s->GetLinkState() == DYING)
328                 {
329                         Utils->timeoutlist.erase(me);
330                         s->Close();
331                 }
332                 else if (curtime > s->age + p.second)
333                 {
334                         ServerInstance->SNO->WriteToSnoMask('l',"CONNECT: Error connecting \002%s\002 (timeout of %d seconds)",p.first.c_str(),p.second);
335                         Utils->timeoutlist.erase(me);
336                         s->Close();
337                 }
338         }
339 }
340
341 ModResult ModuleSpanningTree::HandleVersion(const std::vector<std::string>& parameters, User* user)
342 {
343         // we've already checked if pcnt > 0, so this is safe
344         TreeServer* found = Utils->FindServerMask(parameters[0]);
345         if (found)
346         {
347                 if (found == Utils->TreeRoot)
348                 {
349                         // Pass to default VERSION handler.
350                         return MOD_RES_PASSTHRU;
351                 }
352                 std::string Version = found->GetVersion();
353                 user->WriteNumeric(RPL_VERSION, ":%s", Version.c_str());
354         }
355         else
356         {
357                 user->WriteNumeric(ERR_NOSUCHSERVER, "%s :No such server", parameters[0].c_str());
358         }
359         return MOD_RES_DENY;
360 }
361
362 /* This method will attempt to get a message to a remote user.
363  */
364 void ModuleSpanningTree::RemoteMessage(User* user, const char* format, ...)
365 {
366         std::string text;
367         VAFORMAT(text, format, format);
368
369         if (IS_LOCAL(user))
370                 user->WriteNotice(text);
371         else
372                 ServerInstance->PI->SendUserNotice(user, text);
373 }
374
375 ModResult ModuleSpanningTree::HandleConnect(const std::vector<std::string>& parameters, User* user)
376 {
377         for (std::vector<reference<Link> >::iterator i = Utils->LinkBlocks.begin(); i < Utils->LinkBlocks.end(); i++)
378         {
379                 Link* x = *i;
380                 if (InspIRCd::Match(x->Name.c_str(),parameters[0]))
381                 {
382                         if (InspIRCd::Match(ServerInstance->Config->ServerName, assign(x->Name)))
383                         {
384                                 RemoteMessage(user, "*** CONNECT: Server \002%s\002 is ME, not connecting.",x->Name.c_str());
385                                 return MOD_RES_DENY;
386                         }
387
388                         TreeServer* CheckDupe = Utils->FindServer(x->Name.c_str());
389                         if (!CheckDupe)
390                         {
391                                 RemoteMessage(user, "*** CONNECT: Connecting to server: \002%s\002 (%s:%d)",x->Name.c_str(),(x->HiddenFromStats ? "<hidden>" : x->IPAddr.c_str()),x->Port);
392                                 ConnectServer(x);
393                                 return MOD_RES_DENY;
394                         }
395                         else
396                         {
397                                 RemoteMessage(user, "*** CONNECT: Server \002%s\002 already exists on the network and is connected via \002%s\002", x->Name.c_str(), CheckDupe->GetParent()->GetName().c_str());
398                                 return MOD_RES_DENY;
399                         }
400                 }
401         }
402         RemoteMessage(user, "*** CONNECT: No server matching \002%s\002 could be found in the config file.",parameters[0].c_str());
403         return MOD_RES_DENY;
404 }
405
406 void ModuleSpanningTree::On005Numeric(std::map<std::string, std::string>& tokens)
407 {
408         tokens["MAP"];
409 }
410
411 void ModuleSpanningTree::OnUserInvite(User* source,User* dest,Channel* channel, time_t expiry)
412 {
413         if (IS_LOCAL(source))
414         {
415                 CmdBuilder params(source, "INVITE");
416                 params.push_back(dest->uuid);
417                 params.push_back(channel->name);
418                 params.push_back(ConvToStr(expiry));
419                 params.Broadcast();
420         }
421 }
422
423 void ModuleSpanningTree::OnPostTopicChange(User* user, Channel* chan, const std::string &topic)
424 {
425         // Drop remote events on the floor.
426         if (!IS_LOCAL(user))
427                 return;
428
429         CommandFTopic::Builder(user, chan).Broadcast();
430 }
431
432 void ModuleSpanningTree::OnUserMessage(User* user, void* dest, int target_type, const std::string& text, char status, const CUList& exempt_list, MessageType msgtype)
433 {
434         if (!IS_LOCAL(user))
435                 return;
436
437         const char* message_type = (msgtype == MSG_PRIVMSG ? "PRIVMSG" : "NOTICE");
438         if (target_type == TYPE_USER)
439         {
440                 User* d = (User*) dest;
441                 if (!IS_LOCAL(d))
442                 {
443                         CmdBuilder params(user, message_type);
444                         params.push_back(d->uuid);
445                         params.push_last(text);
446                         params.Unicast(d);
447                 }
448         }
449         else if (target_type == TYPE_CHANNEL)
450         {
451                 Utils->SendChannelMessage(user->uuid, (Channel*)dest, text, status, exempt_list, message_type);
452         }
453         else if (target_type == TYPE_SERVER)
454         {
455                 char* target = (char*) dest;
456                 CmdBuilder par(user, message_type);
457                 par.push_back(target);
458                 par.push_last(text);
459                 par.Broadcast();
460         }
461 }
462
463 void ModuleSpanningTree::OnBackgroundTimer(time_t curtime)
464 {
465         AutoConnectServers(curtime);
466         DoPingChecks(curtime);
467         DoConnectTimeout(curtime);
468 }
469
470 void ModuleSpanningTree::OnUserConnect(LocalUser* user)
471 {
472         if (user->quitting)
473                 return;
474
475         CommandUID::Builder(user).Broadcast();
476
477         if (user->IsOper())
478                 CommandOpertype::Builder(user).Broadcast();
479
480         for(Extensible::ExtensibleStore::const_iterator i = user->GetExtList().begin(); i != user->GetExtList().end(); i++)
481         {
482                 ExtensionItem* item = i->first;
483                 std::string value = item->serialize(FORMAT_NETWORK, user, i->second);
484                 if (!value.empty())
485                         ServerInstance->PI->SendMetaData(user, item->name, value);
486         }
487
488         Utils->TreeRoot->UserCount++;
489 }
490
491 void ModuleSpanningTree::OnUserJoin(Membership* memb, bool sync, bool created_by_local, CUList& excepts)
492 {
493         // Only do this for local users
494         if (!IS_LOCAL(memb->user))
495                 return;
496
497         if (created_by_local)
498         {
499                 CmdBuilder params("FJOIN");
500                 params.push_back(memb->chan->name);
501                 params.push_back(ConvToStr(memb->chan->age));
502                 params.push_raw(" +").push_raw(memb->chan->ChanModes(true));
503                 params.push(memb->modes).push_raw(',').push_raw(memb->user->uuid);
504                 params.Broadcast();
505         }
506         else
507         {
508                 CmdBuilder params(memb->user, "IJOIN");
509                 params.push_back(memb->chan->name);
510                 if (!memb->modes.empty())
511                 {
512                         params.push_back(ConvToStr(memb->chan->age));
513                         params.push_back(memb->modes);
514                 }
515                 params.Broadcast();
516         }
517 }
518
519 void ModuleSpanningTree::OnChangeHost(User* user, const std::string &newhost)
520 {
521         if (user->registered != REG_ALL || !IS_LOCAL(user))
522                 return;
523
524         CmdBuilder(user, "FHOST").push(newhost).Broadcast();
525 }
526
527 void ModuleSpanningTree::OnChangeName(User* user, const std::string &gecos)
528 {
529         if (user->registered != REG_ALL || !IS_LOCAL(user))
530                 return;
531
532         CmdBuilder(user, "FNAME").push(gecos).Broadcast();
533 }
534
535 void ModuleSpanningTree::OnChangeIdent(User* user, const std::string &ident)
536 {
537         if ((user->registered != REG_ALL) || (!IS_LOCAL(user)))
538                 return;
539
540         CmdBuilder(user, "FIDENT").push(ident).Broadcast();
541 }
542
543 void ModuleSpanningTree::OnUserPart(Membership* memb, std::string &partmessage, CUList& excepts)
544 {
545         if (IS_LOCAL(memb->user))
546         {
547                 CmdBuilder params(memb->user, "PART");
548                 params.push_back(memb->chan->name);
549                 if (!partmessage.empty())
550                         params.push_last(partmessage);
551                 params.Broadcast();
552         }
553 }
554
555 void ModuleSpanningTree::OnUserQuit(User* user, const std::string &reason, const std::string &oper_message)
556 {
557         if (IS_LOCAL(user))
558         {
559                 if (oper_message != reason)
560                         ServerInstance->PI->SendMetaData(user, "operquit", oper_message);
561
562                 CmdBuilder(user, "QUIT").push_last(reason).Broadcast();
563         }
564         else
565         {
566                 // Hide the message if one of the following is true:
567                 // - User is being quit due to a netsplit and quietbursts is on
568                 // - Server is a silent uline
569                 bool hide = (((this->SplitInProgress) && (Utils->quiet_bursts)) || (user->server->IsSilentULine()));
570                 if (!hide)
571                 {
572                         ServerInstance->SNO->WriteToSnoMask('Q', "Client exiting on server %s: %s (%s) [%s]",
573                                 user->server->GetName().c_str(), user->GetFullRealHost().c_str(), user->GetIPString().c_str(), oper_message.c_str());
574                 }
575         }
576
577         // Regardless, We need to modify the user Counts..
578         TreeServer::Get(user)->UserCount--;
579 }
580
581 void ModuleSpanningTree::OnUserPostNick(User* user, const std::string &oldnick)
582 {
583         if (IS_LOCAL(user))
584         {
585                 CmdBuilder params(user, "NICK");
586                 params.push_back(user->nick);
587
588                 /** IMPORTANT: We don't update the TS if the oldnick is just a case change of the newnick!
589                  */
590                 if (irc::string(user->nick.c_str()) != assign(oldnick))
591                         user->age = ServerInstance->Time();
592
593                 params.push_back(ConvToStr(user->age));
594                 params.Broadcast();
595         }
596         else if (!loopCall && user->nick == user->uuid)
597         {
598                 CmdBuilder params("SAVE");
599                 params.push_back(user->uuid);
600                 params.push_back(ConvToStr(user->age));
601                 params.Broadcast();
602         }
603 }
604
605 void ModuleSpanningTree::OnUserKick(User* source, Membership* memb, const std::string &reason, CUList& excepts)
606 {
607         if ((!IS_LOCAL(source)) && (source != ServerInstance->FakeClient))
608                 return;
609
610         CmdBuilder params(source, "KICK");
611         params.push_back(memb->chan->name);
612         params.push_back(memb->user->uuid);
613         params.push_last(reason);
614         params.Broadcast();
615 }
616
617 void ModuleSpanningTree::OnPreRehash(User* user, const std::string &parameter)
618 {
619         if (loopCall)
620                 return; // Don't generate a REHASH here if we're in the middle of processing a message that generated this one
621
622         ServerInstance->Logs->Log(MODNAME, LOG_DEBUG, "OnPreRehash called with param %s", parameter.c_str());
623
624         // Send out to other servers
625         if (!parameter.empty() && parameter[0] != '-')
626         {
627                 CmdBuilder params((user ? user->uuid : ServerInstance->Config->GetSID()), "REHASH");
628                 params.push_back(parameter);
629                 params.Forward(user ? TreeServer::Get(user)->GetRoute() : NULL);
630         }
631 }
632
633 void ModuleSpanningTree::ReadConfig(ConfigStatus& status)
634 {
635         // Re-read config stuff
636         try
637         {
638                 Utils->ReadConfiguration();
639         }
640         catch (ModuleException& e)
641         {
642                 // Refresh the IP cache anyway, so servers read before the error will be allowed to connect
643                 Utils->RefreshIPCache();
644                 // Always warn local opers with snomask +l, also warn globally (snomask +L) if the rehash was issued by a remote user
645                 std::string msg = "Error in configuration: ";
646                 msg.append(e.GetReason());
647                 ServerInstance->SNO->WriteToSnoMask('l', msg);
648                 if (status.srcuser && !IS_LOCAL(status.srcuser))
649                         ServerInstance->PI->SendSNONotice('L', msg);
650         }
651 }
652
653 void ModuleSpanningTree::OnLoadModule(Module* mod)
654 {
655         std::string data;
656         data.push_back('+');
657         data.append(mod->ModuleSourceFile);
658         Version v = mod->GetVersion();
659         if (!v.link_data.empty())
660         {
661                 data.push_back('=');
662                 data.append(v.link_data);
663         }
664         ServerInstance->PI->SendMetaData("modules", data);
665 }
666
667 void ModuleSpanningTree::OnUnloadModule(Module* mod)
668 {
669         if (!Utils)
670                 return;
671         ServerInstance->PI->SendMetaData("modules", "-" + mod->ModuleSourceFile);
672
673         // Close all connections which use an IO hook provided by this module
674         const TreeServer::ChildServers& list = Utils->TreeRoot->GetChildren();
675         for (TreeServer::ChildServers::const_iterator i = list.begin(); i != list.end(); ++i)
676         {
677                 TreeSocket* sock = (*i)->GetSocket();
678                 if (sock && sock->GetIOHook() && sock->GetIOHook()->creator == mod)
679                 {
680                         sock->SendError("SSL module unloaded");
681                         sock->Close();
682                 }
683         }
684
685         for (SpanningTreeUtilities::TimeoutList::const_iterator i = Utils->timeoutlist.begin(); i != Utils->timeoutlist.end(); ++i)
686         {
687                 TreeSocket* sock = i->first;
688                 if (sock->GetIOHook() && sock->GetIOHook()->creator == mod)
689                         sock->Close();
690         }
691 }
692
693 // note: the protocol does not allow direct umode +o except
694 // via NICK with 8 params. sending OPERTYPE infers +o modechange
695 // locally.
696 void ModuleSpanningTree::OnOper(User* user, const std::string &opertype)
697 {
698         if (user->registered != REG_ALL || !IS_LOCAL(user))
699                 return;
700         CommandOpertype::Builder(user).Broadcast();
701 }
702
703 void ModuleSpanningTree::OnAddLine(User* user, XLine *x)
704 {
705         if (!x->IsBurstable() || loopCall || (user && !IS_LOCAL(user)))
706                 return;
707
708         if (!user)
709                 user = ServerInstance->FakeClient;
710
711         CommandAddLine::Builder(x, user).Broadcast();
712 }
713
714 void ModuleSpanningTree::OnDelLine(User* user, XLine *x)
715 {
716         if (!x->IsBurstable() || loopCall || (user && !IS_LOCAL(user)))
717                 return;
718
719         if (!user)
720                 user = ServerInstance->FakeClient;
721
722         CmdBuilder params(user, "DELLINE");
723         params.push_back(x->type);
724         params.push_back(x->Displayable());
725         params.Broadcast();
726 }
727
728 ModResult ModuleSpanningTree::OnSetAway(User* user, const std::string &awaymsg)
729 {
730         if (IS_LOCAL(user))
731                 CommandAway::Builder(user, awaymsg).Broadcast();
732
733         return MOD_RES_PASSTHRU;
734 }
735
736 CullResult ModuleSpanningTree::cull()
737 {
738         if (Utils)
739                 Utils->cull();
740         return this->Module::cull();
741 }
742
743 ModuleSpanningTree::~ModuleSpanningTree()
744 {
745         delete ServerInstance->PI;
746         ServerInstance->PI = new ProtocolInterface;
747
748         Server* newsrv = new Server(ServerInstance->Config->ServerName, ServerInstance->Config->ServerDesc);
749         SetLocalUsersServer(newsrv);
750
751         /* This will also free the listeners */
752         delete Utils;
753
754         delete commands;
755 }
756
757 Version ModuleSpanningTree::GetVersion()
758 {
759         return Version("Allows servers to be linked", VF_VENDOR);
760 }
761
762 /* It is IMPORTANT that m_spanningtree is the last module in the chain
763  * so that any activity it sees is FINAL, e.g. we arent going to send out
764  * a NICK message before m_cloaking has finished putting the +x on the user,
765  * etc etc.
766  * Therefore, we return PRIORITY_LAST to make sure we end up at the END of
767  * the module call queue.
768  */
769 void ModuleSpanningTree::Prioritize()
770 {
771         ServerInstance->Modules->SetPriority(this, PRIORITY_LAST);
772 }
773
774 MODULE_INIT(ModuleSpanningTree)