1 local st = require "util.stanza";
2 local uuid_generate = require "util.uuid".generate;
3 local dataform = require"util.dataforms".new;
5 local xmlns_pubsub = "http://jabber.org/protocol/pubsub";
6 local xmlns_pubsub_errors = "http://jabber.org/protocol/pubsub#errors";
7 local xmlns_pubsub_owner = "http://jabber.org/protocol/pubsub#owner";
12 _M.handlers = handlers;
14 local pubsub_errors = {
15 ["conflict"] = { "cancel", "conflict" };
16 ["invalid-jid"] = { "modify", "bad-request", nil, "invalid-jid" };
17 ["jid-required"] = { "modify", "bad-request", nil, "jid-required" };
18 ["nodeid-required"] = { "modify", "bad-request", nil, "nodeid-required" };
19 ["item-not-found"] = { "cancel", "item-not-found" };
20 ["not-subscribed"] = { "modify", "unexpected-request", nil, "not-subscribed" };
21 ["forbidden"] = { "auth", "forbidden" };
22 ["not-allowed"] = { "cancel", "not-allowed" };
24 local function pubsub_error_reply(stanza, error)
25 local e = pubsub_errors[error];
26 local reply = st.error_reply(stanza, unpack(e, 1, 3));
28 reply:tag(e[4], { xmlns = xmlns_pubsub_errors }):up();
32 _M.pubsub_error_reply = pubsub_error_reply;
34 local node_config_form = require"util.dataforms".new {
38 value = "http://jabber.org/protocol/pubsub#node_config";
42 name = "pubsub#max_items";
43 label = "Max # of items to persist";
47 function handlers.get_items(origin, stanza, items, service)
48 local node = items.attr.node;
49 local item = items:get_child("item");
50 local id = item and item.attr.id;
53 return origin.send(pubsub_error_reply(stanza, "nodeid-required"));
55 local ok, results = service:get_items(node, stanza.attr.from, id);
57 return origin.send(pubsub_error_reply(stanza, results));
60 local data = st.stanza("items", { node = node });
61 for _, id in ipairs(results) do
62 data:add_child(results[id]);
66 reply = st.reply(stanza)
67 :tag("pubsub", { xmlns = xmlns_pubsub })
70 reply = pubsub_error_reply(stanza, "item-not-found");
72 return origin.send(reply);
75 function handlers.get_subscriptions(origin, stanza, subscriptions, service)
76 local node = subscriptions.attr.node;
77 local ok, ret = service:get_subscriptions(node, stanza.attr.from, stanza.attr.from);
79 return origin.send(pubsub_error_reply(stanza, ret));
81 local reply = st.reply(stanza)
82 :tag("pubsub", { xmlns = xmlns_pubsub })
83 :tag("subscriptions");
84 for _, sub in ipairs(ret) do
85 reply:tag("subscription", { node = sub.node, jid = sub.jid, subscription = 'subscribed' }):up();
87 return origin.send(reply);
90 function handlers.set_create(origin, stanza, create, service)
91 local node = create.attr.node;
94 ok, ret = service:create(node, stanza.attr.from);
96 reply = st.reply(stanza);
98 reply = pubsub_error_reply(stanza, ret);
102 node = uuid_generate();
103 ok, ret = service:create(node, stanza.attr.from);
104 until ok or ret ~= "conflict";
106 reply = st.reply(stanza)
107 :tag("pubsub", { xmlns = xmlns_pubsub })
108 :tag("create", { node = node });
110 reply = pubsub_error_reply(stanza, ret);
113 return origin.send(reply);
116 function handlers.set_delete(origin, stanza, delete, service)
117 local node = delete.attr.node;
119 local reply, notifier;
121 return origin.send(pubsub_error_reply(stanza, "nodeid-required"));
123 local ok, ret = service:delete(node, stanza.attr.from);
125 reply = st.reply(stanza);
127 reply = pubsub_error_reply(stanza, ret);
129 return origin.send(reply);
132 function handlers.set_subscribe(origin, stanza, subscribe, service)
133 local node, jid = subscribe.attr.node, subscribe.attr.jid;
134 if not (node and jid) then
135 return origin.send(pubsub_error_reply(stanza, jid and "nodeid-required" or "invalid-jid"));
138 local options_tag, options = stanza.tags[1]:get_child("options"), nil;
140 options = options_form:data(options_tag.tags[1]);
143 local options_tag, options; -- FIXME
144 local ok, ret = service:add_subscription(node, stanza.attr.from, jid, options);
147 reply = st.reply(stanza)
148 :tag("pubsub", { xmlns = xmlns_pubsub })
149 :tag("subscription", {
152 subscription = "subscribed"
155 reply:add_child(options_tag);
158 reply = pubsub_error_reply(stanza, ret);
163 function handlers.set_unsubscribe(origin, stanza, unsubscribe, service)
164 local node, jid = unsubscribe.attr.node, unsubscribe.attr.jid;
165 if not (node and jid) then
166 return origin.send(pubsub_error_reply(stanza, jid and "nodeid-required" or "invalid-jid"));
168 local ok, ret = service:remove_subscription(node, stanza.attr.from, jid);
171 reply = st.reply(stanza);
173 reply = pubsub_error_reply(stanza, ret);
175 return origin.send(reply);
178 function handlers.set_publish(origin, stanza, publish, service)
179 local node = publish.attr.node;
181 return origin.send(pubsub_error_reply(stanza, "nodeid-required"));
183 local item = publish:get_child("item");
184 local id = (item and item.attr.id);
186 id = uuid_generate();
191 local ok, ret = service:publish(node, stanza.attr.from, id, item);
194 reply = st.reply(stanza)
195 :tag("pubsub", { xmlns = xmlns_pubsub })
196 :tag("publish", { node = node })
197 :tag("item", { id = id });
199 reply = pubsub_error_reply(stanza, ret);
201 return origin.send(reply);
204 function handlers.set_retract(origin, stanza, retract, service)
205 local node, notify = retract.attr.node, retract.attr.notify;
206 notify = (notify == "1") or (notify == "true");
207 local item = retract:get_child("item");
208 local id = item and item.attr.id
209 if not (node and id) then
210 return origin.send(pubsub_error_reply(stanza, node and "item-not-found" or "nodeid-required"));
212 local reply, notifier;
214 notifier = st.stanza("retract", { id = id });
216 local ok, ret = service:retract(node, stanza.attr.from, id, notifier);
218 reply = st.reply(stanza);
220 reply = pubsub_error_reply(stanza, ret);
222 return origin.send(reply);
225 function handlers.set_purge(origin, stanza, purge, service)
226 local node, notify = purge.attr.node, purge.attr.notify;
227 notify = (notify == "1") or (notify == "true");
230 return origin.send(pubsub_error_reply(stanza, "nodeid-required"));
232 local ok, ret = service:purge(node, stanza.attr.from, notify);
234 reply = st.reply(stanza);
236 reply = pubsub_error_reply(stanza, ret);
238 return origin.send(reply);
241 function handlers.get_configure(origin, stanza, config, service)
242 local node = config.attr.node;
244 return origin.send(pubsub_error_reply(stanza, "nodeid-required"));
247 if not service:may(node, stanza.attr.from, "configure") then
248 return origin.send(pubsub_error_reply(stanza, "forbidden"));
251 local node_obj = service.nodes[node];
253 return origin.send(pubsub_error_reply(stanza, "item-not-found"));
256 local reply = st.reply(stanza)
257 :tag("pubsub", { xmlns = xmlns_pubsub_owner })
258 :tag("configure", { node = node })
259 :add_child(node_config_form:form(node_obj.config));
260 return origin.send(reply);
263 function handlers.set_configure(origin, stanza, config, service)
264 local node = config.attr.node;
266 return origin.send(pubsub_error_reply(stanza, "nodeid-required"));
268 if not service:may(node, stanza.attr.from, "configure") then
269 return origin.send(pubsub_error_reply(stanza, "forbidden"));
271 local new_config, err = node_config_form:data(config.tags[1]);
272 if not new_config then
273 return origin.send(st.error_reply(stanza, "modify", "bad-request", err));
275 local ok, err = service:set_node_config(node, stanza.attr.from, new_config);
277 return origin.send(pubsub_error_reply(stanza, err));
279 return origin.send(st.reply(stanza));
282 function handlers.get_default(origin, stanza, default, service)
283 local reply = st.reply(stanza)
284 :tag("pubsub", { xmlns = xmlns_pubsub_owner })
286 :add_child(node_config_form:form(service.node_defaults));
287 return origin.send(reply);