view mod_groups_muc_bookmarks/mod_groups_muc_bookmarks.lua @ 4738:5aee8d86629a

mod_bookmarks2: Fix handling of nick and password elements This form of child retrieval fails when the stanza elements internally don't have an 'xmlns' attribute, which can happen sometimes for some reason, including when they have been constructed via the stanza builder API. When that is the case then the explicit namespace arguemnt does not match the nil value of the internal attribute. Calling `:get_child()` without the namespace argument does the right thing here, with both nil and the parent namespace as valid values for the internal attribute.
author Kim Alvefur <zash@zash.se>
date Wed, 03 Nov 2021 21:11:55 +0100
parents 240fa534f586
children 5fadb991003d
line wrap: on
line source

local jid_split = require "util.jid".split;

local st = require "util.stanza";

local mod_groups = module:depends("groups_internal")
local mod_pep = module:depends("pep")

local PUBSUB_NODE_XEP0048 = "storage:bookmarks";
local XMLNS_XEP0048 = "storage:bookmarks";
local XMLNS_XEP0060 = "http://jabber.org/protocol/pubsub";

local default_options = {
	["persist_items"] = true;
	["access_model"] = "whitelist";
};

local function get_current_bookmarks(jid, service)
	local ok, id, item = service:get_last_item(PUBSUB_NODE_XEP0048, jid)
	if not ok or id == nil then
		if id == "item-not-found" or id == nil then
			-- return empty
			return st.stanza("storage", { xmlns = XMLNS_XEP0048 });
		end
		return nil, result
	end
	-- first item is the actual storage element
	local hit = item:get_child("storage", XMLNS_XEP0048);
	if not hit then
		return nil, "internal-server-error"
	end
	return hit
end

local function update_bookmarks(jid, service, storage)
	local item = st.stanza("item", { xmlns = XMLNS_XEP0060, id = "current" }):add_child(storage)
	module:log("debug", "updating bookmarks with %q", item)
	local ok, err = service:publish(
		PUBSUB_NODE_XEP0048,
		jid,
		"current",
		item,
		default_options
	)
	if not ok then
		module:log("error", "failed to update bookmarks: %s", err)
	end
end

local function find_matching_bookmark(storage, room)
	for node in storage:childtags("conference") do
		if node.attr.jid == room then
			return node
		end
	end
	return nil
end

local function inject_bookmark(jid, room, autojoin, name)
	local pep_service = mod_pep.get_pep_service(jid_split(jid))

	autojoin = autojoin or false and true
	local current = get_current_bookmarks(jid, pep_service)
	local existing = find_matching_bookmark(current, room)
	if existing then
		if autojoin ~= nil then
			existing.attr.autojoin = autojoin and "true" or "false"
		end
		if name ~= nil then
			-- do not change already configured names
			if not existing.attr.name then
				existing.attr.name = name
			end
		end
		done = true
		module:log("debug", "found existing matching bookmark, updated")
	else
		module:log("debug", "no existing bookmark found, adding new")
		current:tag("conference", {
			name = name,
			autojoin = autojoin and "true" or "false",
			jid = room,
			xmlns = XMLNS_XEP0048,
		})
	end

	update_bookmarks(jid, pep_service, current)
end

local function remove_bookmark(jid, room, autojoin, name)
	local pep_service = mod_pep.get_pep_service(jid_split(jid))

	autojoin = autojoin or false and true
	local current = get_current_bookmarks(jid, pep_service)
	if not current then
		return
	end
	current:maptags(function (node)
		if node.attr.xmlns and node.attr.xmlns ~= XMLNS_XEP0048 then
			return node
		end
		if node.name ~= "conference" then
			return node
		end
		if node.attr.jid == room then
			-- remove matching bookmark
			return nil
		end
		return node
	end)

	update_bookmarks(jid, pep_service, current)
end

local function handle_user_added(event)
	if not event.group_info.muc_jid then
		module:log("debug", "ignoring user added event on group %s because it has no MUC", event.id)
		return
	end
	local jid = event.user .. "@" .. event.host
	inject_bookmark(jid, event.group_info.muc_jid, true, event.group_info.name)
end

local function handle_user_removed(event)
	if not event.group_info.muc_jid then
		module:log("debug", "ignoring user removed event on group %s because it has no MUC", event.id)
		return
	end
	-- Removing the bookmark is fine as the user just lost any privilege to
	-- be in the MUC (as group MUCs are members-only).
	local jid = event.user .. "@" .. event.host
	remove_bookmark(jid, event.group_info.muc_jid, true, event.group_info.name)
end

module:hook("group-user-added", handle_user_added)
module:hook("group-user-removed", handle_user_removed)