Changeset

2998:36c169ed1576

Merge Tobias's fancy SASL branch->trunk
author Matthew Wild <mwild1@gmail.com>
date Wed, 05 May 2010 11:29:10 +0100
parents 2989:87c378c77be3 (diff) 2997:6ccaefea80ec (current diff)
children 2999:9a8f942433c4
files net/server_select.lua util/sasl.lua util/sasl/digest-md5.lua
diffstat 105 files changed, 1002 insertions(+), 590 deletions(-) [+]
line wrap: on
line diff
--- a/.hgignore	Fri Mar 12 18:41:05 2010 +0100
+++ b/.hgignore	Wed May 05 11:29:10 2010 +0100
@@ -6,6 +6,7 @@
 html/*
 prosody.lua
 prosody.cfg.lua
+prosody.version
 config.unix
 *.patch
 *.orig
--- a/.hgtags	Fri Mar 12 18:41:05 2010 +0100
+++ b/.hgtags	Wed May 05 11:29:10 2010 +0100
@@ -36,3 +36,4 @@
 5ae3209fefa2c8dc1c53d08c2c1caa340b8ec542 0.5.2
 1a99a3bf3ce6dbdfb362b7fd101d761fb3cc10af 0.6.0
 81b4e738e4d321b78274132f63a9aec7007e64eb 0.6.1
+0395f2f34bd55a01ec7276884fb9a4e0051b0e7a 0.6.2
--- a/core/componentmanager.lua	Fri Mar 12 18:41:05 2010 +0100
+++ b/core/componentmanager.lua	Wed May 05 11:29:10 2010 +0100
@@ -1,6 +1,6 @@
 -- Prosody IM
--- Copyright (C) 2008-2009 Matthew Wild
--- Copyright (C) 2008-2009 Waqas Hussain
+-- 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.
--- a/core/configmanager.lua	Fri Mar 12 18:41:05 2010 +0100
+++ b/core/configmanager.lua	Wed May 05 11:29:10 2010 +0100
@@ -1,6 +1,6 @@
 -- Prosody IM
--- Copyright (C) 2008-2009 Matthew Wild
--- Copyright (C) 2008-2009 Waqas Hussain
+-- 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.
@@ -30,10 +30,11 @@
 -- When key not found in section, check key in global's section
 function section_mt(section_name)
 	return { __index = 	function (t, k)
-									local section = rawget(global_config, section_name);
-									if not section then return nil; end
-									return section[k];
-							end };
+					local section = rawget(global_config, section_name);
+					if not section then return nil; end
+					return section[k];
+				end
+	};
 end
 
 function getconfig()
@@ -112,16 +113,19 @@
 	function parsers.lua.load(data, filename)
 		local env;
 		-- The ' = true' are needed so as not to set off __newindex when we assign the functions below
-		env = setmetatable({ Host = true; host = true; Component = true, component = true,
-							Include = true, include = true, RunScript = dofile }, { __index = function (t, k)
-												return rawget(_G, k) or
-														function (settings_table)
-															config[__currenthost or "*"][k] = settings_table;
-														end;
-										end,
-								__newindex = function (t, k, v)
-											set(env.__currenthost or "*", "core", k, v);
-										end});
+		env = setmetatable({
+			Host = true; host = true; Component = true, component = true,
+			Include = true, include = true, RunScript = dofile }, {
+				__index = function (t, k)
+					return rawget(_G, k) or
+						function (settings_table)
+							config[__currenthost or "*"][k] = settings_table;
+						end;
+				end,
+				__newindex = function (t, k, v)
+					set(env.__currenthost or "*", "core", k, v);
+				end
+		});
 		
 		rawset(env, "__currenthost", "*") -- Default is global
 		function env.Host(name)
--- a/core/eventmanager.lua	Fri Mar 12 18:41:05 2010 +0100
+++ b/core/eventmanager.lua	Wed May 05 11:29:10 2010 +0100
@@ -1,6 +1,6 @@
 -- Prosody IM
--- Copyright (C) 2008-2009 Matthew Wild
--- Copyright (C) 2008-2009 Waqas Hussain
+-- 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.
@@ -10,24 +10,18 @@
 local t_insert = table.insert;
 local ipairs = ipairs;
 
+local events = _G.prosody.events;
+
 module "eventmanager"
 
 local event_handlers = {};
 
 function add_event_hook(name, handler)
-	if not event_handlers[name] then
-		event_handlers[name] = {};
-	end
-	t_insert(event_handlers[name] , handler);
+	return events.add_handler(name, handler);
 end
 
 function fire_event(name, ...)
-	local event_handlers = event_handlers[name];
-	if event_handlers then
-		for name, handler in ipairs(event_handlers) do
-			handler(...);
-		end
-	end
+	return events.fire_event(name, ...);
 end
 
-return _M;
\ No newline at end of file
+return _M;
--- a/core/hostmanager.lua	Fri Mar 12 18:41:05 2010 +0100
+++ b/core/hostmanager.lua	Wed May 05 11:29:10 2010 +0100
@@ -1,6 +1,6 @@
 -- Prosody IM
--- Copyright (C) 2008-2009 Matthew Wild
--- Copyright (C) 2008-2009 Waqas Hussain
+-- 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.
@@ -42,7 +42,7 @@
 	end
 	
 	if not activated_any_host then
-		log("error", "No hosts defined in the config file. This may cause unexpected behaviour as no modules will be loaded.");
+		log("error", "No active VirtualHost entries in the config file. This may cause unexpected behaviour as no modules will be loaded.");
 	end
 	
 	eventmanager.fire_event("hosts-activated", defined_hosts);
@@ -60,8 +60,8 @@
 			dialback_secret = configmanager.get(host, "core", "dialback_secret") or uuid_gen();
 	              };
 	for option_name in pairs(host_config.core) do
-		if option_name:match("_ports$") then
-			log("warn", "%s: Option '%s' has no effect for virtual hosts - put it in global Host \"*\" instead", host, option_name);
+		if option_name:match("_ports$") or option_name:match("_interface$") then
+			log("warn", "%s: Option '%s' has no effect for virtual hosts - put it in the server-wide section instead", host, option_name);
 		end
 	end
 	
--- a/core/loggingmanager.lua	Fri Mar 12 18:41:05 2010 +0100
+++ b/core/loggingmanager.lua	Wed May 05 11:29:10 2010 +0100
@@ -1,6 +1,6 @@
 -- Prosody IM
--- Copyright (C) 2008-2009 Matthew Wild
--- Copyright (C) 2008-2009 Waqas Hussain
+-- 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.
@@ -33,9 +33,9 @@
 module "loggingmanager"
 
 -- The log config used if none specified in the config file
-local default_logging = { { to = "console" } };
+local default_logging = { { to = "console" , levels = { min = (debug_mode and "debug" ) } } };
 local default_file_logging = { { to = "file", levels = { min = (debug_mode and "debug") or "info" }, timestamps = true } };
-local default_timestamp = "%b %d %T";
+local default_timestamp = "%b %d %H:%M:%S";
 -- The actual config loggingmanager is using
 local logging_config = config.get("*", "core", "log") or default_logging;
 
--- a/core/modulemanager.lua	Fri Mar 12 18:41:05 2010 +0100
+++ b/core/modulemanager.lua	Wed May 05 11:29:10 2010 +0100
@@ -1,6 +1,6 @@
 -- Prosody IM
--- Copyright (C) 2008-2009 Matthew Wild
--- Copyright (C) 2008-2009 Waqas Hussain
+-- 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.
@@ -19,7 +19,7 @@
 local hosts = hosts;
 local prosody = prosody;
 
-local loadfile, pcall = loadfile, pcall;
+local loadfile, pcall, xpcall = loadfile, pcall, xpcall;
 local setmetatable, setfenv, getfenv = setmetatable, setfenv, getfenv;
 local pairs, ipairs = pairs, ipairs;
 local t_insert, t_concat = table.insert, table.concat;
@@ -29,6 +29,14 @@
 local error = error;
 local tostring, tonumber = tostring, tonumber;
 
+local debug_traceback = debug.traceback;
+local unpack, select = unpack, select;
+pcall = function(f, ...)
+	local n = select("#", ...);
+	local params = {...};
+	return xpcall(function() f(unpack(params, 1, n)) end, function(e) return tostring(e).."\n"..debug_traceback(); end);
+end
+
 local array, set = require "util.array", require "util.set";
 
 local autoload_modules = {"presence", "message", "iq"};
@@ -274,7 +282,7 @@
 		(handlers[1])(origin, stanza);
 		return true;
 	else
-		if stanza.attr.xmlns == "jabber:client" then
+		if stanza.attr.xmlns == nil then
 			log("debug", "Unhandled %s stanza: %s; xmlns=%s", origin.type, stanza.name, xmlns); -- we didn't handle it
 			if stanza.attr.type ~= "error" and stanza.attr.type ~= "result" then
 				origin.send(st.error_reply(stanza, "cancel", "service-unavailable"));
--- a/core/offlinemanager.lua	Fri Mar 12 18:41:05 2010 +0100
+++ b/core/offlinemanager.lua	Wed May 05 11:29:10 2010 +0100
@@ -1,6 +1,6 @@
 -- Prosody IM
--- Copyright (C) 2008-2009 Matthew Wild
--- Copyright (C) 2008-2009 Waqas Hussain
+-- 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.
--- a/core/rostermanager.lua	Fri Mar 12 18:41:05 2010 +0100
+++ b/core/rostermanager.lua	Wed May 05 11:29:10 2010 +0100
@@ -1,6 +1,6 @@
 -- Prosody IM
--- Copyright (C) 2008-2009 Matthew Wild
--- Copyright (C) 2008-2009 Waqas Hussain
+-- 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.
@@ -114,8 +114,14 @@
 		--end
 	end
 	if roster then
-		if not roster[false] then roster[false] = {}; end
-		roster[false].version = (roster[false].version or 0) + 1;
+		local metadata = roster[false];
+		if not metadata then
+			metadata = {};
+			roster[false] = metadata;
+		end
+		if metadata.version ~= true then
+			metadata.version = (metadata.version or 0) + 1;
+		end
 		return datamanager.store(username, host, "roster", roster);
 	end
 	log("warn", "save_roster: user had no roster to save");
--- a/core/s2smanager.lua	Fri Mar 12 18:41:05 2010 +0100
+++ b/core/s2smanager.lua	Wed May 05 11:29:10 2010 +0100
@@ -1,6 +1,6 @@
 -- Prosody IM
--- Copyright (C) 2008-2009 Matthew Wild
--- Copyright (C) 2008-2009 Waqas Hussain
+-- 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.
@@ -16,8 +16,10 @@
 local format = string.format;
 local t_insert, t_sort = table.insert, table.sort;
 local get_traceback = debug.traceback;
-local tostring, pairs, ipairs, getmetatable, newproxy, error, tonumber
-    = tostring, pairs, ipairs, getmetatable, newproxy, error, tonumber;
+local tostring, pairs, ipairs, getmetatable, newproxy, error, tonumber,
+      setmetatable
+    = tostring, pairs, ipairs, getmetatable, newproxy, error, tonumber,
+      setmetatable;
 
 local idna_to_ascii = require "util.encodings".idna.to_ascii;
 local connlisteners_get = require "net.connlisteners".get;
@@ -66,7 +68,7 @@
 		for i, data in ipairs(sendq) do
 			local reply = data[2];
 			local xmlns = reply.attr.xmlns;
-			if not xmlns or xmlns == "jabber:client" or xmlns == "jabber:server" then
+			if not xmlns then
 				reply.attr.type = "error";
 				reply:tag("error", {type = "cancel"})
 					:tag("remote-server-not-found", {xmlns = "urn:ietf:params:xml:ns:xmpp-stanzas"}):up();
@@ -89,7 +91,7 @@
 	local host = hosts[from_host].s2sout[to_host];
 	if host then
 		-- We have a connection to this host already
-		if host.type == "s2sout_unauthed" and (data.name ~= "db:verify" or not host.dialback_key) and ((not data.xmlns) or data.xmlns == "jabber:client" or data.xmlns == "jabber:server") then
+		if host.type == "s2sout_unauthed" and (data.name ~= "db:verify" or not host.dialback_key) then
 			(host.log or log)("debug", "trying to send over unauthed s2sout to "..to_host);
 			
 			-- Queue stanza until we are able to send it
@@ -367,11 +369,6 @@
 		session.secure = true;
 	end
 	
-	if session.version >= 1.0 and not (attr.to and attr.from) then
-		(session.log or log)("warn", "Remote of stream "..(session.from_host or "(unknown)").."->"..(session.to_host or "(unknown)")
-			.." failed to specify to (%s) and/or from (%s) hostname as per RFC", tostring(attr.to), tostring(attr.from));
-	end
-	
 	if session.direction == "incoming" then
 		-- Send a reply stream header
 		session.to_host = attr.to and nameprep(attr.to);
@@ -432,11 +429,8 @@
 end
 
 function streamclosed(session)
-	(session.log or log)("debug", "</stream:stream>");
-	if session.sends2s then
-		session.sends2s("</stream:stream>");
-	end
-	session.notopen = true;
+	(session.log or log)("debug", "Received </stream:stream>");
+	session:close();
 end
 
 function initiate_dialback(session)
@@ -510,9 +504,32 @@
 	end
 end
 
-local function null_data_handler(conn, data) log("debug", "Discarding data from destroyed s2s session: %s", data); end
+local resting_session = { -- Resting, not dead
+		destroyed = true;
+		type = "s2s_destroyed";
+		open_stream = function (session)
+			session.log("debug", "Attempt to open stream on resting session");
+		end;
+		close = function (session)
+			session.log("debug", "Attempt to close already-closed session");
+		end;
+	}; resting_session.__index = resting_session;
+
+function retire_session(session)
+	local log = session.log or log;
+	for k in pairs(session) do
+		if k ~= "trace" and k ~= "log" and k ~= "id" then
+			session[k] = nil;
+		end
+	end
+
+	function session.send(data) log("debug", "Discarding data sent to resting session: %s", tostring(data)); end
+	function session.data(data) log("debug", "Discarding data received from resting session: %s", tostring(data)); end
+	return setmetatable(session, resting_session);
+end
 
 function destroy_session(session, reason)
+	if session.destroyed then return; end
 	(session.log or log)("info", "Destroying "..tostring(session.direction).." session "..tostring(session.from_host).."->"..tostring(session.to_host));
 	
 	if session.direction == "outgoing" then
@@ -522,12 +539,7 @@
 		incoming_s2s[session] = nil;
 	end
 	
-	for k in pairs(session) do
-		if k ~= "trace" then
-			session[k] = nil;
-		end
-	end
-	session.data = null_data_handler;
+	retire_session(session); -- Clean session until it is GC'd
 end
 
 return _M;
--- a/core/sessionmanager.lua	Fri Mar 12 18:41:05 2010 +0100
+++ b/core/sessionmanager.lua	Wed May 05 11:29:10 2010 +0100
@@ -1,6 +1,6 @@
 -- Prosody IM
--- Copyright (C) 2008-2009 Matthew Wild
--- Copyright (C) 2008-2009 Waqas Hussain
+-- 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.
@@ -8,7 +8,7 @@
 
 
 
-local tonumber, tostring = tonumber, tostring;
+local tonumber, tostring, setmetatable = tonumber, tostring, setmetatable;
 local ipairs, pairs, print, next= ipairs, pairs, print, next;
 local format = import("string", "format");
 
@@ -66,10 +66,30 @@
 	return session;
 end
 
-local function null_data_handler(conn, data) log("debug", "Discarding data from destroyed c2s session: %s", data); end
+local resting_session = { -- Resting, not dead
+		destroyed = true;
+		type = "c2s_destroyed";
+		close = function (session)
+			session.log("debug", "Attempt to close already-closed session");
+		end;
+	}; resting_session.__index = resting_session;
+
+function retire_session(session)
+	local log = session.log or log;
+	for k in pairs(session) do
+		if k ~= "trace" and k ~= "log" and k ~= "id" then
+			session[k] = nil;
+		end
+	end
+
+	function session.send(data) log("debug", "Discarding data sent to resting session: %s", tostring(data)); end
+	function session.data(data) log("debug", "Discarding data received from resting session: %s", tostring(data)); end
+	return setmetatable(session, resting_session);
+end
 
 function destroy_session(session, err)
 	(session.log or log)("info", "Destroying session for %s (%s@%s)", session.full_jid or "(unknown)", session.username or "(unknown)", session.host or "(unknown)");
+	if session.destroyed then return; end
 	
 	-- Remove session/resource from user's session list
 	if session.full_jid then
@@ -85,12 +105,7 @@
 		hosts[session.host].events.fire_event("resource-unbind", {session=session, error=err});
 	end
 	
-	for k in pairs(session) do
-		if k ~= "trace" then
-			session[k] = nil;
-		end
-	end
-	session.data = null_data_handler;
+	retire_session(session);
 end
 
 function make_authenticated(session, username)
@@ -168,7 +183,12 @@
 
 function streamopened(session, attr)
 	local send = session.send;
-	session.host = attr.to or error("Client failed to specify destination hostname");
+	session.host = attr.to;
+	if not session.host then
+		session:close{ condition = "improper-addressing",
+			text = "A 'to' attribute is required on stream headers" };
+		return;
+	end
 	session.host = nameprep(session.host);
 	session.version = tonumber(attr.version) or 0;
 	session.streamid = uuid_generate();
@@ -201,8 +221,8 @@
 end
 
 function streamclosed(session)
-	session.send("</stream:stream>");
-	session.notopen = true;
+	session.log("debug", "Received </stream:stream>");
+	session:close();
 end
 
 function send_to_available_resources(user, host, stanza)
--- a/core/stanza_router.lua	Fri Mar 12 18:41:05 2010 +0100
+++ b/core/stanza_router.lua	Wed May 05 11:29:10 2010 +0100
@@ -1,6 +1,6 @@
 -- Prosody IM
--- Copyright (C) 2008-2009 Matthew Wild
--- Copyright (C) 2008-2009 Waqas Hussain
+-- 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.
@@ -23,9 +23,6 @@
 function core_process_stanza(origin, stanza)
 	(origin.log or log)("debug", "Received[%s]: %s", origin.type, stanza:top_tag())
 
-	-- Currently we guarantee every stanza to have an xmlns, should we keep this rule?
-	if not stanza.attr.xmlns then stanza.attr.xmlns = "jabber:client"; end
-	
 	-- TODO verify validity of stanza (as well as JID validity)
 	if stanza.attr.type == "error" and #stanza.tags == 0 then return; end -- TODO invalid stanza, log
 	if stanza.name == "iq" then
@@ -36,12 +33,14 @@
 		end
 	end
 
-	if origin.type == "c2s" then
+	if origin.type == "c2s" and not stanza.attr.xmlns then
 		if not origin.full_jid
 			and not(stanza.name == "iq" and stanza.attr.type == "set" and stanza.tags[1] and stanza.tags[1].name == "bind"
 					and stanza.tags[1].attr.xmlns == "urn:ietf:params:xml:ns:xmpp-bind") then
 			-- authenticated client isn't bound and current stanza is not a bind request
-			origin.send(st.error_reply(stanza, "auth", "not-authorized")); -- FIXME maybe allow stanzas to account or server
+			if stanza.attr.type ~= "result" and stanza.attr.type ~= "error" then
+				origin.send(st.error_reply(stanza, "auth", "not-authorized")); -- FIXME maybe allow stanzas to account or server
+			end
 			return;
 		end
 
@@ -90,7 +89,7 @@
 		return; -- FIXME what should we do here?
 	end]] -- FIXME
 
-	if (origin.type == "s2sin" or origin.type == "c2s" or origin.type == "component") and xmlns == "jabber:client" then
+	if (origin.type == "s2sin" or origin.type == "c2s" or origin.type == "component") and xmlns == nil then
 		if origin.type == "s2sin" and not origin.dummy then
 			local host_status = origin.hosts[from_host];
 			if not host_status or not host_status.authed then -- remote server trying to impersonate some other server?
@@ -103,14 +102,14 @@
 		local h = hosts[stanza.attr.to or origin.host or origin.to_host];
 		if h then
 			local event;
-			if stanza.attr.xmlns == "jabber:client" then
+			if xmlns == nil then
 				if stanza.name == "iq" and (stanza.attr.type == "set" or stanza.attr.type == "get") then
 					event = "stanza/iq/"..stanza.tags[1].attr.xmlns..":"..stanza.tags[1].name;
 				else
 					event = "stanza/"..stanza.name;
 				end
 			else
-				event = "stanza/"..stanza.attr.xmlns..":"..stanza.name;
+				event = "stanza/"..xmlns..":"..stanza.name;
 			end
 			if h.events.fire_event(event, {origin = origin, stanza = stanza}) then return; end
 		end
--- a/core/usermanager.lua	Fri Mar 12 18:41:05 2010 +0100
+++ b/core/usermanager.lua	Wed May 05 11:29:10 2010 +0100
@@ -1,6 +1,6 @@
 -- Prosody IM
--- Copyright (C) 2008-2009 Matthew Wild
--- Copyright (C) 2008-2009 Waqas Hussain
+-- 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.
@@ -14,68 +14,119 @@
 local hashes = require "util.hashes";
 local jid_bare = require "util.jid".bare;
 local config = require "core.configmanager";
+local hosts = hosts;
+
+local prosody = _G.prosody;
 
 module "usermanager"
 
-function validate_credentials(host, username, password, method)
-	log("debug", "User '%s' is being validated", username);
-	local credentials = datamanager.load(username, host, "accounts") or {};
+local new_default_provider;
 
-	if method == nil then method = "PLAIN"; end
-	if method == "PLAIN" and credentials.password then -- PLAIN, do directly
+prosody.events.add_handler("host-activated", function (host)
+	local host_session = hosts[host];
+	host_session.events.add_handler("item-added/auth-provider", function (provider)
+		if config.get(host, "core", "authentication") == provider.name then
+			host_session.users = provider;
+		end
+	end);
+	host_session.events.add_handler("item-removed/auth-provider", function (provider)
+		if host_session.users == provider then
+			host_session.users = new_default_provider(host);
+		end
+	end);
+	host_session.users = new_default_provider(host); -- Start with the default usermanager provider
+end);
+
+local function is_cyrus(host) return config.get(host, "core", "sasl_backend") == "cyrus"; end
+
+function new_default_provider(host)
+	local provider = {};
+	
+	function provider.test_password(username, password)
+		if is_cyrus(host) then return nil, "Legacy auth not supported with Cyrus SASL."; end
+		local credentials = datamanager.load(username, host, "accounts") or {};
+	
 		if password == credentials.password then
 			return true;
 		else
 			return nil, "Auth failed. Invalid username or password.";
 		end
-  end
-	-- must do md5
-	-- make credentials md5
-	local pwd = credentials.password;
-	if not pwd then pwd = credentials.md5; else pwd = hashes.md5(pwd, true); end
-	-- make password md5
-	if method == "PLAIN" then
-		password = hashes.md5(password or "", true);
-	elseif method ~= "DIGEST-MD5" then
-		return nil, "Unsupported auth method";
+	end
+
+	function provider.get_password(username)
+		if is_cyrus(host) then return nil, "Passwords unavailable for Cyrus SASL."; end
+		return (datamanager.load(username, host, "accounts") or {}).password;
+	end
+	
+	function provider.set_password(username, password)
+		if is_cyrus(host) then return nil, "Passwords unavailable for Cyrus SASL."; end
+		local account = datamanager.load(username, host, "accounts");
+		if account then
+			account.password = password;
+			return datamanager.store(username, host, "accounts", account);
+		end
+		return nil, "Account not available.";
+	end
+
+	function provider.user_exists(username)
+		if is_cyrus(host) then return true; end
+		return datamanager.load(username, host, "accounts") ~= nil; -- FIXME also check for empty credentials
+	end
+
+	function provider.create_user(username, password)
+		if is_cyrus(host) then return nil, "Account creation/modification not available with Cyrus SASL."; end
+		return datamanager.store(username, host, "accounts", {password = password});
 	end
-	-- compare
-	if password == pwd then
-		return true;
-	else
-		return nil, "Auth failed. Invalid username or password.";
+
+	function provider.get_supported_methods()
+		return {["PLAIN"] = true, ["DIGEST-MD5"] = true}; -- TODO this should be taken from the config
 	end
+
+	function provider.is_admin(jid)
+		host = host or "*";
+		local admins = config.get(host, "core", "admins");
+		if host ~= "*" and admins == config.get("*", "core", "admins") then
+			return nil;
+		end
+		if type(admins) == "table" then
+			jid = jid_bare(jid);
+			for _,admin in ipairs(admins) do
+				if admin == jid then return true; end
+			end
+		elseif admins then
+			log("warn", "Option 'admins' for host '%s' is not a table", host);
+		end
+		return nil;
+	end
+	return provider;
+end
+
+function validate_credentials(host, username, password, method)
+	return hosts[host].users.test_password(username, password);
 end
 
 function get_password(username, host)
-  return (datamanager.load(username, host, "accounts") or {}).password
+	return hosts[host].users.get_password(username);
+end
+
+function set_password(username, host, password)
+	return hosts[host].users.set_password(username, password);
 end
 
 function user_exists(username, host)
-	return datamanager.load(username, host, "accounts") ~= nil; -- FIXME also check for empty credentials
+	return hosts[host].users.user_exists(username);
 end
 
 function create_user(username, password, host)
-	return datamanager.store(username, host, "accounts", {password = password});
+	return hosts[host].users.create_user(username, password);
 end
 
 function get_supported_methods(host)
-	return {["PLAIN"] = true, ["DIGEST-MD5"] = true}; -- TODO this should be taken from the config
+	return hosts[host].users.get_supported_methods();
 end
 
 function is_admin(jid, host)
-	host = host or "*";
-	local admins = config.get(host, "core", "admins");
-	if host ~= "*" and admins == config.get("*", "core", "admins") then
-		return nil;
-	end
-	if type(admins) == "table" then
-		jid = jid_bare(jid);
-		for _,admin in ipairs(admins) do
-			if admin == jid then return true; end
-		end
-	elseif admins then log("warn", "Option 'admins' for host '%s' is not a table", host); end
-	return nil;
+	return hosts[host].users.is_admin(jid);
 end
 
 return _M;
--- a/core/xmlhandlers.lua	Fri Mar 12 18:41:05 2010 +0100
+++ b/core/xmlhandlers.lua	Wed May 05 11:29:10 2010 +0100
@@ -1,6 +1,6 @@
 -- Prosody IM
--- Copyright (C) 2008-2009 Matthew Wild
--- Copyright (C) 2008-2009 Waqas Hussain
+-- 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.
--- a/fallbacks/bit.lua	Fri Mar 12 18:41:05 2010 +0100
+++ b/fallbacks/bit.lua	Wed May 05 11:29:10 2010 +0100
@@ -1,6 +1,6 @@
 -- Prosody IM
--- Copyright (C) 2008-2009 Matthew Wild
--- Copyright (C) 2008-2009 Waqas Hussain
+-- 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.
--- a/net/adns.lua	Fri Mar 12 18:41:05 2010 +0100
+++ b/net/adns.lua	Wed May 05 11:29:10 2010 +0100
@@ -1,6 +1,6 @@
 -- Prosody IM
--- Copyright (C) 2008-2009 Matthew Wild
--- Copyright (C) 2008-2009 Waqas Hussain
+-- 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.
--- a/net/connlisteners.lua	Fri Mar 12 18:41:05 2010 +0100
+++ b/net/connlisteners.lua	Wed May 05 11:29:10 2010 +0100
@@ -1,6 +1,6 @@
 -- Prosody IM
--- Copyright (C) 2008-2009 Matthew Wild
--- Copyright (C) 2008-2009 Waqas Hussain
+-- 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.
--- a/net/http.lua	Fri Mar 12 18:41:05 2010 +0100
+++ b/net/http.lua	Wed May 05 11:29:10 2010 +0100
@@ -1,6 +1,6 @@
 -- Prosody IM
--- Copyright (C) 2008-2009 Matthew Wild
--- Copyright (C) 2008-2009 Waqas Hussain
+-- 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.
--- a/net/httpclient_listener.lua	Fri Mar 12 18:41:05 2010 +0100
+++ b/net/httpclient_listener.lua	Wed May 05 11:29:10 2010 +0100
@@ -1,6 +1,6 @@
 -- Prosody IM
--- Copyright (C) 2008-2009 Matthew Wild
--- Copyright (C) 2008-2009 Waqas Hussain
+-- 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.
--- a/net/httpserver.lua	Fri Mar 12 18:41:05 2010 +0100
+++ b/net/httpserver.lua	Wed May 05 11:29:10 2010 +0100
@@ -1,6 +1,6 @@
 -- Prosody IM
--- Copyright (C) 2008-2009 Matthew Wild
--- Copyright (C) 2008-2009 Waqas Hussain
+-- 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.
--- a/net/httpserver_listener.lua	Fri Mar 12 18:41:05 2010 +0100
+++ b/net/httpserver_listener.lua	Wed May 05 11:29:10 2010 +0100
@@ -1,6 +1,6 @@
 -- Prosody IM
--- Copyright (C) 2008-2009 Matthew Wild
--- Copyright (C) 2008-2009 Waqas Hussain
+-- 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.
--- a/net/server.lua	Fri Mar 12 18:41:05 2010 +0100
+++ b/net/server.lua	Wed May 05 11:29:10 2010 +0100
@@ -1,3 +1,10 @@
+-- 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 use_luaevent = require "core.configmanager".get("*", "core", "use_libevent");
 
--- a/net/server_event.lua	Fri Mar 12 18:41:05 2010 +0100
+++ b/net/server_event.lua	Wed May 05 11:29:10 2010 +0100
@@ -20,14 +20,14 @@
 
 local cfg = {
 	MAX_CONNECTIONS       = 100000,  -- max per server connections (use "ulimit -n" on *nix)
-	MAX_HANDSHAKE_ATTEMPS = 1000,  -- attemps to finish ssl handshake
-	HANDSHAKE_TIMEOUT     = 30,  -- timout in seconds per handshake attemp
+	MAX_HANDSHAKE_ATTEMPS = 1000,  -- attempts to finish ssl handshake
+	HANDSHAKE_TIMEOUT     = 60,  -- timout in seconds per handshake attempt
 	MAX_READ_LENGTH       = 1024 * 1024 * 1024 * 1024,  -- max bytes allowed to read from sockets
 	MAX_SEND_LENGTH       = 1024 * 1024 * 1024 * 1024,  -- max bytes size of write buffer (for writing on sockets)
-	ACCEPT_DELAY          = 10,  -- seconds to wait until the next attemp of a full server to accept
-	READ_TIMEOUT          = 60 * 30,  -- timeout in seconds for read data from socket
-	WRITE_TIMEOUT         = 30,  -- timeout in seconds for write data on socket
-	CONNECT_TIMEOUT       = 20,  -- timeout in seconds for connection attemps
+	ACCEPT_DELAY          = 10,  -- seconds to wait until the next attempt of a full server to accept
+	READ_TIMEOUT          = 60 * 60 * 6,  -- timeout in seconds for read data from socket
+	WRITE_TIMEOUT         = 180,  -- timeout in seconds for write data on socket
+	CONNECT_TIMEOUT       = 20,  -- timeout in seconds for connection attempts
 	CLEAR_DELAY           = 5,  -- seconds to wait for clearing interface list (and calling ondisconnect listeners)
 	DEBUG                 = true,  -- show debug messages
 }
@@ -160,8 +160,8 @@
 			local callback = function( )
 				self:_lock( false,  false, false )
 				--vdebug( "start listening on client socket with id:", self.id )
-				self.eventread = addevent( base, self.conn, EV_READ, self.readcallback, cfg.READ_TIMEOUT )  -- register callback
-				self:onincoming()
+				self.eventread = addevent( base, self.conn, EV_READ, self.readcallback, cfg.READ_TIMEOUT );  -- register callback
+				self:onconnect()
 				self.eventsession = nil
 				return -1
 			end
@@ -437,6 +437,7 @@
 	
 	-- Stub handlers
 	function interface_mt:onconnect()
+		return self:onincoming(nil);
 	end
 	function interface_mt:onincoming()
 	end
@@ -535,7 +536,7 @@
 					end
 					interface.eventwrite = nil
 					return -1
-				elseif byte then  -- want write again
+				elseif byte and (err == "timeout" or err == "wantwrite") then  -- want write again
 					--vdebug( "writebuffer is not empty:", err )
 					interface.writebuffer = string_sub( interface.writebuffer, byte + 1, interface.writebufferlen )  -- new buffer
 					interface.writebufferlen = interface.writebufferlen - byte
--- a/net/server_select.lua	Fri Mar 12 18:41:05 2010 +0100
+++ b/net/server_select.lua	Wed May 05 11:29:10 2010 +0100
@@ -2,7 +2,7 @@
 -- server.lua by blastbeat of the luadch project
 -- Re-used here under the MIT/X Consortium License
 -- 
--- Modifications (C) 2008-2009 Matthew Wild, Waqas Hussain
+-- Modifications (C) 2008-2010 Matthew Wild, Waqas Hussain
 --
 
 -- // wrapping luadch stuff // --
@@ -614,7 +614,16 @@
 
 	_socketlist[ socket ] = handler
 	_readlistlen = addsocket(_readlist, socket, _readlistlen)
-
+	if listeners.onconnect then
+		_sendlistlen = addsocket(_sendlist, socket, _sendlistlen)
+		handler.sendbuffer = function ()
+			listeners.onconnect(handler);
+			handler.sendbuffer = _sendbuffer;
+			if bufferqueuelen > 0 then
+				return _senddbuffer();
+			end
+		end
+	end
 	return handler, socket
 end
 
@@ -664,7 +673,7 @@
 	if type( listeners ) ~= "table" then
 		err = "invalid listener table"
 	end
-	if not type( port ) == "number" or not ( port >= 0 and port <= 65535 ) then
+	if type( port ) ~= "number" or not ( port >= 0 and port <= 65535 ) then
 		err = "invalid port"
 	elseif _server[ port ] then
 		err = "listeners on port '" .. port .. "' already exist"
--- a/net/xmppclient_listener.lua	Fri Mar 12 18:41:05 2010 +0100
+++ b/net/xmppclient_listener.lua	Wed May 05 11:29:10 2010 +0100
@@ -1,6 +1,6 @@
 -- Prosody IM
--- Copyright (C) 2008-2009 Matthew Wild
--- Copyright (C) 2008-2009 Waqas Hussain
+-- 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.
@@ -33,13 +33,32 @@
 local stream_callbacks = { default_ns = "jabber:client",
 		streamopened = sm_streamopened, streamclosed = sm_streamclosed, handlestanza = core_process_stanza };
 
+local xmlns_xmpp_streams = "urn:ietf:params:xml:ns:xmpp-streams";
+
 function stream_callbacks.error(session, error, data)
 	if error == "no-stream" then
 		session.log("debug", "Invalid opening stream header");
 		session:close("invalid-namespace");
-	elseif session.close then
-		(session.log or log)("debug", "Client XML parse error: %s", tostring(error));
+	elseif error == "parse-error" then
+		(session.log or log)("debug", "Client XML parse error: %s", tostring(data));
 		session:close("xml-not-well-formed");
+	elseif error == "stream-error" then
+		local condition, text = "undefined-condition";
+		for child in data:children() do
+			if child.attr.xmlns == xmlns_xmpp_streams then
+				if child.name ~= "text" then
+					condition = child.name;
+				else
+					text = child:get_text();
+				end
+				if condition ~= "undefined-condition" and text then
+					break;
+				end
+			end
+		end
+		text = condition .. (text and (" ("..text..")") or "");
+		session.log("info", "Session closed by remote with error: %s", text);
+		session:close(nil, text);
 	end
 end
 
--- a/net/xmppcomponent_listener.lua	Fri Mar 12 18:41:05 2010 +0100
+++ b/net/xmppcomponent_listener.lua	Wed May 05 11:29:10 2010 +0100
@@ -1,6 +1,6 @@
 -- Prosody IM
--- Copyright (C) 2008-2009 Matthew Wild
--- Copyright (C) 2008-2009 Waqas Hussain
+-- 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.
@@ -34,16 +34,33 @@
 
 local stream_callbacks = { default_ns = xmlns_component };
 
+local xmlns_xmpp_streams = "urn:ietf:params:xml:ns:xmpp-streams";
+
 function stream_callbacks.error(session, error, data, data2)
+	if session.destroyed then return; end
 	log("warn", "Error processing component stream: "..tostring(error));
 	if error == "no-stream" then
 		session:close("invalid-namespace");
-	elseif error == "xml-parse-error" and data == "unexpected-element-close" then
-		session.log("warn", "Unexpected close of '%s' tag", data2);
+	elseif error == "parse-error" then
+		session.log("warn", "External component %s XML parse error: %s", tostring(session.host), tostring(data));
 		session:close("xml-not-well-formed");
-	else
-		session.log("warn", "External component %s XML parse error: %s", tostring(session.host), tostring(error));
-		session:close("xml-not-well-formed");
+	elseif error == "stream-error" then
+		local condition, text = "undefined-condition";
+		for child in data:children() do
+			if child.attr.xmlns == xmlns_xmpp_streams then
+				if child.name ~= "text" then
+					condition = child.name;
+				else
+					text = child:get_text();
+				end
+				if condition ~= "undefined-condition" and text then
+					break;
+				end
+			end
+		end
+		text = condition .. (text and (" ("..text..")") or "");
+		session.log("info", "Session closed by remote with error: %s", text);
+		session:close(nil, text);
 	end
 end
 
@@ -71,8 +88,8 @@
 end
 
 function stream_callbacks.streamclosed(session)
-	session.send("</stream:stream>");
-	session.notopen = true;
+	session.log("Received </stream:stream>");
+	session:close();
 end
 
 local core_process_stanza = core_process_stanza;
@@ -89,6 +106,7 @@
 local stream_xmlns_attr = {xmlns='urn:ietf:params:xml:ns:xmpp-streams'};
 local default_stream_attr = { ["xmlns:stream"] = "http://etherx.jabber.org/streams", xmlns = stream_callbacks.default_ns, version = "1.0", id = "" };
 local function session_close(session, reason)
+	if session.destroyed then return; end
 	local log = session.log or log;
 	if session.conn then
 		if session.notopen then
@@ -146,6 +164,7 @@
 		function session.data(conn, data)
 			local ok, err = parser:parse(data);
 			if ok then return; end
+			log("debug", "Received invalid XML (%s) %d bytes: %s", tostring(err), #data, data:sub(1, 300):gsub("[\r\n]+", " "):gsub("[%z\1-\31]", "_"));
 			session:close("xml-not-well-formed");
 		end
 		
@@ -167,7 +186,12 @@
 			hosts[session.host].connected = nil;
 		end
 		sessions[conn]  = nil;
-		for k in pairs(session) do session[k] = nil; end
+		for k in pairs(session) do
+			if k ~= "log" and k ~= "close" then
+				session[k] = nil;
+			end
+		end
+		session.destroyed = true;
 		session = nil;
 	end
 end
--- a/net/xmppserver_listener.lua	Fri Mar 12 18:41:05 2010 +0100
+++ b/net/xmppserver_listener.lua	Wed May 05 11:29:10 2010 +0100
@@ -1,6 +1,6 @@
 -- Prosody IM
--- Copyright (C) 2008-2009 Matthew Wild
--- Copyright (C) 2008-2009 Waqas Hussain
+-- 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.
@@ -50,6 +50,9 @@
 
 local function handleerr(err) log("error", "Traceback[s2s]: %s: %s", tostring(err), debug.traceback()); end
 function stream_callbacks.handlestanza(a, b)
+	if b.attr.xmlns == "jabber:client" then --COMPAT: Prosody pre-0.6.2 may send jabber:client
+		b.attr.xmlns = nil;
+	end
 	xpcall(function () core_process_stanza(a, b) end, handleerr);
 end
 
@@ -176,7 +179,7 @@
 				return; -- Session lives for now
 			end
 		end
-		(session.log or log)("info", "s2s disconnected: %s->%s (%s)", tostring(session.from_host), tostring(session.to_host), tostring(err));
+		(session.log or log)("info", "s2s disconnected: %s->%s (%s)", tostring(session.from_host), tostring(session.to_host), tostring(err or "closed"));
 		s2s_destroy_session(session, err);
 		sessions[conn]  = nil;
 		session = nil;
--- a/plugins/mod_announce.lua	Fri Mar 12 18:41:05 2010 +0100
+++ b/plugins/mod_announce.lua	Wed May 05 11:29:10 2010 +0100
@@ -1,6 +1,6 @@
 -- Prosody IM
--- Copyright (C) 2008-2009 Matthew Wild
--- Copyright (C) 2008-2009 Waqas Hussain
+-- 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.
--- a/plugins/mod_bosh.lua	Fri Mar 12 18:41:05 2010 +0100
+++ b/plugins/mod_bosh.lua	Wed May 05 11:29:10 2010 +0100
@@ -1,6 +1,6 @@
 -- Prosody IM
--- Copyright (C) 2008-2009 Matthew Wild
--- Copyright (C) 2008-2009 Waqas Hussain
+-- 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.
@@ -23,7 +23,7 @@
 local log = logger.init("mod_bosh");
 
 local xmlns_bosh = "http://jabber.org/protocol/httpbind"; -- (hard-coded into a literal in session.send)
-local stream_callbacks = { stream_ns = "http://jabber.org/protocol/httpbind", stream_tag = "body", default_ns = xmlns_bosh };
+local stream_callbacks = { stream_ns = "http://jabber.org/protocol/httpbind", stream_tag = "body", default_ns = "jabber:client" };
 
 local BOSH_DEFAULT_HOLD = tonumber(module:get_option("bosh_default_hold")) or 1;
 local BOSH_DEFAULT_INACTIVITY = tonumber(module:get_option("bosh_max_inactivity")) or 60;
@@ -274,7 +274,7 @@
 	local session = sessions[request.sid];
 	if session then
 		if stanza.attr.xmlns == xmlns_bosh then
-			stanza.attr.xmlns = "jabber:client";
+			stanza.attr.xmlns = nil;
 		end
 		session.ip = request.handler:ip();
 		core_process_stanza(session, stanza);
--- a/plugins/mod_component.lua	Fri Mar 12 18:41:05 2010 +0100
+++ b/plugins/mod_component.lua	Wed May 05 11:29:10 2010 +0100
@@ -1,6 +1,6 @@
 -- Prosody IM
--- Copyright (C) 2008-2009 Matthew Wild
--- Copyright (C) 2008-2009 Waqas Hussain
+-- 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.
--- a/plugins/mod_compression.lua	Fri Mar 12 18:41:05 2010 +0100
+++ b/plugins/mod_compression.lua	Wed May 05 11:29:10 2010 +0100
@@ -8,6 +8,8 @@
 local st = require "util.stanza";
 local zlib = require "zlib";
 local pcall = pcall;
+local tostring = tostring;
+
 local xmlns_compression_feature = "http://jabber.org/features/compress"
 local xmlns_compression_protocol = "http://jabber.org/protocol/compress"
 local xmlns_stream = "http://etherx.jabber.org/streams";
@@ -71,7 +73,7 @@
 		local error_st = st.stanza("failure", {xmlns=xmlns_compression_protocol}):tag("setup-failed");
 		(session.sends2s or session.send)(error_st);
 		session.log("error", "Failed to create zlib.deflate filter.");
-		module:log("error", deflate_stream);
+		module:log("error", "%s", tostring(deflate_stream));
 		return
 	end
 	return deflate_stream
@@ -83,8 +85,8 @@
 	if status == false then
 		local error_st = st.stanza("failure", {xmlns=xmlns_compression_protocol}):tag("setup-failed");
 		(session.sends2s or session.send)(error_st);
-		session.log("error", "Failed to create zlib.deflate filter.");
-		module:log("error", inflate_stream);
+		session.log("error", "Failed to create zlib.inflate filter.");
+		module:log("error", "%s", tostring(inflate_stream));
 		return
 	end
 	return inflate_stream
@@ -104,7 +106,7 @@
 					text = compressed;
 					extra = st.stanza("failure", {xmlns="http://jabber.org/protocol/compress"}):tag("processing-failed");
 				});
-				module:log("warn", compressed);
+				module:log("warn", "%s", tostring(compressed));
 				return;
 			end
 			session.conn:write(compressed);
@@ -125,7 +127,7 @@
 					text = decompressed;
 					extra = st.stanza("failure", {xmlns="http://jabber.org/protocol/compress"}):tag("processing-failed");
 				});
-				module:log("warn", decompressed);
+				module:log("warn", "%s", tostring(decompressed));
 				return;
 			end
 			old_data(conn, decompressed);
@@ -166,15 +168,17 @@
 		function(session, stanza)
 			-- fail if we are already compressed
 			if session.compressed then
-				local error_st = st.stanza("failure", {xmlns=xmlns_compression_protocol}):tag("unsupported-method");
+				local error_st = st.stanza("failure", {xmlns=xmlns_compression_protocol}):tag("setup-failed");
 				(session.sends2s or session.send)(error_st);
-				session.log("warn", "Tried to establish another compression layer.");
+				session.log("debug", "Client tried to establish another compression layer.");
+				return;
 			end
 			
 			-- checking if the compression method is supported
-			local method = stanza:child_with_name("method")[1];
+			local method = stanza:child_with_name("method");
+			method = method and (method[1] or "");
 			if method == "zlib" then
-				session.log("debug", method.." compression selected.");
+				session.log("debug", "zlib compression enabled.");
 				
 				-- create deflate and inflate streams
 				local deflate_stream = get_deflate_stream(session);
@@ -199,10 +203,12 @@
 						return true;
 					end;
 				session.compressed = true;
-			else
-				session.log("warn", method.." compression selected. But we don't support it.");
+			elseif method then
+				session.log("debug", "%s compression selected, but we don't support it.", tostring(method));
 				local error_st = st.stanza("failure", {xmlns=xmlns_compression_protocol}):tag("unsupported-method");
 				(session.sends2s or session.send)(error_st);
+			else
+				(session.sends2s or session.send)(st.stanza("failure", {xmlns=xmlns_compression_protocol}):tag("setup-failed"));
 			end
 		end
 );
--- a/plugins/mod_console.lua	Fri Mar 12 18:41:05 2010 +0100
+++ b/plugins/mod_console.lua	Wed May 05 11:29:10 2010 +0100
@@ -1,6 +1,6 @@
 -- Prosody IM
--- Copyright (C) 2008-2009 Matthew Wild
--- Copyright (C) 2008-2009 Waqas Hussain
+-- 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.
--- a/plugins/mod_dialback.lua	Fri Mar 12 18:41:05 2010 +0100
+++ b/plugins/mod_dialback.lua	Wed May 05 11:29:10 2010 +0100
@@ -1,6 +1,6 @@
 -- Prosody IM
--- Copyright (C) 2008-2009 Matthew Wild
--- Copyright (C) 2008-2009 Waqas Hussain
+-- 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.
--- a/plugins/mod_disco.lua	Fri Mar 12 18:41:05 2010 +0100
+++ b/plugins/mod_disco.lua	Wed May 05 11:29:10 2010 +0100
@@ -1,6 +1,6 @@
 -- Prosody IM
--- Copyright (C) 2008-2009 Matthew Wild
--- Copyright (C) 2008-2009 Waqas Hussain
+-- 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.
--- a/plugins/mod_groups.lua	Fri Mar 12 18:41:05 2010 +0100
+++ b/plugins/mod_groups.lua	Wed May 05 11:29:10 2010 +0100
@@ -1,14 +1,14 @@
 -- Prosody IM
--- Copyright (C) 2008-2009 Matthew Wild
--- Copyright (C) 2008-2009 Waqas Hussain
+-- 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 groups = { default = {} };
-local members = { [false] = {} };
+local groups;
+local members;
 
 local groups_file;
 
@@ -20,7 +20,7 @@
 function inject_roster_contacts(username, host, roster)
 	module:log("warn", "Injecting group members to roster");
 	local bare_jid = username.."@"..host;
-	if not members[bare_jid] then return; end -- Not a member of any groups
+	if not members[bare_jid] and not members[false] then return; end -- Not a member of any groups
 	
 	local function import_jids_to_roster(group_name)
 		for jid in pairs(groups[group_name]) do
@@ -39,13 +39,23 @@
 	end
 
 	-- Find groups this JID is a member of
-	for _, group_name in ipairs(members[bare_jid]) do
-		import_jids_to_roster(group_name);
+	if members[bare_jid] then
+		for _, group_name in ipairs(members[bare_jid]) do
+			module:log("debug", "Importing group %s", group_name);
+			import_jids_to_roster(group_name);
+		end
 	end
 	
 	-- Import public groups
-	for _, group_name in ipairs(members[false]) do
-		import_jids_to_roster(group_name);
+	if members[false] then
+		for _, group_name in ipairs(members[false]) do
+			module:log("debug", "Importing group %s", group_name);
+			import_jids_to_roster(group_name);
+		end
+	end
+	
+	if roster[false] then
+		roster[false].version = true;
 	end
 end
 
@@ -57,6 +67,7 @@
 				new_roster[jid] = contact;
 			end
 		end
+		new_roster[false].version = nil; -- Version is void
 		return username, host, datastore, new_roster;
 	end
 
@@ -71,20 +82,23 @@
 	datamanager.add_callback(remove_virtual_contacts);
 	
 	groups = { default = {} };
-	members = { [false] = {} };
+	members = { };
 	local curr_group = "default";
 	for line in io.lines(groups_file) do
 		if line:match("^%s*%[.-%]%s*$") then
 			curr_group = line:match("^%s*%[(.-)%]%s*$");
 			if curr_group:match("^%+") then
 				curr_group = curr_group:gsub("^%+", "");
+				if not members[false] then
+					members[false] = {};
+				end
 				members[false][#members[false]+1] = curr_group; -- Is a public group
 			end
 			module:log("debug", "New group: %s", tostring(curr_group));
 			groups[curr_group] = groups[curr_group] or {};
 		else
 			-- Add JID
-			local jid = jid_prep(line);
+			local jid = jid_prep(line:match("%S+"));
 			if jid then
 				module:log("debug", "New member of %s: %s", tostring(curr_group), tostring(jid));
 				groups[curr_group][jid] = true;
--- a/plugins/mod_httpserver.lua	Fri Mar 12 18:41:05 2010 +0100
+++ b/plugins/mod_httpserver.lua	Wed May 05 11:29:10 2010 +0100
@@ -1,6 +1,6 @@
 -- Prosody IM
--- Copyright (C) 2008-2009 Matthew Wild
--- Copyright (C) 2008-2009 Waqas Hussain
+-- 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.
--- a/plugins/mod_iq.lua	Fri Mar 12 18:41:05 2010 +0100
+++ b/plugins/mod_iq.lua	Wed May 05 11:29:10 2010 +0100
@@ -1,6 +1,6 @@
 -- Prosody IM
--- Copyright (C) 2008-2009 Matthew Wild
--- Copyright (C) 2008-2009 Waqas Hussain
+-- 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.
--- a/plugins/mod_lastactivity.lua	Fri Mar 12 18:41:05 2010 +0100
+++ b/plugins/mod_lastactivity.lua	Wed May 05 11:29:10 2010 +0100
@@ -1,6 +1,6 @@
 -- Prosody IM
--- Copyright (C) 2008-2009 Matthew Wild
--- Copyright (C) 2008-2009 Waqas Hussain
+-- 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.
--- a/plugins/mod_legacyauth.lua	Fri Mar 12 18:41:05 2010 +0100
+++ b/plugins/mod_legacyauth.lua	Wed May 05 11:29:10 2010 +0100
@@ -1,6 +1,6 @@
 -- Prosody IM
--- Copyright (C) 2008-2009 Matthew Wild
--- Copyright (C) 2008-2009 Waqas Hussain
+-- 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.
--- a/plugins/mod_message.lua	Fri Mar 12 18:41:05 2010 +0100
+++ b/plugins/mod_message.lua	Wed May 05 11:29:10 2010 +0100
@@ -1,6 +1,6 @@
 -- Prosody IM
--- Copyright (C) 2008-2009 Matthew Wild
--- Copyright (C) 2008-2009 Waqas Hussain
+-- 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.
--- a/plugins/mod_pep.lua	Fri Mar 12 18:41:05 2010 +0100
+++ b/plugins/mod_pep.lua	Wed May 05 11:29:10 2010 +0100
@@ -1,6 +1,6 @@
 -- Prosody IM
--- Copyright (C) 2008-2009 Matthew Wild
--- Copyright (C) 2008-2009 Waqas Hussain
+-- 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.
--- a/plugins/mod_ping.lua	Fri Mar 12 18:41:05 2010 +0100
+++ b/plugins/mod_ping.lua	Wed May 05 11:29:10 2010 +0100
@@ -1,6 +1,6 @@
 -- Prosody IM
--- Copyright (C) 2008-2009 Matthew Wild
--- Copyright (C) 2008-2009 Waqas Hussain
+-- 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.
--- a/plugins/mod_posix.lua	Fri Mar 12 18:41:05 2010 +0100
+++ b/plugins/mod_posix.lua	Wed May 05 11:29:10 2010 +0100
@@ -1,6 +1,6 @@
 -- Prosody IM
--- Copyright (C) 2008-2009 Matthew Wild
--- Copyright (C) 2008-2009 Waqas Hussain
+-- 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.
--- a/plugins/mod_presence.lua	Fri Mar 12 18:41:05 2010 +0100
+++ b/plugins/mod_presence.lua	Wed May 05 11:29:10 2010 +0100
@@ -1,6 +1,6 @@
 -- Prosody IM
--- Copyright (C) 2008-2009 Matthew Wild
--- Copyright (C) 2008-2009 Waqas Hussain
+-- 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.
--- a/plugins/mod_privacy.lua	Fri Mar 12 18:41:05 2010 +0100
+++ b/plugins/mod_privacy.lua	Wed May 05 11:29:10 2010 +0100
@@ -1,6 +1,6 @@
 -- Prosody IM
--- Copyright (C) 2008-2009 Matthew Wild
--- Copyright (C) 2008-2009 Waqas Hussain
+-- Copyright (C) 2009-2010 Matthew Wild
+-- Copyright (C) 2009-2010 Waqas Hussain
 -- Copyright (C) 2009 Thilo Cestonaro
 -- 
 -- This project is MIT/X11 licensed. Please see the
--- a/plugins/mod_private.lua	Fri Mar 12 18:41:05 2010 +0100
+++ b/plugins/mod_private.lua	Wed May 05 11:29:10 2010 +0100
@@ -1,6 +1,6 @@
 -- Prosody IM
--- Copyright (C) 2008-2009 Matthew Wild
--- Copyright (C) 2008-2009 Waqas Hussain
+-- 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.
--- a/plugins/mod_register.lua	Fri Mar 12 18:41:05 2010 +0100
+++ b/plugins/mod_register.lua	Wed May 05 11:29:10 2010 +0100
@@ -1,6 +1,6 @@
 -- Prosody IM
--- Copyright (C) 2008-2009 Matthew Wild
--- Copyright (C) 2008-2009 Waqas Hussain
+-- 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.
@@ -12,6 +12,7 @@
 local datamanager = require "util.datamanager";
 local usermanager_user_exists = require "core.usermanager".user_exists;
 local usermanager_create_user = require "core.usermanager".create_user;
+local usermanager_set_password = require "core.usermanager".set_password;
 local datamanager_store = require "util.datamanager".store;
 local os_time = os.time;
 local nodeprep = require "util.encodings".stringprep.nodeprep;
@@ -34,7 +35,7 @@
 				local username, host = session.username, session.host;
 				--session.send(st.error_reply(stanza, "cancel", "not-allowed"));
 				--return;
-				usermanager_create_user(username, nil, host); -- Disable account
+				usermanager_set_password(username, host, nil); -- Disable account
 				-- FIXME the disabling currently allows a different user to recreate the account
 				-- we should add an in-memory account block mode when we have threading
 				session.send(st.reply(stanza));
@@ -69,7 +70,7 @@
 					username = nodeprep(table.concat(username));
 					password = table.concat(password);
 					if username == session.username then
-						if usermanager_create_user(username, password, session.host) then -- password change -- TODO is this the right way?
+						if usermanager_set_password(username, session.host, password) then
 							session.send(st.reply(stanza));
 						else
 							-- TODO unable to write file, file may be locked, etc, what's the correct error?
--- a/plugins/mod_roster.lua	Fri Mar 12 18:41:05 2010 +0100
+++ b/plugins/mod_roster.lua	Wed May 05 11:29:10 2010 +0100
@@ -1,6 +1,6 @@
 -- Prosody IM
--- Copyright (C) 2008-2009 Matthew Wild
--- Copyright (C) 2008-2009 Waqas Hussain
+-- 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.
@@ -36,9 +36,10 @@
 				if stanza.attr.type == "get" then
 					local roster = st.reply(stanza);
 					
-					local ver = stanza.tags[1].attr.ver
+					local client_ver = tonumber(stanza.tags[1].attr.ver);
+					local server_ver = tonumber(session.roster[false].version or 1);
 					
-					if (not ver) or tonumber(ver) ~= (session.roster[false].version or 1) then
+					if not (client_ver and server_ver) or client_ver ~= server_ver then
 						roster:query("jabber:iq:roster");
 						-- Client does not support versioning, or has stale roster
 						for jid in pairs(session.roster) do
@@ -55,7 +56,7 @@
 								roster:up(); -- move out from item
 							end
 						end
-						roster.tags[1].attr.ver = tostring(session.roster[false].version or "1");
+						roster.tags[1].attr.ver = server_ver;
 					end
 					session.send(roster);
 					session.interested = true; -- resource is interested in roster updates
--- a/plugins/mod_saslauth.lua	Fri Mar 12 18:41:05 2010 +0100
+++ b/plugins/mod_saslauth.lua	Wed May 05 11:29:10 2010 +0100
@@ -1,7 +1,7 @@
 -- Prosody IM
--- Copyright (C) 2008-2009 Matthew Wild
--- Copyright (C) 2008-2009 Waqas Hussain
---
+-- 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.
 --
@@ -35,7 +35,9 @@
 local xmlns_stanzas ='urn:ietf:params:xml:ns:xmpp-stanzas';
 
 local new_sasl;
-if sasl_backend == "cyrus" then
+if sasl_backend == "builtin" then
+	new_sasl = require "util.sasl".new;
+elseif sasl_backend == "cyrus" then
 	prosody.unlock_globals(); --FIXME: Figure out why this is needed and
 	                          -- why cyrussasl isn't caught by the sandbox
 	local ok, cyrus = pcall(require, "util.sasl_cyrus");
@@ -46,14 +48,12 @@
 			return cyrus_new(realm, module:get_option("cyrus_service_name") or "xmpp");
 		end
 	else
-		sasl_backend = "builtin";
-		module:log("warn", "Failed to load Cyrus SASL, falling back to builtin auth mechanisms");
-		module:log("debug", "Failed to load Cyrus because: %s", cyrus);
+		module:log("error", "Failed to load Cyrus SASL because: %s", cyrus);
+		error("Failed to load Cyrus SASL");
 	end
-end
-if not new_sasl then
-	if sasl_backend ~= "builtin" then module:log("warn", "Unknown SASL backend %s", sasl_backend); end;
-	new_sasl = require "util.sasl".new;
+else
+	module:log("error", "Unknown SASL backend: %s", sasl_backend);
+	error("Unknown SASL backend");
 end
 
 local default_authentication_profile = {
@@ -161,10 +161,11 @@
 		if secure_auth_only and not origin.secure then
 			return;
 		end
+		local realm = module:get_option("sasl_realm") or origin.host;
 		if module:get_option("anonymous_login") then
-			origin.sasl_handler = new_sasl(origin.host, anonymous_authentication_profile);
+			origin.sasl_handler = new_sasl(realm, anonymous_authentication_profile);
 		else
-			origin.sasl_handler = new_sasl(origin.host, default_authentication_profile);
+			origin.sasl_handler = new_sasl(realm, default_authentication_profile);
 			if not (module:get_option("allow_unencrypted_plain_auth")) and not origin.secure then
 				origin.sasl_handler:forbidden({"PLAIN"});
 			end
--- a/plugins/mod_time.lua	Fri Mar 12 18:41:05 2010 +0100
+++ b/plugins/mod_time.lua	Wed May 05 11:29:10 2010 +0100
@@ -1,6 +1,6 @@
 -- Prosody IM
--- Copyright (C) 2008-2009 Matthew Wild
--- Copyright (C) 2008-2009 Waqas Hussain
+-- 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.
--- a/plugins/mod_tls.lua	Fri Mar 12 18:41:05 2010 +0100
+++ b/plugins/mod_tls.lua	Wed May 05 11:29:10 2010 +0100
@@ -1,6 +1,6 @@
 -- Prosody IM
--- Copyright (C) 2008-2009 Matthew Wild
--- Copyright (C) 2008-2009 Waqas Hussain
+-- 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.
@@ -10,6 +10,7 @@
 
 local secure_auth_only = module:get_option("c2s_require_encryption") or module:get_option("require_encryption");
 local secure_s2s_only = module:get_option("s2s_require_encryption");
+local allow_s2s_tls = module:get_option("s2s_allow_encryption") ~= false;
 
 local xmlns_starttls = 'urn:ietf:params:xml:ns:xmpp-tls';
 local starttls_attr = { xmlns = xmlns_starttls };
@@ -27,9 +28,9 @@
 local function can_do_tls(session)
 	if session.type == "c2s_unauthed" then
 		return session.conn.starttls and host.ssl_ctx_in;
-	elseif session.type == "s2sin_unauthed" then
+	elseif session.type == "s2sin_unauthed" and allow_s2s_tls then
 		return session.conn.starttls and host.ssl_ctx_in;
-	elseif session.direction == "outgoing" then
+	elseif session.direction == "outgoing" and allow_s2s_tls then
 		return session.conn.starttls and host.ssl_ctx;
 	end
 	return false;
--- a/plugins/mod_uptime.lua	Fri Mar 12 18:41:05 2010 +0100
+++ b/plugins/mod_uptime.lua	Wed May 05 11:29:10 2010 +0100
@@ -1,6 +1,6 @@
 -- Prosody IM
--- Copyright (C) 2008-2009 Matthew Wild
--- Copyright (C) 2008-2009 Waqas Hussain
+-- 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.
--- a/plugins/mod_vcard.lua	Fri Mar 12 18:41:05 2010 +0100
+++ b/plugins/mod_vcard.lua	Wed May 05 11:29:10 2010 +0100
@@ -1,6 +1,6 @@
 -- Prosody IM
--- Copyright (C) 2008-2009 Matthew Wild
--- Copyright (C) 2008-2009 Waqas Hussain
+-- 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.
--- a/plugins/mod_version.lua	Fri Mar 12 18:41:05 2010 +0100
+++ b/plugins/mod_version.lua	Wed May 05 11:29:10 2010 +0100
@@ -1,6 +1,6 @@
 -- Prosody IM
--- Copyright (C) 2008-2009 Matthew Wild
--- Copyright (C) 2008-2009 Waqas Hussain
+-- 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.
--- a/plugins/mod_watchregistrations.lua	Fri Mar 12 18:41:05 2010 +0100
+++ b/plugins/mod_watchregistrations.lua	Wed May 05 11:29:10 2010 +0100
@@ -1,6 +1,6 @@
 -- Prosody IM
--- Copyright (C) 2008-2009 Matthew Wild
--- Copyright (C) 2008-2009 Waqas Hussain
+-- 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.
--- a/plugins/mod_welcome.lua	Fri Mar 12 18:41:05 2010 +0100
+++ b/plugins/mod_welcome.lua	Wed May 05 11:29:10 2010 +0100
@@ -1,6 +1,6 @@
 -- Prosody IM
--- Copyright (C) 2008-2009 Matthew Wild
--- Copyright (C) 2008-2009 Waqas Hussain
+-- 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.
--- a/plugins/muc/mod_muc.lua	Fri Mar 12 18:41:05 2010 +0100
+++ b/plugins/muc/mod_muc.lua	Wed May 05 11:29:10 2010 +0100
@@ -1,6 +1,6 @@
 -- Prosody IM
--- Copyright (C) 2008-2009 Matthew Wild
--- Copyright (C) 2008-2009 Waqas Hussain
+-- 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.
--- a/plugins/muc/muc.lib.lua	Fri Mar 12 18:41:05 2010 +0100
+++ b/plugins/muc/muc.lib.lua	Wed May 05 11:29:10 2010 +0100
@@ -1,6 +1,6 @@
 -- Prosody IM
--- Copyright (C) 2008-2009 Matthew Wild
--- Copyright (C) 2008-2009 Waqas Hussain
+-- 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.
@@ -122,9 +122,13 @@
 		local history = self._data['history'];
 		if not history then history = {}; self._data['history'] = history; end
 		stanza = st.clone(stanza);
-		stanza:tag("delay", {xmlns = "urn:xmpp:delay", from = muc_domain, stamp = datetime.datetime()}):up(); -- XEP-0203
+		stanza.attr.to = "";
+		local stamp = datetime.datetime();
+		local chars = #tostring(stanza);
+		stanza:tag("delay", {xmlns = "urn:xmpp:delay", from = muc_domain, stamp = stamp}):up(); -- XEP-0203
 		stanza:tag("x", {xmlns = "jabber:x:delay", from = muc_domain, stamp = datetime.legacy()}):up(); -- XEP-0091 (deprecated)
-		t_insert(history, st.preserialize(stanza));
+		local entry = { stanza = stanza, stamp = stamp };
+		t_insert(history, entry);
 		while #history > history_length do t_remove(history, 1) end
 	end
 end
@@ -151,12 +155,46 @@
 		end
 	end
 end
-function room_mt:send_history(to)
+function room_mt:send_history(to, stanza)
 	local history = self._data['history']; -- send discussion history
 	if history then
-		for _, msg in ipairs(history) do
-			msg = st.deserialize(msg);
-			msg.attr.to=to;
+		local x_tag = stanza and stanza:get_child("x", "http://jabber.org/protocol/muc");
+		local history_tag = x_tag and x_tag:get_child("history", "http://jabber.org/protocol/muc");
+		
+		local maxchars = history_tag and tonumber(history_tag.attr.maxchars);
+		if maxchars then maxchars = math.floor(maxchars); end
+		
+		local maxstanzas = math.floor(history_tag and tonumber(history_tag.attr.maxstanzas) or #history);
+		if not history_tag then maxstanzas = 20; end
+
+		local seconds = history_tag and tonumber(history_tag.attr.seconds);
+		if seconds then seconds = datetime.datetime(os.time() - math.floor(seconds)); end
+
+		local since = history_tag and history_tag.attr.since;
+		if since and not since:match("^%d%d%d%d%-%d%d%-%d%dT%d%d:%d%d:%d%dZ$") then since = nil; end -- FIXME timezone support
+		if seconds and (not since or since < seconds) then since = seconds; end
+
+		local n = 0;
+		local charcount = 0;
+		local stanzacount = 0;
+		
+		for i=#history,1,-1 do
+			local entry = history[i];
+			if maxchars then
+				if not entry.chars then
+					entry.stanza.attr.to = "";
+					entry.chars = #tostring(entry.stanza);
+				end
+				charcount = charcount + entry.chars + #to;
+				if charcount > maxchars then break; end
+			end
+			if since and since > entry.stamp then break; end
+			if n + 1 > maxstanzas then break; end
+			n = n + 1;
+		end
+		for i=#history-n+1,#history do
+			local msg = history[i].stanza;
+			msg.attr.to = to;
 			self:_route_stanza(msg);
 		end
 	end
@@ -319,7 +357,7 @@
 								:tag("item", {affiliation=affiliation or "none", role=role or "none"}):up()
 								:tag("status", {code='110'}));
 						end
-						self:send_history(from);
+						self:send_history(from, stanza);
 					else -- banned
 						local reply = st.error_reply(stanza, "auth", "forbidden"):up();
 						reply.tags[1].attr.code = "403";
--- a/prosody	Fri Mar 12 18:41:05 2010 +0100
+++ b/prosody	Wed May 05 11:29:10 2010 +0100
@@ -1,7 +1,7 @@
 #!/usr/bin/env lua
 -- Prosody IM
--- Copyright (C) 2008-2009 Matthew Wild
--- Copyright (C) 2008-2009 Waqas Hussain
+-- 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.
@@ -29,6 +29,10 @@
 	end
 end
 
+-- Global 'prosody' object
+prosody = { events = require "util.events".new(); };
+local prosody = prosody;
+
 -- Load the config-parsing module
 config = require "core.configmanager"
 
@@ -120,15 +124,34 @@
 	end
 end
 
+function set_function_metatable()
+	local mt = {};
+	function mt.__index(f, upvalue)
+		local i, name, value = 0;
+		repeat
+			i = i + 1;
+			name, value = debug.getupvalue(f, i);
+		until name == upvalue or name == nil;
+		return value;
+	end
+	function mt.__newindex(f, upvalue, value)
+		local i, name = 0;
+		repeat
+			i = i + 1;
+			name = debug.getupvalue(f, i);
+		until name == upvalue or name == nil;
+		if name then
+			debug.setupvalue(f, i, value);
+		end
+	end
+	debug.setmetatable(function() end, mt);
+end
+
 function init_global_state()
 	bare_sessions = {};
 	full_sessions = {};
 	hosts = {};
 
-	-- Global 'prosody' object
-	prosody = {};
-	local prosody = prosody;
-	
 	prosody.bare_sessions = bare_sessions;
 	prosody.full_sessions = full_sessions;
 	prosody.hosts = hosts;
@@ -138,8 +161,6 @@
 	
 	prosody.arg = _G.arg;
 
-	prosody.events = require "util.events".new();
-	
 	prosody.platform = "unknown";
 	if os.getenv("WINDIR") then
 		prosody.platform = "windows";
@@ -170,7 +191,6 @@
 	-- Function to reopen logfiles
 	function prosody.reopen_logfiles()
 		log("info", "Re-opening log files");
-		eventmanager.fire_event("reopen-log-files"); -- Handled by appropriate log sinks
 		prosody.events.fire_event("reopen-log-files");
 	end
 
@@ -263,7 +283,6 @@
 	require "util.import"
 	require "core.xmlhandlers"
 	require "core.rostermanager"
-	require "core.eventmanager"
 	require "core.hostmanager"
 	require "core.modulemanager"
 	require "core.usermanager"
@@ -307,7 +326,6 @@
 function prepare_to_start()
 	log("info", "Prosody is using the %s backend for connection handling", server.get_backend());
 	-- Signal to modules that we are ready to start
-	eventmanager.fire_event("server-starting");
 	prosody.events.fire_event("server-starting");
 
 	-- start listening on sockets
@@ -415,6 +433,7 @@
 init_logging();
 check_dependencies();
 sandbox_require();
+set_function_metatable();
 load_libraries();
 init_global_state();
 read_version();
@@ -424,14 +443,12 @@
 init_global_protection();
 prepare_to_start();
 
-eventmanager.fire_event("server-started");
 prosody.events.fire_event("server-started");
 
 loop();
 
 log("info", "Shutting down...");
 cleanup();
-eventmanager.fire_event("server-stopped");
 prosody.events.fire_event("server-stopped");
 log("info", "Shutdown complete");
 
--- a/prosody.cfg.lua.dist	Fri Mar 12 18:41:05 2010 +0100
+++ b/prosody.cfg.lua.dist	Wed May 05 11:29:10 2010 +0100
@@ -1,138 +1,127 @@
 -- Prosody Example Configuration File
---
--- If it wasn't already obvious, -- starts a comment, and all
--- text after it on a line is ignored by Prosody.
---
--- The config is split into sections, a global section, and one
--- for each defined host that we serve. You can add as many host
--- sections as you like.
---
--- Lists are written { "like", "this", "one" }
--- Lists can also be of { 1, 2, 3 } numbers, and other things.
--- Either commas, or semi-colons; may be used
--- as seperators.
+-- 
+-- Information on configuring Prosody can be found on our
+-- website at http://prosody.im/doc/configure
+-- 
+-- Tip: You can check that the syntax of this file is correct
+-- when you have finished by running: luac -p prosody.cfg.lua
+-- If there are any errors, it will let you know what and where
+-- they are, otherwise it will keep quiet.
 --
--- A table is a list of values, except each value has a name. An
--- example table would be:
---
--- ssl = { key = "keyfile.key", certificate = "certificate.cert" }
---
--- Whitespace (that is tabs, spaces, line breaks) is mostly
--- insignificant, so
--- can
--- be placed anywhere that      you deem fitting.
---
--- Tip: You can check that the syntax of this file is correct when you
--- have finished by running: luac -p prosody.cfg.lua
--- If there are any errors, it will let you know what and where they
--- are, otherwise it will keep quiet.
---
--- The only thing left to do is rename this file to remove the .dist
--- ending, and fill in the
+-- The only thing left to do is rename this file to remove the .dist ending, and fill in the
 -- blanks. Good luck, and happy Jabbering!
 
--- Server-wide settings go in this section
-Host "*"
+
+---------- Server-wide settings ----------
+-- Settings in this section apply to the whole server and are the default settings
+-- for any virtual hosts
 
-    -- This is a (by default, empty) list of accounts that are admins for the
-    -- server. Note that you must create the accounts separately (see
-    -- http://prosody.im/doc/creating_accounts)
-    -- Example: admins = { "user1@example.com", "user2@example.net" }
-    admins = { }
+-- This is a (by default, empty) list of accounts that are admins
+-- for the server. Note that you must create the accounts separately
+-- (see http://prosody.im/doc/creating_accounts for info)
+-- Example: admins = { "user1@example.com", "user2@example.net" }
+admins = { }
+
+-- Enable use of libevent for better performance under high load
+-- For more information see: http://prosody.im/doc/libevent
+--use_libevent = true;
+
+-- This is the list of modules Prosody will load on startup.
+-- It looks for mod_modulename.lua in the plugins folder, so make sure that exists too.
+-- Documentation on modules can be found at: http://prosody.im/doc/modules
+modules_enabled = {
 
-    -- Enable use of libevent for better performance under high load
-    -- For more information see: http://prosody.im/doc/libevent
-    --use_libevent = true;
-    
-    -- This is the list of modules Prosody will load on startup. It looks for
-    -- mod_modulename.lua in the plugins folder, so make sure that exists too.
-    -- Documentation on modules can be found at: http://prosody.im/doc/modules
-    modules_enabled = {
-        -- Generally required
-        "roster";   -- Allow users to have a roster. Recommended ;)
-        "saslauth"; -- Authentication for clients and servers. Recommended if
-                    -- you want to log in.
-        "dialback"; -- s2s dialback support
-        "disco";    -- Service discovery
-        "posix";    -- POSIX functionality, daemonizes, enables syslog, etc.
+	-- Generally required
+		"roster"; -- Allow users to have a roster. Recommended ;)
+		"saslauth"; -- Authentication for clients and servers. Recommended if you want to log in.
+		"tls"; -- Add support for secure TLS on c2s/s2s connections
+		"dialback"; -- s2s dialback support
+		"disco"; -- Service discovery
+
+	-- Not essential, but recommended
+		"private"; -- Private XML storage (for room bookmarks, etc.)
+		"vcard"; -- Allow users to set vCards
+		--"privacy"; -- Support privacy lists
+		--"compression"; -- Stream compression
 
-        -- Not essential, but recommended
-        "private";       -- Private XML storage (for room bookmarks, etc.)
-        "vcard";         -- Allow users to set vCards
-        "privacy";       -- Support privacy lists
-        "tls";           -- Support for secure TLS on c2s/s2s connections
-        --"compression"; -- Stream compression for client-to-server streams
+	-- Nice to have
+		"legacyauth"; -- Legacy authentication. Only used by some old clients and bots.
+		"version"; -- Replies to server version requests
+		"uptime"; -- Report how long server has been running
+		"time"; -- Let others know the time here on this server
+		"ping"; -- Replies to XMPP pings with pongs
+		"pep"; -- Enables users to publish their mood, activity, playing music and more
+		"register"; -- Allow users to register on this server using a client and change passwords
 
-        -- Nice to have
-        "legacyauth"; -- Legacy authentication. Only used by some old
-                      -- clients and bots.
-        "version";    -- Replies to server version requests
-        "uptime";     -- Report how long server has been running
-        "time";       -- Let others know the time here on this server
-        "ping";       -- Replies to XMPP pings with pongs
-        "pep";        -- Enables users to publish their mood, activity, playing
-                      -- music and more
-        "register";   -- Allow users to register on this server using a client
-                      -- and change passwords
+	-- Other specific functionality
+		--"posix"; -- POSIX functionality, sends server to background, enables syslog, etc.
+		--"console"; -- Opens admin telnet interface on localhost port 5582
+		--"bosh"; -- Enable BOSH clients, aka "Jabber over HTTP"
+		--"httpserver"; -- Serve static files from a directory over HTTP
+		--"groups"; -- Shared roster support
+		--"announce"; -- Send announcement to all online users
+		--"welcome"; -- Welcome users who register accounts
+		--"watchregistrations"; -- Alert admins of registrations
+};
 
-        -- Other specific functionality
-        --"console";            -- telnet to port 5582
-                                -- (needs console_enabled = true)
-        --"bosh";               -- Enable BOSH clients, aka "Jabber over HTTP"
-        --"httpserver";         -- Serve static files from a directory over
-                                -- HTTP
-        --"groups";             -- Shared roster support
-        --"announce";           -- Send announcement to all online users
-        --"welcome";            -- Welcome users who register accounts
-        --"watchregistrations"; -- Alert admins of registrations
-    }
+-- These modules are auto-loaded, should you
+-- for (for some mad reason) want to disable
+-- them then uncomment them below
+modules_disabled = {
+	-- "presence";
+	-- "message";
+	-- "iq";
+};
 
-    -- These modules are auto-loaded, should you for (for some mad
-    -- reason) want to disable them then uncomment them below.
-    modules_disabled = {
-        --"presence";
-        --"message";
-        --"iq";
-    }
+-- Disable account creation by default, for security
+-- For more information see http://prosody.im/doc/creating_accounts
+allow_registration = false;
+	
+-- These are the SSL/TLS-related settings. If you don't want
+-- to use SSL/TLS, you may comment or remove this
+ssl = {
+	key = "certs/localhost.key";
+	certificate = "certs/localhost.cert";
+}
 
-    -- Disable account creation by default, for security
-    -- For more information see http://prosody.im/doc/creating_accounts
-    allow_registration = false;
+-- Require encryption on client/server connections?
+--c2s_require_encryption = false
+--s2s_require_encryption = false
 
-    --These are the SSL/TLS-related settings.
-    --ssl = {
-    --    key = "certs/localhost.key";
-    --    certificate = "certs/localhost.cert";
-    --}
+-- Logging configuration
+-- For advanced logging see http://prosody.im/doc/logging
+log = "prosody.log";
+debug = false; -- Log debug messages?
 
-    -- Require encryption on client/server connections?
-    --c2s_require_encryption = false
-    --s2s_require_encryption = false
+----------- Virtual hosts -----------
+-- You need to add a VirtualHost entry for each domain you wish Prosody to serve.
+-- Settings under each VirtualHost entry apply *only* to that host.
 
-    -- Logging configuration
-    -- For advanced logging see http://prosody.im/doc/logging
-    log = "prosody.log";
-    debug = false; -- Log debug messages?
+VirtualHost "localhost"
+
+VirtualHost "example.com"
+	enabled = false -- Remove this line to enable this host
 
--- This allows clients to connect to localhost. No harm in it.
-Host "localhost"
-
--- Section for example.com
--- (replace example.com with your domain name)
-Host "example.com"
-    enabled = false -- This will disable the host, preserving the config, but
-                    -- denying connections (remove to enable!)
+	-- Assign this host a certificate for TLS, otherwise it would use the one
+	-- set in the global section (if any).
+	-- Note that old-style SSL on port 5223 only supports one certificate, and will always
+	-- use the global one.
+	ssl = { 
+		key = "certs/example.com.key";
+		certificate = "certs/example.com.crt";
+	}
 
-    -- Assign this host a certificate for TLS, otherwise it would use the one
-    -- set in the global section (if any). Note that old-style SSL on port 5223
-    -- only supports one certificate, and will always use the global one.
-    --ssl = {
-    --    key = "certs/example.com.key";
-    --    certificate = "certs/example.com.crt";
-    --}
+------ Components ------
+-- You can specify components to add hosts that provide special services,
+-- like multi-user conferences, and transports.
+-- For more information on components, see http://prosody.im/doc/components
 
--- Set up a MUC (multi-user chat) room server on conference.example.com:
+---Set up a MUC (multi-user chat) room server on conference.example.com:
 --Component "conference.example.com" "muc"
 
 -- Set up a SOCKS5 bytestream proxy for server-proxied file transfers:
 --Component "proxy.example.com" "proxy65"
+
+---Set up an external component (default component port is 5347)
+--Component "gateway.example.com"
+--	component_secret = "password"
--- a/tests/test.lua	Fri Mar 12 18:41:05 2010 +0100
+++ b/tests/test.lua	Wed May 05 11:29:10 2010 +0100
@@ -1,6 +1,6 @@
 -- Prosody IM
--- Copyright (C) 2008-2009 Matthew Wild
--- Copyright (C) 2008-2009 Waqas Hussain
+-- 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.
--- a/tests/test_core_configmanager.lua	Fri Mar 12 18:41:05 2010 +0100
+++ b/tests/test_core_configmanager.lua	Wed May 05 11:29:10 2010 +0100
@@ -1,6 +1,6 @@
 -- Prosody IM
--- Copyright (C) 2008-2009 Matthew Wild
--- Copyright (C) 2008-2009 Waqas Hussain
+-- 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.
--- a/tests/test_core_modulemanager.lua	Fri Mar 12 18:41:05 2010 +0100
+++ b/tests/test_core_modulemanager.lua	Wed May 05 11:29:10 2010 +0100
@@ -1,6 +1,6 @@
 -- Prosody IM
--- Copyright (C) 2008-2009 Matthew Wild
--- Copyright (C) 2008-2009 Waqas Hussain
+-- 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.
--- a/tests/test_core_s2smanager.lua	Fri Mar 12 18:41:05 2010 +0100
+++ b/tests/test_core_s2smanager.lua	Wed May 05 11:29:10 2010 +0100
@@ -1,6 +1,6 @@
 -- Prosody IM
--- Copyright (C) 2008-2009 Matthew Wild
--- Copyright (C) 2008-2009 Waqas Hussain
+-- 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.
--- a/tests/test_core_stanza_router.lua	Fri Mar 12 18:41:05 2010 +0100
+++ b/tests/test_core_stanza_router.lua	Wed May 05 11:29:10 2010 +0100
@@ -1,6 +1,6 @@
 -- Prosody IM
--- Copyright (C) 2008-2009 Matthew Wild
--- Copyright (C) 2008-2009 Waqas Hussain
+-- 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.
--- a/tests/test_sasl.lua	Fri Mar 12 18:41:05 2010 +0100
+++ b/tests/test_sasl.lua	Wed May 05 11:29:10 2010 +0100
@@ -1,6 +1,6 @@
 -- Prosody IM
--- Copyright (C) 2008-2009 Matthew Wild
--- Copyright (C) 2008-2009 Waqas Hussain
+-- 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.
--- a/tests/test_util_jid.lua	Fri Mar 12 18:41:05 2010 +0100
+++ b/tests/test_util_jid.lua	Wed May 05 11:29:10 2010 +0100
@@ -1,6 +1,6 @@
 -- Prosody IM
--- Copyright (C) 2008-2009 Matthew Wild
--- Copyright (C) 2008-2009 Waqas Hussain
+-- 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.
--- a/tests/test_util_multitable.lua	Fri Mar 12 18:41:05 2010 +0100
+++ b/tests/test_util_multitable.lua	Wed May 05 11:29:10 2010 +0100
@@ -1,6 +1,6 @@
 -- Prosody IM
--- Copyright (C) 2008-2009 Matthew Wild
--- Copyright (C) 2008-2009 Waqas Hussain
+-- 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.
--- a/tests/test_util_stanza.lua	Fri Mar 12 18:41:05 2010 +0100
+++ b/tests/test_util_stanza.lua	Wed May 05 11:29:10 2010 +0100
@@ -1,6 +1,6 @@
 -- Prosody IM
--- Copyright (C) 2008-2009 Matthew Wild
--- Copyright (C) 2008-2009 Waqas Hussain
+-- 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.
--- a/tests/util/logger.lua	Fri Mar 12 18:41:05 2010 +0100
+++ b/tests/util/logger.lua	Wed May 05 11:29:10 2010 +0100
@@ -1,6 +1,6 @@
 -- Prosody IM
--- Copyright (C) 2008-2009 Matthew Wild
--- Copyright (C) 2008-2009 Waqas Hussain
+-- 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.
--- a/tools/ejabberd2prosody.lua	Fri Mar 12 18:41:05 2010 +0100
+++ b/tools/ejabberd2prosody.lua	Wed May 05 11:29:10 2010 +0100
@@ -1,7 +1,7 @@
 #!/usr/bin/env lua
 -- Prosody IM
--- Copyright (C) 2008-2009 Matthew Wild
--- Copyright (C) 2008-2009 Waqas Hussain
+-- 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.
@@ -17,6 +17,8 @@
 
 require "erlparse";
 
+prosody = {};
+
 local serialize = require "util.serialization".serialize;
 local st = require "util.stanza";
 package.loaded["util.logger"] = {init = function() return function() end; end}
--- a/tools/ejabberdsql2prosody.lua	Fri Mar 12 18:41:05 2010 +0100
+++ b/tools/ejabberdsql2prosody.lua	Wed May 05 11:29:10 2010 +0100
@@ -1,7 +1,7 @@
 #!/usr/bin/env lua
 -- Prosody IM
--- Copyright (C) 2008-2009 Matthew Wild
--- Copyright (C) 2008-2009 Waqas Hussain
+-- 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.
--- a/tools/erlparse.lua	Fri Mar 12 18:41:05 2010 +0100
+++ b/tools/erlparse.lua	Wed May 05 11:29:10 2010 +0100
@@ -1,21 +1,27 @@
 -- Prosody IM
--- Copyright (C) 2008-2009 Matthew Wild
--- Copyright (C) 2008-2009 Waqas Hussain
+-- 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 string_byte, string_char = string.byte, string.char;
+local t_concat, t_insert = table.concat, table.insert;
+local type, tonumber, tostring = type, tonumber, tostring;
 
 local file = nil;
 local last = nil;
+local line = 1;
 local function read(expected)
 	local ch;
 	if last then
 		ch = last; last = nil;
-	else ch = file:read(1); end
-	if expected and ch ~= expected then error("expected: "..expected.."; got: "..(ch or "nil")); end
+	else
+		ch = file:read(1);
+		if ch == "\n" then line = line + 1; end
+	end
+	if expected and ch ~= expected then error("expected: "..expected.."; got: "..(ch or "nil").." on line "..line); end
 	return ch;
 end
 local function pushback(ch)
@@ -27,21 +33,21 @@
 	return last;
 end
 
-local _A, _a, _Z, _z, _0, _9, __, _at, _space = string.byte("AaZz09@_ ", 1, 9);
+local _A, _a, _Z, _z, _0, _9, __, _at, _space, _minus = string_byte("AaZz09@_ -", 1, 10);
 local function isLowerAlpha(ch)
-	ch = string.byte(ch) or 0;
+	ch = string_byte(ch) or 0;
 	return (ch >= _a and ch <= _z);
 end
 local function isNumeric(ch)
-	ch = string.byte(ch) or 0;
-	return (ch >= _0 and ch <= _9);
+	ch = string_byte(ch) or 0;
+	return (ch >= _0 and ch <= _9) or ch == _minus;
 end
 local function isAtom(ch)
-	ch = string.byte(ch) or 0;
+	ch = string_byte(ch) or 0;
 	return (ch >= _A and ch <= _Z) or (ch >= _a and ch <= _z) or (ch >= _0 and ch <= _9) or ch == __ or ch == _at;
 end
 local function isSpace(ch)
-	ch = string.byte(ch) or "x";
+	ch = string_byte(ch) or "x";
 	return ch <= _space;
 end
 
@@ -49,79 +55,85 @@
 local function readString()
 	read("\""); -- skip quote
 	local slash = nil;
-	local str = "";
+	local str = {};
 	while true do
 		local ch = read();
 		if slash then
 			slash = slash..ch;
 			if not escapes[slash] then error("Unknown escape sequence: "..slash); end
-			str = str..escapes[slash];
+			str[#str+1] = escapes[slash];
 			slash = nil;
 		elseif ch == "\"" then
 			break;
 		elseif ch == "\\" then
 			slash = ch;
 		else
-			str = str..ch;
+			str[#str+1] = ch;
 		end
 	end
-	return str;
+	return t_concat(str);
 end
 local function readAtom1()
-	local var = read();
+	local var = { read() };
 	while isAtom(peek()) do
-		var = var..read();
+		var[#var+1] = read();
 	end
-	return var;
+	return t_concat(var);
 end
 local function readAtom2()
-	local str = read("'");
+	local str = { read("'") };
 	local slash = nil;
 	while true do
 		local ch = read();
-		str = str..ch;
+		str[#str+1] = ch;
 		if ch == "'" and not slash then break; end
 	end
-	return str;
+	return t_concat(str);
 end
 local function readNumber()
-	local num = read();
+	local num = { read() };
 	while isNumeric(peek()) do
-		num = num..read();
+		num[#num+1] = read();
 	end
-	return tonumber(num);
+	return tonumber(t_concat(num));
 end
 local readItem = nil;
 local function readTuple()
 	local t = {};
-	local s = ""; -- string representation
+	local s = {}; -- string representation
 	read(); -- read {, or [, or <
 	while true do
 		local item = readItem();
 		if not item then break; end
-		if type(item) ~= type(0) or item > 255 then
+		if type(item) ~= "number" or item > 255 then
 			s = nil;
 		elseif s then
-			s = s..string.char(item);
+			s[#s+1] = string_char(item);
 		end
-		table.insert(t, item);
+		t_insert(t, item);
 	end
 	read(); -- read }, or ], or >
-	if s and s ~= "" then
-		return s
+	if s and #s > 0  then
+		return t_concat(s)
 	else
 		return t
 	end;
 end
 local function readBinary()
 	read("<"); -- read <
+	-- Discard PIDs
+	if isNumeric(peek()) then
+		while peek() ~= ">" do read(); end
+		read(">");
+		return {};
+	end
 	local t = readTuple();
 	read(">") -- read >
 	local ch = peek();
-	if type(t) == type("") then
+	if type(t) == "string" then
 		-- binary is a list of integers
 		return t;
-	elseif type(t) == type({}) then
+	elseif type(t) == "table" then
 		if t[1] then
 			-- binary contains string
 			return t[1];
--- a/util-src/encodings.c	Fri Mar 12 18:41:05 2010 +0100
+++ b/util-src/encodings.c	Wed May 05 11:29:10 2010 +0100
@@ -1,6 +1,6 @@
-/* Prosody IM v0.4
--- Copyright (C) 2008-2009 Matthew Wild
--- Copyright (C) 2008-2009 Waqas Hussain
+/* 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.
--- a/util-src/hashes.c	Fri Mar 12 18:41:05 2010 +0100
+++ b/util-src/hashes.c	Wed May 05 11:29:10 2010 +0100
@@ -1,6 +1,6 @@
-/* Prosody IM v0.4
--- Copyright (C) 2008 Matthew Wild
--- Copyright (C) 2008 Waqas Hussain
+/* Prosody IM
+-- Copyright (C) 2009-2010 Matthew Wild
+-- Copyright (C) 2009-2010 Waqas Hussain
 -- 
 -- This project is MIT/X11 licensed. Please see the
 -- COPYING file in the source package for more information.
--- a/util-src/pposix.c	Fri Mar 12 18:41:05 2010 +0100
+++ b/util-src/pposix.c	Wed May 05 11:29:10 2010 +0100
@@ -1,6 +1,6 @@
-/* Prosody IM v0.4
--- Copyright (C) 2008-2009 Matthew Wild
--- Copyright (C) 2008-2009 Waqas Hussain
+/* Prosody IM
+-- Copyright (C) 2008-2010 Matthew Wild
+-- Copyright (C) 2008-2010 Waqas Hussain
 -- Copyright (C) 2009 Tobias Markmann
 --
 -- This project is MIT/X11 licensed. Please see the
--- a/util-src/windows.c	Fri Mar 12 18:41:05 2010 +0100
+++ b/util-src/windows.c	Wed May 05 11:29:10 2010 +0100
@@ -1,45 +1,58 @@
-
-#include <stdio.h>
-#include <windows.h>
-#include <windns.h>
-
-#include "lua.h"
-#include "lauxlib.h"
-
-static int Lget_nameservers(lua_State *L) {
-	char stack_buffer[1024]; // stack allocated buffer
-	IP4_ARRAY* ips = (IP4_ARRAY*) stack_buffer;
-	DWORD len = sizeof(stack_buffer);
-	DNS_STATUS status;
-
-	status = DnsQueryConfig(DnsConfigDnsServerList, FALSE, NULL, NULL, ips, &len);
-	if (status == 0) {
-		DWORD i;
-		lua_createtable(L, ips->AddrCount, 0);
-		for (i = 0; i < ips->AddrCount; i++) {
-			DWORD ip = ips->AddrArray[i];
-			char ip_str[16] = "";
-			sprintf_s(ip_str, sizeof(ip_str), "%d.%d.%d.%d", (ip >> 0) & 255, (ip >> 8) & 255, (ip >> 16) & 255, (ip >> 24) & 255);
-			lua_pushstring(L, ip_str);
-			lua_rawseti(L, -2, i+1);
-		}
-		return 1;
-	} else {
-		luaL_error(L, "DnsQueryConfig returned %d", status);
-		return 0; // unreachable, but prevents a compiler warning
-	}
-}
-
-static const luaL_Reg Reg[] =
-{
-	{ "get_nameservers",	Lget_nameservers	},
-	{ NULL,		NULL	}
-};
-
-LUALIB_API int luaopen_util_windows(lua_State *L) {
-	luaL_register(L, "windows", Reg);
-	lua_pushliteral(L, "version");			/** version */
-	lua_pushliteral(L, "-3.14");
-	lua_settable(L,-3);
-	return 1;
-}
+/* 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.
+--
+*/
+
+/*
+* windows.c
+* Windows support functions for Lua
+*/
+
+#include <stdio.h>
+#include <windows.h>
+#include <windns.h>
+
+#include "lua.h"
+#include "lauxlib.h"
+
+static int Lget_nameservers(lua_State *L) {
+	char stack_buffer[1024]; // stack allocated buffer
+	IP4_ARRAY* ips = (IP4_ARRAY*) stack_buffer;
+	DWORD len = sizeof(stack_buffer);
+	DNS_STATUS status;
+
+	status = DnsQueryConfig(DnsConfigDnsServerList, FALSE, NULL, NULL, ips, &len);
+	if (status == 0) {
+		DWORD i;
+		lua_createtable(L, ips->AddrCount, 0);
+		for (i = 0; i < ips->AddrCount; i++) {
+			DWORD ip = ips->AddrArray[i];
+			char ip_str[16] = "";
+			sprintf_s(ip_str, sizeof(ip_str), "%d.%d.%d.%d", (ip >> 0) & 255, (ip >> 8) & 255, (ip >> 16) & 255, (ip >> 24) & 255);
+			lua_pushstring(L, ip_str);
+			lua_rawseti(L, -2, i+1);
+		}
+		return 1;
+	} else {
+		luaL_error(L, "DnsQueryConfig returned %d", status);
+		return 0; // unreachable, but prevents a compiler warning
+	}
+}
+
+static const luaL_Reg Reg[] =
+{
+	{ "get_nameservers",	Lget_nameservers	},
+	{ NULL,		NULL	}
+};
+
+LUALIB_API int luaopen_util_windows(lua_State *L) {
+	luaL_register(L, "windows", Reg);
+	lua_pushliteral(L, "version");			/** version */
+	lua_pushliteral(L, "-3.14");
+	lua_settable(L,-3);
+	return 1;
+}
--- a/util/array.lua	Fri Mar 12 18:41:05 2010 +0100
+++ b/util/array.lua	Wed May 05 11:29:10 2010 +0100
@@ -1,6 +1,6 @@
 -- Prosody IM
