aboutsummaryrefslogtreecommitdiffstats
path: root/plugins/mod_pubsub/pubsub.lib.lua
diff options
context:
space:
mode:
Diffstat (limited to 'plugins/mod_pubsub/pubsub.lib.lua')
-rw-r--r--plugins/mod_pubsub/pubsub.lib.lua119
1 files changed, 92 insertions, 27 deletions
diff --git a/plugins/mod_pubsub/pubsub.lib.lua b/plugins/mod_pubsub/pubsub.lib.lua
index 50ef7ddf..83cef808 100644
--- a/plugins/mod_pubsub/pubsub.lib.lua
+++ b/plugins/mod_pubsub/pubsub.lib.lua
@@ -7,6 +7,7 @@ local st = require "util.stanza";
local it = require "util.iterators";
local uuid_generate = require "util.uuid".generate;
local dataform = require"util.dataforms".new;
+local errors = require "util.error";
local xmlns_pubsub = "http://jabber.org/protocol/pubsub";
local xmlns_pubsub_errors = "http://jabber.org/protocol/pubsub#errors";
@@ -31,9 +32,13 @@ local pubsub_errors = {
["internal-server-error"] = { "wait", "internal-server-error" };
["precondition-not-met"] = { "cancel", "conflict", nil, "precondition-not-met" };
["invalid-item"] = { "modify", "bad-request", "invalid item" };
+ ["persistent-items-unsupported"] = { "cancel", "feature-not-implemented", nil, "persistent-items" };
};
local function pubsub_error_reply(stanza, error)
local e = pubsub_errors[error];
+ if not e and errors.is_err(error) then
+ e = { error.type, error.condition, error.text, error.pubsub_condition };
+ end
local reply = st.error_reply(stanza, t_unpack(e, 1, 3));
if e[4] then
reply:tag(e[4], { xmlns = xmlns_pubsub_errors }):up();
@@ -79,8 +84,9 @@ local node_config_form = dataform {
};
{
type = "text-single";
- datatype = "xs:integer";
+ datatype = "pubsub:integer-or-max";
name = "max_items";
+ range_min = 1;
var = "pubsub#max_items";
label = "Max # of items to persist";
};
@@ -115,6 +121,12 @@ local node_config_form = dataform {
};
};
{
+ type = "list-single";
+ var = "pubsub#send_last_published_item";
+ name = "send_last_published_item";
+ options = { "never"; "on_sub"; "on_sub_and_presence" };
+ };
+ {
type = "boolean";
value = true;
label = "Whether to deliver event notifications";
@@ -153,6 +165,7 @@ local node_config_form = dataform {
value = true;
};
};
+_M.node_config_form = node_config_form;
local subscribe_options_form = dataform {
{
@@ -166,6 +179,7 @@ local subscribe_options_form = dataform {
label = "Receive message body in addition to payload?";
};
};
+_M.subscribe_options_form = subscribe_options_form;
local node_metadata_form = dataform {
{
@@ -185,7 +199,16 @@ local node_metadata_form = dataform {
type = "text-single";
name = "pubsub#type";
};
+ {
+ type = "text-single";
+ name = "pubsub#access_model";
+ };
+ {
+ type = "text-single";
+ name = "pubsub#publish_model";
+ };
};
+_M.node_metadata_form = node_metadata_form;
local service_method_feature_map = {
add_subscription = { "subscribe", "subscription-options" };
@@ -237,19 +260,32 @@ function _M.get_feature_set(service)
supported_features:add("access-"..service.node_defaults.access_model);
end
+ if service.node_defaults.send_last_published_item ~= "never" then
+ supported_features:add("last-published");
+ end
+
if rawget(service.config, "itemstore") and rawget(service.config, "nodestore") then
supported_features:add("persistent-items");
end
+ if true --[[ node_metadata_form[max_items].datatype == "pubsub:integer-or-max" ]] then
+ supported_features:add("config-node-max");
+ end
+
return supported_features;
end
function _M.handle_disco_info_node(event, service)
local stanza, reply, node = event.stanza, event.reply, event.node;
local ok, ret = service:get_nodes(stanza.attr.from);
+ if not ok then
+ event.origin.send(pubsub_error_reply(stanza, ret));
+ return true;
+ end
local node_obj = ret[node];
- if not ok or not node_obj then
- return;
+ if not node_obj then
+ event.origin.send(pubsub_error_reply(stanza, "item-not-found"));
+ return true;
end
event.exists = true;
reply:tag("identity", { category = "pubsub", type = "leaf" }):up();
@@ -258,6 +294,8 @@ function _M.handle_disco_info_node(event, service)
["pubsub#title"] = node_obj.config.title;
["pubsub#description"] = node_obj.config.description;
["pubsub#type"] = node_obj.config.payload_type;
+ ["pubsub#access_model"] = node_obj.config.access_model;
+ ["pubsub#publish_model"] = node_obj.config.publish_model;
}, "result"));
end
end
@@ -266,11 +304,12 @@ function _M.handle_disco_items_node(event, service)
local stanza, reply, node = event.stanza, event.reply, event.node;
local ok, ret = service:get_items(node, stanza.attr.from);
if not ok then
- return;
+ event.origin.send(pubsub_error_reply(stanza, ret));
+ return true;
end
for _, id in ipairs(ret) do
- reply:tag("item", { jid = module.host, name = id }):up();
+ reply:tag("item", { jid = service.jid or module.host, name = id }):up();
end
event.exists = true;
end
@@ -308,24 +347,36 @@ function handlers.get_items(origin, stanza, items, service)
origin.send(pubsub_error_reply(stanza, "nodeid-required"));
return true;
end
- local ok, results = service:get_items(node, stanza.attr.from, requested_items);
+ local resultspec; -- TODO rsm.get()
+ if items.attr.max_items then
+ resultspec = { max = tonumber(items.attr.max_items) };
+ end
+ local ok, results = service:get_items(node, stanza.attr.from, requested_items, resultspec);
if not ok then
origin.send(pubsub_error_reply(stanza, results));
return true;
end
+ local expose_publisher = service.config.expose_publisher;
+
local data = st.stanza("items", { node = node });
- for _, id in ipairs(results) do
- data:add_child(results[id]);
+ local iter, v, i = ipairs(results);
+ if not requested_items then
+ -- XXX Hack to preserve order of explicitly requested items.
+ iter, v, i = it.reverse(iter, v, i);
end
- local reply;
- if data then
- reply = st.reply(stanza)
- :tag("pubsub", { xmlns = xmlns_pubsub })
- :add_child(data);
- else
- reply = pubsub_error_reply(stanza, "item-not-found");
+
+ for _, id in iter, v, i do
+ local item = results[id];
+ if not expose_publisher then
+ item = st.clone(item);
+ item.attr.publisher = nil;
+ end
+ data:add_child(item);
end
+ local reply = st.reply(stanza)
+ :tag("pubsub", { xmlns = xmlns_pubsub })
+ :add_child(data);
origin.send(reply);
return true;
end
@@ -496,6 +547,12 @@ function handlers.set_subscribe(origin, stanza, subscribe, service)
reply = pubsub_error_reply(stanza, ret);
end
origin.send(reply);
+ local ok, config = service:get_node_config(node, true);
+ if ok and config.send_last_published_item ~= "never" then
+ local ok, id, item = service:get_last_item(node, jid);
+ if not (ok and id) then return; end
+ service.config.broadcaster("items", node, { [jid] = true }, item);
+ end
end
function handlers.set_unsubscribe(origin, stanza, unsubscribe, service)
@@ -508,7 +565,13 @@ function handlers.set_unsubscribe(origin, stanza, unsubscribe, service)
local ok, ret = service:remove_subscription(node, stanza.attr.from, jid);
local reply;
if ok then
- reply = st.reply(stanza);
+ reply = st.reply(stanza)
+ :tag("pubsub", { xmlns = xmlns_pubsub })
+ :tag("subscription", {
+ node = node,
+ jid = jid,
+ subscription = "none"
+ }):up();
else
reply = pubsub_error_reply(stanza, ret);
end
@@ -592,6 +655,9 @@ function handlers.set_publish(origin, stanza, publish, service)
item.attr.id = id;
end
end
+ if item then
+ item.attr.publisher = service.config.normalize_jid(stanza.attr.from);
+ end
local ok, ret = service:publish(node, stanza.attr.from, id, item, required_config);
local reply;
if ok then
@@ -633,14 +699,13 @@ function handlers.set_retract(origin, stanza, retract, service)
end
function handlers.owner_set_purge(origin, stanza, purge, service)
- local node, notify = purge.attr.node, purge.attr.notify;
- notify = (notify == "1") or (notify == "true");
+ local node = purge.attr.node;
local reply;
if not node then
origin.send(pubsub_error_reply(stanza, "nodeid-required"));
return true;
end
- local ok, ret = service:purge(node, stanza.attr.from, notify);
+ local ok, ret = service:purge(node, stanza.attr.from, true);
if ok then
reply = st.reply(stanza);
else
@@ -781,16 +846,15 @@ function handlers.owner_set_affiliations(origin, stanza, affiliations, service)
return true;
end
-local function create_encapsulating_item(id, payload)
- local item = st.stanza("item", { id = id, xmlns = xmlns_pubsub });
+local function create_encapsulating_item(id, payload, publisher)
+ local item = st.stanza("item", { id = id, publisher = publisher, xmlns = xmlns_pubsub });
item:add_child(payload);
return item;
end
-local function archive_itemstore(archive, config, user, node)
- module:log("debug", "Creation of itemstore for node %s with config %s", node, config);
+local function archive_itemstore(archive, max_items, user, node)
+ module:log("debug", "Creation of archive itemstore for node %s with limit %d", node, max_items);
local get_set = {};
- local max_items = config["max_items"];
function get_set:items() -- luacheck: ignore 212/self
local data, err = archive:find(user, {
limit = tonumber(max_items);
@@ -801,14 +865,15 @@ local function archive_itemstore(archive, config, user, node)
return true;
end
module:log("debug", "Listed items %s", data);
- return it.reverse(function()
+ return function()
+ -- luacheck: ignore 211/when
local id, payload, when, publisher = data();
if id == nil then
return;
end
local item = create_encapsulating_item(id, payload, publisher);
return id, item;
- end);
+ end;
end
function get_set:get(key) -- luacheck: ignore 212/self
local data, err = archive:find(user, {
@@ -863,7 +928,7 @@ local function archive_itemstore(archive, config, user, node)
return item.attr.id, item;
end
end
- return setmetatable(get_set, archive);
+ return get_set;
end
_M.archive_itemstore = archive_itemstore;