IMPORTANT: due to a drive failure, as of 13-Mar-2021, the Mercurial repository had to be re-mirrored, which changed every commit SHA. The old SHAs and trees are backed up in the vault branches. Please migrate to the new branches as soon as you can.
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
prosody/util/xmlrpc.lua

182 lines
5.7 KiB

-- Prosody IM
-- Copyright (C) 2008-2010 Matthew Wild
-- Copyright (C) 2008-2010 Waqas Hussain
--
-- This project is MIT/X11 licensed. Please see the
-- COPYING file in the source package for more information.
--
local pairs = pairs;
local type = type;
local error = error;
local t_concat = table.concat;
local t_insert = table.insert;
local tostring = tostring;
local tonumber = tonumber;
local select = select;
local st = require "util.stanza";
module "xmlrpc"
local _lua_to_xmlrpc;
local map = {
table=function(stanza, object)
stanza:tag("struct");
for name, value in pairs(object) do
stanza:tag("member");
stanza:tag("name"):text(tostring(name)):up();
stanza:tag("value");
_lua_to_xmlrpc(stanza, value);
stanza:up();
stanza:up();
end
stanza:up();
end;
boolean=function(stanza, object)
stanza:tag("boolean"):text(object and "1" or "0"):up();
end;
string=function(stanza, object)
stanza:tag("string"):text(object):up();
end;
number=function(stanza, object)
stanza:tag("int"):text(tostring(object)):up();
end;
["nil"]=function(stanza, object) -- nil extension
stanza:tag("nil"):up();
end;
};
_lua_to_xmlrpc = function(stanza, object)
local h = map[type(object)];
if h then
h(stanza, object);
else
error("Type not supported by XML-RPC: " .. type(object));
end
end
function create_response(object)
local stanza = st.stanza("methodResponse"):tag("params"):tag("param"):tag("value");
_lua_to_xmlrpc(stanza, object);
stanza:up():up():up();
return stanza;
end
function create_error_response(faultCode, faultString)
local stanza = st.stanza("methodResponse"):tag("fault"):tag("value");
_lua_to_xmlrpc(stanza, {faultCode=faultCode, faultString=faultString});
stanza:up():up();
return stanza;
end
function create_request(method_name, ...)
local stanza = st.stanza("methodCall")
:tag("methodName"):text(method_name):up()
:tag("params");
for i=1,select('#', ...) do
stanza:tag("param"):tag("value");
_lua_to_xmlrpc(stanza, select(i, ...));
stanza:up():up();
end
stanza:up():up():up();
return stanza;
end
local _xmlrpc_to_lua;
local int_parse = function(stanza)
if #stanza.tags ~= 0 or #stanza == 0 then error("<"..stanza.name.."> must have a single text child"); end
local n = tonumber(t_concat(stanza));
if n then return n; end
error("Failed to parse content of <"..stanza.name..">");
end
local rmap = {
methodCall=function(stanza)
if #stanza.tags ~= 2 then error("<methodCall> must have exactly two subtags"); end -- FIXME <params> is optional
if stanza.tags[1].name ~= "methodName" then error("First <methodCall> child tag must be <methodName>") end
if stanza.tags[2].name ~= "params" then error("Second <methodCall> child tag must be <params>") end
return _xmlrpc_to_lua(stanza.tags[1]), _xmlrpc_to_lua(stanza.tags[2]);
end;
methodName=function(stanza)
if #stanza.tags ~= 0 then error("<methodName> must not have any subtags"); end
if #stanza == 0 then error("<methodName> must have text content"); end
return t_concat(stanza);
end;
params=function(stanza)
local t = {};
for _, child in pairs(stanza.tags) do
if child.name ~= "param" then error("<params> can only have <param> children"); end;
t_insert(t, _xmlrpc_to_lua(child));
end
return t;
end;
param=function(stanza)
if not(#stanza.tags == 1 and stanza.tags[1].name == "value") then error("<param> must have exactly one <value> child"); end
return _xmlrpc_to_lua(stanza.tags[1]);
end;
value=function(stanza)
if #stanza.tags == 0 then return t_concat(stanza); end
if #stanza.tags ~= 1 then error("<value> must have a single child"); end
return _xmlrpc_to_lua(stanza.tags[1]);
end;
int=int_parse;
i4=int_parse;
double=int_parse;
boolean=function(stanza)
if #stanza.tags ~= 0 or #stanza == 0 then error("<boolean> must have a single text child"); end
local b = t_concat(stanza);
if b ~= "1" and b ~= "0" then error("Failed to parse content of <boolean>"); end
return b == "1" and true or false;
end;
string=function(stanza)
if #stanza.tags ~= 0 then error("<string> must have a single text child"); end
return t_concat(stanza);
end;
array=function(stanza)
if #stanza.tags ~= 1 then error("<array> must have a single <data> child"); end
return _xmlrpc_to_lua(stanza.tags[1]);
end;
data=function(stanza)
local t = {};
for _,child in pairs(stanza.tags) do
if child.name ~= "value" then error("<data> can only have <value> children"); end
t_insert(t, _xmlrpc_to_lua(child));
end
return t;
end;
struct=function(stanza)
local t = {};
for _,child in pairs(stanza.tags) do
if child.name ~= "member" then error("<struct> can only have <member> children"); end
local name, value = _xmlrpc_to_lua(child);
t[name] = value;
end
return t;
end;
member=function(stanza)
if #stanza.tags ~= 2 then error("<member> must have exactly two subtags"); end -- FIXME <params> is optional
if stanza.tags[1].name ~= "name" then error("First <member> child tag must be <name>") end
if stanza.tags[2].name ~= "value" then error("Second <member> child tag must be <value>") end
return _xmlrpc_to_lua(stanza.tags[1]), _xmlrpc_to_lua(stanza.tags[2]);
end;
name=function(stanza)
if #stanza.tags ~= 0 then error("<name> must have a single text child"); end
local n = t_concat(stanza)
if tostring(tonumber(n)) == n then n = tonumber(n); end
return n;
end;
["nil"]=function(stanza) -- nil extension
return nil;
end;
}
_xmlrpc_to_lua = function(stanza)
local h = rmap[stanza.name];
if h then
return h(stanza);
else
error("Unknown element: "..stanza.name);
end
end
function translate_request(stanza)
if stanza.name ~= "methodCall" then error("XML-RPC requests must have <methodCall> as root element"); end
return _xmlrpc_to_lua(stanza);
end
return _M;