Changeset

5024:d25e1b9332cc

Merge with Florob
author Matthew Wild <mwild1@gmail.com>
date Sat, 28 Jul 2012 01:14:31 +0100
parents 5023:dcc8e789df36 (current diff) 5019:017e864b459d (diff)
children 5025:e68d1b85ee3a
files core/moduleapi.lua core/rostermanager.lua plugins/mod_admin_telnet.lua prosodyctl util/datamanager.lua
diffstat 50 files changed, 646 insertions(+), 433 deletions(-) [+]
line wrap: on
line diff
--- a/TODO	Sat Jun 09 02:27:44 2012 +0200
+++ b/TODO	Sat Jul 28 01:14:31 2012 +0100
@@ -5,5 +5,6 @@
 - Web interface
 
 == 1.0 ==
+- Statistics
 - Clustering
 - World domination
--- a/core/certmanager.lua	Sat Jun 09 02:27:44 2012 +0200
+++ b/core/certmanager.lua	Sat Jul 28 01:14:31 2012 +0100
@@ -11,14 +11,17 @@
 local ssl = ssl;
 local ssl_newcontext = ssl and ssl.newcontext;
 
-local setmetatable, tostring = setmetatable, tostring;
+local tostring = tostring;
 
 local prosody = prosody;
 local resolve_path = configmanager.resolve_relative_path;
 local config_path = prosody.paths.config;
 
-local luasec_major, luasec_minor = ssl._VERSION:match("^(%d+)%.(%d+)");
-local luasec_has_noticket = tonumber(luasec_major)>0 or tonumber(luasec_minor)>=4;
+local luasec_has_noticket;
+if ssl then
+	local luasec_major, luasec_minor = ssl._VERSION:match("^(%d+)%.(%d+)");
+	luasec_has_noticket = tonumber(luasec_major)>0 or tonumber(luasec_minor)>=4;
+end
 
 module "certmanager"
 
@@ -78,7 +81,7 @@
 			else
 				reason = "Reason: "..tostring(reason):lower();
 			end
-			log("error", "SSL/TLS: Failed to load %s: %s (for %s)", file, reason, host);
+			log("error", "SSL/TLS: Failed to load '%s': %s (for %s)", file, reason, host);
 		else
 			log("error", "SSL/TLS: Error initialising for %s: %s", host, err);
 		end
--- a/core/loggingmanager.lua	Sat Jun 09 02:27:44 2012 +0200
+++ b/core/loggingmanager.lua	Sat Jul 28 01:14:31 2012 +0100
@@ -7,14 +7,12 @@
 --
 
 
-local format, rep = string.format, string.rep;
-local pcall = pcall;
-local debug = debug;
-local tostring, setmetatable, rawset, pairs, ipairs, type =
-	tostring, setmetatable, rawset, pairs, ipairs, type;
+local format = string.format;
+local setmetatable, rawset, pairs, ipairs, type =
+	setmetatable, rawset, pairs, ipairs, type;
 local io_open, io_write = io.open, io.write;
 local math_max, rep = math.max, string.rep;
-local os_date, os_getenv = os.date, os.getenv;
+local os_date = os.date;
 local getstyle, setstyle = require "util.termcolours".getstyle, require "util.termcolours".setstyle;
 
 if os.getenv("__FLUSH_LOG") then
--- a/core/moduleapi.lua	Sat Jun 09 02:27:44 2012 +0200
+++ b/core/moduleapi.lua	Sat Jul 28 01:14:31 2012 +0100
@@ -14,8 +14,6 @@
 local pluginloader = require "util.pluginloader";
 local timer = require "util.timer";
 
-local multitable_new = require "util.multitable".new;
-
 local t_insert, t_remove, t_concat = table.insert, table.remove, table.concat;
 local error, setmetatable, type = error, setmetatable, type;
 local ipairs, pairs, select, unpack = ipairs, pairs, select, unpack;
@@ -338,4 +336,4 @@
 	return io.open(path, mode);
 end
 
-return api;
+return api;
\ No newline at end of file
--- a/core/portmanager.lua	Sat Jun 09 02:27:44 2012 +0200
+++ b/core/portmanager.lua	Sat Jul 28 01:14:31 2012 +0100
@@ -52,13 +52,6 @@
 		end
 	elseif err:match("permission") then
 		friendly_message = "Prosody does not have sufficient privileges to use this port";
-	elseif err == "no ssl context" then
-		if not config.get("*", "core", "ssl") then
-			friendly_message = "there is no 'ssl' config under Host \"*\" which is "
-				.."require for legacy SSL ports";
-		else
-			friendly_message = "initializing SSL support failed, see previous log entries";
-		end
 	end
 	return friendly_message;
 end
@@ -110,22 +103,28 @@
 			if #active_services:search(nil, interface, port) > 0 then
 				log("error", "Multiple services configured to listen on the same port ([%s]:%d): %s, %s", interface, port, active_services:search(nil, interface, port)[1][1].service.name or "<unnamed>", service_name or "<unnamed>");
 			else
+				local err;
 				-- Create SSL context for this service/port
 				if service_info.encryption == "ssl" then
 					local ssl_config = config.get("*", config_prefix.."ssl");
