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