--- Copyright (C) 2008-2009 Matthew Wild
--- Copyright (C) 2008-2009 Waqas Hussain
+-- 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.
--- a/util/broadcast.lua	Fri Mar 12 18:41:05 2010 +0100
+++ b/util/broadcast.lua	Wed May 05 11:29:10 2010 +0100
@@ -1,6 +1,6 @@
 -- Prosody IM
--- Copyright (C) 2008-2009 Matthew Wild
--- Copyright (C) 2008-2009 Waqas Hussain
+-- 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.
--- a/util/dataforms.lua	Fri Mar 12 18:41:05 2010 +0100
+++ b/util/dataforms.lua	Wed May 05 11:29:10 2010 +0100
@@ -1,6 +1,6 @@
 -- Prosody IM
--- Copyright (C) 2008-2009 Matthew Wild
--- Copyright (C) 2008-2009 Waqas Hussain
+-- 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.
--- a/util/datamanager.lua	Fri Mar 12 18:41:05 2010 +0100
+++ b/util/datamanager.lua	Wed May 05 11:29:10 2010 +0100
@@ -1,6 +1,6 @@
 -- Prosody IM
--- Copyright (C) 2008-2009 Matthew Wild
--- Copyright (C) 2008-2009 Waqas Hussain
+-- 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.
--- a/util/datetime.lua	Fri Mar 12 18:41:05 2010 +0100
+++ b/util/datetime.lua	Wed May 05 11:29:10 2010 +0100
@@ -1,6 +1,6 @@
 -- Prosody IM
