Merge 0.6.2/waqas with 0.6.2/MattJ

Wed, 03 Mar 2010 22:05:05 +0000

author
Matthew Wild <mwild1@gmail.com>
date
Wed, 03 Mar 2010 22:05:05 +0000
changeset 2877
1edeb8fe7d14
parent 2813
46dfcc33ea9e (diff)
parent 2876
fa84451e9b35 (current diff)
child 2878
9384ee36fc03
child 2882
4e72048d4a24

Merge 0.6.2/waqas with 0.6.2/MattJ

core/hostmanager.lua file | annotate | diff | comparison | revisions
core/s2smanager.lua file | annotate | diff | comparison | revisions
net/dns.lua file | annotate | diff | comparison | revisions
net/httpserver.lua file | annotate | diff | comparison | revisions
net/xmppserver_listener.lua file | annotate | diff | comparison | revisions
plugins/mod_debug.lua file | annotate | diff | comparison | revisions
plugins/mod_offline.lua file | annotate | diff | comparison | revisions
plugins/mod_saslauth.lua file | annotate | diff | comparison | revisions
plugins/mod_selftests.lua file | annotate | diff | comparison | revisions
plugins/mod_tls.lua file | annotate | diff | comparison | revisions
prosody file | annotate | diff | comparison | revisions
--- a/HACKERS	Thu Mar 04 02:22:45 2010 +0500
+++ b/HACKERS	Wed Mar 03 22:05:05 2010 +0000
@@ -5,6 +5,8 @@
 information on these at http://prosody.im/discuss
 
 Patches are welcome, though before sending we would appreciate if you read 
-docs/coding_style.txt for guidelines on how to format your code, and are 
-comfortable with copyright of contributions being assigned to the core 
-developers.
+docs/coding_style.txt for guidelines on how to format your code, and other tips.
+
+Documentation for developers can be found at http://prosody.im/doc/developers
+
+Have fun :)
--- a/README	Thu Mar 04 02:22:45 2010 +0500
+++ b/README	Wed Mar 03 22:05:05 2010 +0000
@@ -32,6 +32,6 @@
 ## Installation
 
 See the accompanying INSTALL file for help on building Prosody from source. Alternatively 
-see our guide at http://prosody.im/install
+see our guide at http://prosody.im/doc/install
 
 
--- a/TODO	Thu Mar 04 02:22:45 2010 +0500
+++ b/TODO	Wed Mar 03 22:05:05 2010 +0000
@@ -1,5 +1,18 @@
-- Ad-hoc commands
-- Clustering
+== 0.7 ==
+DONE: http://blog.prosody.im/prosody-0-7-0rc1-available-for-testing/
+== 0.8 ==
+- Ad-hoc commands:
+	http://code.google.com/p/prosody-modules/wiki/mod_adhoc
+	http://code.google.com/p/prosody-modules/wiki/mod_adhoc_cmd_admin
+	http://code.google.com/p/prosody-modules/wiki/mod_adhoc_cmd_ping
+	http://code.google.com/p/prosody-modules/wiki/mod_adhoc_cmd_uptime
+	
 - Pubsub
+- Data storage backend abstraction
 
+== 0.9 ==
+- Clustering
 
+== 1.0 ==
+- Web interface?
+- World domination
--- a/core/componentmanager.lua	Thu Mar 04 02:22:45 2010 +0500
+++ b/core/componentmanager.lua	Wed Mar 03 22:05:05 2010 +0000
@@ -14,9 +14,10 @@
 local fire_event = require "core.eventmanager".fire_event;
 local events_new = require "util.events".new;
 local st = require "util.stanza";
-local hosts = hosts;
+local prosody, hosts = prosody, prosody.hosts;
+local ssl = ssl;
 
-local pairs, type, tostring = pairs, type, tostring;
+local pairs, setmetatable, type, tostring = pairs, setmetatable, type, tostring;
 
 local components = {};
 
@@ -73,18 +74,24 @@
 
 function create_component(host, component, events)
 	-- TODO check for host well-formedness
