1 local st = require "util.stanza";
2 local uuid_generate = require "util.uuid".generate;
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";
11 _M.handlers = handlers;
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" };
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));
27 reply:tag(e[4], { xmlns = xmlns_pubsub_errors }):up();
31 _M.pubsub_error_reply = pubsub_error_reply;
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;
39 return origin.send(pubsub_error_reply(stanza, "nodeid-required"));
41 local ok, results = service:get_items(node, stanza.attr.from, id);
43 return origin.send(pubsub_error_reply(stanza, results));
46 local data = st.stanza("items", { node = node });
47 for _, id in ipairs(results) do
48 data:add_child(results[id]);
52 reply = st.reply(stanza)
53 :tag("pubsub", { xmlns = xmlns_pubsub })
56 reply = pubsub_error_reply(stanza, "item-not-found");
58 return origin.send(reply);
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);
65 return origin.send(pubsub_error_reply(stanza, ret));
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();
73 return origin.send(reply);
76 function handlers.set_create(origin, stanza, create, service)
77 local node = create.attr.node;
80 ok, ret = service:create(node, stanza.attr.from);
82 reply = st.reply(stanza);
84 reply = pubsub_error_reply(stanza, ret);
88 node = uuid_generate();
89 ok, ret = service:create(node, stanza.attr.from);
90 until ok or ret ~= "conflict";
92 reply = st.reply(stanza)
93 :tag("pubsub", { xmlns = xmlns_pubsub })
94 :tag("create", { node = node });
96 reply = pubsub_error_reply(stanza, ret);
99 return origin.send(reply);
102 function handlers.set_delete(origin, stanza, delete, service)
103 local node = delete.attr.node;
105 local reply, notifier;
107 return origin.send(pubsub_error_reply(stanza, "nodeid-required"));
109 local ok, ret = service:delete(node, stanza.attr.from);
111 reply = st.reply(stanza);
113 reply = pubsub_error_reply(stanza, ret);
115 return origin.send(reply);
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"));
124 local options_tag, options = stanza.tags[1]:get_child("options"), nil;
126 options = options_form:data(options_tag.tags[1]);
129 local options_tag, options; -- FIXME
130 local ok, ret = service:add_subscription(node, stanza.attr.from, jid, options);
133 reply = st.reply(stanza)
134 :tag("pubsub", { xmlns = xmlns_pubsub })
135 :tag("subscription", {
138 subscription = "subscribed"
141 reply:add_child(options_tag);
144 reply = pubsub_error_reply(stanza, ret);
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"));
154 local ok, ret = service:remove_subscription(node, stanza.attr.from, jid);
157 reply = st.reply(stanza);
159 reply = pubsub_error_reply(stanza, ret);
161 return origin.send(reply);
164 function handlers.set_publish(origin, stanza, publish, service)
165 local node = publish.attr.node;
167 return origin.send(pubsub_error_reply(stanza, "nodeid-required"));
169 local item = publish:get_child("item");
170 local id = (item and item.attr.id);
172 id = uuid_generate();
177 local ok, ret = service:publish(node, stanza.attr.from, id, item);
180 reply = st.reply(stanza)
181 :tag("pubsub", { xmlns = xmlns_pubsub })
182 :tag("publish", { node = node })
183 :tag("item", { id = id });
185 reply = pubsub_error_reply(stanza, ret);
187 return origin.send(reply);
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"));
198 local reply, notifier;
200 notifier = st.stanza("retract", { id = id });
202 local ok, ret = service:retract(node, stanza.attr.from, id, notifier);
204 reply = st.reply(stanza);
206 reply = pubsub_error_reply(stanza, ret);
208 return origin.send(reply);
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");
216 return origin.send(pubsub_error_reply(stanza, "nodeid-required"));
218 local ok, ret = service:purge(node, stanza.attr.from, notify);
220 reply = st.reply(stanza);
222 reply = pubsub_error_reply(stanza, ret);
224 return origin.send(reply);
227 function handlers.get_configure(origin, stanza, config, service)
228 local node = config.attr.node;
230 return origin.send(pubsub_error_reply(stanza, "nodeid-required"));
233 if not service:may(node, actor, "configure") then
234 return origin.send(pubsub_error_reply(stanza, "forbidden"));
237 local node_obj = service.nodes[node];
239 return origin.send(pubsub_error_reply(stanza, "item-not-found"));
242 local form = self.config.node_config_form;
244 return origin.send(pubsub_error_reply(stanza, "not-allowed"));
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);
254 function handlers.set_configure(origin, stanza, config, service)
255 local node = config.attr.node;
257 return origin.send(pubsub_error_reply(stanza, "nodeid-required"));
259 local form, node_obj = service:get_node_config_form(node, stanza.attr.from);
261 return origin.send(pubsub_error_reply(stanza, node_obj));
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));
267 local ok, err = service:set_node_config(node, stanza.attr.from, new_config);
269 return origin.send(pubsub_error_reply(stanza, err));
271 return origin.send(st.reply(stanza));