--- Copyright (C) 2008-2009 Matthew Wild
--- Copyright (C) 2008-2009 Waqas Hussain
+-- 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.
--- a/util/dependencies.lua	Fri Mar 12 18:41:05 2010 +0100
+++ b/util/dependencies.lua	Wed May 05 11:29:10 2010 +0100
@@ -1,6 +1,6 @@
 -- Prosody IM
--- Copyright (C) 2008-2009 Matthew Wild
--- Copyright (C) 2008-2009 Waqas Hussain
+-- 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.
--- a/util/events.lua	Fri Mar 12 18:41:05 2010 +0100
+++ b/util/events.lua	Wed May 05 11:29:10 2010 +0100
@@ -1,6 +1,6 @@
 -- Prosody IM
--- Copyright (C) 2008-2009 Matthew Wild
--- Copyright (C) 2008-2009 Waqas Hussain
+-- 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.
--- a/util/helpers.lua	Fri Mar 12 18:41:05 2010 +0100
+++ b/util/helpers.lua	Wed May 05 11:29:10 2010 +0100
@@ -1,6 +1,6 @@
 -- Prosody IM
--- Copyright (C) 2008-2009 Matthew Wild
--- Copyright (C) 2008-2009 Waqas Hussain
+-- 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.
--- a/util/hmac.lua	Fri Mar 12 18:41:05 2010 +0100
+++ b/util/hmac.lua	Wed May 05 11:29:10 2010 +0100
@@ -1,6 +1,6 @@
 -- Prosody IM