-	local ssl_ctx;
-	if host then
+	local ssl_ctx, ssl_ctx_in;
+	if host and ssl then
 		-- We need to find SSL context to use...
 		-- Discussion in prosody@ concluded that
 		-- 1 level back is usually enough by default
 		local base_host = host:gsub("^[^%.]+%.", "");
 		if hosts[base_host] then
 			ssl_ctx = hosts[base_host].ssl_ctx;
+			ssl_ctx_in = hosts[base_host].ssl_ctx_in;
+		elseif prosody.global_ssl_ctx then
+			-- We have no cert, and no parent host to borrow a cert from
+			-- Use global/default cert if there is one
+			ssl_ctx = ssl.newcontext(prosody.global_ssl_ctx);
+			ssl_ctx_in = ssl.newcontext(setmetatable({ mode = "server" }, { __index = prosody.global_ssl_ctx }));
 		end
 	end
 	return { type = "component", host = host, connected = true, s2sout = {}, 
-			ssl_ctx = ssl_ctx, events = events or events_new() };
+			ssl_ctx = ssl_ctx, ssl_ctx_in = ssl_ctx_in, events = events or events_new() };
 end
 
 function register_component(host, component, session)
--- a/core/hostmanager.lua	Thu Mar 04 02:22:45 2010 +0500
+++ b/core/hostmanager.lua	Wed Mar 03 22:05:05 2010 +0000
@@ -20,8 +20,8 @@
 local incoming_s2s = _G.prosody.incoming_s2s;
 
 -- These are the defaults if not overridden in the config
-local default_ssl_ctx = { mode = "client", protocol = "sslv23", capath = "/etc/ssl/certs", verify = "none"; };
-local default_ssl_ctx_in = { mode = "server", protocol = "sslv23", capath = "/etc/ssl/certs", verify = "none"; };
+local default_ssl_ctx = { mode = "client", protocol = "sslv23", capath = "/etc/ssl/certs", verify = "none", options = "no_sslv2"; };
+local default_ssl_ctx_in = { mode = "server", protocol = "sslv23", capath = "/etc/ssl/certs", verify = "none", options = "no_sslv2"; };
 
 local log = require "util.logger".init("hostmanager");
 
--- a/core/s2smanager.lua	Thu Mar 04 02:22:45 2010 +0500
+++ b/core/s2smanager.lua	Wed Mar 03 22:05:05 2010 +0000
@@ -54,7 +54,7 @@
 	return a.priority < b.priority or (a.priority == b.priority and a.weight > b.weight);
 end
 
-local function bounce_sendq(session)
+local function bounce_sendq(session, reason)
 	local sendq = session.sendq;
 	if sendq then
 		session.log("info", "sending error replies for "..#sendq.." queued stanzas because of failed outgoing connection to "..tostring(session.to_host));
@@ -72,6 +72,9 @@
 				reply.attr.type = "error";
 				reply:tag("error", {type = "cancel"})
 					:tag("remote-server-not-found", {xmlns = "urn:ietf:params:xml:ns:xmpp-stanzas"}):up();
+				if reason then
+					reply:tag("text", {xmlns = "urn:ietf:params:xml:ns:xmpp-stanzas"}):text("Connection failed: "..reason):up();
+				end
 				core_process_stanza(dummy, reply);
 			end
 			sendq[i] = nil;
@@ -224,7 +227,7 @@
 			if not ok then
 				if not attempt_connection(host_session, err) then
 					-- No more attempts will be made
-					destroy_session(host_session);
+					destroy_session(host_session, err);
 				end
 			end
 		end, "_xmpp-server._tcp."..connect_host..".", "SRV");
@@ -284,7 +287,7 @@
 			log("debug", "DNS lookup failed to get a response for %s", connect_host);
 			if not 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);
-				destroy_session(host_session); -- End of the line, we can't
+				destroy_session(host_session, "DNS resolution failed"); -- End of the line, we can't
 			end
 		end
 	end, connect_host, "A", "IN");
@@ -300,7 +303,7 @@
 end
 
 function make_connect(host_session, connect_host, connect_port)
-	host_session.log("info", "Beginning new connection attempt to %s (%s:%d)", host_session.to_host, connect_host, connect_port);
+	(host_session.log or log)("info", "Beginning new connection attempt to %s (%s:%d)", host_session.to_host, connect_host, connect_port);
 	-- Ok, we're going to try to connect
 	
 	local from_host, to_host = host_session.from_host, host_session.to_host;
