Comparison

mod_cache_c2s_caps/mod_cache_c2s_caps.lua @ 2899:0273d7583373

mod_cache_c2s_caps: New module caching capabilities from local clients
author Emmanuel Gil Peyrot <linkmauve@linkmauve.fr>
date Wed, 07 Mar 2018 17:11:12 +0100
child 2901:0fb44258d2cf
comparison
equal deleted inserted replaced
2898:9fd61234b6f0 2899:0273d7583373
1 local st_iq = require "util.stanza".iq;
2 local jid_split = require "util.jid".split;
3 local uuid_gen = require "util.uuid".generate;
4 local calculate_hash = require "util.caps".calculate_hash;
5
6 -- Map of jid..node, to avoid querying the same client multiple times for the same value.
7 local in_flight_iqs = {}
8
9 -- Some clients (*ahem* poezio…) don’t include the @node in their result iq.
10 local iq_node_map = {}
11
12 local function iq_result_handler(event)
13 local origin, stanza = event.origin, event.stanza;
14 local from = stanza.attr.from;
15 local id = stanza.attr.id;
16
17 local query = stanza:get_child("query", "http://jabber.org/protocol/disco#info");
18
19 local node_string = query.attr.node;
20 local node_query = iq_node_map[from..id];
21 if node_string == nil then
22 node_string = node_query;
23 query.attr.node = node_query;
24 end
25 iq_node_map[from..id] = nil;
26 in_flight_iqs[from..node_string] = nil;
27
28 if node_string ~= node_query then
29 module:log("debug", "Wrong node for our disco#info query, expected %s, received %s", node_string, node_query);
30 return;
31 end
32
33 local node, ver = node_query:match("([^#]+)#([^#]+)");
34 local hash = calculate_hash(query)
35 if ver ~= hash then
36 module:log("debug", "Wrong hash for disco#info: %s ~= %s", ver, hash);
37 end
38
39 origin.caps_cache = query;
40 module:log("info", "Stored caps %s", ver);
41 module:fire_event("c2s-capabilities-changed", { origin = origin });
42 return true;
43 end
44
45 local function iq_error_handler(event)
46 local origin = event.origin;
47 origin.caps_cache = nil;
48 module:fire_event("c2s-capabilities-changed", { origin = origin });
49 end
50
51 local function presence_stanza_handler(event)
52 local origin, stanza = event.origin, event.stanza;
53
54 local from = stanza.attr.from;
55 if stanza.attr.to ~= nil then
56 return;
57 end
58
59 local caps = stanza:get_child("c", "http://jabber.org/protocol/caps");
60 if caps == nil then
61 module:log("debug", "Presence without caps received, skipping");
62 return;
63 end
64
65 local hash = caps.attr.hash;
66 local node = caps.attr.node;
67 local ver = caps.attr.ver;
68 if not hash or not node or not ver then
69 return;
70 end
71 if hash ~= "sha-1" then
72 module:log("warn", "Non-SHA-1 caps received: %s", hash);
73 return;
74 end
75
76 local node_query = node.."#"..ver;
77 if (origin.caps_cache and origin.caps_cache.attr.node == node_query) or in_flight_iqs[from..node_query] ~= nil then
78 module:log("debug", "Already requested these caps, skipping");
79 return;
80 end
81
82 module:log("debug", "Received presence with SHA-1 caps %s, querying disco#info", node_query);
83
84 local id = uuid_gen();
85 iq_node_map[from..id] = node_query
86 local iq = st_iq({ type = "get", from = module.host, to = from, id = id })
87 :tag("query", { xmlns = "http://jabber.org/protocol/disco#info", node = node_query });
88 module:hook("iq-result/host/"..id, iq_result_handler);
89 module:hook("iq-error/host/"..id, iq_error_handler);
90 module:send(iq);
91
92 in_flight_iqs[from..node_query] = true;
93 end
94
95 -- Handle only non-directed presences for now.
96 module:hook("pre-presence/bare", presence_stanza_handler);