1 module("pubsub", package.seeall);
4 local service_mt = { __index = service };
6 local default_config = {
7 broadcaster = function () end;
8 get_affiliation = function () end;
13 config = config or {};
15 config = setmetatable(config, { __index = default_config });
22 function service:jids_equal(jid1, jid2)
23 local normalize = self.config.normalize_jid;
24 return normalize(jid1) == normalize(jid2);
27 function service:may(node, actor, action)
28 if actor == true then return true; end
30 local node_obj = self.nodes[node];
31 local node_aff = node_obj and node_obj.affiliations[actor];
32 local service_aff = self.affiliations[actor]
33 or self.config.get_affiliation(actor, node, action)
36 -- Check if node allows/forbids it
37 local node_capabilities = node_obj and node_obj.capabilities;
38 if node_capabilities then
39 local caps = node_capabilities[node_aff or service_aff];
41 local can = caps[action];
48 -- Check service-wide capabilities instead
49 local service_capabilities = self.config.capabilities;
50 local caps = service_capabilities[node_aff or service_aff];
52 local can = caps[action];
61 function service:set_affiliation(node, actor, jid, affiliation)
63 if not self:may(node, actor, "set_affiliation") then
64 return false, "forbidden";
67 local node_obj = self.nodes[node];
69 return false, "item-not-found";
71 node_obj.affiliations[jid] = affiliation;
72 local _, jid_sub = self:get_subscription(node, true, jid);
73 if not jid_sub and not self:may(node, jid, "be_unsubscribed") then
74 local ok, err = self:add_subscription(node, true, jid);
78 elseif jid_sub and not self:may(node, jid, "be_subscribed") then
79 local ok, err = self:add_subscription(node, true, jid);
87 function service:add_subscription(node, actor, jid, options)
90 if actor == true or jid == actor or self:jids_equal(actor, jid) then
93 cap = "subscribe_other";
95 if not self:may(node, actor, cap) then
96 return false, "forbidden";
98 if not self:may(node, jid, "be_subscribed") then
99 return false, "forbidden";
102 local node_obj = self.nodes[node];
104 if not self.config.autocreate_on_subscribe then
105 return false, "item-not-found";
107 local ok, err = self:create(node, actor);
111 node_obj = self.nodes[node];
114 node_obj.subscribers[jid] = options or true;
115 local normal_jid = self.config.normalize_jid(jid);
116 local subs = self.subscriptions[normal_jid];
118 if not subs[jid] then
119 subs[jid] = { [node] = true };
121 subs[jid][node] = true;
124 self.subscriptions[normal_jid] = { [jid] = { [node] = true } };
129 function service:remove_subscription(node, actor, jid)
132 if actor == true or jid == actor or self:jids_equal(actor, jid) then
135 cap = "unsubscribe_other";
137 if not self:may(node, actor, cap) then
138 return false, "forbidden";
140 if not self:may(node, jid, "be_unsubscribed") then
141 return false, "forbidden";
144 local node_obj = self.nodes[node];
146 return false, "item-not-found";
148 if not node_obj.subscribers[jid] then
149 return false, "not-subscribed";
151 node_obj.subscribers[jid] = nil;
152 local normal_jid = self.config.normalize_jid(jid);
153 local subs = self.subscriptions[normal_jid];
155 local jid_subs = subs[jid];
157 jid_subs[node] = nil;
158 if next(jid_subs) == nil then
162 if next(subs) == nil then
163 self.subscriptions[normal_jid] = nil;
169 function service:get_subscription(node, actor, jid)
172 if actor == true or jid == actor or self:jids_equal(actor, jid) then
173 cap = "get_subscription";
175 cap = "get_subscription_other";
177 if not self:may(node, actor, cap) then
178 return false, "forbidden";
181 local node_obj = self.nodes[node];
183 return false, "item-not-found";
185 return true, node_obj.subscribers[jid];
188 function service:create(node, actor)
190 if not self:may(node, actor, "create") then
191 return false, "forbidden";
194 if self.nodes[node] then
195 return false, "conflict";
205 local ok, err = self:set_affiliation(node, true, actor, "owner");
207 self.nodes[node] = nil;
212 function service:publish(node, actor, id, item)
214 if not self:may(node, actor, "publish") then
215 return false, "forbidden";
218 local node_obj = self.nodes[node];
220 if not self.config.autocreate_on_publish then
221 return false, "item-not-found";
223 local ok, err = self:create(node, actor);
227 node_obj = self.nodes[node];
229 node_obj.data[id] = item;
230 self.config.broadcaster(node, node_obj.subscribers, item);
234 function service:retract(node, actor, id, retract)
236 if not self:may(node, actor, "retract") then
237 return false, "forbidden";
240 local node_obj = self.nodes[node];
241 if (not node_obj) or (not node_obj.data[id]) then
242 return false, "item-not-found";
244 node_obj.data[id] = nil;
246 self.config.broadcaster(node, node_obj.subscribers, retract);
251 function service:get_items(node, actor, id)
253 if not self:may(node, actor, "get_items") then
254 return false, "forbidden";
257 local node_obj = self.nodes[node];
259 return false, "item-not-found";
261 if id then -- Restrict results to a single specific item
262 return true, { [id] = node_obj.data[id] };
264 return true, node_obj.data;
268 function service:get_nodes(actor)
270 if not self:may(nil, actor, "get_nodes") then
271 return false, "forbidden";
274 return true, self.nodes;
277 function service:get_subscriptions(node, actor, jid)
280 if actor == true or jid == actor or self:jids_equal(actor, jid) then
281 cap = "get_subscriptions";
283 cap = "get_subscriptions_other";
285 if not self:may(node, actor, cap) then
286 return false, "forbidden";
291 node_obj = self.nodes[node];
293 return false, "item-not-found";
296 local normal_jid = self.config.normalize_jid(jid);
297 local subs = self.subscriptions[normal_jid];
298 -- We return the subscription object from the node to save
299 -- a get_subscription() call for each node.
302 for jid, subscribed_nodes in pairs(subs) do
303 if node then -- Return only subscriptions to this node
304 if subscribed_nodes[node] then
306 node = subscribed_node;
308 subscription = node_obj.subscribers[jid];
311 else -- Return subscriptions to all nodes
312 local nodes = self.nodes;
313 for subscribed_node in pairs(subscribed_nodes) do
315 node = subscribed_node;
317 subscription = nodes[subscribed_node].subscribers[jid];
326 -- Access models only affect 'none' affiliation caps, service/default access level...
327 function service:set_node_capabilities(node, actor, capabilities)
329 if not self:may(node, actor, "configure") then
330 return false, "forbidden";
333 local node_obj = self.nodes[node];
335 return false, "item-not-found";
337 node_obj.capabilities = capabilities;