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