anope

- supernets anope source code & configuration
git clone git://git.acid.vegas/anope.git
Log | Files | Refs | Archive | README

m_xmlrpc.cpp (5724B)

      1 /*
      2  *
      3  * (C) 2010-2022 Anope Team
      4  * Contact us at team@anope.org
      5  *
      6  * Please read COPYING and README for further details.
      7  */
      8 
      9 #include "module.h"
     10 #include "modules/xmlrpc.h"
     11 #include "modules/httpd.h"
     12 
     13 static struct special_chars
     14 {
     15 	Anope::string character;
     16 	Anope::string replace;
     17 
     18 	special_chars(const Anope::string &c, const Anope::string &r) : character(c), replace(r) { }
     19 }
     20 special[] = {
     21 	special_chars("&", "&"),
     22 	special_chars("\"", """),
     23 	special_chars("<", "&lt;"),
     24 	special_chars(">", "&qt;"),
     25 	special_chars("'", "&#39;"),
     26 	special_chars("\n", "&#xA;"),
     27 	special_chars("\002", ""), // bold
     28 	special_chars("\003", ""), // color
     29 	special_chars("\035", ""), // italics
     30 	special_chars("\037", ""), // underline
     31 	special_chars("\026", ""), // reverses
     32 	special_chars("", "")
     33 };
     34 
     35 class MyXMLRPCServiceInterface : public XMLRPCServiceInterface, public HTTPPage
     36 {
     37 	std::deque<XMLRPCEvent *> events;
     38 
     39  public:
     40 	MyXMLRPCServiceInterface(Module *creator, const Anope::string &sname) : XMLRPCServiceInterface(creator, sname), HTTPPage("/xmlrpc", "text/xml") { }
     41 
     42 	void Register(XMLRPCEvent *event) anope_override
     43 	{
     44 		this->events.push_back(event);
     45 	}
     46 
     47 	void Unregister(XMLRPCEvent *event) anope_override
     48 	{
     49 		std::deque<XMLRPCEvent *>::iterator it = std::find(this->events.begin(), this->events.end(), event);
     50 
     51 		if (it != this->events.end())
     52 			this->events.erase(it);
     53 	}
     54 
     55 	Anope::string Sanitize(const Anope::string &string) anope_override
     56 	{
     57 		Anope::string ret = string;
     58 		for (int i = 0; special[i].character.empty() == false; ++i)
     59 			ret = ret.replace_all_cs(special[i].character, special[i].replace);
     60 		return ret;
     61 	}
     62 
     63 	static Anope::string Unescape(const Anope::string &string)
     64 	{
     65 		Anope::string ret = string;
     66 		for (int i = 0; special[i].character.empty() == false; ++i)
     67 			if (!special[i].replace.empty())
     68 				ret = ret.replace_all_cs(special[i].replace, special[i].character);
     69 
     70 		for (size_t i, last = 0; (i = string.find("&#", last)) != Anope::string::npos;)
     71 		{
     72 			last = i + 1;
     73 
     74 			size_t end = string.find(';', i);
     75 			if (end == Anope::string::npos)
     76 				break;
     77 
     78 			Anope::string ch = string.substr(i + 2, end - (i + 2));
     79 
     80 			if (ch.empty())
     81 				continue;
     82 
     83 			long l;
     84 			if (!ch.empty() && ch[0] == 'x')
     85 				l = strtol(ch.substr(1).c_str(), NULL, 16);
     86 			else
     87 				l = strtol(ch.c_str(), NULL, 10);
     88 
     89 			if (l > 0 && l < 256)
     90 				ret = ret.replace_all_cs("&#" + ch + ";", Anope::string(l));
     91 		}
     92 
     93 		return ret;
     94 	}
     95 
     96  private:
     97 	static bool GetData(Anope::string &content, Anope::string &tag, Anope::string &data)
     98 	{
     99 		if (content.empty())
    100 			return false;
    101 
    102 		Anope::string prev, cur;
    103 		bool istag;
    104 
    105 		do
    106 		{
    107 			prev = cur;
    108 			cur.clear();
    109 
    110 			size_t len = 0;
    111 			istag = false;
    112 
    113 			if (content[0] == '<')
    114 			{
    115 				len = content.find_first_of('>');
    116 				istag = true;
    117 			}
    118 			else if (content[0] != '>')
    119 			{
    120 				len = content.find_first_of('<');
    121 			}
    122 
    123 			// len must advance
    124 			if (len == Anope::string::npos || len == 0)
    125 				break;
    126 
    127 			if (istag)
    128 			{
    129 				cur = content.substr(1, len - 1);
    130 				content.erase(0, len + 1);
    131 				while (!content.empty() && content[0] == ' ')
    132 					content.erase(content.begin());
    133 			}
    134 			else
    135 			{
    136 				cur = content.substr(0, len);
    137 				content.erase(0, len);
    138 			}
    139 		}
    140 		while (istag && !content.empty());
    141 
    142 		tag = Unescape(prev);
    143 		data = Unescape(cur);
    144 		return !istag && !data.empty();
    145 	}
    146 
    147  public:
    148 	bool OnRequest(HTTPProvider *provider, const Anope::string &page_name, HTTPClient *client, HTTPMessage &message, HTTPReply &reply) anope_override
    149 	{
    150 		Anope::string content = message.content, tname, data;
    151 		XMLRPCRequest request(reply);
    152 
    153 		while (GetData(content, tname, data))
    154 		{
    155 			Log(LOG_DEBUG) << "m_xmlrpc: Tag name: " << tname << ", data: " << data;
    156 			if (tname == "methodName")
    157 				request.name = data;
    158 			else if (tname == "name" && data == "id")
    159 			{
    160 				GetData(content, tname, data);
    161 				request.id = data;
    162 			}
    163 			else if (tname == "string")
    164 				request.data.push_back(data);
    165 		}
    166 
    167 		for (unsigned i = 0; i < this->events.size(); ++i)
    168 		{
    169 			XMLRPCEvent *e = this->events[i];
    170 
    171 			if (!e->Run(this, client, request))
    172 				return false;
    173 			else if (!request.get_replies().empty())
    174 			{
    175 				this->Reply(request);
    176 				return true;
    177 			}
    178 		}
    179 
    180 		reply.error = HTTP_PAGE_NOT_FOUND;
    181 		reply.Write("Unrecognized query");
    182 		return true;
    183 	}
    184 
    185 	void Reply(XMLRPCRequest &request) anope_override
    186 	{
    187 		if (!request.id.empty())
    188 			request.reply("id", request.id);
    189 
    190 		Anope::string r = "<?xml version=\"1.0\" encoding=\"iso-8859-1\"?>\n<methodResponse>\n<params>\n<param>\n<value>\n<struct>\n";
    191 		for (std::map<Anope::string, Anope::string>::const_iterator it = request.get_replies().begin(); it != request.get_replies().end(); ++it)
    192 			r += "<member>\n<name>" + it->first + "</name>\n<value>\n<string>" + this->Sanitize(it->second) + "</string>\n</value>\n</member>\n";
    193 		r += "</struct>\n</value>\n</param>\n</params>\n</methodResponse>";
    194 
    195 		request.r.Write(r);
    196 	}
    197 };
    198 
    199 class ModuleXMLRPC : public Module
    200 {
    201 	ServiceReference<HTTPProvider> httpref;
    202  public:
    203 	MyXMLRPCServiceInterface xmlrpcinterface;
    204 
    205 	ModuleXMLRPC(const Anope::string &modname, const Anope::string &creator) : Module(modname, creator, EXTRA | VENDOR),
    206 		xmlrpcinterface(this, "xmlrpc")
    207 	{
    208 
    209 	}
    210 
    211 	~ModuleXMLRPC()
    212 	{
    213 		if (httpref)
    214 			httpref->UnregisterPage(&xmlrpcinterface);
    215 	}
    216 
    217 	void OnReload(Configuration::Conf *conf) anope_override
    218 	{
    219 		if (httpref)
    220 			httpref->UnregisterPage(&xmlrpcinterface);
    221 		this->httpref = ServiceReference<HTTPProvider>("HTTPProvider", conf->GetModule(this)->Get<const Anope::string>("server", "httpd/main"));
    222 		if (!httpref)
    223 			throw ConfigException("Unable to find http reference, is m_httpd loaded?");
    224 		httpref->RegisterPage(&xmlrpcinterface);
    225 	}
    226 };
    227 
    228 MODULE_INIT(ModuleXMLRPC)