-					ssl = certmanager.create_context(service_info.name.." port "..port, "server", ssl_config and (ssl_config[port]
+					ssl, err = certmanager.create_context(service_info.name.." port "..port, "server", ssl_config and (ssl_config[port]
 						or (ssl_config.certificate and ssl_config)));
+					if not ssl then
+						log("error", "Error binding encrypted port for %s: %s", service_info.name, error_to_friendly_message(service_name, port, err) or "unknown error");
+					end
 				end
-				-- Start listening on interface+port
-				local handler, err = server.addserver(interface, port, listener, mode, ssl);
-				if not handler then
-					log("error", "Failed to open server port %d on %s, %s", port, interface, error_to_friendly_message(service_name, port, err));
-				else
-					log("debug", "Added listening service %s to [%s]:%d", service_name, interface, port);
-					active_services:add(service_name, interface, port, {
-						server = handler;
-						service = service_info;
-					});
+				if not err then
+					-- Start listening on interface+port
+					local handler, err = server.addserver(interface, port, listener, mode, ssl);
+					if not handler then
+						log("error", "Failed to open server port %d on %s, %s", port, interface, error_to_friendly_message(service_name, port, err));
+					else
+						log("debug", "Added listening service %s to [%s]:%d", service_name, interface, port);
+						active_services:add(service_name, interface, port, {
+							server = handler;
+							service = service_info;
+						});
+					end
 				end
 			end
 		end
--- a/core/rostermanager.lua	Sat Jun 09 02:27:44 2012 +0200
+++ b/core/rostermanager.lua	Sat Jul 28 01:14:31 2012 +0100
@@ -83,15 +83,15 @@
 
 function load_roster(username, host)
 	local jid = username.."@"..host;
-	log("debug", "load_roster: asked for: "..jid);
+	log("debug", "load_roster: asked for: %s", jid);
 	local user = bare_sessions[jid];
 	local roster;
 	if user then
 		roster = user.roster;
 		if roster then return roster; end
-		log("debug", "load_roster: loading for new user: "..username.."@"..host);
+		log("debug", "load_roster: loading for new user: %s@%s", username, host);
 	else -- Attempt to load roster for non-loaded user
-		log("debug", "load_roster: loading for offline user: "..username.."@"..host);
+		log("debug", "load_roster: loading for offline user: %s@%s", username, host);
 	end
 	local data, err = datamanager.load(username, host, "roster");
 	roster = data or {};
@@ -99,7 +99,7 @@
 	if not roster[false] then roster[false] = { broken = err or nil }; end
 	if roster[jid] then
 		roster[jid] = nil;
-		log("warn", "roster for "..jid.." has a self-contact");
+		log("warn", "roster for %s has a self-contact", jid);
 	end
 	if not err then
 		hosts[host].events.fire_event("roster-load", username, host, roster);
@@ -108,7 +108,7 @@
 end
 
 function save_roster(username, host, roster)
-	log("debug", "save_roster: saving roster for "..username.."@"..host);
+	log("debug", "save_roster: saving roster for %s@%s", username, host);
 	if not roster then
 		roster = hosts[host] and hosts[host].sessions[username] and hosts[host].sessions[username].roster;
 		--if not roster then
@@ -238,7 +238,7 @@
 		roster[jid] = item;
 	end
 	item.ask = "subscribe";
-	log("debug", "set_contact_pending_out: saving roster; set "..username.."@"..host..".roster["..jid.."].ask=subscribe");
+	log("debug", "set_contact_pending_out: saving roster; set %s@%s.roster[%q].ask=subscribe", username, host, jid);
 	return save_roster(username, host, roster);
 end
 function unsubscribe(username, host, jid)
--- a/core/s2smanager.lua	Sat Jun 09 02:27:44 2012 +0200
+++ b/core/s2smanager.lua	Sat Jul 28 01:14:31 2012 +0100
@@ -74,7 +74,7 @@
 	else
 		return false;
 	end
-	session.log("debug", "connection %s->%s is now authenticated for %s", session.from_host or "(unknown)", session.to_host or "(unknown)", host);
+	session.log("debug", "connection %s->%s is now authenticated for %s", session.from_host, session.to_host, host);
 	
 	mark_connected(session);
 	
@@ -87,7 +87,7 @@
 	
 	local from, to = session.from_host, session.to_host;
 	
-	session.log("info", session.direction.." s2s connection "..from.."->"..to.." complete");
+	session.log("info", "%s s2s connection %s->%s complete", session.direction, from, to);
 
 	local event_data = { session = session };
 	if session.type == "s2sout" then
@@ -105,7 +105,7 @@
 	
 	if session.direction == "outgoing" then
 		if sendq then
-			session.log("debug", "sending "..#sendq.." queued stanzas across new outgoing connection to "..session.to_host);
+			session.log("debug", "sending %d queued stanzas across new outgoing connection to %s", #sendq, session.to_host);
 			for i, data in ipairs(sendq) do
 				send(data[1]);
 				sendq[i] = nil;
@@ -133,7 +133,7 @@
 function retire_session(session, reason)
 	local log = session.log or log;
 	for k in pairs(session) do
-		if k ~= "trace" and k ~= "log" and k ~= "id" then
+		if k ~= "trace" and k ~= "log" and k ~= "id" and k ~= "conn" then
 			session[k] = nil;
 		end
 	end
--- a/core/sessionmanager.lua	Sat Jun 09 02:27:44 2012 +0200
+++ b/core/sessionmanager.lua	Sat Jul 28 01:14:31 2012 +0100
@@ -6,8 +6,8 @@
 -- COPYING file in the source package for more information.
 --
 
-local tonumber, tostring, setmetatable = tonumber, tostring, setmetatable;
-local ipairs, pairs, print, next= ipairs, pairs, print, next;
+local tostring, setmetatable = tostring, setmetatable;
+local pairs, next= pairs, next;
 
 local hosts = hosts;
 local full_sessions = full_sessions;
@@ -15,7 +15,6 @@
 
 local logger = require "util.logger";
 local log = logger.init("sessionmanager");
-local error = error;
 local rm_load_roster = require "core.rostermanager".load_roster;
 local config_get = require "core.configmanager".get;
 local resourceprep = require "util.encodings".stringprep.resourceprep;
@@ -23,12 +22,8 @@
 local uuid_generate = require "util.uuid".generate;
 
 local initialize_filters = require "util.filters".initialize;
-local fire_event = prosody.events.fire_event;
-local add_task = require "util.timer".add_task;
 local gettime = require "socket".gettime;
 
-local st = require "util.stanza";
-
 local newproxy = newproxy;
 local getmetatable = getmetatable;
 
@@ -43,7 +38,7 @@
 		getmetatable(session.trace).__gc = function () open_sessions = open_sessions - 1; end;
 	end
 	open_sessions = open_sessions + 1;
-	log("debug", "open sessions now: ".. open_sessions);
+	log("debug", "open sessions now: %d", open_sessions);
 	
 	local filter = initialize_filters(session);
 	local w = conn.write;
@@ -82,13 +77,13 @@
 		end
 	end
 
-	function session.send(data) log("debug", "Discarding data sent to resting session: %s", tostring(data)); end
+	function session.send(data) log("debug", "Discarding data sent to resting session: %s", tostring(data)); return false; 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)%s", session.full_jid or "(unknown)", session.username or "(unknown)", session.host or "(unknown)", err and (": "..err) or "");
+	(session.log or log)("debug", "Destroying session for %s (%s@%s)%s", session.full_jid or "(unknown)", session.username or "(unknown)", session.host or "(unknown)", err and (": "..err) or "");
 	if session.destroyed then return; end
 	
 	-- Remove session/resource from user's session list
--- a/core/stanza_router.lua	Sat Jun 09 02:27:44 2012 +0200
+++ b/core/stanza_router.lua	Sat Jul 28 01:14:31 2012 +0100
@@ -17,6 +17,18 @@
 local full_sessions = _G.prosody.full_sessions;
 local bare_sessions = _G.prosody.bare_sessions;
 
+local core_post_stanza, core_process_stanza, core_route_stanza;
+
+function deprecated_warning(f)
+	_G[f] = function(...)
+		log("warn", "Using the global %s() is deprecated, use module:send() or prosody.%s(). %s", f, f, debug.traceback());
+		return prosody[f](...);
+	end
+end
+deprecated_warning"core_post_stanza";
+deprecated_warning"core_process_stanza";
+deprecated_warning"core_route_stanza";
+
 local function handle_unhandled_stanza(host, origin, stanza)
 	local name, xmlns, origin_type = stanza.name, stanza.attr.xmlns or "jabber:client", origin.type;
 	if name == "iq" and xmlns == "jabber:client" then
--- a/core/usermanager.lua	Sat Jun 09 02:27:44 2012 +0200
+++ b/core/usermanager.lua	Sat Jul 28 01:14:31 2012 +0100
@@ -91,8 +91,8 @@
 	return hosts[host].users.delete_user(username);
 end
 
-function get_sasl_handler(host)
-	return hosts[host].users.get_sasl_handler();
+function get_sasl_handler(host, session)
+	return hosts[host].users.get_sasl_handler(session);
 end
 
 function get_provider(host)
--- a/net/http.lua	Sat Jun 09 02:27:44 2012 +0200
+++ b/net/http.lua	Sat Jul 28 01:14:31 2012 +0100
@@ -7,7 +7,7 @@
 --
 
 local socket = require "socket"
-local mime = require "mime"
+local b64 = require "util.encodings".base64.encode;
 local url = require "socket.url"
 local httpstream_new = require "util.httpstream".new;
 
@@ -154,7 +154,7 @@
 	};
 	
 	if req.userinfo then
-		headers["Authorization"] = "Basic "..mime.b64(req.userinfo);
+		headers["Authorization"] = "Basic "..b64(req.userinfo);
 	end
 
 	if ex then
@@ -203,7 +203,6 @@
 	if request.conn then
 		request.conn = nil;
 		request.handler:close()
-		listener.ondisconnect(request.handler, "closed");
 	end
 end
 
--- a/net/server_event.lua	Sat Jun 09 02:27:44 2012 +0200
+++ b/net/server_event.lua	Sat Jul 28 01:14:31 2012 +0100
@@ -33,8 +33,6 @@
 }
 
 local function use(x) return rawget(_G, x); end
-local print = use "print"
-local pcall = use "pcall"
 local ipairs = use "ipairs"
 local string = use "string"
 local select = use "select"
@@ -117,7 +115,6 @@
 	
 	local addevent = base.addevent
 	local coroutine_wrap, coroutine_yield = coroutine.wrap,coroutine.yield
-	local string_len = string.len
 	
 	-- Private methods
 	function interface_mt:_position(new_position)
@@ -212,7 +209,6 @@
 								self:_lock( false, false, false )  -- unlock the interface; sending, closing etc allowed
 								self.send = self.conn.send  -- caching table lookups with new client object
 								self.receive = self.conn.receive
-								local onsomething
 								if not call_onconnect then  -- trigger listener
 									self:onstatus("ssl-handshake-complete");
 								end
@@ -249,7 +245,7 @@
 			return true
 	end
 	function interface_mt:_destroy()  -- close this interface + events and call last listener
-			debug( "closing client with id:", self.id )
+			debug( "closing client with id:", self.id, self.fatalerror )
 			self:_lock( true, true, true )  -- first of all, lock the interface to avoid further actions
 			local _
 			_ = self.eventread and self.eventread:close( )  -- close events; this must be called outside of the event callbacks!
@@ -313,7 +309,7 @@
 		if self.nowriting then return nil, "locked" end
 		--vdebug( "try to send data to client, id/data:", self.id, data )
 		data = tostring( data )
-		local len = string_len( data )
+		local len = #data
 		local total = len + self.writebufferlen
 		if total > cfg.MAX_SEND_LENGTH then  -- check buffer length
 			local err = "send buffer exceeded"
@@ -328,22 +324,22 @@
 		end
 		return true
 	end
-	function interface_mt:close(now)
+	function interface_mt:close()
 		if self.nointerface then return nil, "locked"; end
 		debug( "try to close client connection with id:", self.id )
 		if self.type == "client" then
 			self.fatalerror = "client to close"
-			if ( not self.eventwrite ) or now then  -- try to close immediately
+			if self.eventwrite then -- wait for incomplete write request
+				self:_lock( true, true, false )
+				debug "closing delayed until writebuffer is empty"
+				return nil, "writebuffer not empty, waiting"
+			else -- close now
 				self:_lock( true, true, true )
 				self:_close()
 				return true
-			else  -- wait for incomplete write request
-				self:_lock( true, true, false )
-				debug "closing delayed until writebuffer is empty"
-				return nil, "writebuffer not empty, waiting"
 			end
 		else
-			debug( "try to close server with id:", tostring(self.id), "args:", tostring(now) )
+			debug( "try to close server with id:", tostring(self.id))
 			self.fatalerror = "server to close"
 			self:_lock( true )
 			self:_close( 0 )  -- add new event to remove the server interface
@@ -470,9 +466,7 @@
 	local string_sub = string.sub  -- caching table lookups
 	local string_len = string.len
 	local addevent = base.addevent
-	local coroutine_wrap = coroutine.wrap
 	local socket_gettime = socket.gettime
-	local coroutine_yield = coroutine.yield
 	function handleclient( client, ip, port, server, pattern, listener, sslctx )  -- creates an client interface
 		--vdebug("creating client interfacce...")
 		local interface = {
@@ -602,16 +596,14 @@
 				end
 				local buffer, err, part = interface.conn:receive( interface._pattern )  -- receive buffer with "pattern"
 				--vdebug( "read data:", tostring(buffer), "error:", tostring(err), "part:", tostring(part) )
-				buffer = buffer or part or ""
-				local len = string_len( buffer )
-				if len > cfg.MAX_READ_LENGTH then  -- check buffer length
+				buffer = buffer or part
+				if buffer and #buffer > cfg.MAX_READ_LENGTH then  -- check buffer length
 					interface.fatalerror = "receive buffer exceeded"
 					debug( "fatal error:", interface.fatalerror )
 					interface:_close()
 					interface.eventread = nil
 					return -1
 				end
-				interface.onincoming( interface, buffer, err )  -- send new data to listener
 				if err and ( err ~= "timeout" and err ~= "wantread" ) then
 					if "wantwrite" == err then -- need to read on write event
 						if not interface.eventwrite then  -- register new write event if needed
@@ -631,6 +623,8 @@
 						interface.eventread = nil
 						return -1
 					end
+				else
+					interface.onincoming( interface, buffer, err )  -- send new data to listener
 				end
 				if interface.noreading then
 					interface.eventread = nil;
@@ -742,7 +736,7 @@
 
 local addclient, wrapclient
 do
-	function wrapclient( client, ip, port, listeners, pattern, sslctx, startssl )
+	function wrapclient( client, ip, port, listeners, pattern, sslctx )
 		local interface = handleclient( client, ip, port, nil, pattern, listeners, sslctx )
 		interface:_start_connection(sslctx)
 		return interface, client
@@ -778,9 +772,6 @@
 		local res, err = client:connect( addr, serverport )  -- connect
 		if res or ( err == "timeout" ) then
 			local ip, port = client:getsockname( )
-			local server = function( )
-				return nil, "this is a dummy server interface"
-			end
 			local interface = wrapclient( client, ip, serverport, listener, pattern, sslctx, startssl )
 			interface:_start_connection( startssl )
 			debug( "new connection id:", interface.id )
--- a/net/server_select.lua	Sat Jun 09 02:27:44 2012 +0200
+++ b/net/server_select.lua	Sat Jul 28 01:14:31 2012 +0100
@@ -19,7 +19,6 @@
 local log, table_concat = require ("util.logger").init("socket"), table.concat;
 local out_put = function (...) return log("debug", table_concat{...}); end
 local out_error = function (...) return log("warn", table_concat{...}); end
-local mem_free = collectgarbage
 
 ----------------------------------// DECLARATION //--
 
@@ -34,7 +33,6 @@
 local ipairs = use "ipairs"
 local tonumber = use "tonumber"
 local tostring = use "tostring"
-local collectgarbage = use "collectgarbage"
 
 --// lua libs //--
 
@@ -49,7 +47,6 @@
 local math_min = math.min
 local math_huge = math.huge
 local table_concat = table.concat
-local table_remove = table.remove
 local string_len = string.len
 local string_sub = string.sub
 local coroutine_wrap = coroutine.wrap
@@ -67,7 +64,6 @@
 local socket_bind = luasocket.bind
 local socket_sleep = luasocket.sleep
 local socket_select = luasocket.select
-local ssl_newcontext = ( luasec and luasec.newcontext )
 
 --// functions //--
 
@@ -84,7 +80,6 @@
 local closesocket
 local removesocket
 local removeserver
-local changetimeout
 local wrapconnection
 local changesettings
 
@@ -314,22 +309,28 @@
 		end
 		return false, "setoption not implemented";
 	end
-	handler.close = function( self, forced )
+	handler.force_close = function ( self, err )
+		if bufferqueuelen ~= 0 then
+			out_put("server.lua: discarding unwritten data for ", tostring(ip), ":", tostring(clientport))
+			for i = bufferqueuelen, 1, -1 do
+				bufferqueue[i] = nil;
+			end
+			bufferqueuelen = 0;
+		end
+		return self:close(err);
+	end
+	handler.close = function( self, err )
 		if not handler then return true; end
 		_readlistlen = removesocket( _readlist, socket, _readlistlen )
 		_readtimes[ handler ] = nil
 		if bufferqueuelen ~= 0 then
-			if not ( forced or fatalerror ) then
-				handler.sendbuffer( )
-				if bufferqueuelen ~= 0 then -- try again...
-					if handler then
-						handler.write = nil -- ... but no further writing allowed
-					end
-					toclose = true
-					return false
+			handler.sendbuffer() -- Try now to send any outstanding data
+			if bufferqueuelen ~= 0 then -- Still not empty, so we'll try again later
+				if handler then
+					handler.write = nil -- ... but no further writing allowed
 				end
-			else
-				send( socket, table_concat( bufferqueue, "", 1, bufferqueuelen ), 1, bufferlen )	-- forced send
+				toclose = true
+				return false
 			end
 		end
 		if socket then
@@ -347,7 +348,8 @@
 			local _handler = handler;
 			handler = nil
 			if disconnect then
-				disconnect(_handler, "closed");
+				disconnect(_handler, err or false);
+				disconnect = nil
 			end
 		end
 		if server then
@@ -450,8 +452,7 @@
 			local buffer = buffer or part or ""
 			local len = string_len( buffer )
 			if len > maxreadlen then
-				disconnect( handler, "receive buffer exceeded" )
-				handler:close( true )
+				handler:close( "receive buffer exceeded" )
 				return false
 			end
 			local count = len * STAT_UNIT
@@ -463,14 +464,12 @@
 		else	-- connections was closed or fatal error
 			out_put( "server.lua: client ", tostring(ip), ":", tostring(clientport), " read error: ", tostring(err) )
 			fatalerror = true
-			disconnect( handler, err )
-			_ = handler and handler:close( )
+			_ = handler and handler:force_close( err )
 			return false
 		end
 	end
 	local _sendbuffer = function( ) -- this function sends data
 		local succ, err, byte, buffer, count;
-		local count;
 		if socket then
 			buffer = table_concat( bufferqueue, "", 1, bufferqueuelen )
 			succ, err, byte = send( socket, buffer, 1, bufferlen )
@@ -480,7 +479,7 @@
 			_ = _cleanqueue and clean( bufferqueue )
 			--out_put( "server.lua: sended '", buffer, "', bytes: ", tostring(succ), ", error: ", tostring(err), ", part: ", tostring(byte), ", to: ", tostring(ip), ":", tostring(clientport) )
 		else
-			succ, err, count = false, "closed", 0;
+			succ, err, count = false, "unexpected close", 0;
 		end
 		if succ then	-- sending succesful
 			bufferqueuelen = 0
@@ -491,7 +490,7 @@
 				drain(handler)
 			end
 			_ = needtls and handler:starttls(nil)
-			_ = toclose and handler:close( )
+			_ = toclose and handler:force_close( )
 			return true
 		elseif byte and ( err == "timeout" or err == "wantwrite" ) then -- want write
 			buffer = string_sub( buffer, byte + 1, bufferlen ) -- new buffer
@@ -503,8 +502,7 @@
 		else	-- connection was closed during sending or fatal error
 			out_put( "server.lua: client ", tostring(ip), ":", tostring(clientport), " write error: ", tostring(err) )
 			fatalerror = true
-			disconnect( handler, err )
-			_ = handler and handler:close( )
+			_ = handler and handler:force_close( err )
 			return false
 		end
 	end
@@ -546,9 +544,8 @@
 					end
 				end
 				out_put( "server.lua: ssl handshake error: ", tostring(err or "handshake too long") )
-				disconnect( handler, "ssl handshake failed" )
-				_ = handler and handler:close( true )	 -- forced disconnect
-                               return false, err -- handshake failed
+				_ = handler and handler:force_close("ssl handshake failed")
+               return false, err -- handshake failed
 			end
 		)
 	end
@@ -810,7 +807,7 @@
 		end
 		for handler, err in pairs( _closelist ) do
 			handler.disconnect( )( handler, err )
-			handler:close( true )	 -- forced disconnect
+			handler:force_close()	 -- forced disconnect
 		end
 		clean( _closelist )
 		_currenttime = luasocket_gettime( )
@@ -896,7 +893,7 @@
 				if os_difftime( _currenttime - timestamp ) > _sendtimeout then
 					--_writetimes[ handler ] = nil
 					handler.disconnect( )( handler, "send timeout" )
-					handler:close( true )	 -- forced disconnect
+					handler:force_close()	 -- forced disconnect
 				end
 			end
 			for handler, timestamp in pairs( _readtimes ) do
--- a/plugins/adhoc/adhoc.lib.lua	Sat Jun 09 02:27:44 2012 +0200
+++ b/plugins/adhoc/adhoc.lib.lua	Sat Jul 28 01:14:31 2012 +0100
@@ -12,7 +12,7 @@
 
 local _M = {};
 
-function _cmdtag(desc, status, sessionid, action)
+local function _cmdtag(desc, status, sessionid, action)
 	local cmd = st.stanza("command", { xmlns = xmlns_cmd, node = desc.node, status = status });
 	if sessionid then cmd.attr.sessionid = sessionid; end
 	if action then cmd.attr.action = action; end
@@ -35,6 +35,7 @@
 	local data, state = command:handler(dataIn, states[sessionid]);
 	states[sessionid] = state;
 	local stanza = st.reply(stanza);
+	local cmdtag;
 	if data.status == "completed" then
 		states[sessionid] = nil;
 		cmdtag = command:cmdtag("completed", sessionid);
@@ -64,8 +65,8 @@
 				if (action == "prev") or (action == "next") or (action == "complete") then
 					actions:tag(action):up();
 				else
-					module:log("error", 'Command "'..command.name..
-						'" at node "'..command.node..'" provided an invalid action "'..action..'"');
+					module:log("error", "Command %q at node %q provided an invalid action %q",
+						command.name, command.node, action);
 				end
 			end
 			cmdtag:add_child(actions);
--- a/plugins/adhoc/mod_adhoc.lua	Sat Jun 09 02:27:44 2012 +0200
+++ b/plugins/adhoc/mod_adhoc.lua	Sat Jul 28 01:14:31 2012 +0100
@@ -100,3 +100,4 @@
 end
 
 module:handle_items("adhoc", adhoc_added, adhoc_removed);
+module:handle_items("adhoc-provider", adhoc_added, adhoc_removed);
--- a/plugins/mod_admin_adhoc.lua	Sat Jun 09 02:27:44 2012 +0200
+++ b/plugins/mod_admin_adhoc.lua	Sat Jul 28 01:14:31 2012 +0100
@@ -23,10 +23,19 @@
 local dataforms_new = require "util.dataforms".new;
 local array = require "util.array";
 local modulemanager = require "modulemanager";
+local core_post_stanza = prosody.core_post_stanza;
 
 module:depends("adhoc");
 local adhoc_new = module:require "adhoc".new;
 
+local function generate_error_message(errors)
+	local errmsg = {};
+	for name, err in pairs(errors) do
+		errmsg[#errmsg + 1] = name .. ": " .. err;
+	end
+	return { status = "completed", error = { message = t_concat(errmsg, "\n") } };
+end
+
 function add_user_command_handler(self, data, state)
 	local add_user_layout = dataforms_new{
 		title = "Adding a User";
@@ -42,9 +51,9 @@
 		if data.action == "cancel" then
 			return { status = "canceled" };
 		end
-		local fields = add_user_layout:data(data.form);
-		if not fields.accountjid then
-			return { status = "completed", error = { message = "You need to specify a JID." } };
+		local fields, err = add_user_layout:data(data.form);
+		if err then
+			return generate_error_message(err);
 		end
 		local username, host, resource = jid.split(fields.accountjid);
 		if data.to ~= host then
@@ -55,15 +64,14 @@
 				return { status = "completed", error = { message = "Account already exists" } };
 			else
 				if usermanager_create_user(username, fields.password, host) then
-					module:log("info", "Created new account " .. username.."@"..host);
+					module:log("info", "Created new account %s@%s", username, host);
 					return { status = "completed", info = "Account successfully created" };
 				else
 					return { status = "completed", error = { message = "Failed to write data to disk" } };
 				end
 			end
 		else
-			module:log("debug", (fields.accountjid or "<nil>") .. " " .. (fields.password or "<nil>") .. " "
-				.. (fields["password-verify"] or "<nil>"));
+			module:log("debug", "Invalid data, password mismatch or empty username while creating account for %s", fields.accountjid or "<nil>");
 			return { status = "completed", error = { message = "Invalid data.\nPassword mismatch, or empty username" } };
 		end
 	else
@@ -85,9 +93,9 @@
 		if data.action == "cancel" then
 			return { status = "canceled" };
 		end
-		local fields = change_user_password_layout:data(data.form);
-		if not fields.accountjid or fields.accountjid == "" or not fields.password then
-			return { status = "completed", error = { message = "Please specify username and password" } };
+		local fields, err = change_user_password_layout:data(data.form);
+		if err then
+			return generate_error_message(err);
 		end
 		local username, host, resource = jid.split(fields.accountjid);
 		if data.to ~= host then
@@ -126,16 +134,19 @@
 		if data.action == "cancel" then
 			return { status = "canceled" };
 		end
-		local fields = delete_user_layout:data(data.form);
+		local fields, err = delete_user_layout:data(data.form);
+		if err then
+			return generate_error_message(err);
+		end
 		local failed = {};
 		local succeeded = {};
 		for _, aJID in ipairs(fields.accountjids) do
 			local username, host, resource = jid.split(aJID);
 			if (host == data.to) and  usermanager_user_exists(username, host) and disconnect_user(aJID) and usermanager_create_user(username, nil, host) then
-				module:log("debug", "User " .. aJID .. " has been deleted");
+				module:log("debug", "User %s has been deleted", aJID);
 				succeeded[#succeeded+1] = aJID;
 			else
-				module:log("debug", "Tried to delete non-existant user "..aJID);
+				module:log("debug", "Tried to delete non-existant user %s", aJID);
 				failed[#failed+1] = aJID;
 			end
 		end
@@ -154,7 +165,7 @@
 	local sessions = host.sessions[node] and host.sessions[node].sessions;
 	for resource, session in pairs(sessions or {}) do
 		if not givenResource or (resource == givenResource) then
-			module:log("debug", "Disconnecting "..node.."@"..hostname.."/"..resource);
+			module:log("debug", "Disconnecting %s@%s/%s", node, hostname, resource);
 			session:close();
 		end
 	end
@@ -175,7 +186,10 @@
 			return { status = "canceled" };
 		end
 
-		local fields = end_user_session_layout:data(data.form);
+		local fields, err = end_user_session_layout:data(data.form);
+		if err then
+			return generate_error_message(err);
+		end
 		local failed = {};
 		local succeeded = {};
 		for _, aJID in ipairs(fields.accountjids) do
@@ -223,9 +237,9 @@
 		if data.action == "cancel" then
 			return { status = "canceled" };
 		end
-		local fields = get_user_password_layout:data(data.form);
-		if not fields.accountjid then
-			return { status = "completed", error = { message = "Please specify a JID." } };
+		local fields, err = get_user_password_layout:data(data.form);
+		if err then
+			return generate_error_message(err);
 		end
 		local user, host, resource = jid.split(fields.accountjid);
 		local accountjid = "";
@@ -261,10 +275,10 @@
 			return { status = "canceled" };
 		end
 
-		local fields = get_user_roster_layout:data(data.form);
+		local fields, err = get_user_roster_layout:data(data.form);
 
-		if not fields.accountjid then
-			return { status = "completed", error = { message = "Please specify a JID" } };
+		if err then
+			return generate_error_message(err);
 		end
 
 		local user, host, resource = jid.split(fields.accountjid);
@@ -323,10 +337,10 @@
 			return { status = "canceled" };
 		end
 
-		local fields = get_user_stats_layout:data(data.form);
+		local fields, err = get_user_stats_layout:data(data.form);
 
-		if not fields.accountjid then
-			return { status = "completed", error = { message = "Please specify a JID." } };
+		if err then
+			return generate_error_message(err);
 		end
 
 		local user, host, resource = jid.split(fields.accountjid);
@@ -376,7 +390,11 @@
 			return { status = "canceled" };
 		end
 
-		local fields = get_online_users_layout:data(data.form);
+		local fields, err = get_online_users_layout:data(data.form);
+
+		if err then
+			return generate_error_message(err);
+		end
 
 		local max_items = nil
 		if fields.max_items ~= "all" then
@@ -436,11 +454,9 @@
 		if data.action == "cancel" then
 			return { status = "canceled" };
 		end
-		local fields = layout:data(data.form);
-		if (not fields.module) or (fields.module == "") then
-			return { status = "completed", error = {
-				message = "Please specify a module."
-			} };
+		local fields, err = layout:data(data.form);
+		if err then
+			return generate_error_message(err);
 		end
 		if modulemanager.is_loaded(data.to, fields.module) then
 			return { status = "completed", info = "Module already loaded" };
@@ -453,7 +469,6 @@
 			'". Error was: "'..tostring(err or "<unspecified>")..'"' } };
 		end
 	else
-		local modules = array.collect(keys(hosts[data.to].modules)):sort();
 		return { status = "executing", form = layout }, "executing";
 	end
 end
@@ -470,11 +485,9 @@
 		if data.action == "cancel" then
 			return { status = "canceled" };
 		end
-		local fields = layout:data(data.form);
-		if #fields.modules == 0 then
-			return { status = "completed", error = {
-				message = "Please specify a module. (This means your client misbehaved, as this field is required)"
-			} };
+		local fields, err = layout:data(data.form);
+		if err then
+			return generate_error_message(err);
 		end
 		local ok_list, err_list = {}, {};
 		for _, module in ipairs(fields.modules) do
@@ -538,7 +551,11 @@
 			return { status = "canceled" };
 		end
 
-		local fields = shut_down_service_layout:data(data.form);
+		local fields, err = shut_down_service_layout:data(data.form);
+
+		if err then
+			return generate_error_message(err);
+		end
 
 		if fields.announcement and #fields.announcement > 0 then
 			local message = st.message({type = "headline"}, fields.announcement):up()
@@ -566,11 +583,9 @@
 		if data.action == "cancel" then
 			return { status = "canceled" };
 		end
-		local fields = layout:data(data.form);
-		if #fields.modules == 0 then
-			return { status = "completed", error = {
-				message = "Please specify a module. (This means your client misbehaved, as this field is required)"
-			} };
+		local fields, err = layout:data(data.form);
+		if err then
+			return generate_error_message(err);
 		end
 		local ok_list, err_list = {}, {};
 		for _, module in ipairs(fields.modules) do
@@ -605,17 +620,17 @@
 local shut_down_service_desc = adhoc_new("Shut Down Service", "http://jabber.org/protocol/admin#shutdown", shut_down_service_handler, "global_admin");
 local unload_modules_desc = adhoc_new("Unload modules", "http://prosody.im/protocol/modules#unload", unload_modules_handler, "admin");
 
-module:add_item("adhoc", add_user_desc);
-module:add_item("adhoc", change_user_password_desc);
-module:add_item("adhoc", config_reload_desc);
-module:add_item("adhoc", delete_user_desc);
-module:add_item("adhoc", end_user_session_desc);
-module:add_item("adhoc", get_user_password_desc);
-module:add_item("adhoc", get_user_roster_desc);
-module:add_item("adhoc", get_user_stats_desc);
-module:add_item("adhoc", get_online_users_desc);
-module:add_item("adhoc", list_modules_desc);
-module:add_item("adhoc", load_module_desc);
-module:add_item("adhoc", reload_modules_desc);
-module:add_item("adhoc", shut_down_service_desc);
-module:add_item("adhoc", unload_modules_desc);
+module:provides("adhoc", add_user_desc);
+module:provides("adhoc", change_user_password_desc);
+module:provides("adhoc", config_reload_desc);
+module:provides("adhoc", delete_user_desc);
+module:provides("adhoc", end_user_session_desc);
+module:provides("adhoc", get_user_password_desc);
+module:provides("adhoc", get_user_roster_desc);
+module:provides("adhoc", get_user_stats_desc);
+module:provides("adhoc", get_online_users_desc);
+module:provides("adhoc", list_modules_desc);
+module:provides("adhoc", load_module_desc);
+module:provides("adhoc", reload_modules_desc);
+module:provides("adhoc", shut_down_service_desc);
+module:provides("adhoc", unload_modules_desc);
--- a/plugins/mod_admin_telnet.lua	Sat Jun 09 02:27:44 2012 +0200
+++ b/plugins/mod_admin_telnet.lua	Sat Jul 28 01:14:31 2012 +0100
@@ -13,7 +13,7 @@
 local prosody = _G.prosody;
 local hosts = prosody.hosts;
 
-local console_listener = { default_port = 5582; default_mode = "*l"; interface = "127.0.0.1" };
+local console_listener = { default_port = 5582; default_mode = "*a"; interface = "127.0.0.1" };
 
 local hostmanager = require "core.hostmanager";
 local modulemanager = require "core.modulemanager";
@@ -30,6 +30,7 @@
 local commands = module:shared("commands")
 local def_env = module:shared("env");
 local default_env_mt = { __index = def_env };
+local core_post_stanza = prosody.core_post_stanza;
 
 local function redirect_output(_G, session)
 	local env = setmetatable({ print = session.print }, { __index = function (t, k) return rawget(_G, k); end });
@@ -81,67 +82,74 @@
 function console_listener.onincoming(conn, data)
 	local session = sessions[conn];
 
-	-- Handle data
-	(function(session, data)
-		local useglobalenv;
-		
-		if data:match("^>") then
-			data = data:gsub("^>", "");
-			useglobalenv = true;
-		elseif data == "\004" then
-			commands["bye"](session, data);
-			return;
-		else
-			local command = data:lower();
-			command = data:match("^%w+") or data:match("%p");
-			if commands[command] then
-				commands[command](session, data);
-				return;
+	local partial = session.partial_data;
+	if partial then
+		data = partial..data;
+	end
+
+	for line in data:gmatch("[^\n]*[\n\004]") do
+		-- Handle data (loop allows us to break to add \0 after response)
+		repeat
+			local useglobalenv;
+
+			if line:match("^>") then
+				line = line:gsub("^>", "");
+				useglobalenv = true;
+			elseif line == "\004" then
+				commands["bye"](session, line);
+				break;
+			else
+				local command = line:match("^%w+") or line:match("%p");
+				if commands[command] then
+					commands[command](session, line);
+					break;
+				end
 			end
-		end
 
-		session.env._ = data;
-		
-		local chunkname = "=console";
-		local env = (useglobalenv and redirect_output(_G, session)) or session.env or nil
-		local chunk, err = envload("return "..data, chunkname, env);
-		if not chunk then
-			chunk, err = envload(data, chunkname, env);
+			session.env._ = line;
+			
+			local chunkname = "=console";
+			local env = (useglobalenv and redirect_output(_G, session)) or session.env or nil
+			local chunk, err = envload("return "..line, chunkname, env);
 			if not chunk then
-				err = err:gsub("^%[string .-%]:%d+: ", "");
-				err = err:gsub("^:%d+: ", "");
-				err = err:gsub("'<eof>'", "the end of the line");
-				session.print("Sorry, I couldn't understand that... "..err);
-				return;
+				chunk, err = envload(line, chunkname, env);
+				if not chunk then
+					err = err:gsub("^%[string .-%]:%d+: ", "");
+					err = err:gsub("^:%d+: ", "");
+					err = err:gsub("'<eof>'", "the end of the line");
+					session.print("Sorry, I couldn't understand that... "..err);
+					break;
+				end
 			end
-		end
-		
-		local ranok, taskok, message = pcall(chunk);
-		
-		if not (ranok or message or useglobalenv) and commands[data:lower()] then
-			commands[data:lower()](session, data);
-			return;
-		end
 		
-		if not ranok then
-			session.print("Fatal error while running command, it did not complete");
-			session.print("Error: "..taskok);
-			return;
-		end
+			local ranok, taskok, message = pcall(chunk);
+			
+			if not (ranok or message or useglobalenv) and commands[line:lower()] then
+				commands[line:lower()](session, line);
+				break;
+			end
+			
+			if not ranok then
+				session.print("Fatal error while running command, it did not complete");
+				session.print("Error: "..taskok);
+				break;
+			end
+			
+			if not message then
+				session.print("Result: "..tostring(taskok));
+				break;
+			elseif (not taskok) and message then
+				session.print("Command completed with a problem");
+				session.print("Message: "..tostring(message));
+				break;
+			end
+			
+			session.print("OK: "..tostring(message));
+		until true
 		
-		if not message then
-			session.print("Result: "..tostring(taskok));
-			return;
-		elseif (not taskok) and message then
-			session.print("Command completed with a problem");
-			session.print("Message: "..tostring(message));
-			return;
-		end
-		
-		session.print("OK: "..tostring(message));
-	end)(session, data);
-	
-	session.send(string.char(0));
+		session.send(string.char(0));
+	end
+	session.partial_data = data:match("[^\n]+$");
 end
 
 function console_listener.ondisconnect(conn, err)
@@ -191,6 +199,7 @@
 		print [[s2s - Commands to manage sessions between this server and others]]
 		print [[module - Commands to load/reload/unload modules/plugins]]
 		print [[host - Commands to activate, deactivate and list virtual hosts]]
+		print [[user - Commands to create and delete users, and change their passwords]]
 		print [[server - Uptime, version, shutting down, etc.]]
 		print [[config - Reloading the configuration, etc.]]
 		print [[console - Help regarding the console itself]]
@@ -202,6 +211,7 @@
 	elseif section == "s2s" then
 		print [[s2s:show(domain) - Show all s2s connections for the given domain (or all if no domain given)]]
 		print [[s2s:close(from, to) - Close a connection from one domain to another]]
+		print [[s2s:closeall(host) - Close all the incoming/outgoing s2s sessions to specified host]]
 	elseif section == "module" then
 		print [[module:load(module, host) - Load the specified module on the specified host (or all hosts if none given)]]
 		print [[module:reload(module, host) - The same, but unloads and loads the module (saving state if the module supports it)]]
@@ -211,6 +221,10 @@
 		print [[host:activate(hostname) - Activates the specified host]]
 		print [[host:deactivate(hostname) - Disconnects all clients on this host and deactivates]]
 		print [[host:list() - List the currently-activated hosts]]
+	elseif section == "user" then
+		print [[user:create(jid, password) - Create the specified user account]]
+		print [[user:password(jid, password) - Set the password for the specified user account]]
+		print [[user:delete(jid, password) - Permanently remove the specified user account]]
 	elseif section == "server" then
 		print [[server:version() - Show the server's version number]]
 		print [[server:uptime() - Show how long the server has been running]]
@@ -679,7 +693,6 @@
 	end
 	local domain_certs = array.collect(values(cert_set));
 	-- Phew. We now have a array of unique certificates presented by domain.
-	local print = self.session.print;
 	local n_certs = #domain_certs;
 	
 	if n_certs == 0 then
@@ -773,6 +786,40 @@
 	return true, "Closed "..count.." s2s session"..((count == 1 and "") or "s");
 end
 
+function def_env.s2s:closeall(host)
+        local count = 0;
+
+        if not host or type(host) ~= "string" then return false, "wrong syntax: please use s2s:closeall('hostname.tld')"; end
+        if hosts[host] then
+                for session in pairs(incoming_s2s) do
+                        if session.to_host == host then
+                                (session.close or s2smanager.destroy_session)(session);
+                                count = count + 1;
+                        end
+                end
+                for _, session in pairs(hosts[host].s2sout) do
+                        (session.close or s2smanager.destroy_session)(session);
+                        count = count + 1;
+                end
+        else
+                for session in pairs(incoming_s2s) do
+			if session.from_host == host then
+				(session.close or s2smanager.destroy_session)(session);
+				count = count + 1;
+			end
+		end
+		for _, h in pairs(hosts) do
+			if h.s2sout[host] then
+				(h.s2sout[host].close or s2smanager.destroy_session)(h.s2sout[host]);
+				count = count + 1;
+			end
+		end
+        end
+
+	if count == 0 then return false, "No sessions to close.";
+	else return true, "Closed "..count.." s2s session"..((count == 1 and "") or "s"); end
+end
+
 def_env.host = {}; def_env.hosts = def_env.host;
 
 function def_env.host:activate(hostname, config)
@@ -860,6 +907,53 @@
 	return setmetatable({ room = room_obj }, console_room_mt);
 end
 
+local um = require"core.usermanager";
+
+def_env.user = {};
+function def_env.user:create(jid, password)
+	local username, host = jid_split(jid);
+	local ok, err = um.create_user(username, password, host);
+	if ok then
+		return true, "User created";
+	else
+		return nil, "Could not create user: "..err;
+	end
+end
+
+function def_env.user:delete(jid)
+	local username, host = jid_split(jid);
+	local ok, err = um.delete_user(username, host);
+	if ok then
+		return true, "User deleted";
+	else
+		return nil, "Could not delete user: "..err;
+	end
+end
+
+function def_env.user:passwd(jid, password)
+	local username, host = jid_split(jid);
+	local ok, err = um.set_password(username, password, host);
+	if ok then
+		return true, "User created";
+	else
+		return nil, "Could not change password for user: "..err;
+	end
+end
+
+def_env.xmpp = {};
+
+local st = require "util.stanza";
+function def_env.xmpp:ping(localhost, remotehost)
+	if hosts[localhost] then
+		core_post_stanza(hosts[localhost],
+			st.iq{ from=localhost, to=remotehost, type="get", id="ping" }
+				:tag("ping", {xmlns="urn:xmpp:ping"}));
+		return true, "Sent ping";
+	else
+		return nil, "No such host";
+	end
+end
+
 -------------
 
 function printbanner(session)
--- a/plugins/mod_announce.lua	Sat Jun 09 02:27:44 2012 +0200
+++ b/plugins/mod_announce.lua	Sat Jul 28 01:14:31 2012 +0100
@@ -25,7 +25,7 @@
 			for username in pairs(host_session.sessions) do
 				c = c + 1;
 				message.attr.to = username.."@"..hostname;
-				core_post_stanza(host_session, message);
+				module:send(message);
 			end
 		end
 	end
@@ -96,5 +96,5 @@
 
 local adhoc_new = module:require "adhoc".new;
 local announce_desc = adhoc_new("Send Announcement to Online Users", "http://jabber.org/protocol/admin#announce", announce_handler, "admin");
-module:add_item("adhoc", announce_desc);
+module:provides("adhoc", announce_desc);
 
--- a/plugins/mod_auth_cyrus.lua	Sat Jun 09 02:27:44 2012 +0200
+++ b/plugins/mod_auth_cyrus.lua	Sat Jul 28 01:14:31 2012 +0100
@@ -14,6 +14,7 @@
 local cyrus_service_name = module:get_option("cyrus_service_name");
 local cyrus_application_name = module:get_option("cyrus_application_name");
 local require_provisioning = module:get_option("cyrus_require_provisioning") or false;
+local host_fqdn = module:get_option("cyrus_server_fqdn");
 
 prosody.unlock_globals(); --FIXME: Figure out why this is needed and
 						  -- why cyrussasl isn't caught by the sandbox
@@ -23,7 +24,8 @@
 	return cyrus_new(
 		cyrus_service_realm or realm,
 		cyrus_service_name or "xmpp",
-		cyrus_application_name or "prosody"
+		cyrus_application_name or "prosody",
+		host_fqdn
 	);
 end
 
--- a/plugins/mod_bosh.lua	Sat Jun 09 02:27:44 2012 +0200
+++ b/plugins/mod_bosh.lua	Sat Jul 28 01:14:31 2012 +0100
@@ -14,7 +14,7 @@
 local sm_destroy_session = sm.destroy_session;
 local new_uuid = require "util.uuid".generate;
 local fire_event = prosody.events.fire_event;
-local core_process_stanza = core_process_stanza;
+local core_process_stanza = prosody.core_process_stanza;
 local st = require "util.stanza";
 local logger = require "util.logger";
 local log = logger.init("mod_bosh");
@@ -56,7 +56,7 @@
 
 local function get_ip_from_request(request)
 	local ip = request.conn:ip();
-	local forwarded_for = request.headers["x-forwarded-for"];
+	local forwarded_for = request.headers.x_forwarded_for;
 	if forwarded_for then
 		forwarded_for = forwarded_for..", "..ip;
 		for forwarded_ip in forwarded_for:gmatch("[^%s,]+") do
--- a/plugins/mod_c2s.lua	Sat Jun 09 02:27:44 2012 +0200
+++ b/plugins/mod_c2s.lua	Sat Jul 28 01:14:31 2012 +0100
@@ -24,9 +24,11 @@
 local log = module._log;
 
 local c2s_timeout = module:get_option_number("c2s_timeout");
+local stream_close_timeout = module:get_option_number("c2s_close_timeout", 5);
 local opt_keepalives = module:get_option_boolean("tcp_keepalives", false);
 
 local sessions = module:shared("sessions");
+local core_process_stanza = prosody.core_process_stanza;
 
 local stream_callbacks = { default_ns = "jabber:client", handlestanza = core_process_stanza };
 local listener = {};
@@ -75,7 +77,7 @@
 
 function stream_callbacks.streamclosed(session)
 	session.log("debug", "Received </stream:stream>");
-	session:close();
+	session:close(false);
 end
 
 function stream_callbacks.error(session, error, data)
@@ -121,9 +123,9 @@
 			session.send("<?xml version='1.0'?>");
 			session.send(st.stanza("stream:stream", default_stream_attr):top_tag());
 		end
-		if reason then
+		if reason then -- nil == no err, initiated by us, false == initiated by client
 			if type(reason) == "string" then -- assume stream error
-				log("info", "Disconnecting client, <stream:error> is: %s", reason);
+				log("debug", "Disconnecting client, <stream:error> is: %s", reason);
 				session.send(st.stanza("stream:error"):tag(reason, {xmlns = 'urn:ietf:params:xml:ns:xmpp-streams' }));
 			elseif type(reason) == "table" then
 				if reason.condition then
@@ -134,17 +136,36 @@
 					if reason.extra then
 						stanza:add_child(reason.extra);
 					end
-					log("info", "Disconnecting client, <stream:error> is: %s", tostring(stanza));
+					log("debug", "Disconnecting client, <stream:error> is: %s", tostring(stanza));
 					session.send(stanza);
 				elseif reason.name then -- a stanza
-					log("info", "Disconnecting client, <stream:error> is: %s", tostring(reason));
+					log("debug", "Disconnecting client, <stream:error> is: %s", tostring(reason));
 					session.send(reason);
 				end
 			end
 		end
+		
 		session.send("</stream:stream>");
-		session.conn:close();
-		listener.ondisconnect(session.conn, (reason and (reason.text or reason.condition)) or reason or "session closed");
+		function session.send() return false; end
+		
+		local reason = (reason and (reason.text or reason.condition)) or reason;
+		session.log("info", "c2s stream for %s closed: %s", session.full_jid or ("<"..session.ip..">"), reason or "session closed");
+
+		-- Authenticated incoming stream may still be sending us stanzas, so wait for </stream:stream> from remote
+		local conn = session.conn;
+		if reason == nil and not session.notopen and session.type == "c2s" then
+			-- Grace time to process data from authenticated cleanly-closed stream
+			add_task(stream_close_timeout, function ()
+				if not session.destroyed then
+					session.log("warn", "Failed to receive a stream close response, closing connection anyway...");
+					sm_destroy_session(session, reason);
+					conn:close();
+				end
+			end);
+		else
+			sm_destroy_session(session, reason);
+			conn:close();
+		end
 	end
 end
 
@@ -208,10 +229,9 @@
 function listener.ondisconnect(conn, err)
 	local session = sessions[conn];
 	if session then
-		(session.log or log)("info", "Client disconnected: %s", err);
+		(session.log or log)("info", "Client disconnected: %s", err or "connection closed");
 		sm_destroy_session(session, err);
 		sessions[conn]  = nil;
-		session = nil;
 	end
 end
 
--- a/plugins/mod_component.lua	Sat Jun 09 02:27:44 2012 +0200
+++ b/plugins/mod_component.lua	Sat Jul 28 01:14:31 2012 +0100
@@ -18,6 +18,8 @@
 local new_xmpp_stream = require "util.xmppstream".new;
 local uuid_gen = require "util.uuid".generate;
 
+local core_process_stanza = prosody.core_process_stanza;
+
 
 local log = module._log;
 
@@ -124,7 +126,7 @@
 
 function stream_callbacks.error(session, error, data, data2)
 	if session.destroyed then return; end
-	module:log("warn", "Error processing component stream: "..tostring(error));
+	module:log("warn", "Error processing component stream: %s", tostring(error));
 	if error == "no-stream" then
 		session:close("invalid-namespace");
 	elseif error == "parse-error" then
@@ -169,8 +171,6 @@
 	session:close();
 end
 
-local core_process_stanza = core_process_stanza;
-
 function stream_callbacks.handlestanza(session, stanza)
 	-- Namespaces are icky.
 	if not stanza.attr.xmlns and stanza.name == "handshake" then
--- a/plugins/mod_dialback.lua	Sat Jun 09 02:27:44 2012 +0200
+++ b/plugins/mod_dialback.lua	Sat Jul 28 01:14:31 2012 +0100
@@ -41,6 +41,11 @@
 		-- We are being asked to verify the key, to ensure it was generated by us
 		origin.log("debug", "verifying that dialback key is ours...");
 		local attr = stanza.attr;
+		if attr.type then
+			module:log("warn", "Ignoring incoming session from %s claiming a dialback key for %s is %s",
+				origin.from_host or "(unknown)", attr.from or "(unknown)", attr.type);
+			return true;
+		end
 		-- COMPAT: Grr, ejabberd breaks this one too?? it is black and white in XEP-220 example 34
 		--if attr.from ~= origin.to_host then error("invalid-from"); end
 		local type;
@@ -84,7 +89,7 @@
 			origin.from_host = from;
 		end
 		if not origin.to_host then
-			origin.to_host = nameprep(attr.to);
+			origin.to_host = to;
 		end
 
 		origin.log("debug", "asking %s if key %s belongs to them", from, stanza[1]);
@@ -102,7 +107,6 @@
 	if origin.type == "s2sout_unauthed" or origin.type == "s2sout" then
 		local attr = stanza.attr;
 		local dialback_verifying = dialback_requests[attr.from.."/"..(attr.id or "")];
-		module:log("debug", tostring(dialback_verifying).." "..attr.from.." "..origin.to_host);
 		if dialback_verifying and attr.from == origin.to_host then
 			local valid;
 			if attr.type == "valid" then
@@ -110,7 +114,7 @@
 				valid = "valid";
 			else
 				-- Warn the original connection that is was not verified successfully
-				log("warn", "authoritative server for "..(attr.from or "(unknown)").." denied the key");
+				log("warn", "authoritative server for %s denied the key", attr.from or "(unknown)");
 				valid = "invalid";
 			end
 			if not dialback_verifying.sends2s then
--- a/plugins/mod_disco.lua	Sat Jun 09 02:27:44 2012 +0200
+++ b/plugins/mod_disco.lua	Sat Jul 28 01:14:31 2012 +0100
@@ -32,7 +32,7 @@
 	end
 end
 
-module:add_identity("server", "im", "Prosody"); -- FIXME should be in the non-existing mod_router
+module:add_identity("server", "im", module:get_option_string("name", "Prosody")); -- FIXME should be in the non-existing mod_router
 module:add_feature("http://jabber.org/protocol/disco#info");
 module:add_feature("http://jabber.org/protocol/disco#items");
 
--- a/plugins/mod_iq.lua	Sat Jun 09 02:27:44 2012 +0200
+++ b/plugins/mod_iq.lua	Sat Jul 28 01:14:31 2012 +0100
@@ -17,10 +17,7 @@
 		local origin, stanza = data.origin, data.stanza;
 
 		local session = full_sessions[stanza.attr.to];
-		if session then
-			-- TODO fire post processing event
-			session.send(stanza);
-		else -- resource not online
+		if not (session and session.send(stanza)) then
 			if stanza.attr.type == "get" or stanza.attr.type == "set" then
 				origin.send(st.error_reply(stanza, "cancel", "service-unavailable"));
 			end
--- a/plugins/mod_message.lua	Sat Jun 09 02:27:44 2012 +0200
+++ b/plugins/mod_message.lua	Sat Jul 28 01:14:31 2012 +0100
@@ -35,10 +35,13 @@
 		if user then -- some resources are connected
 			local recipients = user.top_resources;
 			if recipients then
+				local sent;
 				for i=1,#recipients do
-					recipients[i].send(stanza);
+					sent = recipients[i].send(stanza) or sent;
 				end
-				return true;
+				if sent then
+					return true;
+				end
 			end
 		end
 		-- no resources are online
@@ -65,9 +68,7 @@
 	local origin, stanza = data.origin, data.stanza;
 	
 	local session = full_sessions[stanza.attr.to];
-	if session then
-		-- TODO fire post processing event
-		session.send(stanza);
+	if session and session.send(stanza) then
 		return true;
 	else -- resource not online
 		return process_to_bare(jid_bare(stanza.attr.to), origin, stanza);
--- a/plugins/mod_motd.lua	Sat Jun 09 02:27:44 2012 +0200
+++ b/plugins/mod_motd.lua	Sat Jul 28 01:14:31 2012 +0100
@@ -24,7 +24,7 @@
 			local motd_stanza =
 				st.message({ to = session.full_jid, from = motd_jid })
 					:tag("body"):text(motd_text);
-			core_route_stanza(hosts[host], motd_stanza);
+			module:send(motd_stanza);
 			module:log("debug", "MOTD send to user %s", session.full_jid);
 		end
 end, 1);
--- a/plugins/mod_pep.lua	Sat Jun 09 02:27:44 2012 +0200
+++ b/plugins/mod_pep.lua	Sat Jul 28 01:14:31 2012 +0100
@@ -17,6 +17,7 @@
 local next = next;
 local type = type;
 local calculate_hash = require "util.caps".calculate_hash;
+local core_post_stanza = prosody.core_post_stanza;
 
 local NULL = {};
 local data = {};
@@ -32,7 +33,7 @@
 	hash_map = state.hash_map or {};
 end
 
-module:add_identity("pubsub", "pep", "Prosody");
+module:add_identity("pubsub", "pep", module:get_option_string("name", "Prosody"));
 module:add_feature("http://jabber.org/protocol/pubsub#publish");
 
 local function subscription_presence(user_bare, recipient)
--- a/plugins/mod_posix.lua	Sat Jun 09 02:27:44 2012 +0200
+++ b/plugins/mod_posix.lua	Sat Jul 28 01:14:31 2012 +0100
@@ -34,19 +34,19 @@
 		if gid then
 			local success, msg = pposix.setgid(gid);
 			if success then
-				module:log("debug", "Changed group to "..gid.." successfully.");
+				module:log("debug", "Changed group to %s successfully.", gid);
 			else
-				module:log("error", "Failed to change group to "..gid..". Error: "..msg);
-				prosody.shutdown("Failed to change group to "..gid);
+				module:log("error", "Failed to change group to %s. Error: %s", gid, msg);
+				prosody.shutdown("Failed to change group to %s", gid);
 			end
 		end
 		if uid then
 			local success, msg = pposix.setuid(uid);
 			if success then
-				module:log("debug", "Changed user to "..uid.." successfully.");
+				module:log("debug", "Changed user to %s successfully.", uid);
 			else
-				module:log("error", "Failed to change user to "..uid..". Error: "..msg);
-				prosody.shutdown("Failed to change user to "..uid);
+				module:log("error", "Failed to change user to %s. Error: %s", uid, msg);
+				prosody.shutdown("Failed to change user to %s", uid);
 			end
 		end
 	end);
--- a/plugins/mod_presence.lua	Sat Jun 09 02:27:44 2012 +0200
+++ b/plugins/mod_presence.lua	Sat Jul 28 01:14:31 2012 +0100
@@ -14,6 +14,7 @@
 local s_find = string.find;
 local tonumber = tonumber;
 
+local core_post_stanza = prosody.core_post_stanza;
 local st = require "util.stanza";
 local jid_split = require "util.jid".split;
 local jid_bare = require "util.jid".bare;
@@ -160,7 +161,7 @@
 			end
 		end
 	end
-	log("debug", "broadcasted presence of "..count.." resources from "..user.."@"..host.." to "..jid);
+	log("debug", "broadcasted presence of %d resources from %s@%s to %s", count, user, host, jid);
 	return count;
 end
 
@@ -169,7 +170,7 @@
 	if to_bare == from_bare then return; end -- No self contacts
 	local st_from, st_to = stanza.attr.from, stanza.attr.to;
 	stanza.attr.from, stanza.attr.to = from_bare, to_bare;
-	log("debug", "outbound presence "..stanza.attr.type.." from "..from_bare.." for "..to_bare);
+	log("debug", "outbound presence %s from %s for %s", stanza.attr.type, from_bare, to_bare);
 	if stanza.attr.type == "probe" then
 		stanza.attr.from, stanza.attr.to = st_from, st_to;
 		return;
@@ -214,7 +215,7 @@
 	local node, host = jid_split(to_bare);
 	local st_from, st_to = stanza.attr.from, stanza.attr.to;
 	stanza.attr.from, stanza.attr.to = from_bare, to_bare;
-	log("debug", "inbound presence "..stanza.attr.type.." from "..from_bare.." for "..to_bare);
+	log("debug", "inbound presence %s from %s for %s", stanza.attr.type, from_bare, to_bare);
 	
 	if stanza.attr.type == "probe" then
 		local result, err = rostermanager.is_contact_subscribed(node, host, from_bare);
@@ -352,13 +353,15 @@
 	-- Send unavailable presence
 	if session.presence then
 		local pres = st.presence{ type = "unavailable" };
-		if not(err) or err == "closed" then err = "connection closed"; end
-		pres:tag("status"):text("Disconnected: "..err):up();
+		if err then
+			pres:tag("status"):text("Disconnected: "..err):up();
+		end
 		session:dispatch_stanza(pres);
 	elseif session.directed then
 		local pres = st.presence{ type = "unavailable", from = session.full_jid };
-		if not(err) or err == "closed" then err = "connection closed"; end
-		pres:tag("status"):text("Disconnected: "..err):up();
+		if err then
+			pres:tag("status"):text("Disconnected: "..err):up();
+		end
 		for jid in pairs(session.directed) do
 			pres.attr.to = jid;
 			core_post_stanza(session, pres, true);
--- a/plugins/mod_pubsub.lua	Sat Jun 09 02:27:44 2012 +0200
+++ b/plugins/mod_pubsub.lua	Sat Jul 28 01:14:31 2012 +0100
@@ -201,7 +201,7 @@
 	for jid in pairs(jids) do
 		module:log("debug", "Sending notification to %s", jid);
 		message.attr.to = jid;
-		core_post_stanza(hosts[module.host], message);
+		module:send(message);
 	end
 end
 
--- a/plugins/mod_register.lua	Sat Jun 09 02:27:44 2012 +0200
+++ b/plugins/mod_register.lua	Sat Jul 28 01:14:31 2012 +0100
@@ -120,10 +120,10 @@
 			for jid, item in pairs(roster) do
 				if jid and jid ~= "pending" then
 					if item.subscription == "both" or item.subscription == "from" or (roster.pending and roster.pending[jid]) then
-						core_post_stanza(hosts[host], st.presence({type="unsubscribed", from=bare, to=jid}));
+						module:send(st.presence({type="unsubscribed", from=bare, to=jid}));
 					end
 					if item.subscription == "both" or item.subscription == "to" or item.ask then
-						core_post_stanza(hosts[host], st.presence({type="unsubscribe", from=bare, to=jid}));
+						module:send(st.presence({type="unsubscribe", from=bare, to=jid}));
 					end
 				end
 			end
--- a/plugins/mod_roster.lua	Sat Jun 09 02:27:44 2012 +0200
+++ b/plugins/mod_roster.lua	Sat Jul 28 01:14:31 2012 +0100
@@ -18,7 +18,7 @@
 local rm_remove_from_roster = require "core.rostermanager".remove_from_roster;
 local rm_add_to_roster = require "core.rostermanager".add_to_roster;
 local rm_roster_push = require "core.rostermanager".roster_push;
-local core_post_stanza = core_post_stanza;
+local core_post_stanza = prosody.core_post_stanza;
 
 module:add_feature("jabber:iq:roster");
 
--- a/plugins/mod_s2s/mod_s2s.lua	Sat Jun 09 02:27:44 2012 +0200
+++ b/plugins/mod_s2s/mod_s2s.lua	Sat Jul 28 01:14:31 2012 +0100
@@ -10,7 +10,7 @@
 
 local prosody = prosody;
 local hosts = prosody.hosts;
-local core_process_stanza = core_process_stanza;
+local core_process_stanza = prosody.core_process_stanza;
 
 local tostring, type = tostring, type;
 local t_insert = table.insert;
@@ -30,7 +30,8 @@
 
 local s2sout = module:require("s2sout");
 
-local connect_timeout = module:get_option_number("s2s_timeout", 60);
+local connect_timeout = module:get_option_number("s2s_timeout", 90);
+local stream_close_timeout = module:get_option_number("s2s_close_timeout", 5);
 
 local sessions = module:shared("sessions");
 
@@ -97,9 +98,10 @@
 				log("error", "WARNING! This might, possibly, be a bug, but it might not...");
 				log("error", "We are going to send from %s instead of %s", tostring(host.from_host), tostring(from_host));
 			end
-			host.sends2s(stanza);
-			host.log("debug", "stanza sent over "..host.type);
-			return true;
+			if host.sends2s(stanza) then
+				host.log("debug", "stanza sent over %s", host.type);
+				return true;
+			end
 		end
 	end
 end
@@ -288,19 +290,7 @@
 
 function stream_callbacks.streamclosed(session)
 	(session.log or log)("debug", "Received </stream:stream>");
-	session:close();
-end
-
-function stream_callbacks.streamdisconnected(session, err)
-	if err and err ~= "closed" and session.direction == "outgoing" and session.notopen then
-		(session.log or log)("debug", "s2s connection attempt failed: %s", err);
-		if s2sout.attempt_connection(session, err) then
-			(session.log or log)("debug", "...so we're going to try another target");
-			return true; -- 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 or "closed"));
-	s2s_destroy_session(session, err);
+	session:close(false);
 end
 
 function stream_callbacks.error(session, error, data)
@@ -352,9 +342,9 @@
 			session.sends2s("<?xml version='1.0'?>");
 			session.sends2s(st.stanza("stream:stream", default_stream_attr):top_tag());
 		end
-		if reason then
+		if reason then -- nil == no err, initiated by us, false == initiated by remote
 			if type(reason) == "string" then -- assume stream error
-				log("info", "Disconnecting %s[%s], <stream:error> is: %s", session.host or "(unknown host)", session.type, reason);
+				log("debug", "Disconnecting %s[%s], <stream:error> is: %s", session.host or "(unknown host)", session.type, reason);
 				session.sends2s(st.stanza("stream:error"):tag(reason, {xmlns = 'urn:ietf:params:xml:ns:xmpp-streams' }));
 			elseif type(reason) == "table" then
 				if reason.condition then
@@ -365,20 +355,35 @@
 					if reason.extra then
 						stanza:add_child(reason.extra);
 					end
-					log("info", "Disconnecting %s[%s], <stream:error> is: %s", session.host or "(unknown host)", session.type, tostring(stanza));
+					log("debug", "Disconnecting %s[%s], <stream:error> is: %s", session.host or "(unknown host)", session.type, tostring(stanza));
 					session.sends2s(stanza);
 				elseif reason.name then -- a stanza
-					log("info", "Disconnecting %s->%s[%s], <stream:error> is: %s", session.from_host or "(unknown host)", session.to_host or "(unknown host)", session.type, tostring(reason));
+					log("debug", "Disconnecting %s->%s[%s], <stream:error> is: %s", session.from_host or "(unknown host)", session.to_host or "(unknown host)", session.type, tostring(reason));
 					session.sends2s(reason);
 				end
 			end
 		end
+
 		session.sends2s("</stream:stream>");
-		if session.notopen or not session.conn:close() then
-			session.conn:close(true); -- Force FIXME: timer?
+		function session.sends2s() return false; end
+		
+		local reason = remote_reason or (reason and (reason.text or reason.condition)) or reason;
+		session.log("info", "%s s2s stream %s->%s closed: %s", session.direction, session.from_host or "(unknown host)", session.to_host or "(unknown host)", reason or "stream closed");
+		
+		-- Authenticated incoming stream may still be sending us stanzas, so wait for </stream:stream> from remote
+		local conn = session.conn;
+		if reason == nil and not session.notopen and session.type == "s2sin" then
+			add_task(stream_close_timeout, function ()
+				if not session.destroyed then
+					session.log("warn", "Failed to receive a stream close response, closing connection anyway...");
+					s2s_destroy_session(session, reason);
+					conn:close();
+				end
+			end);
+		else
+			s2s_destroy_session(session, reason);
+			conn:close(); -- Close immediately, as this is an outgoing connection or is not authed
 		end
-		session.conn:close();
-		listener.ondisconnect(session.conn, remote_reason or (reason and (reason.text or reason.condition)) or reason or "stream closed");
 	end
 end
 
@@ -413,11 +418,9 @@
 		return handlestanza(session, stanza);
 	end
 
-	local conn = session.conn;
 	add_task(connect_timeout, function ()
-		if session.conn ~= conn or session.connecting
-		or session.type == "s2sin" or session.type == "s2sout" then
-			return; -- Ok, we're connect[ed|ing]
+		if session.type == "s2sin" or session.type == "s2sout" then
+			return; -- Ok, we're connected
 		end
 		-- Not connected, need to close session and clean up
 		(session.log or log)("debug", "Destroying incomplete session %s->%s due to inactivity",
@@ -474,11 +477,17 @@
 function listener.ondisconnect(conn, err)
 	local session = sessions[conn];
 	if session then
-		if stream_callbacks.streamdisconnected(session, err) then
-			return; -- Connection lives, for now
+		if err and session.direction == "outgoing" and session.notopen then
+			(session.log or log)("debug", "s2s connection attempt failed: %s", err);
+			if s2sout.attempt_connection(session, err) then
+				(session.log or log)("debug", "...so we're going to try another target");
+				return; -- Session lives for now
+			end
 		end
+		(session.log or log)("debug", "s2s disconnected: %s->%s (%s)", tostring(session.from_host), tostring(session.to_host), tostring(err or "connection closed"));
+		s2s_destroy_session(session, err);
+		sessions[conn] = nil;
 	end
-	sessions[conn] = nil;
 end
 
 function listener.register_outgoing(conn, session)
--- a/plugins/mod_s2s/s2sout.lib.lua	Sat Jun 09 02:27:44 2012 +0200
+++ b/plugins/mod_s2s/s2sout.lib.lua	Sat Jul 28 01:14:31 2012 +0100
@@ -95,14 +95,14 @@
 			handle = nil;
 			host_session.connecting = nil;
 			if answer then
-				log("debug", to_host.." has SRV records, handling...");
+				log("debug", "%s has SRV records, handling...", to_host);
 				local srv_hosts = {};
 				host_session.srv_hosts = srv_hosts;
 				for _, record in ipairs(answer) do
 					t_insert(srv_hosts, record.srv);
 				end
 				if #srv_hosts == 1 and srv_hosts[1].target == "." then
-					log("debug", to_host.." does not provide a XMPP service");
+					log("debug", "%s does not provide a XMPP service", to_host);
 					s2s_destroy_session(host_session, err); -- Nothing to see here
 					return;
 				end
@@ -115,7 +115,7 @@
 					log("debug", "Best record found, will connect to %s:%d", connect_host, connect_port);
 				end
 			else
-				log("debug", to_host.." has no SRV records, falling back to A/AAAA");
+				log("debug", "%s has no SRV records, falling back to A/AAAA", to_host);
 			end
 			-- Try with SRV, or just the plain hostname if no SRV
 			local ok, err = s2sout.try_connect(host_session, connect_host, connect_port);
@@ -170,92 +170,91 @@
 		local IPs = {};
 		host_session.ip_hosts = IPs;
 		local handle4, handle6;
-		local has_other = false;
+		local have_other_result = not(has_ipv4) or not(has_ipv6) or false;
 
 		if has_ipv4 then
-		handle4 = adns.lookup(function (reply, err)
-			handle4 = nil;
-
-			-- COMPAT: This is a compromise for all you CNAME-(ab)users :)
-			if not (reply and reply[#reply] and reply[#reply].a) then
-				local count = max_dns_depth;
-				reply = dns.peek(connect_host, "CNAME", "IN");
-				while count > 0 and reply and reply[#reply] and not reply[#reply].a and reply[#reply].cname do
-					log("debug", "Looking up %s (DNS depth is %d)", tostring(reply[#reply].cname), count);
-					reply = dns.peek(reply[#reply].cname, "A", "IN") or dns.peek(reply[#reply].cname, "CNAME", "IN");
-					count = count - 1;
-				end
-			end
-			-- end of CNAME resolving
+			handle4 = adns.lookup(function (reply, err)
+				handle4 = nil;
 
-			if reply and reply[#reply] and reply[#reply].a then
-				for _, ip in ipairs(reply) do
-					log("debug", "DNS reply for %s gives us %s", connect_host, ip.a);
-					IPs[#IPs+1] = new_ip(ip.a, "IPv4");
+				-- COMPAT: This is a compromise for all you CNAME-(ab)users :)
+				if not (reply and reply[#reply] and reply[#reply].a) then
+					local count = max_dns_depth;
+					reply = dns.peek(connect_host, "CNAME", "IN");
+					while count > 0 and reply and reply[#reply] and not reply[#reply].a and reply[#reply].cname do
+						log("debug", "Looking up %s (DNS depth is %d)", tostring(reply[#reply].cname), count);
+						reply = dns.peek(reply[#reply].cname, "A", "IN") or dns.peek(reply[#reply].cname, "CNAME", "IN");
+						count = count - 1;
+					end
 				end
-			end
+				-- end of CNAME resolving
 
-			if has_other then
-				if #IPs > 0 then
-					rfc3484_dest(host_session.ip_hosts, sources);
-					for i = 1, #IPs do
-						IPs[i] = {ip = IPs[i], port = connect_port};
-					end
-					host_session.ip_choice = 0;
-					s2sout.try_next_ip(host_session);
-				else
-					log("debug", "DNS lookup failed to get a response for %s", connect_host);
-					host_session.ip_hosts = nil;
-					if not s2sout.attempt_connection(host_session, "name resolution failed") then -- Retry if we can
-						log("debug", "No other records to try for %s - destroying", host_session.to_host);
-						err = err and (": "..err) or "";
-						s2s_destroy_session(host_session, "DNS resolution failed"..err); -- End of the line, we can't
+				if reply and reply[#reply] and reply[#reply].a then
+					for _, ip in ipairs(reply) do
+						log("debug", "DNS reply for %s gives us %s", connect_host, ip.a);
+						IPs[#IPs+1] = new_ip(ip.a, "IPv4");
 					end
 				end
-			else
-				has_other = true;
-			end
-		end, connect_host, "A", "IN");
+
+				if have_other_result then
+					if #IPs > 0 then
+						rfc3484_dest(host_session.ip_hosts, sources);
+						for i = 1, #IPs do
+							IPs[i] = {ip = IPs[i], port = connect_port};
+						end
+						host_session.ip_choice = 0;
+						s2sout.try_next_ip(host_session);
+					else
+						log("debug", "DNS lookup failed to get a response for %s", connect_host);
+						host_session.ip_hosts = nil;
+						if not s2sout.attempt_connection(host_session, "name resolution failed") then -- Retry if we can
+							log("debug", "No other records to try for %s - destroying", host_session.to_host);
+							err = err and (": "..err) or "";
+							s2s_destroy_session(host_session, "DNS resolution failed"..err); -- End of the line, we can't
+						end
+					end
+				else
+					have_other_result = true;
+				end
+			end, connect_host, "A", "IN");
 		else
-			has_other = true;
+			have_other_result = true;
 		end
 
 		if has_ipv6 then
-		handle6 = adns.lookup(function (reply, err)
-			handle6 = nil;
-
-			if reply and reply[#reply] and reply[#reply].aaaa then
-				for _, ip in ipairs(reply) do
-					log("debug", "DNS reply for %s gives us %s", connect_host, ip.aaaa);
-					IPs[#IPs+1] = new_ip(ip.aaaa, "IPv6");
-				end
-			end
+			handle6 = adns.lookup(function (reply, err)
+				handle6 = nil;
 
-			if has_other then
-				if #IPs > 0 then
-					rfc3484_dest(host_session.ip_hosts, sources);
-					for i = 1, #IPs do
-						IPs[i] = {ip = IPs[i], port = connect_port};
-					end
-					host_session.ip_choice = 0;
-					s2sout.try_next_ip(host_session);
-				else
-					log("debug", "DNS lookup failed to get a response for %s", connect_host);
-					host_session.ip_hosts = nil;
-					if not s2sout.attempt_connection(host_session, "name resolution failed") then -- Retry if we can
-						log("debug", "No other records to try for %s - destroying", host_session.to_host);
-						err = err and (": "..err) or "";
-						s2s_destroy_session(host_session, "DNS resolution failed"..err); -- End of the line, we can't
+				if reply and reply[#reply] and reply[#reply].aaaa then
+					for _, ip in ipairs(reply) do
+						log("debug", "DNS reply for %s gives us %s", connect_host, ip.aaaa);
+						IPs[#IPs+1] = new_ip(ip.aaaa, "IPv6");
 					end
 				end
-			else
-				has_other = true;
-			end
-		end, connect_host, "AAAA", "IN");
+
+				if have_other_result then
+					if #IPs > 0 then
+						rfc3484_dest(host_session.ip_hosts, sources);
+						for i = 1, #IPs do
+							IPs[i] = {ip = IPs[i], port = connect_port};
+						end
+						host_session.ip_choice = 0;
+						s2sout.try_next_ip(host_session);
+					else
+						log("debug", "DNS lookup failed to get a response for %s", connect_host);
+						host_session.ip_hosts = nil;
+						if not s2sout.attempt_connection(host_session, "name resolution failed") then -- Retry if we can
+							log("debug", "No other records to try for %s - destroying", host_session.to_host);
+							err = err and (": "..err) or "";
+							s2s_destroy_session(host_session, "DNS resolution failed"..err); -- End of the line, we can't
+						end
+					end
+				else
+					have_other_result = true;
+				end
+			end, connect_host, "AAAA", "IN");
 		else
-			has_other = true;
+			have_other_result = true;
 		end
-
 		return true;
 	elseif host_session.ip_hosts and #host_session.ip_hosts > host_session.ip_choice then -- Not our first attempt, and we also have IPs left to try
 		s2sout.try_next_ip(host_session);
--- a/plugins/mod_saslauth.lua	Sat Jun 09 02:27:44 2012 +0200
+++ b/plugins/mod_saslauth.lua	Sat Jul 28 01:14:31 2012 +0100
@@ -208,7 +208,7 @@
 		session.sasl_handler = nil; -- allow starting a new SASL negotiation before completing an old one
 	end
 	if not session.sasl_handler then
-		session.sasl_handler = usermanager_get_sasl_handler(module.host);
+		session.sasl_handler = usermanager_get_sasl_handler(module.host, session);
 	end
 	local mechanism = stanza.attr.mechanism;
 	if not session.secure and (secure_auth_only or (mechanism == "PLAIN" and not allow_unencrypted_plain_auth)) then
@@ -246,7 +246,7 @@
 		if secure_auth_only and not origin.secure then
 			return;
 		end
-		origin.sasl_handler = usermanager_get_sasl_handler(module.host);
+		origin.sasl_handler = usermanager_get_sasl_handler(module.host, origin);
 		local mechanisms = st.stanza("mechanisms", mechanisms_attr);
 		for mechanism in pairs(origin.sasl_handler:mechanisms()) do
 			if mechanism ~= "PLAIN" or origin.secure or allow_unencrypted_plain_auth then
--- a/plugins/mod_vcard.lua	Sat Jun 09 02:27:44 2012 +0200
+++ b/plugins/mod_vcard.lua	Sat Jul 28 01:14:31 2012 +0100
@@ -46,13 +46,8 @@
 module:hook("iq/bare/vcard-temp:vCard", handle_vcard);
 module:hook("iq/host/vcard-temp:vCard", handle_vcard);
 
--- COMPAT: https://support.process-one.net/browse/EJAB-1045
-if module:get_option("vcard_compatibility") then
-	module:hook("iq/full", function(data)
-		local stanza = data.stanza;
-		local payload = stanza.tags[1];
-		if stanza.attr.type == "get" and payload.name == "vCard" and payload.attr.xmlns == "vcard-temp" then
-			return handle_vcard(data);
-		end
-	end, 1);
+-- COMPAT w/0.8
+if module:get_option("vcard_compatibility") ~= nil then
+	module:log("error", "The vcard_compatibility option has been removed, see"..
+		"mod_compat_vcard in prosody-modules if you still need this.");
 end
--- a/plugins/mod_watchregistrations.lua	Sat Jun 09 02:27:44 2012 +0200
+++ b/plugins/mod_watchregistrations.lua	Sat Jul 28 01:14:31 2012 +0100
@@ -25,6 +25,6 @@
 	for jid in registration_watchers do
 		module:log("debug", "Notifying %s", jid);
 		message.attr.to = jid;
-		core_route_stanza(hosts[host], message);
+		module:send(message);
 	end
 end);
--- a/plugins/mod_welcome.lua	Sat Jun 09 02:27:44 2012 +0200
+++ b/plugins/mod_welcome.lua	Sat Jul 28 01:14:31 2012 +0100
@@ -16,6 +16,6 @@
 		local welcome_stanza =
 			st.message({ to = user.username.."@"..user.host, from = host })
 				:tag("body"):text(welcome_text:gsub("$(%w+)", user));
-		core_route_stanza(hosts[host], welcome_stanza);
+		module:send(welcome_stanza);
 		module:log("debug", "Welcomed user %s@%s", user.username, user.host);
 	end);
--- a/plugins/muc/mod_muc.lua	Sat Jun 09 02:27:44 2012 +0200
+++ b/plugins/muc/mod_muc.lua	Sat Jul 28 01:14:31 2012 +0100
@@ -29,11 +29,11 @@
 local uuid_gen = require "util.uuid".generate;
 local datamanager = require "util.datamanager";
 local um_is_admin = require "core.usermanager".is_admin;
+local hosts = hosts;
 
 rooms = {};
 local rooms = rooms;
 local persistent_rooms = datamanager.load(nil, muc_host, "persistent") or {};
-local component = hosts[module.host];
 
 -- Configurable options
 local max_history_messages = module:get_option_number("max_history_messages");
@@ -42,7 +42,7 @@
 	return um_is_admin(jid, module.host);
 end
 
-local function room_route_stanza(room, stanza) core_post_stanza(component, stanza); end
+local function room_route_stanza(room, stanza) module:send(stanza); end
 local function room_save(room, forced)
 	local node = jid_split(room.jid);
 	persistent_rooms[room.jid] = room._data.persistent;
@@ -65,19 +65,27 @@
 	if forced then datamanager.store(nil, muc_host, "persistent", persistent_rooms); end
 end
 
+local persistent_errors = false;
 for jid in pairs(persistent_rooms) do
 	local node = jid_split(jid);
-	local data = datamanager.load(node, muc_host, "config") or {};
-	local room = muc_new_room(jid, {
-		max_history_length = max_history_messages;
-	});
-	room._data = data._data;
-	room._data.max_history_length = max_history_messages; -- Overwrite old max_history_length in data with current settings
-	room._affiliations = data._affiliations;
-	room.route_stanza = room_route_stanza;
-	room.save = room_save;
-	rooms[jid] = room;
+	local data = datamanager.load(node, muc_host, "config");
+	if data then
+		local room = muc_new_room(jid, {
+			max_history_length = max_history_messages;
+		});
+		room._data = data._data;
+		room._data.max_history_length = max_history_messages; -- Overwrite old max_history_length in data with current settings
+		room._affiliations = data._affiliations;
+		room.route_stanza = room_route_stanza;
+		room.save = room_save;
+		rooms[jid] = room;
+	else -- missing room data
+		persistent_rooms[jid] = nil;
+		module:log("error", "Missing data for room '%s', removing from persistent room list", jid);
+		persistent_errors = true;
+	end
 end
+if persistent_errors then datamanager.store(nil, muc_host, "persistent", persistent_rooms); end
 
 local host_room = muc_new_room(muc_host, {
 	max_history_length = max_history_messages;
@@ -160,11 +168,11 @@
 
 hosts[module.host].send = function(stanza) -- FIXME do a generic fix
 	if stanza.attr.type == "result" or stanza.attr.type == "error" then
-		core_post_stanza(component, stanza);
+		module:send(stanza);
 	else error("component.send only supports result and error stanzas at the moment"); end
 end
 
-prosody.hosts[module:get_host()].muc = { rooms = rooms };
+hosts[module:get_host()].muc = { rooms = rooms };
 
 module.save = function()
 	return {rooms = rooms};
@@ -180,5 +188,5 @@
 		room.save = room_save;
 		rooms[jid] = room;
 	end
-	prosody.hosts[module:get_host()].muc = { rooms = rooms };
+	hosts[module:get_host()].muc = { rooms = rooms };
 end
--- a/plugins/muc/muc.lib.lua	Sat Jun 09 02:27:44 2012 +0200
+++ b/plugins/muc/muc.lib.lua	Sat Jul 28 01:14:31 2012 +0100
@@ -856,7 +856,7 @@
 		if not occupant then -- not in room
 			origin.send(st.error_reply(stanza, "cancel", "not-acceptable"));
 		elseif occupant.role == "visitor" then
-			origin.send(st.error_reply(stanza, "cancel", "forbidden"));
+			origin.send(st.error_reply(stanza, "auth", "forbidden"));
 		else
 			local from = stanza.attr.from;
 			stanza.attr.from = current_nick;
@@ -867,7 +867,7 @@
 					self:set_subject(current_nick, subject); -- TODO use broadcast_message_stanza
 				else
 					stanza.attr.from = from;
-					origin.send(st.error_reply(stanza, "cancel", "forbidden"));
+					origin.send(st.error_reply(stanza, "auth", "forbidden"));
 				end
 			else
 				self:broadcast_message(stanza, self:get_historylength() > 0);
--- a/prosodyctl	Sat Jun 09 02:27:44 2012 +0200
+++ b/prosodyctl	Sat Jul 28 01:14:31 2012 +0100
@@ -688,7 +688,7 @@
 		show_message("There was a problem, see OpenSSL output");
 	else
 		show_usage("cert key HOSTNAME <bits>", "Generates a RSA key named HOSTNAME.key\n "
-		.."Promps for a key size if none given")
+		.."Prompts for a key size if none given")
 	end
 end
 
--- a/util-src/pposix.c	Sat Jun 09 02:27:44 2012 +0200
+++ b/util-src/pposix.c	Sat Jul 28 01:14:31 2012 +0100
@@ -34,6 +34,11 @@
 #include "lua.h"
 #include "lauxlib.h"
 
+#if (defined(_SVID_SOURCE) && !defined(WITHOUT_MALLINFO))
+	#include <malloc.h>
+	#define WITH_MALLINFO
+#endif
+
 /* Daemonization support */
 
 static int lc_daemonize(lua_State *L)
@@ -581,6 +586,62 @@
 	return 1;
 }
 
+int lc_setenv(lua_State* L)
+{
+	const char *var = luaL_checkstring(L, 1);
+	const char *value;
+
+	/* If the second argument is nil or nothing, unset the var */
+	if(lua_isnoneornil(L, 2))
+	{
+		if(unsetenv(var) != 0)
+		{
+			lua_pushnil(L);
+			lua_pushstring(L, strerror(errno));
+			return 2;
+		}
+		lua_pushboolean(L, 1);
+		return 1;
+	}
+
+	value = luaL_checkstring(L, 2);
+
+	if(setenv(var, value, 1) != 0)
+	{
+		lua_pushnil(L);
+		lua_pushstring(L, strerror(errno));
+		return 2;
+	}
+
+	lua_pushboolean(L, 1);
+	return 1;
+}
+
+#ifdef WITH_MALLINFO
+int lc_meminfo(lua_State* L)
+{
+	struct mallinfo info = mallinfo();
+	lua_newtable(L);
+	/* This is the total size of memory allocated with sbrk by malloc, in bytes. */
+	lua_pushinteger(L, info.arena);
+	lua_setfield(L, -2, "allocated");
+	/* This is the total size of memory allocated with mmap, in bytes. */
+	lua_pushinteger(L, info.hblkhd);
+	lua_setfield(L, -2, "allocated_mmap");
+	/* This is the total size of memory occupied by chunks handed out by malloc. */
+	lua_pushinteger(L, info.uordblks);
+	lua_setfield(L, -2, "used");
+	/* This is the total size of memory occupied by free (not in use) chunks. */
+	lua_pushinteger(L, info.fordblks);
+	lua_setfield(L, -2, "unused");
+	/* This is the size of the top-most releasable chunk that normally borders the
+	   end of the heap (i.e., the high end of the virtual address space's data segment). */
+	lua_pushinteger(L, info.keepcost);
+	lua_setfield(L, -2, "returnable");
+	return 1;
+}
+#endif
+
 /* Register functions */
 
 int luaopen_util_pposix(lua_State *L)
@@ -612,6 +673,12 @@
 
 		{ "uname", lc_uname },
 
+		{ "setenv", lc_setenv },
+
+#ifdef WITH_MALLINFO
+		{ "meminfo", lc_meminfo },
+#endif
+
 		{ NULL, NULL }
 	};
 
--- a/util/dataforms.lua	Sat Jun 09 02:27:44 2012 +0200
+++ b/util/dataforms.lua	Sat Jul 28 01:14:31 2012 +0100
@@ -185,7 +185,7 @@
 	function (field_tag, required)
 		local result = {};
 		for value in field_tag:childtags("value") do
-			result[#result+1] = value;
+			result[#result+1] = value:get_text();
 		end
 		return result, (required and #result == 0 and "Required value missing" or nil);
 	end
@@ -202,10 +202,10 @@
 field_readers["list-single"] =
 	field_readers["text-single"];
 
-	local boolean_values = {
-		["1"] = true, ["true"] = true,
-		["0"] = false, ["false"] = false,
-	};
+local boolean_values = {
+	["1"] = true, ["true"] = true,
+	["0"] = false, ["false"] = false,
+};
 
 field_readers["boolean"] =
 	function (field_tag, required)
--- a/util/datamanager.lua	Sat Jun 09 02:27:44 2012 +0200
+++ b/util/datamanager.lua	Sat Jul 28 01:14:31 2012 +0100
@@ -116,18 +116,18 @@
 	if not data then
 		local mode = lfs.attributes(getpath(username, host, datastore), "mode");
 		if not mode then
-			log("debug", "Assuming empty "..datastore.." storage ('"..ret.."') for user: "..(username or "nil").."@"..(host or "nil"));
+			log("debug", "Assuming empty %s storage ('%s') for user: %s@%s", datastore, ret, username or "nil", host or "nil");
 			return nil;
 		else -- file exists, but can't be read
 			-- TODO more detailed error checking and logging?
-			log("error", "Failed to load "..datastore.." storage ('"..ret.."') for user: "..(username or "nil").."@"..(host or "nil"));
+			log("error", "Failed to load %s storage ('%s') for user: %s@%s", datastore, ret, username or "nil", host or "nil");
 			return nil, "Error reading storage";
 		end
 	end
 
 	local success, ret = pcall(data);
 	if not success then
-		log("error", "Unable to load "..datastore.." storage ('"..ret.."') for user: "..(username or "nil").."@"..(host or "nil"));
+		log("error", "Unable to load %s storage ('%s') for user: %s@%s", datastore, ret, username or "nil", host or "nil");
 		return nil, "Error reading storage";
 	end
 	return ret;
@@ -146,7 +146,7 @@
 	-- save the datastore
 	local f, msg = io_open(getpath(username, host, datastore, nil, true), "w+");
 	if not f then
-		log("error", "Unable to write to "..datastore.." storage ('"..msg.."') for user: "..(username or "nil").."@"..(host or "nil"));
+		log("error", "Unable to write to %s storage ('%s') for user: %s@%s", datastore, msg, username or "nil", host or "nil");
 		return nil, "Error saving to storage";
 	end
 	f:write("return ");
@@ -167,7 +167,7 @@
 	-- save the datastore
 	local f, msg = io_open(getpath(username, host, datastore, "list", true), "a+");
 	if not f then
-		log("error", "Unable to write to "..datastore.." storage ('"..msg.."') for user: "..(username or "nil").."@"..(host or "nil"));
+		log("error", "Unable to write to %s storage ('%s') for user: %s@%s", datastore, msg, username or "nil", host or "nil");
 		return;
 	end
 	f:write("item(");
@@ -185,7 +185,7 @@
 	-- save the datastore
 	local f, msg = io_open(getpath(username, host, datastore, "list", true), "w+");
 	if not f then
-		log("error", "Unable to write to "..datastore.." storage ('"..msg.."') for user: "..(username or "nil").."@"..(host or "nil"));
+		log("error", "Unable to write to %s storage ('%s') for user: %s@%s", datastore, msg, username or "nil", host or "nil");
 		return;
 	end
 	for _, d in ipairs(data) do
@@ -209,18 +209,18 @@
 	if not data then
 		local mode = lfs.attributes(getpath(username, host, datastore, "list"), "mode");
 		if not mode then
-			log("debug", "Assuming empty "..datastore.." storage ('"..ret.."') for user: "..(username or "nil").."@"..(host or "nil"));
+			log("debug", "Assuming empty %s storage ('%s') for user: %s@%s", datastore, ret, username or "nil", host or "nil");
 			return nil;
 		else -- file exists, but can't be read
 			-- TODO more detailed error checking and logging?
-			log("error", "Failed to load "..datastore.." storage ('"..ret.."') for user: "..(username or "nil").."@"..(host or "nil"));
+			log("error", "Failed to load %s storage ('%s') for user: %s@%s", datastore, ret, username or "nil", host or "nil");
 			return nil, "Error reading storage";
 		end
 	end
 
 	local success, ret = pcall(data);
 	if not success then
-		log("error", "Unable to load "..datastore.." storage ('"..ret.."') for user: "..(username or "nil").."@"..(host or "nil"));
+		log("error", "Unable to load %s storage ('%s') for user: %s@%s", datastore, ret, username or "nil", host or "nil");
 		return nil, "Error reading storage";
 	end
 	return items;
--- a/util/logger.lua	Sat Jun 09 02:27:44 2012 +0200
+++ b/util/logger.lua	Sat Jul 28 01:14:31 2012 +0100
@@ -23,8 +23,6 @@
 	local log_warn = make_logger(name, "warn");
 	local log_error = make_logger(name, "error");
 
-	--name = nil; -- While this line is not commented, will automatically fill in file/line number info
-	local namelen = #name;
 	return function (level, message, ...)
 			if level == "debug" then
 				return log_debug(message, ...);
--- a/util/sasl.lua	Sat Jun 09 02:27:44 2012 +0200
+++ b/util/sasl.lua	Sat Jul 28 01:14:31 2012 +0100
@@ -35,7 +35,7 @@
 local backend_mechanism = {};
 
 -- register a new SASL mechanims
-local function registerMechanism(name, backends, f)
+function registerMechanism(name, backends, f)
 	assert(type(name) == "string", "Parameter name MUST be a string.");
 	assert(type(backends) == "string" or type(backends) == "table", "Parameter backends MUST be either a string or a table.");
 	assert(type(f) == "function", "Parameter f MUST be a function.");
--- a/util/sasl_cyrus.lua	Sat Jun 09 02:27:44 2012 +0200
+++ b/util/sasl_cyrus.lua	Sat Jul 28 01:14:31 2012 +0100
@@ -78,11 +78,15 @@
 end
 
 -- create a new SASL object which can be used to authenticate clients
-function new(realm, service_name, app_name)
+-- host_fqdn may be nil in which case gethostname() gives the value. 
+--      For GSSAPI, this determines the hostname in the service ticket (after
+--      reverse DNS canonicalization, only if [libdefaults] rdns = true which
+--      is the default).  
+function new(realm, service_name, app_name, host_fqdn)
 
 	init(app_name or service_name);
 
-	local st, ret = pcall(cyrussasl.server_new, service_name, nil, realm, nil, nil)
+	local st, ret = pcall(cyrussasl.server_new, service_name, host_fqdn, realm, nil, nil)
 	if not st then
 		log("error", "Creating SASL server connection failed: %s", ret);
 		return nil;
--- a/util/stanza.lua	Sat Jun 09 02:27:44 2012 +0200
+++ b/util/stanza.lua	Sat Jul 28 01:14:31 2012 +0100
@@ -133,14 +133,14 @@
 end
 
 function stanza_mt:childtags(name, xmlns)
-	xmlns = xmlns or self.attr.xmlns;
 	local tags = self.tags;
 	local start_i, max_i = 1, #tags;
 	return function ()
 		for i = start_i, max_i do
 			local v = tags[i];
 			if (not name or v.name == name)
-			and (not xmlns or xmlns == v.attr.xmlns) then
+			and ((not xmlns and self.attr.xmlns == v.attr.xmlns)
+				or v.attr.xmlns == xmlns) then
 				start_i = i+1;
 				return v;
 			end
--- a/util/throttle.lua	Sat Jun 09 02:27:44 2012 +0200
+++ b/util/throttle.lua	Sat Jul 28 01:14:31 2012 +0100
@@ -1,6 +1,7 @@
 
 local gettime = require "socket".gettime;
 local setmetatable = setmetatable;
+local floor = math.floor;
 
 module "throttle"
 
@@ -11,7 +12,7 @@
 	local newt = gettime();
 	local elapsed = newt - self.t;
 	self.t = newt;
-	local balance = self.rate * elapsed + self.balance;
+	local balance = floor(self.rate * elapsed) + self.balance;
 	if balance > self.max then
 		self.balance = self.max;
 	else