--- Copyright (C) 2008-2009 Matthew Wild
--- Copyright (C) 2008-2009 Waqas Hussain
+-- 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.
--- a/util/import.lua	Fri Mar 12 18:41:05 2010 +0100
+++ b/util/import.lua	Wed May 05 11:29:10 2010 +0100
@@ -1,6 +1,6 @@
 -- Prosody IM
--- Copyright (C) 2008-2009 Matthew Wild
--- Copyright (C) 2008-2009 Waqas Hussain
+-- 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.
--- a/util/iterators.lua	Fri Mar 12 18:41:05 2010 +0100
+++ b/util/iterators.lua	Wed May 05 11:29:10 2010 +0100
@@ -1,6 +1,6 @@
 -- Prosody IM
--- Copyright (C) 2008-2009 Matthew Wild
--- Copyright (C) 2008-2009 Waqas Hussain
+-- 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.
--- a/util/jid.lua	Fri Mar 12 18:41:05 2010 +0100
+++ b/util/jid.lua	Wed May 05 11:29:10 2010 +0100
@@ -1,6 +1,6 @@
 -- Prosody IM
--- Copyright (C) 2008-2009 Matthew Wild
--- Copyright (C) 2008-2009 Waqas Hussain
+-- 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.
--- a/util/logger.lua	Fri Mar 12 18:41:05 2010 +0100
+++ b/util/logger.lua	Wed May 05 11:29:10 2010 +0100
@@ -1,6 +1,6 @@
 -- Prosody IM
