]> git.netwichtig.de Git - user/henk/code/inspircd.git/blob - src/modules/m_spanningtree/main.cpp
Use UID/SID as the source for ENCAP commands, not server name
[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                                                 ServerInstance->SE->DelFd(sock);
192                                                 sock->Close();
193                                                 return;
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
209 void ModuleSpanningTree::ConnectServer(Autoconnect* a, bool on_timer)
210 {
211         if (!a)
212                 return;
213         for(unsigned int j=0; j < a->servers.size(); j++)
214         {
215                 if (Utils->FindServer(a->servers[j]))
216                 {
217                         // found something in this block. Should the server fail,
218                         // we want to start at the start of the list, not in the
219                         // middle where we left off
220                         a->position = -1;
221                         return;
222                 }
223         }
224         if (on_timer && a->position >= 0)
225                 return;
226         if (!on_timer && a->position < 0)
227                 return;
228
229         a->position++;
230         while (a->position < (int)a->servers.size())
231         {
232                 Link* x = Utils->FindLink(a->servers[a->position]);
233                 if (x)
234                 {
235                         ServerInstance->SNO->WriteToSnoMask('l', "AUTOCONNECT: Auto-connecting server \002%s\002", x->Name.c_str());
236                         ConnectServer(x, a);
237                         return;
238                 }
239                 a->position++;
240         }
241         // Autoconnect chain has been fully iterated; start at the beginning on the
242         // next AutoConnectServers run
243         a->position = -1;
244 }
245
246 void ModuleSpanningTree::ConnectServer(Link* x, Autoconnect* y)
247 {
248         bool ipvalid = true;
249
250         if (InspIRCd::Match(ServerInstance->Config->ServerName, assign(x->Name)))
251         {
252                 ServerInstance->SNO->WriteToSnoMask('l', "CONNECT: Not connecting to myself.");
253                 return;
254         }
255
256         QueryType start_type = DNS_QUERY_A;
257         start_type = DNS_QUERY_AAAA;
258         if (strchr(x->IPAddr.c_str(),':'))
259         {
260                 in6_addr n;
261                 if (inet_pton(AF_INET6, x->IPAddr.c_str(), &n) < 1)
262                         ipvalid = false;
263         }
264         else
265         {
266                 in_addr n;
267                 if (inet_aton(x->IPAddr.c_str(),&n) < 1)
268                         ipvalid = false;
269         }
270
271         /* Do we already have an IP? If so, no need to resolve it. */
272         if (ipvalid)
273         {
274                 /* Gave a hook, but it wasnt one we know */
275                 TreeSocket* newsocket = new TreeSocket(Utils, x->IPAddr, x->Port, x->Timeout ? x->Timeout : 10,
276                         x->Name.c_str(), x->Bind, y, x->Hook);
277                 if (newsocket->GetFd() > -1)
278                 {
279                         /* Handled automatically on success */
280                 }
281                 else
282                 {
283                         ServerInstance->SNO->WriteToSnoMask('l', "CONNECT: Error connecting \002%s\002: %s.",
284                                 x->Name.c_str(), newsocket->getError().c_str());
285                         ServerInstance->GlobalCulls.AddItem(newsocket);
286                 }
287         }
288         else
289         {
290                 try
291                 {
292                         bool cached;
293                         ServernameResolver* snr = new ServernameResolver(Utils, x->IPAddr, x, cached, start_type, y);
294                         ServerInstance->AddResolver(snr, cached);
295                 }
296                 catch (ModuleException& e)
297                 {
298                         ServerInstance->SNO->WriteToSnoMask('l', "CONNECT: Error connecting \002%s\002: %s.",x->Name.c_str(), e.GetReason());
299                         ConnectServer(y, false);
300                 }
301         }
302 }
303
304 void ModuleSpanningTree::AutoConnectServers(time_t curtime)
305 {
306         for (std::vector<reference<Autoconnect> >::iterator i = Utils->AutoconnectBlocks.begin(); i < Utils->AutoconnectBlocks.end(); ++i)
307         {
308                 Autoconnect* x = *i;
309                 if (curtime >= x->NextConnectTime)
310                 {
311                         x->NextConnectTime = curtime + x->Period;
312                         ConnectServer(x, true);
313                 }
314         }
315 }
316
317 void ModuleSpanningTree::DoConnectTimeout(time_t curtime)
318 {
319         std::map<TreeSocket*, std::pair<std::string, int> >::iterator i = Utils->timeoutlist.begin();
320         while (i != Utils->timeoutlist.end())
321         {
322                 TreeSocket* s = i->first;
323                 std::pair<std::string, int> p = i->second;
324                 std::map<TreeSocket*, std::pair<std::string, int> >::iterator me = i;
325                 i++;
326                 if (curtime > s->age + p.second)
327                 {
328                         ServerInstance->SNO->WriteToSnoMask('l',"CONNECT: Error connecting \002%s\002 (timeout of %d seconds)",p.first.c_str(),p.second);
329                         Utils->timeoutlist.erase(me);
330                         s->Close();
331                         ServerInstance->GlobalCulls.AddItem(s);
332                 }
333         }
334 }
335
336 ModResult ModuleSpanningTree::HandleVersion(const std::vector<std::string>& parameters, User* user)
337 {
338         // we've already checked if pcnt > 0, so this is safe
339         TreeServer* found = Utils->FindServerMask(parameters[0]);
340         if (found)
341         {
342                 std::string Version = found->GetVersion();
343                 user->WriteNumeric(351, "%s :%s",user->nick.c_str(),Version.c_str());
344                 if (found == Utils->TreeRoot)
345                 {
346                         ServerInstance->Config->Send005(user);
347                 }
348         }
349         else
350         {
351                 user->WriteNumeric(402, "%s %s :No such server",user->nick.c_str(),parameters[0].c_str());
352         }
353         return MOD_RES_DENY;
354 }
355
356 /* This method will attempt to get a message to a remote user.
357  */
358 void ModuleSpanningTree::RemoteMessage(User* user, const char* format, ...)
359 {
360         char text[MAXBUF];
361         va_list argsPtr;
362
363         va_start(argsPtr, format);
364         vsnprintf(text, MAXBUF, format, argsPtr);
365         va_end(argsPtr);
366
367         if (IS_LOCAL(user))
368                 user->WriteServ("NOTICE %s :%s", user->nick.c_str(), text);
369         else
370                 ServerInstance->PI->SendUserNotice(user, text);
371 }
372
373 ModResult ModuleSpanningTree::HandleConnect(const std::vector<std::string>& parameters, User* user)
374 {
375         for (std::vector<reference<Link> >::iterator i = Utils->LinkBlocks.begin(); i < Utils->LinkBlocks.end(); i++)
376         {
377                 Link* x = *i;
378                 if (InspIRCd::Match(x->Name.c_str(),parameters[0]))
379                 {
380                         if (InspIRCd::Match(ServerInstance->Config->ServerName, assign(x->Name)))
381                         {
382                                 RemoteMessage(user, "*** CONNECT: Server \002%s\002 is ME, not connecting.",x->Name.c_str());
383                                 return MOD_RES_DENY;
384                         }
385
386                         TreeServer* CheckDupe = Utils->FindServer(x->Name.c_str());
387                         if (!CheckDupe)
388                         {
389                                 RemoteMessage(user, "*** CONNECT: Connecting to server: \002%s\002 (%s:%d)",x->Name.c_str(),(x->HiddenFromStats ? "<hidden>" : x->IPAddr.c_str()),x->Port);
390                                 ConnectServer(x);
391                                 return MOD_RES_DENY;
392                         }
393                         else
394                         {
395                                 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());
396                                 return MOD_RES_DENY;
397                         }
398                 }
399         }
400         RemoteMessage(user, "*** CONNECT: No server matching \002%s\002 could be found in the config file.",parameters[0].c_str());
401         return MOD_RES_DENY;
402 }
403
404 void ModuleSpanningTree::OnGetServerDescription(const std::string &servername,std::string &description)
405 {
406         TreeServer* s = Utils->FindServer(servername);
407         if (s)
408         {
409                 description = s->GetDesc();
410         }
411 }
412
413 void ModuleSpanningTree::OnUserInvite(User* source,User* dest,Channel* channel, time_t expiry)
414 {
415         if (IS_LOCAL(source))
416         {
417                 parameterlist params;
418                 params.push_back(dest->uuid);
419                 params.push_back(channel->name);
420                 params.push_back(ConvToStr(expiry));
421                 Utils->DoOneToMany(source->uuid,"INVITE",params);
422         }
423 }
424
425 void ModuleSpanningTree::OnPostTopicChange(User* user, Channel* chan, const std::string &topic)
426 {
427         // Drop remote events on the floor.
428         if (!IS_LOCAL(user))
429                 return;
430
431         parameterlist params;
432         params.push_back(chan->name);
433         params.push_back(":"+topic);
434         Utils->DoOneToMany(user->uuid,"TOPIC",params);
435 }
436
437 void ModuleSpanningTree::OnWallops(User* user, const std::string &text)
438 {
439         if (IS_LOCAL(user))
440         {
441                 parameterlist params;
442                 params.push_back(":"+text);
443                 Utils->DoOneToMany(user->uuid,"WALLOPS",params);
444         }
445 }
446
447 void ModuleSpanningTree::OnUserNotice(User* user, void* dest, int target_type, const std::string &text, char status, const CUList &exempt_list)
448 {
449         /* Server origin */
450         if (user == NULL)
451                 return;
452
453         if (target_type == TYPE_USER)
454         {
455                 User* d = (User*)dest;
456                 if (!IS_LOCAL(d) && IS_LOCAL(user))
457                 {
458                         parameterlist params;
459                         params.push_back(d->uuid);
460                         params.push_back(":"+text);
461                         Utils->DoOneToOne(user->uuid,"NOTICE",params,d->server);
462                 }
463         }
464         else if (target_type == TYPE_CHANNEL)
465         {
466                 if (IS_LOCAL(user))
467                 {
468                         Channel *c = (Channel*)dest;
469                         if (c)
470                         {
471                                 std::string cname = c->name;
472                                 if (status)
473                                         cname = status + cname;
474                                 TreeServerList list;
475                                 Utils->GetListOfServersForChannel(c,list,status,exempt_list);
476                                 for (TreeServerList::iterator i = list.begin(); i != list.end(); i++)
477                                 {
478                                         TreeSocket* Sock = i->second->GetSocket();
479                                         if (Sock)
480                                                 Sock->WriteLine(":"+std::string(user->uuid)+" NOTICE "+cname+" :"+text);
481                                 }
482                         }
483                 }
484         }
485         else if (target_type == TYPE_SERVER)
486         {
487                 if (IS_LOCAL(user))
488                 {
489                         char* target = (char*)dest;
490                         parameterlist par;
491                         par.push_back(target);
492                         par.push_back(":"+text);
493                         Utils->DoOneToMany(user->uuid,"NOTICE",par);
494                 }
495         }
496 }
497
498 void ModuleSpanningTree::OnUserMessage(User* user, void* dest, int target_type, const std::string &text, char status, const CUList &exempt_list)
499 {
500         /* Server origin */
501         if (user == NULL)
502                 return;
503
504         if (target_type == TYPE_USER)
505         {
506                 // route private messages which are targetted at clients only to the server
507                 // which needs to receive them
508                 User* d = (User*)dest;
509                 if (!IS_LOCAL(d) && (IS_LOCAL(user)))
510                 {
511                         parameterlist params;
512                         params.push_back(d->uuid);
513                         params.push_back(":"+text);
514                         Utils->DoOneToOne(user->uuid,"PRIVMSG",params,d->server);
515                 }
516         }
517         else if (target_type == TYPE_CHANNEL)
518         {
519                 if (IS_LOCAL(user))
520                 {
521                         Channel *c = (Channel*)dest;
522                         if (c)
523                         {
524                                 std::string cname = c->name;
525                                 if (status)
526                                         cname = status + cname;
527                                 TreeServerList list;
528                                 Utils->GetListOfServersForChannel(c,list,status,exempt_list);
529                                 for (TreeServerList::iterator i = list.begin(); i != list.end(); i++)
530                                 {
531                                         TreeSocket* Sock = i->second->GetSocket();
532                                         if (Sock)
533                                                 Sock->WriteLine(":"+std::string(user->uuid)+" PRIVMSG "+cname+" :"+text);
534                                 }
535                         }
536                 }
537         }
538         else if (target_type == TYPE_SERVER)
539         {
540                 if (IS_LOCAL(user))
541                 {
542                         char* target = (char*)dest;
543                         parameterlist par;
544                         par.push_back(target);
545                         par.push_back(":"+text);
546                         Utils->DoOneToMany(user->uuid,"PRIVMSG",par);
547                 }
548         }
549 }
550
551 void ModuleSpanningTree::OnBackgroundTimer(time_t curtime)
552 {
553         AutoConnectServers(curtime);
554         DoPingChecks(curtime);
555         DoConnectTimeout(curtime);
556 }
557
558 void ModuleSpanningTree::OnUserConnect(LocalUser* user)
559 {
560         if (user->quitting)
561                 return;
562
563         parameterlist params;
564         params.push_back(user->uuid);
565         params.push_back(ConvToStr(user->age));
566         params.push_back(user->nick);
567         params.push_back(user->host);
568         params.push_back(user->dhost);
569         params.push_back(user->ident);
570         params.push_back(user->GetIPString());
571         params.push_back(ConvToStr(user->signon));
572         params.push_back("+"+std::string(user->FormatModes(true)));
573         params.push_back(":"+std::string(user->fullname));
574         Utils->DoOneToMany(ServerInstance->Config->GetSID(), "UID", params);
575
576         for(Extensible::ExtensibleStore::const_iterator i = user->GetExtList().begin(); i != user->GetExtList().end(); i++)
577         {
578                 ExtensionItem* item = i->first;
579                 std::string value = item->serialize(FORMAT_NETWORK, user, i->second);
580                 if (!value.empty())
581                         ServerInstance->PI->SendMetaData(user, item->name, value);
582         }
583
584         Utils->TreeRoot->SetUserCount(1); // increment by 1
585 }
586
587 void ModuleSpanningTree::OnUserJoin(Membership* memb, bool sync, bool created, CUList& excepts)
588 {
589         // Only do this for local users
590         if (IS_LOCAL(memb->user))
591         {
592                 parameterlist params;
593                 // set up their permissions and the channel TS with FJOIN.
594                 // All users are FJOINed now, because a module may specify
595                 // new joining permissions for the user.
596                 params.push_back(memb->chan->name);
597                 params.push_back(ConvToStr(memb->chan->age));
598                 params.push_back(std::string("+") + memb->chan->ChanModes(true));
599                 params.push_back(memb->modes+","+std::string(memb->user->uuid));
600                 Utils->DoOneToMany(ServerInstance->Config->GetSID(),"FJOIN",params);
601         }
602 }
603
604 void ModuleSpanningTree::OnChangeHost(User* user, const std::string &newhost)
605 {
606         if (user->registered != REG_ALL || !IS_LOCAL(user))
607                 return;
608
609         parameterlist params;
610         params.push_back(newhost);
611         Utils->DoOneToMany(user->uuid,"FHOST",params);
612 }
613
614 void ModuleSpanningTree::OnChangeName(User* user, const std::string &gecos)
615 {
616         if (user->registered != REG_ALL || !IS_LOCAL(user))
617                 return;
618
619         parameterlist params;
620         params.push_back(gecos);
621         Utils->DoOneToMany(user->uuid,"FNAME",params);
622 }
623
624 void ModuleSpanningTree::OnChangeIdent(User* user, const std::string &ident)
625 {
626         // only occurs for local clients
627         if (user->registered != REG_ALL)
628                 return;
629
630         parameterlist params;
631         params.push_back(ident);
632         Utils->DoOneToMany(user->uuid,"FIDENT",params);
633 }
634
635 void ModuleSpanningTree::OnUserPart(Membership* memb, std::string &partmessage, CUList& excepts)
636 {
637         if (IS_LOCAL(memb->user))
638         {
639                 parameterlist params;
640                 params.push_back(memb->chan->name);
641                 if (!partmessage.empty())
642                         params.push_back(":"+partmessage);
643                 Utils->DoOneToMany(memb->user->uuid,"PART",params);
644         }
645 }
646
647 void ModuleSpanningTree::OnUserQuit(User* user, const std::string &reason, const std::string &oper_message)
648 {
649         if ((IS_LOCAL(user)) && (user->registered == REG_ALL))
650         {
651                 parameterlist params;
652
653                 if (oper_message != reason)
654                 {
655                         params.push_back(":"+oper_message);
656                         Utils->DoOneToMany(user->uuid,"OPERQUIT",params);
657                 }
658                 params.clear();
659                 params.push_back(":"+reason);
660                 Utils->DoOneToMany(user->uuid,"QUIT",params);
661         }
662
663         // Regardless, We need to modify the user Counts..
664         TreeServer* SourceServer = Utils->FindServer(user->server);
665         if (SourceServer)
666         {
667                 SourceServer->SetUserCount(-1); // decrement by 1
668         }
669 }
670
671 void ModuleSpanningTree::OnUserPostNick(User* user, const std::string &oldnick)
672 {
673         if (IS_LOCAL(user))
674         {
675                 parameterlist params;
676                 params.push_back(user->nick);
677
678                 /** IMPORTANT: We don't update the TS if the oldnick is just a case change of the newnick!
679                  */
680                 if (irc::string(user->nick.c_str()) != assign(oldnick))
681                         user->age = ServerInstance->Time();
682
683                 params.push_back(ConvToStr(user->age));
684                 Utils->DoOneToMany(user->uuid,"NICK",params);
685         }
686         else if (!loopCall && user->nick == user->uuid)
687         {
688                 parameterlist params;
689                 params.push_back(user->uuid);
690                 params.push_back(ConvToStr(user->age));
691                 Utils->DoOneToMany(ServerInstance->Config->GetSID(),"SAVE",params);
692         }
693 }
694
695 void ModuleSpanningTree::OnUserKick(User* source, Membership* memb, const std::string &reason, CUList& excepts)
696 {
697         parameterlist params;
698         params.push_back(memb->chan->name);
699         params.push_back(memb->user->uuid);
700         params.push_back(":"+reason);
701         if (IS_LOCAL(source))
702         {
703                 Utils->DoOneToMany(source->uuid,"KICK",params);
704         }
705         else if (source == ServerInstance->FakeClient)
706         {
707                 Utils->DoOneToMany(ServerInstance->Config->GetSID(),"KICK",params);
708         }
709 }
710
711 void ModuleSpanningTree::OnRemoteKill(User* source, User* dest, const std::string &reason, const std::string &operreason)
712 {
713         if (!IS_LOCAL(source))
714                 return; // Only start routing if we're origin.
715
716         ServerInstance->OperQuit.set(dest, operreason);
717         parameterlist params;
718         params.push_back(":"+operreason);
719         Utils->DoOneToMany(dest->uuid,"OPERQUIT",params);
720         params.clear();
721         params.push_back(dest->uuid);
722         params.push_back(":"+reason);
723         Utils->DoOneToMany(source->uuid,"KILL",params);
724 }
725
726 void ModuleSpanningTree::OnPreRehash(User* user, const std::string &parameter)
727 {
728         ServerInstance->Logs->Log("remoterehash", DEBUG, "called with param %s", parameter.c_str());
729
730         // Send out to other servers
731         if (!parameter.empty() && parameter[0] != '-')
732         {
733                 parameterlist params;
734                 params.push_back(parameter);
735                 Utils->DoOneToAllButSender(user ? user->uuid : ServerInstance->Config->GetSID(), "REHASH", params, user ? user->server : ServerInstance->Config->ServerName);
736         }
737 }
738
739 void ModuleSpanningTree::OnRehash(User* user)
740 {
741         // Re-read config stuff
742         Utils->ReadConfiguration();
743 }
744
745 void ModuleSpanningTree::OnLoadModule(Module* mod)
746 {
747         // TODO notify other servers?
748 }
749
750 void ModuleSpanningTree::OnUnloadModule(Module* mod)
751 {
752         // TODO notify other servers?
753 }
754
755 void ModuleSpanningTree::RedoConfig(Module* mod)
756 {
757 }
758
759 // note: the protocol does not allow direct umode +o except
760 // via NICK with 8 params. sending OPERTYPE infers +o modechange
761 // locally.
762 void ModuleSpanningTree::OnOper(User* user, const std::string &opertype)
763 {
764         if (IS_LOCAL(user))
765         {
766                 parameterlist params;
767                 params.push_back(opertype);
768                 Utils->DoOneToMany(user->uuid,"OPERTYPE",params);
769         }
770 }
771
772 void ModuleSpanningTree::OnAddLine(User* user, XLine *x)
773 {
774         if (!x->IsBurstable() || loopCall)
775                 return;
776
777         char data[MAXBUF];
778         snprintf(data,MAXBUF,"%s %s %s %lu %lu :%s", x->type.c_str(), x->Displayable(),
779         ServerInstance->Config->ServerName.c_str(), (unsigned long)x->set_time, (unsigned long)x->duration, x->reason.c_str());
780         parameterlist params;
781         params.push_back(data);
782
783         if (!user)
784         {
785                 /* Server-set lines */
786                 Utils->DoOneToMany(ServerInstance->Config->GetSID(), "ADDLINE", params);
787         }
788         else if (IS_LOCAL(user))
789         {
790                 /* User-set lines */
791                 Utils->DoOneToMany(user->uuid, "ADDLINE", params);
792         }
793 }
794
795 void ModuleSpanningTree::OnDelLine(User* user, XLine *x)
796 {
797         if (x->type == "K")
798                 return;
799
800         char data[MAXBUF];
801         snprintf(data,MAXBUF,"%s %s", x->type.c_str(), x->Displayable());
802         parameterlist params;
803         params.push_back(data);
804
805         if (!user)
806         {
807                 /* Server-unset lines */
808                 Utils->DoOneToMany(ServerInstance->Config->GetSID(), "DELLINE", params);
809         }
810         else if (IS_LOCAL(user))
811         {
812                 /* User-unset lines */
813                 Utils->DoOneToMany(user->uuid, "DELLINE", params);
814         }
815 }
816
817 void ModuleSpanningTree::OnMode(User* user, void* dest, int target_type, const parameterlist &text, const std::vector<TranslateType> &translate)
818 {
819         if ((IS_LOCAL(user)) && (user->registered == REG_ALL))
820         {
821                 parameterlist params;
822                 std::string command;
823                 std::string output_text;
824
825                 ServerInstance->Parser->TranslateUIDs(translate, text, output_text);
826
827                 if (target_type == TYPE_USER)
828                 {
829                         User* u = (User*)dest;
830                         params.push_back(u->uuid);
831                         params.push_back(output_text);
832                         command = "MODE";
833                 }
834                 else
835                 {
836                         Channel* c = (Channel*)dest;
837                         params.push_back(c->name);
838                         params.push_back(ConvToStr(c->age));
839                         params.push_back(output_text);
840                         command = "FMODE";
841                 }
842
843                 Utils->DoOneToMany(user->uuid, command, params);
844         }
845 }
846
847 ModResult ModuleSpanningTree::OnSetAway(User* user, const std::string &awaymsg)
848 {
849         if (IS_LOCAL(user))
850         {
851                 if (awaymsg.empty())
852                 {
853                         parameterlist params;
854                         Utils->DoOneToMany(user->uuid,"AWAY",params);
855                 }
856                 else
857                 {
858                         parameterlist params;
859                         params.push_back(ConvToStr(user->awaytime));
860                         params.push_back(":" + awaymsg);
861                         Utils->DoOneToMany(user->uuid,"AWAY",params);
862                 }
863         }
864
865         return MOD_RES_PASSTHRU;
866 }
867
868 void ModuleSpanningTree::ProtoSendMode(void* opaque, TargetTypeFlags target_type, void* target, const parameterlist &modeline, const std::vector<TranslateType> &translate)
869 {
870         TreeSocket* s = (TreeSocket*)opaque;
871         std::string output_text;
872
873         ServerInstance->Parser->TranslateUIDs(translate, modeline, output_text);
874
875         if (target)
876         {
877                 if (target_type == TYPE_USER)
878                 {
879                         User* u = (User*)target;
880                         s->WriteLine(std::string(":")+ServerInstance->Config->GetSID()+" MODE "+u->uuid+" "+output_text);
881                 }
882                 else if (target_type == TYPE_CHANNEL)
883                 {
884                         Channel* c = (Channel*)target;
885                         s->WriteLine(std::string(":")+ServerInstance->Config->GetSID()+" FMODE "+c->name+" "+ConvToStr(c->age)+" "+output_text);
886                 }
887         }
888 }
889
890 void ModuleSpanningTree::ProtoSendMetaData(void* opaque, Extensible* target, const std::string &extname, const std::string &extdata)
891 {
892         TreeSocket* s = static_cast<TreeSocket*>(opaque);
893         User* u = dynamic_cast<User*>(target);
894         Channel* c = dynamic_cast<Channel*>(target);
895         if (u)
896                 s->WriteLine(std::string(":")+ServerInstance->Config->GetSID()+" METADATA "+u->uuid+" "+extname+" :"+extdata);
897         else if (c)
898                 s->WriteLine(std::string(":")+ServerInstance->Config->GetSID()+" METADATA "+c->name+" "+extname+" :"+extdata);
899         else if (!target)
900                 s->WriteLine(std::string(":")+ServerInstance->Config->GetSID()+" METADATA * "+extname+" :"+extdata);
901 }
902
903 CullResult ModuleSpanningTree::cull()
904 {
905         Utils->cull();
906         ServerInstance->Timers->DelTimer(RefreshTimer);
907         return this->Module::cull();
908 }
909
910 ModuleSpanningTree::~ModuleSpanningTree()
911 {
912         delete ServerInstance->PI;
913         ServerInstance->PI = new ProtocolInterface;
914
915         /* This will also free the listeners */
916         delete Utils;
917
918         delete commands;
919 }
920
921 Version ModuleSpanningTree::GetVersion()
922 {
923         return Version("Allows servers to be linked", VF_VENDOR);
924 }
925
926 /* It is IMPORTANT that m_spanningtree is the last module in the chain
927  * so that any activity it sees is FINAL, e.g. we arent going to send out
928  * a NICK message before m_cloaking has finished putting the +x on the user,
929  * etc etc.
930  * Therefore, we return PRIORITY_LAST to make sure we end up at the END of
931  * the module call queue.
932  */
933 void ModuleSpanningTree::Prioritize()
934 {
935         ServerInstance->Modules->SetPriority(this, PRIORITY_LAST);
936 }
937
938 MODULE_INIT(ModuleSpanningTree)