changeset 1667:c81a981479d4

mod_privilege: implemented probing of rosters items (for existing sessions only) on connection + use a globally shared table for priv_session (and fixed last_presence)
author Goffi <goffi@goffi.org>
date Wed, 08 Apr 2015 13:04:04 +0200
parents 0b1b4b7d5fe0
children fc7cd6cbe228
files mod_privilege/mod_privilege.lua
diffstat 1 files changed, 47 insertions(+), 18 deletions(-) [+]
line wrap: on
line diff
--- a/mod_privilege/mod_privilege.lua	Tue Apr 07 18:02:54 2015 +0200
+++ b/mod_privilege/mod_privilege.lua	Wed Apr 08 13:04:04 2015 +0200
@@ -1,4 +1,4 @@
- -- XEP-0356 (Privileged Entity)
+-- XEP-0356 (Privileged Entity)
 -- Copyright (C) 2015 Jérôme Poisson
 --
 -- This module is MIT/X11 licensed. Please see the
@@ -6,6 +6,7 @@
 --
 -- Some parts come from mod_remote_roster (module by Waqas Hussain and Kim Alvefur, see https://code.google.com/p/prosody-modules/)
 
+-- TODO: manage external <presence/> (for "roster" presence permission) when the account with the roster is offline
 
 local jid = require("util/jid")
 local set = require("util/set")
@@ -14,15 +15,18 @@
 local user_manager = require("core/usermanager")
 local hosts = prosody.hosts
 local full_sessions = prosody.full_sessions;
+
+local priv_session = module:shared("/*/privilege/session")
+
 -- the folowing sets are used to forward presence stanza
-if not prosody._privilege_presence_man_ent then
-	prosody._privilege_presence_man_ent = set.new()
+if not priv_session.presence_man_ent  then
+	priv_session.presence_man_ent = set.new()
 end
-local presence_man_ent = prosody._privilege_presence_man_ent
-if not prosody._privilege_presence_roster then
-	prosody._privilege_presence_roster = set.new()
+local presence_man_ent = priv_session.presence_man_ent
+if not priv_session.presence_roster then
+	priv_session.presence_roster = set.new()
 end
-local presence_roster = prosody._privilege_presence_roster
+local presence_roster = priv_session.presence_roster
 
 local _ALLOWED_ROSTER = set.new({'none', 'get', 'set', 'both'})
 local _ROSTER_GET_PERM = set.new({'get', 'both'})
@@ -34,8 +38,6 @@
 local _PRIV_ENT_NS = 'urn:xmpp:privilege:1'
 local _FORWARDED_NS = 'urn:xmpp:forward:0'
 
-local last_presence = nil -- cache used to avoid to send several times the same stanza (see § 7 #2)
-
 
 module:log("debug", "Loading privileged entity module ");
 
@@ -71,16 +73,42 @@
 function advertise_presences(session, to_jid, perms)
 	-- send presence status for already conencted entities
 	-- as explained in § 7.1
+	-- people in roster are probed only for active sessions
+	-- TODO: manage roster load for inactive sessions
+	if not perms.presence then return; end
+	local to_probe = {}
 	for _, user_session in pairs(full_sessions) do
-		if user_session.presence then
-			if _PRESENCE_MANAGED:contains(perms.presence) then
-				local presence = st.clone(user_session.presence)
-				presence.attr.to = to_jid
-				module:log("debug", "sending current presence for "..tostring(user_session.full_jid))
-				session.send(presence)
+		if user_session.presence and _PRESENCE_MANAGED:contains(perms.presence) then
+			local presence = st.clone(user_session.presence)
+			presence.attr.to = to_jid
+			module:log("debug", "sending current presence for "..tostring(user_session.full_jid))
+			session.send(presence)
+		end
+		if perms.presence == "roster" then
+			-- we reset the cache to avoid to miss a presence that just changed
+			priv_session.last_presence = nil
+
+			if user_session.roster then
+				local bare_jid = jid.bare(user_session.full_jid)
+				for entity, item in pairs(user_session.roster) do
+					if entity~=false and entity~="pending" and (item.subscription=="both" or item.subscription=="to") then
+						_, host = jid.split(entity)
+						if not hosts[host] then -- we don't probe jid from hosts we manage
+							-- using a table with entity as key avoid probing several time the same one
+							to_probe[entity] = bare_jid
+						end
+					end
+				end
 			end
 		end
 	end
+
+	-- now we probe peoples for "roster" presence permission
+	for to_jid, from_jid in pairs(to_probe) do
+		module:log("debug", "probing presence for %s (on behalf of %s)", tostring(to_jid), tostring(from_jid))
+		local probe = st.presence({from=from_jid, to=to_jid, type="probe"})
+		prosody.core_route_stanza(nil, probe)
+	end
 end
 
 function on_auth(event)
@@ -363,7 +391,8 @@
 	presence_fwd.attr.to = to_jid
 	module:log("debug", "presence forwarded to "..to_jid..": "..tostring(presence_fwd))
 	module:send(presence_fwd)
-	last_presence = presence -- we keep the presence in cache
+	-- cache used to avoid to send several times the same stanza
+	priv_session.last_presence = presence
 end
 
 module:hook("presence/bare", function(event)
@@ -381,9 +410,9 @@
 			if hosts[from_host] then return; end
 
 			-- we don't send several time the same presence, as recommended in §7 #2
-			if last_presence and same_presences(last_presence, stanza) then
+			if priv_session.last_presence and same_presences(priv_session.last_presence, stanza) then
 			   return
-		   end
+			end
 
 			for entity in presence_roster:items() do
 				if stanza.attr.from ~= entity then forward_presence(stanza, entity); end