@@ -369,17 +372,17 @@
 	
 		session.streamid = uuid_gen();
 		(session.log or log)("debug", "incoming s2s received <stream:stream>");
-		send("<?xml version='1.0'?>");
-		send(stanza("stream:stream", { xmlns='jabber:server', ["xmlns:db"]='jabber:server:dialback', 
-				["xmlns:stream"]='http://etherx.jabber.org/streams', id=session.streamid, from=session.to_host, version=(session.version > 0 and "1.0" or nil) }):top_tag());
 		if session.to_host and not hosts[session.to_host] then
 			-- Attempting to connect to a host we don't serve
 			session:close({ condition = "host-unknown"; text = "This host does not serve "..session.to_host });
 			return;
 		end
+		send("<?xml version='1.0'?>");
+		send(stanza("stream:stream", { xmlns='jabber:server', ["xmlns:db"]='jabber:server:dialback', 
+				["xmlns:stream"]='http://etherx.jabber.org/streams', id=session.streamid, from=session.to_host, to=session.from_host, version=(session.version > 0 and "1.0" or nil) }):top_tag());
 		if session.version >= 1.0 then
 			local features = st.stanza("stream:features");
-							
+			
 			if session.to_host then
 				hosts[session.to_host].events.fire_event("s2s-stream-features", { session = session, features = features });
 			else
@@ -402,7 +405,7 @@
 		if send_buffer and #send_buffer > 0 then
 			log("debug", "Sending s2s send_buffer now...");
 			for i, data in ipairs(send_buffer) do
-				session.sends2s(tostring(data));
+				session.sends2s(data);
 				send_buffer[i] = nil;
 			end
 		end
@@ -446,6 +449,16 @@
 end
 
 function make_authenticated(session, host)
+	if not session.secure then
+		local local_host = session.direction == "incoming" and session.to_host or session.from_host;
+		if config.get(local_host, "core", "s2s_require_encryption") then
+			session:close({
+				condition = "policy-violation",
+				text = "Encrypted server-to-server communication is required but was not "
+				       ..((session.direction == "outgoing" and "offered") or "used")
+			});
+		end
+	end
 	if session.type == "s2sout_unauthed" then
 		session.type = "s2sout";
 	elseif session.type == "s2sin_unauthed" then
@@ -493,12 +506,12 @@
 
 local function null_data_handler(conn, data) log("debug", "Discarding data from destroyed s2s session: %s", data); end
 
-function destroy_session(session)
+function destroy_session(session, reason)
 	(session.log or log)("info", "Destroying "..tostring(session.direction).." session "..tostring(session.from_host).."->"..tostring(session.to_host));
 	
 	if session.direction == "outgoing" then
 		hosts[session.from_host].s2sout[session.to_host] = nil;
-		bounce_sendq(session);
+		bounce_sendq(session, reason);
 	elseif session.direction == "incoming" then
 		incoming_s2s[session] = nil;
 	end
--- a/net/dns.lua	Thu Mar 04 02:22:45 2010 +0500
+++ b/net/dns.lua	Wed Mar 03 22:05:05 2010 +0000
@@ -183,7 +183,7 @@
 
 
 function dns.random(...)    -- - - - - - - - - - - - - - - - - - -  dns.random
-	math.randomseed(10000*socket.gettime());
+	math.randomseed(math.floor(10000*socket.gettime()));
 	dns.random = math.random;
 	return dns.random(...);
 end
@@ -746,7 +746,7 @@
 					if not next(self.active) then self:closeall(); end
 
 					-- was the query on the wanted list?
-					local q = response.question;
+					local q = response.question[1];
 					local cos = get(self.wanted, q.class, q.type, q.name);
 					if cos then
 						for co in pairs(cos) do
@@ -769,21 +769,18 @@
 	self.time = socket.gettime();
 
 	local response = self:decode(packet);
-	if response then
+	if response and self.active[response.header.id]
+		and self.active[response.header.id][response.question.raw] then
 		--print('received response');
 		--self.print(response);
 
