1 /* +------------------------------------+
2 * | Inspire Internet Relay Chat Daemon |
3 * +------------------------------------+
5 * InspIRCd: (C) 2002-2007 InspIRCd Development Team
6 * See: http://www.inspircd.org/wiki/index.php/Credits
8 * This program is free but copyrighted software; see
9 * the file COPYING for details.
11 * ---------------------------------------------------
14 /* $ModDesc: Provides a spanning tree server link protocol */
16 #include "configreader.h"
20 #include "commands/cmd_whois.h"
21 #include "commands/cmd_stats.h"
26 #include "transport.h"
28 #include "m_spanningtree/main.h"
29 #include "m_spanningtree/utils.h"
30 #include "m_spanningtree/treeserver.h"
31 #include "m_spanningtree/link.h"
32 #include "m_spanningtree/treesocket.h"
36 class cmd_rconnect : public command_t
39 SpanningTreeUtilities* Utils;
41 cmd_rconnect (InspIRCd* Instance, Module* Callback, SpanningTreeUtilities* Util) : command_t(Instance, "RCONNECT", 'o', 2), Creator(Callback), Utils(Util)
43 this->source = "m_spanningtree.so";
44 syntax = "<remote-server-mask> <target-server-mask>";
47 CmdResult Handle (const char** parameters, int pcnt, userrec *user)
51 if (!Utils->FindServer(parameters[0]))
53 user->WriteServ("NOTICE %s :*** RCONNECT: Server \002%s\002 isn't connected to the network!", user->nick, parameters[0]);
57 user->WriteServ("NOTICE %s :*** RCONNECT: Sending remote connect to \002%s\002 to connect server \002%s\002.",user->nick,parameters[0],parameters[1]);
60 /* Is this aimed at our server? */
61 if (ServerInstance->MatchText(ServerInstance->Config->ServerName,parameters[0]))
63 /* Yes, initiate the given connect */
64 ServerInstance->SNO->WriteToSnoMask('l',"Remote CONNECT from %s matching \002%s\002, connecting server \002%s\002",user->nick,parameters[0],parameters[1]);
66 para[0] = parameters[1];
67 std::string original_command = std::string("CONNECT ") + parameters[1];
68 Creator->OnPreCommand("CONNECT", para, 1, user, true, original_command);
75 /** This class is used to resolve server hostnames during /connect and autoconnect.
76 * As of 1.1, the resolver system is seperated out from InspSocket, so we must do this
77 * resolver step first ourselves if we need it. This is totally nonblocking, and will
78 * callback to OnLookupComplete or OnError when completed. Once it has completed we
79 * will have an IP address which we can then use to continue our connection.
81 class ServernameResolver : public Resolver
84 /** A copy of the Link tag info for what we're connecting to.
85 * We take a copy, rather than using a pointer, just in case the
86 * admin takes the tag away and rehashes while the domain is resolving.
89 SpanningTreeUtilities* Utils;
91 ServernameResolver(Module* me, SpanningTreeUtilities* Util, InspIRCd* Instance, const std::string &hostname, Link x, bool &cached) : Resolver(Instance, hostname, DNS_QUERY_FORWARD, cached, me), MyLink(x), Utils(Util)
93 /* Nothing in here, folks */
96 void OnLookupComplete(const std::string &result, unsigned int ttl, bool cached)
98 /* Initiate the connection, now that we have an IP to use.
99 * Passing a hostname directly to InspSocket causes it to
100 * just bail and set its FD to -1.
102 TreeServer* CheckDupe = Utils->FindServer(MyLink.Name.c_str());
103 if (!CheckDupe) /* Check that nobody tried to connect it successfully while we were resolving */
106 if ((!MyLink.Hook.empty()) && (Utils->hooks.find(MyLink.Hook.c_str()) == Utils->hooks.end()))
109 TreeSocket* newsocket = new TreeSocket(this->Utils, ServerInstance, result,MyLink.Port,false,MyLink.Timeout ? MyLink.Timeout : 10,MyLink.Name.c_str(),
110 MyLink.Hook.empty() ? NULL : Utils->hooks[MyLink.Hook.c_str()]);
111 if (newsocket->GetFd() > -1)
117 /* Something barfed, show the opers */
118 ServerInstance->SNO->WriteToSnoMask('l',"CONNECT: Error connecting \002%s\002: %s.",MyLink.Name.c_str(),strerror(errno));
120 Utils->DoFailOver(&MyLink);
125 void OnError(ResolverError e, const std::string &errormessage)
128 ServerInstance->SNO->WriteToSnoMask('l',"CONNECT: Error connecting \002%s\002: Unable to resolve hostname - %s",MyLink.Name.c_str(),errormessage.c_str());
129 Utils->DoFailOver(&MyLink);
133 /** Create a timer which recurs every second, we inherit from InspTimer.
134 * InspTimer is only one-shot however, so at the end of each Tick() we simply
135 * insert another of ourselves into the pending queue :)
137 class TimeSyncTimer : public InspTimer
141 ModuleSpanningTree *Module;
143 TimeSyncTimer(InspIRCd *Instance, ModuleSpanningTree *Mod);
144 virtual void Tick(time_t TIME);
148 ModuleSpanningTree::ModuleSpanningTree(InspIRCd* Me)
149 : Module::Module(Me), max_local(0), max_global(0)
151 ServerInstance->UseInterface("InspSocketHook");
152 Utils = new SpanningTreeUtilities(Me, this);
153 command_rconnect = new cmd_rconnect(ServerInstance, this, Utils);
154 ServerInstance->AddCommand(command_rconnect);
155 if (Utils->EnableTimeSync)
157 SyncTimer = new TimeSyncTimer(ServerInstance, this);
158 ServerInstance->Timers->AddTimer(SyncTimer);
164 void ModuleSpanningTree::ShowLinks(TreeServer* Current, userrec* user, int hops)
166 std::string Parent = Utils->TreeRoot->GetName();
167 if (Current->GetParent())
169 Parent = Current->GetParent()->GetName();
171 for (unsigned int q = 0; q < Current->ChildCount(); q++)
173 if ((Utils->HideULines) && (ServerInstance->ULine(Current->GetChild(q)->GetName().c_str())))
177 ShowLinks(Current->GetChild(q),user,hops+1);
182 ShowLinks(Current->GetChild(q),user,hops+1);
185 /* Don't display the line if its a uline, hide ulines is on, and the user isnt an oper */
186 if ((Utils->HideULines) && (ServerInstance->ULine(Current->GetName().c_str())) && (!*user->oper))
188 user->WriteServ("364 %s %s %s :%d %s",user->nick,Current->GetName().c_str(),(Utils->FlatLinks && (!*user->oper)) ? ServerInstance->Config->ServerName : Parent.c_str(),(Utils->FlatLinks && (!*user->oper)) ? 0 : hops,Current->GetDesc().c_str());
191 int ModuleSpanningTree::CountLocalServs()
193 return Utils->TreeRoot->ChildCount();
196 int ModuleSpanningTree::CountServs()
198 return Utils->serverlist.size();
201 void ModuleSpanningTree::HandleLinks(const char** parameters, int pcnt, userrec* user)
203 ShowLinks(Utils->TreeRoot,user,0);
204 user->WriteServ("365 %s * :End of /LINKS list.",user->nick);
208 void ModuleSpanningTree::HandleLusers(const char** parameters, int pcnt, userrec* user)
210 unsigned int n_users = ServerInstance->UserCount();
212 /* Only update these when someone wants to see them, more efficient */
213 if ((unsigned int)ServerInstance->LocalUserCount() > max_local)
214 max_local = ServerInstance->LocalUserCount();
215 if (n_users > max_global)
216 max_global = n_users;
218 unsigned int ulined_count = 0;
219 unsigned int ulined_local_count = 0;
221 /* If ulined are hidden and we're not an oper, count the number of ulined servers hidden,
222 * locally and globally (locally means directly connected to us)
224 if ((Utils->HideULines) && (!*user->oper))
226 for (server_hash::iterator q = Utils->serverlist.begin(); q != Utils->serverlist.end(); q++)
228 if (ServerInstance->ULine(q->second->GetName().c_str()))
231 if (q->second->GetParent() == Utils->TreeRoot)
232 ulined_local_count++;
236 user->WriteServ("251 %s :There are %d users and %d invisible on %d servers",user->nick,n_users-ServerInstance->InvisibleUserCount(),ServerInstance->InvisibleUserCount(),ulined_count ? this->CountServs() - ulined_count : this->CountServs());
237 if (ServerInstance->OperCount())
238 user->WriteServ("252 %s %d :operator(s) online",user->nick,ServerInstance->OperCount());
239 if (ServerInstance->UnregisteredUserCount())
240 user->WriteServ("253 %s %d :unknown connections",user->nick,ServerInstance->UnregisteredUserCount());
241 if (ServerInstance->ChannelCount())
242 user->WriteServ("254 %s %d :channels formed",user->nick,ServerInstance->ChannelCount());
243 user->WriteServ("254 %s :I have %d clients and %d servers",user->nick,ServerInstance->LocalUserCount(),ulined_local_count ? this->CountLocalServs() - ulined_local_count : this->CountLocalServs());
244 user->WriteServ("265 %s :Current Local Users: %d Max: %d",user->nick,ServerInstance->LocalUserCount(),max_local);
245 user->WriteServ("266 %s :Current Global Users: %d Max: %d",user->nick,n_users,max_global);
249 // WARNING: NOT THREAD SAFE - DONT GET ANY SMART IDEAS.
250 void ModuleSpanningTree::ShowMap(TreeServer* Current, userrec* user, int depth, char matrix[128][80], float &totusers, float &totservers)
254 for (int t = 0; t < depth; t++)
256 matrix[line][t] = ' ';
258 // For Aligning, we need to work out exactly how deep this thing is, and produce
259 // a 'Spacer' String to compensate.
261 memset(spacer,' ',40);
262 if ((40 - Current->GetName().length() - depth) > 1) {
263 spacer[40 - Current->GetName().length() - depth] = '\0';
271 if (ServerInstance->clientlist->size() == 0) {
272 // If there are no users, WHO THE HELL DID THE /MAP?!?!?!
277 percent = ((float)Current->GetUserCount() / (float)ServerInstance->clientlist->size()) * 100;
279 snprintf(text, 80, "%s %s%5d [%5.2f%%]", Current->GetName().c_str(), spacer, Current->GetUserCount(), percent);
280 totusers += Current->GetUserCount();
282 strlcpy(&matrix[line][depth],text,80);
284 for (unsigned int q = 0; q < Current->ChildCount(); q++)
286 if ((Utils->HideULines) && (ServerInstance->ULine(Current->GetChild(q)->GetName().c_str())))
290 ShowMap(Current->GetChild(q),user,(Utils->FlatLinks && (!*user->oper)) ? depth : depth+2,matrix,totusers,totservers);
295 ShowMap(Current->GetChild(q),user,(Utils->FlatLinks && (!*user->oper)) ? depth : depth+2,matrix,totusers,totservers);
301 int ModuleSpanningTree::HandleMotd(const char** parameters, int pcnt, userrec* user)
305 /* Remote MOTD, the server is within the 1st parameter */
306 std::deque<std::string> params;
307 params.push_back(parameters[0]);
308 /* Send it out remotely, generate no reply yet */
309 TreeServer* s = Utils->FindServerMask(parameters[0]);
312 Utils->DoOneToOne(user->nick, "MOTD", params, s->GetName());
316 user->WriteServ( "402 %s %s :No such server", user->nick, parameters[0]);
323 int ModuleSpanningTree::HandleAdmin(const char** parameters, int pcnt, userrec* user)
327 /* Remote ADMIN, the server is within the 1st parameter */
328 std::deque<std::string> params;
329 params.push_back(parameters[0]);
330 /* Send it out remotely, generate no reply yet */
331 TreeServer* s = Utils->FindServerMask(parameters[0]);
334 Utils->DoOneToOne(user->nick, "ADMIN", params, s->GetName());
338 user->WriteServ( "402 %s %s :No such server", user->nick, parameters[0]);
345 int ModuleSpanningTree::HandleStats(const char** parameters, int pcnt, userrec* user)
349 /* Remote STATS, the server is within the 2nd parameter */
350 std::deque<std::string> params;
351 params.push_back(parameters[0]);
352 params.push_back(parameters[1]);
353 /* Send it out remotely, generate no reply yet */
354 TreeServer* s = Utils->FindServerMask(parameters[1]);
357 params[1] = s->GetName();
358 Utils->DoOneToOne(user->nick, "STATS", params, s->GetName());
362 user->WriteServ( "402 %s %s :No such server", user->nick, parameters[0]);
369 // Ok, prepare to be confused.
370 // After much mulling over how to approach this, it struck me that
371 // the 'usual' way of doing a /MAP isnt the best way. Instead of
372 // keeping track of a ton of ascii characters, and line by line
373 // under recursion working out where to place them using multiplications
374 // and divisons, we instead render the map onto a backplane of characters
375 // (a character matrix), then draw the branches as a series of "L" shapes
376 // from the nodes. This is not only friendlier on CPU it uses less stack.
377 void ModuleSpanningTree::HandleMap(const char** parameters, int pcnt, userrec* user)
379 // This array represents a virtual screen which we will
380 // "scratch" draw to, as the console device of an irc
381 // client does not provide for a proper terminal.
383 float totservers = 0;
384 char matrix[128][80];
385 for (unsigned int t = 0; t < 128; t++)
390 // The only recursive bit is called here.
391 ShowMap(Utils->TreeRoot,user,0,matrix,totusers,totservers);
392 // Process each line one by one. The algorithm has a limit of
393 // 128 servers (which is far more than a spanning tree should have
394 // anyway, so we're ok). This limit can be raised simply by making
395 // the character matrix deeper, 128 rows taking 10k of memory.
396 for (int l = 1; l < line; l++)
398 // scan across the line looking for the start of the
399 // servername (the recursive part of the algorithm has placed
400 // the servers at indented positions depending on what they
402 int first_nonspace = 0;
403 while (matrix[l][first_nonspace] == ' ')
408 // Draw the `- (corner) section: this may be overwritten by
409 // another L shape passing along the same vertical pane, becoming
410 // a |- (branch) section instead.
411 matrix[l][first_nonspace] = '-';
412 matrix[l][first_nonspace-1] = '`';
414 // Draw upwards until we hit the parent server, causing possibly
415 // other corners (`-) to become branches (|-)
416 while ((matrix[l2][first_nonspace-1] == ' ') || (matrix[l2][first_nonspace-1] == '`'))
418 matrix[l2][first_nonspace-1] = '|';
422 // dump the whole lot to the user. This is the easy bit, honest.
423 for (int t = 0; t < line; t++)
425 user->WriteServ("006 %s :%s",user->nick,&matrix[t][0]);
427 float avg_users = totusers / totservers;
428 user->WriteServ("270 %s :%.0f server%s and %.0f user%s, average %.2f users per server",user->nick,totservers,(totservers > 1 ? "s" : ""),totusers,(totusers > 1 ? "s" : ""),avg_users);
429 user->WriteServ("007 %s :End of /MAP",user->nick);
433 int ModuleSpanningTree::HandleSquit(const char** parameters, int pcnt, userrec* user)
435 TreeServer* s = Utils->FindServerMask(parameters[0]);
438 if (s == Utils->TreeRoot)
440 user->WriteServ("NOTICE %s :*** SQUIT: Foolish mortal, you cannot make a server SQUIT itself! (%s matches local server name)",user->nick,parameters[0]);
443 TreeSocket* sock = s->GetSocket();
446 ServerInstance->SNO->WriteToSnoMask('l',"SQUIT: Server \002%s\002 removed from network by %s",parameters[0],user->nick);
447 sock->Squit(s,std::string("Server quit by ") + user->GetFullRealHost());
448 ServerInstance->SE->DelFd(sock);
455 std::deque<std::string> params;
456 params.push_back(parameters[0]);
457 params.push_back(std::string(":Server quit by ") + user->GetFullRealHost());
458 Utils->DoOneToOne(user->nick, "RSQUIT", params, parameters[0]);
463 user->WriteServ("NOTICE %s :*** SQUIT: The server \002%s\002 does not exist on the network.",user->nick,parameters[0]);
468 int ModuleSpanningTree::HandleTime(const char** parameters, int pcnt, userrec* user)
470 if ((IS_LOCAL(user)) && (pcnt))
472 TreeServer* found = Utils->FindServerMask(parameters[0]);
475 // we dont' override for local server
476 if (found == Utils->TreeRoot)
479 std::deque<std::string> params;
480 params.push_back(found->GetName());
481 params.push_back(user->nick);
482 Utils->DoOneToOne(ServerInstance->Config->ServerName,"TIME",params,found->GetName());
486 user->WriteServ("402 %s %s :No such server",user->nick,parameters[0]);
492 int ModuleSpanningTree::HandleRemoteWhois(const char** parameters, int pcnt, userrec* user)
494 if ((IS_LOCAL(user)) && (pcnt > 1))
496 userrec* remote = ServerInstance->FindNick(parameters[1]);
497 if ((remote) && (remote->GetFd() < 0))
499 std::deque<std::string> params;
500 params.push_back(parameters[1]);
501 Utils->DoOneToOne(user->nick,"IDLE",params,remote->server);
506 user->WriteServ("401 %s %s :No such nick/channel",user->nick, parameters[1]);
507 user->WriteServ("318 %s %s :End of /WHOIS list.",user->nick, parameters[1]);
514 void ModuleSpanningTree::DoPingChecks(time_t curtime)
516 for (unsigned int j = 0; j < Utils->TreeRoot->ChildCount(); j++)
518 TreeServer* serv = Utils->TreeRoot->GetChild(j);
519 TreeSocket* sock = serv->GetSocket();
522 if (curtime >= serv->NextPingTime())
524 if (serv->AnsweredLastPing())
526 sock->WriteLine(std::string(":")+ServerInstance->Config->ServerName+" PING "+serv->GetName());
527 serv->SetNextPingTime(curtime + 60);
531 // they didnt answer, boot them
532 ServerInstance->SNO->WriteToSnoMask('l',"Server \002%s\002 pinged out",serv->GetName().c_str());
533 sock->Squit(serv,"Ping timeout");
534 ServerInstance->SE->DelFd(sock);
544 void ModuleSpanningTree::ConnectServer(Link* x)
547 /* Do we already have an IP? If so, no need to resolve it. */
548 if (insp_aton(x->IPAddr.c_str(), &binip) > 0)
550 /* Gave a hook, but it wasnt one we know */
551 if ((!x->Hook.empty()) && (Utils->hooks.find(x->Hook.c_str()) == Utils->hooks.end()))
553 TreeSocket* newsocket = new TreeSocket(Utils, ServerInstance, x->IPAddr,x->Port,false,x->Timeout ? x->Timeout : 10,x->Name.c_str(), x->Hook.empty() ? NULL : Utils->hooks[x->Hook.c_str()]);
554 if (newsocket->GetFd() > -1)
556 /* Handled automatically on success */
560 ServerInstance->SNO->WriteToSnoMask('l',"CONNECT: Error connecting \002%s\002: %s.",x->Name.c_str(),strerror(errno));
562 Utils->DoFailOver(x);
570 ServernameResolver* snr = new ServernameResolver((Module*)this, Utils, ServerInstance,x->IPAddr, *x, cached);
571 ServerInstance->AddResolver(snr, cached);
573 catch (ModuleException& e)
575 ServerInstance->SNO->WriteToSnoMask('l',"CONNECT: Error connecting \002%s\002: %s.",x->Name.c_str(), e.GetReason());
576 Utils->DoFailOver(x);
581 void ModuleSpanningTree::AutoConnectServers(time_t curtime)
583 for (std::vector<Link>::iterator x = Utils->LinkBlocks.begin(); x < Utils->LinkBlocks.end(); x++)
585 if ((x->AutoConnect) && (curtime >= x->NextConnectTime))
587 x->NextConnectTime = curtime + x->AutoConnect;
588 TreeServer* CheckDupe = Utils->FindServer(x->Name.c_str());
589 if (x->FailOver.length())
591 TreeServer* CheckFailOver = Utils->FindServer(x->FailOver.c_str());
594 /* The failover for this server is currently a member of the network.
595 * The failover probably succeeded, where the main link did not.
596 * Don't try the main link until the failover is gone again.
603 // an autoconnected server is not connected. Check if its time to connect it
604 ServerInstance->SNO->WriteToSnoMask('l',"AUTOCONNECT: Auto-connecting server \002%s\002 (%lu seconds until next attempt)",x->Name.c_str(),x->AutoConnect);
605 this->ConnectServer(&(*x));
611 int ModuleSpanningTree::HandleVersion(const char** parameters, int pcnt, userrec* user)
613 // we've already checked if pcnt > 0, so this is safe
614 TreeServer* found = Utils->FindServerMask(parameters[0]);
617 std::string Version = found->GetVersion();
618 user->WriteServ("351 %s :%s",user->nick,Version.c_str());
619 if (found == Utils->TreeRoot)
621 ServerInstance->Config->Send005(user);
626 user->WriteServ("402 %s %s :No such server",user->nick,parameters[0]);
631 int ModuleSpanningTree::HandleConnect(const char** parameters, int pcnt, userrec* user)
633 for (std::vector<Link>::iterator x = Utils->LinkBlocks.begin(); x < Utils->LinkBlocks.end(); x++)
635 if (ServerInstance->MatchText(x->Name.c_str(),parameters[0]))
637 TreeServer* CheckDupe = Utils->FindServer(x->Name.c_str());
640 user->WriteServ("NOTICE %s :*** CONNECT: Connecting to server: \002%s\002 (%s:%d)",user->nick,x->Name.c_str(),(x->HiddenFromStats ? "<hidden>" : x->IPAddr.c_str()),x->Port);
641 ConnectServer(&(*x));
646 user->WriteServ("NOTICE %s :*** CONNECT: Server \002%s\002 already exists on the network and is connected via \002%s\002",user->nick,x->Name.c_str(),CheckDupe->GetParent()->GetName().c_str());
651 user->WriteServ("NOTICE %s :*** CONNECT: No server matching \002%s\002 could be found in the config file.",user->nick,parameters[0]);
655 void ModuleSpanningTree::BroadcastTimeSync()
657 std::deque<std::string> params;
658 params.push_back(ConvToStr(ServerInstance->Time(true)));
659 Utils->DoOneToMany(Utils->TreeRoot->GetName(), "TIMESET", params);
662 int ModuleSpanningTree::OnStats(char statschar, userrec* user, string_list &results)
664 if ((statschar == 'c') || (statschar == 'n'))
666 for (unsigned int i = 0; i < Utils->LinkBlocks.size(); i++)
668 results.push_back(std::string(ServerInstance->Config->ServerName)+" 213 "+user->nick+" "+statschar+" *@"+(Utils->LinkBlocks[i].HiddenFromStats ? "<hidden>" : Utils->LinkBlocks[i].IPAddr)+" * "+Utils->LinkBlocks[i].Name.c_str()+" "+ConvToStr(Utils->LinkBlocks[i].Port)+" "+(Utils->LinkBlocks[i].Hook.empty() ? "plaintext" : Utils->LinkBlocks[i].Hook)+" "+(Utils->LinkBlocks[i].AutoConnect ? 'a' : '-')+'s');
669 if (statschar == 'c')
670 results.push_back(std::string(ServerInstance->Config->ServerName)+" 244 "+user->nick+" H * * "+Utils->LinkBlocks[i].Name.c_str());
672 results.push_back(std::string(ServerInstance->Config->ServerName)+" 219 "+user->nick+" "+statschar+" :End of /STATS report");
673 ServerInstance->SNO->WriteToSnoMask('t',"Notice: %s '%c' requested by %s (%s@%s)",(!strcmp(user->server,ServerInstance->Config->ServerName) ? "Stats" : "Remote stats"),statschar,user->nick,user->ident,user->host);
679 int ModuleSpanningTree::OnPreCommand(const std::string &command, const char** parameters, int pcnt, userrec *user, bool validated, const std::string &original_line)
681 /* If the command doesnt appear to be valid, we dont want to mess with it. */
684 if (command == "CONNECT")
686 return this->HandleConnect(parameters,pcnt,user);
688 else if (command == "STATS")
690 return this->HandleStats(parameters,pcnt,user);
692 else if (command == "MOTD")
694 return this->HandleMotd(parameters,pcnt,user);
696 else if (command == "ADMIN")
698 return this->HandleAdmin(parameters,pcnt,user);
700 else if (command == "SQUIT")
702 return this->HandleSquit(parameters,pcnt,user);
704 else if (command == "MAP")
706 this->HandleMap(parameters,pcnt,user);
709 else if ((command == "TIME") && (pcnt > 0))
711 return this->HandleTime(parameters,pcnt,user);
713 else if (command == "LUSERS")
715 this->HandleLusers(parameters,pcnt,user);
718 else if (command == "LINKS")
720 this->HandleLinks(parameters,pcnt,user);
723 else if (command == "WHOIS")
728 return this->HandleRemoteWhois(parameters,pcnt,user);
731 else if ((command == "VERSION") && (pcnt > 0))
733 this->HandleVersion(parameters,pcnt,user);
739 void ModuleSpanningTree::OnPostCommand(const std::string &command, const char** parameters, int pcnt, userrec *user, CmdResult result, const std::string &original_line)
741 if ((result == CMD_SUCCESS) && (ServerInstance->IsValidModuleCommand(command, pcnt, user)))
743 // this bit of code cleverly routes all module commands
744 // to all remote severs *automatically* so that modules
745 // can just handle commands locally, without having
746 // to have any special provision in place for remote
747 // commands and linking protocols.
748 std::deque<std::string> params;
750 for (int j = 0; j < pcnt; j++)
752 if (strchr(parameters[j],' '))
754 params.push_back(":" + std::string(parameters[j]));
758 params.push_back(std::string(parameters[j]));
761 Utils->DoOneToMany(user->nick,command,params);
765 void ModuleSpanningTree::OnGetServerDescription(const std::string &servername,std::string &description)
767 TreeServer* s = Utils->FindServer(servername);
770 description = s->GetDesc();
774 void ModuleSpanningTree::OnUserInvite(userrec* source,userrec* dest,chanrec* channel)
776 if (IS_LOCAL(source))
778 std::deque<std::string> params;
779 params.push_back(dest->nick);
780 params.push_back(channel->name);
781 Utils->DoOneToMany(source->nick,"INVITE",params);
785 void ModuleSpanningTree::OnPostLocalTopicChange(userrec* user, chanrec* chan, const std::string &topic)
787 std::deque<std::string> params;
788 params.push_back(chan->name);
789 params.push_back(":"+topic);
790 Utils->DoOneToMany(user->nick,"TOPIC",params);
793 void ModuleSpanningTree::OnWallops(userrec* user, const std::string &text)
797 std::deque<std::string> params;
798 params.push_back(":"+text);
799 Utils->DoOneToMany(user->nick,"WALLOPS",params);
803 void ModuleSpanningTree::OnUserNotice(userrec* user, void* dest, int target_type, const std::string &text, char status, const CUList &exempt_list)
805 if (target_type == TYPE_USER)
807 userrec* d = (userrec*)dest;
808 if ((d->GetFd() < 0) && (IS_LOCAL(user)))
810 std::deque<std::string> params;
812 params.push_back(d->nick);
813 params.push_back(":"+text);
814 Utils->DoOneToOne(user->nick,"NOTICE",params,d->server);
817 else if (target_type == TYPE_CHANNEL)
821 chanrec *c = (chanrec*)dest;
824 std::string cname = c->name;
826 cname = status + cname;
828 Utils->GetListOfServersForChannel(c,list,status,exempt_list);
829 for (TreeServerList::iterator i = list.begin(); i != list.end(); i++)
831 TreeSocket* Sock = i->second->GetSocket();
833 Sock->WriteLine(":"+std::string(user->nick)+" NOTICE "+cname+" :"+text);
838 else if (target_type == TYPE_SERVER)
842 char* target = (char*)dest;
843 std::deque<std::string> par;
844 par.push_back(target);
845 par.push_back(":"+text);
846 Utils->DoOneToMany(user->nick,"NOTICE",par);
851 void ModuleSpanningTree::OnUserMessage(userrec* user, void* dest, int target_type, const std::string &text, char status, const CUList &exempt_list)
853 if (target_type == TYPE_USER)
855 // route private messages which are targetted at clients only to the server
856 // which needs to receive them
857 userrec* d = (userrec*)dest;
858 if ((d->GetFd() < 0) && (IS_LOCAL(user)))
860 std::deque<std::string> params;
862 params.push_back(d->nick);
863 params.push_back(":"+text);
864 Utils->DoOneToOne(user->nick,"PRIVMSG",params,d->server);
867 else if (target_type == TYPE_CHANNEL)
871 chanrec *c = (chanrec*)dest;
874 std::string cname = c->name;
876 cname = status + cname;
878 Utils->GetListOfServersForChannel(c,list,status,exempt_list);
879 for (TreeServerList::iterator i = list.begin(); i != list.end(); i++)
881 TreeSocket* Sock = i->second->GetSocket();
883 Sock->WriteLine(":"+std::string(user->nick)+" PRIVMSG "+cname+" :"+text);
888 else if (target_type == TYPE_SERVER)
892 char* target = (char*)dest;
893 std::deque<std::string> par;
894 par.push_back(target);
895 par.push_back(":"+text);
896 Utils->DoOneToMany(user->nick,"PRIVMSG",par);
901 void ModuleSpanningTree::OnBackgroundTimer(time_t curtime)
903 AutoConnectServers(curtime);
904 DoPingChecks(curtime);
907 void ModuleSpanningTree::OnUserJoin(userrec* user, chanrec* channel)
909 // Only do this for local users
912 if (channel->GetUserCounter() == 1)
914 std::deque<std::string> params;
915 // set up their permissions and the channel TS with FJOIN.
916 // All users are FJOINed now, because a module may specify
917 // new joining permissions for the user.
918 params.push_back(channel->name);
919 params.push_back(ConvToStr(channel->age));
920 params.push_back(std::string(channel->GetAllPrefixChars(user))+","+std::string(user->nick));
921 Utils->DoOneToMany(ServerInstance->Config->ServerName,"FJOIN",params);
922 /* First user in, sync the modes for the channel */
924 /* This is safe, all inspircd servers default to +nt */
925 params.push_back("+nt");
926 Utils->DoOneToMany(ServerInstance->Config->ServerName,"FMODE",params);
930 std::deque<std::string> params;
931 params.push_back(channel->name);
932 params.push_back(ConvToStr(channel->age));
933 Utils->DoOneToMany(user->nick,"JOIN",params);
938 void ModuleSpanningTree::OnChangeHost(userrec* user, const std::string &newhost)
940 // only occurs for local clients
941 if (user->registered != REG_ALL)
943 std::deque<std::string> params;
944 params.push_back(newhost);
945 Utils->DoOneToMany(user->nick,"FHOST",params);
948 void ModuleSpanningTree::OnChangeName(userrec* user, const std::string &gecos)
950 // only occurs for local clients
951 if (user->registered != REG_ALL)
953 std::deque<std::string> params;
954 params.push_back(gecos);
955 Utils->DoOneToMany(user->nick,"FNAME",params);
958 void ModuleSpanningTree::OnUserPart(userrec* user, chanrec* channel, const std::string &partmessage)
962 std::deque<std::string> params;
963 params.push_back(channel->name);
964 if (partmessage != "")
965 params.push_back(":"+partmessage);
966 Utils->DoOneToMany(user->nick,"PART",params);
970 void ModuleSpanningTree::OnUserConnect(userrec* user)
975 std::deque<std::string> params;
976 snprintf(agestr,MAXBUF,"%lu",(unsigned long)user->age);
977 params.push_back(agestr);
978 params.push_back(user->nick);
979 params.push_back(user->host);
980 params.push_back(user->dhost);
981 params.push_back(user->ident);
982 params.push_back("+"+std::string(user->FormatModes()));
983 params.push_back(user->GetIPString());
984 params.push_back(":"+std::string(user->fullname));
985 Utils->DoOneToMany(ServerInstance->Config->ServerName,"NICK",params);
986 // User is Local, change needs to be reflected!
987 TreeServer* SourceServer = Utils->FindServer(user->server);
990 SourceServer->AddUserCount();
995 void ModuleSpanningTree::OnUserQuit(userrec* user, const std::string &reason)
997 if ((IS_LOCAL(user)) && (user->registered == REG_ALL))
999 std::deque<std::string> params;
1000 params.push_back(":"+reason);
1001 Utils->DoOneToMany(user->nick,"QUIT",params);
1003 // Regardless, We need to modify the user Counts..
1004 TreeServer* SourceServer = Utils->FindServer(user->server);
1007 SourceServer->DelUserCount();
1011 void ModuleSpanningTree::OnUserPostNick(userrec* user, const std::string &oldnick)
1015 std::deque<std::string> params;
1016 params.push_back(user->nick);
1017 Utils->DoOneToMany(oldnick,"NICK",params);
1021 void ModuleSpanningTree::OnUserKick(userrec* source, userrec* user, chanrec* chan, const std::string &reason)
1023 if ((source) && (IS_LOCAL(source)))
1025 std::deque<std::string> params;
1026 params.push_back(chan->name);
1027 params.push_back(user->nick);
1028 params.push_back(":"+reason);
1029 Utils->DoOneToMany(source->nick,"KICK",params);
1033 std::deque<std::string> params;
1034 params.push_back(chan->name);
1035 params.push_back(user->nick);
1036 params.push_back(":"+reason);
1037 Utils->DoOneToMany(ServerInstance->Config->ServerName,"KICK",params);
1041 void ModuleSpanningTree::OnRemoteKill(userrec* source, userrec* dest, const std::string &reason)
1043 std::deque<std::string> params;
1044 params.push_back(dest->nick);
1045 params.push_back(":"+reason);
1046 Utils->DoOneToMany(source->nick,"KILL",params);
1049 void ModuleSpanningTree::OnRehash(userrec* user, const std::string ¶meter)
1051 if (parameter != "")
1053 std::deque<std::string> params;
1054 params.push_back(parameter);
1055 Utils->DoOneToMany(user ? user->nick : ServerInstance->Config->ServerName, "REHASH", params);
1057 if (ServerInstance->MatchText(ServerInstance->Config->ServerName,parameter))
1059 ServerInstance->WriteOpers("*** Remote rehash initiated locally by \002%s\002", user ? user->nick : ServerInstance->Config->ServerName);
1060 ServerInstance->RehashServer();
1063 Utils->ReadConfiguration(false);
1064 InitializeDisabledCommands(ServerInstance->Config->DisabledCommands, ServerInstance);
1067 // note: the protocol does not allow direct umode +o except
1068 // via NICK with 8 params. sending OPERTYPE infers +o modechange
1070 void ModuleSpanningTree::OnOper(userrec* user, const std::string &opertype)
1074 std::deque<std::string> params;
1075 params.push_back(opertype);
1076 Utils->DoOneToMany(user->nick,"OPERTYPE",params);
1080 void ModuleSpanningTree::OnLine(userrec* source, const std::string &host, bool adding, char linetype, long duration, const std::string &reason)
1084 /* Server-set lines */
1086 snprintf(data,MAXBUF,"%c %s %s %lu %lu :%s", linetype, host.c_str(), ServerInstance->Config->ServerName, (unsigned long)ServerInstance->Time(false),
1087 (unsigned long)duration, reason.c_str());
1088 std::deque<std::string> params;
1089 params.push_back(data);
1090 Utils->DoOneToMany(ServerInstance->Config->ServerName, "ADDLINE", params);
1094 if (IS_LOCAL(source))
1097 snprintf(type,8,"%cLINE",linetype);
1098 std::string stype = type;
1101 char sduration[MAXBUF];
1102 snprintf(sduration,MAXBUF,"%ld",duration);
1103 std::deque<std::string> params;
1104 params.push_back(host);
1105 params.push_back(sduration);
1106 params.push_back(":"+reason);
1107 Utils->DoOneToMany(source->nick,stype,params);
1111 std::deque<std::string> params;
1112 params.push_back(host);
1113 Utils->DoOneToMany(source->nick,stype,params);
1119 void ModuleSpanningTree::OnAddGLine(long duration, userrec* source, const std::string &reason, const std::string &hostmask)
1121 OnLine(source,hostmask,true,'G',duration,reason);
1124 void ModuleSpanningTree::OnAddZLine(long duration, userrec* source, const std::string &reason, const std::string &ipmask)
1126 OnLine(source,ipmask,true,'Z',duration,reason);
1129 void ModuleSpanningTree::OnAddQLine(long duration, userrec* source, const std::string &reason, const std::string &nickmask)
1131 OnLine(source,nickmask,true,'Q',duration,reason);
1134 void ModuleSpanningTree::OnAddELine(long duration, userrec* source, const std::string &reason, const std::string &hostmask)
1136 OnLine(source,hostmask,true,'E',duration,reason);
1139 void ModuleSpanningTree::OnDelGLine(userrec* source, const std::string &hostmask)
1141 OnLine(source,hostmask,false,'G',0,"");
1144 void ModuleSpanningTree::OnDelZLine(userrec* source, const std::string &ipmask)
1146 OnLine(source,ipmask,false,'Z',0,"");
1149 void ModuleSpanningTree::OnDelQLine(userrec* source, const std::string &nickmask)
1151 OnLine(source,nickmask,false,'Q',0,"");
1154 void ModuleSpanningTree::OnDelELine(userrec* source, const std::string &hostmask)
1156 OnLine(source,hostmask,false,'E',0,"");
1159 void ModuleSpanningTree::OnMode(userrec* user, void* dest, int target_type, const std::string &text)
1161 if ((IS_LOCAL(user)) && (user->registered == REG_ALL))
1163 if (target_type == TYPE_USER)
1165 userrec* u = (userrec*)dest;
1166 std::deque<std::string> params;
1167 params.push_back(u->nick);
1168 params.push_back(text);
1169 Utils->DoOneToMany(user->nick,"MODE",params);
1173 chanrec* c = (chanrec*)dest;
1174 std::deque<std::string> params;
1175 params.push_back(c->name);
1176 params.push_back(text);
1177 Utils->DoOneToMany(user->nick,"MODE",params);
1182 void ModuleSpanningTree::OnSetAway(userrec* user)
1186 std::deque<std::string> params;
1187 params.push_back(":"+std::string(user->awaymsg));
1188 Utils->DoOneToMany(user->nick,"AWAY",params);
1192 void ModuleSpanningTree::OnCancelAway(userrec* user)
1196 std::deque<std::string> params;
1198 Utils->DoOneToMany(user->nick,"AWAY",params);
1202 void ModuleSpanningTree::ProtoSendMode(void* opaque, int target_type, void* target, const std::string &modeline)
1204 TreeSocket* s = (TreeSocket*)opaque;
1207 if (target_type == TYPE_USER)
1209 userrec* u = (userrec*)target;
1210 s->WriteLine(std::string(":")+ServerInstance->Config->ServerName+" FMODE "+u->nick+" "+ConvToStr(u->age)+" "+modeline);
1214 chanrec* c = (chanrec*)target;
1215 s->WriteLine(std::string(":")+ServerInstance->Config->ServerName+" FMODE "+c->name+" "+ConvToStr(c->age)+" "+modeline);
1220 void ModuleSpanningTree::ProtoSendMetaData(void* opaque, int target_type, void* target, const std::string &extname, const std::string &extdata)
1222 TreeSocket* s = (TreeSocket*)opaque;
1225 if (target_type == TYPE_USER)
1227 userrec* u = (userrec*)target;
1228 s->WriteLine(std::string(":")+ServerInstance->Config->ServerName+" METADATA "+u->nick+" "+extname+" :"+extdata);
1230 else if (target_type == TYPE_CHANNEL)
1232 chanrec* c = (chanrec*)target;
1233 s->WriteLine(std::string(":")+ServerInstance->Config->ServerName+" METADATA "+c->name+" "+extname+" :"+extdata);
1236 if (target_type == TYPE_OTHER)
1238 s->WriteLine(std::string(":")+ServerInstance->Config->ServerName+" METADATA * "+extname+" :"+extdata);
1242 void ModuleSpanningTree::OnEvent(Event* event)
1244 std::deque<std::string>* params = (std::deque<std::string>*)event->GetData();
1245 if (event->GetEventID() == "send_metadata")
1247 if (params->size() < 3)
1249 (*params)[2] = ":" + (*params)[2];
1250 Utils->DoOneToMany(ServerInstance->Config->ServerName,"METADATA",*params);
1252 else if (event->GetEventID() == "send_topic")
1254 if (params->size() < 2)
1256 (*params)[1] = ":" + (*params)[1];
1257 params->insert(params->begin() + 1,ServerInstance->Config->ServerName);
1258 params->insert(params->begin() + 1,ConvToStr(ServerInstance->Time(true)));
1259 Utils->DoOneToMany(ServerInstance->Config->ServerName,"FTOPIC",*params);
1261 else if (event->GetEventID() == "send_mode")
1263 if (params->size() < 2)
1265 // Insert the TS value of the object, either userrec or chanrec
1267 userrec* a = ServerInstance->FindNick((*params)[0]);
1274 chanrec* a = ServerInstance->FindChan((*params)[0]);
1280 params->insert(params->begin() + 1,ConvToStr(ourTS));
1281 Utils->DoOneToMany(ServerInstance->Config->ServerName,"FMODE",*params);
1283 else if (event->GetEventID() == "send_mode_explicit")
1285 if (params->size() < 2)
1287 Utils->DoOneToMany(ServerInstance->Config->ServerName,"MODE",*params);
1289 else if (event->GetEventID() == "send_opers")
1291 if (params->size() < 1)
1293 (*params)[0] = ":" + (*params)[0];
1294 Utils->DoOneToMany(ServerInstance->Config->ServerName,"OPERNOTICE",*params);
1296 else if (event->GetEventID() == "send_modeset")
1298 if (params->size() < 2)
1300 (*params)[1] = ":" + (*params)[1];
1301 Utils->DoOneToMany(ServerInstance->Config->ServerName,"MODENOTICE",*params);
1303 else if (event->GetEventID() == "send_snoset")
1305 if (params->size() < 2)
1307 (*params)[1] = ":" + (*params)[1];
1308 Utils->DoOneToMany(ServerInstance->Config->ServerName,"SNONOTICE",*params);
1310 else if (event->GetEventID() == "send_push")
1312 if (params->size() < 2)
1315 userrec *a = ServerInstance->FindNick((*params)[0]);
1320 (*params)[1] = ":" + (*params)[1];
1321 Utils->DoOneToOne(ServerInstance->Config->ServerName, "PUSH", *params, a->server);
1325 ModuleSpanningTree::~ModuleSpanningTree()
1327 /* This will also free the listeners */
1330 ServerInstance->Timers->DelTimer(SyncTimer);
1332 ServerInstance->DoneWithInterface("InspSocketHook");
1335 Version ModuleSpanningTree::GetVersion()
1337 return Version(1,1,0,2,VF_VENDOR,API_VERSION);
1340 void ModuleSpanningTree::Implements(char* List)
1342 List[I_OnPreCommand] = List[I_OnGetServerDescription] = List[I_OnUserInvite] = List[I_OnPostLocalTopicChange] = 1;
1343 List[I_OnWallops] = List[I_OnUserNotice] = List[I_OnUserMessage] = List[I_OnBackgroundTimer] = 1;
1344 List[I_OnUserJoin] = List[I_OnChangeHost] = List[I_OnChangeName] = List[I_OnUserPart] = List[I_OnUserConnect] = 1;
1345 List[I_OnUserQuit] = List[I_OnUserPostNick] = List[I_OnUserKick] = List[I_OnRemoteKill] = List[I_OnRehash] = 1;
1346 List[I_OnOper] = List[I_OnAddGLine] = List[I_OnAddZLine] = List[I_OnAddQLine] = List[I_OnAddELine] = 1;
1347 List[I_OnDelGLine] = List[I_OnDelZLine] = List[I_OnDelQLine] = List[I_OnDelELine] = List[I_ProtoSendMode] = List[I_OnMode] = 1;
1348 List[I_OnStats] = List[I_ProtoSendMetaData] = List[I_OnEvent] = List[I_OnSetAway] = List[I_OnCancelAway] = List[I_OnPostCommand] = 1;
1351 /* It is IMPORTANT that m_spanningtree is the last module in the chain
1352 * so that any activity it sees is FINAL, e.g. we arent going to send out
1353 * a NICK message before m_cloaking has finished putting the +x on the user,
1355 * Therefore, we return PRIORITY_LAST to make sure we end up at the END of
1356 * the module call queue.
1358 Priority ModuleSpanningTree::Prioritize()
1360 return PRIORITY_LAST;
1363 TimeSyncTimer::TimeSyncTimer(InspIRCd *Inst, ModuleSpanningTree *Mod) : InspTimer(43200, Inst->Time(), true), Instance(Inst), Module(Mod)
1367 void TimeSyncTimer::Tick(time_t TIME)
1369 Module->BroadcastTimeSync();
1373 class ModuleSpanningTreeFactory : public ModuleFactory
1376 ModuleSpanningTreeFactory()
1380 ~ModuleSpanningTreeFactory()
1384 virtual Module * CreateModule(InspIRCd* Me)
1386 return new ModuleSpanningTree(Me);
1392 extern "C" void * init_module( void )
1394 return new ModuleSpanningTreeFactory;