-/* +------------------------------------+\r * | Inspire Internet Relay Chat Daemon |\r * +------------------------------------+\r *\r * InspIRCd: (C) 2002-2007 InspIRCd Development Team\r * See: http://www.inspircd.org/wiki/index.php/Credits\r *\r * This program is free but copyrighted software; see\r * the file COPYING for details.\r *\r * ---------------------------------------------------\r */\r\r/* $ModDesc: Provides a spanning tree server link protocol */\r\r#include "inspircd.h"\r#include "configreader.h"\r#include "users.h"\r#include "channels.h"\r#include "modules.h"\r#include "commands/cmd_whois.h"\r#include "commands/cmd_stats.h"\r#include "socket.h"\r#include "wildcard.h"\r#include "xline.h"\r#include "transport.h"\r\r#include "m_spanningtree/timesynctimer.h"\r#include "m_spanningtree/resolvers.h"\r#include "m_spanningtree/main.h"\r#include "m_spanningtree/utils.h"\r#include "m_spanningtree/treeserver.h"\r#include "m_spanningtree/link.h"\r#include "m_spanningtree/treesocket.h"\r#include "m_spanningtree/rconnect.h"\r#include "m_spanningtree/rsquit.h"\r\r/* $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 */\r\rModuleSpanningTree::ModuleSpanningTree(InspIRCd* Me)\r : Module(Me), max_local(0), max_global(0)\r{\r ServerInstance->UseInterface("InspSocketHook");\r Utils = new SpanningTreeUtilities(Me, this);\r command_rconnect = new cmd_rconnect(ServerInstance, this, Utils);\r ServerInstance->AddCommand(command_rconnect);\r command_rsquit = new cmd_rsquit(ServerInstance, this, Utils);\r ServerInstance->AddCommand(command_rsquit);\r if (Utils->EnableTimeSync)\r {\r SyncTimer = new TimeSyncTimer(ServerInstance, this);\r ServerInstance->Timers->AddTimer(SyncTimer);\r }\r else\r SyncTimer = NULL;\r\r RefreshTimer = new CacheRefreshTimer(ServerInstance, Utils);\r ServerInstance->Timers->AddTimer(RefreshTimer);\r}\r\rvoid ModuleSpanningTree::ShowLinks(TreeServer* Current, userrec* user, int hops)\r{\r std::string Parent = Utils->TreeRoot->GetName();\r if (Current->GetParent())\r {\r Parent = Current->GetParent()->GetName();\r }\r for (unsigned int q = 0; q < Current->ChildCount(); q++)\r {\r if ((Current->GetChild(q)->Hidden) || ((Utils->HideULines) && (ServerInstance->ULine(Current->GetChild(q)->GetName().c_str()))))\r {\r if (*user->oper)\r {\r ShowLinks(Current->GetChild(q),user,hops+1);\r }\r }\r else\r {\r ShowLinks(Current->GetChild(q),user,hops+1);\r }\r }\r /* Don't display the line if its a uline, hide ulines is on, and the user isnt an oper */\r if ((Utils->HideULines) && (ServerInstance->ULine(Current->GetName().c_str())) && (!IS_OPER(user)))\r return;\r /* Or if the server is hidden and they're not an oper */\r else if ((Current->Hidden) && (!IS_OPER(user)))\r return;\r\r user->WriteServ("364 %s %s %s :%d %s", user->nick,Current->GetName().c_str(),\r (Utils->FlatLinks && (!IS_OPER(user))) ? ServerInstance->Config->ServerName : Parent.c_str(),\r (Utils->FlatLinks && (!IS_OPER(user))) ? 0 : hops,\r Current->GetDesc().c_str());\r}\r\rint ModuleSpanningTree::CountLocalServs()\r{\r return Utils->TreeRoot->ChildCount();\r}\r\rint ModuleSpanningTree::CountServs()\r{\r return Utils->serverlist.size();\r}\r\rvoid ModuleSpanningTree::HandleLinks(const char** parameters, int pcnt, userrec* user)\r{\r ShowLinks(Utils->TreeRoot,user,0);\r user->WriteServ("365 %s * :End of /LINKS list.",user->nick);\r return;\r}\r\rvoid ModuleSpanningTree::HandleLusers(const char** parameters, int pcnt, userrec* user)\r{\r unsigned int n_users = ServerInstance->UserCount();\r\r /* Only update these when someone wants to see them, more efficient */\r if ((unsigned int)ServerInstance->LocalUserCount() > max_local)\r max_local = ServerInstance->LocalUserCount();\r if (n_users > max_global)\r max_global = n_users;\r\r unsigned int ulined_count = 0;\r unsigned int ulined_local_count = 0;\r\r /* If ulined are hidden and we're not an oper, count the number of ulined servers hidden,\r * locally and globally (locally means directly connected to us)\r */\r if ((Utils->HideULines) && (!*user->oper))\r {\r for (server_hash::iterator q = Utils->serverlist.begin(); q != Utils->serverlist.end(); q++)\r {\r if (ServerInstance->ULine(q->second->GetName().c_str()))\r {\r ulined_count++;\r if (q->second->GetParent() == Utils->TreeRoot)\r ulined_local_count++;\r }\r }\r }\r 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());\r if (ServerInstance->OperCount())\r user->WriteServ("252 %s %d :operator(s) online",user->nick,ServerInstance->OperCount());\r if (ServerInstance->UnregisteredUserCount())\r user->WriteServ("253 %s %d :unknown connections",user->nick,ServerInstance->UnregisteredUserCount());\r if (ServerInstance->ChannelCount())\r user->WriteServ("254 %s %d :channels formed",user->nick,ServerInstance->ChannelCount());\r 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());\r user->WriteServ("265 %s :Current Local Users: %d Max: %d",user->nick,ServerInstance->LocalUserCount(),max_local);\r user->WriteServ("266 %s :Current Global Users: %d Max: %d",user->nick,n_users,max_global);\r return;\r}\r\rstd::string ModuleSpanningTree::TimeToStr(time_t secs)\r{\r time_t mins_up = secs / 60;\r time_t hours_up = mins_up / 60;\r time_t days_up = hours_up / 24;\r secs = secs % 60;\r mins_up = mins_up % 60;\r hours_up = hours_up % 24;\r return ((days_up ? (ConvToStr(days_up) + "d") : std::string(""))\r + (hours_up ? (ConvToStr(hours_up) + "h") : std::string(""))\r + (mins_up ? (ConvToStr(mins_up) + "m") : std::string(""))\r + ConvToStr(secs) + "s");\r}\r\rconst std::string ModuleSpanningTree::MapOperInfo(TreeServer* Current)\r{\r time_t secs_up = ServerInstance->Time() - Current->age;\r return (" [Up: " + TimeToStr(secs_up) + " Lag: "+ConvToStr(Current->rtt)+"s]");\r}\r\r// WARNING: NOT THREAD SAFE - DONT GET ANY SMART IDEAS.\rvoid ModuleSpanningTree::ShowMap(TreeServer* Current, userrec* user, int depth, char matrix[128][128], float &totusers, float &totservers)\r{\r if (line < 128)\r {\r for (int t = 0; t < depth; t++)\r {\r matrix[line][t] = ' ';\r }\r // For Aligning, we need to work out exactly how deep this thing is, and produce\r // a 'Spacer' String to compensate.\r char spacer[40];\r memset(spacer,' ',40);\r if ((40 - Current->GetName().length() - depth) > 1) {\r spacer[40 - Current->GetName().length() - depth] = '\0';\r }\r else\r {\r spacer[5] = '\0';\r }\r float percent;\r char text[128];\r /* Neat and tidy default values, as we're dealing with a matrix not a simple string */\r memset(text, 0, 128);\r\r if (ServerInstance->clientlist->size() == 0) {\r // If there are no users, WHO THE HELL DID THE /MAP?!?!?!\r percent = 0;\r }\r else\r {\r percent = ((float)Current->GetUserCount() / (float)ServerInstance->clientlist->size()) * 100;\r }\r const std::string operdata = IS_OPER(user) ? MapOperInfo(Current) : "";\r snprintf(text, 126, "%s %s%5d [%5.2f%%]%s", Current->GetName().c_str(), spacer, Current->GetUserCount(), percent, operdata.c_str());\r totusers += Current->GetUserCount();\r totservers++;\r strlcpy(&matrix[line][depth],text,126);\r line++;\r for (unsigned int q = 0; q < Current->ChildCount(); q++)\r {\r if ((Current->GetChild(q)->Hidden) || ((Utils->HideULines) && (ServerInstance->ULine(Current->GetChild(q)->GetName().c_str()))))\r {\r if (*user->oper)\r {\r ShowMap(Current->GetChild(q),user,(Utils->FlatLinks && (!*user->oper)) ? depth : depth+2,matrix,totusers,totservers);\r }\r }\r else\r {\r ShowMap(Current->GetChild(q),user,(Utils->FlatLinks && (!*user->oper)) ? depth : depth+2,matrix,totusers,totservers);\r }\r }\r }\r}\r\rint ModuleSpanningTree::HandleMotd(const char** parameters, int pcnt, userrec* user)\r{\r if (pcnt > 0)\r {\r if (match(ServerInstance->Config->ServerName, parameters[0]))\r return 0;\r\r /* Remote MOTD, the server is within the 1st parameter */\r std::deque<std::string> params;\r params.push_back(parameters[0]);\r /* Send it out remotely, generate no reply yet */\r TreeServer* s = Utils->FindServerMask(parameters[0]);\r if (s)\r {\r params[0] = s->GetName();\r Utils->DoOneToOne(user->nick, "MOTD", params, s->GetName());\r }\r else\r user->WriteServ( "402 %s %s :No such server", user->nick, parameters[0]);\r return 1;\r }\r return 0;\r}\r\rint ModuleSpanningTree::HandleAdmin(const char** parameters, int pcnt, userrec* user)\r{\r if (pcnt > 0)\r {\r if (match(ServerInstance->Config->ServerName, parameters[0]))\r return 0;\r\r /* Remote ADMIN, the server is within the 1st parameter */\r std::deque<std::string> params;\r params.push_back(parameters[0]);\r /* Send it out remotely, generate no reply yet */\r TreeServer* s = Utils->FindServerMask(parameters[0]);\r if (s)\r {\r params[0] = s->GetName();\r Utils->DoOneToOne(user->nick, "ADMIN", params, s->GetName());\r }\r else\r user->WriteServ( "402 %s %s :No such server", user->nick, parameters[0]);\r return 1;\r }\r return 0;\r}\r\rint ModuleSpanningTree::HandleModules(const char** parameters, int pcnt, userrec* user)\r{\r if (pcnt > 0)\r {\r if (match(ServerInstance->Config->ServerName, parameters[0]))\r return 0;\r\r std::deque<std::string> params;\r params.push_back(parameters[0]);\r TreeServer* s = Utils->FindServerMask(parameters[0]);\r if (s)\r {\r params[0] = s->GetName();\r Utils->DoOneToOne(user->nick, "MODULES", params, s->GetName());\r }\r else\r user->WriteServ( "402 %s %s :No such server", user->nick, parameters[0]);\r return 1;\r }\r return 0;\r}\r\rint ModuleSpanningTree::HandleStats(const char** parameters, int pcnt, userrec* user)\r{\r if (pcnt > 1)\r {\r if (match(ServerInstance->Config->ServerName, parameters[1]))\r return 0;\r\r /* Remote STATS, the server is within the 2nd parameter */\r std::deque<std::string> params;\r params.push_back(parameters[0]);\r params.push_back(parameters[1]);\r /* Send it out remotely, generate no reply yet */\r\r TreeServer* s = Utils->FindServerMask(parameters[1]);\r if (s)\r {\r params[1] = s->GetName();\r Utils->DoOneToOne(user->nick, "STATS", params, s->GetName());\r }\r else\r {\r user->WriteServ( "402 %s %s :No such server", user->nick, parameters[1]);\r }\r return 1;\r }\r return 0;\r}\r\r// Ok, prepare to be confused.\r// After much mulling over how to approach this, it struck me that\r// the 'usual' way of doing a /MAP isnt the best way. Instead of\r// keeping track of a ton of ascii characters, and line by line\r// under recursion working out where to place them using multiplications\r// and divisons, we instead render the map onto a backplane of characters\r// (a character matrix), then draw the branches as a series of "L" shapes\r// from the nodes. This is not only friendlier on CPU it uses less stack.\rvoid ModuleSpanningTree::HandleMap(const char** parameters, int pcnt, userrec* user)\r{\r // This array represents a virtual screen which we will\r // "scratch" draw to, as the console device of an irc\r // client does not provide for a proper terminal.\r float totusers = 0;\r float totservers = 0;\r char matrix[128][128];\r for (unsigned int t = 0; t < 128; t++)\r {\r matrix[t][0] = '\0';\r }\r line = 0;\r // The only recursive bit is called here.\r ShowMap(Utils->TreeRoot,user,0,matrix,totusers,totservers);\r // Process each line one by one. The algorithm has a limit of\r // 128 servers (which is far more than a spanning tree should have\r // anyway, so we're ok). This limit can be raised simply by making\r // the character matrix deeper, 128 rows taking 10k of memory.\r for (int l = 1; l < line; l++)\r {\r // scan across the line looking for the start of the\r // servername (the recursive part of the algorithm has placed\r // the servers at indented positions depending on what they\r // are related to)\r int first_nonspace = 0;\r while (matrix[l][first_nonspace] == ' ')\r {\r first_nonspace++;\r }\r first_nonspace--;\r // Draw the `- (corner) section: this may be overwritten by\r // another L shape passing along the same vertical pane, becoming\r // a |- (branch) section instead.\r matrix[l][first_nonspace] = '-';\r matrix[l][first_nonspace-1] = '`';\r int l2 = l - 1;\r // Draw upwards until we hit the parent server, causing possibly\r // other corners (`-) to become branches (|-)\r while ((matrix[l2][first_nonspace-1] == ' ') || (matrix[l2][first_nonspace-1] == '`'))\r {\r matrix[l2][first_nonspace-1] = '|';\r l2--;\r }\r }\r // dump the whole lot to the user. This is the easy bit, honest.\r for (int t = 0; t < line; t++)\r {\r user->WriteServ("006 %s :%s",user->nick,&matrix[t][0]);\r }\r float avg_users = totusers / totservers;\r 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);\r user->WriteServ("007 %s :End of /MAP",user->nick);\r return;\r}\r\rint ModuleSpanningTree::HandleSquit(const char** parameters, int pcnt, userrec* user)\r{\r TreeServer* s = Utils->FindServerMask(parameters[0]);\r if (s)\r {\r if (s == Utils->TreeRoot)\r {\r user->WriteServ("NOTICE %s :*** SQUIT: Foolish mortal, you cannot make a server SQUIT itself! (%s matches local server name)",user->nick,parameters[0]);\r return 1;\r }\r TreeSocket* sock = s->GetSocket();\r if (sock)\r {\r ServerInstance->SNO->WriteToSnoMask('l',"SQUIT: Server \002%s\002 removed from network by %s",parameters[0],user->nick);\r sock->Squit(s,std::string("Server quit by ") + user->GetFullRealHost());\r ServerInstance->SE->DelFd(sock);\r sock->Close();\r }\r else\r {\r if (IS_LOCAL(user))\r user->WriteServ("NOTICE %s :*** WARNING: Using SQUIT to split remote servers is deprecated. Please use RSQUIT instead.",user->nick);\r }\r }\r else\r {\r user->WriteServ("NOTICE %s :*** SQUIT: The server \002%s\002 does not exist on the network.",user->nick,parameters[0]);\r }\r return 1;\r}\r\rint ModuleSpanningTree::HandleTime(const char** parameters, int pcnt, userrec* user)\r{\r if ((IS_LOCAL(user)) && (pcnt))\r {\r TreeServer* found = Utils->FindServerMask(parameters[0]);\r if (found)\r {\r // we dont' override for local server\r if (found == Utils->TreeRoot)\r return 0;\r \r std::deque<std::string> params;\r params.push_back(found->GetName());\r params.push_back(user->nick);\r Utils->DoOneToOne(ServerInstance->Config->ServerName,"TIME",params,found->GetName());\r }\r else\r {\r user->WriteServ("402 %s %s :No such server",user->nick,parameters[0]);\r }\r }\r return 1;\r}\r\rint ModuleSpanningTree::HandleRemoteWhois(const char** parameters, int pcnt, userrec* user)\r{\r if ((IS_LOCAL(user)) && (pcnt > 1))\r {\r userrec* remote = ServerInstance->FindNick(parameters[1]);\r if ((remote) && (remote->GetFd() < 0))\r {\r std::deque<std::string> params;\r params.push_back(parameters[1]);\r Utils->DoOneToOne(user->nick,"IDLE",params,remote->server);\r return 1;\r }\r else if (!remote)\r {\r user->WriteServ("401 %s %s :No such nick/channel",user->nick, parameters[1]);\r user->WriteServ("318 %s %s :End of /WHOIS list.",user->nick, parameters[1]);\r return 1;\r }\r }\r return 0;\r}\r\rvoid ModuleSpanningTree::DoPingChecks(time_t curtime)\r{\r for (unsigned int j = 0; j < Utils->TreeRoot->ChildCount(); j++)\r {\r TreeServer* serv = Utils->TreeRoot->GetChild(j);\r TreeSocket* sock = serv->GetSocket();\r if (sock)\r {\r if (curtime >= serv->NextPingTime())\r {\r if (serv->AnsweredLastPing())\r {\r sock->WriteLine(std::string(":")+ServerInstance->Config->ServerName+" PING "+serv->GetName());\r serv->SetNextPingTime(curtime + 60);\r serv->LastPing = curtime;\r serv->Warned = false;\r }\r else\r {\r /* they didnt answer, boot them */\r sock->SendError("Ping timeout");\r sock->Squit(serv,"Ping timeout");\r /*** XXX SOCKET CULL ***/\r return;\r }\r }\r else if ((Utils->PingWarnTime) && (!serv->Warned) && (curtime >= serv->NextPingTime() - (60 - Utils->PingWarnTime)) && (!serv->AnsweredLastPing()))\r {\r /* The server hasnt responded, send a warning to opers */\r ServerInstance->SNO->WriteToSnoMask('l',"Server \002%s\002 has not responded to PING for %d seconds, high latency.", serv->GetName().c_str(), Utils->PingWarnTime);\r serv->Warned = true;\r }\r }\r }\r\r /* Cancel remote burst mode on any servers which still have it enabled due to latency/lack of data.\r * This prevents lost REMOTECONNECT notices\r */\r for (server_hash::iterator i = Utils->serverlist.begin(); i != Utils->serverlist.end(); i++)\r Utils->SetRemoteBursting(i->second, false);\r}\r\rvoid ModuleSpanningTree::ConnectServer(Link* x)\r{\r bool ipvalid = true;\r QueryType start_type = DNS_QUERY_A;\r#ifdef IPV6\r start_type = DNS_QUERY_AAAA;\r if (strchr(x->IPAddr.c_str(),':'))\r {\r in6_addr n;\r if (inet_pton(AF_INET6, x->IPAddr.c_str(), &n) < 1)\r ipvalid = false;\r }\r else\r#endif\r {\r in_addr n;\r if (inet_aton(x->IPAddr.c_str(),&n) < 1)\r ipvalid = false;\r }\r\r /* Do we already have an IP? If so, no need to resolve it. */\r if (ipvalid)\r {\r /* Gave a hook, but it wasnt one we know */\r if ((!x->Hook.empty()) && (Utils->hooks.find(x->Hook.c_str()) == Utils->hooks.end()))\r return;\r 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()]);\r if (newsocket->GetFd() > -1)\r {\r /* Handled automatically on success */\r }\r else\r {\r ServerInstance->SNO->WriteToSnoMask('l',"CONNECT: Error connecting \002%s\002: %s.",x->Name.c_str(),strerror(errno));\r delete newsocket;\r Utils->DoFailOver(x);\r }\r }\r else\r {\r try\r {\r bool cached;\r ServernameResolver* snr = new ServernameResolver((Module*)this, Utils, ServerInstance,x->IPAddr, *x, cached, start_type);\r ServerInstance->AddResolver(snr, cached);\r }\r catch (ModuleException& e)\r {\r ServerInstance->SNO->WriteToSnoMask('l',"CONNECT: Error connecting \002%s\002: %s.",x->Name.c_str(), e.GetReason());\r Utils->DoFailOver(x);\r }\r }\r}\r\rvoid ModuleSpanningTree::AutoConnectServers(time_t curtime)\r{\r for (std::vector<Link>::iterator x = Utils->LinkBlocks.begin(); x < Utils->LinkBlocks.end(); x++)\r {\r if ((x->AutoConnect) && (curtime >= x->NextConnectTime))\r {\r x->NextConnectTime = curtime + x->AutoConnect;\r TreeServer* CheckDupe = Utils->FindServer(x->Name.c_str());\r if (x->FailOver.length())\r {\r TreeServer* CheckFailOver = Utils->FindServer(x->FailOver.c_str());\r if (CheckFailOver)\r {\r /* The failover for this server is currently a member of the network.\r * The failover probably succeeded, where the main link did not.\r * Don't try the main link until the failover is gone again.\r */\r continue;\r }\r }\r if (!CheckDupe)\r {\r // an autoconnected server is not connected. Check if its time to connect it\r ServerInstance->SNO->WriteToSnoMask('l',"AUTOCONNECT: Auto-connecting server \002%s\002 (%lu seconds until next attempt)",x->Name.c_str(),x->AutoConnect);\r this->ConnectServer(&(*x));\r }\r }\r }\r}\r\rint ModuleSpanningTree::HandleVersion(const char** parameters, int pcnt, userrec* user)\r{\r // we've already checked if pcnt > 0, so this is safe\r TreeServer* found = Utils->FindServerMask(parameters[0]);\r if (found)\r {\r std::string Version = found->GetVersion();\r user->WriteServ("351 %s :%s",user->nick,Version.c_str());\r if (found == Utils->TreeRoot)\r {\r ServerInstance->Config->Send005(user);\r }\r }\r else\r {\r user->WriteServ("402 %s %s :No such server",user->nick,parameters[0]);\r }\r return 1;\r}\r \rint ModuleSpanningTree::HandleConnect(const char** parameters, int pcnt, userrec* user)\r{\r for (std::vector<Link>::iterator x = Utils->LinkBlocks.begin(); x < Utils->LinkBlocks.end(); x++)\r {\r if (ServerInstance->MatchText(x->Name.c_str(),parameters[0]))\r {\r TreeServer* CheckDupe = Utils->FindServer(x->Name.c_str());\r if (!CheckDupe)\r {\r 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);\r ConnectServer(&(*x));\r return 1;\r }\r else\r {\r 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());\r return 1;\r }\r }\r }\r user->WriteServ("NOTICE %s :*** CONNECT: No server matching \002%s\002 could be found in the config file.",user->nick,parameters[0]);\r return 1;\r}\r\rvoid ModuleSpanningTree::BroadcastTimeSync()\r{\r if (Utils->MasterTime)\r {\r std::deque<std::string> params;\r params.push_back(ConvToStr(ServerInstance->Time(false)));\r params.push_back("FORCE");\r Utils->DoOneToMany(Utils->TreeRoot->GetName(), "TIMESET", params);\r }\r}\r\rint ModuleSpanningTree::OnStats(char statschar, userrec* user, string_list &results)\r{\r if ((statschar == 'c') || (statschar == 'n'))\r {\r for (unsigned int i = 0; i < Utils->LinkBlocks.size(); i++)\r {\r 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');\r if (statschar == 'c')\r results.push_back(std::string(ServerInstance->Config->ServerName)+" 244 "+user->nick+" H * * "+Utils->LinkBlocks[i].Name.c_str());\r }\r results.push_back(std::string(ServerInstance->Config->ServerName)+" 219 "+user->nick+" "+statschar+" :End of /STATS report");\r ServerInstance->SNO->WriteToSnoMask('t',"%s '%c' requested by %s (%s@%s)",(!strcmp(user->server,ServerInstance->Config->ServerName) ? "Stats" : "Remote stats"),statschar,user->nick,user->ident,user->host);\r return 1;\r }\r\r if (statschar == 'p')\r {\r /* show all server ports, after showing client ports. -- w00t */\r\r for (unsigned int i = 0; i < Utils->Bindings.size(); i++)\r {\r std::string ip = Utils->Bindings[i]->IP;\r if (ip.empty())\r ip = "*";\r\r std::string transport("plaintext");\r if (Utils->Bindings[i]->GetHook())\r transport = InspSocketNameRequest(this, Utils->Bindings[i]->GetHook()).Send();\r\r results.push_back(ConvToStr(ServerInstance->Config->ServerName) + " 249 "+user->nick+" :" + ip + ":" + ConvToStr(Utils->Bindings[i]->port)+\r " (server, " + transport + ")");\r }\r }\r return 0;\r}\r\rint ModuleSpanningTree::OnPreCommand(const std::string &command, const char** parameters, int pcnt, userrec *user, bool validated, const std::string &original_line)\r{\r /* If the command doesnt appear to be valid, we dont want to mess with it. */\r if (!validated)\r return 0;\r\r if (command == "CONNECT")\r {\r return this->HandleConnect(parameters,pcnt,user);\r }\r else if (command == "STATS")\r {\r return this->HandleStats(parameters,pcnt,user);\r }\r else if (command == "MOTD")\r {\r return this->HandleMotd(parameters,pcnt,user);\r }\r else if (command == "ADMIN")\r {\r return this->HandleAdmin(parameters,pcnt,user);\r }\r else if (command == "SQUIT")\r {\r return this->HandleSquit(parameters,pcnt,user);\r }\r else if (command == "MAP")\r {\r this->HandleMap(parameters,pcnt,user);\r return 1;\r }\r else if ((command == "TIME") && (pcnt > 0))\r {\r return this->HandleTime(parameters,pcnt,user);\r }\r else if (command == "LUSERS")\r {\r this->HandleLusers(parameters,pcnt,user);\r return 1;\r }\r else if (command == "LINKS")\r {\r this->HandleLinks(parameters,pcnt,user);\r return 1;\r }\r else if (command == "WHOIS")\r {\r if (pcnt > 1)\r {\r // remote whois\r return this->HandleRemoteWhois(parameters,pcnt,user);\r }\r }\r else if ((command == "VERSION") && (pcnt > 0))\r {\r this->HandleVersion(parameters,pcnt,user);\r return 1;\r }\r else if ((command == "MODULES") && (pcnt > 0))\r {\r return this->HandleModules(parameters,pcnt,user);\r }\r return 0;\r}\r\rvoid ModuleSpanningTree::OnPostCommand(const std::string &command, const char** parameters, int pcnt, userrec *user, CmdResult result, const std::string &original_line)\r{\r if ((result == CMD_SUCCESS) && (ServerInstance->IsValidModuleCommand(command, pcnt, user)))\r {\r // this bit of code cleverly routes all module commands\r // to all remote severs *automatically* so that modules\r // can just handle commands locally, without having\r // to have any special provision in place for remote\r // commands and linking protocols.\r std::deque<std::string> params;\r params.clear();\r for (int j = 0; j < pcnt; j++)\r {\r if (strchr(parameters[j],' '))\r {\r params.push_back(":" + std::string(parameters[j]));\r }\r else\r {\r params.push_back(std::string(parameters[j]));\r }\r }\r Utils->DoOneToMany(user->nick,command,params);\r }\r}\r\rvoid ModuleSpanningTree::OnGetServerDescription(const std::string &servername,std::string &description)\r{\r TreeServer* s = Utils->FindServer(servername);\r if (s)\r {\r description = s->GetDesc();\r }\r}\r\rvoid ModuleSpanningTree::OnUserInvite(userrec* source,userrec* dest,chanrec* channel)\r{\r if (IS_LOCAL(source))\r {\r std::deque<std::string> params;\r params.push_back(dest->nick);\r params.push_back(channel->name);\r Utils->DoOneToMany(source->nick,"INVITE",params);\r }\r}\r\rvoid ModuleSpanningTree::OnPostLocalTopicChange(userrec* user, chanrec* chan, const std::string &topic)\r{\r std::deque<std::string> params;\r params.push_back(chan->name);\r params.push_back(":"+topic);\r Utils->DoOneToMany(user->nick,"TOPIC",params);\r}\r\rvoid ModuleSpanningTree::OnWallops(userrec* user, const std::string &text)\r{\r if (IS_LOCAL(user))\r {\r std::deque<std::string> params;\r params.push_back(":"+text);\r Utils->DoOneToMany(user->nick,"WALLOPS",params);\r }\r}\r\rvoid ModuleSpanningTree::OnUserNotice(userrec* user, void* dest, int target_type, const std::string &text, char status, const CUList &exempt_list)\r{\r if (target_type == TYPE_USER)\r {\r userrec* d = (userrec*)dest;\r if ((d->GetFd() < 0) && (IS_LOCAL(user)))\r {\r std::deque<std::string> params;\r params.clear();\r params.push_back(d->nick);\r params.push_back(":"+text);\r Utils->DoOneToOne(user->nick,"NOTICE",params,d->server);\r }\r }\r else if (target_type == TYPE_CHANNEL)\r {\r if (IS_LOCAL(user))\r {\r chanrec *c = (chanrec*)dest;\r if (c)\r {\r std::string cname = c->name;\r if (status)\r cname = status + cname;\r TreeServerList list;\r Utils->GetListOfServersForChannel(c,list,status,exempt_list);\r for (TreeServerList::iterator i = list.begin(); i != list.end(); i++)\r {\r TreeSocket* Sock = i->second->GetSocket();\r if (Sock)\r Sock->WriteLine(":"+std::string(user->nick)+" NOTICE "+cname+" :"+text);\r }\r }\r }\r }\r else if (target_type == TYPE_SERVER)\r {\r if (IS_LOCAL(user))\r {\r char* target = (char*)dest;\r std::deque<std::string> par;\r par.push_back(target);\r par.push_back(":"+text);\r Utils->DoOneToMany(user->nick,"NOTICE",par);\r }\r }\r}\r\rvoid ModuleSpanningTree::OnUserMessage(userrec* user, void* dest, int target_type, const std::string &text, char status, const CUList &exempt_list)\r{\r if (target_type == TYPE_USER)\r {\r // route private messages which are targetted at clients only to the server\r // which needs to receive them\r userrec* d = (userrec*)dest;\r if ((d->GetFd() < 0) && (IS_LOCAL(user)))\r {\r std::deque<std::string> params;\r params.clear();\r params.push_back(d->nick);\r params.push_back(":"+text);\r Utils->DoOneToOne(user->nick,"PRIVMSG",params,d->server);\r }\r }\r else if (target_type == TYPE_CHANNEL)\r {\r if (IS_LOCAL(user))\r {\r chanrec *c = (chanrec*)dest;\r if (c)\r {\r std::string cname = c->name;\r if (status)\r cname = status + cname;\r TreeServerList list;\r Utils->GetListOfServersForChannel(c,list,status,exempt_list);\r for (TreeServerList::iterator i = list.begin(); i != list.end(); i++)\r {\r TreeSocket* Sock = i->second->GetSocket();\r if (Sock)\r Sock->WriteLine(":"+std::string(user->nick)+" PRIVMSG "+cname+" :"+text);\r }\r }\r }\r }\r else if (target_type == TYPE_SERVER)\r {\r if (IS_LOCAL(user))\r {\r char* target = (char*)dest;\r std::deque<std::string> par;\r par.push_back(target);\r par.push_back(":"+text);\r Utils->DoOneToMany(user->nick,"PRIVMSG",par);\r }\r }\r}\r\rvoid ModuleSpanningTree::OnBackgroundTimer(time_t curtime)\r{\r AutoConnectServers(curtime);\r DoPingChecks(curtime);\r}\r\rvoid ModuleSpanningTree::OnUserJoin(userrec* user, chanrec* channel, bool &silent)\r{\r // Only do this for local users\r if (IS_LOCAL(user))\r {\r if (channel->GetUserCounter() == 1)\r {\r std::deque<std::string> params;\r // set up their permissions and the channel TS with FJOIN.\r // All users are FJOINed now, because a module may specify\r // new joining permissions for the user.\r params.push_back(channel->name);\r params.push_back(ConvToStr(channel->age));\r params.push_back(std::string(channel->GetAllPrefixChars(user))+","+std::string(user->nick));\r Utils->DoOneToMany(ServerInstance->Config->ServerName,"FJOIN",params);\r /* First user in, sync the modes for the channel */\r params.pop_back();\r params.push_back(channel->ChanModes(true));\r Utils->DoOneToMany(ServerInstance->Config->ServerName,"FMODE",params);\r }\r else\r {\r std::deque<std::string> params;\r params.push_back(channel->name);\r params.push_back(ConvToStr(channel->age));\r Utils->DoOneToMany(user->nick,"JOIN",params);\r }\r }\r}\r\rvoid ModuleSpanningTree::OnChangeHost(userrec* user, const std::string &newhost)\r{\r // only occurs for local clients\r if (user->registered != REG_ALL)\r return;\r std::deque<std::string> params;\r params.push_back(newhost);\r Utils->DoOneToMany(user->nick,"FHOST",params);\r}\r\rvoid ModuleSpanningTree::OnChangeName(userrec* user, const std::string &gecos)\r{\r // only occurs for local clients\r if (user->registered != REG_ALL)\r return;\r std::deque<std::string> params;\r params.push_back(gecos);\r Utils->DoOneToMany(user->nick,"FNAME",params);\r}\r\rvoid ModuleSpanningTree::OnUserPart(userrec* user, chanrec* channel, const std::string &partmessage, bool &silent)\r{\r if (IS_LOCAL(user))\r {\r std::deque<std::string> params;\r params.push_back(channel->name);\r if (!partmessage.empty())\r params.push_back(":"+partmessage);\r Utils->DoOneToMany(user->nick,"PART",params);\r }\r}\r\rvoid ModuleSpanningTree::OnUserConnect(userrec* user)\r{\r char agestr[MAXBUF];\r if (IS_LOCAL(user))\r {\r std::deque<std::string> params;\r snprintf(agestr,MAXBUF,"%lu",(unsigned long)user->age);\r params.push_back(agestr);\r params.push_back(user->nick);\r params.push_back(user->host);\r params.push_back(user->dhost);\r params.push_back(user->ident);\r params.push_back("+"+std::string(user->FormatModes()));\r params.push_back(user->GetIPString());\r params.push_back(":"+std::string(user->fullname));\r Utils->DoOneToMany(ServerInstance->Config->ServerName,"NICK",params);\r // User is Local, change needs to be reflected!\r TreeServer* SourceServer = Utils->FindServer(user->server);\r if (SourceServer)\r {\r SourceServer->AddUserCount();\r }\r }\r}\r\rvoid ModuleSpanningTree::OnUserQuit(userrec* user, const std::string &reason, const std::string &oper_message)\r{\r if ((IS_LOCAL(user)) && (user->registered == REG_ALL))\r {\r std::deque<std::string> params;\r\r if (oper_message != reason)\r {\r params.push_back(":"+oper_message);\r Utils->DoOneToMany(user->nick,"OPERQUIT",params);\r }\r params.clear();\r params.push_back(":"+reason);\r Utils->DoOneToMany(user->nick,"QUIT",params);\r }\r // Regardless, We need to modify the user Counts..\r TreeServer* SourceServer = Utils->FindServer(user->server);\r if (SourceServer)\r {\r SourceServer->DelUserCount();\r }\r}\r\rvoid ModuleSpanningTree::OnUserPostNick(userrec* user, const std::string &oldnick)\r{\r if (IS_LOCAL(user))\r {\r std::deque<std::string> params;\r params.push_back(user->nick);\r Utils->DoOneToMany(oldnick,"NICK",params);\r }\r}\r\rvoid ModuleSpanningTree::OnUserKick(userrec* source, userrec* user, chanrec* chan, const std::string &reason, bool &silent)\r{\r if ((source) && (IS_LOCAL(source)))\r {\r std::deque<std::string> params;\r params.push_back(chan->name);\r params.push_back(user->nick);\r params.push_back(":"+reason);\r Utils->DoOneToMany(source->nick,"KICK",params);\r }\r else if (!source)\r {\r std::deque<std::string> params;\r params.push_back(chan->name);\r params.push_back(user->nick);\r params.push_back(":"+reason);\r Utils->DoOneToMany(ServerInstance->Config->ServerName,"KICK",params);\r }\r}\r\rvoid ModuleSpanningTree::OnRemoteKill(userrec* source, userrec* dest, const std::string &reason, const std::string &operreason)\r{\r std::deque<std::string> params;\r params.push_back(":"+reason);\r Utils->DoOneToMany(dest->nick,"OPERQUIT",params);\r params.clear();\r params.push_back(dest->nick);\r params.push_back(":"+reason);\r dest->SetOperQuit(operreason);\r Utils->DoOneToMany(source->nick,"KILL",params);\r}\r\rvoid ModuleSpanningTree::OnRehash(userrec* user, const std::string ¶meter)\r{\r if (!parameter.empty())\r {\r std::deque<std::string> params;\r params.push_back(parameter);\r Utils->DoOneToMany(user ? user->nick : ServerInstance->Config->ServerName, "REHASH", params);\r // check for self\r if (ServerInstance->MatchText(ServerInstance->Config->ServerName,parameter))\r {\r ServerInstance->WriteOpers("*** Remote rehash initiated locally by \002%s\002", user ? user->nick : ServerInstance->Config->ServerName);\r ServerInstance->RehashServer();\r }\r }\r Utils->ReadConfiguration(false);\r InitializeDisabledCommands(ServerInstance->Config->DisabledCommands, ServerInstance);\r}\r\r// note: the protocol does not allow direct umode +o except\r// via NICK with 8 params. sending OPERTYPE infers +o modechange\r// locally.\rvoid ModuleSpanningTree::OnOper(userrec* user, const std::string &opertype)\r{\r if (IS_LOCAL(user))\r {\r std::deque<std::string> params;\r params.push_back(opertype);\r Utils->DoOneToMany(user->nick,"OPERTYPE",params);\r }\r}\r\rvoid ModuleSpanningTree::OnLine(userrec* source, const std::string &host, bool adding, char linetype, long duration, const std::string &reason)\r{\r if (!source)\r {\r /* Server-set lines */\r char data[MAXBUF];\r snprintf(data,MAXBUF,"%c %s %s %lu %lu :%s", linetype, host.c_str(), ServerInstance->Config->ServerName, (unsigned long)ServerInstance->Time(false),\r (unsigned long)duration, reason.c_str());\r std::deque<std::string> params;\r params.push_back(data);\r Utils->DoOneToMany(ServerInstance->Config->ServerName, "ADDLINE", params);\r }\r else\r {\r if (IS_LOCAL(source))\r {\r char type[8];\r snprintf(type,8,"%cLINE",linetype);\r std::string stype = type;\r if (adding)\r {\r char sduration[MAXBUF];\r snprintf(sduration,MAXBUF,"%ld",duration);\r std::deque<std::string> params;\r params.push_back(host);\r params.push_back(sduration);\r params.push_back(":"+reason);\r Utils->DoOneToMany(source->nick,stype,params);\r }\r else\r {\r std::deque<std::string> params;\r params.push_back(host);\r Utils->DoOneToMany(source->nick,stype,params);\r }\r }\r }\r}\r\rvoid ModuleSpanningTree::OnAddGLine(long duration, userrec* source, const std::string &reason, const std::string &hostmask)\r{\r OnLine(source,hostmask,true,'G',duration,reason);\r}\r \rvoid ModuleSpanningTree::OnAddZLine(long duration, userrec* source, const std::string &reason, const std::string &ipmask)\r{\r OnLine(source,ipmask,true,'Z',duration,reason);\r}\r\rvoid ModuleSpanningTree::OnAddQLine(long duration, userrec* source, const std::string &reason, const std::string &nickmask)\r{\r OnLine(source,nickmask,true,'Q',duration,reason);\r}\r\rvoid ModuleSpanningTree::OnAddELine(long duration, userrec* source, const std::string &reason, const std::string &hostmask)\r{\r OnLine(source,hostmask,true,'E',duration,reason);\r}\r\rvoid ModuleSpanningTree::OnDelGLine(userrec* source, const std::string &hostmask)\r{\r OnLine(source,hostmask,false,'G',0,"");\r}\r\rvoid ModuleSpanningTree::OnDelZLine(userrec* source, const std::string &ipmask)\r{\r OnLine(source,ipmask,false,'Z',0,"");\r}\r\rvoid ModuleSpanningTree::OnDelQLine(userrec* source, const std::string &nickmask)\r{\r OnLine(source,nickmask,false,'Q',0,"");\r}\r\rvoid ModuleSpanningTree::OnDelELine(userrec* source, const std::string &hostmask)\r{\r OnLine(source,hostmask,false,'E',0,"");\r}\r\rvoid ModuleSpanningTree::OnMode(userrec* user, void* dest, int target_type, const std::string &text)\r{\r if ((IS_LOCAL(user)) && (user->registered == REG_ALL))\r {\r std::deque<std::string> params;\r std::string command;\r\r if (target_type == TYPE_USER)\r {\r userrec* u = (userrec*)dest;\r params.push_back(u->nick);\r params.push_back(text);\r command = "MODE";\r }\r else\r {\r chanrec* c = (chanrec*)dest;\r params.push_back(c->name);\r params.push_back(ConvToStr(c->age));\r params.push_back(text);\r command = "FMODE";\r }\r Utils->DoOneToMany(user->nick, command, params);\r }\r}\r\rvoid ModuleSpanningTree::OnSetAway(userrec* user)\r{\r if (IS_LOCAL(user))\r {\r std::deque<std::string> params;\r params.push_back(":"+std::string(user->awaymsg));\r Utils->DoOneToMany(user->nick,"AWAY",params);\r }\r}\r\rvoid ModuleSpanningTree::OnCancelAway(userrec* user)\r{\r if (IS_LOCAL(user))\r {\r std::deque<std::string> params;\r params.clear();\r Utils->DoOneToMany(user->nick,"AWAY",params);\r }\r}\r\rvoid ModuleSpanningTree::ProtoSendMode(void* opaque, int target_type, void* target, const std::string &modeline)\r{\r TreeSocket* s = (TreeSocket*)opaque;\r if (target)\r {\r if (target_type == TYPE_USER)\r {\r userrec* u = (userrec*)target;\r s->WriteLine(std::string(":")+ServerInstance->Config->ServerName+" FMODE "+u->nick+" "+ConvToStr(u->age)+" "+modeline);\r }\r else\r {\r chanrec* c = (chanrec*)target;\r s->WriteLine(std::string(":")+ServerInstance->Config->ServerName+" FMODE "+c->name+" "+ConvToStr(c->age)+" "+modeline);\r }\r }\r}\r\rvoid ModuleSpanningTree::ProtoSendMetaData(void* opaque, int target_type, void* target, const std::string &extname, const std::string &extdata)\r{\r TreeSocket* s = (TreeSocket*)opaque;\r if (target)\r {\r if (target_type == TYPE_USER)\r {\r userrec* u = (userrec*)target;\r s->WriteLine(std::string(":")+ServerInstance->Config->ServerName+" METADATA "+u->nick+" "+extname+" :"+extdata);\r }\r else if (target_type == TYPE_CHANNEL)\r {\r chanrec* c = (chanrec*)target;\r s->WriteLine(std::string(":")+ServerInstance->Config->ServerName+" METADATA "+c->name+" "+extname+" :"+extdata);\r }\r }\r if (target_type == TYPE_OTHER)\r {\r s->WriteLine(std::string(":")+ServerInstance->Config->ServerName+" METADATA * "+extname+" :"+extdata);\r }\r}\r\rvoid ModuleSpanningTree::OnEvent(Event* event)\r{\r std::deque<std::string>* params = (std::deque<std::string>*)event->GetData();\r if (event->GetEventID() == "send_metadata")\r {\r if (params->size() < 3)\r return;\r (*params)[2] = ":" + (*params)[2];\r Utils->DoOneToMany(ServerInstance->Config->ServerName,"METADATA",*params);\r }\r else if (event->GetEventID() == "send_topic")\r {\r if (params->size() < 2)\r return;\r (*params)[1] = ":" + (*params)[1];\r params->insert(params->begin() + 1,ServerInstance->Config->ServerName);\r params->insert(params->begin() + 1,ConvToStr(ServerInstance->Time(true)));\r Utils->DoOneToMany(ServerInstance->Config->ServerName,"FTOPIC",*params);\r }\r else if (event->GetEventID() == "send_mode")\r {\r if (params->size() < 2)\r return;\r // Insert the TS value of the object, either userrec or chanrec\r time_t ourTS = 0;\r userrec* a = ServerInstance->FindNick((*params)[0]);\r if (a)\r {\r ourTS = a->age;\r Utils->DoOneToMany(ServerInstance->Config->ServerName,"MODE",*params);\r return;\r }\r else\r {\r chanrec* a = ServerInstance->FindChan((*params)[0]);\r if (a)\r {\r ourTS = a->age;\r params->insert(params->begin() + 1,ConvToStr(ourTS));\r Utils->DoOneToMany(ServerInstance->Config->ServerName,"FMODE",*params);\r }\r }\r }\r else if (event->GetEventID() == "send_mode_explicit")\r {\r if (params->size() < 2)\r return;\r Utils->DoOneToMany(ServerInstance->Config->ServerName,"MODE",*params);\r }\r else if (event->GetEventID() == "send_opers")\r {\r if (params->size() < 1)\r return;\r (*params)[0] = ":" + (*params)[0];\r Utils->DoOneToMany(ServerInstance->Config->ServerName,"OPERNOTICE",*params);\r }\r else if (event->GetEventID() == "send_modeset")\r {\r if (params->size() < 2)\r return;\r (*params)[1] = ":" + (*params)[1];\r Utils->DoOneToMany(ServerInstance->Config->ServerName,"MODENOTICE",*params);\r }\r else if (event->GetEventID() == "send_snoset")\r {\r if (params->size() < 2)\r return;\r (*params)[1] = ":" + (*params)[1];\r Utils->DoOneToMany(ServerInstance->Config->ServerName,"SNONOTICE",*params);\r }\r else if (event->GetEventID() == "send_push")\r {\r if (params->size() < 2)\r return;\r \r userrec *a = ServerInstance->FindNick((*params)[0]);\r \r if (!a)\r return;\r \r (*params)[1] = ":" + (*params)[1];\r Utils->DoOneToOne(ServerInstance->Config->ServerName, "PUSH", *params, a->server);\r }\r}\r\rModuleSpanningTree::~ModuleSpanningTree()\r{\r /* This will also free the listeners */\r delete Utils;\r if (SyncTimer)\r ServerInstance->Timers->DelTimer(SyncTimer);\r\r ServerInstance->Timers->DelTimer(RefreshTimer);\r\r ServerInstance->DoneWithInterface("InspSocketHook");\r}\r\rVersion ModuleSpanningTree::GetVersion()\r{\r return Version(1,1,0,2,VF_VENDOR,API_VERSION);\r}\r\rvoid ModuleSpanningTree::Implements(char* List)\r{\r List[I_OnPreCommand] = List[I_OnGetServerDescription] = List[I_OnUserInvite] = List[I_OnPostLocalTopicChange] = 1;\r List[I_OnWallops] = List[I_OnUserNotice] = List[I_OnUserMessage] = List[I_OnBackgroundTimer] = 1;\r List[I_OnUserJoin] = List[I_OnChangeHost] = List[I_OnChangeName] = List[I_OnUserPart] = List[I_OnUserConnect] = 1;\r List[I_OnUserQuit] = List[I_OnUserPostNick] = List[I_OnUserKick] = List[I_OnRemoteKill] = List[I_OnRehash] = 1;\r List[I_OnOper] = List[I_OnAddGLine] = List[I_OnAddZLine] = List[I_OnAddQLine] = List[I_OnAddELine] = 1;\r List[I_OnDelGLine] = List[I_OnDelZLine] = List[I_OnDelQLine] = List[I_OnDelELine] = List[I_ProtoSendMode] = List[I_OnMode] = 1;\r List[I_OnStats] = List[I_ProtoSendMetaData] = List[I_OnEvent] = List[I_OnSetAway] = List[I_OnCancelAway] = List[I_OnPostCommand] = 1;\r}\r\r/* It is IMPORTANT that m_spanningtree is the last module in the chain\r * so that any activity it sees is FINAL, e.g. we arent going to send out\r * a NICK message before m_cloaking has finished putting the +x on the user,\r * etc etc.\r * Therefore, we return PRIORITY_LAST to make sure we end up at the END of\r * the module call queue.\r */\rPriority ModuleSpanningTree::Prioritize()\r{\r return PRIORITY_LAST;\r}\r\rMODULE_INIT(ModuleSpanningTree)\r\r
\ No newline at end of file
+/* +------------------------------------+
+ * | Inspire Internet Relay Chat Daemon |
+ * +------------------------------------+
+ *
+ * InspIRCd: (C) 2002-2008 InspIRCd Development Team
+ * See: http://www.inspircd.org/wiki/index.php/Credits
+ *
+ * This program is free but copyrighted software; see
+ * the file COPYING for details.
+ *
+ * ---------------------------------------------------
+ */
+
+/* $ModDesc: Provides a spanning tree server link protocol */
+
+#include "inspircd.h"
+#include "commands/cmd_whois.h"
+#include "commands/cmd_stats.h"
+#include "socket.h"
+#include "wildcard.h"
+#include "xline.h"
+#include "transport.h"
+
+#include "m_spanningtree/timesynctimer.h"
+#include "m_spanningtree/resolvers.h"
+#include "m_spanningtree/main.h"
+#include "m_spanningtree/utils.h"
+#include "m_spanningtree/treeserver.h"
+#include "m_spanningtree/link.h"
+#include "m_spanningtree/treesocket.h"
+#include "m_spanningtree/rconnect.h"
+#include "m_spanningtree/rsquit.h"
+
+/* $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 */
+
+ModuleSpanningTree::ModuleSpanningTree(InspIRCd* Me)
+ : Module(Me), max_local(0), max_global(0)
+{
+ ServerInstance->Modules->UseInterface("BufferedSocketHook");
+ Utils = new SpanningTreeUtilities(ServerInstance, this);
+ command_rconnect = new cmd_rconnect(ServerInstance, this, Utils);
+ ServerInstance->AddCommand(command_rconnect);
+ command_rsquit = new cmd_rsquit(ServerInstance, this, Utils);
+ ServerInstance->AddCommand(command_rsquit);
+ RefreshTimer = new CacheRefreshTimer(ServerInstance, Utils);
+ ServerInstance->Timers->AddTimer(RefreshTimer);
+
+ Implementation eventlist[] =
+ {
+ I_OnPreCommand, I_OnGetServerDescription, I_OnUserInvite, I_OnPostLocalTopicChange,
+ I_OnWallops, I_OnUserNotice, I_OnUserMessage, I_OnBackgroundTimer,
+ I_OnUserJoin, I_OnChangeHost, I_OnChangeName, I_OnUserPart, I_OnPostConnect,
+ I_OnUserQuit, I_OnUserPostNick, I_OnUserKick, I_OnRemoteKill, I_OnRehash,
+ I_OnOper, I_OnAddLine, I_OnDelLine, I_ProtoSendMode, I_OnMode,
+ I_OnStats, I_ProtoSendMetaData, I_OnEvent, I_OnSetAway, I_OnCancelAway, I_OnPostCommand
+ };
+ ServerInstance->Modules->Attach(eventlist, this, 29);
+
+ for (std::vector<User*>::const_iterator i = ServerInstance->Users->local_users.begin(); i != ServerInstance->Users->local_users.end(); i++)
+ {
+ this->OnPostConnect((*i));
+ }
+}
+
+void ModuleSpanningTree::ShowLinks(TreeServer* Current, User* user, int hops)
+{
+ std::string Parent = Utils->TreeRoot->GetName();
+ if (Current->GetParent())
+ {
+ Parent = Current->GetParent()->GetName();
+ }
+ for (unsigned int q = 0; q < Current->ChildCount(); q++)
+ {
+ if ((Current->GetChild(q)->Hidden) || ((Utils->HideULines) && (ServerInstance->ULine(Current->GetChild(q)->GetName().c_str()))))
+ {
+ if (*user->oper)
+ {
+ ShowLinks(Current->GetChild(q),user,hops+1);
+ }
+ }
+ else
+ {
+ ShowLinks(Current->GetChild(q),user,hops+1);
+ }
+ }
+ /* Don't display the line if its a uline, hide ulines is on, and the user isnt an oper */
+ if ((Utils->HideULines) && (ServerInstance->ULine(Current->GetName().c_str())) && (!IS_OPER(user)))
+ return;
+ /* Or if the server is hidden and they're not an oper */
+ else if ((Current->Hidden) && (!IS_OPER(user)))
+ return;
+
+ user->WriteNumeric(364, "%s %s %s :%d %s", user->nick,Current->GetName().c_str(),
+ (Utils->FlatLinks && (!IS_OPER(user))) ? ServerInstance->Config->ServerName : Parent.c_str(),
+ (Utils->FlatLinks && (!IS_OPER(user))) ? 0 : hops,
+ Current->GetDesc().c_str());
+}
+
+int ModuleSpanningTree::CountLocalServs()
+{
+ return Utils->TreeRoot->ChildCount();
+}
+
+int ModuleSpanningTree::CountServs()
+{
+ return Utils->serverlist.size();
+}
+
+void ModuleSpanningTree::HandleLinks(const char* const* parameters, int pcnt, User* user)
+{
+ ShowLinks(Utils->TreeRoot,user,0);
+ user->WriteNumeric(365, "%s * :End of /LINKS list.",user->nick);
+ return;
+}
+
+void ModuleSpanningTree::HandleLusers(const char* const* parameters, int pcnt, User* user)
+{
+ unsigned int n_users = ServerInstance->Users->UserCount();
+
+ /* Only update these when someone wants to see them, more efficient */
+ if ((unsigned int)ServerInstance->Users->LocalUserCount() > max_local)
+ max_local = ServerInstance->Users->LocalUserCount();
+ if (n_users > max_global)
+ max_global = n_users;
+
+ unsigned int ulined_count = 0;
+ unsigned int ulined_local_count = 0;
+
+ /* If ulined are hidden and we're not an oper, count the number of ulined servers hidden,
+ * locally and globally (locally means directly connected to us)
+ */
+ if ((Utils->HideULines) && (!*user->oper))
+ {
+ for (server_hash::iterator q = Utils->serverlist.begin(); q != Utils->serverlist.end(); q++)
+ {
+ if (ServerInstance->ULine(q->second->GetName().c_str()))
+ {
+ ulined_count++;
+ if (q->second->GetParent() == Utils->TreeRoot)
+ ulined_local_count++;
+ }
+ }
+ }
+ user->WriteNumeric(251, "%s :There are %d users and %d invisible on %d servers",user->nick,
+ n_users-ServerInstance->Users->ModeCount('i'),
+ ServerInstance->Users->ModeCount('i'),
+ ulined_count ? this->CountServs() - ulined_count : this->CountServs());
+
+ if (ServerInstance->Users->OperCount())
+ user->WriteNumeric(252, "%s %d :operator(s) online",user->nick,ServerInstance->Users->OperCount());
+
+ if (ServerInstance->Users->UnregisteredUserCount())
+ user->WriteNumeric(253, "%s %d :unknown connections",user->nick,ServerInstance->Users->UnregisteredUserCount());
+
+ if (ServerInstance->ChannelCount())
+ user->WriteNumeric(254, "%s %ld :channels formed",user->nick,ServerInstance->ChannelCount());
+
+ user->WriteNumeric(255, "%s :I have %d clients and %d servers",user->nick,ServerInstance->Users->LocalUserCount(),ulined_local_count ? this->CountLocalServs() - ulined_local_count : this->CountLocalServs());
+ user->WriteNumeric(265, "%s :Current Local Users: %d Max: %d",user->nick,ServerInstance->Users->LocalUserCount(),max_local);
+ user->WriteNumeric(266, "%s :Current Global Users: %d Max: %d",user->nick,n_users,max_global);
+ return;
+}
+
+std::string ModuleSpanningTree::TimeToStr(time_t secs)
+{
+ time_t mins_up = secs / 60;
+ time_t hours_up = mins_up / 60;
+ time_t days_up = hours_up / 24;
+ secs = secs % 60;
+ mins_up = mins_up % 60;
+ hours_up = hours_up % 24;
+ return ((days_up ? (ConvToStr(days_up) + "d") : std::string(""))
+ + (hours_up ? (ConvToStr(hours_up) + "h") : std::string(""))
+ + (mins_up ? (ConvToStr(mins_up) + "m") : std::string(""))
+ + ConvToStr(secs) + "s");
+}
+
+void ModuleSpanningTree::DoPingChecks(time_t curtime)
+{
+ for (unsigned int j = 0; j < Utils->TreeRoot->ChildCount(); j++)
+ {
+ TreeServer* serv = Utils->TreeRoot->GetChild(j);
+ TreeSocket* sock = serv->GetSocket();
+ if (sock)
+ {
+ if (curtime >= serv->NextPingTime())
+ {
+ if (serv->AnsweredLastPing())
+ {
+ sock->WriteLine(std::string(":")+ServerInstance->Config->GetSID()+" PING "+serv->GetID());
+ serv->SetNextPingTime(curtime + Utils->PingFreq);
+ timeval t;
+ gettimeofday(&t, NULL);
+ long ts = (t.tv_sec * 1000) + (t.tv_usec / 1000);
+ serv->LastPingMsec = ts;
+ serv->Warned = false;
+ }
+ else
+ {
+ /* they didnt answer, boot them */
+ sock->SendError("Ping timeout");
+ sock->Squit(serv,"Ping timeout");
+ ServerInstance->SE->DelFd(sock);
+ sock->Close();
+ return;
+ }
+ }
+ else if ((Utils->PingWarnTime) && (!serv->Warned) && (curtime >= serv->NextPingTime() - (Utils->PingFreq - Utils->PingWarnTime)) && (!serv->AnsweredLastPing()))
+ {
+ /* The server hasnt responded, send a warning to opers */
+ ServerInstance->SNO->WriteToSnoMask('l',"Server \002%s\002 has not responded to PING for %d seconds, high latency.", serv->GetName().c_str(), Utils->PingWarnTime);
+ serv->Warned = true;
+ }
+ }
+ }
+
+ /*
+ * Cancel remote burst mode on any servers which still have it enabled due to latency/lack of data.
+ * This prevents lost REMOTECONNECT notices
+ * XXX this should probably not do this until server has been bursting for, say, 60 seconds or something
+ */
+ for (server_hash::iterator i = Utils->serverlist.begin(); i != Utils->serverlist.end(); i++)
+ {
+ if (i->second->bursting)
+ {
+ ServerInstance->SNO->WriteToSnoMask('l',"Server \002%s\002 has not finished burst, forcing end of burst.", i->second->GetName().c_str());
+ i->second->FinishBurst();
+ }
+ }
+}
+
+void ModuleSpanningTree::ConnectServer(Link* x)
+{
+ bool ipvalid = true;
+ QueryType start_type = DNS_QUERY_A;
+#ifdef IPV6
+ start_type = DNS_QUERY_AAAA;
+ if (strchr(x->IPAddr.c_str(),':'))
+ {
+ in6_addr n;
+ if (inet_pton(AF_INET6, x->IPAddr.c_str(), &n) < 1)
+ ipvalid = false;
+ }
+ else
+#endif
+ {
+ in_addr n;
+ if (inet_aton(x->IPAddr.c_str(),&n) < 1)
+ ipvalid = false;
+ }
+
+ /* Do we already have an IP? If so, no need to resolve it. */
+ if (ipvalid)
+ {
+ /* Gave a hook, but it wasnt one we know */
+ if ((!x->Hook.empty()) && (Utils->hooks.find(x->Hook.c_str()) == Utils->hooks.end()))
+ return;
+ 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()]);
+ if (newsocket->GetFd() > -1)
+ {
+ /* Handled automatically on success */
+ }
+ else
+ {
+ RemoteMessage(NULL, "CONNECT: Error connecting \002%s\002: %s.",x->Name.c_str(),strerror(errno));
+ if (ServerInstance->SocketCull.find(newsocket) == ServerInstance->SocketCull.end())
+ ServerInstance->SocketCull[newsocket] = newsocket;
+ Utils->DoFailOver(x);
+ }
+ }
+ else
+ {
+ try
+ {
+ bool cached;
+ ServernameResolver* snr = new ServernameResolver((Module*)this, Utils, ServerInstance,x->IPAddr, *x, cached, start_type);
+ ServerInstance->AddResolver(snr, cached);
+ }
+ catch (ModuleException& e)
+ {
+ RemoteMessage(NULL, "CONNECT: Error connecting \002%s\002: %s.",x->Name.c_str(), e.GetReason());
+ Utils->DoFailOver(x);
+ }
+ }
+}
+
+void ModuleSpanningTree::AutoConnectServers(time_t curtime)
+{
+ for (std::vector<Link>::iterator x = Utils->LinkBlocks.begin(); x < Utils->LinkBlocks.end(); x++)
+ {
+ if ((x->AutoConnect) && (curtime >= x->NextConnectTime))
+ {
+ x->NextConnectTime = curtime + x->AutoConnect;
+ TreeServer* CheckDupe = Utils->FindServer(x->Name.c_str());
+ if (x->FailOver.length())
+ {
+ TreeServer* CheckFailOver = Utils->FindServer(x->FailOver.c_str());
+ if (CheckFailOver)
+ {
+ /* The failover for this server is currently a member of the network.
+ * The failover probably succeeded, where the main link did not.
+ * Don't try the main link until the failover is gone again.
+ */
+ continue;
+ }
+ }
+ if (!CheckDupe)
+ {
+ // an autoconnected server is not connected. Check if its time to connect it
+ ServerInstance->SNO->WriteToSnoMask('l',"AUTOCONNECT: Auto-connecting server \002%s\002 (%lu seconds until next attempt)",x->Name.c_str(),x->AutoConnect);
+ this->ConnectServer(&(*x));
+ }
+ }
+ }
+}
+
+int ModuleSpanningTree::HandleVersion(const char* const* parameters, int pcnt, User* user)
+{
+ // we've already checked if pcnt > 0, so this is safe
+ TreeServer* found = Utils->FindServerMask(parameters[0]);
+ if (found)
+ {
+ std::string Version = found->GetVersion();
+ user->WriteNumeric(351, "%s :%s",user->nick,Version.c_str());
+ if (found == Utils->TreeRoot)
+ {
+ ServerInstance->Config->Send005(user);
+ }
+ }
+ else
+ {
+ user->WriteNumeric(402, "%s %s :No such server",user->nick,parameters[0]);
+ }
+ return 1;
+}
+
+/* This method will attempt to get a link message out to as many people as is required.
+ * If a user is provided, and that user is local, then the user is sent the message using
+ * WriteServ (they are the local initiator of that message). If the user is remote, they are
+ * sent that message remotely via PUSH.
+ * If the user is NULL, then the notice is sent locally via WriteToSnoMask with snomask 'l',
+ * and remotely via SNONOTICE with mask 'l'.
+ */
+void ModuleSpanningTree::RemoteMessage(User* user, const char* format, ...)
+{
+ /* This could cause an infinite loop, because DoOneToMany() will, on error,
+ * call TreeSocket::OnError(), which in turn will call this function to
+ * notify everyone of the error. So, drop any messages that are generated
+ * during the sending of another message. -Special */
+ static bool SendingRemoteMessage = false;
+ if (SendingRemoteMessage)
+ return;
+ SendingRemoteMessage = true;
+
+ std::deque<std::string> params;
+ char text[MAXBUF];
+ va_list argsPtr;
+
+ va_start(argsPtr, format);
+ vsnprintf(text, MAXBUF, format, argsPtr);
+ va_end(argsPtr);
+
+ if (!user)
+ {
+ /* No user, target it generically at everyone */
+ ServerInstance->SNO->WriteToSnoMask('l', "%s", text);
+ params.push_back("l");
+ params.push_back(std::string(":") + text);
+ Utils->DoOneToMany(ServerInstance->Config->GetSID(), "SNONOTICE", params);
+ }
+ else
+ {
+ if (IS_LOCAL(user))
+ user->WriteServ("NOTICE %s :%s", user->nick, text);
+ else
+ {
+ params.push_back(user->uuid);
+ params.push_back(std::string("::") + ServerInstance->Config->ServerName + " NOTICE " + user->nick + " :*** From " +
+ ServerInstance->Config->ServerName+ ": " + text);
+ Utils->DoOneToMany(ServerInstance->Config->GetSID(), "PUSH", params);
+ }
+ }
+
+ SendingRemoteMessage = false;
+}
+
+int ModuleSpanningTree::HandleConnect(const char* const* parameters, int pcnt, User* user)
+{
+ for (std::vector<Link>::iterator x = Utils->LinkBlocks.begin(); x < Utils->LinkBlocks.end(); x++)
+ {
+ if (ServerInstance->MatchText(x->Name.c_str(),parameters[0]))
+ {
+ TreeServer* CheckDupe = Utils->FindServer(x->Name.c_str());
+ if (!CheckDupe)
+ {
+ RemoteMessage(user, "*** CONNECT: Connecting to server: \002%s\002 (%s:%d)",x->Name.c_str(),(x->HiddenFromStats ? "<hidden>" : x->IPAddr.c_str()),x->Port);
+ ConnectServer(&(*x));
+ return 1;
+ }
+ else
+ {
+ 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());
+ return 1;
+ }
+ }
+ }
+ RemoteMessage(user, "*** CONNECT: No server matching \002%s\002 could be found in the config file.",parameters[0]);
+ return 1;
+}
+
+void ModuleSpanningTree::OnGetServerDescription(const std::string &servername,std::string &description)
+{
+ TreeServer* s = Utils->FindServer(servername);
+ if (s)
+ {
+ description = s->GetDesc();
+ }
+}
+
+void ModuleSpanningTree::OnUserInvite(User* source,User* dest,Channel* channel, time_t expiry)
+{
+ if (IS_LOCAL(source))
+ {
+ std::deque<std::string> params;
+ params.push_back(dest->uuid);
+ params.push_back(channel->name);
+ params.push_back(ConvToStr(expiry));
+ Utils->DoOneToMany(source->uuid,"INVITE",params);
+ }
+}
+
+void ModuleSpanningTree::OnPostLocalTopicChange(User* user, Channel* chan, const std::string &topic)
+{
+ std::deque<std::string> params;
+ params.push_back(chan->name);
+ params.push_back(":"+topic);
+ Utils->DoOneToMany(user->uuid,"TOPIC",params);
+}
+
+void ModuleSpanningTree::OnWallops(User* user, const std::string &text)
+{
+ if (IS_LOCAL(user))
+ {
+ std::deque<std::string> params;
+ params.push_back(":"+text);
+ Utils->DoOneToMany(user->uuid,"WALLOPS",params);
+ }
+}
+
+void ModuleSpanningTree::OnUserNotice(User* user, void* dest, int target_type, const std::string &text, char status, const CUList &exempt_list)
+{
+ if (target_type == TYPE_USER)
+ {
+ User* d = (User*)dest;
+ if ((d->GetFd() < 0) && (IS_LOCAL(user)))
+ {
+ std::deque<std::string> params;
+ params.clear();
+ params.push_back(d->uuid);
+ params.push_back(":"+text);
+ Utils->DoOneToOne(user->uuid,"NOTICE",params,d->server);
+ }
+ }
+ else if (target_type == TYPE_CHANNEL)
+ {
+ if (IS_LOCAL(user))
+ {
+ Channel *c = (Channel*)dest;
+ if (c)
+ {
+ std::string cname = c->name;
+ if (status)
+ cname = status + cname;
+ TreeServerList list;
+ Utils->GetListOfServersForChannel(c,list,status,exempt_list);
+ for (TreeServerList::iterator i = list.begin(); i != list.end(); i++)
+ {
+ TreeSocket* Sock = i->second->GetSocket();
+ if (Sock)
+ Sock->WriteLine(":"+std::string(user->uuid)+" NOTICE "+cname+" :"+text);
+ }
+ }
+ }
+ }
+ else if (target_type == TYPE_SERVER)
+ {
+ if (IS_LOCAL(user))
+ {
+ char* target = (char*)dest;
+ std::deque<std::string> par;
+ par.push_back(target);
+ par.push_back(":"+text);
+ Utils->DoOneToMany(user->uuid,"NOTICE",par);
+ }
+ }
+}
+
+void ModuleSpanningTree::OnUserMessage(User* user, void* dest, int target_type, const std::string &text, char status, const CUList &exempt_list)
+{
+ if (target_type == TYPE_USER)
+ {
+ // route private messages which are targetted at clients only to the server
+ // which needs to receive them
+ User* d = (User*)dest;
+ if ((d->GetFd() < 0) && (IS_LOCAL(user)))
+ {
+ std::deque<std::string> params;
+ params.clear();
+ params.push_back(d->uuid);
+ params.push_back(":"+text);
+ Utils->DoOneToOne(user->uuid,"PRIVMSG",params,d->server);
+ }
+ }
+ else if (target_type == TYPE_CHANNEL)
+ {
+ if (IS_LOCAL(user))
+ {
+ Channel *c = (Channel*)dest;
+ if (c)
+ {
+ std::string cname = c->name;
+ if (status)
+ cname = status + cname;
+ TreeServerList list;
+ Utils->GetListOfServersForChannel(c,list,status,exempt_list);
+ for (TreeServerList::iterator i = list.begin(); i != list.end(); i++)
+ {
+ TreeSocket* Sock = i->second->GetSocket();
+ if (Sock)
+ Sock->WriteLine(":"+std::string(user->uuid)+" PRIVMSG "+cname+" :"+text);
+ }
+ }
+ }
+ }
+ else if (target_type == TYPE_SERVER)
+ {
+ if (IS_LOCAL(user))
+ {
+ char* target = (char*)dest;
+ std::deque<std::string> par;
+ par.push_back(target);
+ par.push_back(":"+text);
+ Utils->DoOneToMany(user->uuid,"PRIVMSG",par);
+ }
+ }
+}
+
+void ModuleSpanningTree::OnBackgroundTimer(time_t curtime)
+{
+ AutoConnectServers(curtime);
+ DoPingChecks(curtime);
+}
+
+void ModuleSpanningTree::OnUserJoin(User* user, Channel* channel, bool sync, bool &silent)
+{
+ // Only do this for local users
+ if (IS_LOCAL(user))
+ {
+ if (channel->GetUserCounter() == 1)
+ {
+ std::deque<std::string> params;
+ // set up their permissions and the channel TS with FJOIN.
+ // All users are FJOINed now, because a module may specify
+ // new joining permissions for the user.
+ params.push_back(channel->name);
+ params.push_back(ConvToStr(channel->age));
+ params.push_back(std::string(channel->GetAllPrefixChars(user))+","+std::string(user->uuid));
+ Utils->DoOneToMany(ServerInstance->Config->GetSID(),"FJOIN",params);
+ /* First user in, sync the modes for the channel */
+ params.pop_back();
+ params.push_back(std::string("+") + channel->ChanModes(true));
+ Utils->DoOneToMany(ServerInstance->Config->GetSID(),"FMODE",params);
+ }
+ else
+ {
+ std::deque<std::string> params;
+ params.push_back(channel->name);
+ params.push_back(ConvToStr(channel->age));
+ Utils->DoOneToMany(user->uuid,"JOIN",params);
+ }
+ }
+}
+
+void ModuleSpanningTree::OnChangeHost(User* user, const std::string &newhost)
+{
+ // only occurs for local clients
+ if (user->registered != REG_ALL)
+ return;
+ std::deque<std::string> params;
+ params.push_back(newhost);
+ Utils->DoOneToMany(user->uuid,"FHOST",params);
+}
+
+void ModuleSpanningTree::OnChangeName(User* user, const std::string &gecos)
+{
+ // only occurs for local clients
+ if (user->registered != REG_ALL)
+ return;
+ std::deque<std::string> params;
+ params.push_back(gecos);
+ Utils->DoOneToMany(user->uuid,"FNAME",params);
+}
+
+void ModuleSpanningTree::OnUserPart(User* user, Channel* channel, const std::string &partmessage, bool &silent)
+{
+ if (IS_LOCAL(user))
+ {
+ std::deque<std::string> params;
+ params.push_back(channel->name);
+ if (!partmessage.empty())
+ params.push_back(":"+partmessage);
+ Utils->DoOneToMany(user->uuid,"PART",params);
+ }
+}
+
+void ModuleSpanningTree::OnPostConnect(User* user)
+{
+ if (IS_LOCAL(user))
+ {
+ std::deque<std::string> params;
+ params.push_back(user->uuid);
+ params.push_back(ConvToStr(user->age));
+ params.push_back(user->nick);
+ params.push_back(user->host);
+ params.push_back(user->dhost);
+ params.push_back(user->ident);
+ params.push_back("+"+std::string(user->FormatModes()));
+ params.push_back(user->GetIPString());
+ params.push_back(ConvToStr(user->signon));
+ params.push_back(":"+std::string(user->fullname));
+ Utils->DoOneToMany(ServerInstance->Config->GetSID(), "UID", params);
+ }
+
+ TreeServer* SourceServer = Utils->FindServer(user->server);
+ if (SourceServer)
+ {
+ SourceServer->SetUserCount(1); // increment by 1
+ }
+}
+
+void ModuleSpanningTree::OnUserQuit(User* user, const std::string &reason, const std::string &oper_message)
+{
+ if ((IS_LOCAL(user)) && (user->registered == REG_ALL))
+ {
+ std::deque<std::string> params;
+
+ if (oper_message != reason)
+ {
+ params.push_back(":"+oper_message);
+ Utils->DoOneToMany(user->uuid,"OPERQUIT",params);
+ }
+ params.clear();
+ params.push_back(":"+reason);
+ Utils->DoOneToMany(user->uuid,"QUIT",params);
+ }
+
+ // Regardless, We need to modify the user Counts..
+ TreeServer* SourceServer = Utils->FindServer(user->server);
+ if (SourceServer)
+ {
+ SourceServer->SetUserCount(-1); // decrement by 1
+ }
+}
+
+void ModuleSpanningTree::OnUserPostNick(User* user, const std::string &oldnick)
+{
+ if (IS_LOCAL(user))
+ {
+ std::deque<std::string> params;
+ params.push_back(user->nick);
+
+ /** IMPORTANT: We don't update the TS if the oldnick is just a case change of the newnick!
+ */
+ if (irc::string(user->nick) != assign(oldnick))
+ user->age = ServerInstance->Time();
+
+ params.push_back(ConvToStr(user->age));
+ Utils->DoOneToMany(user->uuid,"NICK",params);
+ }
+}
+
+void ModuleSpanningTree::OnUserKick(User* source, User* user, Channel* chan, const std::string &reason, bool &silent)
+{
+ if ((source) && (IS_LOCAL(source)))
+ {
+ std::deque<std::string> params;
+ params.push_back(chan->name);
+ params.push_back(user->uuid);
+ params.push_back(":"+reason);
+ Utils->DoOneToMany(source->uuid,"KICK",params);
+ }
+ else if (!source)
+ {
+ std::deque<std::string> params;
+ params.push_back(chan->name);
+ params.push_back(user->uuid);
+ params.push_back(":"+reason);
+ Utils->DoOneToMany(ServerInstance->Config->GetSID(),"KICK",params);
+ }
+}
+
+void ModuleSpanningTree::OnRemoteKill(User* source, User* dest, const std::string &reason, const std::string &operreason)
+{
+ std::deque<std::string> params;
+ params.push_back(":"+reason);
+ Utils->DoOneToMany(dest->uuid,"OPERQUIT",params);
+ params.clear();
+ params.push_back(dest->uuid);
+ params.push_back(":"+reason);
+ dest->SetOperQuit(operreason);
+ Utils->DoOneToMany(source->uuid,"KILL",params);
+}
+
+void ModuleSpanningTree::OnRehash(User* user, const std::string ¶meter)
+{
+ if (!parameter.empty())
+ {
+ std::deque<std::string> params;
+ params.push_back(parameter);
+ Utils->DoOneToMany(user ? user->nick : ServerInstance->Config->GetSID(), "REHASH", params);
+ // check for self
+ if (ServerInstance->MatchText(ServerInstance->Config->ServerName,parameter))
+ {
+ ServerInstance->SNO->WriteToSnoMask('A', "Remote rehash initiated locally by \002%s\002", user ? user->nick : ServerInstance->Config->ServerName);
+ ServerInstance->RehashServer();
+ }
+ }
+ Utils->ReadConfiguration(true);
+ InitializeDisabledCommands(ServerInstance->Config->DisabledCommands, ServerInstance);
+}
+
+// note: the protocol does not allow direct umode +o except
+// via NICK with 8 params. sending OPERTYPE infers +o modechange
+// locally.
+void ModuleSpanningTree::OnOper(User* user, const std::string &opertype)
+{
+ if (IS_LOCAL(user))
+ {
+ std::deque<std::string> params;
+ params.push_back(opertype);
+ Utils->DoOneToMany(user->uuid,"OPERTYPE",params);
+ }
+}
+
+void ModuleSpanningTree::OnAddLine(User* user, XLine *x)
+{
+ if (!x->IsBurstable())
+ return;
+
+ char data[MAXBUF];
+ snprintf(data,MAXBUF,"%s %s %s %lu %lu :%s", x->type.c_str(), x->Displayable(),
+ ServerInstance->Config->ServerName, (unsigned long)x->set_time, (unsigned long)x->duration, x->reason);
+ std::deque<std::string> params;
+ params.push_back(data);
+
+ if (!user)
+ {
+ /* Server-set lines */
+ Utils->DoOneToMany(ServerInstance->Config->GetSID(), "ADDLINE", params);
+ }
+ else if (IS_LOCAL(user))
+ {
+ /* User-set lines */
+ Utils->DoOneToMany(user->uuid, "ADDLINE", params);
+ }
+}
+
+void ModuleSpanningTree::OnDelLine(User* user, XLine *x)
+{
+ if (x->type == "K")
+ return;
+
+ char data[MAXBUF];
+ snprintf(data,MAXBUF,"%s %s", x->type.c_str(), x->Displayable());
+ std::deque<std::string> params;
+ params.push_back(data);
+
+ if (!user)
+ {
+ /* Server-unset lines */
+ Utils->DoOneToMany(ServerInstance->Config->GetSID(), "DELLINE", params);
+ }
+ else if (IS_LOCAL(user))
+ {
+ /* User-unset lines */
+ Utils->DoOneToMany(user->uuid, "DELLINE", params);
+ }
+}
+
+void ModuleSpanningTree::OnMode(User* user, void* dest, int target_type, const std::string &text)
+{
+ if ((IS_LOCAL(user)) && (user->registered == REG_ALL))
+ {
+ std::deque<std::string> params;
+ std::string command;
+ std::string output_text;
+
+ ServerInstance->Parser->TranslateUIDs(TR_SPACENICKLIST, text, output_text);
+
+ if (target_type == TYPE_USER)
+ {
+ User* u = (User*)dest;
+ params.push_back(u->uuid);
+ params.push_back(output_text);
+ command = "MODE";
+ }
+ else
+ {
+ Channel* c = (Channel*)dest;
+ params.push_back(c->name);
+ params.push_back(ConvToStr(c->age));
+ params.push_back(output_text);
+ command = "FMODE";
+ }
+
+ Utils->DoOneToMany(user->uuid, command, params);
+ }
+}
+
+void ModuleSpanningTree::OnSetAway(User* user)
+{
+ if (IS_LOCAL(user))
+ {
+ std::deque<std::string> params;
+ params.push_back(":"+std::string(user->awaymsg));
+ Utils->DoOneToMany(user->uuid,"AWAY",params);
+ }
+}
+
+void ModuleSpanningTree::OnCancelAway(User* user)
+{
+ if (IS_LOCAL(user))
+ {
+ std::deque<std::string> params;
+ params.clear();
+ Utils->DoOneToMany(user->uuid,"AWAY",params);
+ }
+}
+
+void ModuleSpanningTree::ProtoSendMode(void* opaque, int target_type, void* target, const std::string &modeline)
+{
+ TreeSocket* s = (TreeSocket*)opaque;
+ std::string output_text;
+
+ ServerInstance->Parser->TranslateUIDs(TR_SPACENICKLIST, modeline, output_text);
+
+ if (target)
+ {
+ if (target_type == TYPE_USER)
+ {
+ User* u = (User*)target;
+ s->WriteLine(std::string(":")+ServerInstance->Config->GetSID()+" FMODE "+u->uuid+" "+ConvToStr(u->age)+" "+output_text);
+ }
+ else
+ {
+ Channel* c = (Channel*)target;
+ s->WriteLine(std::string(":")+ServerInstance->Config->GetSID()+" FMODE "+c->name+" "+ConvToStr(c->age)+" "+output_text);
+ }
+ }
+}
+
+void ModuleSpanningTree::ProtoSendMetaData(void* opaque, int target_type, void* target, const std::string &extname, const std::string &extdata)
+{
+ TreeSocket* s = (TreeSocket*)opaque;
+ if (target)
+ {
+ if (target_type == TYPE_USER)
+ {
+ User* u = (User*)target;
+ s->WriteLine(std::string(":")+ServerInstance->Config->GetSID()+" METADATA "+u->uuid+" "+extname+" :"+extdata);
+ }
+ else if (target_type == TYPE_CHANNEL)
+ {
+ Channel* c = (Channel*)target;
+ s->WriteLine(std::string(":")+ServerInstance->Config->GetSID()+" METADATA "+c->name+" "+extname+" :"+extdata);
+ }
+ }
+ if (target_type == TYPE_OTHER)
+ {
+ s->WriteLine(std::string(":")+ServerInstance->Config->GetSID()+" METADATA * "+extname+" :"+extdata);
+ }
+}
+
+void ModuleSpanningTree::OnEvent(Event* event)
+{
+ std::deque<std::string>* params = (std::deque<std::string>*)event->GetData();
+ if (event->GetEventID() == "send_encap")
+ {
+ if (params->size() < 2)
+ return;
+
+ Utils->DoOneToMany(ServerInstance->Config->GetSID(), "ENCAP", *params);
+ }
+ else if (event->GetEventID() == "send_metadata")
+ {
+ if (params->size() < 3)
+ return;
+ (*params)[2] = ":" + (*params)[2];
+ Utils->DoOneToMany(ServerInstance->Config->GetSID(),"METADATA",*params);
+ }
+ else if (event->GetEventID() == "send_topic")
+ {
+ if (params->size() < 2)
+ return;
+ (*params)[1] = ":" + (*params)[1];
+ params->insert(params->begin() + 1,ServerInstance->Config->ServerName);
+ params->insert(params->begin() + 1,ConvToStr(ServerInstance->Time()));
+ Utils->DoOneToMany(ServerInstance->Config->GetSID(),"FTOPIC",*params);
+ }
+ else if (event->GetEventID() == "send_mode")
+ {
+ if (params->size() < 2)
+ return;
+ // Insert the TS value of the object, either User or Channel
+ time_t ourTS = 0;
+ std::string output_text;
+
+ /* Warning: in-place translation is only safe for type TR_NICK */
+ for (size_t n = 0; n < params->size(); n++)
+ ServerInstance->Parser->TranslateUIDs(TR_NICK, (*params)[n], (*params)[n]);
+
+ User* a = ServerInstance->FindNick((*params)[0]);
+ if (a)
+ {
+ ourTS = a->age;
+ Utils->DoOneToMany(ServerInstance->Config->GetSID(),"MODE",*params);
+ return;
+ }
+ else
+ {
+ Channel* c = ServerInstance->FindChan((*params)[0]);
+ if (c)
+ {
+ ourTS = c->age;
+ params->insert(params->begin() + 1,ConvToStr(ourTS));
+ Utils->DoOneToMany(ServerInstance->Config->GetSID(),"FMODE",*params);
+ }
+ }
+ }
+ else if (event->GetEventID() == "send_mode_explicit")
+ {
+ if (params->size() < 2)
+ return;
+ std::string output_text;
+
+ /* Warning: in-place translation is only safe for type TR_NICK */
+ for (size_t n = 0; n < params->size(); n++)
+ ServerInstance->Parser->TranslateUIDs(TR_NICK, (*params)[n], (*params)[n]);
+
+ Utils->DoOneToMany(ServerInstance->Config->GetSID(),"MODE",*params);
+ }
+ else if (event->GetEventID() == "send_opers")
+ {
+ if (params->size() < 1)
+ return;
+ (*params)[0] = ":" + (*params)[0];
+ Utils->DoOneToMany(ServerInstance->Config->GetSID(),"OPERNOTICE",*params);
+ }
+ else if (event->GetEventID() == "send_modeset")
+ {
+ if (params->size() < 2)
+ return;
+ (*params)[1] = ":" + (*params)[1];
+ Utils->DoOneToMany(ServerInstance->Config->GetSID(),"MODENOTICE",*params);
+ }
+ else if (event->GetEventID() == "send_snoset")
+ {
+ if (params->size() < 2)
+ return;
+ (*params)[1] = ":" + (*params)[1];
+ Utils->DoOneToMany(ServerInstance->Config->GetSID(),"SNONOTICE",*params);
+ }
+ else if (event->GetEventID() == "send_push")
+ {
+ if (params->size() < 2)
+ return;
+
+ User *a = ServerInstance->FindNick((*params)[0]);
+
+ if (!a)
+ return;
+
+ (*params)[0] = a->uuid;
+ (*params)[1] = ":" + (*params)[1];
+ Utils->DoOneToOne(ServerInstance->Config->GetSID(), "PUSH", *params, a->server);
+ }
+}
+
+ModuleSpanningTree::~ModuleSpanningTree()
+{
+ /* This will also free the listeners */
+ delete Utils;
+
+ ServerInstance->Timers->DelTimer(RefreshTimer);
+
+ ServerInstance->Modules->DoneWithInterface("BufferedSocketHook");
+}
+
+Version ModuleSpanningTree::GetVersion()
+{
+ return Version(1,2,0,2,VF_VENDOR,API_VERSION);
+}
+
+/* It is IMPORTANT that m_spanningtree is the last module in the chain
+ * so that any activity it sees is FINAL, e.g. we arent going to send out
+ * a NICK message before m_cloaking has finished putting the +x on the user,
+ * etc etc.
+ * Therefore, we return PRIORITY_LAST to make sure we end up at the END of
+ * the module call queue.
+ */
+void ModuleSpanningTree::Prioritize()
+{
+ ServerInstance->Modules->SetPriority(this, PRIO_LAST);
+}
+
+MODULE_INIT(ModuleSpanningTree)