pubsub.lib: Fix actor in configure
[prosody.git] / plugins / mod_pubsub / pubsub.lib.lua
1 local st = require "util.stanza";
2 local uuid_generate = require "util.uuid".generate;
3
4 local xmlns_pubsub = "http://jabber.org/protocol/pubsub";
5 local xmlns_pubsub_errors = "http://jabber.org/protocol/pubsub#errors";
6 local xmlns_pubsub_owner = "http://jabber.org/protocol/pubsub#owner";
7
8 local _M = {};
9
10 local handlers = {};
11 _M.handlers = handlers;
12
13 local pubsub_errors = {
14         ["conflict"] = { "cancel", "conflict" };
15         ["invalid-jid"] = { "modify", "bad-request", nil, "invalid-jid" };
16         ["jid-required"] = { "modify", "bad-request", nil, "jid-required" };
17         ["nodeid-required"] = { "modify", "bad-request", nil, "nodeid-required" };
18         ["item-not-found"] = { "cancel", "item-not-found" };
19         ["not-subscribed"] = { "modify", "unexpected-request", nil, "not-subscribed" };
20         ["forbidden"] = { "cancel", "forbidden" };
21         ["not-allowed"] = { "cancel", "not-allowed" };
22 };
23 local function pubsub_error_reply(stanza, error)
24         local e = pubsub_errors[error];
25         local reply = st.error_reply(stanza, unpack(e, 1, 3));
26         if e[4] then
27                 reply:tag(e[4], { xmlns = xmlns_pubsub_errors }):up();
28         end
29         return reply;
30 end
31 _M.pubsub_error_reply = pubsub_error_reply;
32
33 function handlers.get_items(origin, stanza, items, service)
34         local node = items.attr.node;
35         local item = items:get_child("item");
36         local id = item and item.attr.id;
37
38         if not node then
39                 return origin.send(pubsub_error_reply(stanza, "nodeid-required"));
40         end
41         local ok, results = service:get_items(node, stanza.attr.from, id);
42         if not ok then
43                 return origin.send(pubsub_error_reply(stanza, results));
44         end
45
46         local data = st.stanza("items", { node = node });
47         for _, id in ipairs(results) do
48                 data:add_child(results[id]);
49         end
50         local reply;
51         if data then
52                 reply = st.reply(stanza)
53                         :tag("pubsub", { xmlns = xmlns_pubsub })
54                                 :add_child(data);
55         else
56                 reply = pubsub_error_reply(stanza, "item-not-found");
57         end
58         return origin.send(reply);
59 end
60
61 function handlers.get_subscriptions(origin, stanza, subscriptions, service)
62         local node = subscriptions.attr.node;
63         local ok, ret = service:get_subscriptions(node, stanza.attr.from, stanza.attr.from);
64         if not ok then
65                 return origin.send(pubsub_error_reply(stanza, ret));
66         end
67         local reply = st.reply(stanza)
68                 :tag("pubsub", { xmlns = xmlns_pubsub })
69                         :tag("subscriptions");
70         for _, sub in ipairs(ret) do
71                 reply:tag("subscription", { node = sub.node, jid = sub.jid, subscription = 'subscribed' }):up();
72         end
73         return origin.send(reply);
74 end
75
76 function handlers.set_create(origin, stanza, create, service)
77         local node = create.attr.node;
78         local ok, ret, reply;
79         if node then
80                 ok, ret = service:create(node, stanza.attr.from);
81                 if ok then
82                         reply = st.reply(stanza);
83                 else
84                         reply = pubsub_error_reply(stanza, ret);
85                 end
86         else
87                 repeat
88                         node = uuid_generate();
89                         ok, ret = service:create(node, stanza.attr.from);
90                 until ok or ret ~= "conflict";
91                 if ok then
92                         reply = st.reply(stanza)
93                                 :tag("pubsub", { xmlns = xmlns_pubsub })
94                                         :tag("create", { node = node });
95                 else
96                         reply = pubsub_error_reply(stanza, ret);
97                 end
98         end
99         return origin.send(reply);
100 end
101
102 function handlers.set_delete(origin, stanza, delete, service)
103         local node = delete.attr.node;
104
105         local reply, notifier;
106         if not node then
107                 return origin.send(pubsub_error_reply(stanza, "nodeid-required"));
108         end
109         local ok, ret = service:delete(node, stanza.attr.from);
110         if ok then
111                 reply = st.reply(stanza);
112         else
113                 reply = pubsub_error_reply(stanza, ret);
114         end
115         return origin.send(reply);
116 end
117
118 function handlers.set_subscribe(origin, stanza, subscribe, service)
119         local node, jid = subscribe.attr.node, subscribe.attr.jid;
120         if not (node and jid) then
121                 return origin.send(pubsub_error_reply(stanza, jid and "nodeid-required" or "invalid-jid"));
122         end
123         --[[
124         local options_tag, options = stanza.tags[1]:get_child("options"), nil;
125         if options_tag then
126                 options = options_form:data(options_tag.tags[1]);
127         end
128         --]]
129         local options_tag, options; -- FIXME
130         local ok, ret = service:add_subscription(node, stanza.attr.from, jid, options);
131         local reply;
132         if ok then
133                 reply = st.reply(stanza)
134                         :tag("pubsub", { xmlns = xmlns_pubsub })
135                                 :tag("subscription", {
136                                         node = node,
137                                         jid = jid,
138                                         subscription = "subscribed"
139                                 }):up();
140                 if options_tag then
141                         reply:add_child(options_tag);
142                 end
143         else
144                 reply = pubsub_error_reply(stanza, ret);
145         end
146         origin.send(reply);
147 end
148
149 function handlers.set_unsubscribe(origin, stanza, unsubscribe, service)
150         local node, jid = unsubscribe.attr.node, unsubscribe.attr.jid;
151         if not (node and jid) then
152                 return origin.send(pubsub_error_reply(stanza, jid and "nodeid-required" or "invalid-jid"));
153         end
154         local ok, ret = service:remove_subscription(node, stanza.attr.from, jid);
155         local reply;
156         if ok then
157                 reply = st.reply(stanza);
158         else
159                 reply = pubsub_error_reply(stanza, ret);
160         end
161         return origin.send(reply);
162 end
163
164 function handlers.set_publish(origin, stanza, publish, service)
165         local node = publish.attr.node;
166         if not node then
167                 return origin.send(pubsub_error_reply(stanza, "nodeid-required"));
168         end
169         local item = publish:get_child("item");
170         local id = (item and item.attr.id);
171         if not id then
172                 id = uuid_generate();
173                 if item then
174                         item.attr.id = id;
175                 end
176         end
177         local ok, ret = service:publish(node, stanza.attr.from, id, item);
178         local reply;
179         if ok then
180                 reply = st.reply(stanza)
181                         :tag("pubsub", { xmlns = xmlns_pubsub })
182                                 :tag("publish", { node = node })
183                                         :tag("item", { id = id });
184         else
185                 reply = pubsub_error_reply(stanza, ret);
186         end
187         return origin.send(reply);
188 end
189
190 function handlers.set_retract(origin, stanza, retract, service)
191         local node, notify = retract.attr.node, retract.attr.notify;
192         notify = (notify == "1") or (notify == "true");
193         local item = retract:get_child("item");
194         local id = item and item.attr.id
195         if not (node and id) then
196                 return origin.send(pubsub_error_reply(stanza, node and "item-not-found" or "nodeid-required"));
197         end
198         local reply, notifier;
199         if notify then
200                 notifier = st.stanza("retract", { id = id });
201         end
202         local ok, ret = service:retract(node, stanza.attr.from, id, notifier);
203         if ok then
204                 reply = st.reply(stanza);
205         else
206                 reply = pubsub_error_reply(stanza, ret);
207         end
208         return origin.send(reply);
209 end
210
211 function handlers.set_purge(origin, stanza, purge, service)
212         local node, notify = purge.attr.node, purge.attr.notify;
213         notify = (notify == "1") or (notify == "true");
214         local reply;
215         if not node then
216                 return origin.send(pubsub_error_reply(stanza, "nodeid-required"));
217         end
218         local ok, ret = service:purge(node, stanza.attr.from, notify);
219         if ok then
220                 reply = st.reply(stanza);
221         else
222                 reply = pubsub_error_reply(stanza, ret);
223         end
224         return origin.send(reply);
225 end
226
227 function handlers.get_configure(origin, stanza, config, service)
228         local node = config.attr.node;
229         if not node then
230                 return origin.send(pubsub_error_reply(stanza, "nodeid-required"));
231         end
232
233         if not service:may(node, stanza.attr.from, "configure") then
234                 return origin.send(pubsub_error_reply(stanza, "forbidden"));
235         end
236
237         local node_obj = service.nodes[node];
238         if not node_obj then
239                 return origin.send(pubsub_error_reply(stanza, "item-not-found"));
240         end
241
242         local form = self.config.node_config_form;
243         if not form then
244                 return origin.send(pubsub_error_reply(stanza, "not-allowed"));
245         end
246
247         local reply = st.reply(stanza)
248                 :tag("pubsub", { xmlns = xmlns_pubsub_owner })
249                         :tag("configure", { node = node })
250                                 :add_child(form:form(node_obj.config));
251         return origin.send(reply);
252 end
253
254 function handlers.set_configure(origin, stanza, config, service)
255         local node = config.attr.node;
256         if not node then
257                 return origin.send(pubsub_error_reply(stanza, "nodeid-required"));
258         end
259         local form, node_obj = service:get_node_config_form(node, stanza.attr.from);
260         if not form then
261                 return origin.send(pubsub_error_reply(stanza, node_obj));
262         end
263         local new_config, err = form:data(config.tags[1]);
264         if not new_config then
265                 return origin.send(st.error_reply(stanza, "modify", "bad-request", err));
266         end
267         local ok, err = service:set_node_config(node, stanza.attr.from, new_config);
268         if not ok then
269                 return origin.send(pubsub_error_reply(stanza, err));
270         end
271         return origin.send(st.reply(stanza));
272 end
273
274 function handlers.get_default(origin, stanza, default, service)
275         local reply = st.reply(stanza)
276                 :tag("pubsub", { xmlns = xmlns_pubsub_owner })
277                         :tag("configure", { node = node })
278                                 :add_child(form:form(service.node_default_config));
279         return origin.send(reply);
280 end
281
282 return _M;