Tue, 21 Sep 2010 10:22:30 +0100
Correctly close <title> tag in report.html
require "verse" require "verse.client" local array = require "array"; local calculate_hash = require "caps".calculate_hash; -- Configurable: local jid, pass = arg[1], nil; --- -- -- -- --- local escape_table = { ["'"] = "'", ["\""] = """, ["<"] = "<", [">"] = ">", ["&"] = "&" }; local function escape(str) return (string.gsub(str, "['&<>\"]", escape_table)); end local xmlns_caps = "http://jabber.org/protocol/caps"; local xmlns_disco = "http://jabber.org/protocol/disco#info"; if not jid then io.write("Please give a JID as the first argument\n"); os.exit(1); end if not pass then io.write("Password (not blanked): "); pass = io.read("*l"); end local conn = verse.new(verse.logger()); conn:add_plugin("version"); conn:connect_client(jid, pass); local contacts = {}; conn:hook("ready", function () conn:hook("presence", function (presence) if contacts[presence.attr.from] or presence.attr.type then return; end; local contact = { jid = presence.attr.from }; contacts[contact.jid] = contact; local caps_tag = presence:get_child("c", xmlns_caps); if not caps_tag then conn:debug("No caps from %s: %s", presence.attr.from, tostring(presence)); return; end contact.hash_type = caps_tag.attr.hash; contact.ver = caps_tag.attr.ver; contact.node = caps_tag.attr.node; local node_string = contact.node.."#"..contact.ver; conn:query_version(contact.jid, function (version) contact.version = ("%s%s%s"):format( version.name or "[no name]", version.version and (" "..version.version) or "", version.platform and (" ("..version.platform..")") or "" ); end); conn:send_iq(verse.iq({ to = contact.jid, type = "get" }) :tag("query", { xmlns = xmlns_disco, node = node_string }), function (result) if result.attr.type == "error" then contact.error = { result:get_error() }; return; end contact.calculated_ver, contact.calculated_S = calculate_hash(result.tags[1]); if contact.calculated_ver ~= contact.ver then conn:warn("Invalid caps hash: %s", contact.jid); conn:warn("Received: %s Calculated: %s", contact.ver, contact.calculated_ver); conn:warn("Received stanza: %s", tostring(result)); conn:warn("Calculated S: %s", contact.calculated_S); else conn:warn("Valid caps hash: %s", contact.jid); end end); end, 1000); conn:send(verse.presence():tag("priority"):text("-1")); end); verse.loop(); --- Write report local report = io.open("report.html", "w+"); report:write[[<html> <head> <title>]]; report:write("Entity capabilities validity for contacts of ", jid); report:write[[</title> </head> <body> ]]; local contact_jids = array.collect(keys(contacts)):sort(); report:write("<h1>Entity capabilities report for contacts of ", jid, "</h1>\n"); report:write("<p>", tostring(#contact_jids), " contacts</p>\n"); local function write_section(title, jids, show) report:write("\n<h1>", title, "</h1>\n"); report:write("<p>", tostring(#jids), " contacts</p>\n"); report:write("<ul>\n"); for _, jid in ipairs(jids) do local contact = contacts[jid]; local client_link = (" <a href='%s'>%s</a>"):format( escape(contacts[jid].node or "#"), escape(contacts[jid].version or "Unknown client") ); report:write(" <li>", escape(jid), client_link); if show then report:write("\n <ul>\n"); for _, field in ipairs(show) do local friendly_field = field:gsub("^.", string.upper):gsub("_", " "); local value = escape(contacts[jid][field] or ""); report:write((" "):rep(12), "<li>", friendly_field, ": ", value, "</li>\n"); end report:write(" </ul>\n"); end report:write("</li>\n"); end report:write("</ul>\n"); end local function no_caps_filter(jid) return not contacts[jid].ver; end local function legacy_caps_filter(jid) return contacts[jid].ver and not contacts[jid].hash_type; end local function valid_caps_filter(jid) return not(legacy_caps_filter(jid)) and (contacts[jid].ver == contacts[jid].calculated_ver); end local function invalid_caps_filter(jid) return not(legacy_caps_filter(jid)) and not(no_caps_filter(jid)) and not(valid_caps_filter(jid)); end write_section("Valid caps", array.filter(contact_jids, valid_caps_filter)); write_section("Invalid caps", array.filter(contact_jids, invalid_caps_filter), { "ver", "calculated_ver", "calculated_S" }); write_section("No caps", array.filter(contact_jids, no_caps_filter)); write_section("Legacy caps", array.filter(contact_jids, legacy_caps_filter), { "ver" }); report:write("</body></html>");