view plugins/muc/occupant.lib.lua @ 13587:fdb2e0568cf8

mod_authz_internal: Make 'prosody:guest' default role for all unknown JIDs This fixes an issue where e.g. remote users or even other users on the server were unable to list MUC rooms. We want to define a permission to list MUC rooms, but we want it to be available to everyone by default (the traditional behaviour). prosody:guest is the lowest role we have. I ran a quick check and it isn't really used for anything right now that would be concerning. It was originally designed for anonymous logins. I think it's safe to treat remote JIDs as equivalent, since we have no trust relationship with anonymous users either.
author Matthew Wild <mwild1@gmail.com>
date Tue, 07 Jan 2025 14:41:32 +0000
parents 74b9e05af71e
children 4af7d00a2966
line wrap: on
line source

local pairs = pairs;
local setmetatable = setmetatable;
local st = require "prosody.util.stanza";
local util = module:require "muc/util";

local function get_filtered_presence(stanza)
	return util.filter_muc_x(st.clone(stanza));
end

local occupant_mt = {};
occupant_mt.__index = occupant_mt;

local function new_occupant(bare_real_jid, nick)
	return setmetatable({
		bare_jid = bare_real_jid;
		nick = nick; -- in-room jid
		sessions = {}; -- hash from real_jid to presence stanzas. stanzas should not be modified
		role = nil;
		jid = nil; -- Primary session
	}, occupant_mt);
end

-- Deep copy an occupant
local function copy_occupant(occupant)
	local sessions = {};
	for full_jid, presence_stanza in pairs(occupant.sessions) do
		-- Don't keep unavailable presences, as they'll accumulate; unless they're the primary session
		if presence_stanza.attr.type ~= "unavailable" or full_jid == occupant.jid then
			sessions[full_jid] = presence_stanza;
		end
	end
	return setmetatable({
		bare_jid = occupant.bare_jid;
		nick = occupant.nick;
		sessions = sessions;
		role = occupant.role;
		jid = occupant.jid;
	}, occupant_mt);
end

-- finds another session to be the primary (there might not be one)
function occupant_mt:choose_new_primary()
	for jid, pr in self:each_session() do
		if pr.attr.type == nil then
			return jid;
		end
	end
	return nil;
end

function occupant_mt:set_session(real_jid, presence_stanza, replace_primary)
	local pr = get_filtered_presence(presence_stanza);
	pr.attr.from = self.nick;
	pr.attr.to = real_jid;

	self.sessions[real_jid] = pr;
	if replace_primary then
		self.jid = real_jid;
	elseif self.jid == nil or (pr.attr.type == "unavailable" and self.jid == real_jid) then
		-- Only leave an unavailable presence as primary when there are no other options
		self.jid = self:choose_new_primary() or real_jid;
	end
end

function occupant_mt:remove_session(real_jid)
	-- Delete original session
	self.sessions[real_jid] = nil;
	if self.jid == real_jid then
		self.jid = self:choose_new_primary();
	end
end

function occupant_mt:each_session()
	return pairs(self.sessions)
end

function occupant_mt:get_presence(real_jid)
	return self.sessions[real_jid or self.jid]
end

return {
	new = new_occupant;
	copy = copy_occupant;
	mt = occupant_mt;
}