1 local pubsub = require "util.pubsub";
2 local st = require "util.stanza";
3 local jid_bare = require "util.jid".bare;
4 local uuid_generate = require "util.uuid".generate;
6 require "core.modulemanager".load(module.host, "iq");
8 local xmlns_pubsub = "http://jabber.org/protocol/pubsub";
9 local xmlns_pubsub_errors = "http://jabber.org/protocol/pubsub#errors";
10 local xmlns_pubsub_event = "http://jabber.org/protocol/pubsub#event";
16 function handle_pubsub_iq(event)
17 local origin, stanza = event.origin, event.stanza;
18 local pubsub = stanza.tags[1];
19 local action = pubsub.tags[1];
20 local handler = handlers[stanza.attr.type.."_"..action.name];
22 handler(origin, stanza, action);
27 local pubsub_errors = {
28 ["conflict"] = { "cancel", "conflict" };
29 ["invalid-jid"] = { "modify", "bad-request", nil, "invalid-jid" };
30 ["item-not-found"] = { "cancel", "item-not-found" };
31 ["not-subscribed"] = { "modify", "unexpected-request", nil, "not-subscribed" };
32 ["forbidden"] = { "cancel", "forbidden" };
34 function pubsub_error_reply(stanza, error)
35 local e = pubsub_errors[error];
36 local reply = st.error_reply(stanza, unpack(e, 1, 3));
38 reply:tag(e[4], { xmlns = xmlns_pubsub_errors }):up();
43 function handlers.get_items(origin, stanza, items)
44 local node = items.attr.node;
45 local item = items:get_child("item");
46 local id = item and item.attr.id;
47 local data = st.stanza("items", { node = node });
48 for _, entry in pairs(service:get(node, stanza.attr.from, id)) do
49 data:add_child(entry);
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.set_create(origin, stanza, create)
62 local node = create.attr.node;
65 ok, ret = service:create(node, stanza.attr.from);
67 reply = st.reply(stanza);
69 reply = pubsub_error_reply(stanza, ret);
73 node = uuid_generate();
74 ok, ret = service:create(node, stanza.attr.from);
76 reply = st.reply(stanza)
77 :tag("pubsub", { xmlns = xmlns_pubsub })
78 :tag("create", { node = node });
80 return origin.send(reply);
83 function handlers.set_subscribe(origin, stanza, subscribe)
84 local node, jid = subscribe.attr.node, subscribe.attr.jid;
85 if jid_bare(jid) ~= jid_bare(stanza.attr.from) then
86 return origin.send(pubsub_error_reply(stanza, "invalid-jid"));
88 local ok, ret = service:add_subscription(node, stanza.attr.from, jid);
91 reply = st.reply(stanza)
92 :tag("pubsub", { xmlns = xmlns_pubsub })
93 :tag("subscription", {
96 subscription = "subscribed"
99 reply = pubsub_error_reply(stanza, ret);
101 return origin.send(reply);
104 function handlers.set_unsubscribe(origin, stanza, unsubscribe)
105 local node, jid = unsubscribe.attr.node, unsubscribe.attr.jid;
106 if jid_bare(jid) ~= jid_bare(stanza.attr.from) then
107 return origin.send(pubsub_error_reply(stanza, "invalid-jid"));
109 local ok, ret = service:remove_subscription(node, stanza.attr.from, jid);
112 reply = st.reply(stanza);
114 reply = pubsub_error_reply(stanza, ret);
116 return origin.send(reply);
119 function handlers.set_publish(origin, stanza, publish)
120 local node = publish.attr.node;
121 local item = publish:get_child("item");
122 local id = (item and item.attr.id) or uuid_generate();
123 local ok, ret = service:publish(node, stanza.attr.from, id, item);
126 reply = st.reply(stanza)
127 :tag("pubsub", { xmlns = xmlns_pubsub })
128 :tag("publish", { node = node })
129 :tag("item", { id = id });
131 reply = pubsub_error_reply(stanza, ret);
133 return origin.send(reply);
136 function handlers.set_retract(origin, stanza, retract)
137 local node, notify = retract.attr.node, retract.attr.notify;
138 notify = (notify == "1") or (notify == "true");
139 local item = retract:get_child("item");
140 local id = item and item.attr.id
141 local reply, notifier;
143 notifier = st.stanza("retract", { id = id });
145 local ok, ret = service:retract(node, stanza.attr.from, id, notifier);
147 reply = st.reply(stanza);
149 reply = pubsub_error_reply(stanza, ret);
151 return origin.send(reply);
154 function simple_broadcast(node, jids, item)
155 item = st.clone(item);
156 item.attr.xmlns = nil; -- Clear the pubsub namespace
157 local message = st.message({ from = module.host, type = "headline" })
158 :tag("event", { xmlns = xmlns_pubsub_event })
159 :tag("items", { node = node })
161 for jid in pairs(jids) do
162 module:log("debug", "Sending notification to %s", jid);
163 message.attr.to = jid;
164 core_post_stanza(hosts[module.host], message);
168 module:hook("iq/host/http://jabber.org/protocol/pubsub:pubsub", handle_pubsub_iq);
170 local disco_info = st.stanza("query", { xmlns = "http://jabber.org/protocol/disco#info" })
171 :tag("identity", { category = "pubsub", type = "service" }):up()
172 :tag("feature", { var = "http://jabber.org/protocol/pubsub" }):up();
174 module:hook("iq-get/host/http://jabber.org/protocol/disco#info:query", function (event)
175 event.origin.send(st.reply(event.stanza):add_child(disco_info));
179 module:hook("iq-get/host/http://jabber.org/protocol/disco#items:query", function (event)
180 local ok, ret = service:get_nodes(event.stanza.attr.from);
182 event.origin.send(pubsub_error_reply(stanza, ret));
184 local reply = st.reply(event.stanza)
185 :tag("query", { xmlns = "http://jabber.org/protocol/disco#items" });
186 for node, node_obj in pairs(ret) do
187 reply:tag("item", { jid = module.host, node = node, name = node_obj.config.name }):up();
189 event.origin.send(reply);
194 service = pubsub.new({
195 broadcaster = simple_broadcast
197 module.environment.service = service;
199 function module.save()
200 return { service = service };
203 function module.restore(data)
204 service = data.service;
205 module.environment.service = service;