X-Git-Url: https://git.netwichtig.de/gitweb/?a=blobdiff_plain;f=src%2Fmodules%2Fm_spanningtree%2Foverride_map.cpp;h=0592eeaf1dcb3aa8bac4b153c41576fd70aa68ea;hb=6c2b6fa23d3a65ecdcebbf9154b8daa2e55727e9;hp=01423b60a2a57b57c05b3f27106e0e4ee749b8e9;hpb=3699b899c0eadadaba960a3263c82894685a86c4;p=user%2Fhenk%2Fcode%2Finspircd.git diff --git a/src/modules/m_spanningtree/override_map.cpp b/src/modules/m_spanningtree/override_map.cpp index 01423b60a..0592eeaf1 100644 --- a/src/modules/m_spanningtree/override_map.cpp +++ b/src/modules/m_spanningtree/override_map.cpp @@ -1,223 +1,240 @@ -/* +------------------------------------+ - * | Inspire Internet Relay Chat Daemon | - * +------------------------------------+ +/* + * InspIRCd -- Internet Relay Chat Daemon * - * InspIRCd: (C) 2002-2008 InspIRCd Development Team - * See: http://www.inspircd.org/wiki/index.php/Credits + * Copyright (C) 2020 Matt Schatz + * Copyright (C) 2017 B00mX0r + * Copyright (C) 2016, 2018-2020 Sadie Powell + * Copyright (C) 2014 Adam + * Copyright (C) 2013-2016 Attila Molnar + * Copyright (C) 2012 Robby + * Copyright (C) 2009 Uli Schlachter + * Copyright (C) 2009 Daniel De Graaf + * Copyright (C) 2008 Thomas Stagner + * Copyright (C) 2008 Robin Burchell + * Copyright (C) 2007, 2010 Craig Edwards * - * This program is free but copyrighted software; see - * the file COPYING for details. + * This file is part of InspIRCd. InspIRCd is free software: you can + * redistribute it and/or modify it under the terms of the GNU General Public + * License as published by the Free Software Foundation, version 2. * - * --------------------------------------------------- + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS + * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more + * details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . */ -/* $ModDesc: Provides a spanning tree server link protocol */ - -#include "inspircd.h" -#include "wildcard.h" -#include "m_spanningtree/main.h" -#include "m_spanningtree/utils.h" -#include "m_spanningtree/treeserver.h" -#include "m_spanningtree/treesocket.h" +#include "inspircd.h" -/* $ModDep: m_spanningtree/main.h m_spanningtree/utils.h m_spanningtree/treeserver.h m_spanningtree/treesocket.h */ +#include "main.h" +#include "utils.h" +#include "treeserver.h" +#include "commands.h" -const std::string ModuleSpanningTree::MapOperInfo(TreeServer* Current) +CommandMap::CommandMap(Module* Creator) + : Command(Creator, "MAP", 0, 1) { - time_t secs_up = ServerInstance->Time() - Current->age; - return (" [Up: " + TimeToStr(secs_up) + " Lag: " + (Current->rtt == 0 ? "<1" : ConvToStr(Current->rtt)) + "ms]"); + allow_empty_last_param = false; + Penalty = 2; } - -// WARNING: NOT THREAD SAFE - DONT GET ANY SMART IDEAS. -void ModuleSpanningTree::ShowMap(TreeServer* Current, User* user, int depth, char matrix[128][128], float &totusers, float &totservers) + +static inline bool IsHidden(User* user, TreeServer* server) { - ServerInstance->Logs->Log("map",DEBUG,"ShowMap depth %d totusers %0.2f totservers %0.2f", depth, totusers, totservers); - if (line < 128) - { - for (int t = 0; t < depth; t++) - { - ServerInstance->Logs->Log("map",DEBUG,"Zero to depth"); - matrix[line][t] = ' '; - } - - // For Aligning, we need to work out exactly how deep this thing is, and produce - // a 'Spacer' String to compensate. - char spacer[40]; - memset(spacer,' ',40); - if ((40 - Current->GetName().length() - depth) > 1) { - spacer[40 - Current->GetName().length() - depth] = '\0'; - } - else - { - spacer[5] = '\0'; - } + if (!user->IsOper()) + { + if (server->Hidden) + return true; + if (Utils->HideULines && server->IsULine()) + return true; + } - float percent; - char text[128]; - /* Neat and tidy default values, as we're dealing with a matrix not a simple string */ - memset(text, 0, 128); + return false; +} - if (ServerInstance->Users->clientlist->size() == 0) - { - // If there are no users, WHO THE HELL DID THE /MAP?!?!?! - percent = 0; - } - else - { - percent = ((float)Current->GetUserCount() / (float)ServerInstance->Users->clientlist->size()) * 100; - } +// Calculate the map depth the servers go, and the longest server name +static void GetDepthAndLen(TreeServer* current, unsigned int depth, unsigned int& max_depth, unsigned int& max_len, unsigned int& max_version) +{ + if (depth > max_depth) + max_depth = depth; - const std::string operdata = IS_OPER(user) ? MapOperInfo(Current) : ""; - snprintf(text, 126, "%s (%s)%s%5d [%5.2f%%]%s", Current->GetName().c_str(), Current->GetID().c_str(), spacer, Current->GetUserCount(), percent, operdata.c_str()); - totusers += Current->GetUserCount(); - totservers++; - strlcpy(&matrix[line][depth],text,126); - line++; + if (current->GetName().length() > max_len) + max_len = current->GetName().length(); - ServerInstance->Logs->Log("map",DEBUG,"Increment line to %d, ChildCount %d", line, Current->ChildCount()); + if (current->GetRawVersion().length() > max_version) + max_version = current->GetRawVersion().length(); - for (unsigned int q = 0; q < Current->ChildCount(); q++) - { - ServerInstance->Logs->Log("map",DEBUG,"Hidden? %d HideULines? %d GetName %s", Current->GetChild(q)->Hidden, Utils->HideULines, Current->GetChild(q)->GetName().c_str()); - if ((Current->GetChild(q)->Hidden) || ((Utils->HideULines) && (ServerInstance->ULine(Current->GetChild(q)->GetName().c_str())))) - { - if (IS_OPER(user)) - { - ShowMap(Current->GetChild(q),user,(Utils->FlatLinks && (!IS_OPER(user))) ? depth : depth+2,matrix,totusers,totservers); - ServerInstance->Logs->Log("map",DEBUG,"Show to oper"); - } - ServerInstance->Logs->Log("map",DEBUG,"Fall through"); - } - else - { - ShowMap(Current->GetChild(q),user,(Utils->FlatLinks && (!IS_OPER(user))) ? depth : depth+2,matrix,totusers,totservers); - ServerInstance->Logs->Log("map",DEBUG,"Show to non oper"); - } - } - ServerInstance->Logs->Log("map",DEBUG,"After loop"); + const TreeServer::ChildServers& servers = current->GetChildren(); + for (TreeServer::ChildServers::const_iterator i = servers.begin(); i != servers.end(); ++i) + { + TreeServer* child = *i; + GetDepthAndLen(child, depth + 1, max_depth, max_len, max_version); } } - -// Ok, prepare to be confused. -// After much mulling over how to approach this, it struck me that -// the 'usual' way of doing a /MAP isnt the best way. Instead of -// keeping track of a ton of ascii characters, and line by line -// under recursion working out where to place them using multiplications -// and divisons, we instead render the map onto a backplane of characters -// (a character matrix), then draw the branches as a series of "L" shapes -// from the nodes. This is not only friendlier on CPU it uses less stack. -int ModuleSpanningTree::HandleMap(const std::vector& parameters, User* user) +static std::vector GetMap(User* user, TreeServer* current, unsigned int max_len, unsigned int max_version_len, unsigned int depth) { - if (parameters.size() > 0) + float percent = 0; + + const user_hash& users = ServerInstance->Users->GetUsers(); + if (!users.empty()) { - /* Remote MAP, the server is within the 1st parameter */ - TreeServer* s = Utils->FindServerMask(parameters[0]); - bool ret = false; - if (!s) - { - user->WriteServ( "402 %s %s :No such server", user->nick.c_str(), parameters[0].c_str()); - ret = true; - } - else if (s && s != Utils->TreeRoot) - { - std::deque params; - params.push_back(parameters[0]); + // If there are no users, WHO THE HELL DID THE /MAP?!?!?! + percent = current->UserCount * 100.0 / users.size(); + } - params[0] = s->GetName(); - Utils->DoOneToOne(user->uuid, "MAP", params, s->GetName()); - ret = true; - } + std::string buffer = current->GetName(); + if (user->IsOper()) + { + buffer += " (" + current->GetId(); + + const std::string& cur_vers = current->GetRawVersion(); + if (!cur_vers.empty()) + buffer += " " + cur_vers; - // Don't return if s == Utils->TreeRoot (us) - if (ret) - return 1; + buffer += ")"; + + buffer.append(max_version_len - current->GetRawVersion().length(), ' '); } - // This array represents a virtual screen which we will - // "scratch" draw to, as the console device of an irc - // client does not provide for a proper terminal. - float totusers = 0; - float totservers = 0; - static char matrix[128][128]; + // Pad with spaces until its at max len, max_len must always be >= my names length + buffer.append(max_len - current->GetName().length(), ' '); + + buffer += InspIRCd::Format("%5d [%5.2f%%]", current->UserCount, percent); - for (unsigned int t = 0; t < 128; t++) + if (user->IsOper()) { - matrix[t][0] = '\0'; + time_t secs_up = ServerInstance->Time() - current->age; + buffer += " [Up: " + InspIRCd::DurationString(secs_up) + (current->rtt == 0 ? "]" : " Lag: " + ConvToStr(current->rtt) + "ms]"); } - line = 0; + std::vector map; + map.push_back(buffer); - // The only recursive bit is called here. - ShowMap(Utils->TreeRoot,user,0,matrix,totusers,totservers); - - // Process each line one by one. The algorithm has a limit of - // 128 servers (which is far more than a spanning tree should have - // anyway, so we're ok). This limit can be raised simply by making - // the character matrix deeper, 128 rows taking 10k of memory. - for (int l = 1; l < line; l++) + const TreeServer::ChildServers& servers = current->GetChildren(); + for (TreeServer::ChildServers::const_iterator i = servers.begin(); i != servers.end(); ++i) { - // scan across the line looking for the start of the - // servername (the recursive part of the algorithm has placed - // the servers at indented positions depending on what they - // are related to) - int first_nonspace = 0; + TreeServer* child = *i; + + if (IsHidden(user, child)) + continue; + + bool last = true; + for (TreeServer::ChildServers::const_iterator j = i + 1; last && j != servers.end(); ++j) + if (!IsHidden(user, *j)) + last = false; - while (matrix[l][first_nonspace] == ' ') + unsigned int next_len; + + if (user->IsOper() || !Utils->FlatLinks) { - first_nonspace++; + // This child is indented by us, so remove the depth from the max length to align the users properly + next_len = max_len - 2; + } + else + { + // This user can not see depth, so max_len remains constant + next_len = max_len; } - first_nonspace--; + // Build the map for this child + std::vector child_map = GetMap(user, child, next_len, max_version_len, depth + 1); - // Draw the `- (corner) section: this may be overwritten by - // another L shape passing along the same vertical pane, becoming - // a |- (branch) section instead. + for (std::vector::const_iterator j = child_map.begin(); j != child_map.end(); ++j) + { + const char* prefix; - matrix[l][first_nonspace] = '-'; - matrix[l][first_nonspace-1] = '`'; - int l2 = l - 1; + if (user->IsOper() || !Utils->FlatLinks) + { + // If this server is not the root child + if (j != child_map.begin()) + { + // If this child is not my last child, then add | + // to be able to "link" the next server in my list to me, and to indent this child's servers + if (!last) + prefix = "| "; + // Otherwise this is my last child, so just use a space as there's nothing else linked to me below this + else + prefix = " "; + } + // If we get here, this server must be the root child + else + { + // If this is the last child, it gets a `- + if (last) + prefix = "`-"; + // Otherwise this isn't the last child, so it gets |- + else + prefix = "|-"; + } + } + else + // User can't see depth, so use no prefix + prefix = ""; - // Draw upwards until we hit the parent server, causing possibly - // other corners (`-) to become branches (|-) - while ((matrix[l2][first_nonspace-1] == ' ') || (matrix[l2][first_nonspace-1] == '`')) - { - matrix[l2][first_nonspace-1] = '|'; - l2--; + // Add line to the map + map.push_back(prefix + *j); } } - float avg_users = totusers / totservers; + return map; +} - // dump the whole lot to the user. - if (IS_LOCAL(user)) +CmdResult CommandMap::Handle(User* user, const Params& parameters) +{ + if (parameters.size() > 0) { - ServerInstance->Logs->Log("map",DEBUG,"local"); - for (int t = 0; t < line; t++) + // Remote MAP, the target server is the 1st parameter + TreeServer* s = Utils->FindServerMask(parameters[0]); + if (!s) { - user->WriteNumeric(6, "%s :%s",user->nick.c_str(),&matrix[t][0]); + user->WriteNumeric(ERR_NOSUCHSERVER, parameters[0], "No such server"); + return CMD_FAILURE; } - user->WriteNumeric(270, "%s :%.0f server%s and %.0f user%s, average %.2f users per server",user->nick.c_str(),totservers,(totservers > 1 ? "s" : ""),totusers,(totusers > 1 ? "s" : ""),avg_users); - user->WriteNumeric(7, "%s :End of /MAP",user->nick.c_str()); + + if (!s->IsRoot()) + return CMD_SUCCESS; + } + + // Max depth and max server name length + unsigned int max_depth = 0; + unsigned int max_len = 0; + unsigned int max_version = 0; + GetDepthAndLen(Utils->TreeRoot, 0, max_depth, max_len, max_version); + + unsigned int max; + if (user->IsOper() || !Utils->FlatLinks) + { + // Each level of the map is indented by 2 characters, making the max possible line (max_depth * 2) + max_len + max = (max_depth * 2) + max_len; } else { + // This user can't see any depth + max = max_len; + if (!user->IsOper()) + max_version = 0; + } - //void SpanningTreeProtocolInterface::PushToClient(User* target, const std::string &rawline) - // - ServerInstance->Logs->Log("map", DEBUG, "remote dump lines=%d", line); + std::vector map = GetMap(user, Utils->TreeRoot, max, max_version, 0); + for (std::vector::const_iterator i = map.begin(); i != map.end(); ++i) + user->WriteRemoteNumeric(RPL_MAP, *i); - for (int t = 0; t < line; t++) - { - ServerInstance->Logs->Log("map",DEBUG,"Dump %d", line); - ServerInstance->PI->PushToClient(user, std::string("::") + ServerInstance->Config->ServerName + " 006 " + user->nick + " :" + &matrix[t][0]); - } + size_t totusers = ServerInstance->Users->GetUsers().size(); + float avg_users = (float) totusers / Utils->serverlist.size(); - ServerInstance->PI->PushToClient(user, std::string("::") + ServerInstance->Config->ServerName + " 270 " + user->nick + " :" + ConvToStr(totservers) + " server"+(totservers > 1 ? "s" : "") + " and " + ConvToStr(totusers) + " user"+(totusers > 1 ? "s" : "") + ", average " + ConvToStr(avg_users) + " users per server"); - ServerInstance->PI->PushToClient(user, std::string("::") + ServerInstance->Config->ServerName + " 007 " + user->nick + " :End of /MAP"); - } + user->WriteRemoteNumeric(RPL_MAPUSERS, InspIRCd::Format("%u server%s and %u user%s, average %.2f users per server", + (unsigned int)Utils->serverlist.size(), (Utils->serverlist.size() > 1 ? "s" : ""), (unsigned int)totusers, (totusers > 1 ? "s" : ""), avg_users)); + user->WriteRemoteNumeric(RPL_ENDMAP, "End of /MAP"); - return 1; + return CMD_SUCCESS; } +RouteDescriptor CommandMap::GetRouting(User* user, const Params& parameters) +{ + if (!parameters.empty()) + return ROUTE_UNICAST(parameters[0]); + return ROUTE_LOCALONLY; +}