-		for i,section in pairs({ 'answer', 'authority', 'additional' }) do
-			for j,rr in pairs(response[section]) do
-				self:remember(rr, response.question[1].type);
-			end
+		for j,rr in pairs(response.answer) do
+			self:remember(rr, response.question[1].type);
 		end
 
 		-- retire the query
 		local queries = self.active[response.header.id];
-		if queries[response.question.raw] then
-			queries[response.question.raw] = nil;
-		end
+		queries[response.question.raw] = nil;
 		if not next(queries) then self.active[response.header.id] = nil; end
 		if not next(self.active) then self:closeall(); end
 
--- a/net/http.lua	Thu Mar 04 02:22:45 2010 +0500
+++ b/net/http.lua	Wed Mar 03 22:05:05 2010 +0000
@@ -30,7 +30,7 @@
 
 local function expectbody(reqt, code)
     if reqt.method == "HEAD" then return nil end
-    if code == 204 or code == 304 then return nil end
+    if code == 204 or code == 304 or code == 301 then return nil end
     if code >= 100 and code < 200 then return nil end
     return 1
 end
@@ -43,6 +43,7 @@
 		elseif request.state ~= "completed" then
 			-- Error.. connection was closed prematurely
 			request.callback("connection-closed", 0, request);
+			return;
 		end
 		destroy_request(request);
 		request.body = nil;
@@ -73,22 +74,31 @@
 	elseif request.state == "headers" then
 		print("Reading headers...")
 		local pos = startpos;
-		local headers = request.responseheaders or {};
+		local headers, headers_complete = request.responseheaders;
+		if not headers then
+			headers = {};
+			request.responseheaders = headers;
+		end
 		for line in data:sub(startpos, -1):gmatch("(.-)\r\n") do
 			startpos = startpos + #line + 2;
 			local k, v = line:match("(%S+): (.+)");
 			if k and v then
 				headers[k:lower()] = v;
-				print("Header: "..k:lower().." = "..v);
+				--print("Header: "..k:lower().." = "..v);
 			elseif #line == 0 then
-				request.responseheaders = headers;
+				headers_complete = true;
 				break;
 			else
 				print("Unhandled header line: "..line);
 			end
 		end
+		if not headers_complete then return; end
 		-- Reached the end of the headers
-		request.state = "body";
+		if not expectbody(request, request.code) then
+			request.callback(nil, request.code, request);
+			return;
+		end
+			request.state = "body";
 		if #data > startpos then
 			return request_reader(request, data, startpos);
 		end
@@ -97,12 +107,15 @@
 		local http, code, text, linelen = data:match("^HTTP/(%S+) (%d+) (.-)\r\n()", startpos);
 		code = tonumber(code);
 		if not code then
-			return request.callback("invalid-status-line", 0, request);
+			log("warn", "Invalid HTTP status line, telling callback then closing");
+			local ret = request.callback("invalid-status-line", 0, request);
+			destroy_request(request);
+			return ret;
 		end
 		
 		request.code, request.responseversion = code, http;
 		
-		if request.onlystatus or not expectbody(request, code) then
+		if request.onlystatus then
 			if request.callback then
 				request.callback(nil, code, request);
 			end
@@ -200,7 +213,7 @@
 function destroy_request(request)
 	if request.conn then
 		request.handler.close()
-		listener.disconnect(request.conn, "closed");
+		listener.disconnect(request.handler, "closed");
 	end
 end
 
--- a/net/httpclient_listener.lua	Thu Mar 04 02:22:45 2010 +0500
+++ b/net/httpclient_listener.lua	Wed Mar 03 22:05:05 2010 +0000
@@ -30,7 +30,7 @@
 
 function httpclient.disconnect(conn, err)
 	local request = requests[conn];
-	if request then
+	if request and err ~= "closed" then
 		request:reader(nil);
 	end
 	requests[conn] = nil;
--- a/net/httpserver.lua	Thu Mar 04 02:22:45 2010 +0500
+++ b/net/httpserver.lua	Wed Mar 03 22:05:05 2010 +0000
@@ -65,6 +65,7 @@
 		
 		resp = { "HTTP/1.0 200 OK\r\n" };
 		t_insert(resp, "Connection: close\r\n");
