summaryrefslogtreecommitdiff
path: root/modules/xmlrpc.cpp
diff options
context:
space:
mode:
authorSadie Powell <sadie@witchery.services>2024-01-24 12:01:50 +0000
committerSadie Powell <sadie@witchery.services>2024-01-24 12:22:51 +0000
commit7ac1fe58478d58e2480b6919c4abf3a82929169c (patch)
tree198ad9a6e23d4c189dce57fd95306b6b22d8c23f /modules/xmlrpc.cpp
parent72acef4e159df5dcdb93b3c13b2f9d2e5e4c21a9 (diff)
Rename several modules to remove the m_ prefix.
Diffstat (limited to 'modules/xmlrpc.cpp')
-rw-r--r--modules/xmlrpc.cpp229
1 files changed, 229 insertions, 0 deletions
diff --git a/modules/xmlrpc.cpp b/modules/xmlrpc.cpp
new file mode 100644
index 000000000..319730be0
--- /dev/null
+++ b/modules/xmlrpc.cpp
@@ -0,0 +1,229 @@
+/*
+ *
+ * (C) 2010-2024 Anope Team
+ * Contact us at team@anope.org
+ *
+ * Please read COPYING and README for further details.
+ */
+
+#include "module.h"
+#include "modules/xmlrpc.h"
+#include "modules/httpd.h"
+
+static struct special_chars final
+{
+ Anope::string character;
+ Anope::string replace;
+
+ special_chars(const Anope::string &c, const Anope::string &r) : character(c), replace(r) { }
+}
+special[] = {
+ special_chars("&", "&amp;"),
+ special_chars("\"", "&quot;"),
+ special_chars("<", "&lt;"),
+ special_chars(">", "&qt;"),
+ special_chars("'", "&#39;"),
+ special_chars("\n", "&#xA;"),
+ special_chars("\002", ""), // bold
+ special_chars("\003", ""), // color
+ special_chars("\035", ""), // italics
+ special_chars("\037", ""), // underline
+ special_chars("\026", ""), // reverses
+ special_chars("", "")
+};
+
+class MyXMLRPCServiceInterface final
+ : public XMLRPCServiceInterface
+ , public HTTPPage
+{
+ std::deque<XMLRPCEvent *> events;
+
+public:
+ MyXMLRPCServiceInterface(Module *creator, const Anope::string &sname) : XMLRPCServiceInterface(creator, sname), HTTPPage("/xmlrpc", "text/xml") { }
+
+ void Register(XMLRPCEvent *event) override
+ {
+ this->events.push_back(event);
+ }
+
+ void Unregister(XMLRPCEvent *event) override
+ {
+ std::deque<XMLRPCEvent *>::iterator it = std::find(this->events.begin(), this->events.end(), event);
+
+ if (it != this->events.end())
+ this->events.erase(it);
+ }
+
+ Anope::string Sanitize(const Anope::string &string) override
+ {
+ Anope::string ret = string;
+ for (int i = 0; special[i].character.empty() == false; ++i)
+ ret = ret.replace_all_cs(special[i].character, special[i].replace);
+ return ret;
+ }
+
+ static Anope::string Unescape(const Anope::string &string)
+ {
+ Anope::string ret = string;
+ for (int i = 0; special[i].character.empty() == false; ++i)
+ if (!special[i].replace.empty())
+ ret = ret.replace_all_cs(special[i].replace, special[i].character);
+
+ for (size_t i, last = 0; (i = string.find("&#", last)) != Anope::string::npos;)
+ {
+ last = i + 1;
+
+ size_t end = string.find(';', i);
+ if (end == Anope::string::npos)
+ break;
+
+ Anope::string ch = string.substr(i + 2, end - (i + 2));
+
+ if (ch.empty())
+ continue;
+
+ long l;
+ if (!ch.empty() && ch[0] == 'x')
+ l = strtol(ch.substr(1).c_str(), NULL, 16);
+ else
+ l = strtol(ch.c_str(), NULL, 10);
+
+ if (l > 0 && l < 256)
+ ret = ret.replace_all_cs("&#" + ch + ";", Anope::string(l));
+ }
+
+ return ret;
+ }
+
+private:
+ static bool GetData(Anope::string &content, Anope::string &tag, Anope::string &data)
+ {
+ if (content.empty())
+ return false;
+
+ Anope::string prev, cur;
+ bool istag;
+
+ do
+ {
+ prev = cur;
+ cur.clear();
+
+ size_t len = 0;
+ istag = false;
+
+ if (content[0] == '<')
+ {
+ len = content.find_first_of('>');
+ istag = true;
+ }
+ else if (content[0] != '>')
+ {
+ len = content.find_first_of('<');
+ }
+
+ // len must advance
+ if (len == Anope::string::npos || len == 0)
+ break;
+
+ if (istag)
+ {
+ cur = content.substr(1, len - 1);
+ content.erase(0, len + 1);
+ while (!content.empty() && content[0] == ' ')
+ content.erase(content.begin());
+ }
+ else
+ {
+ cur = content.substr(0, len);
+ content.erase(0, len);
+ }
+ }
+ while (istag && !content.empty());
+
+ tag = Unescape(prev);
+ data = Unescape(cur);
+ return !istag && !data.empty();
+ }
+
+public:
+ bool OnRequest(HTTPProvider *provider, const Anope::string &page_name, HTTPClient *client, HTTPMessage &message, HTTPReply &reply) override
+ {
+ Anope::string content = message.content, tname, data;
+ XMLRPCRequest request(reply);
+
+ while (GetData(content, tname, data))
+ {
+ Log(LOG_DEBUG) << "xmlrpc: Tag name: " << tname << ", data: " << data;
+ if (tname == "methodName")
+ request.name = data;
+ else if (tname == "name" && data == "id")
+ {
+ GetData(content, tname, data);
+ request.id = data;
+ }
+ else if (tname == "string")
+ request.data.push_back(data);
+ }
+
+ for (auto *e : this->events)
+ {
+ if (!e->Run(this, client, request))
+ return false;
+ else if (!request.get_replies().empty())
+ {
+ this->Reply(request);
+ return true;
+ }
+ }
+
+ reply.error = HTTP_PAGE_NOT_FOUND;
+ reply.Write("Unrecognized query");
+ return true;
+ }
+
+ void Reply(XMLRPCRequest &request) override
+ {
+ if (!request.id.empty())
+ request.reply("id", request.id);
+
+ Anope::string r = "<?xml version=\"1.0\" encoding=\"iso-8859-1\"?>\n<methodResponse>\n<params>\n<param>\n<value>\n<struct>\n";
+ for (const auto &[name, value] : request.get_replies())
+ r += "<member>\n<name>" + name + "</name>\n<value>\n<string>" + this->Sanitize(value) + "</string>\n</value>\n</member>\n";
+ r += "</struct>\n</value>\n</param>\n</params>\n</methodResponse>";
+
+ request.r.Write(r);
+ }
+};
+
+class ModuleXMLRPC final
+ : public Module
+{
+ ServiceReference<HTTPProvider> httpref;
+public:
+ MyXMLRPCServiceInterface xmlrpcinterface;
+
+ ModuleXMLRPC(const Anope::string &modname, const Anope::string &creator) : Module(modname, creator, EXTRA | VENDOR),
+ xmlrpcinterface(this, "xmlrpc")
+ {
+
+ }
+
+ ~ModuleXMLRPC() override
+ {
+ if (httpref)
+ httpref->UnregisterPage(&xmlrpcinterface);
+ }
+
+ void OnReload(Configuration::Conf *conf) override
+ {
+ if (httpref)
+ httpref->UnregisterPage(&xmlrpcinterface);
+ this->httpref = ServiceReference<HTTPProvider>("HTTPProvider", conf->GetModule(this)->Get<const Anope::string>("server", "httpd/main"));
+ if (!httpref)
+ throw ConfigException("Unable to find http reference, is httpd loaded?");
+ httpref->RegisterPage(&xmlrpcinterface);
+ }
+};
+
+MODULE_INIT(ModuleXMLRPC)