Software / code / verse
Comparison
plugins/disco.lua @ 423:e98cef597393
plugins.disco: Fix to use util.caps instead of broken hacky implementation
| author | Matthew Wild <mwild1@gmail.com> |
|---|---|
| date | Wed, 27 Jun 2018 19:19:11 +0100 |
| parent | 392:cdea6a28369e |
| child | 429:46897d7769c3 |
comparison
equal
deleted
inserted
replaced
| 422:ff59e4a1a600 | 423:e98cef597393 |
|---|---|
| 7 -- | 7 -- |
| 8 | 8 |
| 9 local verse = require "verse"; | 9 local verse = require "verse"; |
| 10 local b64 = require("mime").b64; | 10 local b64 = require("mime").b64; |
| 11 local sha1 = require("util.hashes").sha1; | 11 local sha1 = require("util.hashes").sha1; |
| 12 local calculate_hash = require "util.caps".calculate_hash; | |
| 12 | 13 |
| 13 local xmlns_caps = "http://jabber.org/protocol/caps"; | 14 local xmlns_caps = "http://jabber.org/protocol/caps"; |
| 14 local xmlns_disco = "http://jabber.org/protocol/disco"; | 15 local xmlns_disco = "http://jabber.org/protocol/disco"; |
| 15 local xmlns_disco_info = xmlns_disco.."#info"; | 16 local xmlns_disco_info = xmlns_disco.."#info"; |
| 16 local xmlns_disco_items = xmlns_disco.."#items"; | 17 local xmlns_disco_items = xmlns_disco.."#items"; |
| 52 }; | 53 }; |
| 53 | 54 |
| 54 stream.caps = {} | 55 stream.caps = {} |
| 55 stream.caps.node = 'http://code.matthewwild.co.uk/verse/' | 56 stream.caps.node = 'http://code.matthewwild.co.uk/verse/' |
| 56 | 57 |
| 57 local function cmp_identity(item1, item2) | 58 local function build_self_disco_info_stanza(query_node) |
| 58 if item1.category < item2.category then | 59 local node = stream.disco.info[query_node or false]; |
| 59 return true; | 60 if query_node and query_node == stream.caps.node .. "#" .. stream.caps.hash then |
| 60 elseif item2.category < item1.category then | 61 node = stream.disco.info[false]; |
| 61 return false; | 62 end |
| 62 end | 63 local identities, features = node.identities, node.features |
| 63 if item1.type < item2.type then | 64 |
| 64 return true; | 65 -- construct the response |
| 65 elseif item2.type < item1.type then | 66 local result = verse.stanza("query", { |
| 66 return false; | 67 xmlns = xmlns_disco_info, |
| 67 end | 68 node = query_node, |
| 68 if (not item1['xml:lang'] and item2['xml:lang']) or | 69 }); |
| 69 (item2['xml:lang'] and item1['xml:lang'] < item2['xml:lang']) then | 70 for _,identity in pairs(identities) do |
| 70 return true | 71 result:tag('identity', identity):up() |
| 71 end | 72 end |
| 72 return false | 73 for feature in pairs(features) do |
| 73 end | 74 result:tag('feature', { var = feature }):up() |
| 74 | 75 end |
| 75 local function cmp_feature(item1, item2) | 76 return result; |
| 76 return item1.var < item2.var | |
| 77 end | |
| 78 | |
| 79 local function calculate_hash(node) | |
| 80 local identities = stream.disco.info[node or false].identities; | |
| 81 table.sort(identities, cmp_identity) | |
| 82 local features = {}; | |
| 83 for var in pairs(stream.disco.info[node or false].features) do | |
| 84 features[#features+1] = { var = var }; | |
| 85 end | |
| 86 table.sort(features, cmp_feature) | |
| 87 local S = {}; | |
| 88 for key,identity in pairs(identities) do | |
| 89 S[#S+1] = table.concat({ | |
| 90 identity.category, identity.type or '', | |
| 91 identity['xml:lang'] or '', identity.name or '' | |
| 92 }, '/'); | |
| 93 end | |
| 94 for key,feature in pairs(features) do | |
| 95 S[#S+1] = feature.var | |
| 96 end | |
| 97 S[#S+1] = ''; | |
| 98 S = table.concat(S,'<'); | |
| 99 -- FIXME: make sure S is utf8-encoded | |
| 100 --stream:debug("Computed hash string: "..S); | |
| 101 --stream:debug("Computed hash string (sha1): "..sha1(S, true)); | |
| 102 --stream:debug("Computed hash string (sha1+b64): "..b64(sha1(S))); | |
| 103 return (b64(sha1(S))) | |
| 104 end | 77 end |
| 105 | 78 |
| 106 setmetatable(stream.caps, { | 79 setmetatable(stream.caps, { |
| 107 __call = function (...) -- vararg: allow calling as function or member | 80 __call = function (...) -- vararg: allow calling as function or member |
| 108 -- retrieve the c stanza to insert into the | 81 -- retrieve the c stanza to insert into the |
| 109 -- presence stanza | 82 -- presence stanza |
| 110 local hash = calculate_hash() | 83 local hash = calculate_hash(build_self_disco_info_stanza()) |
| 111 stream.caps.hash = hash; | 84 stream.caps.hash = hash; |
| 112 -- TODO proper caching.... some day | 85 -- TODO proper caching.... some day |
| 113 return verse.stanza('c', { | 86 return verse.stanza('c', { |
| 114 xmlns = xmlns_caps, | 87 xmlns = xmlns_caps, |
| 115 hash = 'sha-1', | 88 hash = 'sha-1', |
| 294 end | 267 end |
| 295 | 268 |
| 296 stream:hook("iq/"..xmlns_disco_info, function (stanza) | 269 stream:hook("iq/"..xmlns_disco_info, function (stanza) |
| 297 local query = stanza.tags[1]; | 270 local query = stanza.tags[1]; |
| 298 if stanza.attr.type == 'get' and query.name == "query" then | 271 if stanza.attr.type == 'get' and query.name == "query" then |
| 299 local query_node = query.attr.node; | 272 local query_tag = build_self_disco_info_stanza(query.attr.node); |
| 300 local node = stream.disco.info[query_node or false]; | 273 local result = verse.reply(stanza):add_child(query_tag); |
| 301 if query_node and query_node == stream.caps.node .. "#" .. stream.caps.hash then | |
| 302 node = stream.disco.info[false]; | |
| 303 end | |
| 304 local identities, features = node.identities, node.features | |
| 305 | |
| 306 -- construct the response | |
| 307 local result = verse.reply(stanza):tag("query", { | |
| 308 xmlns = xmlns_disco_info, | |
| 309 node = query_node, | |
| 310 }); | |
| 311 for _,identity in pairs(identities) do | |
| 312 result:tag('identity', identity):up() | |
| 313 end | |
| 314 for feature in pairs(features) do | |
| 315 result:tag('feature', { var = feature }):up() | |
| 316 end | |
| 317 stream:send(result); | 274 stream:send(result); |
| 318 return true | 275 return true |
| 319 end | 276 end |
| 320 end); | 277 end); |
| 321 | 278 |
| 340 | 297 |
| 341 local initial_disco_started; | 298 local initial_disco_started; |
| 342 stream:hook("ready", function () | 299 stream:hook("ready", function () |
| 343 if initial_disco_started then return; end | 300 if initial_disco_started then return; end |
| 344 initial_disco_started = true; | 301 initial_disco_started = true; |
| 302 | |
| 303 -- Using the disco cache, fires events for each identity of a given JID | |
| 304 local function scan_identities_for_service(service_jid) | |
| 305 local service_disco_info = stream.disco.cache[service_jid]; | |
| 306 if service_disco_info then | |
| 307 for identity in pairs(service_disco_info.identities) do | |
| 308 local category, type = identity:match("^(.*)/(.*)$"); | |
| 309 print(service_jid, category, type) | |
| 310 stream:event("disco/service-discovered/"..category, { | |
| 311 type = type, jid = service_jid; | |
| 312 }); | |
| 313 end | |
| 314 end | |
| 315 end | |
| 316 | |
| 317 stream:disco_info(stream.host, nil, function () | |
| 318 scan_identities_for_service(stream.host); | |
| 319 end); | |
| 320 | |
| 345 stream:disco_local_services(function (services) | 321 stream:disco_local_services(function (services) |
| 346 for _, service in ipairs(services) do | 322 for _, service in ipairs(services) do |
| 347 local service_disco_info = stream.disco.cache[service.jid]; | 323 scan_identities_for_service(service.jid); |
| 348 if service_disco_info then | |
| 349 for identity in pairs(service_disco_info.identities) do | |
| 350 local category, type = identity:match("^(.*)/(.*)$"); | |
| 351 stream:event("disco/service-discovered/"..category, { | |
| 352 type = type, jid = service.jid; | |
| 353 }); | |
| 354 end | |
| 355 end | |
| 356 end | 324 end |
| 357 stream:event("ready"); | 325 stream:event("ready"); |
| 358 end); | 326 end); |
| 359 return true; | 327 return true; |
| 360 end, 50); | 328 end, 50); |