+		t_insert(resp, "Content-Type: text/html\r\n");
 		t_insert(resp, "Content-Length: ");
 		t_insert(resp, #response);
 		t_insert(resp, "\r\n\r\n");
@@ -286,6 +287,7 @@
 		if ssl then
 			ssl.mode = "server";
 			ssl.protocol = "sslv23";
+			ssl.options = "no_sslv2";
 		end
 		
 		new{ port = port, interface = interface, 
--- a/net/server.lua	Thu Mar 04 02:22:45 2010 +0500
+++ b/net/server.lua	Wed Mar 03 22:05:05 2010 +0000
@@ -434,7 +434,7 @@
     handler.bufferlen = function( readlen, sendlen )
         maxsendlen = sendlen or maxsendlen
         maxreadlen = readlen or maxreadlen
-        return maxreadlen, maxsendlen
+        return bufferlen, maxreadlen, maxsendlen
     end
     handler.lock = function( switch )
         if switch == true then
@@ -467,7 +467,7 @@
     end
     local _readbuffer = function( )    -- this function reads data
         local buffer, err, part = receive( socket, pattern )    -- receive buffer with "pattern"
-        if not err or ( err == "timeout" or err == "wantread" ) then    -- received something
+        if not err or (err == "wantread" or err == "timeout") or string_len(part) > 0 then    -- received something
             local buffer = buffer or part or ""
             local len = string_len( buffer )
             if len > maxreadlen then
--- a/net/xmppserver_listener.lua	Thu Mar 04 02:22:45 2010 +0500
+++ b/net/xmppserver_listener.lua	Wed Mar 03 22:05:05 2010 +0000
@@ -152,14 +152,14 @@
 	local session = sessions[conn];
 	if session then
 		if err and err ~= "closed" and session.srv_hosts then
-			(session.log or log)("debug", "s2s connection closed unexpectedly");
+			(session.log or log)("debug", "s2s connection attempt failed: %s", err);
 			if s2s_attempt_connect(session, err) then
-				(session.log or log)("debug", "...so we're going to try again");
+				(session.log or log)("debug", "...so we're going to try another target");
 				return; -- Session lives for now
 			end
 		end
 		(session.log or log)("info", "s2s disconnected: %s->%s (%s)", tostring(session.from_host), tostring(session.to_host), tostring(err));
-		s2s_destroy_session(session);
+		s2s_destroy_session(session, err);
 		sessions[conn]  = nil;
 		session = nil;
 	end
--- a/plugins/mod_httpserver.lua	Thu Mar 04 02:22:45 2010 +0500
+++ b/plugins/mod_httpserver.lua	Wed Mar 03 22:05:05 2010 +0000
@@ -15,8 +15,20 @@
 local http_base = config.get("*", "core", "http_path") or "www_files";
 
 local response_400 = { status = "400 Bad Request", body = "<h1>Bad Request</h1>Sorry, we didn't understand your request :(" };
+local response_403 = { status = "403 Forbidden", body = "<h1>Forbidden</h1>You don't have permission to view the contents of this directory :(" };
 local response_404 = { status = "404 Not Found", body = "<h1>Page Not Found</h1>Sorry, we couldn't find what you were looking for :(" };
 
+-- TODO: Should we read this from /etc/mime.types if it exists? (startup time...?)
+local mime_map = {
+	html = "text/html";
+	htm = "text/html";
+	xml = "text/xml";
+	xsl = "text/xml";
+	txt = "text/plain; charset=utf-8";
+	js = "text/javascript";
+	css = "text/css";
+};
+
 local function preprocess_path(path)
 	if path:sub(1,1) ~= "/" then
 		path = "/"..path;
@@ -36,11 +48,19 @@
 end
 
 function serve_file(path)
-	local f, err = open(http_base..path, "r");
+	local f, err = open(http_base..path, "rb");
 	if not f then return response_404; end
 	local data = f:read("*a");
 	f:close();
-	return data;
+	if not data then
+		return response_403;
+	end
+	local ext = path:match("%.([^.]*)$");
+	local mime = mime_map[ext]; -- Content-Type should be nil when not known
+	return {
+		headers = { ["Content-Type"] = mime; };
+		body = data;
+	};
 end
 
 local function handle_file_request(method, body, request)
--- a/plugins/mod_posix.lua	Thu Mar 04 02:22:45 2010 +0500
+++ b/plugins/mod_posix.lua	Wed Mar 03 22:05:05 2010 +0000
@@ -19,6 +19,9 @@
 
 local logger_set = require "util.logger".setwriter;
 
+local lfs = require "lfs";
+local stat = lfs.attributes;
+
 local prosody = _G.prosody;
 
 module.host = "*"; -- we're a global module
@@ -59,28 +62,38 @@
 		end
 	end);
 
-local pidfile_written;
+local pidfile;
+local pidfile_handle;
 
 local function remove_pidfile()
-	if pidfile_written then
-		os.remove(pidfile_written);
-		pidfile_written = nil;
+	if pidfile_handle then
+		pidfile_handle:close();
+		os.remove(pidfile);
+		pidfile, pidfile_handle = nil, nil;
 	end
 end
 
 local function write_pidfile()
-	if pidfile_written then
+	if pidfile_handle then
 		remove_pidfile();
 	end
-	local pidfile = module:get_option("pidfile");
+	pidfile = module:get_option("pidfile");
 	if pidfile then
-		local pf, err = io.open(pidfile, "w+");
-		if not pf then
-			module:log("error", "Couldn't write pidfile; %s", err);
+		local mode = stat(pidfile) and "r+" or "w+";
+		pidfile_handle, err = io.open(pidfile, mode);
+		if not pidfile_handle then
+			module:log("error", "Couldn't write pidfile at %s; %s", pidfile, err);
+			prosody.shutdown("Couldn't write pidfile");
 		else
-			pf:write(tostring(pposix.getpid()));
-			pf:close();
-			pidfile_written = pidfile;
+			if not lfs.lock(pidfile_handle, "w") then -- Exclusive lock
+				local other_pid = pidfile_handle:read("*a");
+				module:log("error", "Another Prosody instance seems to be running with PID %s, quitting", other_pid);
+				pidfile_handle = nil;
+				prosody.shutdown("Prosody already running");
+			else
+				pidfile_handle:write(tostring(pposix.getpid()));
+				pidfile_handle:flush();
+			end
 		end
 	end
 end
--- a/plugins/mod_saslauth.lua	Thu Mar 04 02:22:45 2010 +0500
+++ b/plugins/mod_saslauth.lua	Wed Mar 03 22:05:05 2010 +0000
@@ -115,6 +115,9 @@
 		if not session.sasl_handler then
 			return session.send(build_reply("failure", "invalid-mechanism"));
 		end
+		if secure_auth_only and not session.secure then
+			return session.send(build_reply("failure", "encryption-required"));
+		end
 	elseif not session.sasl_handler then
 		return; -- FIXME ignoring out of order stanzas because ejabberd does
 	end
--- a/plugins/mod_tls.lua	Thu Mar 04 02:22:45 2010 +0500
+++ b/plugins/mod_tls.lua	Wed Mar 03 22:05:05 2010 +0000
@@ -94,6 +94,8 @@
 		function (session, stanza)
 			module:log("debug", "Proceeding with TLS on s2sout...");
 			local format, to_host, from_host = string.format, session.to_host, session.from_host;
+			local ssl_ctx = session.from_host and hosts[session.from_host].ssl_ctx or global_ssl_ctx;
+			session.conn.set_sslctx(ssl_ctx);
 			session:reset_stream();
 			session.conn.starttls(true);
 			session.secure = false;
--- a/prosody	Thu Mar 04 02:22:45 2010 +0500
+++ b/prosody	Wed Mar 03 22:05:05 2010 +0000
@@ -31,7 +31,9 @@
 end
 
 -- Required to be able to find packages installed with luarocks
-pcall(require, "luarocks.require")
+if not pcall(require, "luarocks.loader") then -- Try LuaRocks 2.x
+	pcall(require, "luarocks.require") -- Try LuaRocks 1.x
+end
 
 -- Replace require with one that doesn't pollute _G
 do
@@ -177,7 +179,7 @@
 	-- Load SSL settings from config, and create a ctx table
 	local global_ssl_ctx = rawget(_G, "ssl") and config.get("*", "core", "ssl");
 	if global_ssl_ctx then
-		local default_ssl_ctx = { mode = "server", protocol = "sslv23", capath = "/etc/ssl/certs", verify = "none"; };
+		local default_ssl_ctx = { mode = "server", protocol = "sslv23", capath = "/etc/ssl/certs", verify = "none", options = "no_sslv2"; };
 		setmetatable(global_ssl_ctx, { __index = default_ssl_ctx });
 	end
 
@@ -192,14 +194,15 @@
 			log("error", "core."..option.." is not a table");
 		else
 			for _, port in ipairs(ports) do
+				port = tonumber(port);
 				if type(port) ~= "number" then
 					log("error", "Non-numeric "..option.."_ports: "..tostring(port));
 				else
 					local ok, err = cl.start(listener, {
 						ssl = conntype ~= "tcp" and global_ssl_ctx,
 						port = port,
-						interface = config.get("*", "core", option.."_interface") 
-							or cl.get(listener).default_interface 
+						interface = (option and config.get("*", "core", option.."_interface"))
+							or cl.get(listener).default_interface
 							or config.get("*", "core", "interface"),
 						type = conntype
 					});
@@ -332,7 +335,7 @@
 function loop()
 	-- Error handler for errors that make it this far
 	local function catch_uncaught_error(err)
-		if type(err) == "string" and err:match("%d*: interrupted!$") then
+		if type(err) == "string" and err:match("interrupted!$") then
 			return "quitting";
 		end
 		
--- a/prosodyctl	Thu Mar 04 02:22:45 2010 +0500
+++ b/prosodyctl	Wed Mar 03 22:05:05 2010 +0000
@@ -30,7 +30,10 @@
 end
 
 -- Required to be able to find packages installed with luarocks
-pcall(require, "luarocks.require")
+if not pcall(require, "luarocks.loader") then -- Try LuaRocks 2.x
+	pcall(require, "luarocks.require") -- Try LuaRocks 1.x
+end
+
 
 config = require "core.configmanager"
 
@@ -137,18 +140,33 @@
 end
 
 local function getchar(n)
-	os.execute("stty raw -echo");
-	local ok, char = pcall(io.read, n or 1);
-	os.execute("stty sane");
+	local stty_ret = os.execute("stty raw -echo 2>/dev/null");
+	local ok, char;
+	if stty_ret == 0 then
+		ok, char = pcall(io.read, n or 1);
+		os.execute("stty sane");
+	else
+		ok, char = pcall(io.read, "*l");
+		if ok then
+			char = char:sub(1, n or 1);
+		end
+	end
 	if ok then
 		return char;
 	end
 end
 	
 local function getpass()
-	os.execute("stty -echo");
+	local stty_ret = os.execute("stty -echo 2>/dev/null");
+	if stty_ret ~= 0 then
+		io.write("\027[08m"); -- ANSI 'hidden' text attribute
+	end
 	local ok, pass = pcall(io.read, "*l");
-	os.execute("stty sane");
+	if stty_ret == 0 then
+		os.execute("stty sane");
+	else
+		io.write("\027[00m");
+	end
 	io.write("\n");
 	if ok then
 		return pass;
--- a/util-src/lsignal.c	Thu Mar 04 02:22:45 2010 +0500
+++ b/util-src/lsignal.c	Wed Mar 03 22:05:05 2010 +0000
@@ -1,9 +1,9 @@
 /*
  * lsignal.h -- Signal Handler Library for Lua
  *
- * Version: 1.000
+ * Version: 1.000+changes
  *
- * Copyright (C) 2007  Patrick J. Donnelly (batrick@unm.edu)
+ * Copyright (C) 2007  Patrick J. Donnelly (batrick@batbytes.com)
  *
  * This software is distributed under the same license as Lua 5.0:
  *
@@ -27,6 +27,7 @@
 */
 
 #include <signal.h>
+#include <stdlib.h>
 
 #include "lua.h"
 #include "lauxlib.h"
@@ -149,43 +150,67 @@
   {NULL, 0}
 };
 