--- Copyright (C) 2008-2009 Matthew Wild
--- Copyright (C) 2008-2009 Waqas Hussain
+-- 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.
--- a/util/multitable.lua	Fri Mar 12 18:41:05 2010 +0100
+++ b/util/multitable.lua	Wed May 05 11:29:10 2010 +0100
@@ -1,6 +1,6 @@
 -- Prosody IM
--- Copyright (C) 2008-2009 Matthew Wild
--- Copyright (C) 2008-2009 Waqas Hussain
+-- 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.
--- a/util/pluginloader.lua	Fri Mar 12 18:41:05 2010 +0100
+++ b/util/pluginloader.lua	Wed May 05 11:29:10 2010 +0100
@@ -1,6 +1,6 @@
 -- Prosody IM
--- Copyright (C) 2008-2009 Matthew Wild
--- Copyright (C) 2008-2009 Waqas Hussain
+-- 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.
--- a/util/prosodyctl.lua	Fri Mar 12 18:41:05 2010 +0100
+++ b/util/prosodyctl.lua	Wed May 05 11:29:10 2010 +0100
@@ -1,6 +1,6 @@
 -- Prosody IM
--- Copyright (C) 2008-2009 Matthew Wild
--- Copyright (C) 2008-2009 Waqas Hussain
+-- 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.
--- a/util/sasl/digest-md5.lua	Fri Mar 12 18:41:05 2010 +0100
+++ b/util/sasl/digest-md5.lua	Wed May 05 11:29:10 2010 +0100
@@ -50,8 +50,6 @@
 	local function serialize(message)
 		local data = ""
 
