diff options
Diffstat (limited to 'plugins/mod_pubsub')
-rw-r--r-- | plugins/mod_pubsub/mod_pubsub.lua | 56 | ||||
-rw-r--r-- | plugins/mod_pubsub/pubsub.lib.lua | 119 |
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; |