diff options
Diffstat (limited to 'plugins/mod_pubsub')
-rw-r--r-- | plugins/mod_pubsub/mod_pubsub.lua | 101 | ||||
-rw-r--r-- | plugins/mod_pubsub/pubsub.lib.lua | 66 |
2 files changed, 129 insertions, 38 deletions
diff --git a/plugins/mod_pubsub/mod_pubsub.lua b/plugins/mod_pubsub/mod_pubsub.lua index ef31f326..6efc419f 100644 --- a/plugins/mod_pubsub/mod_pubsub.lua +++ b/plugins/mod_pubsub/mod_pubsub.lua @@ -1,10 +1,9 @@ -local pubsub = require "util.pubsub"; -local st = require "util.stanza"; -local jid_bare = require "util.jid".bare; -local usermanager = require "core.usermanager"; -local new_id = require "util.id".medium; -local storagemanager = require "core.storagemanager"; -local xtemplate = require "util.xtemplate"; +local pubsub = require "prosody.util.pubsub"; +local st = require "prosody.util.stanza"; +local jid_bare = require "prosody.util.jid".bare; +local new_id = require "prosody.util.id".medium; +local storagemanager = require "prosody.core.storagemanager"; +local xtemplate = require "prosody.util.xtemplate"; local xmlns_pubsub = "http://jabber.org/protocol/pubsub"; local xmlns_pubsub_event = "http://jabber.org/protocol/pubsub#event"; @@ -13,7 +12,7 @@ local xmlns_pubsub_owner = "http://jabber.org/protocol/pubsub#owner"; local autocreate_on_publish = module:get_option_boolean("autocreate_on_publish", false); local autocreate_on_subscribe = module:get_option_boolean("autocreate_on_subscribe", false); local pubsub_disco_name = module:get_option_string("name", "Prosody PubSub Service"); -local expose_publisher = module:get_option_boolean("expose_publisher", false) +local service_expose_publisher = module:get_option_boolean("expose_publisher") local service; @@ -40,7 +39,7 @@ end -- get(node_name) -- users(): iterator over (node_name) -local max_max_items = module:get_option_number("pubsub_max_items", 256); +local max_max_items = module:get_option_integer("pubsub_max_items", 256, 1); local function tonumber_max_items(n) if n == "max" then @@ -82,7 +81,11 @@ function simple_broadcast(kind, node, jids, item, actor, node_obj, service) --lu if node_obj and node_obj.config.include_payload == false then item:maptags(function () return nil; end); end - if not expose_publisher then + local node_expose_publisher = service_expose_publisher; + if node_expose_publisher == nil and node_obj and node_obj.config.itemreply == "publisher" then + node_expose_publisher = true; + end + if not node_expose_publisher then item.attr.publisher = nil; elseif not item.attr.publisher and actor ~= true then item.attr.publisher = service.config.normalize_jid(actor); @@ -136,12 +139,22 @@ end -- Compose a textual representation of Atom payloads local summary_templates = module:get_option("pubsub_summary_templates", { - ["http://www.w3.org/2005/Atom"] = "{summary|or{{author/name|and{{author/name} posted }}{title}}}"; + ["http://www.w3.org/2005/Atom"] = "{@pubsub:title|and{*{@pubsub:title}*\n\n}}{summary|or{{author/name|and{{author/name} posted }}{title}}}"; }) for pubsub_type, template in pairs(summary_templates) do module:hook("pubsub-summary/"..pubsub_type, function (event) local payload = event.payload; + + local got_config, node_config = service:get_node_config(event.node, true); + if got_config then + payload = st.clone(payload); + payload.attr["xmlns:pubsub"] = xmlns_pubsub; + payload.attr["pubsub:node"] = event.node; + payload.attr["pubsub:title"] = node_config.title; + payload.attr["pubsub:description"] = node_config.description; + end + return xtemplate.render(template, payload, tostring); end, -1); end @@ -176,10 +189,23 @@ module:hook("host-disco-items", function (event) end end); -local admin_aff = module:get_option_string("default_admin_affiliation", "owner"); -local function get_affiliation(jid) +local admin_aff = module:get_option_enum("default_admin_affiliation", "owner", "publisher", "member", "outcast", "none"); + +module:default_permission("prosody:admin", ":service-admin"); +module:default_permission("prosody:admin", ":create-node"); + +local function get_affiliation(jid, _, action) local bare_jid = jid_bare(jid); - if bare_jid == module.host or usermanager.is_admin(bare_jid, module.host) then + if bare_jid == module.host then + -- The host itself (i.e. local modules) is treated as an admin. + -- Check this first as to avoid sendig a host JID to :may() + return admin_aff; + end + if action == "create" and module:may(":create-node", bare_jid) then + -- Only one affiliation is allowed to create nodes by default + return "owner"; + end + if module:may(":service-admin", bare_jid) then return admin_aff; end end @@ -192,7 +218,7 @@ 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.expose_publisher = service_expose_publisher; service.config.nodestore = node_store; service.config.itemstore = create_simple_itemstore; @@ -219,7 +245,7 @@ function module.load() set_service(pubsub.new({ autocreate_on_publish = autocreate_on_publish; autocreate_on_subscribe = autocreate_on_subscribe; - expose_publisher = expose_publisher; + expose_publisher = service_expose_publisher; node_defaults = { ["persist_items"] = true; @@ -236,3 +262,46 @@ function module.load() normalize_jid = jid_bare; })); end + +local function get_service(service_jid) + return assert(assert(prosody.hosts[service_jid], "Unknown pubsub service").modules.pubsub, "Not a pubsub service").service; +end + +module:add_item("shell-command", { + section = "pubsub"; + section_desc = "Manage publish/subscribe nodes"; + name = "create_node"; + desc = "Create a node with the specified name"; + args = { + { name = "service_jid", type = "string" }; + { name = "node_name", type = "string" }; + }; + host_selector = "service_jid"; + + handler = function (self, service_jid, node_name) --luacheck: ignore 212/self + return get_service(service_jid):create(node_name, true); + end; +}); + +module:add_item("shell-command", { + section = "pubsub"; + section_desc = "Manage publish/subscribe nodes"; + name = "list_nodes"; + desc = "List nodes on a pubsub service"; + args = { + { name = "service_jid", type = "string" }; + }; + host_selector = "service_jid"; + + handler = function (self, service_jid) --luacheck: ignore 212/self + -- luacheck: ignore 431/service + local service = get_service(service_jid); + local nodes = select(2, assert(service:get_nodes(true))); + local count = 0; + for node_name in pairs(nodes) do + count = count + 1; + self.session.print(node_name); + end + return true, ("%d nodes"):format(count); + end; +}); diff --git a/plugins/mod_pubsub/pubsub.lib.lua b/plugins/mod_pubsub/pubsub.lib.lua index 3196569f..5a9be149 100644 --- a/plugins/mod_pubsub/pubsub.lib.lua +++ b/plugins/mod_pubsub/pubsub.lib.lua @@ -1,13 +1,12 @@ -local t_unpack = table.unpack or unpack; -- luacheck: ignore 113 local time_now = os.time; -local jid_prep = require "util.jid".prep; -local set = require "util.set"; -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 jid_prep = require "prosody.util.jid".prep; +local set = require "prosody.util.set"; +local st = require "prosody.util.stanza"; +local it = require "prosody.util.iterators"; +local uuid_generate = require "prosody.util.uuid".generate; +local dataform = require"prosody.util.dataforms".new; +local errors = require "prosody.util.error"; local xmlns_pubsub = "http://jabber.org/protocol/pubsub"; local xmlns_pubsub_errors = "http://jabber.org/protocol/pubsub#errors"; @@ -18,7 +17,7 @@ local _M = {}; local handlers = {}; _M.handlers = handlers; -local pubsub_errors = { +local pubsub_errors = errors.init("pubsub", xmlns_pubsub_errors, { ["conflict"] = { "cancel", "conflict" }; ["invalid-jid"] = { "modify", "bad-request", nil, "invalid-jid" }; ["jid-required"] = { "modify", "bad-request", nil, "jid-required" }; @@ -33,16 +32,13 @@ local pubsub_errors = { ["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(); +}); +local function pubsub_error_reply(stanza, error, context) + local err = pubsub_errors.wrap(error, context); + if error == "precondition-not-met" and type(context) == "table" and type(context.field) == "string" then + err.text = "Field does not match: " .. context.field; end + local reply = st.error_reply(stanza, err); return reply; end _M.pubsub_error_reply = pubsub_error_reply; @@ -110,6 +106,12 @@ local node_config_form = dataform { }; }; { + type = "list-multi"; -- TODO some way to inject options + name = "roster_groups_allowed"; + var = "pubsub#roster_groups_allowed"; + label = "Roster groups allowed to subscribe"; + }; + { type = "list-single"; name = "publish_model"; var = "pubsub#publish_model"; @@ -164,6 +166,17 @@ local node_config_form = dataform { var = "pubsub#notify_retract"; value = true; }; + { + type = "list-single"; + label = "Specify whose JID to include as the publisher of items"; + name = "itemreply"; + var = "pubsub#itemreply"; + options = { + { label = "Include the node owner's JID", value = "owner" }; + { label = "Include the item publisher's JID", value = "publisher" }; + { label = "Don't include any JID with items", value = "none", default = true }; + }; + }; }; _M.node_config_form = node_config_form; @@ -347,6 +360,13 @@ function handlers.get_items(origin, stanza, items, service) origin.send(pubsub_error_reply(stanza, "nodeid-required")); return true; end + + local node_obj = service.nodes[node]; + if not node_obj then + origin.send(pubsub_error_reply(stanza, "item-not-found")); + return true; + end + local resultspec; -- TODO rsm.get() if items.attr.max_items then resultspec = { max = tonumber(items.attr.max_items) }; @@ -358,6 +378,9 @@ function handlers.get_items(origin, stanza, items, service) end local expose_publisher = service.config.expose_publisher; + if expose_publisher == nil and node_obj.config.itemreply == "publisher" then + expose_publisher = true; + end local data = st.stanza("items", { node = node }); local iter, v, i = ipairs(results); @@ -658,7 +681,7 @@ function handlers.set_publish(origin, stanza, publish, service) 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 ok, ret, context = service:publish(node, stanza.attr.from, id, item, required_config); local reply; if ok then if type(ok) == "string" then @@ -669,7 +692,7 @@ function handlers.set_publish(origin, stanza, publish, service) :tag("publish", { node = node }) :tag("item", { id = id }); else - reply = pubsub_error_reply(stanza, ret); + reply = pubsub_error_reply(stanza, ret, context); end origin.send(reply); return true; @@ -678,8 +701,7 @@ end function handlers.set_retract(origin, stanza, retract, service) local node, notify = retract.attr.node, retract.attr.notify; notify = (notify == "1") or (notify == "true"); - local item = retract:get_child("item"); - local id = item and item.attr.id + local id = retract:get_child_attr("item", nil, "id"); if not (node and id) then origin.send(pubsub_error_reply(stanza, node and "item-not-found" or "nodeid-required")); return true; |