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);
113 node_obj.subscribers[jid] = options or true;
117 function service:remove_subscription(node, actor, jid)
120 if jid == actor or self:jids_equal(actor, jid) then
123 cap = "unsubscribe_other";
125 if not self:may(node, actor, cap) then
126 return false, "forbidden";
128 if not self:may(node, jid, "be_unsubscribed") then
129 return false, "forbidden";
132 local node_obj = self.nodes[node];
134 return false, "item-not-found";
136 if not node_obj.subscribers[jid] then
137 return false, "not-subscribed";
139 node_obj.subscribers[jid] = nil;
143 function service:get_subscription(node, actor, jid)
146 if jid == actor or self:jids_equal(actor, jid) then
147 cap = "get_subscription";
149 cap = "get_subscription_other";
151 if not self:may(node, actor, cap) then
152 return false, "forbidden";
155 local node_obj = self.nodes[node];
157 return true, node_obj.subscribers[jid];
161 function service:create(node, actor)
163 if not self:may(node, actor, "create") then
164 return false, "forbidden";
167 if self.nodes[node] then
168 return false, "conflict";
178 local ok, err = self:set_affiliation(node, true, actor, "owner");
180 self.nodes[node] = nil;
185 function service:publish(node, actor, id, item)
187 if not self:may(node, actor, "publish") then
188 return false, "forbidden";
191 local node_obj = self.nodes[node];
193 if not self.config.autocreate_on_publish then
194 return false, "item-not-found";
196 local ok, err = self:create(node, actor);
200 node_obj = self.nodes[node];
202 node_obj.data[id] = item;
203 self.config.broadcaster(node, node_obj.subscribers, item);
207 function service:retract(node, actor, id, retract)
209 if not self:may(node, actor, "retract") then
210 return false, "forbidden";
213 local node_obj = self.nodes[node];
214 if (not node_obj) or (not node_obj.data[id]) then
215 return false, "item-not-found";
217 node_obj.data[id] = nil;
219 self.config.broadcaster(node, node_obj.subscribers, retract);
224 function service:get_items(node, actor, id)
226 if not self:may(node, actor, "get_items") then
227 return false, "forbidden";
230 local node_obj = self.nodes[node];
232 return false, "item-not-found";
234 if id then -- Restrict results to a single specific item
235 return true, { [id] = node_obj.data[id] };
237 return true, node_obj.data;
241 function service:get_nodes(actor)
243 if not self:may(nil, actor, "get_nodes") then
244 return false, "forbidden";
247 return true, self.nodes;
250 -- Access models only affect 'none' affiliation caps, service/default access level...
251 function service:set_node_capabilities(node, actor, capabilities)
253 if not self:may(node, actor, "configure") then
254 return false, "forbidden";
257 local node_obj = self.nodes[node];
259 return false, "item-not-found";
261 node_obj.capabilities = capabilities;