1 local st_iq
= require
"util.stanza".iq
;
2 local uuid_gen
= require
"util.uuid".generate
;
3 local calculate_hash
= require
"util.caps".calculate_hash
;
5 -- Map of jid..node, to avoid querying the same client multiple times for the same value.
6 local in_flight_iqs
= {}
8 -- Some clients (*ahem* poezio…) don’t include the @node in their result iq.
11 assert(module
.send_iq
, "This module is not compatible with this version of Prosody.");
13 local function iq_result_handler(event
)
14 local origin
, stanza
= event
.origin
, event
.stanza
;
16 local query
= stanza
:get_child("query", "http://jabber.org/protocol/disco#info");
18 origin
.log("debug", "Wrong iq payload in disco#info result: %s", stanza
);
19 origin
.caps_cache
= nil;
23 local from
= stanza
.attr
.from
;
24 local id
= stanza
.attr
.id
;
25 local node_string
= query
.attr
.node
;
26 local node_query
= iq_node_map
[from
..id
];
27 if node_string
== nil then
28 node_string
= node_query
;
29 query
.attr
.node
= node_query
;
31 iq_node_map
[from
..id
] = nil;
33 if node_string
~= node_query
then
34 origin
.log("debug", "Wrong node for our disco#info query, expected %s, received %s", node_string
, node_query
);
35 origin
.caps_cache
= nil;
39 local node
, ver
= node_query
:match("([^#]+)#([^#]+)");
40 local hash
= calculate_hash(query
)
42 origin
.log("debug", "Wrong hash for disco#info: %s ~= %s", ver
, hash
);
43 origin
.caps_cache
= nil;
47 origin
.caps_cache
= query
;
48 origin
.log("info", "Stored caps %s", ver
);
49 module
:fire_event("c2s-capabilities-changed", { origin
= origin
});
53 local function iq_error_handler(err
)
54 local origin
= err
.context
.origin
;
56 origin
.caps_cache
= nil;
57 module
:fire_event("c2s-capabilities-changed", { origin
= origin
});
61 local function presence_stanza_handler(event
)
62 local origin
, stanza
= event
.origin
, event
.stanza
;
64 local from
= stanza
.attr
.from
;
65 if stanza
.attr
.to
~= nil then
69 local caps
= stanza
:get_child("c", "http://jabber.org/protocol/caps");
71 origin
.log("debug", "Presence without caps received, skipping");
75 local hash
= caps
.attr
.hash
;
76 local node
= caps
.attr
.node
;
77 local ver
= caps
.attr
.ver
;
78 if not hash
or not node
or not ver
then
81 if hash
~= "sha-1" then
82 origin
.log("warn", "Non-SHA-1 caps received: %s", hash
);
86 local node_query
= node
.."#"..ver
;
87 if (origin
.caps_cache
and origin
.caps_cache
.attr
.node
== node_query
) or in_flight_iqs
[from
..node_query
] ~= nil then
88 origin
.log("debug", "Already requested these caps, skipping");
92 origin
.log("debug", "Received presence with SHA-1 caps %s, querying disco#info", node_query
);
94 local id
= uuid_gen();
95 iq_node_map
[from
..id
] = node_query
96 local iq
= st_iq({ type = "get", from
= module
.host
, to
= from
, id
= id
})
97 :tag("query", { xmlns
= "http://jabber.org/protocol/disco#info", node
= node_query
});
98 in_flight_iqs
[from
..node_query
] = true;
99 module
:send_iq(iq
, origin
)
100 :next(iq_result_handler
, iq_error_handler
)
101 :finally(function () in_flight_iqs
[from
..node_query
] = nil; end)
104 -- Handle only non-directed presences for now.
105 module
:hook("pre-presence/bare", presence_stanza_handler
);