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 });
21 function service:jids_equal(jid1, jid2)
22 local normalize = self.config.normalize_jid;
23 return normalize(jid1) == normalize(jid2);
26 function service:may(node, actor, action)
27 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 local node_capabilities = node_obj and node_obj.capabilities;
37 local service_capabilities = self.config.capabilities;
39 -- Check if node allows/forbids it
40 if node_capabilities then
41 local caps = node_capabilities[node_aff or service_aff];
43 local can = caps[action];
49 -- Check service-wide capabilities instead
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, nil, jid);
73 if not jid_sub and not self:may(node, jid, "be_unsubscribed") then
74 local ok, err = self:add_subscription(node, nil, jid);
78 elseif jid_sub and not self:may(node, jid, "be_subscribed") then
79 local ok, err = self:add_subscription(node, nil, jid);
87 function service:add_subscription(node, actor, jid, options)
90 if 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;
118 function service:remove_subscription(node, actor, jid)
121 if jid == actor or self:jids_equal(actor, jid) then
124 cap = "unsubscribe_other";
126 if not self:may(node, actor, cap) then
127 return false, "forbidden";
129 if not self:may(node, jid, "be_unsubscribed") then
130 return false, "forbidden";
133 local node_obj = self.nodes[node];
135 return false, "item-not-found";
137 if not node_obj.subscribers[jid] then
138 return false, "not-subscribed";
140 node_obj.subscribers[jid] = nil;
144 function service:get_subscription(node, actor, jid)
147 if jid == actor or self:jids_equal(actor, jid) then
148 cap = "get_subscription";
150 cap = "get_subscription_other";
152 if not self:may(node, actor, cap) then
153 return false, "forbidden";
156 local node_obj = self.nodes[node];
158 return true, node_obj.subscribers[jid];
162 function service:create(node, actor)
164 if not self:may(node, actor, "create") then
165 return false, "forbidden";
168 if self.nodes[node] then
169 return false, "conflict";
179 local ok, err = self:set_affiliation(node, true, actor, "owner");
181 self.nodes[node] = nil;
186 function service:publish(node, actor, id, item)
188 if not self:may(node, actor, "publish") then
189 return false, "forbidden";
192 local node_obj = self.nodes[node];
194 if not self.config.autocreate_on_publish then
195 return false, "item-not-found";
197 local ok, err = self:create(node, actor);
201 node_obj = self.nodes[node];
203 node_obj.data[id] = item;
204 self.config.broadcaster(node, node_obj.subscribers, item);
208 function service:retract(node, actor, id, retract)
210 if not self:may(node, actor, "retract") then
211 return false, "forbidden";
214 local node_obj = self.nodes[node];
215 if (not node_obj) or (not node_obj.data[id]) then
216 return false, "item-not-found";
218 node_obj.data[id] = nil;
220 self.config.broadcaster(node, node_obj.subscribers, retract);
225 function service:get_items(node, actor, id)
227 if not self:may(node, actor, "get_items") then
228 return false, "forbidden";
231 local node_obj = self.nodes[node];
233 return false, "item-not-found";
235 if id then -- Restrict results to a single specific item
236 return true, { [id] = node_obj.data[id] };
238 return true, node_obj.data;
242 function service:get_nodes(actor)
244 if not self:may(nil, actor, "get_nodes") then
245 return false, "forbidden";
248 return true, self.nodes;
251 -- Access models only affect 'none' affiliation caps, service/default access level...
252 function service:set_node_capabilities(node, actor, capabilities)
254 if not self:may(node, actor, "configure") then
255 return false, "forbidden";
258 local node_obj = self.nodes[node];
260 return false, "item-not-found";
262 node_obj.capabilities = capabilities;