util/xmlrpc.lua

1
-- Prosody IM
2
-- Copyright (C) 2008-2010 Matthew Wild
3
-- Copyright (C) 2008-2010 Waqas Hussain
4
-- 
5
-- This project is MIT/X11 licensed. Please see the
6
-- COPYING file in the source package for more information.
7
--
8
 
9
 
10
local pairs = pairs;
11
local type = type;
12
local error = error;
13
local t_concat = table.concat;
14
local t_insert = table.insert;
15
local tostring = tostring;
16
local tonumber = tonumber;
17
local select = select;
18
local st = require "util.stanza";
19
 
20
module "xmlrpc"
21
 
22
local _lua_to_xmlrpc;
23
local map = {
24
	table=function(stanza, object)
25
		stanza:tag("struct");
26
		for name, value in pairs(object) do
27
			stanza:tag("member");
28
				stanza:tag("name"):text(tostring(name)):up();
29
				stanza:tag("value");
30
					_lua_to_xmlrpc(stanza, value);
31
				stanza:up();
32
			stanza:up();
33
		end
34
		stanza:up();
35
	end;
36
	boolean=function(stanza, object)
37
		stanza:tag("boolean"):text(object and "1" or "0"):up();
38
	end;
39
	string=function(stanza, object)
40
		stanza:tag("string"):text(object):up();
41
	end;
42
	number=function(stanza, object)
43
		stanza:tag("int"):text(tostring(object)):up();
44
	end;
45
	["nil"]=function(stanza, object) -- nil extension
46
		stanza:tag("nil"):up();
47
	end;
48
};
49
_lua_to_xmlrpc = function(stanza, object)
50
	local h = map[type(object)];
51
	if h then
52
		h(stanza, object);
53
	else
54
		error("Type not supported by XML-RPC: " .. type(object));
55
	end
56
end
57
function create_response(object)
58
	local stanza = st.stanza("methodResponse"):tag("params"):tag("param"):tag("value");
59
	_lua_to_xmlrpc(stanza, object);
60
	stanza:up():up():up();
61
	return stanza;
62
end
63
function create_error_response(faultCode, faultString)
64
	local stanza = st.stanza("methodResponse"):tag("fault"):tag("value");
65
	_lua_to_xmlrpc(stanza, {faultCode=faultCode, faultString=faultString});
66
	stanza:up():up();
67
	return stanza;
68
end
69
 
70
function create_request(method_name, ...)
71
	local stanza = st.stanza("methodCall")
72
		:tag("methodName"):text(method_name):up()
73
		:tag("params");
74
	for i=1,select('#', ...) do
75
		stanza:tag("param"):tag("value");
76
		_lua_to_xmlrpc(stanza, select(i, ...));
77
		stanza:up():up();
78
	end
79
	stanza:up():up():up();
80
	return stanza;
81
end
82
 
83
local _xmlrpc_to_lua;
84
local int_parse = function(stanza)
85
	if #stanza.tags ~= 0 or #stanza == 0 then error("<"..stanza.name.."> must have a single text child"); end
86
	local n = tonumber(t_concat(stanza));
87
	if n then return n; end
88
	error("Failed to parse content of <"..stanza.name..">");
89
end
90
local rmap = {
91
	methodCall=function(stanza)
92
		if #stanza.tags ~= 2 then error("<methodCall> must have exactly two subtags"); end -- FIXME <params> is optional
93
		if stanza.tags[1].name ~= "methodName" then error("First <methodCall> child tag must be <methodName>") end
94
		if stanza.tags[2].name ~= "params" then error("Second <methodCall> child tag must be <params>") end
95
		return _xmlrpc_to_lua(stanza.tags[1]), _xmlrpc_to_lua(stanza.tags[2]);
96
	end;
97
	methodName=function(stanza)
98
		if #stanza.tags ~= 0 then error("<methodName> must not have any subtags"); end
99
		if #stanza == 0 then error("<methodName> must have text content"); end
100
		return t_concat(stanza);
101
	end;
102
	params=function(stanza)
103
		local t = {};
104
		for _, child in pairs(stanza.tags) do
105
			if child.name ~= "param" then error("<params> can only have <param> children"); end;
106
			t_insert(t, _xmlrpc_to_lua(child));
107
		end
108
		return t;
109
	end;
110
	param=function(stanza)
111
		if not(#stanza.tags == 1 and stanza.tags[1].name == "value") then error("<param> must have exactly one <value> child"); end
112
		return _xmlrpc_to_lua(stanza.tags[1]);
113
	end;
114
	value=function(stanza)
115
		if #stanza.tags == 0 then return t_concat(stanza); end
116
		if #stanza.tags ~= 1 then error("<value> must have a single child"); end
117
		return _xmlrpc_to_lua(stanza.tags[1]);
118
	end;
119
	int=int_parse;
120
	i4=int_parse;
121
	double=int_parse;
122
	boolean=function(stanza)
123
		if #stanza.tags ~= 0 or #stanza == 0 then error("<boolean> must have a single text child"); end
124
		local b = t_concat(stanza);
125
		if b ~= "1" and b ~= "0" then error("Failed to parse content of <boolean>"); end
126
		return b == "1" and true or false;
127
	end;
128
	string=function(stanza)
129
		if #stanza.tags ~= 0 then error("<string> must have a single text child"); end
130
		return t_concat(stanza);
131
	end;
132
	array=function(stanza)
133
		if #stanza.tags ~= 1 then error("<array> must have a single <data> child"); end
134
		return _xmlrpc_to_lua(stanza.tags[1]);
135
	end;
136
	data=function(stanza)
137
		local t = {};
138
		for _,child in pairs(stanza.tags) do
139
			if child.name ~= "value" then error("<data> can only have <value> children"); end
140
			t_insert(t, _xmlrpc_to_lua(child));
141
		end
142
		return t;
143
	end;
144
	struct=function(stanza)
145
		local t = {};
146
		for _,child in pairs(stanza.tags) do
147
			if child.name ~= "member" then error("<struct> can only have <member> children"); end
148
			local name, value = _xmlrpc_to_lua(child);
149
			t[name] = value;
150
		end
151
		return t;
152
	end;
153
	member=function(stanza)
154
		if #stanza.tags ~= 2 then error("<member> must have exactly two subtags"); end -- FIXME <params> is optional
155
		if stanza.tags[1].name ~= "name" then error("First <member> child tag must be <name>") end
156
		if stanza.tags[2].name ~= "value" then error("Second <member> child tag must be <value>") end
157
		return _xmlrpc_to_lua(stanza.tags[1]), _xmlrpc_to_lua(stanza.tags[2]);
158
	end;
159
	name=function(stanza)
160
		if #stanza.tags ~= 0 then error("<name> must have a single text child"); end
161
		local n = t_concat(stanza)
162
		if tostring(tonumber(n)) == n then n = tonumber(n); end
163
		return n;
164
	end;
165
	["nil"]=function(stanza) -- nil extension
166
		return nil;
167
	end;
168
}
169
_xmlrpc_to_lua = function(stanza)
170
	local h = rmap[stanza.name];
171
	if h then
172
		return h(stanza);
173
	else
174
		error("Unknown element: "..stanza.name);
175
	end
176
end
177
function translate_request(stanza)
178
	if stanza.name ~= "methodCall" then error("XML-RPC requests must have <methodCall> as root element"); end
179
	return _xmlrpc_to_lua(stanza);
180
end
181
 
182
return _M;