plugins/mod_xmlrpc.lua

Wed, 13 Jan 2010 00:04:38 +0000

author
Matthew Wild <mwild1@gmail.com>
date
Wed, 13 Jan 2010 00:04:38 +0000
changeset 2796
1e287badd033
parent 1640
726ac98306d8
child 2923
b7049746bd29
permissions
-rw-r--r--

prosodyctl: Use mode r+ for opening the file so 1) it fails if the file doesn't exist 2) we have write access to lock it

1523
841d61be198f Remove version number from copyright headers
Matthew Wild <mwild1@gmail.com>
parents: 894
diff changeset
1 -- Prosody IM
875
e73b6ef579c7 Added mod_xmlrpc
Waqas Hussain <waqas20@gmail.com>
parents:
diff changeset
2 -- Copyright (C) 2008-2009 Matthew Wild
e73b6ef579c7 Added mod_xmlrpc
Waqas Hussain <waqas20@gmail.com>
parents:
diff changeset
3 -- Copyright (C) 2008-2009 Waqas Hussain
e73b6ef579c7 Added mod_xmlrpc
Waqas Hussain <waqas20@gmail.com>
parents:
diff changeset
4 --
e73b6ef579c7 Added mod_xmlrpc
Waqas Hussain <waqas20@gmail.com>
parents:
diff changeset
5 -- This project is MIT/X11 licensed. Please see the
e73b6ef579c7 Added mod_xmlrpc
Waqas Hussain <waqas20@gmail.com>
parents:
diff changeset
6 -- COPYING file in the source package for more information.
e73b6ef579c7 Added mod_xmlrpc
Waqas Hussain <waqas20@gmail.com>
parents:
diff changeset
7 --
e73b6ef579c7 Added mod_xmlrpc
Waqas Hussain <waqas20@gmail.com>
parents:
diff changeset
8
e73b6ef579c7 Added mod_xmlrpc
Waqas Hussain <waqas20@gmail.com>
parents:
diff changeset
9
e73b6ef579c7 Added mod_xmlrpc
Waqas Hussain <waqas20@gmail.com>
parents:
diff changeset
10 module.host = "*" -- Global module
e73b6ef579c7 Added mod_xmlrpc
Waqas Hussain <waqas20@gmail.com>
parents:
diff changeset
11
e73b6ef579c7 Added mod_xmlrpc
Waqas Hussain <waqas20@gmail.com>
parents:
diff changeset
12 local httpserver = require "net.httpserver";
e73b6ef579c7 Added mod_xmlrpc
Waqas Hussain <waqas20@gmail.com>
parents:
diff changeset
13 local st = require "util.stanza";
e73b6ef579c7 Added mod_xmlrpc
Waqas Hussain <waqas20@gmail.com>
parents:
diff changeset
14 local pcall = pcall;
e73b6ef579c7 Added mod_xmlrpc
Waqas Hussain <waqas20@gmail.com>
parents:
diff changeset
15 local unpack = unpack;
e73b6ef579c7 Added mod_xmlrpc
Waqas Hussain <waqas20@gmail.com>
parents:
diff changeset
16 local tostring = tostring;
892
2128891180b7 mod_xmlrpc: Limit usage to admins
Waqas Hussain <waqas20@gmail.com>
parents: 889
diff changeset
17 local is_admin = require "core.usermanager".is_admin;
2128891180b7 mod_xmlrpc: Limit usage to admins
Waqas Hussain <waqas20@gmail.com>
parents: 889
diff changeset
18 local jid_split = require "util.jid".split;
1587
81992255a374 mod_xmlrpc: Added support for secure calls by non-admins
Waqas Hussain <waqas20@gmail.com>
parents: 1523
diff changeset
19 local jid_bare = require "util.jid".bare;
892
2128891180b7 mod_xmlrpc: Limit usage to admins
Waqas Hussain <waqas20@gmail.com>
parents: 889
diff changeset
20 local b64_decode = require "util.encodings".base64.decode;
2128891180b7 mod_xmlrpc: Limit usage to admins
Waqas Hussain <waqas20@gmail.com>
parents: 889
diff changeset
21 local get_method = require "core.objectmanager".get_object;
2128891180b7 mod_xmlrpc: Limit usage to admins
Waqas Hussain <waqas20@gmail.com>
parents: 889
diff changeset
22 local validate_credentials = require "core.usermanager".validate_credentials;
875
e73b6ef579c7 Added mod_xmlrpc
Waqas Hussain <waqas20@gmail.com>
parents:
diff changeset
23
e73b6ef579c7 Added mod_xmlrpc
Waqas Hussain <waqas20@gmail.com>
parents:
diff changeset
24 local translate_request = require "util.xmlrpc".translate_request;
e73b6ef579c7 Added mod_xmlrpc
Waqas Hussain <waqas20@gmail.com>
parents:
diff changeset
25 local create_response = require "util.xmlrpc".create_response;
e73b6ef579c7 Added mod_xmlrpc
Waqas Hussain <waqas20@gmail.com>
parents:
diff changeset
26 local create_error_response = require "util.xmlrpc".create_error_response;
e73b6ef579c7 Added mod_xmlrpc
Waqas Hussain <waqas20@gmail.com>
parents:
diff changeset
27
e73b6ef579c7 Added mod_xmlrpc
Waqas Hussain <waqas20@gmail.com>
parents:
diff changeset
28 local entity_map = setmetatable({
e73b6ef579c7 Added mod_xmlrpc
Waqas Hussain <waqas20@gmail.com>
parents:
diff changeset
29 ["amp"] = "&";
e73b6ef579c7 Added mod_xmlrpc
Waqas Hussain <waqas20@gmail.com>
parents:
diff changeset
30 ["gt"] = ">";
e73b6ef579c7 Added mod_xmlrpc
Waqas Hussain <waqas20@gmail.com>
parents:
diff changeset
31 ["lt"] = "<";
e73b6ef579c7 Added mod_xmlrpc
Waqas Hussain <waqas20@gmail.com>
parents:
diff changeset
32 ["apos"] = "'";
e73b6ef579c7 Added mod_xmlrpc
Waqas Hussain <waqas20@gmail.com>
parents:
diff changeset
33 ["quot"] = "\"";
e73b6ef579c7 Added mod_xmlrpc
Waqas Hussain <waqas20@gmail.com>
parents:
diff changeset
34 }, {__index = function(_, s)
e73b6ef579c7 Added mod_xmlrpc
Waqas Hussain <waqas20@gmail.com>
parents:
diff changeset
35 if s:sub(1,1) == "#" then
e73b6ef579c7 Added mod_xmlrpc
Waqas Hussain <waqas20@gmail.com>
parents:
diff changeset
36 if s:sub(2,2) == "x" then
e73b6ef579c7 Added mod_xmlrpc
Waqas Hussain <waqas20@gmail.com>
parents:
diff changeset
37 return string.char(tonumber(s:sub(3), 16));
e73b6ef579c7 Added mod_xmlrpc
Waqas Hussain <waqas20@gmail.com>
parents:
diff changeset
38 else
e73b6ef579c7 Added mod_xmlrpc
Waqas Hussain <waqas20@gmail.com>
parents:
diff changeset
39 return string.char(tonumber(s:sub(2)));
e73b6ef579c7 Added mod_xmlrpc
Waqas Hussain <waqas20@gmail.com>
parents:
diff changeset
40 end
e73b6ef579c7 Added mod_xmlrpc
Waqas Hussain <waqas20@gmail.com>
parents:
diff changeset
41 end
e73b6ef579c7 Added mod_xmlrpc
Waqas Hussain <waqas20@gmail.com>
parents:
diff changeset
42 end
e73b6ef579c7 Added mod_xmlrpc
Waqas Hussain <waqas20@gmail.com>
parents:
diff changeset
43 });
e73b6ef579c7 Added mod_xmlrpc
Waqas Hussain <waqas20@gmail.com>
parents:
diff changeset
44 local function xml_unescape(str)
e73b6ef579c7 Added mod_xmlrpc
Waqas Hussain <waqas20@gmail.com>
parents:
diff changeset
45 return (str:gsub("&(.-);", entity_map));
e73b6ef579c7 Added mod_xmlrpc
Waqas Hussain <waqas20@gmail.com>
parents:
diff changeset
46 end
e73b6ef579c7 Added mod_xmlrpc
Waqas Hussain <waqas20@gmail.com>
parents:
diff changeset
47 local function parse_xml(xml)
e73b6ef579c7 Added mod_xmlrpc
Waqas Hussain <waqas20@gmail.com>
parents:
diff changeset
48 local stanza = st.stanza("root");
e73b6ef579c7 Added mod_xmlrpc
Waqas Hussain <waqas20@gmail.com>
parents:
diff changeset
49 local regexp = "<([^>]*)>([^<]*)";
e73b6ef579c7 Added mod_xmlrpc
Waqas Hussain <waqas20@gmail.com>
parents:
diff changeset
50 for elem, text in xml:gmatch(regexp) do
e73b6ef579c7 Added mod_xmlrpc
Waqas Hussain <waqas20@gmail.com>
parents:
diff changeset
51 --print("[<"..elem..">|"..text.."]");
e73b6ef579c7 Added mod_xmlrpc
Waqas Hussain <waqas20@gmail.com>
parents:
diff changeset
52 if elem:sub(1,1) == "!" or elem:sub(1,1) == "?" then -- neglect comments and processing-instructions
e73b6ef579c7 Added mod_xmlrpc
Waqas Hussain <waqas20@gmail.com>
parents:
diff changeset
53 elseif elem:sub(1,1) == "/" then -- end tag
e73b6ef579c7 Added mod_xmlrpc
Waqas Hussain <waqas20@gmail.com>
parents:
diff changeset
54 elem = elem:sub(2);
e73b6ef579c7 Added mod_xmlrpc
Waqas Hussain <waqas20@gmail.com>
parents:
diff changeset
55 stanza:up(); -- TODO check for start-end tag name match
e73b6ef579c7 Added mod_xmlrpc
Waqas Hussain <waqas20@gmail.com>
parents:
diff changeset
56 elseif elem:sub(-1,-1) == "/" then -- empty tag
e73b6ef579c7 Added mod_xmlrpc
Waqas Hussain <waqas20@gmail.com>
parents:
diff changeset
57 elem = elem:sub(1,-2);
e73b6ef579c7 Added mod_xmlrpc
Waqas Hussain <waqas20@gmail.com>
parents:
diff changeset
58 stanza:tag(elem):up();
e73b6ef579c7 Added mod_xmlrpc
Waqas Hussain <waqas20@gmail.com>
parents:
diff changeset
59 else -- start tag
e73b6ef579c7 Added mod_xmlrpc
Waqas Hussain <waqas20@gmail.com>
parents:
diff changeset
60 stanza:tag(elem);
e73b6ef579c7 Added mod_xmlrpc
Waqas Hussain <waqas20@gmail.com>
parents:
diff changeset
61 end
e73b6ef579c7 Added mod_xmlrpc
Waqas Hussain <waqas20@gmail.com>
parents:
diff changeset
62 if #text ~= 0 then -- text
e73b6ef579c7 Added mod_xmlrpc
Waqas Hussain <waqas20@gmail.com>
parents:
diff changeset
63 stanza:text(xml_unescape(text));
e73b6ef579c7 Added mod_xmlrpc
Waqas Hussain <waqas20@gmail.com>
parents:
diff changeset
64 end
e73b6ef579c7 Added mod_xmlrpc
Waqas Hussain <waqas20@gmail.com>
parents:
diff changeset
65 end
e73b6ef579c7 Added mod_xmlrpc
Waqas Hussain <waqas20@gmail.com>
parents:
diff changeset
66 return stanza.tags[1];
e73b6ef579c7 Added mod_xmlrpc
Waqas Hussain <waqas20@gmail.com>
parents:
diff changeset
67 end
e73b6ef579c7 Added mod_xmlrpc
Waqas Hussain <waqas20@gmail.com>
parents:
diff changeset
68
1587
81992255a374 mod_xmlrpc: Added support for secure calls by non-admins
Waqas Hussain <waqas20@gmail.com>
parents: 1523
diff changeset
69 local function handle_xmlrpc_request(jid, method, args)
1590
638761692663 mod_xmlrpc: Fixed typos
Waqas Hussain <waqas20@gmail.com>
parents: 1587
diff changeset
70 local is_secure_call = (method:sub(1,7) == "secure/");
1587
81992255a374 mod_xmlrpc: Added support for secure calls by non-admins
Waqas Hussain <waqas20@gmail.com>
parents: 1523
diff changeset
71 if not is_admin(jid) and not is_secure_call then
81992255a374 mod_xmlrpc: Added support for secure calls by non-admins
Waqas Hussain <waqas20@gmail.com>
parents: 1523
diff changeset
72 return create_error_response(401, "not authorized");
81992255a374 mod_xmlrpc: Added support for secure calls by non-admins
Waqas Hussain <waqas20@gmail.com>
parents: 1523
diff changeset
73 end
875
e73b6ef579c7 Added mod_xmlrpc
Waqas Hussain <waqas20@gmail.com>
parents:
diff changeset
74 method = get_method(method);
e73b6ef579c7 Added mod_xmlrpc
Waqas Hussain <waqas20@gmail.com>
parents:
diff changeset
75 if not method then return create_error_response(404, "method not found"); end
e73b6ef579c7 Added mod_xmlrpc
Waqas Hussain <waqas20@gmail.com>
parents:
diff changeset
76 args = args or {};
1590
638761692663 mod_xmlrpc: Fixed typos
Waqas Hussain <waqas20@gmail.com>
parents: 1587
diff changeset
77 if is_secure_call then table.insert(args, 1, jid); end
875
e73b6ef579c7 Added mod_xmlrpc
Waqas Hussain <waqas20@gmail.com>
parents:
diff changeset
78 local success, result = pcall(method, unpack(args));
e73b6ef579c7 Added mod_xmlrpc
Waqas Hussain <waqas20@gmail.com>
parents:
diff changeset
79 if success then
e73b6ef579c7 Added mod_xmlrpc
Waqas Hussain <waqas20@gmail.com>
parents:
diff changeset
80 success, result = pcall(create_response, result or "nil");
e73b6ef579c7 Added mod_xmlrpc
Waqas Hussain <waqas20@gmail.com>
parents:
diff changeset
81 if success then
e73b6ef579c7 Added mod_xmlrpc
Waqas Hussain <waqas20@gmail.com>
parents:
diff changeset
82 return result;
e73b6ef579c7 Added mod_xmlrpc
Waqas Hussain <waqas20@gmail.com>
parents:
diff changeset
83 end
e73b6ef579c7 Added mod_xmlrpc
Waqas Hussain <waqas20@gmail.com>
parents:
diff changeset
84 return create_error_response(500, "Error in creating response: "..result);
e73b6ef579c7 Added mod_xmlrpc
Waqas Hussain <waqas20@gmail.com>
parents:
diff changeset
85 end
1640
726ac98306d8 mod_xmlrpc: Correct stripping of filename/line number prefix in RPC method error results
Matthew Wild <mwild1@gmail.com>
parents: 1591
diff changeset
86 return create_error_response(0, tostring(result):gsub("^[^:]+:%d+: ", ""));
875
e73b6ef579c7 Added mod_xmlrpc
Waqas Hussain <waqas20@gmail.com>
parents:
diff changeset
87 end
e73b6ef579c7 Added mod_xmlrpc
Waqas Hussain <waqas20@gmail.com>
parents:
diff changeset
88
877
0bababc930dd mod_xmlrpc: Handle RPC stanzas sent over XMPP (XEP-0009: Jabber-RPC)
Waqas Hussain <waqas20@gmail.com>
parents: 875
diff changeset
89 local function handle_xmpp_request(origin, stanza)
875
e73b6ef579c7 Added mod_xmlrpc
Waqas Hussain <waqas20@gmail.com>
parents:
diff changeset
90 local query = stanza.tags[1];
e73b6ef579c7 Added mod_xmlrpc
Waqas Hussain <waqas20@gmail.com>
parents:
diff changeset
91 if query.name == "query" then
e73b6ef579c7 Added mod_xmlrpc
Waqas Hussain <waqas20@gmail.com>
parents:
diff changeset
92 if #query.tags == 1 then
1587
81992255a374 mod_xmlrpc: Added support for secure calls by non-admins
Waqas Hussain <waqas20@gmail.com>
parents: 1523
diff changeset
93 local success, method, args = pcall(translate_request, query.tags[1]);
81992255a374 mod_xmlrpc: Added support for secure calls by non-admins
Waqas Hussain <waqas20@gmail.com>
parents: 1523
diff changeset
94 if success then
81992255a374 mod_xmlrpc: Added support for secure calls by non-admins
Waqas Hussain <waqas20@gmail.com>
parents: 1523
diff changeset
95 local result = handle_xmlrpc_request(jid_bare(stanza.attr.from), method, args);
81992255a374 mod_xmlrpc: Added support for secure calls by non-admins
Waqas Hussain <waqas20@gmail.com>
parents: 1523
diff changeset
96 origin.send(st.reply(stanza):tag('query', {xmlns='jabber:iq:rpc'}):add_child(result));
81992255a374 mod_xmlrpc: Added support for secure calls by non-admins
Waqas Hussain <waqas20@gmail.com>
parents: 1523
diff changeset
97 else
81992255a374 mod_xmlrpc: Added support for secure calls by non-admins
Waqas Hussain <waqas20@gmail.com>
parents: 1523
diff changeset
98 origin.send(st.error_reply(stanza, "modify", "bad-request", method));
81992255a374 mod_xmlrpc: Added support for secure calls by non-admins
Waqas Hussain <waqas20@gmail.com>
parents: 1523
diff changeset
99 end
892
2128891180b7 mod_xmlrpc: Limit usage to admins
Waqas Hussain <waqas20@gmail.com>
parents: 889
diff changeset
100 else origin.send(st.error_reply(stanza, "modify", "bad-request", "No content in XML-RPC request")); end
2128891180b7 mod_xmlrpc: Limit usage to admins
Waqas Hussain <waqas20@gmail.com>
parents: 889
diff changeset
101 else origin.send(st.error_reply(stanza, "cancel", "service-unavailable")); end
875
e73b6ef579c7 Added mod_xmlrpc
Waqas Hussain <waqas20@gmail.com>
parents:
diff changeset
102 end
877
0bababc930dd mod_xmlrpc: Handle RPC stanzas sent over XMPP (XEP-0009: Jabber-RPC)
Waqas Hussain <waqas20@gmail.com>
parents: 875
diff changeset
103 module:add_iq_handler({"c2s", "s2sin"}, "jabber:iq:rpc", handle_xmpp_request);
0bababc930dd mod_xmlrpc: Handle RPC stanzas sent over XMPP (XEP-0009: Jabber-RPC)
Waqas Hussain <waqas20@gmail.com>
parents: 875
diff changeset
104 module:add_feature("jabber:iq:rpc");
892
2128891180b7 mod_xmlrpc: Limit usage to admins
Waqas Hussain <waqas20@gmail.com>
parents: 889
diff changeset
105 -- TODO add <identity category='automation' type='rpc'/> to disco replies
875
e73b6ef579c7 Added mod_xmlrpc
Waqas Hussain <waqas20@gmail.com>
parents:
diff changeset
106
892
2128891180b7 mod_xmlrpc: Limit usage to admins
Waqas Hussain <waqas20@gmail.com>
parents: 889
diff changeset
107 local default_headers = { ['Content-Type'] = 'text/xml' };
2128891180b7 mod_xmlrpc: Limit usage to admins
Waqas Hussain <waqas20@gmail.com>
parents: 889
diff changeset
108 local unauthorized_response = { status = '401 UNAUTHORIZED', headers = {['Content-Type']='text/html', ['WWW-Authenticate']='Basic realm="WallyWorld"'}; body = "<html><body>Authentication required</body></html>"; };
875
e73b6ef579c7 Added mod_xmlrpc
Waqas Hussain <waqas20@gmail.com>
parents:
diff changeset
109 local function handle_http_request(method, body, request)
892
2128891180b7 mod_xmlrpc: Limit usage to admins
Waqas Hussain <waqas20@gmail.com>
parents: 889
diff changeset
110 -- authenticate user
2128891180b7 mod_xmlrpc: Limit usage to admins
Waqas Hussain <waqas20@gmail.com>
parents: 889
diff changeset
111 local username, password = b64_decode(request['authorization'] or ''):gmatch('([^:]*):(.*)')(); -- TODO digest auth
2128891180b7 mod_xmlrpc: Limit usage to admins
Waqas Hussain <waqas20@gmail.com>
parents: 889
diff changeset
112 local node, host = jid_split(username);
1587
81992255a374 mod_xmlrpc: Added support for secure calls by non-admins
Waqas Hussain <waqas20@gmail.com>
parents: 1523
diff changeset
113 if not validate_credentials(host, node, password) then
892
2128891180b7 mod_xmlrpc: Limit usage to admins
Waqas Hussain <waqas20@gmail.com>
parents: 889
diff changeset
114 return unauthorized_response;
2128891180b7 mod_xmlrpc: Limit usage to admins
Waqas Hussain <waqas20@gmail.com>
parents: 889
diff changeset
115 end
2128891180b7 mod_xmlrpc: Limit usage to admins
Waqas Hussain <waqas20@gmail.com>
parents: 889
diff changeset
116 -- parse request
875
e73b6ef579c7 Added mod_xmlrpc
Waqas Hussain <waqas20@gmail.com>
parents:
diff changeset
117 local stanza = body and parse_xml(body);
e73b6ef579c7 Added mod_xmlrpc
Waqas Hussain <waqas20@gmail.com>
parents:
diff changeset
118 if (not stanza) or request.method ~= "POST" then
e73b6ef579c7 Added mod_xmlrpc
Waqas Hussain <waqas20@gmail.com>
parents:
diff changeset
119 return "<html><body>You really don't look like an XML-RPC client to me... what do you want?</body></html>";
e73b6ef579c7 Added mod_xmlrpc
Waqas Hussain <waqas20@gmail.com>
parents:
diff changeset
120 end
892
2128891180b7 mod_xmlrpc: Limit usage to admins
Waqas Hussain <waqas20@gmail.com>
parents: 889
diff changeset
121 -- execute request
875
e73b6ef579c7 Added mod_xmlrpc
Waqas Hussain <waqas20@gmail.com>
parents:
diff changeset
122 local success, method, args = pcall(translate_request, stanza);
e73b6ef579c7 Added mod_xmlrpc
Waqas Hussain <waqas20@gmail.com>
parents:
diff changeset
123 if success then
1587
81992255a374 mod_xmlrpc: Added support for secure calls by non-admins
Waqas Hussain <waqas20@gmail.com>
parents: 1523
diff changeset
124 return { headers = default_headers; body = tostring(handle_xmlrpc_request(node.."@"..host, method, args)) };
875
e73b6ef579c7 Added mod_xmlrpc
Waqas Hussain <waqas20@gmail.com>
parents:
diff changeset
125 end
880
ff4a08d73772 XML-RPC: Set appropriate Content-Type header in HTTP response
Waqas Hussain <waqas20@gmail.com>
parents: 877
diff changeset
126 return "<html><body>Error parsing XML-RPC request: "..tostring(method).."</body></html>";
875
e73b6ef579c7 Added mod_xmlrpc
Waqas Hussain <waqas20@gmail.com>
parents:
diff changeset
127 end
e73b6ef579c7 Added mod_xmlrpc
Waqas Hussain <waqas20@gmail.com>
parents:
diff changeset
128 httpserver.new{ port = 9000, base = "xmlrpc", handler = handle_http_request }

mercurial