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
31 local node_obj = self.nodes[node];
32 local node_aff = node_obj and node_obj.affiliations[actor];
33 local service_aff = self.affiliations[actor]
34 or self.config.get_affiliation(actor, node, action)
37 local node_capabilities = node_obj and node_obj.capabilities;
38 local service_capabilities = self.config.capabilities;
40 -- Check if node allows/forbids it
41 if node_capabilities then
42 local caps = node_capabilities[node_aff or service_aff];
44 local can = caps[action];
50 -- Check service-wide capabilities instead
51 local caps = service_capabilities[node_aff or service_aff];
53 local can = caps[action];
62 function service:set_affiliation(node, actor, jid, affiliation)
64 if not self:may(node, actor, "set_affiliation") then
65 return false, "forbidden";
68 local node_obj = self.nodes[node];
70 return false, "item-not-found";
72 node_obj.affiliations[jid] = affiliation;
73 local _, jid_sub = self:get_subscription(node, nil, jid);
74 if not jid_sub and not self:may(node, jid, "be_unsubscribed") then
75 local ok, err = self:add_subscription(node, nil, jid);
79 elseif jid_sub and not self:may(node, jid, "be_subscribed") then
80 local ok, err = self:add_subscription(node, nil, jid);
88 function service:add_subscription(node, actor, jid, options)
91 if jid == actor or self:jids_equal(actor, jid) then
94 cap = "subscribe_other";
96 if not self:may(node, actor, cap) then
97 return false, "forbidden";
99 if not self:may(node, jid, "be_subscribed") then
100 return false, "forbidden";
103 local node_obj = self.nodes[node];
105 if not self.config.autocreate_on_subscribe then
106 return false, "item-not-found";
108 local ok, err = self:create(node, actor);
112 node_obj = self.nodes[node];
115 node_obj.subscribers[jid] = options or true;
116 local normal_jid = self.config.normalize_jid(jid);
117 local subs = self.subscriptions[normal_jid];
119 if not subs[jid] then
120 subs[jid] = { [node] = true };
122 subs[jid][node] = true;
125 self.subscriptions[normal_jid] = { [jid] = { [node] = true } };
130 function service:remove_subscription(node, actor, jid)
133 if jid == actor or self:jids_equal(actor, jid) then
136 cap = "unsubscribe_other";
138 if not self:may(node, actor, cap) then
139 return false, "forbidden";
141 if not self:may(node, jid, "be_unsubscribed") then
142 return false, "forbidden";
145 local node_obj = self.nodes[node];
147 return false, "item-not-found";
149 if not node_obj.subscribers[jid] then
150 return false, "not-subscribed";
152 node_obj.subscribers[jid] = nil;
153 local normal_jid = self.config.normalize_jid(jid);
154 local subs = self.subscriptions[normal_jid];
156 local jid_subs = subs[jid];
158 jid_subs[node] = nil;
159 if next(jid_subs) == nil then
163 if next(subs) == nil then
164 self.subscriptions[normal_jid] = nil;
170 function service:get_subscription(node, actor, jid)
173 if jid == actor or self:jids_equal(actor, jid) then
174 cap = "get_subscription";
176 cap = "get_subscription_other";
178 if not self:may(node, actor, cap) then
179 return false, "forbidden";
182 local node_obj = self.nodes[node];
184 return false, "item-not-found";
186 return true, node_obj.subscribers[jid];
189 function service:create(node, actor)
191 if not self:may(node, actor, "create") then
192 return false, "forbidden";
195 if self.nodes[node] then
196 return false, "conflict";
206 local ok, err = self:set_affiliation(node, true, actor, "owner");
208 self.nodes[node] = nil;
213 function service:publish(node, actor, id, item)
215 if not self:may(node, actor, "publish") then
216 return false, "forbidden";
219 local node_obj = self.nodes[node];
221 if not self.config.autocreate_on_publish then
222 return false, "item-not-found";
224 local ok, err = self:create(node, actor);
228 node_obj = self.nodes[node];
230 node_obj.data[id] = item;
231 self.config.broadcaster(node, node_obj.subscribers, item);
235 function service:retract(node, actor, id, retract)
237 if not self:may(node, actor, "retract") then
238 return false, "forbidden";
241 local node_obj = self.nodes[node];
242 if (not node_obj) or (not node_obj.data[id]) then
243 return false, "item-not-found";
245 node_obj.data[id] = nil;
247 self.config.broadcaster(node, node_obj.subscribers, retract);
252 function service:get_items(node, actor, id)
254 if not self:may(node, actor, "get_items") then
255 return false, "forbidden";
258 local node_obj = self.nodes[node];
260 return false, "item-not-found";
262 if id then -- Restrict results to a single specific item
263 return true, { [id] = node_obj.data[id] };
265 return true, node_obj.data;
269 function service:get_nodes(actor)
271 if not self:may(nil, actor, "get_nodes") then
272 return false, "forbidden";
275 return true, self.nodes;
278 function service:get_subscriptions(node, actor, jid)
281 if jid == actor or self:jids_equal(actor, jid) then
282 cap = "get_subscriptions";
284 cap = "get_subscriptions_other";
286 if not self:may(node, actor, cap) then
287 return false, "forbidden";
292 node_obj = self.nodes[node];
294 return false, "item-not-found";
297 local normal_jid = self.config.normalize_jid(jid);
298 local subs = self.subscriptions[normal_jid];
299 -- We return the subscription object from the node to save
300 -- a get_subscription() call for each node.
303 for jid, subscribed_nodes in pairs(subs) do
304 if node then -- Return only subscriptions to this node
305 if subscribed_nodes[node] then
307 node = subscribed_node;
309 subscription = node_obj.subscribers[jid];
312 else -- Return subscriptions to all nodes
313 local nodes = self.nodes;
314 for subscribed_node in pairs(subscribed_nodes) do
316 node = subscribed_node;
318 subscription = nodes[subscribed_node].subscribers[jid];
327 -- Access models only affect 'none' affiliation caps, service/default access level...
328 function service:set_node_capabilities(node, actor, capabilities)
330 if not self:may(node, actor, "configure") then
331 return false, "forbidden";
334 local node_obj = self.nodes[node];
336 return false, "item-not-found";
338 node_obj.capabilities = capabilities;