File

mod_cache_c2s_caps/mod_cache_c2s_caps.lua @ 6302:06fbbd45ba75

mod_cloud_notify: Readme: fix links and labels that were removed in the last commit diff --git a/mod_cloud_notify/README.md b/mod_cloud_notify/README.md --- a/mod_cloud_notify/README.md +++ b/mod_cloud_notify/README.md @@ -1,3 +1,9 @@ +---- +-labels: +-- 'Stage-Beta' +-summary: 'XEP-0357: Cloud push notifications' +---- + # Introduction This module enables support for sending "push notifications" to clients @@ -32,15 +38,15 @@ notification to your device. When your d it will display it or wake up the app so it can connect to XMPP and receive any pending messages. -This protocol is described for developers in \[XEP-0357: Push -Notifications\]. +This protocol is described for developers in [XEP-0357: Push +Notifications]. -For this module to work reliably, you must have \[mod_smacks\], -\[mod_mam\] and \[mod_carbons\] also enabled on your server. +For this module to work reliably, you must have [mod_smacks], +[mod_mam] and [mod_carbons] also enabled on your server. Some clients, notably Siskin and Snikket iOS need some additional extensions that are not currently defined in a standard XEP. To support -these clients, see \[mod_cloud_notify_extensions\]. +these clients, see [mod_cloud_notify_extensions]. # Configuration @@ -58,18 +64,18 @@ these clients, see \[mod_cloud_notify_ex # Internal design notes App servers are notified about offline messages, messages stored by -\[mod_mam\] or messages waiting in the smacks queue. The business rules +[mod_mam] or messages waiting in the smacks queue. The business rules outlined [here](//mail.jabber.org/pipermail/standards/2016-February/030925.html) are all honored[^2]. -To cooperate with \[mod_smacks\] this module consumes some events: +To cooperate with [mod_smacks] this module consumes some events: `smacks-ack-delayed`, `smacks-hibernation-start` and `smacks-hibernation-end`. These events allow this module to send out notifications for messages received while the session is hibernated by -\[mod_smacks\] or even when smacks acknowledgements for messages are +[mod_smacks] or even when smacks acknowledgements for messages are delayed by a certain amount of seconds configurable with the -\[mod_smacks\] setting `smacks_max_ack_delay`. +[mod_smacks] setting `smacks_max_ack_delay`. The `smacks_max_ack_delay` setting allows to send out notifications to clients which aren't already in smacks hibernation state (because the
author Menel <menel@snikket.de>
date Fri, 13 Jun 2025 10:44:37 +0200
parent 4320:8fc16174fec6
line wrap: on
line source

local st_iq = require "util.stanza".iq;
local uuid_gen = require "util.uuid".generate;
local calculate_hash = require "util.caps".calculate_hash;

-- Map of jid..node, to avoid querying the same client multiple times for the same value.
local in_flight_iqs = {}

-- Some clients (*ahem* poezio…) don’t include the @node in their result iq.
local iq_node_map = {}

assert(module.send_iq, "This module is not compatible with this version of Prosody.");

local function iq_result_handler(event)
	local origin, stanza = event.origin, event.stanza;

	local query = stanza:get_child("query", "http://jabber.org/protocol/disco#info");
	if not query then
		origin.log("debug", "Wrong iq payload in disco#info result: %s", stanza);
		origin.caps_cache = nil;
		return;
	end

	local from = stanza.attr.from;
	local id = stanza.attr.id;
	local node_string = query.attr.node;
	local node_query = iq_node_map[from..id];
	if node_string == nil then
		node_string = node_query;
		query.attr.node = node_query;
	end
	iq_node_map[from..id] = nil;

	if node_string ~= node_query then
		origin.log("debug", "Wrong node for our disco#info query, expected %s, received %s", node_string, node_query);
		origin.caps_cache = nil;
		return;
	end

	local node, ver = node_query:match("([^#]+)#([^#]+)");
	local hash = calculate_hash(query)
	if ver ~= hash then
		origin.log("debug", "Wrong hash for disco#info: %s ~= %s", ver, hash);
		origin.caps_cache = nil;
		return;
	end

	origin.caps_cache = query;
	origin.log("info", "Stored caps %s", ver);
	module:fire_event("c2s-capabilities-changed", { origin = origin });
	return true;
end

local function iq_error_handler(err)
	local origin = err.context.origin;
	if origin ~= nil then
		origin.caps_cache = nil;
		module:fire_event("c2s-capabilities-changed", { origin = origin });
	end
end

local function presence_stanza_handler(event)
	local origin, stanza = event.origin, event.stanza;

	local from = stanza.attr.from;
	if stanza.attr.to ~= nil then
		return;
	end

	local caps = stanza:get_child("c", "http://jabber.org/protocol/caps");
	if caps == nil then
		origin.log("debug", "Presence from %s without caps received, skipping", from);
		return;
	end

	local hash = caps.attr.hash;
	local node = caps.attr.node;
	local ver = caps.attr.ver;
	if not hash or not node or not ver then
		return;
	end
	if hash ~= "sha-1" then
		origin.log("warn", "Presence from %s with non-SHA-1 caps : %s", from, hash);
		return;
	end

	local node_query = node.."#"..ver;
	if (origin.caps_cache and origin.caps_cache.attr.node == node_query) or in_flight_iqs[from..node_query] ~= nil then
		origin.log("debug", "Already requested these caps, skipping");
		return;
	end

	origin.log("debug", "Presence from %s with SHA-1 caps %s, querying disco#info", from, node_query);

	local id = uuid_gen();
	iq_node_map[from..id] = node_query
	local iq = st_iq({ type = "get", from = module.host, to = from, id = id })
		:tag("query", { xmlns = "http://jabber.org/protocol/disco#info", node = node_query });
	in_flight_iqs[from..node_query] = true;
	module:send_iq(iq, origin)
		:next(iq_result_handler, iq_error_handler)
		:finally(function () in_flight_iqs[from..node_query] = nil; end)
end

-- Handle only non-directed presences for now.
module:hook("pre-presence/bare", presence_stanza_handler);