-		if type(message) ~= "table" then error("serialize needs an argument of type table.") end
-
 		-- testing all possible values
 		if message["realm"] then data = data..[[realm="]]..message.realm..[[",]] end
 		if message["nonce"] then data = data..[[nonce="]]..message.nonce..[[",]] end
--- a/util/sasl_cyrus.lua	Fri Mar 12 18:41:05 2010 +0100
+++ b/util/sasl_cyrus.lua	Wed May 05 11:29:10 2010 +0100
@@ -39,7 +39,7 @@
 		if st then
 			initialized = true;
 		else
-			log("error", "Failed to initialize CyrusSASL: %s", errmsg);
+			log("error", "Failed to initialize Cyrus SASL: %s", errmsg);
 		end
 	end
 end
@@ -52,11 +52,24 @@
 
 	sasl_i.realm = realm;
 	sasl_i.service_name = service_name;
-	sasl_i.cyrus = cyrussasl.server_new(service_name, nil, nil, nil, nil)
-	if sasl_i.cyrus == 0 then
-		log("error", "got NULL return value from server_new")
+
+	local st, ret = pcall(cyrussasl.server_new, service_name, nil, realm, nil, nil)
+	if st then
+		sasl_i.cyrus = ret;
+	else
+		log("error", "Creating SASL server connection failed: %s", ret);
 		return nil;
 	end
