aboutsummaryrefslogtreecommitdiffstats
path: root/src/modules/m_httpd_stats.cpp
diff options
context:
space:
mode:
authorGravatar Sadie Powell2024-07-17 00:06:50 +0100
committerGravatar Sadie Powell2024-07-17 00:06:50 +0100
commit889d521e05f2e922683d48c74eb00290e7a2f548 (patch)
treeb4c56bd4cdf0881601c7e4d6ff571e491af10bf4 /src/modules/m_httpd_stats.cpp
parentUse std::endian from C++20 in the sha1 module. (diff)
Shuffle the modules about a bit.
Diffstat (limited to 'src/modules/m_httpd_stats.cpp')
-rw-r--r--src/modules/m_httpd_stats.cpp506
1 files changed, 0 insertions, 506 deletions
diff --git a/src/modules/m_httpd_stats.cpp b/src/modules/m_httpd_stats.cpp
deleted file mode 100644
index cc86c8830..000000000
--- a/src/modules/m_httpd_stats.cpp
+++ /dev/null
@@ -1,506 +0,0 @@
-/*
- * InspIRCd -- Internet Relay Chat Daemon
- *
- * Copyright (C) 2019 linuxdaemon <linuxdaemon.irc@gmail.com>
- * Copyright (C) 2016 Johanna A
- * Copyright (C) 2013, 2020-2023 Sadie Powell <sadie@witchery.services>
- * Copyright (C) 2013, 2015 Attila Molnar <attilamolnar@hush.com>
- * Copyright (C) 2012 Robby <robby@chatbelgie.be>
- * Copyright (C) 2009 Daniel De Graaf <danieldg@inspircd.org>
- * Copyright (C) 2008 Robin Burchell <robin+git@viroteck.net>
- * Copyright (C) 2008 Pippijn van Steenhoven <pip88nl@gmail.com>
- * Copyright (C) 2007 John Brooks <john@jbrooks.io>
- * Copyright (C) 2007 Dennis Friis <peavey@inspircd.org>
- * Copyright (C) 2006-2008 Craig Edwards <brain@inspircd.org>
- *
- * 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 <http://www.gnu.org/licenses/>.
- */
-
-
-#include "inspircd.h"
-#include "extension.h"
-#include "modules/isupport.h"
-#include "modules/httpd.h"
-#include "xline.h"
-
-#include <stack>
-
-static ISupport::EventProvider* isevprov;
-
-namespace Stats
-{
- static const insp::flat_map<char, char const*> xmlentities = {
- { '<', "lt" },
- { '>', "gt" },
- { '&', "amp" },
- { '"', "quot" },
- };
-
- std::string Sanitize(const std::string& str)
- {
- std::string ret;
- ret.reserve(str.length() * 2);
-
- for (const auto chr : str)
- {
- insp::flat_map<char, char const*>::const_iterator it = xmlentities.find(chr);
- if (it != xmlentities.end())
- {
- ret += '&';
- ret += it->second;
- ret += ';';
- }
- else if (chr == 0x09 || chr == 0x0A || chr == 0x0D || ((chr >= 0x20) && (chr <= 0x7e)))
- {
- // The XML specification defines the following characters as valid inside an XML document:
- // Char ::= #x9 | #xA | #xD | [#x20-#xD7FF] | [#xE000-#xFFFD] | [#x10000-#x10FFFF]
- ret += chr;
- }
- else
- {
- // If we reached this point then the string contains characters which can
- // not be represented in XML, even using a numeric escape. Therefore, we
- // Base64 encode the entire string and wrap it in a CDATA.
- ret.clear();
- ret += "<![CDATA[";
- ret += Base64::Encode(str);
- ret += "]]>";
- break;
- }
- }
- return ret;
- }
-
- class XMLSerializer final
- {
- private:
- std::stack<const char*> blocks;
- std::stringstream data;
-
- public:
- XMLSerializer& Attribute(const char* name, const std::string& value)
- {
- if (value.empty())
- data << '<' << name << "/>";
- else
- data << '<' << name << '>' << Sanitize(value) << "</" << name << '>';
- return *this;
- }
-
- template<typename Numeric>
- std::enable_if_t<std::is_arithmetic_v<Numeric>, XMLSerializer&> Attribute(const char* name, const Numeric& value)
- {
- return Attribute(name, ConvToStr(value));
- }
-
- std::stringstream* GetData() { return &data; }
-
- XMLSerializer& BeginBlock(const char* name)
- {
- blocks.push(name);
- data << '<' << name << '>';
- return *this;
- }
-
- XMLSerializer& EndBlock()
- {
- const char* name = blocks.top();
- data << "</" << name << '>';
- blocks.pop();
- return *this;
- }
- };
-
- void DumpMeta(XMLSerializer& serializer, Extensible* ext)
- {
- serializer.BeginBlock("metadata");
- for (const auto& [item, obj] : ext->GetExtList())
- {
- serializer.BeginBlock("meta")
- .Attribute("name", item->name);
-
- const std::string value = item->ToHuman(ext, obj);
- serializer.Attribute("value", value)
- .EndBlock();
- }
- serializer.EndBlock();
- }
-
- void ServerInfo(XMLSerializer& serializer)
- {
- serializer.BeginBlock("server")
- .Attribute("id", ServerInstance->Config->ServerId)
- .Attribute("name", ServerInstance->Config->ServerName)
- .Attribute("description", ServerInstance->Config->ServerDesc)
- .Attribute("customversion", ServerInstance->Config->CustomVersion)
- .Attribute("version", INSPIRCD_VERSION)
- .EndBlock();
- }
-
- void ISupport(XMLSerializer& serializer)
- {
- ISupport::TokenMap tokens;
- isevprov->Call(&ISupport::EventListener::OnBuildISupport, tokens);
-
- serializer.BeginBlock("isupport");
- for (const auto& [key, value] : tokens)
- {
- serializer.BeginBlock("token")
- .Attribute("name", key)
- .Attribute("value", value)
- .EndBlock();
- }
- serializer.EndBlock();
- }
-
- void General(XMLSerializer& serializer)
- {
- serializer.BeginBlock("general")
- .Attribute("usercount", ServerInstance->Users.GetUsers().size())
- .Attribute("localusercount", ServerInstance->Users.GetLocalUsers().size())
- .Attribute("channelcount", ServerInstance->Channels.GetChans().size())
- .Attribute("opercount", ServerInstance->Users.all_opers.size())
- .Attribute("socketcount", SocketEngine::GetUsedFds())
- .Attribute("socketmax", SocketEngine::GetMaxFds())
- .Attribute("boottime", ServerInstance->startup_time)
- .Attribute("currenttime", ServerInstance->Time());
-
- ISupport(serializer);
- serializer.EndBlock();
- }
-
- void XLines(XMLSerializer& serializer)
- {
- serializer.BeginBlock("xlines");
- for (const auto& xltype : ServerInstance->XLines->GetAllTypes())
- {
- XLineLookup* lookup = ServerInstance->XLines->GetAll(xltype);
- if (!lookup)
- continue;
-
- for (const auto& [_, xline] : *lookup)
- {
- serializer.BeginBlock("xline")
- .Attribute("type", xltype)
- .Attribute("mask", xline->Displayable())
- .Attribute("settime", xline->set_time)
- .Attribute("duration", xline->duration)
- .Attribute("reason", xline->reason)
- .EndBlock();
- }
- }
- serializer.EndBlock();
- }
-
- void Modules(XMLSerializer& serializer)
- {
- serializer.BeginBlock("modulelist");
- for (const auto& [modname, mod] : ServerInstance->Modules.GetModules())
- {
- serializer.BeginBlock("module")
- .Attribute("name", modname)
- .Attribute("description", mod->description)
- .EndBlock();
- }
- serializer.EndBlock();
- }
-
- void Channels(XMLSerializer& serializer)
- {
- serializer.BeginBlock("channellist");
- for (const auto& [_, c] : ServerInstance->Channels.GetChans())
- {
- serializer.BeginBlock("channel")
- .Attribute("channelname", c->name)
- .Attribute("usercount", c->GetUsers().size())
- .Attribute("channelmodes", c->ChanModes(true));
-
- if (!c->topic.empty())
- {
- serializer.BeginBlock("channeltopic")
- .Attribute("topictext", c->topic)
- .Attribute("setby", c->setby)
- .Attribute("settime", c->topicset)
- .EndBlock();
- }
-
- for (const auto& [__, memb] : c->GetUsers())
- {
- serializer.BeginBlock("channelmember")
- .Attribute("uid", memb->user->uuid)
- .Attribute("privs", memb->GetAllPrefixChars())
- .Attribute("modes", memb->GetAllPrefixModes());
-
- DumpMeta(serializer, memb);
- serializer.EndBlock();
- }
-
- DumpMeta(serializer, c);
- serializer.EndBlock();
- }
- serializer.EndBlock();
- }
-
- void DumpUser(XMLSerializer& serializer, User* u)
- {
- serializer.BeginBlock("user")
- .Attribute("nickname", u->nick)
- .Attribute("uuid", u->uuid)
- .Attribute("realhost", u->GetRealHost())
- .Attribute("displayhost", u->GetDisplayedHost())
- .Attribute("realname", u->GetRealName())
- .Attribute("server", u->server->GetName())
- .Attribute("signon", u->signon)
- .Attribute("nickchanged", u->nickchanged)
- .Attribute("modes", u->GetModeLetters().substr(1))
- .Attribute("realuser", u->GetRealUser())
- .Attribute("displayuser", u->GetDisplayedUser())
- .Attribute("ipaddress", u->GetAddress());
-
- if (u->IsAway())
- {
- serializer.Attribute("away", u->away->message)
- .Attribute("awaytime", u->away->time);
- }
-
- if (u->IsOper())
- serializer.Attribute("opertype", u->oper->GetType());
-
- LocalUser* lu = IS_LOCAL(u);
- if (lu)
- {
- serializer.Attribute("port", lu->server_sa.port())
- .Attribute("servaddr", lu->server_sa.addr())
- .Attribute("connectclass", lu->GetClass()->GetName())
- .Attribute("lastmsg", lu->idle_lastmsg);
- }
-
- DumpMeta(serializer, u);
- serializer.EndBlock();
- }
-
- void Users(XMLSerializer& serializer)
- {
- serializer.BeginBlock("userlist");
- for (const auto& [_, u] : ServerInstance->Users.GetUsers())
- {
- if (!u->IsFullyConnected())
- continue;
-
- DumpUser(serializer, u);
- }
- serializer.EndBlock();
- }
-
- void Servers(XMLSerializer& serializer)
- {
- ProtocolInterface::ServerList sl;
- ServerInstance->PI->GetServerList(sl);
-
- serializer.BeginBlock("serverlist");
- for (const auto& server : sl)
- {
- serializer.BeginBlock("server")
- .Attribute("servername", server.servername)
- .Attribute("parentname", server.parentname)
- .Attribute("description", server.description)
- .Attribute("usercount", server.usercount)
- .Attribute("opercount", server.opercount)
- .Attribute("lagmillisecs", server.latencyms)
- .EndBlock();
- }
- serializer.EndBlock();
- }
-
- void Commands(XMLSerializer& serializer)
- {
- serializer.BeginBlock("commandlist");
- for (const auto& [cmdname, cmd] : ServerInstance->Parser.GetCommands())
- {
- serializer.BeginBlock("command")
- .Attribute("name", cmdname)
- .Attribute("usecount", cmd->use_count)
- .EndBlock();
- }
- serializer.EndBlock();
- }
-
- enum OrderBy
- {
- OB_NICK,
- OB_LASTMSG,
-
- OB_NONE
- };
-
- struct UserSorter final
- {
- OrderBy order;
- bool desc;
-
- UserSorter(OrderBy Order, bool Desc = false)
- : order(Order)
- , desc(Desc)
- {
- }
-
- template <typename T>
- inline bool Compare(const T& a, const T& b)
- {
- return desc ? a > b : a < b;
- }
-
- bool operator()(User* u1, User* u2)
- {
- switch (order)
- {
- case OB_LASTMSG:
- return Compare(IS_LOCAL(u1)->idle_lastmsg, IS_LOCAL(u2)->idle_lastmsg);
- case OB_NICK:
- return Compare(u1->nick, u2->nick);
- default:
- return false;
- }
- }
- };
-
- void ListUsers(XMLSerializer& serializer, const HTTPQueryParameters& params)
- {
- if (params.empty())
- {
- Users(serializer);
- return;
- }
-
- serializer.BeginBlock("userlist");
-
- // Filters
- size_t limit = params.getNum<size_t>("limit");
- bool showunreg = params.getBool("showunreg");
- bool localonly = params.getBool("localonly");
-
- // Minimum time since a user's last message
- unsigned long min_idle = params.getDuration("minidle");
- time_t maxlastmsg = ServerInstance->Time() - min_idle;
-
- if (min_idle)
- // We can only check idle times on local users
- localonly = true;
-
- // Sorting
- const std::string& sortmethod = params.getString("sortby");
- bool desc = params.getBool("desc", false);
-
- OrderBy orderby;
- if (insp::equalsci(sortmethod, "nick"))
- orderby = OB_NICK;
- else if (insp::equalsci(sortmethod, "lastmsg"))
- {
- orderby = OB_LASTMSG;
- // We can only check idle times on local users
- localonly = true;
- }
- else
- orderby = OB_NONE;
-
- typedef std::list<User*> NewUserList;
- NewUserList user_list;
- for (const auto& [_, u] : ServerInstance->Users.GetUsers())
- {
- if (!showunreg && !u->IsFullyConnected())
- continue;
-
- LocalUser* lu = IS_LOCAL(u);
- if (localonly && !lu)
- continue;
-
- if (min_idle && lu->idle_lastmsg > maxlastmsg)
- continue;
-
- user_list.push_back(u);
- }
-
- UserSorter sorter(orderby, desc);
- if (sorter.order != OB_NONE && !(!localonly && sorter.order == OB_LASTMSG))
- user_list.sort(sorter);
-
- size_t count = 0;
- for (NewUserList::const_iterator i = user_list.begin(); i != user_list.end() && (!limit || count < limit); ++i, ++count)
- DumpUser(serializer, *i);
-
- serializer.EndBlock();
- }
-}
-
-class ModuleHttpStats final
- : public Module
- , public HTTPRequestEventListener
-{
-private:
- HTTPdAPI API;
- ISupport::EventProvider isupportevprov;
-
-public:
- ModuleHttpStats()
- : Module(VF_VENDOR, "Provides XML-serialised statistics about the server, channels, and users over HTTP via the /stats path.")
- , HTTPRequestEventListener(this)
- , API(this)
- , isupportevprov(this)
- {
- isevprov = &isupportevprov;
- }
-
- ModResult OnHTTPRequest(HTTPRequest& request) override
- {
- if (request.GetPath().compare(0, 6, "/stats"))
- return MOD_RES_PASSTHRU;
-
- ServerInstance->Logs.Debug(MODNAME, "Handling HTTP request for {}", request.GetPath());
-
- Stats::XMLSerializer serializer;
- serializer.BeginBlock("inspircdstats");
- if (request.GetPath() == "/stats")
- {
- Stats::ServerInfo(serializer);
- Stats::General(serializer);
- Stats::XLines(serializer);
- Stats::Modules(serializer);
- Stats::Channels(serializer);
- Stats::Users(serializer);
- Stats::Servers(serializer);
- Stats::Commands(serializer);
- }
- else if (request.GetPath() == "/stats/general")
- {
- Stats::General(serializer);
- }
- else if (request.GetPath() == "/stats/users")
- {
- Stats::ListUsers(serializer, request.GetParsedURI().query_params);
- }
- else
- {
- return MOD_RES_PASSTHRU;
- }
- serializer.EndBlock();
-
- /* Send the document back to m_httpd */
- HTTPDocumentResponse response(this, request, serializer.GetData(), 200);
- response.headers.SetHeader("X-Powered-By", MODNAME);
- response.headers.SetHeader("Content-Type", "text/xml");
- API->SendResponse(response);
- return MOD_RES_DENY; // Handled
- }
-};
-
-MODULE_INIT(ModuleHttpStats)