aboutsummaryrefslogtreecommitdiffstats
path: root/plugins/mod_pubsub
diff options
context:
space:
mode:
Diffstat (limited to 'plugins/mod_pubsub')
-rw-r--r--plugins/mod_pubsub/mod_pubsub.lua56
-rw-r--r--plugins/mod_pubsub/pubsub.lib.lua119
2 files changed, 137 insertions, 38 deletions
diff --git a/plugins/mod_pubsub/mod_pubsub.lua b/plugins/mod_pubsub/mod_pubsub.lua
index c13630c9..be460f72 100644
--- a/plugins/mod_pubsub/mod_pubsub.lua
+++ b/plugins/mod_pubsub/mod_pubsub.lua
@@ -39,16 +39,32 @@ end
-- get(node_name)
-- users(): iterator over (node_name)
+local max_max_items = module:get_option_number("pubsub_max_items", 256);
+
+local function tonumber_max_items(n)
+ if n == "max" then
+ return max_max_items;
+ end
+ return tonumber(n);
+end
+
+for _, field in ipairs(lib_pubsub.node_config_form) do
+ if field.var == "pubsub#max_items" then
+ field.range_max = max_max_items;
+ break;
+ end
+end
local node_store = module:open_store(module.name.."_nodes");
-local function create_simple_itemstore(node_config, node_name)
+local function create_simple_itemstore(node_config, node_name) --> util.cache like object
local driver = storagemanager.get_driver(module.host, "pubsub_data");
local archive = driver:open("pubsub_"..node_name, "archive");
- return lib_pubsub.archive_itemstore(archive, node_config, nil, node_name);
+ local max_items = tonumber_max_items(node_config["max_items"]);
+ return lib_pubsub.archive_itemstore(archive, max_items, nil, node_name);
end
-function simple_broadcast(kind, node, jids, item, actor, node_obj)
+function simple_broadcast(kind, node, jids, item, actor, node_obj, service) --luacheck: ignore 431/service
if node_obj then
if node_obj.config["notify_"..kind] == false then
return;
@@ -65,8 +81,10 @@ function simple_broadcast(kind, node, jids, item, actor, node_obj)
if node_obj and node_obj.config.include_payload == false then
item:maptags(function () return nil; end);
end
- if expose_publisher and actor then
- item.attr.publisher = actor
+ if not expose_publisher then
+ item.attr.publisher = nil;
+ elseif not item.attr.publisher then
+ item.attr.publisher = service.config.normalize_jid(actor);
end
end
end
@@ -75,14 +93,13 @@ function simple_broadcast(kind, node, jids, item, actor, node_obj)
local msg_type = node_obj and node_obj.config.notification_type or "headline";
local message = st.message({ from = module.host, type = msg_type, id = id })
:tag("event", { xmlns = xmlns_pubsub_event })
- :tag(kind, { node = node })
+ :tag(kind, { node = node });
if item then
message:add_child(item);
end
local summary;
- -- Compose a sensible textual representation of at least Atom payloads
if item and item.tags[1] then
local payload = item.tags[1];
summary = module:fire_event("pubsub-summary/"..payload.attr.xmlns, {
@@ -100,12 +117,12 @@ function simple_broadcast(kind, node, jids, item, actor, node_obj)
end
end
-local max_max_items = module:get_option_number("pubsub_max_items", 256);
-function check_node_config(node, actor, new_config) -- luacheck: ignore 212/actor 212/node
- if (new_config["max_items"] or 1) > max_max_items then
+function check_node_config(node, actor, new_config) -- luacheck: ignore 212/node 212/actor
+ if (tonumber_max_items(new_config["max_items"]) or 1) > max_max_items then
return false;
end
- if new_config["access_model"] ~= "whitelist" and new_config["access_model"] ~= "open" then
+ if new_config["access_model"] ~= "whitelist"
+ and new_config["access_model"] ~= "open" then
return false;
end
return true;
@@ -115,6 +132,7 @@ function is_item_stanza(item)
return st.is_stanza(item) and item.attr.xmlns == xmlns_pubsub and item.name == "item" and #item.tags == 1;
end
+-- Compose a textual representation of Atom payloads
module:hook("pubsub-summary/http://www.w3.org/2005/Atom", function (event)
local payload = event.payload;
local title = payload:get_child_text("title");
@@ -172,6 +190,17 @@ end
function set_service(new_service)
service = new_service;
+ service.config.autocreate_on_publish = autocreate_on_publish;
+ service.config.autocreate_on_subscribe = autocreate_on_subscribe;
+ service.config.expose_publisher = expose_publisher;
+
+ service.config.nodestore = node_store;
+ service.config.itemstore = create_simple_itemstore;
+ service.config.broadcaster = simple_broadcast;
+ service.config.itemcheck = is_item_stanza;
+ service.config.check_node_config = check_node_config;
+ service.config.get_affiliation = get_affiliation;
+
module.environment.service = service;
add_disco_features_from_service(service);
end
@@ -190,7 +219,11 @@ function module.load()
set_service(pubsub.new({
autocreate_on_publish = autocreate_on_publish;
autocreate_on_subscribe = autocreate_on_subscribe;
+ expose_publisher = expose_publisher;
+ node_defaults = {
+ ["persist_items"] = true;
+ };
nodestore = node_store;
itemstore = create_simple_itemstore;
broadcaster = simple_broadcast;
@@ -198,6 +231,7 @@ function module.load()
check_node_config = check_node_config;
get_affiliation = get_affiliation;
+ jid = module.host;
normalize_jid = jid_bare;
}));
end
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;