changeset 13904:f8779be95156

MUC: Support for filtering presence broadcasts with per-session filters Currently no filters are defined, but could be added by modules (hint hint).
author Matthew Wild <mwild1@gmail.com>
date Mon, 11 Nov 2024 12:52:54 +0000
parents 4af7d00a2966
children 0e346f4d88ce
files plugins/muc/muc.lib.lua
diffstat 1 files changed, 34 insertions(+), 14 deletions(-) [+]
line wrap: on
line diff
--- a/plugins/muc/muc.lib.lua	Mon Nov 11 12:47:01 2024 +0000
+++ b/plugins/muc/muc.lib.lua	Mon Nov 11 12:52:54 2024 +0000
@@ -133,11 +133,27 @@
 	return occupant
 end
 
-function room_mt:route_to_occupant(occupant, stanza)
+function room_mt:route_to_occupant(occupant, stanza, session_filter)
 	local to = stanza.attr.to;
-	for jid in occupant:each_session() do
-		stanza.attr.to = jid;
-		self:route_stanza(stanza);
+	for session_jid, session_pres, session_meta in occupant:each_session() do
+		if session_filter then
+			stanza = session_filter(session_jid, session_pres, session_meta, stanza);
+		end
+		if stanza then
+			stanza.attr.to = session_jid;
+			self:route_stanza(stanza);
+		end
+	end
+	stanza.attr.to = to;
+end
+
+function room_mt:route_presence_to_occupant(occupant, stanza)
+	local to = stanza.attr.to;
+	for jid, _, session_meta in occupant:each_session() do
+		if not session_meta.filter_presence then
+			stanza.attr.to = jid;
+			self:route_stanza(stanza);
+		end
 	end
 	stanza.attr.to = to;
 end
@@ -297,7 +313,7 @@
 	end
 
 	if recipient then
-		return self:route_to_occupant(recipient, get_p(recipient));
+		return self:route_presence_to_occupant(recipient, get_p(recipient));
 	end
 
 	local broadcast_roles = self:get_presence_broadcast();
@@ -305,11 +321,11 @@
 	for occupant_nick, n_occupant in self:each_occupant() do
 		if occupant_nick ~= occupant.nick then
 			if broadcast_roles[occupant.role or "none"] or force_unavailable then
-				self:route_to_occupant(n_occupant, get_p(n_occupant));
+				self:route_presence_to_occupant(n_occupant, get_p(n_occupant));
 			elseif prev_role and broadcast_roles[prev_role] then
 				local pr = get_p(n_occupant);
 				pr.attr.type = 'unavailable';
-				self:route_to_occupant(n_occupant, pr);
+				self:route_presence_to_occupant(n_occupant, pr);
 			end
 
 		end
@@ -332,10 +348,14 @@
 	end
 end
 
-function room_mt:send_occupant_list(to, filter)
-	local to_bare = jid_bare(to);
+-- Send the current room list to an occupant (specifically to the real jid 'to',
+-- which is one of the occupant's sessions).
+function room_mt:send_occupant_list(to_occupant, to_jid, filter)
+	local to_session = to_occupant:get_session(to_jid);
+	if to_session.filter_presence then return; end
+	local to_bare = to_occupant.bare_jid;
 	local broadcast_roles = self:get_presence_broadcast();
-	local is_anonymous = self:is_anonymous_for(to);
+	local is_anonymous = self:is_anonymous_for(to_jid);
 	local broadcast_bare_jids = {}; -- Track which bare JIDs we have sent presence for
 	for occupant_jid, occupant in self:each_occupant() do
 		broadcast_bare_jids[occupant.bare_jid] = true;
@@ -343,7 +363,7 @@
 			local x = st.stanza("x", {xmlns='http://jabber.org/protocol/muc#user'});
 			self:build_item_list(occupant, x, is_anonymous and to_bare ~= occupant.bare_jid); -- can always see your own jids
 			local pres = st.clone(occupant:get_presence());
-			pres.attr.to = to;
+			pres.attr.to = to_jid;
 			pres:add_child(x);
 			module:fire_event("muc-build-occupant-presence", { room = self, occupant = occupant, stanza = pres });
 			self:route_stanza(pres);
@@ -356,7 +376,7 @@
 			if (nick or not is_anonymous) and not broadcast_bare_jids[affiliated_jid]
 			and (filter == nil or filter(affiliated_jid, nil)) then
 				local from = nick and (self.jid.."/"..nick) or self.jid;
-				local pres = st.presence({ to = to, from = from, type = "unavailable" })
+				local pres = st.presence({ to = to_jid, from = from, type = "unavailable" })
 					:tag("x", { xmlns = 'http://jabber.org/protocol/muc#user' })
 						:tag("item", {
 							affiliation = affiliation;
@@ -758,7 +778,7 @@
 
 		if orig_occupant == nil or muc_x then
 			-- Send occupant list to newly joined or desynced user
-			self:send_occupant_list(real_jid, function(nick, occupant) -- luacheck: ignore 212
+			self:send_occupant_list(dest_occupant, real_jid, function(nick, occupant) -- luacheck: ignore 212
 				-- Don't include self
 				return (not occupant) or occupant:get_presence(real_jid) == nil;
 			end)
@@ -1514,7 +1534,7 @@
 				(old_role ~= "moderator" and occupant.role == "moderator")) then -- Has gained or lost moderator status
 				-- Send everyone else's presences (as jid visibility has changed)
 				for real_jid in occupant:each_session() do
-					self:send_occupant_list(real_jid, function(occupant_jid, occupant) --luacheck: ignore 212 433
+					self:send_occupant_list(occupant, real_jid, function(occupant_jid, occupant) --luacheck: ignore 212 433
 						return (not occupant) or occupant.bare_jid ~= jid;
 					end);
 				end