]> git.netwichtig.de Git - user/henk/code/inspircd.git/blob - src/modules/m_spanningtree/main.cpp
More craq
[user/henk/code/inspircd.git] / src / modules / m_spanningtree / main.cpp
1 /*       +------------------------------------+
2  *       | Inspire Internet Relay Chat Daemon |
3  *       +------------------------------------+
4  *
5  *  InspIRCd: (C) 2002-2007 InspIRCd Development Team
6  * See: http://www.inspircd.org/wiki/index.php/Credits
7  *
8  * This program is free but copyrighted software; see
9  *          the file COPYING for details.
10  *
11  * ---------------------------------------------------
12  */
13
14 /* $ModDesc: Provides a spanning tree server link protocol */
15
16 #include "inspircd.h"
17 #include "commands/cmd_whois.h"
18 #include "commands/cmd_stats.h"
19 #include "socket.h"
20 #include "wildcard.h"
21 #include "xline.h"
22 #include "transport.h"
23
24 #include "m_spanningtree/timesynctimer.h"
25 #include "m_spanningtree/resolvers.h"
26 #include "m_spanningtree/main.h"
27 #include "m_spanningtree/utils.h"
28 #include "m_spanningtree/treeserver.h"
29 #include "m_spanningtree/link.h"
30 #include "m_spanningtree/treesocket.h"
31 #include "m_spanningtree/rconnect.h"
32 #include "m_spanningtree/rsquit.h"
33
34 /* $ModDep: m_spanningtree/timesynctimer.h m_spanningtree/resolvers.h m_spanningtree/main.h m_spanningtree/utils.h m_spanningtree/treeserver.h m_spanningtree/link.h m_spanningtree/treesocket.h m_spanningtree/rconnect.h m_spanningtree/rsquit.h */
35
36 ModuleSpanningTree::ModuleSpanningTree(InspIRCd* Me)
37         : Module(Me), max_local(0), max_global(0)
38 {
39         ServerInstance->Modules->UseInterface("BufferedSocketHook");
40         Utils = new SpanningTreeUtilities(Me, this);
41         command_rconnect = new cmd_rconnect(ServerInstance, this, Utils);
42         ServerInstance->AddCommand(command_rconnect);
43         command_rsquit = new cmd_rsquit(ServerInstance, this, Utils);
44         ServerInstance->AddCommand(command_rsquit);
45         if (Utils->EnableTimeSync)
46         {
47                 SyncTimer = new TimeSyncTimer(ServerInstance, this);
48                 ServerInstance->Timers->AddTimer(SyncTimer);
49         }
50         else
51                 SyncTimer = NULL;
52
53         RefreshTimer = new CacheRefreshTimer(ServerInstance, Utils);
54         ServerInstance->Timers->AddTimer(RefreshTimer);
55 }
56
57 void ModuleSpanningTree::ShowLinks(TreeServer* Current, User* user, int hops)
58 {
59         std::string Parent = Utils->TreeRoot->GetName();
60         if (Current->GetParent())
61         {
62                 Parent = Current->GetParent()->GetName();
63         }
64         for (unsigned int q = 0; q < Current->ChildCount(); q++)
65         {
66                 if ((Current->GetChild(q)->Hidden) || ((Utils->HideULines) && (ServerInstance->ULine(Current->GetChild(q)->GetName().c_str()))))
67                 {
68                         if (*user->oper)
69                         {
70                                  ShowLinks(Current->GetChild(q),user,hops+1);
71                         }
72                 }
73                 else
74                 {
75                         ShowLinks(Current->GetChild(q),user,hops+1);
76                 }
77         }
78         /* Don't display the line if its a uline, hide ulines is on, and the user isnt an oper */
79         if ((Utils->HideULines) && (ServerInstance->ULine(Current->GetName().c_str())) && (!IS_OPER(user)))
80                 return;
81         /* Or if the server is hidden and they're not an oper */
82         else if ((Current->Hidden) && (!IS_OPER(user)))
83                 return;
84
85         user->WriteServ("364 %s %s %s :%d %s",  user->nick,Current->GetName().c_str(),
86                         (Utils->FlatLinks && (!IS_OPER(user))) ? ServerInstance->Config->ServerName : Parent.c_str(),
87                         (Utils->FlatLinks && (!IS_OPER(user))) ? 0 : hops,
88                         Current->GetDesc().c_str());
89 }
90
91 int ModuleSpanningTree::CountLocalServs()
92 {
93         return Utils->TreeRoot->ChildCount();
94 }
95
96 int ModuleSpanningTree::CountServs()
97 {
98         return Utils->serverlist.size();
99 }
100
101 void ModuleSpanningTree::HandleLinks(const char** parameters, int pcnt, User* user)
102 {
103         ShowLinks(Utils->TreeRoot,user,0);
104         user->WriteServ("365 %s * :End of /LINKS list.",user->nick);
105         return;
106 }
107
108 void ModuleSpanningTree::HandleLusers(const char** parameters, int pcnt, User* user)
109 {
110         unsigned int n_users = ServerInstance->UserCount();
111
112         /* Only update these when someone wants to see them, more efficient */
113         if ((unsigned int)ServerInstance->LocalUserCount() > max_local)
114                 max_local = ServerInstance->LocalUserCount();
115         if (n_users > max_global)
116                 max_global = n_users;
117
118         unsigned int ulined_count = 0;
119         unsigned int ulined_local_count = 0;
120
121         /* If ulined are hidden and we're not an oper, count the number of ulined servers hidden,
122          * locally and globally (locally means directly connected to us)
123          */
124         if ((Utils->HideULines) && (!*user->oper))
125         {
126                 for (server_hash::iterator q = Utils->serverlist.begin(); q != Utils->serverlist.end(); q++)
127                 {
128                         if (ServerInstance->ULine(q->second->GetName().c_str()))
129                         {
130                                 ulined_count++;
131                                 if (q->second->GetParent() == Utils->TreeRoot)
132                                         ulined_local_count++;
133                         }
134                 }
135         }
136         user->WriteServ("251 %s :There are %d users and %d invisible on %d servers",user->nick,
137                         n_users-ServerInstance->ModeCount('i'),
138                         ServerInstance->ModeCount('i'),
139                         ulined_count ? this->CountServs() - ulined_count : this->CountServs());
140
141         if (ServerInstance->OperCount())
142                 user->WriteServ("252 %s %d :operator(s) online",user->nick,ServerInstance->OperCount());
143
144         if (ServerInstance->UnregisteredUserCount())
145                 user->WriteServ("253 %s %d :unknown connections",user->nick,ServerInstance->UnregisteredUserCount());
146         
147         if (ServerInstance->ChannelCount())
148                 user->WriteServ("254 %s %d :channels formed",user->nick,ServerInstance->ChannelCount());
149         
150         user->WriteServ("255 %s :I have %d clients and %d servers",user->nick,ServerInstance->LocalUserCount(),ulined_local_count ? this->CountLocalServs() - ulined_local_count : this->CountLocalServs());
151         user->WriteServ("265 %s :Current Local Users: %d  Max: %d",user->nick,ServerInstance->LocalUserCount(),max_local);
152         user->WriteServ("266 %s :Current Global Users: %d  Max: %d",user->nick,n_users,max_global);
153         return;
154 }
155
156 std::string ModuleSpanningTree::TimeToStr(time_t secs)
157 {
158         time_t mins_up = secs / 60;
159         time_t hours_up = mins_up / 60;
160         time_t days_up = hours_up / 24;
161         secs = secs % 60;
162         mins_up = mins_up % 60;
163         hours_up = hours_up % 24;
164         return ((days_up ? (ConvToStr(days_up) + "d") : std::string(""))
165                         + (hours_up ? (ConvToStr(hours_up) + "h") : std::string(""))
166                         + (mins_up ? (ConvToStr(mins_up) + "m") : std::string(""))
167                         + ConvToStr(secs) + "s");
168 }
169
170 void ModuleSpanningTree::DoPingChecks(time_t curtime)
171 {
172         for (unsigned int j = 0; j < Utils->TreeRoot->ChildCount(); j++)
173         {
174                 TreeServer* serv = Utils->TreeRoot->GetChild(j);
175                 TreeSocket* sock = serv->GetSocket();
176                 if (sock)
177                 {
178                         if (curtime >= serv->NextPingTime())
179                         {
180                                 if (serv->AnsweredLastPing())
181                                 {
182                                         sock->WriteLine(std::string(":")+ServerInstance->Config->GetSID()+" PING "+serv->GetID());
183                                         serv->SetNextPingTime(curtime + Utils->PingFreq);
184                                         serv->LastPing = curtime;
185                                         timeval t;
186                                         gettimeofday(&t, NULL);
187                                         long ts = (t.tv_sec * 1000) + (t.tv_usec / 1000);
188                                         serv->LastPingMsec = ts;
189                                         serv->Warned = false;
190                                 }
191                                 else
192                                 {
193                                         /* they didnt answer, boot them */
194                                         sock->SendError("Ping timeout");
195                                         sock->Squit(serv,"Ping timeout");
196                                         ServerInstance->SE->DelFd(sock);
197                                         sock->Close();
198                                         return;
199                                 }
200                         }
201                         else if ((Utils->PingWarnTime) && (!serv->Warned) && (curtime >= serv->NextPingTime() - (Utils->PingFreq - Utils->PingWarnTime)) && (!serv->AnsweredLastPing()))
202                         {
203                                 /* The server hasnt responded, send a warning to opers */
204                                 ServerInstance->SNO->WriteToSnoMask('l',"Server \002%s\002 has not responded to PING for %d seconds, high latency.", serv->GetName().c_str(), Utils->PingWarnTime);
205                                 serv->Warned = true;
206                         }
207                 }
208         }
209
210         /* Cancel remote burst mode on any servers which still have it enabled due to latency/lack of data.
211          * This prevents lost REMOTECONNECT notices
212          */
213         for (server_hash::iterator i = Utils->serverlist.begin(); i != Utils->serverlist.end(); i++)
214                 Utils->SetRemoteBursting(i->second, false);
215 }
216
217 void ModuleSpanningTree::ConnectServer(Link* x)
218 {
219         bool ipvalid = true;
220         QueryType start_type = DNS_QUERY_A;
221 #ifdef IPV6
222         start_type = DNS_QUERY_AAAA;
223         if (strchr(x->IPAddr.c_str(),':'))
224         {
225                 in6_addr n;
226                 if (inet_pton(AF_INET6, x->IPAddr.c_str(), &n) < 1)
227                         ipvalid = false;
228         }
229         else
230 #endif
231         {
232                 in_addr n;
233                 if (inet_aton(x->IPAddr.c_str(),&n) < 1)
234                         ipvalid = false;
235         }
236
237         /* Do we already have an IP? If so, no need to resolve it. */
238         if (ipvalid)
239         {
240                 /* Gave a hook, but it wasnt one we know */
241                 if ((!x->Hook.empty()) && (Utils->hooks.find(x->Hook.c_str()) == Utils->hooks.end()))
242                         return;
243                 TreeSocket* newsocket = new TreeSocket(Utils, ServerInstance, x->IPAddr,x->Port,false,x->Timeout ? x->Timeout : 10,x->Name.c_str(), x->Bind, x->Hook.empty() ? NULL : Utils->hooks[x->Hook.c_str()]);
244                 if (newsocket->GetFd() > -1)
245                 {
246                         /* Handled automatically on success */
247                 }
248                 else
249                 {
250                         RemoteMessage(NULL, "CONNECT: Error connecting \002%s\002: %s.",x->Name.c_str(),strerror(errno));
251                         if (ServerInstance->SocketCull.find(newsocket) == ServerInstance->SocketCull.end())
252                                 ServerInstance->SocketCull[newsocket] = newsocket;
253                         Utils->DoFailOver(x);
254                 }
255         }
256         else
257         {
258                 try
259                 {
260                         bool cached;
261                         ServernameResolver* snr = new ServernameResolver((Module*)this, Utils, ServerInstance,x->IPAddr, *x, cached, start_type);
262                         ServerInstance->AddResolver(snr, cached);
263                 }
264                 catch (ModuleException& e)
265                 {
266                         RemoteMessage(NULL, "CONNECT: Error connecting \002%s\002: %s.",x->Name.c_str(), e.GetReason());
267                         Utils->DoFailOver(x);
268                 }
269         }
270 }
271
272 void ModuleSpanningTree::AutoConnectServers(time_t curtime)
273 {
274         for (std::vector<Link>::iterator x = Utils->LinkBlocks.begin(); x < Utils->LinkBlocks.end(); x++)
275         {
276                 if ((x->AutoConnect) && (curtime >= x->NextConnectTime))
277                 {
278                         x->NextConnectTime = curtime + x->AutoConnect;
279                         TreeServer* CheckDupe = Utils->FindServer(x->Name.c_str());
280                         if (x->FailOver.length())
281                         {
282                                 TreeServer* CheckFailOver = Utils->FindServer(x->FailOver.c_str());
283                                 if (CheckFailOver)
284                                 {
285                                         /* The failover for this server is currently a member of the network.
286                                          * The failover probably succeeded, where the main link did not.
287                                          * Don't try the main link until the failover is gone again.
288                                          */
289                                         continue;
290                                 }
291                         }
292                         if (!CheckDupe)
293                         {
294                                 // an autoconnected server is not connected. Check if its time to connect it
295                                 ServerInstance->SNO->WriteToSnoMask('l',"AUTOCONNECT: Auto-connecting server \002%s\002 (%lu seconds until next attempt)",x->Name.c_str(),x->AutoConnect);
296                                 this->ConnectServer(&(*x));
297                         }
298                 }
299         }
300 }
301
302 int ModuleSpanningTree::HandleVersion(const char** parameters, int pcnt, User* user)
303 {
304         // we've already checked if pcnt > 0, so this is safe
305         TreeServer* found = Utils->FindServerMask(parameters[0]);
306         if (found)
307         {
308                 std::string Version = found->GetVersion();
309                 user->WriteServ("351 %s :%s",user->nick,Version.c_str());
310                 if (found == Utils->TreeRoot)
311                 {
312                         ServerInstance->Config->Send005(user);
313                 }
314         }
315         else
316         {
317                 user->WriteServ("402 %s %s :No such server",user->nick,parameters[0]);
318         }
319         return 1;
320 }
321
322 /* This method will attempt to get a link message out to as many people as is required.
323  * If a user is provided, and that user is local, then the user is sent the message using
324  * WriteServ (they are the local initiator of that message). If the user is remote, they are
325  * sent that message remotely via PUSH.
326  * If the user is NULL, then the notice is sent locally via WriteToSnoMask with snomask 'l',
327  * and remotely via SNONOTICE with mask 'l'.
328  */
329 void ModuleSpanningTree::RemoteMessage(User* user, const char* format, ...)
330 {
331         /* This could cause an infinite loop, because DoOneToMany() will, on error,
332          * call TreeSocket::OnError(), which in turn will call this function to
333          * notify everyone of the error. So, drop any messages that are generated
334          * during the sending of another message. -Special */
335         static bool SendingRemoteMessage = false;
336         if (SendingRemoteMessage)
337                 return;
338         SendingRemoteMessage = true;
339
340         std::deque<std::string> params;
341         char text[MAXBUF];
342         va_list argsPtr;
343
344         va_start(argsPtr, format);
345         vsnprintf(text, MAXBUF, format, argsPtr);
346         va_end(argsPtr);
347
348         if (!user)
349         {
350                 /* No user, target it generically at everyone */
351                 ServerInstance->SNO->WriteToSnoMask('l', "%s", text);
352                 params.push_back("l");
353                 params.push_back(std::string(":") + text);
354                 Utils->DoOneToMany(ServerInstance->Config->GetSID(), "SNONOTICE", params);
355         }
356         else
357         {
358                 if (IS_LOCAL(user))
359                         user->WriteServ("NOTICE %s :%s", user->nick, text);
360                 else
361                 {
362                         params.push_back(user->uuid);
363                         params.push_back(std::string("::") + ServerInstance->Config->ServerName + " NOTICE " + user->nick + " :*** From " +
364                                         ServerInstance->Config->ServerName+ ": " + text);
365                         Utils->DoOneToMany(ServerInstance->Config->GetSID(), "PUSH", params);
366                 }
367         }
368         
369         SendingRemoteMessage = false;
370 }
371         
372 int ModuleSpanningTree::HandleConnect(const char** parameters, int pcnt, User* user)
373 {
374         for (std::vector<Link>::iterator x = Utils->LinkBlocks.begin(); x < Utils->LinkBlocks.end(); x++)
375         {
376                 if (ServerInstance->MatchText(x->Name.c_str(),parameters[0]))
377                 {
378                         TreeServer* CheckDupe = Utils->FindServer(x->Name.c_str());
379                         if (!CheckDupe)
380                         {
381                                 RemoteMessage(user, "*** CONNECT: Connecting to server: \002%s\002 (%s:%d)",x->Name.c_str(),(x->HiddenFromStats ? "<hidden>" : x->IPAddr.c_str()),x->Port);
382                                 ConnectServer(&(*x));
383                                 return 1;
384                         }
385                         else
386                         {
387                                 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());
388                                 return 1;
389                         }
390                 }
391         }
392         RemoteMessage(user, "*** CONNECT: No server matching \002%s\002 could be found in the config file.",parameters[0]);
393         return 1;
394 }
395
396 void ModuleSpanningTree::BroadcastTimeSync()
397 {
398         if (Utils->MasterTime)
399         {
400                 std::deque<std::string> params;
401                 params.push_back(ConvToStr(ServerInstance->Time(false)));
402                 params.push_back("FORCE");
403                 Utils->DoOneToMany(ServerInstance->Config->GetSID(), "TIMESET", params);
404         }
405 }
406
407 void ModuleSpanningTree::OnGetServerDescription(const std::string &servername,std::string &description)
408 {
409         TreeServer* s = Utils->FindServer(servername);
410         if (s)
411         {
412                 description = s->GetDesc();
413         }
414 }
415
416 void ModuleSpanningTree::OnUserInvite(User* source,User* dest,Channel* channel)
417 {
418         if (IS_LOCAL(source))
419         {
420                 std::deque<std::string> params;
421                 params.push_back(dest->uuid);
422                 params.push_back(channel->name);
423                 Utils->DoOneToMany(source->uuid,"INVITE",params);
424         }
425 }
426
427 void ModuleSpanningTree::OnPostLocalTopicChange(User* user, Channel* chan, const std::string &topic)
428 {
429         std::deque<std::string> params;
430         params.push_back(chan->name);
431         params.push_back(":"+topic);
432         Utils->DoOneToMany(user->uuid,"TOPIC",params);
433 }
434
435 void ModuleSpanningTree::OnWallops(User* user, const std::string &text)
436 {
437         if (IS_LOCAL(user))
438         {
439                 std::deque<std::string> params;
440                 params.push_back(":"+text);
441                 Utils->DoOneToMany(user->uuid,"WALLOPS",params);
442         }
443 }
444
445 void ModuleSpanningTree::OnUserNotice(User* user, void* dest, int target_type, const std::string &text, char status, const CUList &exempt_list)
446 {
447         if (target_type == TYPE_USER)
448         {
449                 User* d = (User*)dest;
450                 if ((d->GetFd() < 0) && (IS_LOCAL(user)))
451                 {
452                         std::deque<std::string> params;
453                         params.clear();
454                         params.push_back(d->uuid);
455                         params.push_back(":"+text);
456                         Utils->DoOneToOne(user->uuid,"NOTICE",params,d->server);
457                 }
458         }
459         else if (target_type == TYPE_CHANNEL)
460         {
461                 if (IS_LOCAL(user))
462                 {
463                         Channel *c = (Channel*)dest;
464                         if (c)
465                         {
466                                 std::string cname = c->name;
467                                 if (status)
468                                         cname = status + cname;
469                                 TreeServerList list;
470                                 Utils->GetListOfServersForChannel(c,list,status,exempt_list);
471                                 for (TreeServerList::iterator i = list.begin(); i != list.end(); i++)
472                                 {
473                                         TreeSocket* Sock = i->second->GetSocket();
474                                         if (Sock)
475                                                 Sock->WriteLine(":"+std::string(user->uuid)+" NOTICE "+cname+" :"+text);
476                                 }
477                         }
478                 }
479         }
480         else if (target_type == TYPE_SERVER)
481         {
482                 if (IS_LOCAL(user))
483                 {
484                         char* target = (char*)dest;
485                         std::deque<std::string> par;
486                         par.push_back(target);
487                         par.push_back(":"+text);
488                         Utils->DoOneToMany(user->uuid,"NOTICE",par);
489                 }
490         }
491 }
492
493 void ModuleSpanningTree::OnUserMessage(User* user, void* dest, int target_type, const std::string &text, char status, const CUList &exempt_list)
494 {
495         if (target_type == TYPE_USER)
496         {
497                 // route private messages which are targetted at clients only to the server
498                 // which needs to receive them
499                 User* d = (User*)dest;
500                 if ((d->GetFd() < 0) && (IS_LOCAL(user)))
501                 {
502                         std::deque<std::string> params;
503                         params.clear();
504                         params.push_back(d->uuid);
505                         params.push_back(":"+text);
506                         Utils->DoOneToOne(user->uuid,"PRIVMSG",params,d->server);
507                 }
508         }
509         else if (target_type == TYPE_CHANNEL)
510         {
511                 if (IS_LOCAL(user))
512                 {
513                         Channel *c = (Channel*)dest;
514                         if (c)
515                         {
516                                 std::string cname = c->name;
517                                 if (status)
518                                         cname = status + cname;
519                                 TreeServerList list;
520                                 Utils->GetListOfServersForChannel(c,list,status,exempt_list);
521                                 for (TreeServerList::iterator i = list.begin(); i != list.end(); i++)
522                                 {
523                                         TreeSocket* Sock = i->second->GetSocket();
524                                         if (Sock)
525                                                 Sock->WriteLine(":"+std::string(user->uuid)+" PRIVMSG "+cname+" :"+text);
526                                 }
527                         }
528                 }
529         }
530         else if (target_type == TYPE_SERVER)
531         {
532                 if (IS_LOCAL(user))
533                 {
534                         char* target = (char*)dest;
535                         std::deque<std::string> par;
536                         par.push_back(target);
537                         par.push_back(":"+text);
538                         Utils->DoOneToMany(user->uuid,"PRIVMSG",par);
539                 }
540         }
541 }
542
543 void ModuleSpanningTree::OnBackgroundTimer(time_t curtime)
544 {
545         AutoConnectServers(curtime);
546         DoPingChecks(curtime);
547 }
548
549 void ModuleSpanningTree::OnUserJoin(User* user, Channel* channel, bool &silent)
550 {
551         // Only do this for local users
552         if (IS_LOCAL(user))
553         {
554                 if (channel->GetUserCounter() == 1)
555                 {
556                         std::deque<std::string> params;
557                         // set up their permissions and the channel TS with FJOIN.
558                         // All users are FJOINed now, because a module may specify
559                         // new joining permissions for the user.
560                         params.push_back(channel->name);
561                         params.push_back(ConvToStr(channel->age));
562                         params.push_back(std::string(channel->GetAllPrefixChars(user))+","+std::string(user->uuid));
563                         Utils->DoOneToMany(ServerInstance->Config->GetSID(),"FJOIN",params);
564                         /* First user in, sync the modes for the channel */
565                         params.pop_back();
566                         params.push_back(channel->ChanModes(true));
567                         Utils->DoOneToMany(ServerInstance->Config->GetSID(),"FMODE",params);
568                 }
569                 else
570                 {
571                         std::deque<std::string> params;
572                         params.push_back(channel->name);
573                         params.push_back(ConvToStr(channel->age));
574                         Utils->DoOneToMany(user->uuid,"JOIN",params);
575                 }
576         }
577 }
578
579 void ModuleSpanningTree::OnChangeHost(User* user, const std::string &newhost)
580 {
581         // only occurs for local clients
582         if (user->registered != REG_ALL)
583                 return;
584         std::deque<std::string> params;
585         params.push_back(newhost);
586         Utils->DoOneToMany(user->uuid,"FHOST",params);
587 }
588
589 void ModuleSpanningTree::OnChangeName(User* user, const std::string &gecos)
590 {
591         // only occurs for local clients
592         if (user->registered != REG_ALL)
593                 return;
594         std::deque<std::string> params;
595         params.push_back(gecos);
596         Utils->DoOneToMany(user->uuid,"FNAME",params);
597 }
598
599 void ModuleSpanningTree::OnUserPart(User* user, Channel* channel, const std::string &partmessage, bool &silent)
600 {
601         if (IS_LOCAL(user))
602         {
603                 std::deque<std::string> params;
604                 params.push_back(channel->name);
605                 if (!partmessage.empty())
606                         params.push_back(":"+partmessage);
607                 Utils->DoOneToMany(user->uuid,"PART",params);
608         }
609 }
610
611 void ModuleSpanningTree::OnUserConnect(User* user)
612 {
613         if (IS_LOCAL(user))
614         {
615                 std::deque<std::string> params;
616                 params.push_back(user->uuid);
617                 params.push_back(ConvToStr(user->age));
618                 params.push_back(user->nick);
619                 params.push_back(user->host);
620                 params.push_back(user->dhost);
621                 params.push_back(user->ident);
622                 params.push_back("+"+std::string(user->FormatModes()));
623                 params.push_back(user->GetIPString());
624                 params.push_back(ConvToStr(user->signon));
625                 params.push_back(":"+std::string(user->fullname));
626                 Utils->DoOneToMany(ServerInstance->Config->GetSID(), "UID", params);
627                 // User is Local, change needs to be reflected!
628                 TreeServer* SourceServer = Utils->FindServer(user->server);
629                 if (SourceServer)
630                 {
631                         SourceServer->AddUserCount();
632                 }
633         }
634 }
635
636 void ModuleSpanningTree::OnUserQuit(User* user, const std::string &reason, const std::string &oper_message)
637 {
638         if ((IS_LOCAL(user)) && (user->registered == REG_ALL))
639         {
640                 std::deque<std::string> params;
641
642                 if (oper_message != reason)
643                 {
644                         params.push_back(":"+oper_message);
645                         Utils->DoOneToMany(user->uuid,"OPERQUIT",params);
646                 }
647                 params.clear();
648                 params.push_back(":"+reason);
649                 Utils->DoOneToMany(user->uuid,"QUIT",params);
650         }
651         // Regardless, We need to modify the user Counts..
652         TreeServer* SourceServer = Utils->FindServer(user->server);
653         if (SourceServer)
654         {
655                 SourceServer->DelUserCount();
656         }
657 }
658
659 void ModuleSpanningTree::OnUserPostNick(User* user, const std::string &oldnick)
660 {
661         if (IS_LOCAL(user))
662         {
663                 std::deque<std::string> params;
664                 params.push_back(user->nick);
665
666                 /** IMPORTANT: We don't update the TS if the oldnick is just a case change of the newnick!
667                  */
668                 if (irc::string(user->nick) != assign(oldnick))
669                         user->age = ServerInstance->Time(true);
670
671                 params.push_back(ConvToStr(user->age));
672                 Utils->DoOneToMany(user->uuid,"NICK",params);
673         }
674 }
675
676 void ModuleSpanningTree::OnUserKick(User* source, User* user, Channel* chan, const std::string &reason, bool &silent)
677 {
678         if ((source) && (IS_LOCAL(source)))
679         {
680                 std::deque<std::string> params;
681                 params.push_back(chan->name);
682                 params.push_back(user->uuid);
683                 params.push_back(":"+reason);
684                 Utils->DoOneToMany(source->uuid,"KICK",params);
685         }
686         else if (!source)
687         {
688                 std::deque<std::string> params;
689                 params.push_back(chan->name);
690                 params.push_back(user->uuid);
691                 params.push_back(":"+reason);
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         std::deque<std::string> params;
699         params.push_back(":"+reason);
700         Utils->DoOneToMany(dest->uuid,"OPERQUIT",params);
701         params.clear();
702         params.push_back(dest->uuid);
703         params.push_back(":"+reason);
704         dest->SetOperQuit(operreason);
705         Utils->DoOneToMany(source->uuid,"KILL",params);
706 }
707
708 void ModuleSpanningTree::OnRehash(User* user, const std::string &parameter)
709 {
710         if (!parameter.empty())
711         {
712                 std::deque<std::string> params;
713                 params.push_back(parameter);
714                 Utils->DoOneToMany(user ? user->nick : ServerInstance->Config->GetSID(), "REHASH", params);
715                 // check for self
716                 if (ServerInstance->MatchText(ServerInstance->Config->ServerName,parameter))
717                 {
718                         ServerInstance->WriteOpers("*** Remote rehash initiated locally by \002%s\002", user ? user->nick : ServerInstance->Config->ServerName);
719                         ServerInstance->RehashServer();
720                 }
721         }
722         Utils->ReadConfiguration(false);
723         InitializeDisabledCommands(ServerInstance->Config->DisabledCommands, ServerInstance);
724 }
725
726 // note: the protocol does not allow direct umode +o except
727 // via NICK with 8 params. sending OPERTYPE infers +o modechange
728 // locally.
729 void ModuleSpanningTree::OnOper(User* user, const std::string &opertype)
730 {
731         if (IS_LOCAL(user))
732         {
733                 std::deque<std::string> params;
734                 params.push_back(opertype);
735                 Utils->DoOneToMany(user->uuid,"OPERTYPE",params);
736         }
737 }
738
739 void ModuleSpanningTree::OnAddLine(XLine* line, User* user)
740 {
741         if (line->type == "K")
742                 return;
743
744         char data[MAXBUF];
745         snprintf(data,MAXBUF,"%s %s %s %lu %lu :%s", line->type.c_str(), line->Displayable(), ServerInstance->Config->ServerName, line->set_time,
746                         line->duration, line->reason);
747         std::deque<std::string> params;
748         params.push_back(data);
749
750         if (!user)
751         {
752                 /* Server-set lines */
753                 Utils->DoOneToMany(ServerInstance->Config->GetSID(), "ADDLINE", params);
754         }
755         else if (IS_LOCAL(user))
756         {
757                 /* User-set lines */
758                 Utils->DoOneToMany(user->uuid, "ADDLINE", params);
759         }
760 }
761
762 void ModuleSpanningTree::OnDelLine(XLine* line, User* user)
763 {
764         if (line->type == "K")
765                 return;
766
767         char data[MAXBUF];
768         snprintf(data,MAXBUF,"%s %s", line->type.c_str(), line->Displayable());
769         std::deque<std::string> params;
770         params.push_back(data);
771
772         if (!user)
773         {
774                 /* Server-unset lines */
775                 Utils->DoOneToMany(ServerInstance->Config->GetSID(), "DELLINE", params);
776         }
777         else if (IS_LOCAL(user))
778         {
779                 /* User-unset lines */
780                 Utils->DoOneToMany(user->uuid, "DELLINE", params);
781         }
782 }
783
784 void ModuleSpanningTree::OnMode(User* user, void* dest, int target_type, const std::string &text)
785 {
786         if ((IS_LOCAL(user)) && (user->registered == REG_ALL))
787         {
788                 std::deque<std::string> params;
789                 std::string command;
790                 std::string output_text;
791
792                 ServerInstance->Parser->TranslateUIDs(TR_SPACENICKLIST, text, output_text);
793
794                 if (target_type == TYPE_USER)
795                 {
796                         User* u = (User*)dest;
797                         params.push_back(u->uuid);
798                         params.push_back(output_text);
799                         command = "MODE";
800                 }
801                 else
802                 {
803                         Channel* c = (Channel*)dest;
804                         params.push_back(c->name);
805                         params.push_back(ConvToStr(c->age));
806                         params.push_back(output_text);
807                         command = "FMODE";
808                 }
809
810                 Utils->DoOneToMany(user->uuid, command, params);
811         }
812 }
813
814 void ModuleSpanningTree::OnSetAway(User* user)
815 {
816         if (IS_LOCAL(user))
817         {
818                 std::deque<std::string> params;
819                 params.push_back(":"+std::string(user->awaymsg));
820                 Utils->DoOneToMany(user->uuid,"AWAY",params);
821         }
822 }
823
824 void ModuleSpanningTree::OnCancelAway(User* user)
825 {
826         if (IS_LOCAL(user))
827         {
828                 std::deque<std::string> params;
829                 params.clear();
830                 Utils->DoOneToMany(user->uuid,"AWAY",params);
831         }
832 }
833
834 void ModuleSpanningTree::ProtoSendMode(void* opaque, int target_type, void* target, const std::string &modeline)
835 {
836         TreeSocket* s = (TreeSocket*)opaque;
837         std::string output_text;
838
839         ServerInstance->Parser->TranslateUIDs(TR_SPACENICKLIST, modeline, output_text);
840
841         if (target)
842         {
843                 if (target_type == TYPE_USER)
844                 {
845                         User* u = (User*)target;
846                         s->WriteLine(std::string(":")+ServerInstance->Config->GetSID()+" FMODE "+u->uuid+" "+ConvToStr(u->age)+" "+output_text);
847                 }
848                 else
849                 {
850                         Channel* c = (Channel*)target;
851                         s->WriteLine(std::string(":")+ServerInstance->Config->GetSID()+" FMODE "+c->name+" "+ConvToStr(c->age)+" "+output_text);
852                 }
853         }
854 }
855
856 void ModuleSpanningTree::ProtoSendMetaData(void* opaque, int target_type, void* target, const std::string &extname, const std::string &extdata)
857 {
858         TreeSocket* s = (TreeSocket*)opaque;
859         if (target)
860         {
861                 if (target_type == TYPE_USER)
862                 {
863                         User* u = (User*)target;
864                         s->WriteLine(std::string(":")+ServerInstance->Config->GetSID()+" METADATA "+u->uuid+" "+extname+" :"+extdata);
865                 }
866                 else if (target_type == TYPE_CHANNEL)
867                 {
868                         Channel* c = (Channel*)target;
869                         s->WriteLine(std::string(":")+ServerInstance->Config->GetSID()+" METADATA "+c->name+" "+extname+" :"+extdata);
870                 }
871         }
872         if (target_type == TYPE_OTHER)
873         {
874                 s->WriteLine(std::string(":")+ServerInstance->Config->GetSID()+" METADATA * "+extname+" :"+extdata);
875         }
876 }
877
878 void ModuleSpanningTree::OnEvent(Event* event)
879 {
880         std::deque<std::string>* params = (std::deque<std::string>*)event->GetData();
881         if (event->GetEventID() == "send_metadata")
882         {
883                 if (params->size() < 3)
884                         return;
885                 (*params)[2] = ":" + (*params)[2];
886                 Utils->DoOneToMany(ServerInstance->Config->GetSID(),"METADATA",*params);
887         }
888         else if (event->GetEventID() == "send_topic")
889         {
890                 if (params->size() < 2)
891                         return;
892                 (*params)[1] = ":" + (*params)[1];
893                 params->insert(params->begin() + 1,ServerInstance->Config->ServerName);
894                 params->insert(params->begin() + 1,ConvToStr(ServerInstance->Time(true)));
895                 Utils->DoOneToMany(ServerInstance->Config->GetSID(),"FTOPIC",*params);
896         }
897         else if (event->GetEventID() == "send_mode")
898         {
899                 if (params->size() < 2)
900                         return;
901                 // Insert the TS value of the object, either User or Channel
902                 time_t ourTS = 0;
903                 std::string output_text;
904
905                 /* Warning: in-place translation is only safe for type TR_NICK */
906                 for (size_t n = 0; n < params->size(); n++)
907                         ServerInstance->Parser->TranslateUIDs(TR_NICK, (*params)[n], (*params)[n]);
908
909                 User* a = ServerInstance->FindNick((*params)[0]);
910                 if (a)
911                 {
912                         ourTS = a->age;
913                         Utils->DoOneToMany(ServerInstance->Config->GetSID(),"MODE",*params);
914                         return;
915                 }
916                 else
917                 {
918                         Channel* a = ServerInstance->FindChan((*params)[0]);
919                         if (a)
920                         {
921                                 ourTS = a->age;
922                                 params->insert(params->begin() + 1,ConvToStr(ourTS));
923                                 Utils->DoOneToMany(ServerInstance->Config->GetSID(),"FMODE",*params);
924                         }
925                 }
926         }
927         else if (event->GetEventID() == "send_mode_explicit")
928         {
929                 if (params->size() < 2)
930                         return;
931                 std::string output_text;
932
933                 /* Warning: in-place translation is only safe for type TR_NICK */
934                 for (size_t n = 0; n < params->size(); n++)
935                         ServerInstance->Parser->TranslateUIDs(TR_NICK, (*params)[n], (*params)[n]);
936
937                 Utils->DoOneToMany(ServerInstance->Config->GetSID(),"MODE",*params);
938         }
939         else if (event->GetEventID() == "send_opers")
940         {
941                 if (params->size() < 1)
942                         return;
943                 (*params)[0] = ":" + (*params)[0];
944                 Utils->DoOneToMany(ServerInstance->Config->GetSID(),"OPERNOTICE",*params);
945         }
946         else if (event->GetEventID() == "send_modeset")
947         {
948                 if (params->size() < 2)
949                         return;
950                 (*params)[1] = ":" + (*params)[1];
951                 Utils->DoOneToMany(ServerInstance->Config->GetSID(),"MODENOTICE",*params);
952         }
953         else if (event->GetEventID() == "send_snoset")
954         {
955                 if (params->size() < 2)
956                         return;
957                 (*params)[1] = ":" + (*params)[1];
958                 Utils->DoOneToMany(ServerInstance->Config->GetSID(),"SNONOTICE",*params);
959         }
960         else if (event->GetEventID() == "send_push")
961         {
962                 if (params->size() < 2)
963                         return;
964                         
965                 User *a = ServerInstance->FindNick((*params)[0]);
966                         
967                 if (!a)
968                         return;
969
970                 (*params)[0] = a->uuid;
971                 (*params)[1] = ":" + (*params)[1];
972                 Utils->DoOneToOne(ServerInstance->Config->GetSID(), "PUSH", *params, a->server);
973         }
974 }
975
976 ModuleSpanningTree::~ModuleSpanningTree()
977 {
978         /* This will also free the listeners */
979         delete Utils;
980         if (SyncTimer)
981                 ServerInstance->Timers->DelTimer(SyncTimer);
982
983         ServerInstance->Timers->DelTimer(RefreshTimer);
984
985         ServerInstance->Modules->DoneWithInterface("BufferedSocketHook");
986 }
987
988 Version ModuleSpanningTree::GetVersion()
989 {
990         return Version(1,1,0,2,VF_VENDOR,API_VERSION);
991 }
992
993 void ModuleSpanningTree::Implements(char* List)
994 {
995         List[I_OnPreCommand] = List[I_OnGetServerDescription] = List[I_OnUserInvite] = List[I_OnPostLocalTopicChange] = 1;
996         List[I_OnWallops] = List[I_OnUserNotice] = List[I_OnUserMessage] = List[I_OnBackgroundTimer] = 1;
997         List[I_OnUserJoin] = List[I_OnChangeHost] = List[I_OnChangeName] = List[I_OnUserPart] = List[I_OnUserConnect] = 1;
998         List[I_OnUserQuit] = List[I_OnUserPostNick] = List[I_OnUserKick] = List[I_OnRemoteKill] = List[I_OnRehash] = 1;
999         List[I_OnOper] = List[I_OnAddLine] = List[I_OnDelLine] = List[I_ProtoSendMode] = List[I_OnMode] = 1;
1000         List[I_OnStats] = List[I_ProtoSendMetaData] = List[I_OnEvent] = List[I_OnSetAway] = List[I_OnCancelAway] = List[I_OnPostCommand] = 1;
1001 }
1002
1003 /* It is IMPORTANT that m_spanningtree is the last module in the chain
1004  * so that any activity it sees is FINAL, e.g. we arent going to send out
1005  * a NICK message before m_cloaking has finished putting the +x on the user,
1006  * etc etc.
1007  * Therefore, we return PRIORITY_LAST to make sure we end up at the END of
1008  * the module call queue.
1009  */
1010 void ModuleSpanningTree::Prioritize()
1011 {
1012         ServerInstance->Modules->SetPriority(this, PRIO_LAST);
1013 }
1014
1015 MODULE_INIT(ModuleSpanningTree)