-static int Nsig = 0;
 static lua_State *Lsig = NULL;
 static lua_Hook Hsig = NULL;
 static int Hmask = 0;
 static int Hcount = 0;
 
+static struct signal_event
+{
+	int Nsig;
+	struct signal_event *next_event;
+} *signal_queue = NULL;
+
+static struct signal_event *last_event = NULL;
+
 static void sighook(lua_State *L, lua_Debug *ar)
 {
+  /* restore the old hook */
+  lua_sethook(L, Hsig, Hmask, Hcount);
+
   lua_pushstring(L, LUA_SIGNAL);
   lua_gettable(L, LUA_REGISTRYINDEX);
-  lua_pushnumber(L, Nsig);
-  lua_gettable(L, -2);
 
-  lua_call(L, 0, 0);
+  struct signal_event *event;
+  while((event = signal_queue))
+  {
+    lua_pushnumber(L, event->Nsig);
+    lua_gettable(L, -2);
+    lua_call(L, 0, 0);
+    signal_queue = event->next_event;
+    free(event);
+  };
 
-  /* set the old hook */
-  lua_sethook(L, Hsig, Hmask, Hcount);
+  lua_pop(L, 1); /* pop lua_signal table */
+
 }
 
 static void handle(int sig)
 {
-  Hsig = lua_gethook(Lsig);
-  Hmask = lua_gethookmask(Lsig);
-  Hcount = lua_gethookcount(Lsig);
-  Nsig = sig;
+  if(!signal_queue)
+  {
+    /* Store the existing debug hook (if any) and its parameters */
+    Hsig = lua_gethook(Lsig);
+    Hmask = lua_gethookmask(Lsig);
+    Hcount = lua_gethookcount(Lsig);
+    
+    signal_queue = malloc(sizeof(struct signal_event));
+    signal_queue->Nsig = sig;
+    signal_queue->next_event = NULL;
 
-  lua_sethook(Lsig, sighook, LUA_MASKCALL | LUA_MASKRET | LUA_MASKCOUNT, 1);
-  /*
-  switch (sig)
+    last_event = signal_queue;
+    
+    /* Set our new debug hook */
+    lua_sethook(Lsig, sighook, LUA_MASKCALL | LUA_MASKRET | LUA_MASKCOUNT, 1);
+  }
+  else
   {
-    case SIGABRT: ;
-    case SIGFPE: ;
-    case SIGILL: ;
-    case SIGINT: ;
-    case SIGSEGV: ;
-    case SIGTERM: ;
-  } */
+    last_event->next_event = malloc(sizeof(struct signal_event));
+    last_event->next_event->Nsig = sig;
+    last_event->next_event->next_event = NULL;
+    
+    last_event = last_event->next_event;
+  }
 }
 
 /*
@@ -348,7 +373,7 @@
 static const struct luaL_Reg lsignal_lib[] = {
   {"signal", l_signal},
   {"raise", l_raise},
-#ifdef _POSIX_SOURCE
+#if defined _POSIX_SOURCE || (defined(sun) || defined(__sun))
   {"kill", l_kill},
 #endif
   {NULL, NULL}
--- a/util/prosodyctl.lua	Thu Mar 04 02:22:45 2010 +0500
+++ b/util/prosodyctl.lua	Wed Mar 03 22:05:05 2010 +0000
@@ -12,6 +12,7 @@
 local stringprep = encodings.stringprep;
 local usermanager = require "core.usermanager";
 local signal = require "util.signal";
+local lfs = require "lfs";
 
 local nodeprep, nameprep = stringprep.nodeprep, stringprep.nameprep;
 
@@ -64,11 +65,17 @@
 		return false, "no-pidfile";
 	end
 	
-	local file, err = io.open(pidfile);
+	local file, err = io.open(pidfile, "r+");
 	if not file then
 		return false, "pidfile-read-failed", err;
 	end
 	
+	local locked, err = lfs.lock(file, "w");
+	if locked then
+		file:close();
+		return false, "pidfile-not-locked";
+	end
+	
 	local pid = tonumber(file:read("*a"));
 	file:close();
 	
@@ -82,7 +89,7 @@
 function isrunning()
 	local ok, pid, err = _M.getpid();
 	if not ok then
-		if pid == "pidfile-read-failed" then
+		if pid == "pidfile-read-failed" or pid == "pidfile-not-locked" then
 			-- Report as not running, since we can't open the pidfile
 			-- (it probably doesn't exist)
 			return true, false;

mercurial