]> git.netwichtig.de Git - user/henk/code/inspircd.git/blob - src/modules/m_spanningtree/main.cpp
Send module load/unload notifications so that services can learn of new features...
[user/henk/code/inspircd.git] / src / modules / m_spanningtree / main.cpp
1 /*       +------------------------------------+
2  *       | Inspire Internet Relay Chat Daemon |
3  *       +------------------------------------+
4  *
5  *  InspIRCd: (C) 2002-2010 InspIRCd Development Team
6  * See: http://wiki.inspircd.org/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 "inspircd.h"
17 #include "socket.h"
18 #include "xline.h"
19
20 #include "cachetimer.h"
21 #include "resolvers.h"
22 #include "main.h"
23 #include "utils.h"
24 #include "treeserver.h"
25 #include "link.h"
26 #include "treesocket.h"
27 #include "commands.h"
28 #include "protocolinterface.h"
29
30 ModuleSpanningTree::ModuleSpanningTree()
31 {
32         Utils = new SpanningTreeUtilities(this);
33         commands = new SpanningTreeCommands(this);
34 }
35
36 SpanningTreeCommands::SpanningTreeCommands(ModuleSpanningTree* module)
37         : rconnect(module, module->Utils), rsquit(module, module->Utils),
38         svsjoin(module), svspart(module), svsnick(module), metadata(module),
39         uid(module), opertype(module), fjoin(module), fmode(module), ftopic(module),
40         fhost(module), fident(module), fname(module)
41 {
42 }
43
44 void ModuleSpanningTree::init()
45 {
46         RefreshTimer = new CacheRefreshTimer(Utils);
47         ServerInstance->Modules->AddService(commands->rconnect);
48         ServerInstance->Modules->AddService(commands->rsquit);
49         ServerInstance->Modules->AddService(commands->svsjoin);
50         ServerInstance->Modules->AddService(commands->svspart);
51         ServerInstance->Modules->AddService(commands->svsnick);
52         ServerInstance->Modules->AddService(commands->metadata);
53         ServerInstance->Modules->AddService(commands->uid);
54         ServerInstance->Modules->AddService(commands->opertype);
55         ServerInstance->Modules->AddService(commands->fjoin);
56         ServerInstance->Modules->AddService(commands->fmode);
57         ServerInstance->Modules->AddService(commands->ftopic);
58         ServerInstance->Modules->AddService(commands->fhost);
59         ServerInstance->Modules->AddService(commands->fident);
60         ServerInstance->Modules->AddService(commands->fname);
61         ServerInstance->Timers->AddTimer(RefreshTimer);
62
63         Implementation eventlist[] =
64         {
65                 I_OnPreCommand, I_OnGetServerDescription, I_OnUserInvite, I_OnPostTopicChange,
66                 I_OnWallops, I_OnUserNotice, I_OnUserMessage, I_OnBackgroundTimer, I_OnUserJoin,
67                 I_OnChangeHost, I_OnChangeName, I_OnChangeIdent, I_OnUserPart, I_OnUnloadModule,
68                 I_OnUserQuit, I_OnUserPostNick, I_OnUserKick, I_OnRemoteKill, I_OnRehash, I_OnPreRehash,
69                 I_OnOper, I_OnAddLine, I_OnDelLine, I_OnMode, I_OnLoadModule, I_OnStats,
70                 I_OnSetAway, I_OnPostCommand, I_OnUserConnect, I_OnAcceptConnection
71         };
72         ServerInstance->Modules->Attach(eventlist, this, sizeof(eventlist)/sizeof(Implementation));
73
74         delete ServerInstance->PI;
75         ServerInstance->PI = new SpanningTreeProtocolInterface(this, Utils);
76         loopCall = false;
77
78         // update our local user count
79         Utils->TreeRoot->SetUserCount(ServerInstance->Users->local_users.size());
80 }
81
82 void ModuleSpanningTree::ShowLinks(TreeServer* Current, User* user, int hops)
83 {
84         std::string Parent = Utils->TreeRoot->GetName();
85         if (Current->GetParent())
86         {
87                 Parent = Current->GetParent()->GetName();
88         }
89         for (unsigned int q = 0; q < Current->ChildCount(); q++)
90         {
91                 if ((Current->GetChild(q)->Hidden) || ((Utils->HideULines) && (ServerInstance->ULine(Current->GetChild(q)->GetName().c_str()))))
92                 {
93                         if (IS_OPER(user))
94                         {
95                                  ShowLinks(Current->GetChild(q),user,hops+1);
96                         }
97                 }
98                 else
99                 {
100                         ShowLinks(Current->GetChild(q),user,hops+1);
101                 }
102         }
103         /* Don't display the line if its a uline, hide ulines is on, and the user isnt an oper */
104         if ((Utils->HideULines) && (ServerInstance->ULine(Current->GetName().c_str())) && (!IS_OPER(user)))
105                 return;
106         /* Or if the server is hidden and they're not an oper */
107         else if ((Current->Hidden) && (!IS_OPER(user)))
108                 return;
109
110         user->WriteNumeric(364, "%s %s %s :%d %s",      user->nick.c_str(),Current->GetName().c_str(),
111                         (Utils->FlatLinks && (!IS_OPER(user))) ? ServerInstance->Config->ServerName.c_str() : Parent.c_str(),
112                         (Utils->FlatLinks && (!IS_OPER(user))) ? 0 : hops,
113                         Current->GetDesc().c_str());
114 }
115
116 int ModuleSpanningTree::CountServs()
117 {
118         return Utils->serverlist.size();
119 }
120
121 void ModuleSpanningTree::HandleLinks(const std::vector<std::string>& parameters, User* user)
122 {
123         ShowLinks(Utils->TreeRoot,user,0);
124         user->WriteNumeric(365, "%s * :End of /LINKS list.",user->nick.c_str());
125         return;
126 }
127
128 std::string ModuleSpanningTree::TimeToStr(time_t secs)
129 {
130         time_t mins_up = secs / 60;
131         time_t hours_up = mins_up / 60;
132         time_t days_up = hours_up / 24;
133         secs = secs % 60;
134         mins_up = mins_up % 60;
135         hours_up = hours_up % 24;
136         return ((days_up ? (ConvToStr(days_up) + "d") : std::string(""))
137                         + (hours_up ? (ConvToStr(hours_up) + "h") : std::string(""))
138                         + (mins_up ? (ConvToStr(mins_up) + "m") : std::string(""))
139                         + ConvToStr(secs) + "s");
140 }
141
142 void ModuleSpanningTree::DoPingChecks(time_t curtime)
143 {
144         /*
145          * Cancel remote burst mode on any servers which still have it enabled due to latency/lack of data.
146          * This prevents lost REMOTECONNECT notices
147          */
148         long ts = ServerInstance->Time() * 1000 + (ServerInstance->Time_ns() / 1000000);
149
150         for (server_hash::iterator i = Utils->serverlist.begin(); i != Utils->serverlist.end(); i++)
151         {
152                 TreeServer *s = i->second;
153
154                 // Fix for bug #792, do not ping servers that are not connected yet!
155                 // Remote servers have Socket == NULL and local connected servers have
156                 // Socket->LinkState == CONNECTED
157                 if (s->GetSocket() && s->GetSocket()->GetLinkState() != CONNECTED)
158                         continue;
159
160                 // Now do PING checks on all servers
161                 TreeServer *mts = Utils->BestRouteTo(s->GetID());
162
163                 if (mts)
164                 {
165                         // Only ping if this server needs one
166                         if (curtime >= s->NextPingTime())
167                         {
168                                 // And if they answered the last
169                                 if (s->AnsweredLastPing())
170                                 {
171                                         // They did, send a ping to them
172                                         s->SetNextPingTime(curtime + Utils->PingFreq);
173                                         TreeSocket *tsock = mts->GetSocket();
174
175                                         // ... if we can find a proper route to them
176                                         if (tsock)
177                                         {
178                                                 tsock->WriteLine(std::string(":") + ServerInstance->Config->GetSID() + " PING " +
179                                                                 ServerInstance->Config->GetSID() + " " + s->GetID());
180                                                 s->LastPingMsec = ts;
181                                         }
182                                 }
183                                 else
184                                 {
185                                         // They didn't answer the last ping, if they are locally connected, get rid of them.
186                                         TreeSocket *sock = s->GetSocket();
187                                         if (sock)
188                                         {
189                                                 sock->SendError("Ping timeout");
190                                                 sock->Squit(s,"Ping timeout");
191                                                 sock->Close();
192                                                 return;
193                                         }
194                                 }
195                         }
196
197                         // If warn on ping enabled and not warned and the difference is sufficient and they didn't answer the last ping...
198                         if ((Utils->PingWarnTime) && (!s->Warned) && (curtime >= s->NextPingTime() - (Utils->PingFreq - Utils->PingWarnTime)) && (!s->AnsweredLastPing()))
199                         {
200                                 /* The server hasnt responded, send a warning to opers */
201                                 ServerInstance->SNO->WriteToSnoMask('l',"Server \002%s\002 has not responded to PING for %d seconds, high latency.", s->GetName().c_str(), Utils->PingWarnTime);
202                                 s->Warned = true;
203                         }
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         QueryType start_type = DNS_QUERY_A;
256         start_type = DNS_QUERY_AAAA;
257         if (strchr(x->IPAddr.c_str(),':'))
258         {
259                 in6_addr n;
260                 if (inet_pton(AF_INET6, x->IPAddr.c_str(), &n) < 1)
261                         ipvalid = false;
262         }
263         else
264         {
265                 in_addr n;
266                 if (inet_aton(x->IPAddr.c_str(),&n) < 1)
267                         ipvalid = false;
268         }
269
270         /* Do we already have an IP? If so, no need to resolve it. */
271         if (ipvalid)
272         {
273                 /* Gave a hook, but it wasnt one we know */
274                 TreeSocket* newsocket = new TreeSocket(Utils, x->IPAddr, x->Port, x->Timeout ? x->Timeout : 10,
275                         x->Name.c_str(), x->Bind, y, x->Hook);
276                 if (newsocket->GetFd() > -1)
277                 {
278                         /* Handled automatically on success */
279                 }
280                 else
281                 {
282                         ServerInstance->SNO->WriteToSnoMask('l', "CONNECT: Error connecting \002%s\002: %s.",
283                                 x->Name.c_str(), newsocket->getError().c_str());
284                         ServerInstance->GlobalCulls.AddItem(newsocket);
285                 }
286         }
287         else
288         {
289                 try
290                 {
291                         bool cached;
292                         ServernameResolver* snr = new ServernameResolver(Utils, x->IPAddr, x, cached, start_type, y);
293                         ServerInstance->AddResolver(snr, cached);
294                 }
295                 catch (ModuleException& e)
296                 {
297                         ServerInstance->SNO->WriteToSnoMask('l', "CONNECT: Error connecting \002%s\002: %s.",x->Name.c_str(), e.GetReason());
298                         ConnectServer(y, false);
299                 }
300         }
301 }
302
303 void ModuleSpanningTree::AutoConnectServers(time_t curtime)
304 {
305         for (std::vector<reference<Autoconnect> >::iterator i = Utils->AutoconnectBlocks.begin(); i < Utils->AutoconnectBlocks.end(); ++i)
306         {
307                 Autoconnect* x = *i;
308                 if (curtime >= x->NextConnectTime)
309                 {
310                         x->NextConnectTime = curtime + x->Period;
311                         ConnectServer(x, true);
312                 }
313         }
314 }
315
316 void ModuleSpanningTree::DoConnectTimeout(time_t curtime)
317 {
318         std::map<TreeSocket*, std::pair<std::string, int> >::iterator i = Utils->timeoutlist.begin();
319         while (i != Utils->timeoutlist.end())
320         {
321                 TreeSocket* s = i->first;
322                 std::pair<std::string, int> p = i->second;
323                 std::map<TreeSocket*, std::pair<std::string, int> >::iterator me = i;
324                 i++;
325                 if (curtime > s->age + p.second)
326                 {
327                         ServerInstance->SNO->WriteToSnoMask('l',"CONNECT: Error connecting \002%s\002 (timeout of %d seconds)",p.first.c_str(),p.second);
328                         Utils->timeoutlist.erase(me);
329                         s->Close();
330                         ServerInstance->GlobalCulls.AddItem(s);
331                 }
332         }
333 }
334
335 ModResult ModuleSpanningTree::HandleVersion(const std::vector<std::string>& parameters, User* user)
336 {
337         // we've already checked if pcnt > 0, so this is safe
338         TreeServer* found = Utils->FindServerMask(parameters[0]);
339         if (found)
340         {
341                 std::string Version = found->GetVersion();
342                 user->WriteNumeric(351, "%s :%s",user->nick.c_str(),Version.c_str());
343                 if (found == Utils->TreeRoot)
344                 {
345                         ServerInstance->Config->Send005(user);
346                 }
347         }
348         else
349         {
350                 user->WriteNumeric(402, "%s %s :No such server",user->nick.c_str(),parameters[0].c_str());
351         }
352         return MOD_RES_DENY;
353 }
354
355 /* This method will attempt to get a message to a remote user.
356  */
357 void ModuleSpanningTree::RemoteMessage(User* user, const char* format, ...)
358 {
359         char text[MAXBUF];
360         va_list argsPtr;
361
362         va_start(argsPtr, format);
363         vsnprintf(text, MAXBUF, format, argsPtr);
364         va_end(argsPtr);
365
366         if (IS_LOCAL(user))
367                 user->WriteServ("NOTICE %s :%s", user->nick.c_str(), text);
368         else
369                 ServerInstance->PI->SendUserNotice(user, text);
370 }
371
372 ModResult ModuleSpanningTree::HandleConnect(const std::vector<std::string>& parameters, User* user)
373 {
374         for (std::vector<reference<Link> >::iterator i = Utils->LinkBlocks.begin(); i < Utils->LinkBlocks.end(); i++)
375         {
376                 Link* x = *i;
377                 if (InspIRCd::Match(x->Name.c_str(),parameters[0]))
378                 {
379                         if (InspIRCd::Match(ServerInstance->Config->ServerName, assign(x->Name)))
380                         {
381                                 RemoteMessage(user, "*** CONNECT: Server \002%s\002 is ME, not connecting.",x->Name.c_str());
382                                 return MOD_RES_DENY;
383                         }
384
385                         TreeServer* CheckDupe = Utils->FindServer(x->Name.c_str());
386                         if (!CheckDupe)
387                         {
388                                 RemoteMessage(user, "*** CONNECT: Connecting to server: \002%s\002 (%s:%d)",x->Name.c_str(),(x->HiddenFromStats ? "<hidden>" : x->IPAddr.c_str()),x->Port);
389                                 ConnectServer(x);
390                                 return MOD_RES_DENY;
391                         }
392                         else
393                         {
394                                 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());
395                                 return MOD_RES_DENY;
396                         }
397                 }
398         }
399         RemoteMessage(user, "*** CONNECT: No server matching \002%s\002 could be found in the config file.",parameters[0].c_str());
400         return MOD_RES_DENY;
401 }
402
403 void ModuleSpanningTree::OnGetServerDescription(const std::string &servername,std::string &description)
404 {
405         TreeServer* s = Utils->FindServer(servername);
406         if (s)
407         {
408                 description = s->GetDesc();
409         }
410 }
411
412 void ModuleSpanningTree::OnUserInvite(User* source,User* dest,Channel* channel, time_t expiry)
413 {
414         if (IS_LOCAL(source))
415         {
416                 parameterlist params;
417                 params.push_back(dest->uuid);
418                 params.push_back(channel->name);
419                 params.push_back(ConvToStr(expiry));
420                 Utils->DoOneToMany(source->uuid,"INVITE",params);
421         }
422 }
423
424 void ModuleSpanningTree::OnPostTopicChange(User* user, Channel* chan, const std::string &topic)
425 {
426         // Drop remote events on the floor.
427         if (!IS_LOCAL(user))
428                 return;
429
430         parameterlist params;
431         params.push_back(chan->name);
432         params.push_back(":"+topic);
433         Utils->DoOneToMany(user->uuid,"TOPIC",params);
434 }
435
436 void ModuleSpanningTree::OnWallops(User* user, const std::string &text)
437 {
438         if (IS_LOCAL(user))
439         {
440                 parameterlist params;
441                 params.push_back(":"+text);
442                 Utils->DoOneToMany(user->uuid,"WALLOPS",params);
443         }
444 }
445
446 void ModuleSpanningTree::OnUserNotice(User* user, void* dest, int target_type, const std::string &text, char status, const CUList &exempt_list)
447 {
448         /* Server origin */
449         if (user == NULL)
450                 return;
451
452         if (target_type == TYPE_USER)
453         {
454                 User* d = (User*)dest;
455                 if (!IS_LOCAL(d) && IS_LOCAL(user))
456                 {
457                         parameterlist params;
458                         params.push_back(d->uuid);
459                         params.push_back(":"+text);
460                         Utils->DoOneToOne(user->uuid,"NOTICE",params,d->server);
461                 }
462         }
463         else if (target_type == TYPE_CHANNEL)
464         {
465                 if (IS_LOCAL(user))
466                 {
467                         Channel *c = (Channel*)dest;
468                         if (c)
469                         {
470                                 std::string cname = c->name;
471                                 if (status)
472                                         cname = status + cname;
473                                 TreeServerList list;
474                                 Utils->GetListOfServersForChannel(c,list,status,exempt_list);
475                                 for (TreeServerList::iterator i = list.begin(); i != list.end(); i++)
476                                 {
477                                         TreeSocket* Sock = i->second->GetSocket();
478                                         if (Sock)
479                                                 Sock->WriteLine(":"+std::string(user->uuid)+" NOTICE "+cname+" :"+text);
480                                 }
481                         }
482                 }
483         }
484         else if (target_type == TYPE_SERVER)
485         {
486                 if (IS_LOCAL(user))
487                 {
488                         char* target = (char*)dest;
489                         parameterlist par;
490                         par.push_back(target);
491                         par.push_back(":"+text);
492                         Utils->DoOneToMany(user->uuid,"NOTICE",par);
493                 }
494         }
495 }
496
497 void ModuleSpanningTree::OnUserMessage(User* user, void* dest, int target_type, const std::string &text, char status, const CUList &exempt_list)
498 {
499         /* Server origin */
500         if (user == NULL)
501                 return;
502
503         if (target_type == TYPE_USER)
504         {
505                 // route private messages which are targetted at clients only to the server
506                 // which needs to receive them
507                 User* d = (User*)dest;
508                 if (!IS_LOCAL(d) && (IS_LOCAL(user)))
509                 {
510                         parameterlist params;
511                         params.push_back(d->uuid);
512                         params.push_back(":"+text);
513                         Utils->DoOneToOne(user->uuid,"PRIVMSG",params,d->server);
514                 }
515         }
516         else if (target_type == TYPE_CHANNEL)
517         {
518                 if (IS_LOCAL(user))
519                 {
520                         Channel *c = (Channel*)dest;
521                         if (c)
522                         {
523                                 std::string cname = c->name;
524                                 if (status)
525                                         cname = status + cname;
526                                 TreeServerList list;
527                                 Utils->GetListOfServersForChannel(c,list,status,exempt_list);
528                                 for (TreeServerList::iterator i = list.begin(); i != list.end(); i++)
529                                 {
530                                         TreeSocket* Sock = i->second->GetSocket();
531                                         if (Sock)
532                                                 Sock->WriteLine(":"+std::string(user->uuid)+" PRIVMSG "+cname+" :"+text);
533                                 }
534                         }
535                 }
536         }
537         else if (target_type == TYPE_SERVER)
538         {
539                 if (IS_LOCAL(user))
540                 {
541                         char* target = (char*)dest;
542                         parameterlist par;
543                         par.push_back(target);
544                         par.push_back(":"+text);
545                         Utils->DoOneToMany(user->uuid,"PRIVMSG",par);
546                 }
547         }
548 }
549
550 void ModuleSpanningTree::OnBackgroundTimer(time_t curtime)
551 {
552         AutoConnectServers(curtime);
553         DoPingChecks(curtime);
554         DoConnectTimeout(curtime);
555 }
556
557 void ModuleSpanningTree::OnUserConnect(LocalUser* user)
558 {
559         if (user->quitting)
560                 return;
561
562         parameterlist params;
563         params.push_back(user->uuid);
564         params.push_back(ConvToStr(user->age));
565         params.push_back(user->nick);
566         params.push_back(user->host);
567         params.push_back(user->dhost);
568         params.push_back(user->ident);
569         params.push_back(user->GetIPString());
570         params.push_back(ConvToStr(user->signon));
571         params.push_back("+"+std::string(user->FormatModes(true)));
572         params.push_back(":"+std::string(user->fullname));
573         Utils->DoOneToMany(ServerInstance->Config->GetSID(), "UID", params);
574
575         for(Extensible::ExtensibleStore::const_iterator i = user->GetExtList().begin(); i != user->GetExtList().end(); i++)
576         {
577                 ExtensionItem* item = i->first;
578                 std::string value = item->serialize(FORMAT_NETWORK, user, i->second);
579                 if (!value.empty())
580                         ServerInstance->PI->SendMetaData(user, item->name, value);
581         }
582
583         Utils->TreeRoot->SetUserCount(1); // increment by 1
584 }
585
586 void ModuleSpanningTree::OnUserJoin(Membership* memb, bool sync, bool created, CUList& excepts)
587 {
588         // Only do this for local users
589         if (IS_LOCAL(memb->user))
590         {
591                 parameterlist params;
592                 // set up their permissions and the channel TS with FJOIN.
593                 // All users are FJOINed now, because a module may specify
594                 // new joining permissions for the user.
595                 params.push_back(memb->chan->name);
596                 params.push_back(ConvToStr(memb->chan->age));
597                 params.push_back(std::string("+") + memb->chan->ChanModes(true));
598                 params.push_back(memb->modes+","+std::string(memb->user->uuid));
599                 Utils->DoOneToMany(ServerInstance->Config->GetSID(),"FJOIN",params);
600         }
601 }
602
603 void ModuleSpanningTree::OnChangeHost(User* user, const std::string &newhost)
604 {
605         if (user->registered != REG_ALL || !IS_LOCAL(user))
606                 return;
607
608         parameterlist params;
609         params.push_back(newhost);
610         Utils->DoOneToMany(user->uuid,"FHOST",params);
611 }
612
613 void ModuleSpanningTree::OnChangeName(User* user, const std::string &gecos)
614 {
615         if (user->registered != REG_ALL || !IS_LOCAL(user))
616                 return;
617
618         parameterlist params;
619         params.push_back(gecos);
620         Utils->DoOneToMany(user->uuid,"FNAME",params);
621 }
622
623 void ModuleSpanningTree::OnChangeIdent(User* user, const std::string &ident)
624 {
625         // only occurs for local clients
626         if (user->registered != REG_ALL)
627                 return;
628
629         parameterlist params;
630         params.push_back(ident);
631         Utils->DoOneToMany(user->uuid,"FIDENT",params);
632 }
633
634 void ModuleSpanningTree::OnUserPart(Membership* memb, std::string &partmessage, CUList& excepts)
635 {
636         if (IS_LOCAL(memb->user))
637         {
638                 parameterlist params;
639                 params.push_back(memb->chan->name);
640                 if (!partmessage.empty())
641                         params.push_back(":"+partmessage);
642                 Utils->DoOneToMany(memb->user->uuid,"PART",params);
643         }
644 }
645
646 void ModuleSpanningTree::OnUserQuit(User* user, const std::string &reason, const std::string &oper_message)
647 {
648         if ((IS_LOCAL(user)) && (user->registered == REG_ALL))
649         {
650                 parameterlist params;
651
652                 if (oper_message != reason)
653                 {
654                         params.push_back(":"+oper_message);
655                         Utils->DoOneToMany(user->uuid,"OPERQUIT",params);
656                 }
657                 params.clear();
658                 params.push_back(":"+reason);
659                 Utils->DoOneToMany(user->uuid,"QUIT",params);
660         }
661
662         // Regardless, We need to modify the user Counts..
663         TreeServer* SourceServer = Utils->FindServer(user->server);
664         if (SourceServer)
665         {
666                 SourceServer->SetUserCount(-1); // decrement by 1
667         }
668 }
669
670 void ModuleSpanningTree::OnUserPostNick(User* user, const std::string &oldnick)
671 {
672         if (IS_LOCAL(user))
673         {
674                 parameterlist params;
675                 params.push_back(user->nick);
676
677                 /** IMPORTANT: We don't update the TS if the oldnick is just a case change of the newnick!
678                  */
679                 if (irc::string(user->nick.c_str()) != assign(oldnick))
680                         user->age = ServerInstance->Time();
681
682                 params.push_back(ConvToStr(user->age));
683                 Utils->DoOneToMany(user->uuid,"NICK",params);
684         }
685         else if (!loopCall && user->nick == user->uuid)
686         {
687                 parameterlist params;
688                 params.push_back(user->uuid);
689                 params.push_back(ConvToStr(user->age));
690                 Utils->DoOneToMany(ServerInstance->Config->GetSID(),"SAVE",params);
691         }
692 }
693
694 void ModuleSpanningTree::OnUserKick(User* source, Membership* memb, const std::string &reason, CUList& excepts)
695 {
696         parameterlist params;
697         params.push_back(memb->chan->name);
698         params.push_back(memb->user->uuid);
699         params.push_back(":"+reason);
700         if (IS_LOCAL(source))
701         {
702                 Utils->DoOneToMany(source->uuid,"KICK",params);
703         }
704         else if (source == ServerInstance->FakeClient)
705         {
706                 Utils->DoOneToMany(ServerInstance->Config->GetSID(),"KICK",params);
707         }
708 }
709
710 void ModuleSpanningTree::OnRemoteKill(User* source, User* dest, const std::string &reason, const std::string &operreason)
711 {
712         if (!IS_LOCAL(source))
713                 return; // Only start routing if we're origin.
714
715         ServerInstance->OperQuit.set(dest, operreason);
716         parameterlist params;
717         params.push_back(":"+operreason);
718         Utils->DoOneToMany(dest->uuid,"OPERQUIT",params);
719         params.clear();
720         params.push_back(dest->uuid);
721         params.push_back(":"+reason);
722         Utils->DoOneToMany(source->uuid,"KILL",params);
723 }
724
725 void ModuleSpanningTree::OnPreRehash(User* user, const std::string &parameter)
726 {
727         ServerInstance->Logs->Log("remoterehash", DEBUG, "called with param %s", parameter.c_str());
728
729         // Send out to other servers
730         if (!parameter.empty() && parameter[0] != '-')
731         {
732                 parameterlist params;
733                 params.push_back(parameter);
734                 Utils->DoOneToAllButSender(user ? user->uuid : ServerInstance->Config->GetSID(), "REHASH", params, user ? user->server : ServerInstance->Config->ServerName);
735         }
736 }
737
738 void ModuleSpanningTree::OnRehash(User* user)
739 {
740         // Re-read config stuff
741         Utils->ReadConfiguration();
742 }
743
744 void ModuleSpanningTree::OnLoadModule(Module* mod)
745 {
746         std::string data;
747         data.push_back('+');
748         data.append(mod->ModuleSourceFile);
749         Version v = mod->GetVersion();
750         if (!v.link_data.empty())
751         {
752                 data.push_back('=');
753                 data.append(v.link_data);
754         }
755         ServerInstance->PI->SendMetaData(NULL, "modules", data);
756 }
757
758 void ModuleSpanningTree::OnUnloadModule(Module* mod)
759 {
760         ServerInstance->PI->SendMetaData(NULL, "modules", "-" + mod->ModuleSourceFile);
761
762         unsigned int items = Utils->TreeRoot->ChildCount();
763         for(unsigned int x = 0; x < items; x++)
764         {
765                 TreeServer* srv = Utils->TreeRoot->GetChild(x);
766                 TreeSocket* sock = srv->GetSocket();
767                 if (sock && sock->GetIOHook() == mod)
768                 {
769                         sock->SendError("SSL module unloaded");
770                         sock->Squit(srv,"SSL module unloaded");
771                         sock->Close();
772                 }
773         }
774 }
775
776 void ModuleSpanningTree::RedoConfig(Module* mod)
777 {
778 }
779
780 // note: the protocol does not allow direct umode +o except
781 // via NICK with 8 params. sending OPERTYPE infers +o modechange
782 // locally.
783 void ModuleSpanningTree::OnOper(User* user, const std::string &opertype)
784 {
785         if (IS_LOCAL(user))
786         {
787                 parameterlist params;
788                 params.push_back(opertype);
789                 Utils->DoOneToMany(user->uuid,"OPERTYPE",params);
790         }
791 }
792
793 void ModuleSpanningTree::OnAddLine(User* user, XLine *x)
794 {
795         if (!x->IsBurstable() || loopCall)
796                 return;
797
798         char data[MAXBUF];
799         snprintf(data,MAXBUF,"%s %s %s %lu %lu :%s", x->type.c_str(), x->Displayable(),
800         ServerInstance->Config->ServerName.c_str(), (unsigned long)x->set_time, (unsigned long)x->duration, x->reason.c_str());
801         parameterlist params;
802         params.push_back(data);
803
804         if (!user)
805         {
806                 /* Server-set lines */
807                 Utils->DoOneToMany(ServerInstance->Config->GetSID(), "ADDLINE", params);
808         }
809         else if (IS_LOCAL(user))
810         {
811                 /* User-set lines */
812                 Utils->DoOneToMany(user->uuid, "ADDLINE", params);
813         }
814 }
815
816 void ModuleSpanningTree::OnDelLine(User* user, XLine *x)
817 {
818         if (x->type == "K")
819                 return;
820
821         char data[MAXBUF];
822         snprintf(data,MAXBUF,"%s %s", x->type.c_str(), x->Displayable());
823         parameterlist params;
824         params.push_back(data);
825
826         if (!user)
827         {
828                 /* Server-unset lines */
829                 Utils->DoOneToMany(ServerInstance->Config->GetSID(), "DELLINE", params);
830         }
831         else if (IS_LOCAL(user))
832         {
833                 /* User-unset lines */
834                 Utils->DoOneToMany(user->uuid, "DELLINE", params);
835         }
836 }
837
838 void ModuleSpanningTree::OnMode(User* user, void* dest, int target_type, const parameterlist &text, const std::vector<TranslateType> &translate)
839 {
840         if ((IS_LOCAL(user)) && (user->registered == REG_ALL))
841         {
842                 parameterlist params;
843                 std::string command;
844                 std::string output_text;
845
846                 ServerInstance->Parser->TranslateUIDs(translate, text, output_text);
847
848                 if (target_type == TYPE_USER)
849                 {
850                         User* u = (User*)dest;
851                         params.push_back(u->uuid);
852                         params.push_back(output_text);
853                         command = "MODE";
854                 }
855                 else
856                 {
857                         Channel* c = (Channel*)dest;
858                         params.push_back(c->name);
859                         params.push_back(ConvToStr(c->age));
860                         params.push_back(output_text);
861                         command = "FMODE";
862                 }
863
864                 Utils->DoOneToMany(user->uuid, command, params);
865         }
866 }
867
868 ModResult ModuleSpanningTree::OnSetAway(User* user, const std::string &awaymsg)
869 {
870         if (IS_LOCAL(user))
871         {
872                 if (awaymsg.empty())
873                 {
874                         parameterlist params;
875                         Utils->DoOneToMany(user->uuid,"AWAY",params);
876                 }
877                 else
878                 {
879                         parameterlist params;
880                         params.push_back(ConvToStr(user->awaytime));
881                         params.push_back(":" + awaymsg);
882                         Utils->DoOneToMany(user->uuid,"AWAY",params);
883                 }
884         }
885
886         return MOD_RES_PASSTHRU;
887 }
888
889 void ModuleSpanningTree::ProtoSendMode(void* opaque, TargetTypeFlags target_type, void* target, const parameterlist &modeline, const std::vector<TranslateType> &translate)
890 {
891         TreeSocket* s = (TreeSocket*)opaque;
892         std::string output_text;
893
894         ServerInstance->Parser->TranslateUIDs(translate, modeline, output_text);
895
896         if (target)
897         {
898                 if (target_type == TYPE_USER)
899                 {
900                         User* u = (User*)target;
901                         s->WriteLine(std::string(":")+ServerInstance->Config->GetSID()+" MODE "+u->uuid+" "+output_text);
902                 }
903                 else if (target_type == TYPE_CHANNEL)
904                 {
905                         Channel* c = (Channel*)target;
906                         s->WriteLine(std::string(":")+ServerInstance->Config->GetSID()+" FMODE "+c->name+" "+ConvToStr(c->age)+" "+output_text);
907                 }
908         }
909 }
910
911 void ModuleSpanningTree::ProtoSendMetaData(void* opaque, Extensible* target, const std::string &extname, const std::string &extdata)
912 {
913         TreeSocket* s = static_cast<TreeSocket*>(opaque);
914         User* u = dynamic_cast<User*>(target);
915         Channel* c = dynamic_cast<Channel*>(target);
916         if (u)
917                 s->WriteLine(std::string(":")+ServerInstance->Config->GetSID()+" METADATA "+u->uuid+" "+extname+" :"+extdata);
918         else if (c)
919                 s->WriteLine(std::string(":")+ServerInstance->Config->GetSID()+" METADATA "+c->name+" "+extname+" :"+extdata);
920         else if (!target)
921                 s->WriteLine(std::string(":")+ServerInstance->Config->GetSID()+" METADATA * "+extname+" :"+extdata);
922 }
923
924 CullResult ModuleSpanningTree::cull()
925 {
926         Utils->cull();
927         ServerInstance->Timers->DelTimer(RefreshTimer);
928         return this->Module::cull();
929 }
930
931 ModuleSpanningTree::~ModuleSpanningTree()
932 {
933         delete ServerInstance->PI;
934         ServerInstance->PI = new ProtocolInterface;
935
936         /* This will also free the listeners */
937         delete Utils;
938
939         delete commands;
940 }
941
942 Version ModuleSpanningTree::GetVersion()
943 {
944         return Version("Allows servers to be linked", VF_VENDOR);
945 }
946
947 /* It is IMPORTANT that m_spanningtree is the last module in the chain
948  * so that any activity it sees is FINAL, e.g. we arent going to send out
949  * a NICK message before m_cloaking has finished putting the +x on the user,
950  * etc etc.
951  * Therefore, we return PRIORITY_LAST to make sure we end up at the END of
952  * the module call queue.
953  */
954 void ModuleSpanningTree::Prioritize()
955 {
956         ServerInstance->Modules->SetPriority(this, PRIORITY_LAST);
957 }
958
959 MODULE_INIT(ModuleSpanningTree)