# HG changeset patch # User Matthew Wild # Date 1284830377 -3600 # Node ID d17a1b6598523b86a7f71b5163b6ab5c6766d324 Initial commit diff -r 000000000000 -r d17a1b659852 array.lua --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/array.lua Sat Sep 18 18:19:37 2010 +0100 @@ -0,0 +1,129 @@ +-- Prosody IM +-- Copyright (C) 2008-2010 Matthew Wild +-- Copyright (C) 2008-2010 Waqas Hussain +-- +-- This project is MIT/X11 licensed. Please see the +-- COPYING file in the source package for more information. +-- + +local t_insert, t_sort, t_remove, t_concat + = table.insert, table.sort, table.remove, table.concat; + +local array = {}; +local array_base = {}; +local array_methods = {}; +local array_mt = { __index = array_methods, __tostring = function (array) return array:concat(", "); end }; + +local function new_array(_, t) + return setmetatable(t or {}, array_mt); +end + +function array_mt.__add(a1, a2) + local res = new_array(); + return res:append(a1):append(a2); +end + +setmetatable(array, { __call = new_array }); + +function array_base.map(outa, ina, func) + for k,v in ipairs(ina) do + outa[k] = func(v); + end + return outa; +end + +function array_base.filter(outa, ina, func) + local inplace, start_length = ina == outa, #ina; + local write = 1; + for read=1,start_length do + local v = ina[read]; + if func(v) then + outa[write] = v; + write = write + 1; + end + end + + if inplace and write <= start_length then + for i=write,start_length do + outa[i] = nil; + end + end + + return outa; +end + +function array_base.sort(outa, ina, ...) + if ina ~= outa then + outa:append(ina); + end + t_sort(outa, ...); + return outa; +end + +--- These methods only mutate +function array_methods:random() + return self[math.random(1,#self)]; +end + +function array_methods:shuffle(outa, ina) + local len = #self; + for i=1,#self do + local r = math.random(i,len); + self[i], self[r] = self[r], self[i]; + end + return self; +end + +function array_methods:reverse() + local len = #self-1; + for i=len,1,-1 do + self:push(self[i]); + self:pop(i); + end + return self; +end + +function array_methods:append(array) + local len,len2 = #self, #array; + for i=1,len2 do + self[len+i] = array[i]; + end + return self; +end + +array_methods.push = table.insert; +array_methods.pop = table.remove; +array_methods.concat = table.concat; +array_methods.length = function (t) return #t; end + +--- These methods always create a new array +function array.collect(f, s, var) + local t, var = {}; + while true do + var = f(s, var); + if var == nil then break; end + table.insert(t, var); + end + return setmetatable(t, array_mt); +end + +--- + +-- Setup methods from array_base +for method, f in pairs(array_base) do + local base_method = f; + -- Setup global array method which makes new array + array[method] = function (old_a, ...) + local a = new_array(); + return base_method(a, old_a, ...); + end + -- Setup per-array (mutating) method + array_methods[method] = function (self, ...) + return base_method(self, self, ...); + end +end + +_G.array = array; +module("array"); + +return array; diff -r 000000000000 -r d17a1b659852 capscan.lua --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/capscan.lua Sat Sep 18 18:19:37 2010 +0100 @@ -0,0 +1,154 @@ +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 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] 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[[ + + ]]; +report:write("Entity capabilities validity for contacts of ", jid); +report:write[[</head> +<body> +]]; + +report:write[[<style type="text/css"> + .good-hash { background-color: #33aa33; } + .bad-hash { background-color: #aa3333; } + .no-hash { background-color: #ffffff; } + .unknown-hash { background-color: #aaaa33; } +</style> +]] + + +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>"); diff -r 000000000000 -r d17a1b659852 iterators.lua --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/iterators.lua Sat Sep 18 18:19:37 2010 +0100 @@ -0,0 +1,144 @@ +-- Prosody IM +-- Copyright (C) 2008-2010 Matthew Wild +-- Copyright (C) 2008-2010 Waqas Hussain +-- +-- This project is MIT/X11 licensed. Please see the +-- COPYING file in the source package for more information. +-- + +--[[ Iterators ]]-- + +-- Reverse an iterator +function reverse(f, s, var) + local results = {}; + + -- First call the normal iterator + while true do + local ret = { f(s, var) }; + var = ret[1]; + if var == nil then break; end + table.insert(results, 1, ret); + end + + -- Then return our reverse one + local i,max = 0, #results; + return function (results) + if i<max then + i = i + 1; + return unpack(results[i]); + end + end, results; +end + +-- Iterate only over keys in a table +local function _keys_it(t, key) + return (next(t, key)); +end +function keys(t) + return _keys_it, t; +end + +-- Iterate only over values in a table +function values(t) + local key, val; + return function (t) + key, val = next(t, key); + return val; + end, t; +end + +-- Given an iterator, iterate only over unique items +function unique(f, s, var) + local set = {}; + + return function () + while true do + local ret = { f(s, var) }; + var = ret[1]; + if var == nil then break; end + if not set[var] then + set[var] = true; + return var; + end + end + end; +end + +--[[ Return the number of items an iterator returns ]]-- +function count(f, s, var) + local x = 0; + + while true do + local ret = { f(s, var) }; + var = ret[1]; + if var == nil then break; end + x = x + 1; + end + + return x; +end + +-- Return the first n items an iterator returns +function head(n, f, s, var) + local c = 0; + return function (s, var) + if c >= n then + return nil; + end + c = c + 1; + return f(s, var); + end, s; +end + +-- Skip the first n items an iterator returns +function skip(n, f, s, var) + for i=1,n do + var = f(s, var); + end + return f, s, var; +end + +-- Return the last n items an iterator returns +function tail(n, f, s, var) + local results, count = {}, 0; + while true do + local ret = { f(s, var) }; + var = ret[1]; + if var == nil then break; end + results[(count%n)+1] = ret; + count = count + 1; + end + + if n > count then n = count; end + + local pos = 0; + return function () + pos = pos + 1; + if pos > n then return nil; end + return unpack(results[((count-1+pos)%n)+1]); + end + --return reverse(head(n, reverse(f, s, var))); +end + +-- Convert the values returned by an iterator to an array +function it2array(f, s, var) + local t, var = {}; + while true do + var = f(s, var); + if var == nil then break; end + table.insert(t, var); + end + return t; +end + +-- Treat the return of an iterator as key,value pairs, +-- and build a table +function it2table(f, s, var) + local t, var = {}; + while true do + var, var2 = f(s, var); + if var == nil then break; end + t[var] = var2; + end + return t; +end diff -r 000000000000 -r d17a1b659852 squishy --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/squishy Sat Sep 18 18:19:37 2010 +0100 @@ -0,0 +1,9 @@ +Module "verse" +Module "caps" +Module "print_table" +Module "array" + +Main "iterators.lua" +Main "capscan.lua" + +Output "capscan"