aboutsummaryrefslogtreecommitdiffstats
path: root/util/xmlrpc.lua
blob: 29815b0d94fc3a427642475fdc2185b486de0b5c (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
-- 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;