# HG changeset patch # User Emmanuel Gil Peyrot # Date 1520439072 -3600 # Node ID 0273d7583373a853e1499f18b615397ae4075d05 # Parent 9fd61234b6f036bffcb629dbcc436c2636fd93db mod_cache_c2s_caps: New module caching capabilities from local clients diff -r 9fd61234b6f0 -r 0273d7583373 mod_cache_c2s_caps/README.markdown --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/mod_cache_c2s_caps/README.markdown Wed Mar 07 17:11:12 2018 +0100 @@ -0,0 +1,12 @@ +--- +summary: Cache caps on user sessions +--- + +Description +=========== + +This module listens on presences containing caps (XEP-0115) and asks the client +for the corresponding disco#info if it changed. + +It fires the c2s-capabilities-changed event once the disco#info result is +received. diff -r 9fd61234b6f0 -r 0273d7583373 mod_cache_c2s_caps/mod_cache_c2s_caps.lua --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/mod_cache_c2s_caps/mod_cache_c2s_caps.lua Wed Mar 07 17:11:12 2018 +0100 @@ -0,0 +1,96 @@ +local st_iq = require "util.stanza".iq; +local jid_split = require "util.jid".split; +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 = {} + +local function iq_result_handler(event) + local origin, stanza = event.origin, event.stanza; + local from = stanza.attr.from; + local id = stanza.attr.id; + + local query = stanza:get_child("query", "http://jabber.org/protocol/disco#info"); + + 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; + in_flight_iqs[from..node_string] = nil; + + if node_string ~= node_query then + module:log("debug", "Wrong node for our disco#info query, expected %s, received %s", node_string, node_query); + return; + end + + local node, ver = node_query:match("([^#]+)#([^#]+)"); + local hash = calculate_hash(query) + if ver ~= hash then + module:log("debug", "Wrong hash for disco#info: %s ~= %s", ver, hash); + end + + origin.caps_cache = query; + module:log("info", "Stored caps %s", ver); + module:fire_event("c2s-capabilities-changed", { origin = origin }); + return true; +end + +local function iq_error_handler(event) + local origin = event.origin; + origin.caps_cache = nil; + module:fire_event("c2s-capabilities-changed", { origin = origin }); +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 + module:log("debug", "Presence without caps received, skipping"); + 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 + module:log("warn", "Non-SHA-1 caps received: %s", 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 + module:log("debug", "Already requested these caps, skipping"); + return; + end + + module:log("debug", "Received presence with SHA-1 caps %s, querying disco#info", 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 }); + module:hook("iq-result/host/"..id, iq_result_handler); + module:hook("iq-error/host/"..id, iq_error_handler); + module:send(iq); + + in_flight_iqs[from..node_query] = true; +end + +-- Handle only non-directed presences for now. +module:hook("pre-presence/bare", presence_stanza_handler);