Mercurial > prosody-modules
view mod_pep_plus/mod_pep_plus.lua @ 2670:6e01878103c0
mod_smacks: Ignore user when writing or reading session_cache on prosody 0.9
At least under some circumstances it seems that session.username is nil when
a user tries to resume his session in prosody 0.9.
The username is not relevant when no limiting is done (limiting the number of
entries in the session cache is only possible in prosody 0.10), so this
commit removes the usage of the username when accessing the prosody 0.9 session
cache.
author | tmolitor <thilo@eightysoft.de> |
---|---|
date | Thu, 06 Apr 2017 02:12:14 +0200 |
parents | f8fc79b3051a |
children | cb2342cf3f3c |
line wrap: on
line source
local pubsub = require "util.pubsub"; local jid_bare = require "util.jid".bare; local jid_split = require "util.jid".split; local set_new = require "util.set".new; local st = require "util.stanza"; local calculate_hash = require "util.caps".calculate_hash; local is_contact_subscribed = require "core.rostermanager".is_contact_subscribed; local xmlns_pubsub = "http://jabber.org/protocol/pubsub"; local xmlns_pubsub_event = "http://jabber.org/protocol/pubsub#event"; local xmlns_pubsub_owner = "http://jabber.org/protocol/pubsub#owner"; local lib_pubsub = module:require "pubsub"; local handlers = lib_pubsub.handlers; local pubsub_error_reply = lib_pubsub.pubsub_error_reply; local empty_set = set_new(); local services = {}; local recipients = {}; local hash_map = {}; function module.save() return { services = services }; end function module.restore(data) services = data.services; end local function subscription_presence(user_bare, recipient) local recipient_bare = jid_bare(recipient); if (recipient_bare == user_bare) then return true; end local username, host = jid_split(user_bare); return is_contact_subscribed(username, host, recipient_bare); end local function get_broadcaster(name) local function simple_broadcast(kind, node, jids, item) if item then item = st.clone(item); item.attr.xmlns = nil; -- Clear the pubsub namespace end local message = st.message({ from = name, type = "headline" }) :tag("event", { xmlns = xmlns_pubsub_event }) :tag(kind, { node = node }) :add_child(item); for jid in pairs(jids) do module:log("debug", "Sending notification to %s from %s: %s", jid, name, tostring(item)); message.attr.to = jid; module:send(message); end end return simple_broadcast; end function get_pep_service(name) local service = services[name]; if service then return service; end service = pubsub.new({ capabilities = { none = { create = false; publish = false; retract = false; get_nodes = false; subscribe = false; unsubscribe = false; get_subscription = false; get_subscriptions = false; get_items = false; subscribe_other = false; unsubscribe_other = false; get_subscription_other = false; get_subscriptions_other = false; be_subscribed = true; be_unsubscribed = true; set_affiliation = false; }; subscriber = { create = false; publish = false; retract = false; get_nodes = true; subscribe = true; unsubscribe = true; get_subscription = true; get_subscriptions = true; get_items = true; subscribe_other = false; unsubscribe_other = false; get_subscription_other = false; get_subscriptions_other = false; be_subscribed = true; be_unsubscribed = true; set_affiliation = false; }; publisher = { create = false; publish = true; retract = true; get_nodes = true; subscribe = true; unsubscribe = true; get_subscription = true; get_subscriptions = true; get_items = true; subscribe_other = false; unsubscribe_other = false; get_subscription_other = false; get_subscriptions_other = false; be_subscribed = true; be_unsubscribed = true; set_affiliation = false; }; owner = { create = true; publish = true; retract = true; delete = true; get_nodes = true; configure = true; subscribe = true; unsubscribe = true; get_subscription = true; get_subscriptions = true; get_items = true; subscribe_other = true; unsubscribe_other = true; get_subscription_other = true; get_subscriptions_other = true; be_subscribed = true; be_unsubscribed = true; set_affiliation = true; }; }; node_defaults = { ["pubsub#max_items"] = "1"; }; autocreate_on_publish = true; autocreate_on_subscribe = true; broadcaster = get_broadcaster(name); get_affiliation = function (jid) if jid_bare(jid) == name then return "owner"; elseif subscription_presence(name, jid) then return "subscriber"; end end; normalize_jid = jid_bare; }); services[name] = service; module:add_item("pep-service", { service = service, jid = name }); return service; end function handle_pubsub_iq(event) local origin, stanza = event.origin, event.stanza; local pubsub = stanza.tags[1]; local action = pubsub.tags[1]; if not action then return origin.send(st.error_reply(stanza, "cancel", "bad-request")); end local service_name = stanza.attr.to or origin.username.."@"..origin.host local service = get_pep_service(service_name); local handler = handlers[stanza.attr.type.."_"..action.name]; if handler then handler(origin, stanza, action, service); return true; end end module:hook("iq/bare/"..xmlns_pubsub..":pubsub", handle_pubsub_iq); module:hook("iq/bare/"..xmlns_pubsub_owner..":pubsub", handle_pubsub_iq); module:add_identity("pubsub", "pep", module:get_option_string("name", "Prosody")); module:add_feature("http://jabber.org/protocol/pubsub#publish"); local function get_caps_hash_from_presence(stanza, current) local t = stanza.attr.type; if not t then local child = stanza:get_child("c", "http://jabber.org/protocol/caps"); if child then local attr = child.attr; if attr.hash then -- new caps if attr.hash == 'sha-1' and attr.node and attr.ver then return attr.ver, attr.node.."#"..attr.ver; end else -- legacy caps if attr.node and attr.ver then return attr.node.."#"..attr.ver.."#"..(attr.ext or ""), attr.node.."#"..attr.ver; end end end return; -- no or bad caps elseif t == "unavailable" or t == "error" then return; end return current; -- no caps, could mean caps optimization, so return current end local function resend_last_item(jid, node, service) local ok, items = service:get_items(node, jid); if not ok then return; end for i, id in ipairs(items) do service.config.broadcaster("items", node, { [jid] = true }, items[id]); end end local function update_subscriptions(recipient, service_name, nodes) local service = get_pep_service(service_name); nodes = nodes or empty_set; local service_recipients = recipients[service_name]; if not service_recipients then service_recipients = {}; recipients[service_name] = service_recipients; end local current = service_recipients[recipient]; if not current or type(current) ~= "table" then current = empty_set; end if (current == empty_set or current:empty()) and (nodes == empty_set or nodes:empty()) then return; end for node in current - nodes do service:remove_subscription(node, recipient, recipient); end for node in nodes - current do service:add_subscription(node, recipient, recipient); resend_last_item(recipient, node, service); end if nodes == empty_set or nodes:empty() then nodes = nil; end service_recipients[recipient] = nodes; end module:hook("presence/bare", function(event) -- inbound presence to bare JID recieved local origin, stanza = event.origin, event.stanza; local user = stanza.attr.to or (origin.username..'@'..origin.host); local t = stanza.attr.type; local self = not stanza.attr.to; local service = get_pep_service(user); if not t then -- available presence if self or subscription_presence(user, stanza.attr.from) then local recipient = stanza.attr.from; local current = recipients[user] and recipients[user][recipient]; local hash, query_node = get_caps_hash_from_presence(stanza, current); if current == hash or (current and current == hash_map[hash]) then return; end if not hash then update_subscriptions(recipient, user); else recipients[user] = recipients[user] or {}; if hash_map[hash] then update_subscriptions(recipient, user, hash_map[hash]); else recipients[user][recipient] = hash; local from_bare = origin.type == "c2s" and origin.username.."@"..origin.host; if self or origin.type ~= "c2s" or (recipients[from_bare] and recipients[from_bare][origin.full_jid]) ~= hash then -- COMPAT from ~= stanza.attr.to because OneTeam can't deal with missing from attribute origin.send( st.stanza("iq", {from=user, to=stanza.attr.from, id="disco", type="get"}) :tag("query", {xmlns = "http://jabber.org/protocol/disco#info", node = query_node}) ); end end end end elseif t == "unavailable" then update_subscriptions(stanza.attr.from, user); elseif not self and t == "unsubscribe" then local from = jid_bare(stanza.attr.from); local subscriptions = recipients[user]; if subscriptions then for subscriber in pairs(subscriptions) do if jid_bare(subscriber) == from then update_subscriptions(subscriber, user); end end end end end, 10); module:hook("iq-result/bare/disco", function(event) local origin, stanza = event.origin, event.stanza; local disco = stanza:get_child("query", "http://jabber.org/protocol/disco#info"); if not disco then return; end -- Process disco response local self = not stanza.attr.to; local user = stanza.attr.to or (origin.username..'@'..origin.host); local contact = stanza.attr.from; local current = recipients[user] and recipients[user][contact]; if type(current) ~= "string" then return; end -- check if waiting for recipient's response local ver = current; if not string.find(current, "#") then ver = calculate_hash(disco.tags); -- calculate hash end local notify = set_new(); for _, feature in pairs(disco.tags) do if feature.name == "feature" and feature.attr.var then local nfeature = feature.attr.var:match("^(.*)%+notify$"); if nfeature then notify:add(nfeature); end end end hash_map[ver] = notify; -- update hash map if self then for jid, item in pairs(origin.roster) do -- for all interested contacts if item.subscription == "both" or item.subscription == "from" then if not recipients[jid] then recipients[jid] = {}; end update_subscriptions(contact, jid, notify); end end end update_subscriptions(contact, user, notify); end); module:hook("account-disco-info-node", function(event) local reply, stanza, origin = event.reply, event.stanza, event.origin; local service_name = stanza.attr.to or origin.username.."@"..origin.host local service = get_pep_service(service_name); local node = event.node; local ok = service:get_items(node, jid_bare(stanza.attr.from) or true); if not ok then return; end event.exists = true; reply:tag('identity', {category='pubsub', type='leaf'}):up(); end); module:hook("account-disco-info", function(event) local reply = event.reply; reply:tag('identity', {category='pubsub', type='pep'}):up(); reply:tag('feature', {var='http://jabber.org/protocol/pubsub#publish'}):up(); end); module:hook("account-disco-items-node", function(event) local reply, stanza, origin = event.reply, event.stanza, event.origin; local node = event.node; local service_name = stanza.attr.to or origin.username.."@"..origin.host local service = get_pep_service(service_name); local ok, ret = service:get_items(node, jid_bare(stanza.attr.from) or true); if not ok then return; end event.exists = true; for _, id in ipairs(ret) do reply:tag("item", { jid = service_name, name = id }):up(); end end); module:hook("account-disco-items", function(event) local reply, stanza, origin = event.reply, event.stanza, event.origin; local service_name = reply.attr.from or origin.username.."@"..origin.host local service = get_pep_service(service_name); local ok, ret = service:get_nodes(jid_bare(stanza.attr.from)); if not ok then return; end for node, node_obj in pairs(ret) do reply:tag("item", { jid = service_name, node = node, name = node_obj.config.name }):up(); end end);