]> git.netwichtig.de Git - user/henk/code/inspircd.git/blob - src/modules/m_spanningtree/main.cpp
Split up spanningtree some more, the filenames should be more intuitive so that devel...
[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("InspSocketHook");
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, userrec* 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, userrec* 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, userrec* 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->InvisibleUserCount(),
138                         ServerInstance->InvisibleUserCount(),
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, userrec* 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(userrec* 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, userrec* 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(userrec* source,userrec* dest,chanrec* 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(userrec* user, chanrec* 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(userrec* 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(userrec* 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                 userrec* d = (userrec*)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                         chanrec *c = (chanrec*)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(userrec* 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                 userrec* d = (userrec*)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                         chanrec *c = (chanrec*)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(userrec* user, chanrec* 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(userrec* 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(userrec* 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(userrec* user, chanrec* 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(userrec* 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(userrec* 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(userrec* 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(userrec* source, userrec* user, chanrec* 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(userrec* source, userrec* 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(userrec* 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(userrec* 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::OnLine(userrec* source, const std::string &host, bool adding, char linetype, long duration, const std::string &reason)
740 {
741         if (!source)
742         {
743                 /* Server-set lines */
744                 char data[MAXBUF];
745                 snprintf(data,MAXBUF,"%c %s %s %lu %lu :%s", linetype, host.c_str(), ServerInstance->Config->ServerName, (unsigned long)ServerInstance->Time(false),
746                                 (unsigned long)duration, reason.c_str());
747                 std::deque<std::string> params;
748                 params.push_back(data);
749                 Utils->DoOneToMany(ServerInstance->Config->GetSID(), "ADDLINE", params);
750         }
751         else
752         {
753                 if (IS_LOCAL(source))
754                 {
755                         char type[8];
756                         snprintf(type,8,"%cLINE",linetype);
757                         std::string stype = type;
758                         if (adding)
759                         {
760                                 char sduration[MAXBUF];
761                                 snprintf(sduration,MAXBUF,"%ld",duration);
762                                 std::deque<std::string> params;
763                                 params.push_back(host);
764                                 params.push_back(sduration);
765                                 params.push_back(":"+reason);
766                                 Utils->DoOneToMany(source->uuid,stype,params);
767                         }
768                         else
769                         {
770                                 std::deque<std::string> params;
771                                 params.push_back(host);
772                                 Utils->DoOneToMany(source->uuid,stype,params);
773                         }
774                 }
775         }
776 }
777
778 void ModuleSpanningTree::OnAddGLine(long duration, userrec* source, const std::string &reason, const std::string &hostmask)
779 {
780         OnLine(source,hostmask,true,'G',duration,reason);
781 }
782         
783 void ModuleSpanningTree::OnAddZLine(long duration, userrec* source, const std::string &reason, const std::string &ipmask)
784 {
785         OnLine(source,ipmask,true,'Z',duration,reason);
786 }
787
788 void ModuleSpanningTree::OnAddQLine(long duration, userrec* source, const std::string &reason, const std::string &nickmask)
789 {
790         OnLine(source,nickmask,true,'Q',duration,reason);
791 }
792
793 void ModuleSpanningTree::OnAddELine(long duration, userrec* source, const std::string &reason, const std::string &hostmask)
794 {
795         OnLine(source,hostmask,true,'E',duration,reason);
796 }
797
798 void ModuleSpanningTree::OnDelGLine(userrec* source, const std::string &hostmask)
799 {
800         OnLine(source,hostmask,false,'G',0,"");
801 }
802
803 void ModuleSpanningTree::OnDelZLine(userrec* source, const std::string &ipmask)
804 {
805         OnLine(source,ipmask,false,'Z',0,"");
806 }
807
808 void ModuleSpanningTree::OnDelQLine(userrec* source, const std::string &nickmask)
809 {
810         OnLine(source,nickmask,false,'Q',0,"");
811 }
812
813 void ModuleSpanningTree::OnDelELine(userrec* source, const std::string &hostmask)
814 {
815         OnLine(source,hostmask,false,'E',0,"");
816 }
817
818 void ModuleSpanningTree::OnMode(userrec* user, void* dest, int target_type, const std::string &text)
819 {
820         if ((IS_LOCAL(user)) && (user->registered == REG_ALL))
821         {
822                 std::deque<std::string> params;
823                 std::string command;
824                 std::string output_text;
825
826                 ServerInstance->Parser->TranslateUIDs(TR_SPACENICKLIST, text, output_text);
827
828                 if (target_type == TYPE_USER)
829                 {
830                         userrec* u = (userrec*)dest;
831                         params.push_back(u->uuid);
832                         params.push_back(output_text);
833                         command = "MODE";
834                 }
835                 else
836                 {
837                         chanrec* c = (chanrec*)dest;
838                         params.push_back(c->name);
839                         params.push_back(ConvToStr(c->age));
840                         params.push_back(output_text);
841                         command = "FMODE";
842                 }
843
844                 Utils->DoOneToMany(user->uuid, command, params);
845         }
846 }
847
848 void ModuleSpanningTree::OnSetAway(userrec* user)
849 {
850         if (IS_LOCAL(user))
851         {
852                 std::deque<std::string> params;
853                 params.push_back(":"+std::string(user->awaymsg));
854                 Utils->DoOneToMany(user->uuid,"AWAY",params);
855         }
856 }
857
858 void ModuleSpanningTree::OnCancelAway(userrec* user)
859 {
860         if (IS_LOCAL(user))
861         {
862                 std::deque<std::string> params;
863                 params.clear();
864                 Utils->DoOneToMany(user->uuid,"AWAY",params);
865         }
866 }
867
868 void ModuleSpanningTree::ProtoSendMode(void* opaque, int target_type, void* target, const std::string &modeline)
869 {
870         TreeSocket* s = (TreeSocket*)opaque;
871         std::string output_text;
872
873         ServerInstance->Parser->TranslateUIDs(TR_SPACENICKLIST, modeline, output_text);
874
875         if (target)
876         {
877                 if (target_type == TYPE_USER)
878                 {
879                         userrec* u = (userrec*)target;
880                         s->WriteLine(std::string(":")+ServerInstance->Config->GetSID()+" FMODE "+u->uuid+" "+ConvToStr(u->age)+" "+output_text);
881                 }
882                 else
883                 {
884                         chanrec* c = (chanrec*)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, int target_type, void* target, const std::string &extname, const std::string &extdata)
891 {
892         TreeSocket* s = (TreeSocket*)opaque;
893         if (target)
894         {
895                 if (target_type == TYPE_USER)
896                 {
897                         userrec* u = (userrec*)target;
898                         s->WriteLine(std::string(":")+ServerInstance->Config->GetSID()+" METADATA "+u->uuid+" "+extname+" :"+extdata);
899                 }
900                 else if (target_type == TYPE_CHANNEL)
901                 {
902                         chanrec* c = (chanrec*)target;
903                         s->WriteLine(std::string(":")+ServerInstance->Config->GetSID()+" METADATA "+c->name+" "+extname+" :"+extdata);
904                 }
905         }
906         if (target_type == TYPE_OTHER)
907         {
908                 s->WriteLine(std::string(":")+ServerInstance->Config->GetSID()+" METADATA * "+extname+" :"+extdata);
909         }
910 }
911
912 void ModuleSpanningTree::OnEvent(Event* event)
913 {
914         std::deque<std::string>* params = (std::deque<std::string>*)event->GetData();
915         if (event->GetEventID() == "send_metadata")
916         {
917                 if (params->size() < 3)
918                         return;
919                 (*params)[2] = ":" + (*params)[2];
920                 Utils->DoOneToMany(ServerInstance->Config->GetSID(),"METADATA",*params);
921         }
922         else if (event->GetEventID() == "send_topic")
923         {
924                 if (params->size() < 2)
925                         return;
926                 (*params)[1] = ":" + (*params)[1];
927                 params->insert(params->begin() + 1,ServerInstance->Config->ServerName);
928                 params->insert(params->begin() + 1,ConvToStr(ServerInstance->Time(true)));
929                 Utils->DoOneToMany(ServerInstance->Config->GetSID(),"FTOPIC",*params);
930         }
931         else if (event->GetEventID() == "send_mode")
932         {
933                 if (params->size() < 2)
934                         return;
935                 // Insert the TS value of the object, either userrec or chanrec
936                 time_t ourTS = 0;
937                 std::string output_text;
938
939                 /* Warning: in-place translation is only safe for type TR_NICK */
940                 for (size_t n = 0; n < params->size(); n++)
941                         ServerInstance->Parser->TranslateUIDs(TR_NICK, (*params)[n], (*params)[n]);
942
943                 userrec* a = ServerInstance->FindNick((*params)[0]);
944                 if (a)
945                 {
946                         ourTS = a->age;
947                         Utils->DoOneToMany(ServerInstance->Config->GetSID(),"MODE",*params);
948                         return;
949                 }
950                 else
951                 {
952                         chanrec* a = ServerInstance->FindChan((*params)[0]);
953                         if (a)
954                         {
955                                 ourTS = a->age;
956                                 params->insert(params->begin() + 1,ConvToStr(ourTS));
957                                 Utils->DoOneToMany(ServerInstance->Config->GetSID(),"FMODE",*params);
958                         }
959                 }
960         }
961         else if (event->GetEventID() == "send_mode_explicit")
962         {
963                 if (params->size() < 2)
964                         return;
965                 std::string output_text;
966
967                 /* Warning: in-place translation is only safe for type TR_NICK */
968                 for (size_t n = 0; n < params->size(); n++)
969                         ServerInstance->Parser->TranslateUIDs(TR_NICK, (*params)[n], (*params)[n]);
970
971                 Utils->DoOneToMany(ServerInstance->Config->GetSID(),"MODE",*params);
972         }
973         else if (event->GetEventID() == "send_opers")
974         {
975                 if (params->size() < 1)
976                         return;
977                 (*params)[0] = ":" + (*params)[0];
978                 Utils->DoOneToMany(ServerInstance->Config->GetSID(),"OPERNOTICE",*params);
979         }
980         else if (event->GetEventID() == "send_modeset")
981         {
982                 if (params->size() < 2)
983                         return;
984                 (*params)[1] = ":" + (*params)[1];
985                 Utils->DoOneToMany(ServerInstance->Config->GetSID(),"MODENOTICE",*params);
986         }
987         else if (event->GetEventID() == "send_snoset")
988         {
989                 if (params->size() < 2)
990                         return;
991                 (*params)[1] = ":" + (*params)[1];
992                 Utils->DoOneToMany(ServerInstance->Config->GetSID(),"SNONOTICE",*params);
993         }
994         else if (event->GetEventID() == "send_push")
995         {
996                 if (params->size() < 2)
997                         return;
998                         
999                 userrec *a = ServerInstance->FindNick((*params)[0]);
1000                         
1001                 if (!a)
1002                         return;
1003
1004                 (*params)[0] = a->uuid;
1005                 (*params)[1] = ":" + (*params)[1];
1006                 Utils->DoOneToOne(ServerInstance->Config->GetSID(), "PUSH", *params, a->server);
1007         }
1008 }
1009
1010 ModuleSpanningTree::~ModuleSpanningTree()
1011 {
1012         /* This will also free the listeners */
1013         delete Utils;
1014         if (SyncTimer)
1015                 ServerInstance->Timers->DelTimer(SyncTimer);
1016
1017         ServerInstance->Timers->DelTimer(RefreshTimer);
1018
1019         ServerInstance->Modules->DoneWithInterface("InspSocketHook");
1020 }
1021
1022 Version ModuleSpanningTree::GetVersion()
1023 {
1024         return Version(1,1,0,2,VF_VENDOR,API_VERSION);
1025 }
1026
1027 void ModuleSpanningTree::Implements(char* List)
1028 {
1029         List[I_OnPreCommand] = List[I_OnGetServerDescription] = List[I_OnUserInvite] = List[I_OnPostLocalTopicChange] = 1;
1030         List[I_OnWallops] = List[I_OnUserNotice] = List[I_OnUserMessage] = List[I_OnBackgroundTimer] = 1;
1031         List[I_OnUserJoin] = List[I_OnChangeHost] = List[I_OnChangeName] = List[I_OnUserPart] = List[I_OnUserConnect] = 1;
1032         List[I_OnUserQuit] = List[I_OnUserPostNick] = List[I_OnUserKick] = List[I_OnRemoteKill] = List[I_OnRehash] = 1;
1033         List[I_OnOper] = List[I_OnAddGLine] = List[I_OnAddZLine] = List[I_OnAddQLine] = List[I_OnAddELine] = 1;
1034         List[I_OnDelGLine] = List[I_OnDelZLine] = List[I_OnDelQLine] = List[I_OnDelELine] = List[I_ProtoSendMode] = List[I_OnMode] = 1;
1035         List[I_OnStats] = List[I_ProtoSendMetaData] = List[I_OnEvent] = List[I_OnSetAway] = List[I_OnCancelAway] = List[I_OnPostCommand] = 1;
1036 }
1037
1038 /* It is IMPORTANT that m_spanningtree is the last module in the chain
1039  * so that any activity it sees is FINAL, e.g. we arent going to send out
1040  * a NICK message before m_cloaking has finished putting the +x on the user,
1041  * etc etc.
1042  * Therefore, we return PRIORITY_LAST to make sure we end up at the END of
1043  * the module call queue.
1044  */
1045 Priority ModuleSpanningTree::Prioritize()
1046 {
1047         return PRIORITY_LAST;
1048 }
1049
1050 MODULE_INIT(ModuleSpanningTree)