Initial commit

Sat, 18 Sep 2010 18:19:37 +0100

author
Matthew Wild <mwild1@gmail.com>
date
Sat, 18 Sep 2010 18:19:37 +0100
changeset 0
d17a1b659852
child 1
ce892ac8bec2

Initial commit

array.lua file | annotate | diff | comparison | revisions
capscan.lua file | annotate | diff | comparison | revisions
iterators.lua file | annotate | diff | comparison | revisions
squishy file | annotate | diff | comparison | revisions
--- /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;
--- /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 = { ["'"] = "&apos;", ["\""] = "&quot;", ["<"] = "&lt;", [">"] = "&gt;", ["&"] = "&amp;" };
+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[[<html>
+<head>
+	<title>]];
+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 = ("&nbsp;<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>");
--- /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
--- /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"

mercurial