+
+	if cyrussasl.set_canon_cb then
+		local c14n_cb = function (user)
+			local node = s_match(user, "^([^@]+)");
+			log("debug", "Canonicalizing username %s to %s", user, node)
+			return node
+		end
+		cyrussasl.set_canon_cb(sasl_i.cyrus, c14n_cb);
+	end
+
 	cyrussasl.setssf(sasl_i.cyrus, 0, 0xffffffff)
 	local s = setmetatable(sasl_i, method);
 	return s;
@@ -69,7 +82,7 @@
 
 -- set the forbidden mechanisms
 function method:forbidden( restrict )
-	log("debug", "Called method:forbidden. NOT IMPLEMENTED.")
+	log("warn", "Called method:forbidden. NOT IMPLEMENTED.")
 	return {}
 end
 
@@ -110,16 +123,12 @@
 	   return "challenge", data
 	elseif (err == -4) then -- SASL_NOMECH
 	   log("debug", "SASL mechanism not available from remote end")
-	   return "failure", 
-	     "undefined-condition",
-	     "SASL mechanism not available"
+	   return "failure", "invalid-mechanism", "SASL mechanism not available"
 	elseif (err == -13) then -- SASL_BADAUTH
 	   return "failure", "not-authorized", cyrussasl.get_message( self.cyrus )
 	else
 	   log("debug", "Got SASL error condition %d", err)
-	   return "failure", 
-	     "undefined-condition",
-	     cyrussasl.get_message( self.cyrus )
+	   return "failure", "undefined-condition", cyrussasl.get_message( self.cyrus )
 	end
 end
 
--- a/util/serialization.lua	Fri Mar 12 18:41:05 2010 +0100
+++ b/util/serialization.lua	Wed May 05 11:29:10 2010 +0100
@@ -1,6 +1,6 @@
 -- Prosody IM
--- Copyright (C) 2008-2009 Matthew Wild
--- Copyright (C) 2008-2009 Waqas Hussain
+-- 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.
--- a/util/set.lua	Fri Mar 12 18:41:05 2010 +0100
+++ b/util/set.lua	Wed May 05 11:29:10 2010 +0100
@@ -1,6 +1,6 @@
 -- Prosody IM
--- Copyright (C) 2008-2009 Matthew Wild
--- Copyright (C) 2008-2009 Waqas Hussain
+-- 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.
--- a/util/stanza.lua	Fri Mar 12 18:41:05 2010 +0100
+++ b/util/stanza.lua	Wed May 05 11:29:10 2010 +0100
@@ -1,6 +1,6 @@
 -- Prosody IM
--- Copyright (C) 2008-2009 Matthew Wild
--- Copyright (C) 2008-2009 Waqas Hussain
+-- 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.
--- a/util/termcolours.lua	Fri Mar 12 18:41:05 2010 +0100
+++ b/util/termcolours.lua	Wed May 05 11:29:10 2010 +0100
@@ -1,6 +1,6 @@
 -- Prosody IM
--- Copyright (C) 2008-2009 Matthew Wild
--- Copyright (C) 2008-2009 Waqas Hussain
+-- 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.
--- a/util/timer.lua	Fri Mar 12 18:41:05 2010 +0100
+++ b/util/timer.lua	Wed May 05 11:29:10 2010 +0100
@@ -1,6 +1,6 @@
 -- Prosody IM
--- Copyright (C) 2008-2009 Matthew Wild
--- Copyright (C) 2008-2009 Waqas Hussain
+-- 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.
@@ -55,11 +55,12 @@
 else
 	local EVENT_LEAVE = (event.core and event.core.LEAVE) or -1;
 	function _add_task(delay, func)
-		event_base:addevent(nil, 0, function ()
+		local event_handle;
+		event_handle = event_base:addevent(nil, 0, function ()
 			local ret = func();
 			if ret then
 				return 0, ret;
-			else
+			elseif event_handle then
 				return EVENT_LEAVE;
 			end
 		end
--- a/util/uuid.lua	Fri Mar 12 18:41:05 2010 +0100
+++ b/util/uuid.lua	Wed May 05 11:29:10 2010 +0100
@@ -1,6 +1,6 @@
 -- Prosody IM
--- Copyright (C) 2008-2009 Matthew Wild
--- Copyright (C) 2008-2009 Waqas Hussain
+-- 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.
@@ -32,7 +32,7 @@
 	buffer = new_random(buffer..x);
 end
 local function get_nibbles(n)
-	if #buffer < n then seed(uniq_time()); end
+	if #buffer < n then _seed(uniq_time()); end
 	local r = buffer:sub(0, n);
 	buffer = buffer:sub(n+1);
 	return r;
--- a/util/xmlrpc.lua	Fri Mar 12 18:41:05 2010 +0100
+++ b/util/xmlrpc.lua	Wed May 05 11:29:10 2010 +0100
@@ -1,6 +1,6 @@
 -- Prosody IM
--- Copyright (C) 2008-2009 Matthew Wild
--- Copyright (C) 2008-2009 Waqas Hussain
+-- 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.
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/util/xmppstream.lua	Wed May 05 11:29:10 2010 +0100
@@ -0,0 +1,150 @@
+-- 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 lxp = require "lxp";
+local st = require "util.stanza";
+
+local tostring = tostring;
+local t_insert = table.insert;
+local t_concat = table.concat;
+
+local default_log = require "util.logger".init("xmlhandlers");
+
+local error = error;
+
+module "xmppstream"
+
+local new_parser = lxp.new;
+
+local ns_prefixes = {
+	["http://www.w3.org/XML/1998/namespace"] = "xml";
+};
+
+local xmlns_streams = "http://etherx.jabber.org/streams";
+
+local ns_separator = "\1";
+local ns_pattern = "^([^"..ns_separator.."]*)"..ns_separator.."?(.*)$";
+
+function new_sax_handlers(session, stream_callbacks)
+	local chardata = {};
+	local xml_handlers = {};
+	local log = session.log or default_log;
+	
+	local cb_streamopened = stream_callbacks.streamopened;
+	local cb_streamclosed = stream_callbacks.streamclosed;
+	local cb_error = stream_callbacks.error or function(session, e) error("XML stream error: "..tostring(e)); end;
+	local cb_handlestanza = stream_callbacks.handlestanza;
+	
+	local stream_ns = stream_callbacks.stream_ns or xmlns_streams;
+	local stream_tag = stream_ns..ns_separator..(stream_callbacks.stream_tag or "stream");
+	local stream_error_tag = stream_ns..ns_separator..(stream_callbacks.error_tag or "error");
+	
+	local stream_default_ns = stream_callbacks.default_ns;
+	
+	local stanza;
+	function xml_handlers:StartElement(tagname, attr)
+		if stanza and #chardata > 0 then
+			-- We have some character data in the buffer
+			stanza:text(t_concat(chardata));
+			chardata = {};
+		end
+		local curr_ns,name = tagname:match(ns_pattern);
+		if name == "" then
+			curr_ns, name = "", curr_ns;
+		end
+
+		if curr_ns ~= stream_default_ns then
+			attr.xmlns = curr_ns;
+		end
+		
+		-- FIXME !!!!!
+		for i=1,#attr do
+			local k = attr[i];
+			attr[i] = nil;
+			local ns, nm = k:match(ns_pattern);
+			if nm ~= "" then
+				ns = ns_prefixes[ns]; 
+				if ns then 
+					attr[ns..":"..nm] = attr[k];
+					attr[k] = nil;
+				end
+			end
+		end
+		
+		if not stanza then --if we are not currently inside a stanza
+			if session.notopen then
+				if tagname == stream_tag then
+					if cb_streamopened then
+						cb_streamopened(session, attr);
+					end
+				else
+					-- Garbage before stream?
+					cb_error(session, "no-stream");
+				end
+				return;
+			end
+			if curr_ns == "jabber:client" and name ~= "iq" and name ~= "presence" and name ~= "message" then
+				cb_error(session, "invalid-top-level-element");
+			end
+			
+			stanza = st.stanza(name, attr);
+		else -- we are inside a stanza, so add a tag
+			attr.xmlns = nil;
+			if curr_ns ~= stream_default_ns then
+				attr.xmlns = curr_ns;
+			end
+			stanza:tag(name, attr);
+		end
+	end
+	function xml_handlers:CharacterData(data)
+		if stanza then
+			t_insert(chardata, data);
+		end
+	end
+	function xml_handlers:EndElement(tagname)
+		if stanza then
+			if #chardata > 0 then
+				-- We have some character data in the buffer
+				stanza:text(t_concat(chardata));
+				chardata = {};
+			end
+			-- Complete stanza
+			if #stanza.last_add == 0 then
+				if tagname ~= stream_error_tag then
+					cb_handlestanza(session, stanza);
+				else
+					cb_error(session, "stream-error", stanza);
+				end
+				stanza = nil;
+			else
+				stanza:up();
+			end
+		else
+			if tagname == stream_tag then
+				if cb_streamclosed then
+					cb_streamclosed(session);
+				end
+			else
+				local curr_ns,name = tagname:match(ns_pattern);
+				if name == "" then
+					curr_ns, name = "", curr_ns;
+				end
+				cb_error(session, "parse-error", "unexpected-element-close", name);
+			end
+			stanza, chardata = nil, {};
+		end
+	end
+	return xml_handlers;
+end
+
+function new(session, stream_callbacks)
+	return new_parser(new_sax_handlers(session, stream_callbacks), ns_separator);
+end
+
+return _M;
--- a/util/ztact.lua	Fri Mar 12 18:41:05 2010 +0100
+++ b/util/ztact.lua	Wed May 05 11:29:10 2010 +0100
@@ -114,7 +114,7 @@
 
 function tostring_r (d, indent, tab0)    -- - - - - - - - - - - - -  tostring_r
 
-  tab1 = tab0 or {}
+  local tab1 = tab0 or {}
   local rep = string.rep ('  ', indent or 0)
   if type (d) == 'table' then
     for k,v in pairs (d) do
@@ -210,7 +210,7 @@
 
 
 local function test_queue ()
-  t = {}
+  local t = {}
   enqueue (t, 1)
   enqueue (t, 2)
   enqueue (t, 3)