From 8292f713bab8e71624f03111115bd3a97cf8dae9 Mon Sep 17 00:00:00 2001
From: Matthew Wild <mwild1@gmail.com>
Date: Sun, 22 Jan 2012 19:50:08 +0000
Subject: mod_admin_telnet: Use module:shared() to expose commands table and
 default console environment

---
 plugins/mod_admin_telnet.lua | 6 ++----
 1 file changed, 2 insertions(+), 4 deletions(-)

(limited to 'plugins')

diff --git a/plugins/mod_admin_telnet.lua b/plugins/mod_admin_telnet.lua
index b211d271..b118e27a 100644
--- a/plugins/mod_admin_telnet.lua
+++ b/plugins/mod_admin_telnet.lua
@@ -21,12 +21,10 @@ local jid_bare = require "util.jid".bare;
 local set, array = require "util.set", require "util.array";
 local cert_verify_identity = require "util.x509".verify_identity;
 
-local commands = {};
-local def_env = {};
+local commands = module:shared("commands")
+local def_env = module:shared("env");
 local default_env_mt = { __index = def_env };
 
-prosody.console = { commands = commands, env = def_env };
-
 local function redirect_output(_G, session)
 	local env = setmetatable({ print = session.print }, { __index = function (t, k) return rawget(_G, k); end });
 	env.dofile = function(name)
-- 
cgit v1.2.3


From 13b36f8ded00794f3142887c41ac8b31efb01e4b Mon Sep 17 00:00:00 2001
From: Matthew Wild <mwild1@gmail.com>
Date: Sun, 22 Jan 2012 23:57:13 +0000
Subject: mod_c2s, sessionmanager, xmppclient_listener: Move all c2s network
 and stream logic into a new module, mod_c2s

---
 plugins/mod_c2s.lua | 224 ++++++++++++++++++++++++++++++++++++++++++++++++++++
 1 file changed, 224 insertions(+)
 create mode 100644 plugins/mod_c2s.lua

(limited to 'plugins')

diff --git a/plugins/mod_c2s.lua b/plugins/mod_c2s.lua
new file mode 100644
index 00000000..2fbed1c9
--- /dev/null
+++ b/plugins/mod_c2s.lua
@@ -0,0 +1,224 @@
+-- Prosody IM
+-- Copyright (C) 2008-2010 Matthew Wild
+-- Copyright (C) 2008-2010 Waqas Hussain
+-- 
+-- This project is MIT/X11 licensed. Please see the
+-- COPYING file in the source package for more information.
+--
+
+module:set_global();
+
+local new_xmpp_stream = require "util.xmppstream".new;
+local nameprep = require "util.encodings".stringprep.nameprep;
+local portmanager = require "core.portmanager";
+local sessionmanager = require "core.sessionmanager";
+local st = require "util.stanza";
+local sm_new_session, sm_destroy_session = sessionmanager.new_session, sessionmanager.destroy_session;
+local uuid_generate = require "util.uuid".generate;
+
+local xpcall, tostring, type = xpcall, tostring, type;
+local format = string.format;
+local traceback = debug.traceback;
+
+local xmlns_xmpp_streams = "urn:ietf:params:xml:ns:xmpp-streams";
+
+local log = module._log;
+
+local opt_keepalives = module:get_option_boolean("tcp_keepalives", false);
+
+local sessions = module:shared("sessions");
+
+local stream_callbacks = { default_ns = "jabber:client", handlestanza = core_process_stanza };
+local listener = { default_port = 5222, default_mode = "*a" };
+
+--- Stream events handlers
+local stream_xmlns_attr = {xmlns='urn:ietf:params:xml:ns:xmpp-streams'};
+local default_stream_attr = { ["xmlns:stream"] = "http://etherx.jabber.org/streams", xmlns = stream_callbacks.default_ns, version = "1.0", id = "" };
+
+function stream_callbacks.streamopened(session, attr)
+	local send = session.send;
+	session.host = attr.to;
+	if not session.host then
+		session:close{ condition = "improper-addressing",
+			text = "A 'to' attribute is required on stream headers" };
+		return;
+	end
+	session.host = nameprep(session.host);
+	session.version = tonumber(attr.version) or 0;
+	session.streamid = uuid_generate();
+	(session.log or session)("debug", "Client sent opening <stream:stream> to %s", session.host);
+
+	if not hosts[session.host] then
+		-- We don't serve this host...
+		session:close{ condition = "host-unknown", text = "This server does not serve "..tostring(session.host)};
+		return;
+	end
+
+	send("<?xml version='1.0'?>");
+	send(format("<stream:stream xmlns='jabber:client' xmlns:stream='http://etherx.jabber.org/streams' id='%s' from='%s' version='1.0' xml:lang='en'>", session.streamid, session.host));
+
+	(session.log or log)("debug", "Sent reply <stream:stream> to client");
+	session.notopen = nil;
+
+	-- If session.secure is *false* (not nil) then it means we /were/ encrypting
+	-- since we now have a new stream header, session is secured
+	if session.secure == false then
+		session.secure = true;
+	end
+
+	local features = st.stanza("stream:features");
+	hosts[session.host].events.fire_event("stream-features", { origin = session, features = features });
+	module:fire_event("stream-features", session, features);
+
+	send(features);
+end
+
+function stream_callbacks.streamclosed(session)
+	session.log("debug", "Received </stream:stream>");
+	session:close();
+end
+
+function stream_callbacks.error(session, error, data)
+	if error == "no-stream" then
+		session.log("debug", "Invalid opening stream header");
+		session:close("invalid-namespace");
+	elseif error == "parse-error" then
+		(session.log or log)("debug", "Client XML parse error: %s", tostring(data));
+		session:close("not-well-formed");
+	elseif error == "stream-error" then
+		local condition, text = "undefined-condition";
+		for child in data:children() do
+			if child.attr.xmlns == xmlns_xmpp_streams then
+				if child.name ~= "text" then
+					condition = child.name;
+				else
+					text = child:get_text();
+				end
+				if condition ~= "undefined-condition" and text then
+					break;
+				end
+			end
+		end
+		text = condition .. (text and (" ("..text..")") or "");
+		session.log("info", "Session closed by remote with error: %s", text);
+		session:close(nil, text);
+	end
+end
+
+local function handleerr(err) log("error", "Traceback[c2s]: %s: %s", tostring(err), traceback()); end
+function stream_callbacks.handlestanza(session, stanza)
+	stanza = session.filter("stanzas/in", stanza);
+	if stanza then
+		return xpcall(function () return core_process_stanza(session, stanza) end, handleerr);
+	end
+end
+
+--- Session methods
+local function session_close(session, reason)
+	local log = session.log or log;
+	if session.conn then
+		if session.notopen then
+			session.send("<?xml version='1.0'?>");
+			session.send(st.stanza("stream:stream", default_stream_attr):top_tag());
+		end
+		if reason then
+			if type(reason) == "string" then -- assume stream error
+				log("info", "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
+					local stanza = st.stanza("stream:error"):tag(reason.condition, stream_xmlns_attr):up();
+					if reason.text then
+						stanza:tag("text", stream_xmlns_attr):text(reason.text):up();
+					end
+					if reason.extra then
+						stanza:add_child(reason.extra);
+					end
+					log("info", "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));
+					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");
+	end
+end
+
+--- Port listener
+function listener.onconnect(conn)
+	local session = sm_new_session(conn);
+	sessions[conn] = session;
+	
+	session.log("info", "Client connected");
+	
+	-- Client is using legacy SSL (otherwise mod_tls sets this flag)
+	if conn:ssl() then
+		session.secure = true;
+	end
+	
+	if opt_keepalives then
+		conn:setoption("keepalive", opt_keepalives);
+	end
+	
+	session.close = session_close;
+	
+	local stream = new_xmpp_stream(session, stream_callbacks);
+	session.stream = stream;
+	session.notopen = true;
+	
+	function session.reset_stream()
+		session.notopen = true;
+		session.stream:reset();
+	end
+	
+	local filter = session.filter;
+	function session.data(data)
+		data = filter("bytes/in", data);
+		if data then
+			local ok, err = stream:feed(data);
+			if ok then return; end
+			log("debug", "Received invalid XML (%s) %d bytes: %s", tostring(err), #data, data:sub(1, 300):gsub("[\r\n]+", " "):gsub("[%z\1-\31]", "_"));
+			session:close("not-well-formed");
+		end
+	end
+
+	session.dispatch_stanza = stream_callbacks.handlestanza;
+end
+
+function listener.onincoming(conn, data)
+	local session = sessions[conn];
+	if session then
+		session.data(data);
+	end
+end
+
+function listener.ondisconnect(conn, err)
+	local session = sessions[conn];
+	if session then
+		(session.log or log)("info", "Client disconnected: %s", err);
+		sm_destroy_session(session, err);
+		sessions[conn]  = nil;
+		session = nil;
+	end
+end
+
+function listener.associate_session(conn, session)
+	sessions[conn] = session;
+end
+
+portmanager.register_service("c2s", {
+	listener = listener;
+	default_port = 5222;
+	encryption = "starttls";
+});
+
+portmanager.register_service("legacy_ssl", {
+	listener = listener;
+	encryption = "ssl";
+});
+
+
-- 
cgit v1.2.3


From b14f0af41d94b5ee137d48e269ad29b45ee765d9 Mon Sep 17 00:00:00 2001
From: Matthew Wild <mwild1@gmail.com>
Date: Mon, 23 Jan 2012 00:56:57 +0000
Subject: sessionmanager, mod_c2s: Move timeout logic to mod_c2s

---
 plugins/mod_c2s.lua | 11 +++++++++++
 1 file changed, 11 insertions(+)

(limited to 'plugins')

diff --git a/plugins/mod_c2s.lua b/plugins/mod_c2s.lua
index 2fbed1c9..4d7318a5 100644
--- a/plugins/mod_c2s.lua
+++ b/plugins/mod_c2s.lua
@@ -8,6 +8,7 @@
 
 module:set_global();
 
+local add_task = require "util.timer".add_task;
 local new_xmpp_stream = require "util.xmppstream".new;
 local nameprep = require "util.encodings".stringprep.nameprep;
 local portmanager = require "core.portmanager";
@@ -24,6 +25,7 @@ local xmlns_xmpp_streams = "urn:ietf:params:xml:ns:xmpp-streams";
 
 local log = module._log;
 
+local c2s_timeout = module:get_option_number("c2s_timeout");
 local opt_keepalives = module:get_option_boolean("tcp_keepalives", false);
 
 local sessions = module:shared("sessions");
@@ -186,6 +188,15 @@ function listener.onconnect(conn)
 		end
 	end
 
+	
+	if c2s_timeout then
+		add_task(c2s_timeout, function ()
+			if session.type == "c2s_unauthed" then
+				session:close("connection-timeout");
+			end
+		end);
+	end
+
 	session.dispatch_stanza = stream_callbacks.handlestanza;
 end
 
-- 
cgit v1.2.3


From 7a2a2a4cd7a54945df1a849af7f37a9a91783d53 Mon Sep 17 00:00:00 2001
From: Matthew Wild <mwild1@gmail.com>
Date: Mon, 23 Jan 2012 01:05:32 +0000
Subject: mod_admin_telnet: Port to portmanager

---
 plugins/mod_admin_telnet.lua | 10 +++++-----
 1 file changed, 5 insertions(+), 5 deletions(-)

(limited to 'plugins')

diff --git a/plugins/mod_admin_telnet.lua b/plugins/mod_admin_telnet.lua
index b118e27a..9e9065bb 100644
--- a/plugins/mod_admin_telnet.lua
+++ b/plugins/mod_admin_telnet.lua
@@ -12,9 +12,8 @@ local _G = _G;
 
 local prosody = _G.prosody;
 local hosts = prosody.hosts;
-local connlisteners_register = require "net.connlisteners".register;
 
-local console_listener = { default_port = 5582; default_mode = "*l"; default_interface = "127.0.0.1" };
+local console_listener = { default_port = 5582; default_mode = "*l"; interface = "127.0.0.1" };
 
 require "util.iterators";
 local jid_bare = require "util.jid".bare;
@@ -147,8 +146,6 @@ function console_listener.ondisconnect(conn, err)
 	end
 end
 
-connlisteners_register('console', console_listener);
-
 -- Console commands --
 -- These are simple commands, not valid standalone in Lua
 
@@ -776,4 +773,7 @@ if option and option ~= "short" and option ~= "full" and option ~= "graphic" the
 end
 end
 
-prosody.net_activate_ports("console", "console", {5582}, "tcp");
+require "core.portmanager".register_service("console", {
+	listener = console_listener;
+	default_port = 5582;
+});
-- 
cgit v1.2.3


From 5c1ba3bb130a78863510e6f62f9be51f83e94a95 Mon Sep 17 00:00:00 2001
From: Matthew Wild <mwild1@gmail.com>
Date: Mon, 23 Jan 2012 02:15:28 +0000
Subject: mod_c2s: Code reduction

---
 plugins/mod_c2s.lua | 5 ++---
 1 file changed, 2 insertions(+), 3 deletions(-)

(limited to 'plugins')

diff --git a/plugins/mod_c2s.lua b/plugins/mod_c2s.lua
index 4d7318a5..682c4e3b 100644
--- a/plugins/mod_c2s.lua
+++ b/plugins/mod_c2s.lua
@@ -39,13 +39,12 @@ local default_stream_attr = { ["xmlns:stream"] = "http://etherx.jabber.org/strea
 
 function stream_callbacks.streamopened(session, attr)
 	local send = session.send;
-	session.host = attr.to;
+	session.host = nameprep(attr.to);
 	if not session.host then
 		session:close{ condition = "improper-addressing",
-			text = "A 'to' attribute is required on stream headers" };
+			text = "A valid 'to' attribute is required on stream headers" };
 		return;
 	end
-	session.host = nameprep(session.host);
 	session.version = tonumber(attr.version) or 0;
 	session.streamid = uuid_generate();
 	(session.log or session)("debug", "Client sent opening <stream:stream> to %s", session.host);
-- 
cgit v1.2.3


From f851289311ce15196d3dc7ae0d912b17586901b9 Mon Sep 17 00:00:00 2001
From: Matthew Wild <mwild1@gmail.com>
Date: Mon, 23 Jan 2012 16:28:20 +0000
Subject: s2smanager, mod_s2s, mod_s2s/s2sout: Split connection handling out of
 s2smanager into mod_s2s, and further split connection logic for s2sout to a
 module lib, s2sout.lib.lua

---
 plugins/s2s/mod_s2s.lua    | 447 +++++++++++++++++++++++++++++++++++++++++++++
 plugins/s2s/s2sout.lib.lua | 314 +++++++++++++++++++++++++++++++
 2 files changed, 761 insertions(+)
 create mode 100644 plugins/s2s/mod_s2s.lua
 create mode 100644 plugins/s2s/s2sout.lib.lua

(limited to 'plugins')

diff --git a/plugins/s2s/mod_s2s.lua b/plugins/s2s/mod_s2s.lua
new file mode 100644
index 00000000..5be4d84b
--- /dev/null
+++ b/plugins/s2s/mod_s2s.lua
@@ -0,0 +1,447 @@
+-- Prosody IM
+-- Copyright (C) 2008-2010 Matthew Wild
+-- Copyright (C) 2008-2010 Waqas Hussain
+-- 
+-- This project is MIT/X11 licensed. Please see the
+-- COPYING file in the source package for more information.
+--
+
+module:set_global();
+
+local tostring, type = tostring, type;
+local xpcall, traceback = xpcall, debug.traceback;
+
+local add_task = require "util.timer".add_task;
+local st = require "util.stanza";
+local initialize_filters = require "util.filters".initialize;
+local new_xmpp_stream = require "util.xmppstream".new;
+local s2s_new_incoming = require "core.s2smanager".new_incoming;
+local s2s_new_outgoing = require "core.s2smanager".new_outgoing;
+local s2s_destroy_session = require "core.s2smanager".destroy_session;
+
+local s2sout = module:require("s2sout");
+
+local connect_timeout = module:get_option_number("s2s_timeout", 60);
+
+local sessions = module:shared("sessions");
+
+--- Handle stanzas to remote domains
+
+local bouncy_stanzas = { message = true, presence = true, iq = true };
+local function bounce_sendq(session, reason)
+	local sendq = session.sendq;
+	if not sendq then return; end
+	session.log("info", "sending error replies for "..#sendq.." queued stanzas because of failed outgoing connection to "..tostring(session.to_host));
+	local dummy = {
+		type = "s2sin";
+		send = function(s)
+			(session.log or log)("error", "Replying to to an s2s error reply, please report this! Traceback: %s", get_traceback());
+		end;
+		dummy = true;
+	};
+	for i, data in ipairs(sendq) do
+		local reply = data[2];
+		if reply and not(reply.attr.xmlns) and bouncy_stanzas[reply.name] then
+			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("Server-to-server connection failed: "..reason):up();
+			end
+			core_process_stanza(dummy, reply);
+		end
+		sendq[i] = nil;
+	end
+	session.sendq = nil;
+end
+
+function send_to_host(from_host, to_host, stanza)
+	if not hosts[from_host] then
+		log("warn", "Attempt to send stanza from %s - a host we don't serve", from_host);
+		return false;
+	end
+	local host = hosts[from_host].s2sout[to_host];
+	if host then
+		-- We have a connection to this host already
+		if host.type == "s2sout_unauthed" and (stanza.name ~= "db:verify" or not host.dialback_key) then
+			(host.log or log)("debug", "trying to send over unauthed s2sout to "..to_host);
+			
+			-- Queue stanza until we are able to send it
+			if host.sendq then t_insert(host.sendq, {tostring(stanza), stanza.attr.type ~= "error" and stanza.attr.type ~= "result" and st.reply(stanza)});
+			else host.sendq = { {tostring(stanza), stanza.attr.type ~= "error" and stanza.attr.type ~= "result" and st.reply(stanza)} }; end
+			host.log("debug", "stanza [%s] queued ", stanza.name);
+		elseif host.type == "local" or host.type == "component" then
+			log("error", "Trying to send a stanza to ourselves??")
+			log("error", "Traceback: %s", get_traceback());
+			log("error", "Stanza: %s", tostring(stanza));
+			return false;
+		else
+			(host.log or log)("debug", "going to send stanza to "..to_host.." from "..from_host);
+			-- FIXME
+			if host.from_host ~= from_host then
+				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);
+		end
+	else
+		log("debug", "opening a new outgoing connection for this stanza");
+		local host_session = s2s_new_outgoing(from_host, to_host);
+
+		-- Store in buffer
+		host_session.bounce_sendq = bounce_sendq;
+		host_session.sendq = { {tostring(stanza), stanza.attr.type ~= "error" and stanza.attr.type ~= "result" and st.reply(stanza)} };
+		log("debug", "stanza [%s] queued until connection complete", tostring(stanza.name));
+		if (not host_session.connecting) and (not host_session.conn) then
+			log("warn", "Connection to %s failed already, destroying session...", to_host);
+			if not s2s_destroy_session(host_session, "Connection failed") then
+				-- Already destroyed, we need to bounce our stanza
+				host_session:bounce_sendq(host_session.destruction_reason);
+			end
+			return false;
+		end
+		s2sout.initiate_connection(host_session);
+	end
+	return true;
+end
+
+module:hook("route/remote", function (event)
+	return send_to_host(event.from_host, event.to_host, event.stanza);
+end);
+
+--- Helper to check that a session peer's certificate is valid
+local function check_cert_status(session)
+	local conn = session.conn:socket()
+	local cert
+	if conn.getpeercertificate then
+		cert = conn:getpeercertificate()
+	end
+
+	if cert then
+		local chain_valid, errors = conn:getpeerverification()
+		-- Is there any interest in printing out all/the number of errors here?
+		if not chain_valid then
+			(session.log or log)("debug", "certificate chain validation result: invalid");
+			session.cert_chain_status = "invalid";
+		else
+			(session.log or log)("debug", "certificate chain validation result: valid");
+			session.cert_chain_status = "valid";
+
+			local host = session.direction == "incoming" and session.from_host or session.to_host
+
+			-- We'll go ahead and verify the asserted identity if the
+			-- connecting server specified one.
+			if host then
+				if cert_verify_identity(host, "xmpp-server", cert) then
+					session.cert_identity_status = "valid"
+				else
+					session.cert_identity_status = "invalid"
+				end
+			end
+		end
+	end
+end
+
+--- XMPP stream event handlers
+
+local stream_callbacks = { default_ns = "jabber:server", handlestanza =  core_process_stanza };
+
+local xmlns_xmpp_streams = "urn:ietf:params:xml:ns:xmpp-streams";
+
+function stream_callbacks.streamopened(session, attr)
+	local send = session.sends2s;
+	
+	-- TODO: #29: SASL/TLS on s2s streams
+	session.version = tonumber(attr.version) or 0;
+	
+	-- TODO: Rename session.secure to session.encrypted
+	if session.secure == false then
+		session.secure = true;
+	end
+
+	if session.direction == "incoming" then
+		-- Send a reply stream header
+		session.to_host = attr.to and nameprep(attr.to);
+		session.from_host = attr.from and nameprep(attr.from);
+	
+		session.streamid = uuid_gen();
+		(session.log or log)("debug", "Incoming s2s received <stream:stream>");
+		if session.to_host then
+			if 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;
+			elseif hosts[session.to_host].disallow_s2s then
+				-- Attempting to connect to a host that disallows s2s
+				session:close({
+					condition = "policy-violation";
+					text = "Server-to-server communication is not allowed to this host";
+				});
+				return;
+			end
+		end
+
+		if session.secure and not session.cert_chain_status then check_cert_status(session); 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", { origin = session, features = features });
+			else
+				(session.log or log)("warn", "No 'to' on stream header from %s means we can't offer any features", session.from_host or "unknown host");
+			end
+			
+			log("debug", "Sending stream features: %s", tostring(features));
+			send(features);
+		end
+	elseif session.direction == "outgoing" then
+		-- If we are just using the connection for verifying dialback keys, we won't try and auth it
+		if not attr.id then error("stream response did not give us a streamid!!!"); end
+		session.streamid = attr.id;
+
+		if session.secure and not session.cert_chain_status then check_cert_status(session); end
+
+		-- Send unauthed buffer
+		-- (stanzas which are fine to send before dialback)
+		-- Note that this is *not* the stanza queue (which
+		-- we can only send if auth succeeds) :)
+		local send_buffer = session.send_buffer;
+		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));
+				send_buffer[i] = nil;
+			end
+		end
+		session.send_buffer = nil;
+	
+		-- If server is pre-1.0, don't wait for features, just do dialback
+		if session.version < 1.0 then
+			if not session.dialback_verifying then
+				log("debug", "Initiating dialback...");
+				initiate_dialback(session);
+			else
+				s2s_mark_connected(session);
+			end
+		end
+	end
+	session.notopen = nil;
+end
+
+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" 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"));
+	sessions[session.conn]  = nil;
+	s2s_destroy_session(session, err);
+end
+
+function stream_callbacks.error(session, error, data)
+	if error == "no-stream" then
+		session:close("invalid-namespace");
+	elseif error == "parse-error" then
+		session.log("debug", "Server-to-server XML parse error: %s", tostring(error));
+		session:close("not-well-formed");
+	elseif error == "stream-error" then
+		local condition, text = "undefined-condition";
+		for child in data:children() do
+			if child.attr.xmlns == xmlns_xmpp_streams then
+				if child.name ~= "text" then
+					condition = child.name;
+				else
+					text = child:get_text();
+				end
+				if condition ~= "undefined-condition" and text then
+					break;
+				end
+			end
+		end
+		text = condition .. (text and (" ("..text..")") or "");
+		session.log("info", "Session closed by remote with error: %s", text);
+		session:close(nil, text);
+	end
+end
+
+local function handleerr(err) log("error", "Traceback[s2s]: %s: %s", tostring(err), traceback()); end
+function stream_callbacks.handlestanza(session, stanza)
+	if stanza.attr.xmlns == "jabber:client" then --COMPAT: Prosody pre-0.6.2 may send jabber:client
+		stanza.attr.xmlns = nil;
+	end
+	stanza = session.filter("stanzas/in", stanza);
+	if stanza then
+		return xpcall(function () return core_process_stanza(session, stanza) end, handleerr);
+	end
+end
+
+local listener = { default_port = 5269, default_mode = "*a", default_interface = "*" };
+
+--- Session methods
+local stream_xmlns_attr = {xmlns='urn:ietf:params:xml:ns:xmpp-streams'};
+local default_stream_attr = { ["xmlns:stream"] = "http://etherx.jabber.org/streams", xmlns = stream_callbacks.default_ns, version = "1.0", id = "" };
+local function session_close(session, reason, remote_reason)
+	local log = session.log or log;
+	if session.conn then
+		if session.notopen then
+			session.sends2s("<?xml version='1.0'?>");
+			session.sends2s(st.stanza("stream:stream", default_stream_attr):top_tag());
+		end
+		if reason then
+			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);
+				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
+					local stanza = st.stanza("stream:error"):tag(reason.condition, stream_xmlns_attr):up();
+					if reason.text then
+						stanza:tag("text", stream_xmlns_attr):text(reason.text):up();
+					end
+					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));
+					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));
+					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?
+		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
+
+-- Session initialization logic shared by incoming and outgoing
+local function initialize_session(session)
+	local stream = new_xmpp_stream(session, stream_callbacks);
+	session.stream = stream;
+	
+	session.notopen = true;
+		
+	function session.reset_stream()
+		session.notopen = true;
+		session.stream:reset();
+	end
+	
+	local filter = session.filter;
+	function session.data(data)
+		data = filter("bytes/in", data);
+		if data then
+			local ok, err = stream:feed(data);
+			if ok then return; end
+			(session.log or log)("warn", "Received invalid XML: %s", data);
+			(session.log or log)("warn", "Problem was: %s", err);
+			session:close("not-well-formed");
+		end
+	end
+
+	session.close = session_close;
+
+	local handlestanza = stream_callbacks.handlestanza;
+	function session.dispatch_stanza(session, stanza)
+		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]
+		end
+		-- Not connected, need to close session and clean up
+		(session.log or log)("debug", "Destroying incomplete session %s->%s due to inactivity",
+		session.from_host or "(unknown)", session.to_host or "(unknown)");
+		session:close("connection-timeout");
+	end);
+end
+
+function listener.onconnect(conn)
+	if not sessions[conn] then -- May be an existing outgoing session
+		local session = s2s_new_incoming(conn);
+		sessions[conn] = session;
+		session.log("debug", "Incoming s2s connection");
+
+		local filter = initialize_filters(session);
+		local w = conn.write;
+		session.sends2s = function (t)
+			log("debug", "sending: %s", t.top_tag and t:top_tag() or t:match("^([^>]*>?)"));
+			if t.name then
+				t = filter("stanzas/out", t);
+			end
+			if t then
+				t = filter("bytes/out", tostring(t));
+				if t then
+					return w(conn, t);
+				end
+			end
+		end
+	
+		initialize_session(session);
+	end
+end
+
+function listener.onincoming(conn, data)
+	local session = sessions[conn];
+	if session then
+		session.data(data);
+	end
+end
+	
+function listener.onstatus(conn, status)
+	if status == "ssl-handshake-complete" then
+		local session = sessions[conn];
+		if session and session.direction == "outgoing" then
+			local to_host, from_host = session.to_host, session.from_host;
+			session.log("debug", "Sending stream header...");
+			session:open_stream(session.from_host, session.to_host);
+		end
+	end
+end
+
+function listener.ondisconnect(conn, err)
+	local session = sessions[conn];
+	if session then
+		if stream_callbacks.streamdisconnected(session, err) then
+			return; -- Connection lives, for now
+		end
+	end
+	sessions[conn]  = nil;
+end
+
+function listener.register_outgoing(conn, session)
+	session.direction = "outgoing";
+	sessions[conn] = session;
+	initialize_session(session);
+end
+
+s2sout.set_listener(listener);
+
+require "core.portmanager".register_service("s2s", {
+	listener = listener;
+	default_port = 5269;
+	encryption = "starttls";
+});
+
diff --git a/plugins/s2s/s2sout.lib.lua b/plugins/s2s/s2sout.lib.lua
new file mode 100644
index 00000000..094126e7
--- /dev/null
+++ b/plugins/s2s/s2sout.lib.lua
@@ -0,0 +1,314 @@
+-- Prosody IM
+-- Copyright (C) 2008-2010 Matthew Wild
+-- Copyright (C) 2008-2010 Waqas Hussain
+-- 
+-- This project is MIT/X11 licensed. Please see the
+-- COPYING file in the source package for more information.
+--
+
+--- Module containing all the logic for connecting to a remote server
+
+local initialize_filters = require "util.filters".initialize;
+local idna_to_ascii = require "util.encodings".idna.to_ascii;
+local add_task = require "util.timer".add_task;
+local socket = require "socket";
+
+local s2s_destroy_session = require "core.s2smanager".destroy_session;
+
+local s2sout = {};
+
+local s2s_listener;
+
+function s2sout.set_listener(listener)
+	s2s_listener = listener;
+end
+
+local function compare_srv_priorities(a,b)
+	return a.priority < b.priority or (a.priority == b.priority and a.weight > b.weight);
+end
+
+local function session_open_stream(session, from, to)
+	session.sends2s(st.stanza("stream:stream", {
+		xmlns='jabber:server', ["xmlns:db"]='jabber:server:dialback',
+		["xmlns:stream"]='http://etherx.jabber.org/streams',
+		from=from, to=to, version='1.0', ["xml:lang"]='en'}):top_tag());
+end
+
+function s2sout.initiate_connection(host_session)
+	initialize_filters(host_session);
+	session.open_stream = session_open_stream;
+	
+	-- Kick the connection attempting machine into life
+	if not s2sout.attempt_connection(host_session) then
+		-- Intentionally not returning here, the
+		-- session is needed, connected or not
+		s2s_destroy_session(host_session);
+	end
+	
+	if not host_session.sends2s then
+		-- A sends2s which buffers data (until the stream is opened)
+		-- note that data in this buffer will be sent before the stream is authed
+		-- and will not be ack'd in any way, successful or otherwise
+		local buffer;
+		function host_session.sends2s(data)
+			if not buffer then
+				buffer = {};
+				host_session.send_buffer = buffer;
+			end
+			log("debug", "Buffering data on unconnected s2sout to %s", to_host);
+			buffer[#buffer+1] = data;
+			log("debug", "Buffered item %d: %s", #buffer, tostring(data));
+		end
+	end
+end
+
+function s2sout.attempt_connection(host_session, err)
+	local from_host, to_host = host_session.from_host, host_session.to_host;
+	local connect_host, connect_port = to_host and idna_to_ascii(to_host), 5269;
+	
+	if not connect_host then
+		return false;
+	end
+	
+	if not err then -- This is our first attempt
+		log("debug", "First attempt to connect to %s, starting with SRV lookup...", to_host);
+		host_session.connecting = true;
+		local handle;
+		handle = adns.lookup(function (answer)
+			handle = nil;
+			host_session.connecting = nil;
+			if answer then
+				log("debug", to_host.." has SRV records, handling...");
+				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");
+					s2s_destroy_session(host_session, err); -- Nothing to see here
+					return;
+				end
+				t_sort(srv_hosts, compare_srv_priorities);
+				
+				local srv_choice = srv_hosts[1];
+				host_session.srv_choice = 1;
+				if srv_choice then
+					connect_host, connect_port = srv_choice.target or to_host, srv_choice.port or connect_port;
+					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");
+			end
+			-- Try with SRV, or just the plain hostname if no SRV
+			local ok, err = s2sout.try_connect(host_session, connect_host, connect_port);
+			if not ok then
+				if not s2sout.attempt_connection(host_session, err) then
+					-- No more attempts will be made
+					s2s_destroy_session(host_session, err);
+				end
+			end
+		end, "_xmpp-server._tcp."..connect_host..".", "SRV");
+		
+		return true; -- Attempt in progress
+	elseif host_session.ip_hosts then
+		return s2sout.try_connect(host_session, connect_host, connect_port, err);
+	elseif host_session.srv_hosts and #host_session.srv_hosts > host_session.srv_choice then -- Not our first attempt, and we also have SRV
+		host_session.srv_choice = host_session.srv_choice + 1;
+		local srv_choice = host_session.srv_hosts[host_session.srv_choice];
+		connect_host, connect_port = srv_choice.target or to_host, srv_choice.port or connect_port;
+		host_session.log("info", "Connection failed (%s). Attempt #%d: This time to %s:%d", tostring(err), host_session.srv_choice, connect_host, connect_port);
+	else
+		host_session.log("info", "Out of connection options, can't connect to %s", tostring(host_session.to_host));
+		-- We're out of options
+		return false;
+	end
+	
+	if not (connect_host and connect_port) then
+		-- Likely we couldn't resolve DNS
+		log("warn", "Hmm, we're without a host (%s) and port (%s) to connect to for %s, giving up :(", tostring(connect_host), tostring(connect_port), tostring(to_host));
+		return false;
+	end
+
+	return s2sout.try_connect(host_session, connect_host, connect_port);
+end
+
+function s2sout.try_next_ip(host_session)
+	host_session.connecting = nil;
+	host_session.ip_choice = host_session.ip_choice + 1;
+	local ip = host_session.ip_hosts[host_session.ip_choice];
+	local ok, err= s2sout.make_connect(host_session, ip.ip, ip.port);
+	if not ok then
+		if not s2sout.attempt_connection(host_session, err or "closed") then
+			err = err and (": "..err) or "";
+			s2s_destroy_session(host_session, "Connection failed"..err);
+		end
+	end
+end
+
+function s2sout.try_connect(host_session, connect_host, connect_port, err)
+	host_session.connecting = true;
+
+	if not err then
+		local IPs = {};
+		host_session.ip_hosts = IPs;
+		local handle4, handle6;
+		local has_other = false;
+
+		if not sources then
+			sources =  {};
+			for i, source in ipairs(cfg_sources) do
+				if source == "*" then
+					sources[i] = new_ip("0.0.0.0", "IPv4");
+				else
+					sources[i] = new_ip(source, (source:find(":") and "IPv6") or "IPv4");
+				end
+			end
+		end
+
+		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
+
+			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
+
+			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
+					end
+				end
+			else
+				has_other = true;
+			end
+		end, connect_host, "A", "IN");
+
+		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
+
+			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
+					end
+				end
+			else
+				has_other = true;
+			end
+		end, connect_host, "AAAA", "IN");
+
+		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);
+	else
+		host_session.ip_hosts = nil;
+		if not s2sout.attempt_connection(host_session, "out of IP addresses") 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, "Connecting failed"..err); -- End of the line, we can't
+			return false;
+		end
+	end
+
+	return true;
+end
+
+function s2sout.make_connect(host_session, connect_host, connect_port)
+	(host_session.log or log)("info", "Beginning new connection attempt to %s ([%s]:%d)", host_session.to_host, connect_host.addr, connect_port);
+	-- Ok, we're going to try to connect
+	
+	local from_host, to_host = host_session.from_host, host_session.to_host;
+	
+	local conn, handler;
+	if connect_host.proto == "IPv4" then
+		conn, handler = socket.tcp();
+	else
+		conn, handler = socket.tcp6();
+	end
+	
+	if not conn then
+		log("warn", "Failed to create outgoing connection, system error: %s", handler);
+		return false, handler;
+	end
+
+	conn:settimeout(0);
+	local success, err = conn:connect(connect_host.addr, connect_port);
+	if not success and err ~= "timeout" then
+		log("warn", "s2s connect() to %s (%s:%d) failed: %s", host_session.to_host, connect_host.addr, connect_port, err);
+		return false, err;
+	end
+	
+	conn = wrapclient(conn, connect_host.addr, connect_port, s2s_listener, "*a");
+	host_session.conn = conn;
+	
+	local filter = initialize_filters(host_session);
+	local w, log = conn.write, host_session.log;
+	host_session.sends2s = function (t)
+		log("debug", "sending: %s", (t.top_tag and t:top_tag()) or t:match("^[^>]*>?"));
+		if t.name then
+			t = filter("stanzas/out", t);
+		end
+		if t then
+			t = filter("bytes/out", tostring(t));
+			if t then
+				return w(conn, tostring(t));
+			end
+		end
+	end
+	
+	-- Register this outgoing connection so that xmppserver_listener knows about it
+	-- otherwise it will assume it is a new incoming connection
+	s2s_listener.register_outgoing(conn, host_session);
+	
+	host_session:open_stream(from_host, to_host);
+	
+	log("debug", "Connection attempt in progress...");
+	return true;
+end
+
+return s2sout;
-- 
cgit v1.2.3


From 6193d32da9e97508e62c3f4dfb0eb4d1a129c72a Mon Sep 17 00:00:00 2001
From: Kim Alvefur <zash@zash.se>
Date: Wed, 22 Feb 2012 23:12:57 +0100
Subject: mod_s2s: Add some missing imports

---
 plugins/s2s/mod_s2s.lua | 3 +++
 1 file changed, 3 insertions(+)

(limited to 'plugins')

diff --git a/plugins/s2s/mod_s2s.lua b/plugins/s2s/mod_s2s.lua
index 5be4d84b..219e6952 100644
--- a/plugins/s2s/mod_s2s.lua
+++ b/plugins/s2s/mod_s2s.lua
@@ -18,6 +18,9 @@ local new_xmpp_stream = require "util.xmppstream".new;
 local s2s_new_incoming = require "core.s2smanager".new_incoming;
 local s2s_new_outgoing = require "core.s2smanager".new_outgoing;
 local s2s_destroy_session = require "core.s2smanager".destroy_session;
+local nameprep = require "util.encodings".stringprep.nameprep;
+local uuid_gen = require "util.uuid".generate;
+local cert_verify_identity = require "util.x509".verify_identity;
 
 local s2sout = module:require("s2sout");
 
-- 
cgit v1.2.3


From 364c78e2497fcc5d2e285970c48935e88a0261ff Mon Sep 17 00:00:00 2001
From: Kim Alvefur <zash@zash.se>
Date: Wed, 22 Feb 2012 23:14:21 +0100
Subject: mod_s2s: Fix typo

---
 plugins/s2s/mod_s2s.lua | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

(limited to 'plugins')

diff --git a/plugins/s2s/mod_s2s.lua b/plugins/s2s/mod_s2s.lua
index 219e6952..7c0a99de 100644
--- a/plugins/s2s/mod_s2s.lua
+++ b/plugins/s2s/mod_s2s.lua
@@ -192,7 +192,7 @@ function stream_callbacks.streamopened(session, attr)
 		if session.secure and not session.cert_chain_status then check_cert_status(session); end
 
 		send("<?xml version='1.0'?>");
-		send(stanza("stream:stream", { xmlns='jabber:server', ["xmlns:db"]='jabber:server:dialback',
+		send(st.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");
-- 
cgit v1.2.3


From 5ddde9afe607893a858d7249cb2fbf0b1b022f8f Mon Sep 17 00:00:00 2001
From: Kim Alvefur <zash@zash.se>
Date: Thu, 23 Feb 2012 23:03:28 +0100
Subject: mod_s2s: Initiate connections

---
 plugins/s2s/mod_s2s.lua | 1 +
 1 file changed, 1 insertion(+)

(limited to 'plugins')

diff --git a/plugins/s2s/mod_s2s.lua b/plugins/s2s/mod_s2s.lua
index 7c0a99de..bb1a6dd7 100644
--- a/plugins/s2s/mod_s2s.lua
+++ b/plugins/s2s/mod_s2s.lua
@@ -92,6 +92,7 @@ function send_to_host(from_host, to_host, stanza)
 	else
 		log("debug", "opening a new outgoing connection for this stanza");
 		local host_session = s2s_new_outgoing(from_host, to_host);
+		s2sout.initiate_connection(host_session);
 
 		-- Store in buffer
 		host_session.bounce_sendq = bounce_sendq;
-- 
cgit v1.2.3


From 7a3928f68e1bb18f483ce9c58c6f2e1d73204213 Mon Sep 17 00:00:00 2001
From: Kim Alvefur <zash@zash.se>
Date: Thu, 23 Feb 2012 23:04:59 +0100
Subject: mod_s2s: Attach send function to session

---
 plugins/s2s/mod_s2s.lua | 3 +++
 1 file changed, 3 insertions(+)

(limited to 'plugins')

diff --git a/plugins/s2s/mod_s2s.lua b/plugins/s2s/mod_s2s.lua
index bb1a6dd7..90bedce9 100644
--- a/plugins/s2s/mod_s2s.lua
+++ b/plugins/s2s/mod_s2s.lua
@@ -192,6 +192,9 @@ function stream_callbacks.streamopened(session, attr)
 
 		if session.secure and not session.cert_chain_status then check_cert_status(session); end
 
+		function session.send(data)
+			return send_to_host(session.to_host, session.from_host, data);
+		end
 		send("<?xml version='1.0'?>");
 		send(st.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());
-- 
cgit v1.2.3


From d30dec6f355008b4b60b7ae7739e5ca6baa48f05 Mon Sep 17 00:00:00 2001
From: Kim Alvefur <zash@zash.se>
Date: Thu, 23 Feb 2012 23:09:09 +0100
Subject: s2sout.lib: Import various util.*

---
 plugins/s2s/s2sout.lib.lua | 6 ++++++
 1 file changed, 6 insertions(+)

(limited to 'plugins')

diff --git a/plugins/s2s/s2sout.lib.lua b/plugins/s2s/s2sout.lib.lua
index 094126e7..d84854a8 100644
--- a/plugins/s2s/s2sout.lib.lua
+++ b/plugins/s2s/s2sout.lib.lua
@@ -8,12 +8,18 @@
 
 --- Module containing all the logic for connecting to a remote server
 
+local wrapclient = require "net.server".wrapclient;
 local initialize_filters = require "util.filters".initialize;
 local idna_to_ascii = require "util.encodings".idna.to_ascii;
 local add_task = require "util.timer".add_task;
+local new_ip = require "util.ip".new_ip;
+local rfc3484_dest = require "util.rfc3484".destination;
 local socket = require "socket";
+local t_insert, t_sort = table.insert, table.sort;
+local st = require "util.stanza";
 
 local s2s_destroy_session = require "core.s2smanager".destroy_session;
+local s2s_new_outgoing = require "core.s2smanager".new_outgoing;
 
 local s2sout = {};
 
-- 
cgit v1.2.3


From cf1aa003da3524d839effd7c9f7dd5eb1a46510d Mon Sep 17 00:00:00 2001
From: Kim Alvefur <zash@zash.se>
Date: Thu, 23 Feb 2012 23:12:08 +0100
Subject: s2sout.lib: Add locals for source interfaces

---
 plugins/s2s/s2sout.lib.lua | 3 +++
 1 file changed, 3 insertions(+)

(limited to 'plugins')

diff --git a/plugins/s2s/s2sout.lib.lua b/plugins/s2s/s2sout.lib.lua
index d84854a8..04a5c072 100644
--- a/plugins/s2s/s2sout.lib.lua
+++ b/plugins/s2s/s2sout.lib.lua
@@ -25,6 +25,9 @@ local s2sout = {};
 
 local s2s_listener;
 
+local cfg_sources = config.get("*", "core", "s2s_interfaces") or {"*"};
+local sources
+
 function s2sout.set_listener(listener)
 	s2s_listener = listener;
 end
-- 
cgit v1.2.3


From ddef415e167351b9197a06bfd1d401e70dd37e13 Mon Sep 17 00:00:00 2001
From: Kim Alvefur <zash@zash.se>
Date: Thu, 23 Feb 2012 23:12:24 +0100
Subject: s2sout.lib: Fix wrong variable name

---
 plugins/s2s/s2sout.lib.lua | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

(limited to 'plugins')

diff --git a/plugins/s2s/s2sout.lib.lua b/plugins/s2s/s2sout.lib.lua
index 04a5c072..64786862 100644
--- a/plugins/s2s/s2sout.lib.lua
+++ b/plugins/s2s/s2sout.lib.lua
@@ -45,7 +45,7 @@ end
 
 function s2sout.initiate_connection(host_session)
 	initialize_filters(host_session);
-	session.open_stream = session_open_stream;
+	host_session.open_stream = session_open_stream;
 	
 	-- Kick the connection attempting machine into life
 	if not s2sout.attempt_connection(host_session) then
-- 
cgit v1.2.3


From 24bfbf5ce26959ffcd466ce43c69a07adaa4c81b Mon Sep 17 00:00:00 2001
From: Florian Zeitz <florob@babelmonkeys.de>
Date: Fri, 24 Feb 2012 15:20:03 +0000
Subject: mod_dialback: import util.hashes and functionality once in
 s2smanager.

---
 plugins/mod_dialback.lua | 30 +++++++++++++++++++++++-------
 1 file changed, 23 insertions(+), 7 deletions(-)

(limited to 'plugins')

diff --git a/plugins/mod_dialback.lua b/plugins/mod_dialback.lua
index e27f8657..977e58c3 100644
--- a/plugins/mod_dialback.lua
+++ b/plugins/mod_dialback.lua
@@ -6,22 +6,37 @@
 -- COPYING file in the source package for more information.
 --
 
+local format = string.format;
 
 local hosts = _G.hosts;
 local send_s2s = require "core.s2smanager".send_to_host;
 local s2s_make_authenticated = require "core.s2smanager".make_authenticated;
-local s2s_initiate_dialback = require "core.s2smanager".initiate_dialback;
-local s2s_verify_dialback = require "core.s2smanager".verify_dialback;
 
 local log = module._log;
 
 local st = require "util.stanza";
+local sha256_hash = require "util.hashes".sha256;
 
 local xmlns_stream = "http://etherx.jabber.org/streams";
 local xmlns_dialback = "jabber:server:dialback";
 
 local dialback_requests = setmetatable({}, { __mode = 'v' });
 
+function generate_dialback(id, to, from)
+	return sha256_hash(id..to..from..hosts[from].dialback_secret, true);
+end
+
+function initiate_dialback(session)
+	-- generate dialback key
+	session.dialback_key = generate_dialback(session.streamid, session.to_host, session.from_host);
+	session.sends2s(format("<db:result from='%s' to='%s'>%s</db:result>", session.from_host, session.to_host, session.dialback_key));
+	session.log("info", "sent dialback key on outgoing s2s stream");
+end
+
+function verify_dialback(id, to, from, key)
+	return key == generate_dialback(id, to, from);
+end
+
 module:hook("stanza/jabber:server:dialback:verify", function(event)
 	local origin, stanza = event.origin, event.stanza;
 	
@@ -32,7 +47,7 @@ module:hook("stanza/jabber:server:dialback:verify", function(event)
 		-- 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;
-		if s2s_verify_dialback(attr.id, attr.from, attr.to, stanza[1]) then
+		if verify_dialback(attr.id, attr.from, attr.to, stanza[1]) then
 			type = "valid"
 		else
 			type = "invalid"
@@ -72,8 +87,8 @@ module:hook("stanza/jabber:server:dialback:result", function(event)
 		end
 		
 		origin.log("debug", "asking %s if key %s belongs to them", attr.from, stanza[1]);
-		send_s2s(attr.to, attr.from,
-			st.stanza("db:verify", { from = attr.to, to = attr.from, id = origin.streamid }):text(stanza[1]));
+		--send_s2s(attr.to, attr.from,
+		origin.send(st.stanza("db:verify", { from = attr.to, to = attr.from, id = origin.streamid }):text(stanza[1]));
 		return true;
 	end
 end);
@@ -84,6 +99,7 @@ module:hook("stanza/jabber:server:dialback:verify", function(event)
 	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
@@ -134,14 +150,14 @@ end);
 module:hook_stanza("urn:ietf:params:xml:ns:xmpp-sasl", "failure", function (origin, stanza)
 	if origin.external_auth == "failed" then
 		module:log("debug", "SASL EXTERNAL failed, falling back to dialback");
-		s2s_initiate_dialback(origin);
+		initiate_dialback(origin);
 		return true;
 	end
 end, 100);
 
 module:hook_stanza(xmlns_stream, "features", function (origin, stanza)
 	if not origin.external_auth or origin.external_auth == "failed" then
-		s2s_initiate_dialback(origin);
+		initiate_dialback(origin);
 		return true;
 	end
 end, 100);
-- 
cgit v1.2.3


From eda5f26eb482f9f74949248f0fd9bd60e4f5580d Mon Sep 17 00:00:00 2001
From: Florian Zeitz <florob@babelmonkeys.de>
Date: Fri, 24 Feb 2012 15:21:21 +0000
Subject: mod_s2s: port functionality once in s2smanager.

---
 plugins/s2s/mod_s2s.lua | 7 +++++--
 1 file changed, 5 insertions(+), 2 deletions(-)

(limited to 'plugins')

diff --git a/plugins/s2s/mod_s2s.lua b/plugins/s2s/mod_s2s.lua
index 5be4d84b..0de821b2 100644
--- a/plugins/s2s/mod_s2s.lua
+++ b/plugins/s2s/mod_s2s.lua
@@ -14,10 +14,12 @@ local xpcall, traceback = xpcall, debug.traceback;
 local add_task = require "util.timer".add_task;
 local st = require "util.stanza";
 local initialize_filters = require "util.filters".initialize;
+local nameprep = require "util.encodings".stringprep.nameprep;
 local new_xmpp_stream = require "util.xmppstream".new;
 local s2s_new_incoming = require "core.s2smanager".new_incoming;
 local s2s_new_outgoing = require "core.s2smanager".new_outgoing;
 local s2s_destroy_session = require "core.s2smanager".destroy_session;
+local uuid_gen = require "util.uuid".generate;
 
 local s2sout = module:require("s2sout");
 
@@ -94,6 +96,7 @@ function send_to_host(from_host, to_host, stanza)
 		host_session.bounce_sendq = bounce_sendq;
 		host_session.sendq = { {tostring(stanza), stanza.attr.type ~= "error" and stanza.attr.type ~= "result" and st.reply(stanza)} };
 		log("debug", "stanza [%s] queued until connection complete", tostring(stanza.name));
+		s2sout.initiate_connection(host_session);
 		if (not host_session.connecting) and (not host_session.conn) then
 			log("warn", "Connection to %s failed already, destroying session...", to_host);
 			if not s2s_destroy_session(host_session, "Connection failed") then
@@ -102,7 +105,6 @@ function send_to_host(from_host, to_host, stanza)
 			end
 			return false;
 		end
-		s2sout.initiate_connection(host_session);
 	end
 	return true;
 end
@@ -189,7 +191,7 @@ function stream_callbacks.streamopened(session, attr)
 		if session.secure and not session.cert_chain_status then check_cert_status(session); end
 
 		send("<?xml version='1.0'?>");
-		send(stanza("stream:stream", { xmlns='jabber:server', ["xmlns:db"]='jabber:server:dialback',
+		send(stanza.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");
@@ -235,6 +237,7 @@ function stream_callbacks.streamopened(session, attr)
 		end
 	end
 	session.notopen = nil;
+	session.send = function(stanza) send_to_host(session.to_host, session.from_host, stanza); end;
 end
 
 function stream_callbacks.streamclosed(session)
-- 
cgit v1.2.3


From 35ddcf0af31198baef0f4ce2e580b041486d5194 Mon Sep 17 00:00:00 2001
From: Florian Zeitz <florob@babelmonkeys.de>
Date: Fri, 24 Feb 2012 15:24:10 +0000
Subject: s2sout.lib: import utils/functionality once in s2smanager.

---
 plugins/s2s/s2sout.lib.lua | 16 ++++++++++++++--
 1 file changed, 14 insertions(+), 2 deletions(-)

(limited to 'plugins')

diff --git a/plugins/s2s/s2sout.lib.lua b/plugins/s2s/s2sout.lib.lua
index 094126e7..12669d97 100644
--- a/plugins/s2s/s2sout.lib.lua
+++ b/plugins/s2s/s2sout.lib.lua
@@ -8,13 +8,24 @@
 
 --- Module containing all the logic for connecting to a remote server
 
+local t_insert = table.insert;
+local t_sort = table.sort;
+local ipairs = ipairs;
+
+local wrapclient = require "net.server".wrapclient;
 local initialize_filters = require "util.filters".initialize;
 local idna_to_ascii = require "util.encodings".idna.to_ascii;
 local add_task = require "util.timer".add_task;
+local st = require "util.stanza";
+local new_ip = require "util.ip".new_ip;
+local rfc3484_dest = require "util.rfc3484".destination;
 local socket = require "socket";
 
+local s2s_new_outgoing = require "core.s2smanager".new_outgoing;
 local s2s_destroy_session = require "core.s2smanager".destroy_session;
 
+local cfg_sources = config.get("*", "core", "s2s_interfaces") or socket.local_addresses();
+
 local s2sout = {};
 
 local s2s_listener;
@@ -36,7 +47,7 @@ end
 
 function s2sout.initiate_connection(host_session)
 	initialize_filters(host_session);
-	session.open_stream = session_open_stream;
+	host_session.open_stream = session_open_stream;
 	
 	-- Kick the connection attempting machine into life
 	if not s2sout.attempt_connection(host_session) then
@@ -147,6 +158,7 @@ function s2sout.try_next_ip(host_session)
 end
 
 function s2sout.try_connect(host_session, connect_host, connect_port, err)
+	local sources;
 	host_session.connecting = true;
 
 	if not err then
@@ -156,7 +168,7 @@ function s2sout.try_connect(host_session, connect_host, connect_port, err)
 		local has_other = false;
 
 		if not sources then
-			sources =  {};
+			sources = {};
 			for i, source in ipairs(cfg_sources) do
 				if source == "*" then
 					sources[i] = new_ip("0.0.0.0", "IPv4");
-- 
cgit v1.2.3


From c5f76e99d91e5e0fc2c2b47a7ce7499c46fb57ec Mon Sep 17 00:00:00 2001
From: Marco Cirillo <maranda@lightwitch.org>
Date: Fri, 24 Feb 2012 15:34:25 +0000
Subject: mod_s2s, s2sout.lib: import cert verify and add another fallback
 method in case socket.local_addresses isn't there.

---
 plugins/s2s/mod_s2s.lua    | 1 +
 plugins/s2s/s2sout.lib.lua | 2 +-
 2 files changed, 2 insertions(+), 1 deletion(-)

(limited to 'plugins')

diff --git a/plugins/s2s/mod_s2s.lua b/plugins/s2s/mod_s2s.lua
index 0de821b2..88e8cded 100644
--- a/plugins/s2s/mod_s2s.lua
+++ b/plugins/s2s/mod_s2s.lua
@@ -20,6 +20,7 @@ local s2s_new_incoming = require "core.s2smanager".new_incoming;
 local s2s_new_outgoing = require "core.s2smanager".new_outgoing;
 local s2s_destroy_session = require "core.s2smanager".destroy_session;
 local uuid_gen = require "util.uuid".generate;
+local cert_verify_identity = require "util.x509".verify_identity;
 
 local s2sout = module:require("s2sout");
 
diff --git a/plugins/s2s/s2sout.lib.lua b/plugins/s2s/s2sout.lib.lua
index 12669d97..2856a15c 100644
--- a/plugins/s2s/s2sout.lib.lua
+++ b/plugins/s2s/s2sout.lib.lua
@@ -24,7 +24,7 @@ local socket = require "socket";
 local s2s_new_outgoing = require "core.s2smanager".new_outgoing;
 local s2s_destroy_session = require "core.s2smanager".destroy_session;
 
-local cfg_sources = config.get("*", "core", "s2s_interfaces") or socket.local_addresses();
+local cfg_sources = config.get("*", "core", "s2s_interfaces") or socket.local_addresses and socket.local_addresses() or { "*" };
 
 local s2sout = {};
 
-- 
cgit v1.2.3


From f04b6b3bada77a1888082383b85981fe1ced6784 Mon Sep 17 00:00:00 2001
From: Marco Cirillo <maranda@lightwitch.org>
Date: Fri, 24 Feb 2012 15:35:04 +0000
Subject: mod_admin_telnet: make service private.

---
 plugins/mod_admin_telnet.lua | 1 +
 1 file changed, 1 insertion(+)

(limited to 'plugins')

diff --git a/plugins/mod_admin_telnet.lua b/plugins/mod_admin_telnet.lua
index 9e9065bb..d3f5543e 100644
--- a/plugins/mod_admin_telnet.lua
+++ b/plugins/mod_admin_telnet.lua
@@ -776,4 +776,5 @@ end
 require "core.portmanager".register_service("console", {
 	listener = console_listener;
 	default_port = 5582;
+	private = true;
 });
-- 
cgit v1.2.3


From 0f9c104f0cb7f20a63bf6f890d0d04a690418a1d Mon Sep 17 00:00:00 2001
From: Marco Cirillo <maranda@lightwitch.org>
Date: Fri, 24 Feb 2012 15:36:36 +0000
Subject: mod_s2s: prevent attempting to reconnect when the stream is
 gracefully closed and fix TB by checking session.conn is set (racy racy?)

---
 plugins/s2s/mod_s2s.lua | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

(limited to 'plugins')

diff --git a/plugins/s2s/mod_s2s.lua b/plugins/s2s/mod_s2s.lua
index 88e8cded..d1fdedb3 100644
--- a/plugins/s2s/mod_s2s.lua
+++ b/plugins/s2s/mod_s2s.lua
@@ -247,7 +247,7 @@ function stream_callbacks.streamclosed(session)
 end
 
 function stream_callbacks.streamdisconnected(session, err)
-	if err and err ~= "closed" then
+	if err and err ~= "stream closed" 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");
@@ -255,7 +255,7 @@ function stream_callbacks.streamdisconnected(session, err)
 		end
 	end
 	(session.log or log)("info", "s2s disconnected: %s->%s (%s)", tostring(session.from_host), tostring(session.to_host), tostring(err or "closed"));
-	sessions[session.conn]  = nil;
+	if session.con then sessions[session.conn] = nil; else (session.log or log)("debug", "stale session's connection already closed"); end
 	s2s_destroy_session(session, err);
 end
 
-- 
cgit v1.2.3


From b51c0bea3f88b1bc58ffc83c3ec2b0ef6fc31235 Mon Sep 17 00:00:00 2001
From: Matthew Wild <mwild1@gmail.com>
Date: Fri, 24 Feb 2012 16:25:38 +0000
Subject: Backed out changeset aba47e6dff43

---
 plugins/s2s/mod_s2s.lua | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

(limited to 'plugins')

diff --git a/plugins/s2s/mod_s2s.lua b/plugins/s2s/mod_s2s.lua
index d1fdedb3..88e8cded 100644
--- a/plugins/s2s/mod_s2s.lua
+++ b/plugins/s2s/mod_s2s.lua
@@ -247,7 +247,7 @@ function stream_callbacks.streamclosed(session)
 end
 
 function stream_callbacks.streamdisconnected(session, err)
-	if err and err ~= "stream closed" then
+	if err and err ~= "closed" 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");
@@ -255,7 +255,7 @@ function stream_callbacks.streamdisconnected(session, err)
 		end
 	end
 	(session.log or log)("info", "s2s disconnected: %s->%s (%s)", tostring(session.from_host), tostring(session.to_host), tostring(err or "closed"));
-	if session.con then sessions[session.conn] = nil; else (session.log or log)("debug", "stale session's connection already closed"); end
+	sessions[session.conn]  = nil;
 	s2s_destroy_session(session, err);
 end
 
-- 
cgit v1.2.3


From 74bff42057bbe1dbf278959cff1ae8cab077ac19 Mon Sep 17 00:00:00 2001
From: Marco Cirillo <maranda@lightwitch.org>
Date: Fri, 24 Feb 2012 18:03:27 +0000
Subject: s2smanager, mod_s2s: clear up ip_hosts after s2s is marked as
 established, remove useless space from mod_s2s code

---
 plugins/s2s/mod_s2s.lua | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

(limited to 'plugins')

diff --git a/plugins/s2s/mod_s2s.lua b/plugins/s2s/mod_s2s.lua
index 2573251f..9f522595 100644
--- a/plugins/s2s/mod_s2s.lua
+++ b/plugins/s2s/mod_s2s.lua
@@ -255,7 +255,7 @@ function stream_callbacks.streamdisconnected(session, err)
 		end
 	end
 	(session.log or log)("info", "s2s disconnected: %s->%s (%s)", tostring(session.from_host), tostring(session.to_host), tostring(err or "closed"));
-	sessions[session.conn]  = nil;
+	sessions[session.conn] = nil;
 	s2s_destroy_session(session, err);
 end
 
-- 
cgit v1.2.3


From f1e9bb68bd0f366d060c1834dd4955e5dbb1efc4 Mon Sep 17 00:00:00 2001
From: Kim Alvefur <zash@zash.se>
Date: Thu, 1 Mar 2012 00:14:53 +0100
Subject: mod_s2s: Add missing local table.insert

---
 plugins/s2s/mod_s2s.lua | 1 +
 1 file changed, 1 insertion(+)

(limited to 'plugins')

diff --git a/plugins/s2s/mod_s2s.lua b/plugins/s2s/mod_s2s.lua
index 9f522595..ec01934d 100644
--- a/plugins/s2s/mod_s2s.lua
+++ b/plugins/s2s/mod_s2s.lua
@@ -9,6 +9,7 @@
 module:set_global();
 
 local tostring, type = tostring, type;
+local t_insert = table.insert;
 local xpcall, traceback = xpcall, debug.traceback;
 
 local add_task = require "util.timer".add_task;
-- 
cgit v1.2.3


From 11993525c0a7cae7fe52aba0bafdfca912973499 Mon Sep 17 00:00:00 2001
From: Kim Alvefur <zash@zash.se>
Date: Sat, 3 Mar 2012 00:01:10 +0100
Subject: mod_dialback: Remove unused import of s2smanager.send_to_host()

---
 plugins/mod_dialback.lua | 2 --
 1 file changed, 2 deletions(-)

(limited to 'plugins')

diff --git a/plugins/mod_dialback.lua b/plugins/mod_dialback.lua
index 977e58c3..eccfd3d6 100644
--- a/plugins/mod_dialback.lua
+++ b/plugins/mod_dialback.lua
@@ -9,7 +9,6 @@
 local format = string.format;
 
 local hosts = _G.hosts;
-local send_s2s = require "core.s2smanager".send_to_host;
 local s2s_make_authenticated = require "core.s2smanager".make_authenticated;
 
 local log = module._log;
@@ -87,7 +86,6 @@ module:hook("stanza/jabber:server:dialback:result", function(event)
 		end
 		
 		origin.log("debug", "asking %s if key %s belongs to them", attr.from, stanza[1]);
-		--send_s2s(attr.to, attr.from,
 		origin.send(st.stanza("db:verify", { from = attr.to, to = attr.from, id = origin.streamid }):text(stanza[1]));
 		return true;
 	end
-- 
cgit v1.2.3


From 8f72c46ae033d0f57e37fd1c8e44f94304f8afde Mon Sep 17 00:00:00 2001
From: Kim Alvefur <zash@zash.se>
Date: Sat, 3 Mar 2012 00:03:06 +0100
Subject: mod_s2s: Split send_to_host() into two route/remote hooks, one for
 already exsisting sessions and one for non-existent.

---
 plugins/s2s/mod_s2s.lua | 47 +++++++++++++++++++++++------------------------
 1 file changed, 23 insertions(+), 24 deletions(-)

(limited to 'plugins')

diff --git a/plugins/s2s/mod_s2s.lua b/plugins/s2s/mod_s2s.lua
index ec01934d..c00ff653 100644
--- a/plugins/s2s/mod_s2s.lua
+++ b/plugins/s2s/mod_s2s.lua
@@ -60,7 +60,8 @@ local function bounce_sendq(session, reason)
 	session.sendq = nil;
 end
 
-function send_to_host(from_host, to_host, stanza)
+module:hook("route/remote", function (event)
+	local from_host, to_host, stanza = event.from_host, event.to_host, event.stanza;
 	if not hosts[from_host] then
 		log("warn", "Attempt to send stanza from %s - a host we don't serve", from_host);
 		return false;
@@ -70,7 +71,7 @@ function send_to_host(from_host, to_host, stanza)
 		-- We have a connection to this host already
 		if host.type == "s2sout_unauthed" and (stanza.name ~= "db:verify" or not host.dialback_key) then
 			(host.log or log)("debug", "trying to send over unauthed s2sout to "..to_host);
-			
+
 			-- Queue stanza until we are able to send it
 			if host.sendq then t_insert(host.sendq, {tostring(stanza), stanza.attr.type ~= "error" and stanza.attr.type ~= "result" and st.reply(stanza)});
 			else host.sendq = { {tostring(stanza), stanza.attr.type ~= "error" and stanza.attr.type ~= "result" and st.reply(stanza)} }; end
@@ -90,30 +91,28 @@ function send_to_host(from_host, to_host, stanza)
 			host.sends2s(stanza);
 			host.log("debug", "stanza sent over "..host.type);
 		end
-	else
-		log("debug", "opening a new outgoing connection for this stanza");
-		local host_session = s2s_new_outgoing(from_host, to_host);
-
-		-- Store in buffer
-		host_session.bounce_sendq = bounce_sendq;
-		host_session.sendq = { {tostring(stanza), stanza.attr.type ~= "error" and stanza.attr.type ~= "result" and st.reply(stanza)} };
-		log("debug", "stanza [%s] queued until connection complete", tostring(stanza.name));
-		s2sout.initiate_connection(host_session);
-		if (not host_session.connecting) and (not host_session.conn) then
-			log("warn", "Connection to %s failed already, destroying session...", to_host);
-			if not s2s_destroy_session(host_session, "Connection failed") then
-				-- Already destroyed, we need to bounce our stanza
-				host_session:bounce_sendq(host_session.destruction_reason);
-			end
-			return false;
-		end
 	end
-	return true;
-end
+end, 200);
 
 module:hook("route/remote", function (event)
-	return send_to_host(event.from_host, event.to_host, event.stanza);
-end);
+	local from_host, to_host, stanza = event.from_host, event.to_host, event.stanza;
+	log("debug", "opening a new outgoing connection for this stanza");
+	local host_session = s2s_new_outgoing(from_host, to_host);
+
+	-- Store in buffer
+	host_session.bounce_sendq = bounce_sendq;
+	host_session.sendq = { {tostring(stanza), stanza.attr.type ~= "error" and stanza.attr.type ~= "result" and st.reply(stanza)} };
+	log("debug", "stanza [%s] queued until connection complete", tostring(stanza.name));
+	s2sout.initiate_connection(host_session);
+	if (not host_session.connecting) and (not host_session.conn) then
+		log("warn", "Connection to %s failed already, destroying session...", to_host);
+		if not s2s_destroy_session(host_session, "Connection failed") then
+			-- Already destroyed, we need to bounce our stanza
+			host_session:bounce_sendq(host_session.destruction_reason);
+		end
+		return false;
+	end
+end, 100);
 
 --- Helper to check that a session peer's certificate is valid
 local function check_cert_status(session)
@@ -239,7 +238,7 @@ function stream_callbacks.streamopened(session, attr)
 		end
 	end
 	session.notopen = nil;
-	session.send = function(stanza) send_to_host(session.to_host, session.from_host, stanza); end;
+	session.send = function(stanza) prosody.events.fire_event("route/remote", { from_host = session.to_host, to_host = session.from_host, stanza = stanza}) end;
 end
 
 function stream_callbacks.streamclosed(session)
-- 
cgit v1.2.3


From 82925c2003205354bc57d48769eefbc289e825bf Mon Sep 17 00:00:00 2001
From: Kim Alvefur <zash@zash.se>
Date: Sat, 3 Mar 2012 00:14:48 +0100
Subject: mod_s2s: return true when we sent the stanza, or initiated a new
 s2sout

---
 plugins/s2s/mod_s2s.lua | 2 ++
 1 file changed, 2 insertions(+)

(limited to 'plugins')

diff --git a/plugins/s2s/mod_s2s.lua b/plugins/s2s/mod_s2s.lua
index c00ff653..de3ad4e1 100644
--- a/plugins/s2s/mod_s2s.lua
+++ b/plugins/s2s/mod_s2s.lua
@@ -90,6 +90,7 @@ module:hook("route/remote", function (event)
 			end
 			host.sends2s(stanza);
 			host.log("debug", "stanza sent over "..host.type);
+			return true;
 		end
 	end
 end, 200);
@@ -112,6 +113,7 @@ module:hook("route/remote", function (event)
 		end
 		return false;
 	end
+	return true;
 end, 100);
 
 --- Helper to check that a session peer's certificate is valid
-- 
cgit v1.2.3


From 92dbf7e7a2a3ad3139d2a884cf90c740acfa9987 Mon Sep 17 00:00:00 2001
From: Kim Alvefur <zash@zash.se>
Date: Sat, 3 Mar 2012 00:32:57 +0100
Subject: mod_admin_telnet: Import util.iterators properly

---
 plugins/mod_admin_telnet.lua | 3 ++-
 1 file changed, 2 insertions(+), 1 deletion(-)

(limited to 'plugins')

diff --git a/plugins/mod_admin_telnet.lua b/plugins/mod_admin_telnet.lua
index d3f5543e..fd72787e 100644
--- a/plugins/mod_admin_telnet.lua
+++ b/plugins/mod_admin_telnet.lua
@@ -15,7 +15,8 @@ local hosts = prosody.hosts;
 
 local console_listener = { default_port = 5582; default_mode = "*l"; interface = "127.0.0.1" };
 
-require "util.iterators";
+local iterators = require "util.iterators";
+local keys, values = iterators.keys, iterators.values;
 local jid_bare = require "util.jid".bare;
 local set, array = require "util.set", require "util.array";
 local cert_verify_identity = require "util.x509".verify_identity;
-- 
cgit v1.2.3


From 9af8ea363badf58a8d59e475035a4def10dd42c9 Mon Sep 17 00:00:00 2001
From: Kim Alvefur <zash@zash.se>
Date: Sat, 3 Mar 2012 01:35:39 +0100
Subject: mod_s2s, mod_dialback: Event on pre-XMPP streams, so we can try
 dialback.

---
 plugins/mod_dialback.lua | 5 +++++
 plugins/s2s/mod_s2s.lua  | 2 +-
 2 files changed, 6 insertions(+), 1 deletion(-)

(limited to 'plugins')

diff --git a/plugins/mod_dialback.lua b/plugins/mod_dialback.lua
index eccfd3d6..5cb59fef 100644
--- a/plugins/mod_dialback.lua
+++ b/plugins/mod_dialback.lua
@@ -160,6 +160,11 @@ module:hook_stanza(xmlns_stream, "features", function (origin, stanza)
 	end
 end, 100);
 
+module:hook("s2s-no-stream-features", function (event)
+	initiate_dialback(event.origin);
+	return true;
+end, 100);
+
 -- Offer dialback to incoming hosts
 module:hook("s2s-stream-features", function (data)
 	data.features:tag("dialback", { xmlns='urn:xmpp:features:dialback' }):up();
diff --git a/plugins/s2s/mod_s2s.lua b/plugins/s2s/mod_s2s.lua
index de3ad4e1..9ab2ee0a 100644
--- a/plugins/s2s/mod_s2s.lua
+++ b/plugins/s2s/mod_s2s.lua
@@ -233,7 +233,7 @@ function stream_callbacks.streamopened(session, attr)
 		if session.version < 1.0 then
 			if not session.dialback_verifying then
 				log("debug", "Initiating dialback...");
-				initiate_dialback(session);
+				hosts[session.from_host].events.fire_event("s2s-no-stream-features", { origin = session });
 			else
 				s2s_mark_connected(session);
 			end
-- 
cgit v1.2.3


From d0424f6f4e316f435f8e4db6f4906964c7d8f31f Mon Sep 17 00:00:00 2001
From: Kim Alvefur <zash@zash.se>
Date: Sat, 3 Mar 2012 13:28:33 +0100
Subject: mod_s2s, mod_dialback: Rename event to s2s-authenticate-legacy

---
 plugins/mod_dialback.lua | 2 +-
 plugins/s2s/mod_s2s.lua  | 2 +-
 2 files changed, 2 insertions(+), 2 deletions(-)

(limited to 'plugins')

diff --git a/plugins/mod_dialback.lua b/plugins/mod_dialback.lua
index 5cb59fef..2c2dc795 100644
--- a/plugins/mod_dialback.lua
+++ b/plugins/mod_dialback.lua
@@ -160,7 +160,7 @@ module:hook_stanza(xmlns_stream, "features", function (origin, stanza)
 	end
 end, 100);
 
-module:hook("s2s-no-stream-features", function (event)
+module:hook("s2s-authenticate-legacy", function (event)
 	initiate_dialback(event.origin);
 	return true;
 end, 100);
diff --git a/plugins/s2s/mod_s2s.lua b/plugins/s2s/mod_s2s.lua
index 9ab2ee0a..bebf1962 100644
--- a/plugins/s2s/mod_s2s.lua
+++ b/plugins/s2s/mod_s2s.lua
@@ -233,7 +233,7 @@ function stream_callbacks.streamopened(session, attr)
 		if session.version < 1.0 then
 			if not session.dialback_verifying then
 				log("debug", "Initiating dialback...");
-				hosts[session.from_host].events.fire_event("s2s-no-stream-features", { origin = session });
+				hosts[session.from_host].events.fire_event("s2s-authenticate-legacy", { origin = session });
 			else
 				s2s_mark_connected(session);
 			end
-- 
cgit v1.2.3


From 58900081f68e8db61675febc4f9c4078e44d384a Mon Sep 17 00:00:00 2001
From: Kim Alvefur <zash@zash.se>
Date: Sat, 3 Mar 2012 16:45:34 +0100
Subject: mod_dialback, mod_s2s: Log initiation of dialback in mod_dialback

---
 plugins/mod_dialback.lua | 2 ++
 plugins/s2s/mod_s2s.lua  | 1 -
 2 files changed, 2 insertions(+), 1 deletion(-)

(limited to 'plugins')

diff --git a/plugins/mod_dialback.lua b/plugins/mod_dialback.lua
index 2c2dc795..e578c412 100644
--- a/plugins/mod_dialback.lua
+++ b/plugins/mod_dialback.lua
@@ -155,12 +155,14 @@ end, 100);
 
 module:hook_stanza(xmlns_stream, "features", function (origin, stanza)
 	if not origin.external_auth or origin.external_auth == "failed" then
+		module:log("debug", "Initiating dialback...");
 		initiate_dialback(origin);
 		return true;
 	end
 end, 100);
 
 module:hook("s2s-authenticate-legacy", function (event)
+	module:log("debug", "Initiating dialback...");
 	initiate_dialback(event.origin);
 	return true;
 end, 100);
diff --git a/plugins/s2s/mod_s2s.lua b/plugins/s2s/mod_s2s.lua
index bebf1962..407a7e04 100644
--- a/plugins/s2s/mod_s2s.lua
+++ b/plugins/s2s/mod_s2s.lua
@@ -232,7 +232,6 @@ function stream_callbacks.streamopened(session, attr)
 		-- If server is pre-1.0, don't wait for features, just do dialback
 		if session.version < 1.0 then
 			if not session.dialback_verifying then
-				log("debug", "Initiating dialback...");
 				hosts[session.from_host].events.fire_event("s2s-authenticate-legacy", { origin = session });
 			else
 				s2s_mark_connected(session);
-- 
cgit v1.2.3


From 14a9e3f3c9919642364ea3076fe97cf3adecdd65 Mon Sep 17 00:00:00 2001
From: Kim Alvefur <zash@zash.se>
Date: Sun, 4 Mar 2012 17:39:19 +0100
Subject: mod_s2s: Log the entire stream header.

---
 plugins/s2s/mod_s2s.lua | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

(limited to 'plugins')

diff --git a/plugins/s2s/mod_s2s.lua b/plugins/s2s/mod_s2s.lua
index 407a7e04..5e03e896 100644
--- a/plugins/s2s/mod_s2s.lua
+++ b/plugins/s2s/mod_s2s.lua
@@ -172,7 +172,7 @@ function stream_callbacks.streamopened(session, attr)
 		session.from_host = attr.from and nameprep(attr.from);
 	
 		session.streamid = uuid_gen();
-		(session.log or log)("debug", "Incoming s2s received <stream:stream>");
+		(session.log or log)("debug", "Incoming s2s received %s", st.stanza("stream:stream", attr):top_tag());
 		if session.to_host then
 			if not hosts[session.to_host] then
 				-- Attempting to connect to a host we don't serve
-- 
cgit v1.2.3


From 5ccc1d34107c59823d859fe77c1b404b817de194 Mon Sep 17 00:00:00 2001
From: Matthew Wild <mwild1@gmail.com>
Date: Mon, 5 Mar 2012 11:07:10 +0000
Subject: mod_s2s: streamopened(): Tighter validation around stream 'to' and
 'from' attributes, and only set to_host and from_host if they aren't set
 already and if the session hasn't already been authenticated

---
 plugins/s2s/mod_s2s.lua | 30 +++++++++++++++++++++++++++---
 1 file changed, 27 insertions(+), 3 deletions(-)

(limited to 'plugins')

diff --git a/plugins/s2s/mod_s2s.lua b/plugins/s2s/mod_s2s.lua
index 407a7e04..fcdd9dd6 100644
--- a/plugins/s2s/mod_s2s.lua
+++ b/plugins/s2s/mod_s2s.lua
@@ -168,9 +168,33 @@ function stream_callbacks.streamopened(session, attr)
 
 	if session.direction == "incoming" then
 		-- Send a reply stream header
-		session.to_host = attr.to and nameprep(attr.to);
-		session.from_host = attr.from and nameprep(attr.from);
-	
+		
+		-- Validate to/from
+		local to, from = nameprep(attr.to), nameprep(attr.from);
+		if not to and attr.to then -- COMPAT: Some servers do not reliably set 'to' (especially on stream restarts)
+			session:close({ condition = "improper-addressing", text = "Invalid 'to' address" });
+			return;
+		end
+		if not from and attr.from then -- COMPAT: Some servers do not reliably set 'from' (especially on stream restarts)
+			session:close({ condition = "improper-addressing", text = "Invalid 'from' address" });
+			return;
+		end
+		
+		-- Set session.[from/to]_host if they have not been set already and if
+		-- this session isn't already authenticated
+		if session.type == "s2sin_unauthed" and from and not session.from_host then
+			session.from_host = from;
+		elseif from ~= session.from_host then
+			session:close({ condition = "improper-addressing", text = "New stream 'from' attribute does not match original" });
+			return;
+		end
+		if session.type == "s2sin_unauthed" and to and not session.to_host then
+			session.to_host = to;
+		elseif to ~= session.to_host then
+			session:close({ condition = "improper-addressing", text = "New stream 'to' attribute does not match original" });
+			return;
+		end
+		
 		session.streamid = uuid_gen();
 		(session.log or log)("debug", "Incoming s2s received <stream:stream>");
 		if session.to_host then
-- 
cgit v1.2.3


From c779ee4b9a029c14d23b71afe65b9462c097633a Mon Sep 17 00:00:00 2001
From: Marco Cirillo <maranda@lightwitch.org>
Date: Mon, 5 Mar 2012 16:39:50 +0000
Subject: mod_s2s: remove conn sessions clearing redundancy and leave it only
 in listener.ondisconnect

---
 plugins/s2s/mod_s2s.lua | 3 +--
 1 file changed, 1 insertion(+), 2 deletions(-)

(limited to 'plugins')

diff --git a/plugins/s2s/mod_s2s.lua b/plugins/s2s/mod_s2s.lua
index fcdd9dd6..ab122f12 100644
--- a/plugins/s2s/mod_s2s.lua
+++ b/plugins/s2s/mod_s2s.lua
@@ -280,7 +280,6 @@ function stream_callbacks.streamdisconnected(session, err)
 		end
 	end
 	(session.log or log)("info", "s2s disconnected: %s->%s (%s)", tostring(session.from_host), tostring(session.to_host), tostring(err or "closed"));
-	sessions[session.conn] = nil;
 	s2s_destroy_session(session, err);
 end
 
@@ -457,7 +456,7 @@ function listener.ondisconnect(conn, err)
 			return; -- Connection lives, for now
 		end
 	end
-	sessions[conn]  = nil;
+	sessions[conn] = nil;
 end
 
 function listener.register_outgoing(conn, session)
-- 
cgit v1.2.3


From df4bd370e7ad7bc3807df566d10b3d491e706bb8 Mon Sep 17 00:00:00 2001
From: Kim Alvefur <zash@zash.se>
Date: Wed, 7 Mar 2012 21:12:04 +0100
Subject: mod_admin_adhoc: Import util.iterators properly

---
 plugins/mod_admin_adhoc.lua | 3 ++-
 1 file changed, 2 insertions(+), 1 deletion(-)

(limited to 'plugins')

diff --git a/plugins/mod_admin_adhoc.lua b/plugins/mod_admin_adhoc.lua
index d78c1aee..a594483c 100644
--- a/plugins/mod_admin_adhoc.lua
+++ b/plugins/mod_admin_adhoc.lua
@@ -10,7 +10,8 @@ local prosody = _G.prosody;
 local hosts = prosody.hosts;
 local t_concat = table.concat;
 
-require "util.iterators";
+local iterators = require "util.iterators";
+local keys, values = iterators.keys, iterators.values;
 local usermanager_user_exists = require "core.usermanager".user_exists;
 local usermanager_create_user = require "core.usermanager".create_user;
 local usermanager_get_password = require "core.usermanager".get_password;
-- 
cgit v1.2.3


From d0e26c42f6716298c0cb7f245e25036c4f5d7561 Mon Sep 17 00:00:00 2001
From: Kim Alvefur <zash@zash.se>
Date: Wed, 7 Mar 2012 21:14:08 +0100
Subject: mod_admin_adhoc: Use module:depends()

---
 plugins/mod_admin_adhoc.lua | 1 +
 1 file changed, 1 insertion(+)

(limited to 'plugins')

diff --git a/plugins/mod_admin_adhoc.lua b/plugins/mod_admin_adhoc.lua
index a594483c..6f1357a9 100644
--- a/plugins/mod_admin_adhoc.lua
+++ b/plugins/mod_admin_adhoc.lua
@@ -24,6 +24,7 @@ local dataforms_new = require "util.dataforms".new;
 local array = require "util.array";
 local modulemanager = require "modulemanager";
 
+module:depends"adhoc";
 local adhoc_new = module:require "adhoc".new;
 
 function add_user_command_handler(self, data, state)
-- 
cgit v1.2.3


From 26768f3e54805074211fc0846221acce5765f570 Mon Sep 17 00:00:00 2001
From: Kim Alvefur <zash@zash.se>
Date: Sun, 11 Mar 2012 19:14:28 +0100
Subject: s2smanager, mod_s2s: Move import of dns_max_depth to mod_s2s

---
 plugins/s2s/s2sout.lib.lua | 2 ++
 1 file changed, 2 insertions(+)

(limited to 'plugins')

diff --git a/plugins/s2s/s2sout.lib.lua b/plugins/s2s/s2sout.lib.lua
index 808c7e74..4fcb094a 100644
--- a/plugins/s2s/s2sout.lib.lua
+++ b/plugins/s2s/s2sout.lib.lua
@@ -23,6 +23,8 @@ local s2s_destroy_session = require "core.s2smanager".destroy_session;
 
 local cfg_sources = config.get("*", "core", "s2s_interfaces") or socket.local_addresses and socket.local_addresses() or { "*" };
 
+local max_dns_depth = module:get_option_number("dns_max_depth", 3);
+
 local s2sout = {};
 
 local s2s_listener;
-- 
cgit v1.2.3


From 7497593c4e0dded060418304f23f709a2d1d664a Mon Sep 17 00:00:00 2001
From: Florian Zeitz <florob@babelmonkeys.de>
Date: Sun, 11 Mar 2012 20:15:42 +0100
Subject: mod_s2s: Remove obsolete default_interface

---
 plugins/s2s/mod_s2s.lua | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

(limited to 'plugins')

diff --git a/plugins/s2s/mod_s2s.lua b/plugins/s2s/mod_s2s.lua
index aa4d5d56..a496af57 100644
--- a/plugins/s2s/mod_s2s.lua
+++ b/plugins/s2s/mod_s2s.lua
@@ -320,7 +320,7 @@ function stream_callbacks.handlestanza(session, stanza)
 	end
 end
 
-local listener = { default_port = 5269, default_mode = "*a", default_interface = "*" };
+local listener = { default_port = 5269, default_mode = "*a" };
 
 --- Session methods
 local stream_xmlns_attr = {xmlns='urn:ietf:params:xml:ns:xmpp-streams'};
-- 
cgit v1.2.3


From eca225365d45123176a348c70f788c4719b43ee1 Mon Sep 17 00:00:00 2001
From: Florian Zeitz <florob@babelmonkeys.de>
Date: Sun, 11 Mar 2012 20:16:57 +0100
Subject: mod_s2s: Collect s2s sources from portmanager and get local address
 if necessary

---
 plugins/s2s/s2sout.lib.lua | 43 ++++++++++++++++++++++++++++++-------------
 1 file changed, 30 insertions(+), 13 deletions(-)

(limited to 'plugins')

diff --git a/plugins/s2s/s2sout.lib.lua b/plugins/s2s/s2sout.lib.lua
index 4fcb094a..f9f9d362 100644
--- a/plugins/s2s/s2sout.lib.lua
+++ b/plugins/s2s/s2sout.lib.lua
@@ -8,6 +8,7 @@
 
 --- Module containing all the logic for connecting to a remote server
 
+local portmanager = require "core.portmanager";
 local wrapclient = require "net.server".wrapclient;
 local initialize_filters = require "util.filters".initialize;
 local idna_to_ascii = require "util.encodings".idna.to_ascii;
@@ -21,7 +22,7 @@ local st = require "util.stanza";
 local s2s_new_outgoing = require "core.s2smanager".new_outgoing;
 local s2s_destroy_session = require "core.s2smanager".destroy_session;
 
-local cfg_sources = config.get("*", "core", "s2s_interfaces") or socket.local_addresses and socket.local_addresses() or { "*" };
+local sources = {};
 
 local max_dns_depth = module:get_option_number("dns_max_depth", 3);
 
@@ -158,7 +159,6 @@ function s2sout.try_next_ip(host_session)
 end
 
 function s2sout.try_connect(host_session, connect_host, connect_port, err)
-	local sources;
 	host_session.connecting = true;
 
 	if not err then
@@ -167,17 +167,6 @@ function s2sout.try_connect(host_session, connect_host, connect_port, err)
 		local handle4, handle6;
 		local has_other = false;
 
-		if not sources then
-			sources = {};
-			for i, source in ipairs(cfg_sources) do
-				if source == "*" then
-					sources[i] = new_ip("0.0.0.0", "IPv4");
-				else
-					sources[i] = new_ip(source, (source:find(":") and "IPv6") or "IPv4");
-				end
-			end
-		end
-
 		handle4 = adns.lookup(function (reply, err)
 			handle4 = nil;
 
@@ -323,4 +312,32 @@ function s2sout.make_connect(host_session, connect_host, connect_port)
 	return true;
 end
 
+module:hook_global("service-added", function (event)
+	if event.name ~= "s2s" then return end
+
+	local s2s_sources = portmanager.get_active_services():get("s2s");
+
+	for source, _ in pairs(s2s_sources) do
+		if source == "*" or source == "0.0.0.0" then
+			if not socket.local_addresses then
+				sources[#sources + 1] = new_ip("0.0.0.0", "IPv4");
+			else
+				for _, addr in ipairs(socket.local_addresses("ipv4", true)) do
+					sources[#sources + 1] = new_ip(addr, "IPv4");
+				end
+			end
+		elseif source == "::" then
+			if not socket.local_addresses then
+				sources[#sources + 1] = new_ip("::", "IPv4");
+			else
+				for _, addr in ipairs(socket.local_addresses("ipv6", true)) do
+					sources[#sources + 1] = new_ip(addr, "IPv6");
+				end
+			end
+		else
+			sources[#sources + 1] = new_ip(source, (source:find(":") and "IPv6") or "IPv4");
+		end
+	end
+end);
+
 return s2sout;
-- 
cgit v1.2.3


From c46d5d361b040b984882bb1917bcdc90d094ea94 Mon Sep 17 00:00:00 2001
From: Kim Alvefur <zash@zash.se>
Date: Sun, 11 Mar 2012 21:37:55 +0100
Subject: mod_s2s: Don't bounce sendq on failed connections since it's handled
 by s2smanager

---
 plugins/s2s/mod_s2s.lua | 5 +----
 1 file changed, 1 insertion(+), 4 deletions(-)

(limited to 'plugins')

diff --git a/plugins/s2s/mod_s2s.lua b/plugins/s2s/mod_s2s.lua
index a496af57..1fa8ae81 100644
--- a/plugins/s2s/mod_s2s.lua
+++ b/plugins/s2s/mod_s2s.lua
@@ -107,10 +107,7 @@ module:hook("route/remote", function (event)
 	s2sout.initiate_connection(host_session);
 	if (not host_session.connecting) and (not host_session.conn) then
 		log("warn", "Connection to %s failed already, destroying session...", to_host);
-		if not s2s_destroy_session(host_session, "Connection failed") then
-			-- Already destroyed, we need to bounce our stanza
-			host_session:bounce_sendq(host_session.destruction_reason);
-		end
+		s2s_destroy_session(host_session, "Connection failed");
 		return false;
 	end
 	return true;
-- 
cgit v1.2.3


From afebf2da34488b615692a35a66ae2b9a8584adbe Mon Sep 17 00:00:00 2001
From: Matthew Wild <mwild1@gmail.com>
Date: Sun, 11 Mar 2012 20:56:09 +0000
Subject: mod_auth_internal_{plain,hashed}: Clarify log messages on
 initialization

---
 plugins/mod_auth_internal_hashed.lua | 2 +-
 plugins/mod_auth_internal_plain.lua  | 2 +-
 2 files changed, 2 insertions(+), 2 deletions(-)

(limited to 'plugins')

diff --git a/plugins/mod_auth_internal_hashed.lua b/plugins/mod_auth_internal_hashed.lua
index ee810426..399044ad 100644
--- a/plugins/mod_auth_internal_hashed.lua
+++ b/plugins/mod_auth_internal_hashed.lua
@@ -54,7 +54,7 @@ local iteration_count = 4096;
 
 function new_hashpass_provider(host)
 	local provider = { name = "internal_hashed" };
-	log("debug", "initializing hashpass authentication provider for host '%s'", host);
+	log("debug", "initializing internal_hashed authentication provider for host '%s'", host);
 
 	function provider.test_password(username, password)
 		local credentials = datamanager.load(username, host, "accounts") or {};
diff --git a/plugins/mod_auth_internal_plain.lua b/plugins/mod_auth_internal_plain.lua
index 784553ea..93b50351 100644
--- a/plugins/mod_auth_internal_plain.lua
+++ b/plugins/mod_auth_internal_plain.lua
@@ -23,7 +23,7 @@ local prosody = _G.prosody;
 
 function new_default_provider(host)
 	local provider = { name = "internal_plain" };
-	log("debug", "initializing default authentication provider for host '%s'", host);
+	log("debug", "initializing internal_plain authentication provider for host '%s'", host);
 
 	function provider.test_password(username, password)
 		log("debug", "test password '%s' for user %s at host %s", password, username, module.host);
-- 
cgit v1.2.3


From 107458131fbd723e4a0bf486000afd61fdcc9824 Mon Sep 17 00:00:00 2001
From: Matthew Wild <mwild1@gmail.com>
Date: Wed, 14 Mar 2012 21:42:08 +0000
Subject: mod_c2s: Use module:add_item() to add the net-provider for
 portmanager

---
 plugins/mod_c2s.lua     | 6 ++++--
 plugins/s2s/mod_s2s.lua | 3 ++-
 2 files changed, 6 insertions(+), 3 deletions(-)

(limited to 'plugins')

diff --git a/plugins/mod_c2s.lua b/plugins/mod_c2s.lua
index 682c4e3b..e3557492 100644
--- a/plugins/mod_c2s.lua
+++ b/plugins/mod_c2s.lua
@@ -220,13 +220,15 @@ function listener.associate_session(conn, session)
 	sessions[conn] = session;
 end
 
-portmanager.register_service("c2s", {
+module:add_item("net-provider", {
+	name = "c2s";
 	listener = listener;
 	default_port = 5222;
 	encryption = "starttls";
 });
 
-portmanager.register_service("legacy_ssl", {
+module:add_item("net-provider", {
+	name = "legacy_ssl";
 	listener = listener;
 	encryption = "ssl";
 });
diff --git a/plugins/s2s/mod_s2s.lua b/plugins/s2s/mod_s2s.lua
index a496af57..194364cd 100644
--- a/plugins/s2s/mod_s2s.lua
+++ b/plugins/s2s/mod_s2s.lua
@@ -467,7 +467,8 @@ end
 
 s2sout.set_listener(listener);
 
-require "core.portmanager".register_service("s2s", {
+module:add_item("net-provider", {
+	name = "s2s";
 	listener = listener;
 	default_port = 5269;
 	encryption = "starttls";
-- 
cgit v1.2.3


From 437ed35fad3be0ce72b97400dfc765c370e3cf2b Mon Sep 17 00:00:00 2001
From: Matthew Wild <mwild1@gmail.com>
Date: Wed, 14 Mar 2012 21:43:46 +0000
Subject: mod_s2s/s2sout.lib: Log message instead of traceback when s2s isn't
 configured for any ports

---
 plugins/s2s/s2sout.lib.lua | 5 ++++-
 1 file changed, 4 insertions(+), 1 deletion(-)

(limited to 'plugins')

diff --git a/plugins/s2s/s2sout.lib.lua b/plugins/s2s/s2sout.lib.lua
index f9f9d362..6ee1ca83 100644
--- a/plugins/s2s/s2sout.lib.lua
+++ b/plugins/s2s/s2sout.lib.lua
@@ -316,7 +316,10 @@ module:hook_global("service-added", function (event)
 	if event.name ~= "s2s" then return end
 
 	local s2s_sources = portmanager.get_active_services():get("s2s");
-
+	if not s2s_sources then
+		module:log("warn", "s2s not listening on any ports, outgoing connections may fail");
+		return;
+	end
 	for source, _ in pairs(s2s_sources) do
 		if source == "*" or source == "0.0.0.0" then
 			if not socket.local_addresses then
-- 
cgit v1.2.3


From 46ae4849710de0b5b31d1983c72d6f3d40253ec0 Mon Sep 17 00:00:00 2001
From: Matthew Wild <mwild1@gmail.com>
Date: Thu, 15 Mar 2012 03:05:02 +0000
Subject: mod_net_multiplex: Port multiplexing (run multiple different services
 on a the same port(s))... now pluggable for use with any net plugin

---
 plugins/mod_net_multiplex.lua | 70 +++++++++++++++++++++++++++++++++++++++++++
 1 file changed, 70 insertions(+)
 create mode 100644 plugins/mod_net_multiplex.lua

(limited to 'plugins')

diff --git a/plugins/mod_net_multiplex.lua b/plugins/mod_net_multiplex.lua
new file mode 100644
index 00000000..44e1c1ee
--- /dev/null
+++ b/plugins/mod_net_multiplex.lua
@@ -0,0 +1,70 @@
+module:set_global();
+
+local max_buffer_len = module:get_option_number("multiplex_buffer_size", 1024);
+
+local portmanager = require "core.portmanager";
+
+local available_services = {};
+
+local function add_service(service)
+	local multiplex_pattern = service.multiplex and service.multiplex.pattern;
+	if multiplex_pattern then
+		module:log("debug", "Adding multiplex service %q with pattern %q", service.name, multiplex_pattern);
+		available_services[service] = multiplex_pattern;
+	else
+		module:log("debug", "Service %q is not multiplex-capable", service.name);
+	end
+end
+module:hook("service-added", function (event) add_service(event.service); end);
+module:hook("service-removed", function (event)	available_services[event.service] = nil; end);
+
+for service_name, services in pairs(portmanager.get_registered_services()) do
+	for i, service in ipairs(services) do
+		add_service(service);
+	end
+end
+
+local buffers = {};
+
+local listener = { default_mode = "*a" };
+
+function listener.onconnect()
+end
+
+function listener.onincoming(conn, data)
+	if not data then return; end
+	local buf = buffers[conn];
+	buffers[conn] = nil;
+	buf = buf and buf..data or data;
+	for service, multiplex_pattern in pairs(available_services) do
+		if buf:match(multiplex_pattern) then
+			module:log("debug", "Routing incoming connection to %s", service.name);
+			local listener = service.listener;
+			conn:setlistener(listener);
+			local onconnect = listener.onconnect;
+			if onconnect then onconnect(conn) end
+			return listener.onincoming(conn, buf);
+		end
+	end
+	if #buf > max_buffer_len then -- Give up
+		conn:close();
+	else
+		buffers[conn] = buf;
+	end
+end
+
+function listener.ondisconnect(conn, err)
+	buffers[conn] = nil; -- warn if no buffer?
+end
+
+module:add_item("net-provider", {
+	name = "multiplex";
+	config_prefix = "";
+	listener = listener;
+});
+
+module:provides("net", {
+	name = "multiplex_ssl";
+	config_prefix = "ssl";
+	listener = listener;
+});
-- 
cgit v1.2.3


From 99096dfa23df0d6a7f01d2dcba500cd8330ac086 Mon Sep 17 00:00:00 2001
From: Matthew Wild <mwild1@gmail.com>
Date: Thu, 15 Mar 2012 03:05:24 +0000
Subject: mod_c2s, mod_s2s: Add multiplex support

---
 plugins/mod_c2s.lua     | 3 +++
 plugins/s2s/mod_s2s.lua | 3 +++
 2 files changed, 6 insertions(+)

(limited to 'plugins')

diff --git a/plugins/mod_c2s.lua b/plugins/mod_c2s.lua
index e3557492..67156f55 100644
--- a/plugins/mod_c2s.lua
+++ b/plugins/mod_c2s.lua
@@ -231,6 +231,9 @@ module:add_item("net-provider", {
 	name = "legacy_ssl";
 	listener = listener;
 	encryption = "ssl";
+	multiplex = {
+		pattern = "^<.*:stream.*%sxmlns%s*=%s*(['\"])jabber:client%1.*>";
+	};
 });
 
 
diff --git a/plugins/s2s/mod_s2s.lua b/plugins/s2s/mod_s2s.lua
index 194364cd..04b8ae04 100644
--- a/plugins/s2s/mod_s2s.lua
+++ b/plugins/s2s/mod_s2s.lua
@@ -472,5 +472,8 @@ module:add_item("net-provider", {
 	listener = listener;
 	default_port = 5269;
 	encryption = "starttls";
+	multiplex = {
+		pattern = "^<.*:stream.*%sxmlns%s*=%s*(['\"])jabber:server%1.*>";
+	};
 });
 
-- 
cgit v1.2.3


From 3c2e464d58249bad2f19d3fade6e38f5dccea2be Mon Sep 17 00:00:00 2001
From: Kim Alvefur <zash@zash.se>
Date: Thu, 15 Mar 2012 14:47:46 +0100
Subject: mod_posix, mod_bosh, mod_admin_telnet: Use module:set_global()

---
 plugins/mod_admin_telnet.lua | 2 +-
 plugins/mod_bosh.lua         | 2 +-
 plugins/mod_posix.lua        | 2 +-
 3 files changed, 3 insertions(+), 3 deletions(-)

(limited to 'plugins')

diff --git a/plugins/mod_admin_telnet.lua b/plugins/mod_admin_telnet.lua
index fd72787e..3799efc0 100644
--- a/plugins/mod_admin_telnet.lua
+++ b/plugins/mod_admin_telnet.lua
@@ -6,7 +6,7 @@
 -- COPYING file in the source package for more information.
 --
 
-module.host = "*";
+module:set_global();
 
 local _G = _G;
 
diff --git a/plugins/mod_bosh.lua b/plugins/mod_bosh.lua
index 9de27b4b..20a6fa1f 100644
--- a/plugins/mod_bosh.lua
+++ b/plugins/mod_bosh.lua
@@ -6,7 +6,7 @@
 -- COPYING file in the source package for more information.
 --
 
-module.host = "*" -- Global module
+module:set_global(); -- Global module
 
 local hosts = _G.hosts;
 local new_xmpp_stream = require "util.xmppstream".new;
diff --git a/plugins/mod_posix.lua b/plugins/mod_posix.lua
index d229c1b8..b388fb9d 100644
--- a/plugins/mod_posix.lua
+++ b/plugins/mod_posix.lua
@@ -22,7 +22,7 @@ local stat = lfs.attributes;
 
 local prosody = _G.prosody;
 
-module.host = "*"; -- we're a global module
+module:set_global(); -- we're a global module
 
 local umask = module:get_option("umask") or "027";
 pposix.umask(umask);
-- 
cgit v1.2.3


From fdd0bb03e8e6b19b7eb68f33594ecace655dc4bc Mon Sep 17 00:00:00 2001
From: Matthew Wild <mwild1@gmail.com>
Date: Thu, 15 Mar 2012 16:31:10 +0000
Subject: mod_c2s, mod_s2s: Drop default_port and default_mode from listener
 objects (default_port is deprecated, and default_mode already defaults to *a)

---
 plugins/mod_c2s.lua     | 2 +-
 plugins/s2s/mod_s2s.lua | 2 +-
 2 files changed, 2 insertions(+), 2 deletions(-)

(limited to 'plugins')

diff --git a/plugins/mod_c2s.lua b/plugins/mod_c2s.lua
index 67156f55..12d10b18 100644
--- a/plugins/mod_c2s.lua
+++ b/plugins/mod_c2s.lua
@@ -31,7 +31,7 @@ local opt_keepalives = module:get_option_boolean("tcp_keepalives", false);
 local sessions = module:shared("sessions");
 
 local stream_callbacks = { default_ns = "jabber:client", handlestanza = core_process_stanza };
-local listener = { default_port = 5222, default_mode = "*a" };
+local listener = {};
 
 --- Stream events handlers
 local stream_xmlns_attr = {xmlns='urn:ietf:params:xml:ns:xmpp-streams'};
diff --git a/plugins/s2s/mod_s2s.lua b/plugins/s2s/mod_s2s.lua
index ad7f4862..e1102528 100644
--- a/plugins/s2s/mod_s2s.lua
+++ b/plugins/s2s/mod_s2s.lua
@@ -317,7 +317,7 @@ function stream_callbacks.handlestanza(session, stanza)
 	end
 end
 
-local listener = { default_port = 5269, default_mode = "*a" };
+local listener = {};
 
 --- Session methods
 local stream_xmlns_attr = {xmlns='urn:ietf:params:xml:ns:xmpp-streams'};
-- 
cgit v1.2.3


From 8a4a259bde8e94104a5b9ea59290c29d5f835493 Mon Sep 17 00:00:00 2001
From: Kim Alvefur <zash@zash.se>
Date: Thu, 15 Mar 2012 17:37:07 +0100
Subject: mod_c2s: Add missing multiplexed service discovery pattern.

---
 plugins/mod_c2s.lua | 3 +++
 1 file changed, 3 insertions(+)

(limited to 'plugins')

diff --git a/plugins/mod_c2s.lua b/plugins/mod_c2s.lua
index 12d10b18..743fe3d2 100644
--- a/plugins/mod_c2s.lua
+++ b/plugins/mod_c2s.lua
@@ -225,6 +225,9 @@ module:add_item("net-provider", {
 	listener = listener;
 	default_port = 5222;
 	encryption = "starttls";
+	multiplex = {
+		pattern = "^<.*:stream.*%sxmlns%s*=%s*(['\"])jabber:client%1.*>";
+	};
 });
 
 module:add_item("net-provider", {
-- 
cgit v1.2.3


From eb824294fe75a98771f3397c6a212d270e76550b Mon Sep 17 00:00:00 2001
From: Florian Zeitz <florob@babelmonkeys.de>
Date: Mon, 26 Mar 2012 18:23:49 +0200
Subject: mod_s2s: Add "::" as a IPv6 interface (thanks darkrain)

---
 plugins/s2s/s2sout.lib.lua | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

(limited to 'plugins')

diff --git a/plugins/s2s/s2sout.lib.lua b/plugins/s2s/s2sout.lib.lua
index 6ee1ca83..af55b273 100644
--- a/plugins/s2s/s2sout.lib.lua
+++ b/plugins/s2s/s2sout.lib.lua
@@ -331,7 +331,7 @@ module:hook_global("service-added", function (event)
 			end
 		elseif source == "::" then
 			if not socket.local_addresses then
-				sources[#sources + 1] = new_ip("::", "IPv4");
+				sources[#sources + 1] = new_ip("::", "IPv6");
 			else
 				for _, addr in ipairs(socket.local_addresses("ipv6", true)) do
 					sources[#sources + 1] = new_ip(addr, "IPv6");
-- 
cgit v1.2.3


From dc93045ece17b36f7b9dfcd96837638913ed45f8 Mon Sep 17 00:00:00 2001
From: Paul Aurich <paul@darkrain42.org>
Date: Mon, 26 Mar 2012 19:17:09 -0700
Subject: mod_s2s: Queuing a stanza constitutes handling it.

---
 plugins/s2s/mod_s2s.lua | 1 +
 1 file changed, 1 insertion(+)

(limited to 'plugins')

diff --git a/plugins/s2s/mod_s2s.lua b/plugins/s2s/mod_s2s.lua
index e1102528..b0bd5b40 100644
--- a/plugins/s2s/mod_s2s.lua
+++ b/plugins/s2s/mod_s2s.lua
@@ -76,6 +76,7 @@ module:hook("route/remote", function (event)
 			if host.sendq then t_insert(host.sendq, {tostring(stanza), stanza.attr.type ~= "error" and stanza.attr.type ~= "result" and st.reply(stanza)});
 			else host.sendq = { {tostring(stanza), stanza.attr.type ~= "error" and stanza.attr.type ~= "result" and st.reply(stanza)} }; end
 			host.log("debug", "stanza [%s] queued ", stanza.name);
+			return true;
 		elseif host.type == "local" or host.type == "component" then
 			log("error", "Trying to send a stanza to ourselves??")
 			log("error", "Traceback: %s", get_traceback());
-- 
cgit v1.2.3


From cc13a475a6010a301fb92f14e3120d41dfc45214 Mon Sep 17 00:00:00 2001
From: Florian Zeitz <florob@babelmonkeys.de>
Date: Fri, 13 Apr 2012 00:39:00 +0200
Subject: mod_adhoc: Always allow at least the "complete" action

---
 plugins/adhoc/adhoc.lib.lua | 11 +++++++++--
 1 file changed, 9 insertions(+), 2 deletions(-)

(limited to 'plugins')

diff --git a/plugins/adhoc/adhoc.lib.lua b/plugins/adhoc/adhoc.lib.lua
index 0cb4efe1..57059b3f 100644
--- a/plugins/adhoc/adhoc.lib.lua
+++ b/plugins/adhoc/adhoc.lib.lua
@@ -25,6 +25,7 @@ function _M.new(name, node, handler, permission)
 end
 
 function _M.handle_cmd(command, origin, stanza)
+	local cmdtag, actions;
 	local sessionid = stanza.tags[1].attr.sessionid or uuid.generate();
 	local dataIn = {};
 	dataIn.to = stanza.attr.to;
@@ -58,7 +59,7 @@ function _M.handle_cmd(command, origin, stanza)
 		elseif name == "error" then
 			cmdtag:tag("note", {type="error"}):text(content.message):up();
 		elseif name =="actions" then
-			local actions = st.stanza("actions");
+			actions = st.stanza("actions");
 			for _, action in ipairs(content) do
 				if (action == "prev") or (action == "next") or (action == "complete") then
 					actions:tag(action):up();
@@ -67,7 +68,6 @@ function _M.handle_cmd(command, origin, stanza)
 						'" at node "'..command.node..'" provided an invalid action "'..action..'"');
 				end
 			end
-			cmdtag:add_child(actions);
 		elseif name == "form" then
 			cmdtag:add_child((content.layout or content):form(content.values));
 		elseif name == "result" then
@@ -76,6 +76,13 @@ function _M.handle_cmd(command, origin, stanza)
 			cmdtag:add_child(content);
 		end
 	end
+
+	if not actions then
+		actions = st.stanza("actions");
+		actions:tag("complete"):up();
+	end
+	cmdtag:add_child(actions);
+
 	stanza:add_child(cmdtag);
 	origin.send(stanza);
 
-- 
cgit v1.2.3


From 8d2bb74a6a3e34e09c0361d5f22e6df66c51c74d Mon Sep 17 00:00:00 2001
From: Waqas Hussain <waqas20@gmail.com>
Date: Sat, 21 Apr 2012 17:38:48 +0500
Subject: mod_http: Provide HTTP service.

---
 plugins/mod_http.lua | 69 ++++++++++++++++++++++++++++++++++++++++++++++++++++
 1 file changed, 69 insertions(+)
 create mode 100644 plugins/mod_http.lua

(limited to 'plugins')

diff --git a/plugins/mod_http.lua b/plugins/mod_http.lua
new file mode 100644
index 00000000..4713cf13
--- /dev/null
+++ b/plugins/mod_http.lua
@@ -0,0 +1,69 @@
+-- Prosody IM
+-- Copyright (C) 2008-2010 Matthew Wild
+-- Copyright (C) 2008-2010 Waqas Hussain
+-- 
+-- This project is MIT/X11 licensed. Please see the
+-- COPYING file in the source package for more information.
+--
+
+module:set_global();
+
+--local sessions = module:shared("sessions");
+
+--[[function listener.associate_session(conn, session)
+	sessions[conn] = session;
+end]]
+
+local handlers;
+
+function build_handlers()
+	handlers = {};
+	for _, item in ipairs(module:get_host_items("http-handler")) do
+		local previous = handlers[item.path];
+		if not previous and item.path then
+			handlers[item.path] = item;
+		end
+	end
+end
+function clear_handlers()
+	handlers = nil;
+end
+module:handle_items("http-handler", clear_handlers, clear_handlers, false);
+
+function http_handler(event)
+	local request, response = event.request, event.response;
+
+	if not handlers then build_handlers(); end
+	local item = handlers[request.path:match("[^?]*")];
+	local handler = item and item.handler;
+	if handler then
+		handler(request, response);
+		return true;
+	end
+end
+
+local server = require "net.http.server";
+local listener = server.listener;
+server.add_handler("*", http_handler);
+function module.unload()
+	server.remove_handler("*", http_handler);
+end
+--require "net.http.server".listen_on(8080);
+
+module:add_item("net-provider", {
+	name = "http";
+	listener = listener;
+	default_port = 5280;
+	multiplex = {
+		pattern = "^[A-Z]";
+	};
+});
+
+module:add_item("net-provider", {
+	name = "https";
+	listener = listener;
+	encryption = "ssl";
+	multiplex = {
+		pattern = "^[A-Z]";
+	};
+});
-- 
cgit v1.2.3


From 4ce0cecfe4dbd9e1bfe71a16c81478c574070645 Mon Sep 17 00:00:00 2001
From: Waqas Hussain <waqas20@gmail.com>
Date: Sat, 21 Apr 2012 18:23:44 +0500
Subject: mod_http: Include handlers of non-global modules.

---
 plugins/mod_http.lua | 40 ++++++++++++++++++++++++++++------------
 1 file changed, 28 insertions(+), 12 deletions(-)

(limited to 'plugins')

diff --git a/plugins/mod_http.lua b/plugins/mod_http.lua
index 4713cf13..24ef8ab3 100644
--- a/plugins/mod_http.lua
+++ b/plugins/mod_http.lua
@@ -14,28 +14,44 @@ module:set_global();
 	sessions[conn] = session;
 end]]
 
-local handlers;
+local NULL = {};
+local handlers = {};
 
-function build_handlers()
-	handlers = {};
-	for _, item in ipairs(module:get_host_items("http-handler")) do
-		local previous = handlers[item.path];
-		if not previous and item.path then
-			handlers[item.path] = item;
+function build_handlers(host)
+	if not hosts[host] then return; end
+	local h = {};
+	handlers[host] = h;
+	
+	for mod_name, module in pairs(modulemanager.get_modules(host)) do
+		module = module.module;
+		if module.items then
+			for _, item in ipairs(module.items["http-handler"] or NULL) do
+				local previous = handlers[item.path];
+				if not previous and item.path then
+					h[item.path] = item;
+				end
+			end
 		end
 	end
+
+	return h;
+end
+function clear_handlers(event)
+	handlers[event.source.host] = nil;
 end
-function clear_handlers()
-	handlers = nil;
+function get_handler(host, path)
+	local h = handlers[host] or build_handlers(host);
+	if h then
+		local item = h[path];
+		return item and item.handler;
+	end
 end
 module:handle_items("http-handler", clear_handlers, clear_handlers, false);
 
 function http_handler(event)
 	local request, response = event.request, event.response;
 
-	if not handlers then build_handlers(); end
-	local item = handlers[request.path:match("[^?]*")];
-	local handler = item and item.handler;
+	local handler = get_handler(request.headers.host:match("[^:]*"):lower(), request.path:match("[^?]*"));
 	if handler then
 		handler(request, response);
 		return true;
-- 
cgit v1.2.3


From 18497219e053d2bc28798b27d6adc23b039cf3ec Mon Sep 17 00:00:00 2001
From: Matthew Wild <mwild1@gmail.com>
Date: Sat, 21 Apr 2012 20:38:27 +0100
Subject: mod_admin_telnet: get_host_set(): Include '*' in the set if no
 specific hosts are specified and the module is loaded there

---
 plugins/mod_admin_telnet.lua | 6 +++++-
 1 file changed, 5 insertions(+), 1 deletion(-)

(limited to 'plugins')

diff --git a/plugins/mod_admin_telnet.lua b/plugins/mod_admin_telnet.lua
index 3799efc0..e60a2245 100644
--- a/plugins/mod_admin_telnet.lua
+++ b/plugins/mod_admin_telnet.lua
@@ -277,8 +277,12 @@ local function get_hosts_set(hosts, module)
 		return set.new { hosts };
 	elseif hosts == nil then
 		local mm = require "modulemanager";
-		return set.new(array.collect(keys(prosody.hosts)))
+		local hosts_set = set.new(array.collect(keys(prosody.hosts)))
 			/ function (host) return prosody.hosts[host].type == "local" or module and mm.is_loaded(host, module); end;
+		if module and mm.get_module("*", module) then
+			hosts_set:add("*");
+		end
+		return hosts_set;
 	end
 end
 
-- 
cgit v1.2.3


From 726f3804c3ed5b4d0d3e94b3f4c860c30d76a500 Mon Sep 17 00:00:00 2001
From: Matthew Wild <mwild1@gmail.com>
Date: Sat, 21 Apr 2012 20:39:21 +0100
Subject: mod_admin_telnet: module:reload(): If module is loaded on *, reload
 it there first (ensuring shared module code is reloaded before per-host
 children of that module)

---
 plugins/mod_admin_telnet.lua | 10 +++++++---
 1 file changed, 7 insertions(+), 3 deletions(-)

(limited to 'plugins')

diff --git a/plugins/mod_admin_telnet.lua b/plugins/mod_admin_telnet.lua
index e60a2245..2f2e057b 100644
--- a/plugins/mod_admin_telnet.lua
+++ b/plugins/mod_admin_telnet.lua
@@ -334,11 +334,15 @@ end
 function def_env.module:reload(name, hosts)
 	local mm = require "modulemanager";
 
-	hosts = get_hosts_set(hosts, name);
-	
+	hosts = array.collect(get_hosts_set(hosts, name)):sort(function (a, b)
+		if a == "*" then return true
+		elseif b == "*" then return false
+		else return a < b; end
+	end);
+
 	-- Reload the module for each host
 	local ok, err, count = true, nil, 0;
-	for host in hosts do
+	for _, host in ipairs(hosts) do
 		if mm.is_loaded(host, name) then
 			ok, err = mm.reload(host, name);
 			if not ok then
-- 
cgit v1.2.3


From 785fcb0ee39111f31aaa0ebd0d00a797108b6c68 Mon Sep 17 00:00:00 2001
From: Matthew Wild <mwild1@gmail.com>
Date: Sat, 21 Apr 2012 20:39:52 +0100
Subject: mod_admin_telnet: module:list(): List global modules (part-fixes
 #228)

---
 plugins/mod_admin_telnet.lua | 5 +++--
 1 file changed, 3 insertions(+), 2 deletions(-)

(limited to 'plugins')

diff --git a/plugins/mod_admin_telnet.lua b/plugins/mod_admin_telnet.lua
index 2f2e057b..412e1e62 100644
--- a/plugins/mod_admin_telnet.lua
+++ b/plugins/mod_admin_telnet.lua
@@ -363,6 +363,7 @@ end
 function def_env.module:list(hosts)
 	if hosts == nil then
 		hosts = array.collect(keys(prosody.hosts));
+		table.insert(hosts, 1, "*");
 	end
 	if type(hosts) == "string" then
 		hosts = { hosts };
@@ -373,8 +374,8 @@ function def_env.module:list(hosts)
 	
 	local print = self.session.print;
 	for _, host in ipairs(hosts) do
-		print(host..":");
-		local modules = array.collect(keys(prosody.hosts[host] and prosody.hosts[host].modules or {})):sort();
+		print((host == "*" and "Global" or host)..":");
+		local modules = array.collect(keys(modulemanager.get_modules(host) or {})):sort();
 		if #modules == 0 then
 			if prosody.hosts[host] then
 				print("    No modules loaded");
-- 
cgit v1.2.3


From 89fb66dabbceb336e9e78d8f2e13c39f67f14eec Mon Sep 17 00:00:00 2001
From: Matthew Wild <mwild1@gmail.com>
Date: Sat, 21 Apr 2012 20:52:12 +0100
Subject: mod_admin_telnet: module:load(): Fix 'global-module-already-loaded'
 errors when successfully loading a global module (fixes #228)

---
 plugins/mod_admin_telnet.lua | 14 ++++++++++----
 1 file changed, 10 insertions(+), 4 deletions(-)

(limited to 'plugins')

diff --git a/plugins/mod_admin_telnet.lua b/plugins/mod_admin_telnet.lua
index 412e1e62..cb6302c7 100644
--- a/plugins/mod_admin_telnet.lua
+++ b/plugins/mod_admin_telnet.lua
@@ -292,16 +292,22 @@ function def_env.module:load(name, hosts, config)
 	hosts = get_hosts_set(hosts);
 	
 	-- Load the module for each host
-	local ok, err, count = true, nil, 0;
+	local ok, err, count, mod = true, nil, 0, nil;
 	for host in hosts do
 		if (not mm.is_loaded(host, name)) then
-			ok, err = mm.load(host, name, config);
-			if not ok then
+			mod, err = mm.load(host, name, config);
+			if not mod then
 				ok = false;
+				if err == "global-module-already-loaded" then
+					if count > 0 then
+						ok, err, count = true, nil, 1;
+					end
+					break;
+				end
 				self.session.print(err or "Unknown error loading module");
 			else
 				count = count + 1;
-				self.session.print("Loaded for "..host);
+				self.session.print("Loaded for "..mod.module.host);
 			end
 		end
 	end
-- 
cgit v1.2.3


From 615325cf153e6ebb5f07bb92545bfcedf1c2266b Mon Sep 17 00:00:00 2001
From: Matthew Wild <mwild1@gmail.com>
Date: Sat, 21 Apr 2012 21:16:53 +0100
Subject: mod_motd: Send only to resource coming online, not the user's bare
 JID (fixes #282)

---
 plugins/mod_motd.lua | 6 +++---
 1 file changed, 3 insertions(+), 3 deletions(-)

(limited to 'plugins')

diff --git a/plugins/mod_motd.lua b/plugins/mod_motd.lua
index d567288b..646bf8cd 100644
--- a/plugins/mod_motd.lua
+++ b/plugins/mod_motd.lua
@@ -13,15 +13,15 @@ local motd_jid = module:get_option_string("motd_jid", host);
 
 if not motd_text then return; end
 
+local jid_join = require "util.jid".join;
 local st = require "util.stanza";
 
 motd_text = motd_text:gsub("^%s*(.-)%s*$", "%1"):gsub("\n%s+", "\n"); -- Strip indentation from the config
 
-module:hook("resource-bind",
-	function (event)
+module:hook("resource-bind", function (event)
 		local session = event.session;
 		local motd_stanza =
-			st.message({ to = session.username..'@'..session.host, from = motd_jid })
+			st.message({ to = jid_join(session.username, session.host, session.resource), from = motd_jid })
 				:tag("body"):text(motd_text);
 		core_route_stanza(hosts[host], motd_stanza);
 		module:log("debug", "MOTD send to user %s@%s", session.username, session.host);
-- 
cgit v1.2.3


From 9d478011222d360bb8d7bdaccd94da0e9e331621 Mon Sep 17 00:00:00 2001
From: Matthew Wild <mwild1@gmail.com>
Date: Sat, 21 Apr 2012 22:50:57 +0100
Subject: mod_component: Make a shared module, and move the
 xmppcomponent_listener into it ('port'ing over to portmanager). Ha ha.

---
 plugins/mod_component.lua | 333 +++++++++++++++++++++++++++++++++++++---------
 1 file changed, 268 insertions(+), 65 deletions(-)

(limited to 'plugins')

diff --git a/plugins/mod_component.lua b/plugins/mod_component.lua
index f7d09930..96d55e40 100644
--- a/plugins/mod_component.lua
+++ b/plugins/mod_component.lua
@@ -6,95 +6,298 @@
 -- COPYING file in the source package for more information.
 --
 
-if module:get_host_type() ~= "component" then
-	error("Don't load mod_component manually, it should be for a component, please see http://prosody.im/doc/components", 0);
-end
+module:set_global();
 
 local t_concat = table.concat;
 
+local logger = require "util.logger";
 local sha1 = require "util.hashes".sha1;
 local st = require "util.stanza";
 
+local jid_split = require "util.jid".split;
+local new_xmpp_stream = require "util.xmppstream".new;
+local uuid_gen = require "util.uuid".generate;
+
+
 local log = module._log;
 
-local main_session, send;
+local sessions = module:shared("sessions");
 
-local function on_destroy(session, err)
-	if main_session == session then
-		connected = false;
-		main_session = nil;
+function module.add_host(module)
+	if module:get_host_type() ~= "component" then
+		error("Don't load mod_component manually, it should be for a component, please see http://prosody.im/doc/components", 0);
+	end
+	
+	local env = module.environment;
+	env.connected = false;
+
+	local send;
+
+	local function on_destroy(session, err)
+		env.connected = false;
 		send = nil;
 		session.on_destroy = nil;
 	end
+	
+	-- Handle authentication attempts by component
+	local function handle_component_auth(event)
+		local session, stanza = event.origin, event.stanza;
+		
+		if session.type ~= "component" then return; end
+	
+		if (not session.host) or #stanza.tags > 0 then
+			(session.log or log)("warn", "Invalid component handshake for host: %s", session.host);
+			session:close("not-authorized");
+			return true;
+		end
+		
+		local secret = module:get_option("component_secret");
+		if not secret then
+			(session.log or log)("warn", "Component attempted to identify as %s, but component_secret is not set", session.host);
+			session:close("not-authorized");
+			return true;
+		end
+		
+		local supplied_token = t_concat(stanza);
+		local calculated_token = sha1(session.streamid..secret, true);
+		if supplied_token:lower() ~= calculated_token:lower() then
+			module:log("info", "Component authentication failed for %s", session.host);
+			session:close{ condition = "not-authorized", text = "Given token does not match calculated token" };
+			return true;
+		end
+		
+		if env.connected then
+			module:log("error", "Second component attempted to connect, denying connection");
+			session:close{ condition = "conflict", text = "Component already connected" };
+		end
+		
+		env.connected = true;
+		send = session.send;
+		session.on_destroy = on_destroy;
+		session.component_validate_from = module:get_option_boolean("validate_from_addresses", true);
+		module:log("info", "External component successfully authenticated");
+		session.send(st.stanza("handshake"));
+	
+		return true;
+	end
+	module:hook("stanza/jabber:component:accept:handshake", handle_component_auth);
+
+	-- Handle stanzas addressed to this component
+	local function handle_stanza(event)
+		local stanza = event.stanza;
+		if send then
+			stanza.attr.xmlns = nil;
+			send(stanza);
+		else
+			module:log("warn", "Component not connected, bouncing error for: %s", stanza:top_tag());
+			if stanza.attr.type ~= "error" and stanza.attr.type ~= "result" then
+				event.origin.send(st.error_reply(stanza, "wait", "service-unavailable", "Component unavailable"));
+			end
+		end
+		return true;
+	end
+	
+	module:hook("iq/bare", handle_stanza, -1);
+	module:hook("message/bare", handle_stanza, -1);
+	module:hook("presence/bare", handle_stanza, -1);
+	module:hook("iq/full", handle_stanza, -1);
+	module:hook("message/full", handle_stanza, -1);
+	module:hook("presence/full", handle_stanza, -1);
+	module:hook("iq/host", handle_stanza, -1);
+	module:hook("message/host", handle_stanza, -1);
+	module:hook("presence/host", handle_stanza, -1);
 end
 
-local function handle_stanza(event)
-	local stanza = event.stanza;
-	if send then
-		stanza.attr.xmlns = nil;
-		send(stanza);
-	else
-		log("warn", "Component not connected, bouncing error for: %s", stanza:top_tag());
-		if stanza.attr.type ~= "error" and stanza.attr.type ~= "result" then
-			event.origin.send(st.error_reply(stanza, "wait", "service-unavailable", "Component unavailable"));
+--- Network and stream part ---
+
+local xmlns_component = 'jabber:component:accept';
+
+local listener = {};
+
+--- Callbacks/data for xmppstream to handle streams for us ---
+
+local stream_callbacks = { default_ns = xmlns_component };
+
+local xmlns_xmpp_streams = "urn:ietf:params:xml:ns:xmpp-streams";
+
+function stream_callbacks.error(session, error, data, data2)
+	if session.destroyed then return; end
+	module:log("warn", "Error processing component stream: "..tostring(error));
+	if error == "no-stream" then
+		session:close("invalid-namespace");
+	elseif error == "parse-error" then
+		session.log("warn", "External component %s XML parse error: %s", tostring(session.host), tostring(data));
+		session:close("not-well-formed");
+	elseif error == "stream-error" then
+		local condition, text = "undefined-condition";
+		for child in data:children() do
+			if child.attr.xmlns == xmlns_xmpp_streams then
+				if child.name ~= "text" then
+					condition = child.name;
+				else
+					text = child:get_text();
+				end
+				if condition ~= "undefined-condition" and text then
+					break;
+				end
+			end
 		end
+		text = condition .. (text and (" ("..text..")") or "");
+		session.log("info", "Session closed by remote with error: %s", text);
+		session:close(nil, text);
 	end
-	return true;
 end
 
-module:hook("iq/bare", handle_stanza, -1);
-module:hook("message/bare", handle_stanza, -1);
-module:hook("presence/bare", handle_stanza, -1);
-module:hook("iq/full", handle_stanza, -1);
-module:hook("message/full", handle_stanza, -1);
-module:hook("presence/full", handle_stanza, -1);
-module:hook("iq/host", handle_stanza, -1);
-module:hook("message/host", handle_stanza, -1);
-module:hook("presence/host", handle_stanza, -1);
-
---- Handle authentication attempts by components
-function handle_component_auth(event)
-	local session, stanza = event.origin, event.stanza;
-	
-	if session.type ~= "component" then return; end
-	if main_session == session then return; end
+function stream_callbacks.streamopened(session, attr)
+	if not hosts[attr.to].modules.component then
+		session:close{ condition = "host-unknown", text = tostring(attr.to).." does not match any configured external components" };
+		return;
+	end
+	session.host = attr.to;
+	session.streamid = uuid_gen();
+	session.notopen = nil;
+	-- Return stream header
+	session.send(st.stanza("stream:stream", { xmlns=xmlns_component,
+			["xmlns:stream"]='http://etherx.jabber.org/streams', id=session.streamid, from=session.host }):top_tag());
+end
 
-	if (not session.host) or #stanza.tags > 0 then
-		(session.log or log)("warn", "Invalid component handshake for host: %s", session.host);
-		session:close("not-authorized");
-		return true;
+function stream_callbacks.streamclosed(session)
+	session.log("debug", "Received </stream:stream>");
+	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
+		stanza.attr.xmlns = xmlns_component;
 	end
-	
-	local secret = module:get_option("component_secret");
-	if not secret then
-		(session.log or log)("warn", "Component attempted to identify as %s, but component_secret is not set", session.host);
-		session:close("not-authorized");
-		return true;
+	if not stanza.attr.xmlns or stanza.attr.xmlns == "jabber:client" then
+		local from = stanza.attr.from;
+		if from then
+			if session.component_validate_from then
+				local _, domain = jid_split(stanza.attr.from);
+				if domain ~= session.host then
+					-- Return error
+					session.log("warn", "Component sent stanza with missing or invalid 'from' address");
+					session:close{
+						condition = "invalid-from";
+						text = "Component tried to send from address <"..tostring(from)
+							   .."> which is not in domain <"..tostring(session.host)..">";
+					};
+					return;
+				end
+			end
+		else
+			stanza.attr.from = session.host; -- COMPAT: Strictly we shouldn't allow this
+		end
+		if not stanza.attr.to then
+			session.log("warn", "Rejecting stanza with no 'to' address");
+			session.send(st.error_reply(stanza, "modify", "bad-request", "Components MUST specify a 'to' address on stanzas"));
+			return;
+		end
 	end
-	
-	local supplied_token = t_concat(stanza);
-	local calculated_token = sha1(session.streamid..secret, true);
-	if supplied_token:lower() ~= calculated_token:lower() then
-		log("info", "Component authentication failed for %s", session.host);
-		session:close{ condition = "not-authorized", text = "Given token does not match calculated token" };
-		return true;
+	return core_process_stanza(session, stanza);
+end
+
+--- Closing a component connection
+local stream_xmlns_attr = {xmlns='urn:ietf:params:xml:ns:xmpp-streams'};
+local default_stream_attr = { ["xmlns:stream"] = "http://etherx.jabber.org/streams", xmlns = stream_callbacks.default_ns, version = "1.0", id = "" };
+local function session_close(session, reason)
+	if session.destroyed then return; end
+	local log = session.log or log;
+	if session.conn then
+		if session.notopen then
+			session.send("<?xml version='1.0'?>");
+			session.send(st.stanza("stream:stream", default_stream_attr):top_tag());
+		end
+		if reason then
+			if type(reason) == "string" then -- assume stream error
+				module:log("info", "Disconnecting component, <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
+					local stanza = st.stanza("stream:error"):tag(reason.condition, stream_xmlns_attr):up();
+					if reason.text then
+						stanza:tag("text", stream_xmlns_attr):text(reason.text):up();
+					end
+					if reason.extra then
+						stanza:add_child(reason.extra);
+					end
+					module:log("info", "Disconnecting component, <stream:error> is: %s", tostring(stanza));
+					session.send(stanza);
+				elseif reason.name then -- a stanza
+					module:log("info", "Disconnecting component, <stream:error> is: %s", tostring(reason));
+					session.send(reason);
+				end
+			end
+		end
+		session.send("</stream:stream>");
+		session.conn:close();
+		listener.ondisconnect(session.conn, "stream error");
 	end
+end
+
+--- Component connlistener
+
+function listener.onconnect(conn)
+	local _send = conn.write;
+	local session = { type = "component", conn = conn, send = function (data) return _send(conn, tostring(data)); end };
+
+	-- Logging functions --
+	local conn_name = "jcp"..tostring(conn):match("[a-f0-9]+$");
+	session.log = logger.init(conn_name);
+	session.close = session_close;
 	
-	-- If component not already created for this host, create one now
-	if not main_session then
-		connected = true;
-		send = session.send;
-		main_session = session;
-		session.on_destroy = on_destroy;
-		session.component_validate_from = module:get_option_boolean("validate_from_addresses", true);
-		log("info", "Component successfully authenticated: %s", session.host);
-		session.send(st.stanza("handshake"));
-	else -- TODO: Implement stanza distribution
-		log("error", "Multiple components bound to the same address, first one wins: %s", session.host);
-		session:close{ condition = "conflict", text = "Component already connected" };
+	session.log("info", "Incoming Jabber component connection");
+	
+	local stream = new_xmpp_stream(session, stream_callbacks);
+	session.stream = stream;
+	
+	session.notopen = true;
+	
+	function session.reset_stream()
+		session.notopen = true;
+		session.stream:reset();
+	end
+
+	function session.data(conn, data)
+		local ok, err = stream:feed(data);
+		if ok then return; end
+		module:log("debug", "Received invalid XML (%s) %d bytes: %s", tostring(err), #data, data:sub(1, 300):gsub("[\r\n]+", " "):gsub("[%z\1-\31]", "_"));
+		session:close("not-well-formed");
 	end
 	
-	return true;
+	session.dispatch_stanza = stream_callbacks.handlestanza;
+
+	sessions[conn] = session;
+end
+function listener.onincoming(conn, data)
+	local session = sessions[conn];
+	session.data(conn, data);
+end
+function listener.ondisconnect(conn, err)
+	local session = sessions[conn];
+	if session then
+		(session.log or log)("info", "component disconnected: %s (%s)", tostring(session.host), tostring(err));
+		if session.on_destroy then session:on_destroy(err); end
+		sessions[conn] = nil;
+		for k in pairs(session) do
+			if k ~= "log" and k ~= "close" then
+				session[k] = nil;
+			end
+		end
+		session.destroyed = true;
+		session = nil;
+	end
 end
 
-module:hook("stanza/jabber:component:accept:handshake", handle_component_auth);
+module:add_item("net-provider", {
+	name = "component";
+	listener = listener;
+	default_port = 5347;
+	multiplex = {
+		pattern = "^<.*:stream.*%sxmlns%s*=%s*(['\"])jabber:component%1.*>";
+	};
+});
-- 
cgit v1.2.3


From 51ab962e5540b1af96170b7801135f768f4c75cf Mon Sep 17 00:00:00 2001
From: Matthew Wild <mwild1@gmail.com>
Date: Sun, 22 Apr 2012 14:54:36 +0100
Subject: mod_motd: Use presence/bare to catch a client's initial presence and
 send the MOTD then (fixes #282)

---
 plugins/mod_motd.lua | 19 ++++++++++---------
 1 file changed, 10 insertions(+), 9 deletions(-)

(limited to 'plugins')

diff --git a/plugins/mod_motd.lua b/plugins/mod_motd.lua
index 646bf8cd..39b74de9 100644
--- a/plugins/mod_motd.lua
+++ b/plugins/mod_motd.lua
@@ -18,12 +18,13 @@ local st = require "util.stanza";
 
 motd_text = motd_text:gsub("^%s*(.-)%s*$", "%1"):gsub("\n%s+", "\n"); -- Strip indentation from the config
 
-module:hook("resource-bind", function (event)
-		local session = event.session;
-		local motd_stanza =
-			st.message({ to = jid_join(session.username, session.host, session.resource), from = motd_jid })
-				:tag("body"):text(motd_text);
-		core_route_stanza(hosts[host], motd_stanza);
-		module:log("debug", "MOTD send to user %s@%s", session.username, session.host);
-
-end);
+module:hook("presence/bare", function (event)
+		local session, stanza = event.origin, event.stanza;
+		if not session.presence and not stanza.attr.type then
+			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:log("debug", "MOTD send to user %s", session.full_jid);
+		end
+end, 1);
-- 
cgit v1.2.3


From c1e2674fd4b5dd08abe07e9f3bbf92d34c096c56 Mon Sep 17 00:00:00 2001
From: Matthew Wild <mwild1@gmail.com>
Date: Sun, 22 Apr 2012 16:35:26 +0100
Subject: mod_component: Handle component connecting to non-existent host

---
 plugins/mod_component.lua | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

(limited to 'plugins')

diff --git a/plugins/mod_component.lua b/plugins/mod_component.lua
index 96d55e40..5b1eefc7 100644
--- a/plugins/mod_component.lua
+++ b/plugins/mod_component.lua
@@ -149,7 +149,7 @@ function stream_callbacks.error(session, error, data, data2)
 end
 
 function stream_callbacks.streamopened(session, attr)
-	if not hosts[attr.to].modules.component then
+	if not hosts[attr.to] or not hosts[attr.to].modules.component then
 		session:close{ condition = "host-unknown", text = tostring(attr.to).." does not match any configured external components" };
 		return;
 	end
-- 
cgit v1.2.3


From c51dcd332fe2e74486fffa2cb3e7b9851813b544 Mon Sep 17 00:00:00 2001
From: Matthew Wild <mwild1@gmail.com>
Date: Mon, 23 Apr 2012 14:16:59 +0100
Subject: mod_http: Revamp module for new API and config

---
 plugins/mod_http.lua | 106 ++++++++++++++++++++++++++-------------------------
 1 file changed, 54 insertions(+), 52 deletions(-)

(limited to 'plugins')

diff --git a/plugins/mod_http.lua b/plugins/mod_http.lua
index 24ef8ab3..d8e1e10a 100644
--- a/plugins/mod_http.lua
+++ b/plugins/mod_http.lua
@@ -1,6 +1,6 @@
 -- Prosody IM
--- Copyright (C) 2008-2010 Matthew Wild
--- Copyright (C) 2008-2010 Waqas Hussain
+-- Copyright (C) 2008-2012 Matthew Wild
+-- Copyright (C) 2008-2012 Waqas Hussain
 -- 
 -- This project is MIT/X11 licensed. Please see the
 -- COPYING file in the source package for more information.
@@ -8,67 +8,68 @@
 
 module:set_global();
 
---local sessions = module:shared("sessions");
+local parse_url = require "socket.url".parse;
+local server = require "net.http.server";
 
---[[function listener.associate_session(conn, session)
-	sessions[conn] = session;
-end]]
+local function normalize_path(path)
+	if path:sub(1,1) ~= "/" then path = "/"..path; end
+	if path:sub(-1,-1) == "/" then path = path:sub(1, -2); end
+	return path;
+end
 
-local NULL = {};
-local handlers = {};
+local function get_http_event(http_module, app_name, key)
+	local method, path = key:match("^(%S+)%s+(.+)$");
+	if not method and key:sub(1,1) == "/" then
+		method, path = "GET", key;
+	else
+		return nil;
+	end
+	path = normalize_path(path);
+	local app_path = normalize_path(http_module:get_option_string(app_name.."_http_path", "/"..app_name));
+	return method:upper().." "..http_module.host..app_path..path;
+end
 
-function build_handlers(host)
-	if not hosts[host] then return; end
-	local h = {};
-	handlers[host] = h;
-	
-	for mod_name, module in pairs(modulemanager.get_modules(host)) do
-		module = module.module;
-		if module.items then
-			for _, item in ipairs(module.items["http-handler"] or NULL) do
-				local previous = handlers[item.path];
-				if not previous and item.path then
-					h[item.path] = item;
+function module.add_host(module)
+	local apps = {};
+	module.environment.apps = apps;
+	local function http_app_added(event)
+		local app_name = event.item.name;
+		if not app_name then		
+			-- TODO: Link to docs
+			module:log("error", "HTTP app has no 'name', add one or use module:provides('http', app)");
+			return;
+		end
+		apps[app_name] = apps[app_name] or {};
+		local app_handlers = apps[app_name];
+		for key, handler in pairs(event.item.route or {}) do
+			local event_name = get_http_event(module, app_name, key);
+			if event_name then
+				if not app_handlers[event_name] then
+					app_handlers[event_name] = handler;
+					server.add_handler(event_name, handler);
+				else
+					module:log("warn", "App %s added handler twice for '%s', ignoring", app_name, event_name);
 				end
+			else
+				module:log("error", "Invalid route in %s: %q", app_name, key);
 			end
 		end
 	end
-
-	return h;
-end
-function clear_handlers(event)
-	handlers[event.source.host] = nil;
-end
-function get_handler(host, path)
-	local h = handlers[host] or build_handlers(host);
-	if h then
-		local item = h[path];
-		return item and item.handler;
-	end
-end
-module:handle_items("http-handler", clear_handlers, clear_handlers, false);
-
-function http_handler(event)
-	local request, response = event.request, event.response;
-
-	local handler = get_handler(request.headers.host:match("[^:]*"):lower(), request.path:match("[^?]*"));
-	if handler then
-		handler(request, response);
-		return true;
+	
+	local function http_app_removed(event)
+		local app_handlers = apps[event.item.name];
+		apps[event.item.name] = nil;
+		for event, handler in pairs(app_handlers) do
+			server.remove_handler(event, handler);
+		end
 	end
+	
+	module:handle_items("http-provider", http_app_added, http_app_removed);
 end
 
-local server = require "net.http.server";
-local listener = server.listener;
-server.add_handler("*", http_handler);
-function module.unload()
-	server.remove_handler("*", http_handler);
-end
---require "net.http.server".listen_on(8080);
-
 module:add_item("net-provider", {
 	name = "http";
-	listener = listener;
+	listener = server.listener;
 	default_port = 5280;
 	multiplex = {
 		pattern = "^[A-Z]";
@@ -77,7 +78,8 @@ module:add_item("net-provider", {
 
 module:add_item("net-provider", {
 	name = "https";
-	listener = listener;
+	listener = server.listener;
+	default_port = 5281;
 	encryption = "ssl";
 	multiplex = {
 		pattern = "^[A-Z]";
-- 
cgit v1.2.3


From f28df31a1bdc6546b271c67f2b11185f9fc39583 Mon Sep 17 00:00:00 2001
From: Matthew Wild <mwild1@gmail.com>
Date: Mon, 23 Apr 2012 18:23:49 +0100
Subject: mod_http: Support for default_path in apps

---
 plugins/mod_http.lua | 10 ++++++----
 1 file changed, 6 insertions(+), 4 deletions(-)

(limited to 'plugins')

diff --git a/plugins/mod_http.lua b/plugins/mod_http.lua
index d8e1e10a..cd1984c0 100644
--- a/plugins/mod_http.lua
+++ b/plugins/mod_http.lua
@@ -17,7 +17,7 @@ local function normalize_path(path)
 	return path;
 end
 
-local function get_http_event(http_module, app_name, key)
+local function get_http_event(host, app_path, key)
 	local method, path = key:match("^(%S+)%s+(.+)$");
 	if not method and key:sub(1,1) == "/" then
 		method, path = "GET", key;
@@ -25,15 +25,17 @@ local function get_http_event(http_module, app_name, key)
 		return nil;
 	end
 	path = normalize_path(path);
-	local app_path = normalize_path(http_module:get_option_string(app_name.."_http_path", "/"..app_name));
-	return method:upper().." "..http_module.host..app_path..path;
+	return method:upper().." "..host..app_path..path;
 end
 
 function module.add_host(module)
+	local host = module.host;
 	local apps = {};
 	module.environment.apps = apps;
 	local function http_app_added(event)
 		local app_name = event.item.name;
+		local default_app_path = event.item.default_path or "/"..app_name;
+		local app_path = normalize_path(module:get_option_string(app_name.."_http_path", default_app_path));
 		if not app_name then		
 			-- TODO: Link to docs
 			module:log("error", "HTTP app has no 'name', add one or use module:provides('http', app)");
@@ -42,7 +44,7 @@ function module.add_host(module)
 		apps[app_name] = apps[app_name] or {};
 		local app_handlers = apps[app_name];
 		for key, handler in pairs(event.item.route or {}) do
-			local event_name = get_http_event(module, app_name, key);
+			local event_name = get_http_event(host, app_path, key);
 			if event_name then
 				if not app_handlers[event_name] then
 					app_handlers[event_name] = handler;
-- 
cgit v1.2.3


From a3ceb27baef9490381162c206e89b7d2f67ef3c7 Mon Sep 17 00:00:00 2001
From: Matthew Wild <mwild1@gmail.com>
Date: Mon, 23 Apr 2012 21:34:05 +0100
Subject: mod_http: Pass portion of path that matched wildcard to wildcard
 handlers, as a second parameter

---
 plugins/mod_http.lua | 8 ++++++++
 1 file changed, 8 insertions(+)

(limited to 'plugins')

diff --git a/plugins/mod_http.lua b/plugins/mod_http.lua
index cd1984c0..3dcb14d7 100644
--- a/plugins/mod_http.lua
+++ b/plugins/mod_http.lua
@@ -46,6 +46,14 @@ function module.add_host(module)
 		for key, handler in pairs(event.item.route or {}) do
 			local event_name = get_http_event(host, app_path, key);
 			if event_name then
+				if event_name:sub(-2, -1) == "/*" then
+					local base_path = event_name:match("/(.+)/*$");
+					local _handler = handler;
+					handler = function (event)
+						local path = event.request.path:sub(#base_path+1);
+						return _handler(event, path);
+					end;
+				end
 				if not app_handlers[event_name] then
 					app_handlers[event_name] = handler;
 					server.add_handler(event_name, handler);
-- 
cgit v1.2.3


From 148a2fd256c8236cf0e2c0b2f98c17c116bc1e6f Mon Sep 17 00:00:00 2001
From: Kim Alvefur <zash@zash.se>
Date: Mon, 23 Apr 2012 23:36:50 +0200
Subject: mod_httpserver: Adapt to use the new HTTP API

---
 plugins/mod_httpserver.lua | 67 +++++++++++++++++++---------------------------
 1 file changed, 28 insertions(+), 39 deletions(-)

(limited to 'plugins')

diff --git a/plugins/mod_httpserver.lua b/plugins/mod_httpserver.lua
index 654aff06..bc9cbcd3 100644
--- a/plugins/mod_httpserver.lua
+++ b/plugins/mod_httpserver.lua
@@ -6,19 +6,17 @@
 -- COPYING file in the source package for more information.
 --
 
-
-local httpserver = require "net.httpserver";
+module:depends("http");
 local lfs = require "lfs";
 
 local open = io.open;
-local t_concat = table.concat;
 local stat = lfs.attributes;
 
-local http_base = config.get("*", "core", "http_path") or "www_files";
+local http_base = module:get_option_string("http_path", "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 :(" };
+local response_400 = "<h1>Bad Request</h1>Sorry, we didn't understand your request :(";
+local response_403 = "<h1>Forbidden</h1>You don't have permission to view the contents of this directory :(";
+local response_404 = "<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 = {
@@ -49,49 +47,40 @@ local function preprocess_path(path)
 	return path;
 end
 
-function serve_file(path)
+function serve_file(request, path)
+	local response = request.response;
+	path = path and preprocess_path(path);
+	if not path then
+		response.status = 400;
+		return response:send(response_400);
+	end
 	local full_path = http_base..path;
 	if stat(full_path, "mode") == "directory" then
 		if stat(full_path.."/index.html", "mode") == "file" then
-			return serve_file(path.."/index.html");
+			return serve_file(request, path.."/index.html");
 		end
-		return response_403;
+		response.status = 403;
+		return response:send(response_403);
 	end
 	local f, err = open(full_path, "rb");
-	if not f then return response_404; end
+	if not f then
+		response.status = 404;
+		return response:send(response_404.."<br/>"..tostring(err));
+	end
 	local data = f:read("*a");
 	f:close();
 	if not data then
-		return response_403;
+		response.status = 403;
+		return response:send(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)
-	local path = preprocess_path(request.url.path);
-	if not path then return response_400; end
-	path = path:gsub("^/[^/]+", ""); -- Strip /files/
-	return serve_file(path);
+	response.headers.content_type = mime_map[ext]; -- Content-Type should be nil when not known
+	return response:send(data);
 end
 
-local function handle_default_request(method, body, request)
-	local path = preprocess_path(request.url.path);
-	if not path then return response_400; end
-	return serve_file(path);
-end
+module:provides("http", {
+	route = {
+		["/*"] = serve_file;
+	};
+});
 
-local function setup()
-	local ports = config.get(module.host, "core", "http_ports") or { 5280 };
-	httpserver.set_default_handler(handle_default_request);
-	httpserver.new_from_config(ports, handle_file_request, { base = "files" });
-end
-if prosody.start_time then -- already started
-	setup();
-else
-	prosody.events.add_handler("server-started", setup);
-end
-- 
cgit v1.2.3


From b12b795d37a1be763c870886faad75abfe7a2228 Mon Sep 17 00:00:00 2001
From: Kim Alvefur <zash@zash.se>
Date: Mon, 23 Apr 2012 23:37:43 +0200
Subject: mod_httpserver: Rename to mod_http_files

---
 plugins/mod_http_files.lua | 86 ++++++++++++++++++++++++++++++++++++++++++++++
 plugins/mod_httpserver.lua | 86 ----------------------------------------------
 2 files changed, 86 insertions(+), 86 deletions(-)
 create mode 100644 plugins/mod_http_files.lua
 delete mode 100644 plugins/mod_httpserver.lua

(limited to 'plugins')

diff --git a/plugins/mod_http_files.lua b/plugins/mod_http_files.lua
new file mode 100644
index 00000000..bc9cbcd3
--- /dev/null
+++ b/plugins/mod_http_files.lua
@@ -0,0 +1,86 @@
+-- Prosody IM
+-- Copyright (C) 2008-2010 Matthew Wild
+-- Copyright (C) 2008-2010 Waqas Hussain
+-- 
+-- This project is MIT/X11 licensed. Please see the
+-- COPYING file in the source package for more information.
+--
+
+module:depends("http");
+local lfs = require "lfs";
+
+local open = io.open;
+local stat = lfs.attributes;
+
+local http_base = module:get_option_string("http_path", "www_files");
+
+local response_400 = "<h1>Bad Request</h1>Sorry, we didn't understand your request :(";
+local response_403 = "<h1>Forbidden</h1>You don't have permission to view the contents of this directory :(";
+local response_404 = "<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;
+	end
+	local level = 0;
+	for component in path:gmatch("([^/]+)/") do
+		if component == ".." then
+			level = level - 1;
+		elseif component ~= "." then
+			level = level + 1;
+		end
+		if level < 0 then
+			return nil;
+		end
+	end
+	return path;
+end
+
+function serve_file(request, path)
+	local response = request.response;
+	path = path and preprocess_path(path);
+	if not path then
+		response.status = 400;
+		return response:send(response_400);
+	end
+	local full_path = http_base..path;
+	if stat(full_path, "mode") == "directory" then
+		if stat(full_path.."/index.html", "mode") == "file" then
+			return serve_file(request, path.."/index.html");
+		end
+		response.status = 403;
+		return response:send(response_403);
+	end
+	local f, err = open(full_path, "rb");
+	if not f then
+		response.status = 404;
+		return response:send(response_404.."<br/>"..tostring(err));
+	end
+	local data = f:read("*a");
+	f:close();
+	if not data then
+		response.status = 403;
+		return response:send(response_403);
+	end
+	local ext = path:match("%.([^.]*)$");
+	response.headers.content_type = mime_map[ext]; -- Content-Type should be nil when not known
+	return response:send(data);
+end
+
+module:provides("http", {
+	route = {
+		["/*"] = serve_file;
+	};
+});
+
diff --git a/plugins/mod_httpserver.lua b/plugins/mod_httpserver.lua
deleted file mode 100644
index bc9cbcd3..00000000
--- a/plugins/mod_httpserver.lua
+++ /dev/null
@@ -1,86 +0,0 @@
--- Prosody IM
--- Copyright (C) 2008-2010 Matthew Wild
--- Copyright (C) 2008-2010 Waqas Hussain
--- 
--- This project is MIT/X11 licensed. Please see the
--- COPYING file in the source package for more information.
---
-
-module:depends("http");
-local lfs = require "lfs";
-
-local open = io.open;
-local stat = lfs.attributes;
-
-local http_base = module:get_option_string("http_path", "www_files");
-
-local response_400 = "<h1>Bad Request</h1>Sorry, we didn't understand your request :(";
-local response_403 = "<h1>Forbidden</h1>You don't have permission to view the contents of this directory :(";
-local response_404 = "<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;
-	end
-	local level = 0;
-	for component in path:gmatch("([^/]+)/") do
-		if component == ".." then
-			level = level - 1;
-		elseif component ~= "." then
-			level = level + 1;
-		end
-		if level < 0 then
-			return nil;
-		end
-	end
-	return path;
-end
-
-function serve_file(request, path)
-	local response = request.response;
-	path = path and preprocess_path(path);
-	if not path then
-		response.status = 400;
-		return response:send(response_400);
-	end
-	local full_path = http_base..path;
-	if stat(full_path, "mode") == "directory" then
-		if stat(full_path.."/index.html", "mode") == "file" then
-			return serve_file(request, path.."/index.html");
-		end
-		response.status = 403;
-		return response:send(response_403);
-	end
-	local f, err = open(full_path, "rb");
-	if not f then
-		response.status = 404;
-		return response:send(response_404.."<br/>"..tostring(err));
-	end
-	local data = f:read("*a");
-	f:close();
-	if not data then
-		response.status = 403;
-		return response:send(response_403);
-	end
-	local ext = path:match("%.([^.]*)$");
-	response.headers.content_type = mime_map[ext]; -- Content-Type should be nil when not known
-	return response:send(data);
-end
-
-module:provides("http", {
-	route = {
-		["/*"] = serve_file;
-	};
-});
-
-- 
cgit v1.2.3


From 84e65b574580b2b8101f61c440cb29a85ea31f0f Mon Sep 17 00:00:00 2001
From: Kim Alvefur <zash@zash.se>
Date: Tue, 24 Apr 2012 00:17:15 +0200
Subject: mod_http_files: Rename argument to reflect what it actually is

---
 plugins/mod_http_files.lua | 6 +++---
 1 file changed, 3 insertions(+), 3 deletions(-)

(limited to 'plugins')

diff --git a/plugins/mod_http_files.lua b/plugins/mod_http_files.lua
index bc9cbcd3..437633e7 100644
--- a/plugins/mod_http_files.lua
+++ b/plugins/mod_http_files.lua
@@ -47,8 +47,8 @@ local function preprocess_path(path)
 	return path;
 end
 
-function serve_file(request, path)
-	local response = request.response;
+function serve_file(event, path)
+	local response = event.response;
 	path = path and preprocess_path(path);
 	if not path then
 		response.status = 400;
@@ -57,7 +57,7 @@ function serve_file(request, path)
 	local full_path = http_base..path;
 	if stat(full_path, "mode") == "directory" then
 		if stat(full_path.."/index.html", "mode") == "file" then
-			return serve_file(request, path.."/index.html");
+			return serve_file(event, path.."/index.html");
 		end
 		response.status = 403;
 		return response:send(response_403);
-- 
cgit v1.2.3


From 44654c1b36a82889cc5d25cabccad88b6d6b1590 Mon Sep 17 00:00:00 2001
From: Matthew Wild <mwild1@gmail.com>
Date: Tue, 24 Apr 2012 15:40:00 +0100
Subject: mod_admin_telnet: Add initial port:list() and port:close() commands

---
 plugins/mod_admin_telnet.lua | 48 +++++++++++++++++++++++++++++++++++++++++++-
 1 file changed, 47 insertions(+), 1 deletion(-)

(limited to 'plugins')

diff --git a/plugins/mod_admin_telnet.lua b/plugins/mod_admin_telnet.lua
index cb6302c7..aa65b05b 100644
--- a/plugins/mod_admin_telnet.lua
+++ b/plugins/mod_admin_telnet.lua
@@ -759,6 +759,51 @@ function def_env.host:list()
 	return true, i.." hosts";
 end
 
+def_env.port = {};
+
+function def_env.port:list()
+	local print = self.session.print;
+	local services = portmanager.get_active_services().data;
+	local ordered_services, n_ports = {}, 0;
+	for service, interfaces in pairs(services) do
+		table.insert(ordered_services, service);
+	end
+	table.sort(ordered_services);
+	for _, service in ipairs(ordered_services) do
+		local ports_list = {};
+		for interface, ports in pairs(services[service]) do
+			for port in pairs(ports) do
+				table.insert(ports_list, "["..interface.."]:"..port);
+			end
+		end
+		n_ports = n_ports + #ports_list;
+		print(service..": "..table.concat(ports_list, ", "));
+	end
+	return true, #ordered_services.." services listening on "..n_ports.." ports";
+end
+
+function def_env.port:close(close_port, close_interface)
+	close_port = assert(tonumber(close_port), "Invalid port number");
+	local n_closed = 0;
+	local services = portmanager.get_active_services().data;
+	for service, interfaces in pairs(services) do
+		for interface, ports in pairs(interfaces) do
+			if not close_interface or close_interface == interface then
+				if ports[close_port] then
+					self.session.print("Closing ["..interface.."]:"..close_port.."...");
+					local ok, err = portmanager.close(interface, close_port)
+					if not ok then
+						self.session.print("Failed to close "..interface.." "..port..": "..err);
+					else
+						n_closed = n_closed + 1;
+					end
+				end
+			end
+		end
+	end
+	return true, "Closed "..n_closed.." ports";
+end
+
 -------------
 
 function printbanner(session)
@@ -789,7 +834,8 @@ if option and option ~= "short" and option ~= "full" and option ~= "graphic" the
 end
 end
 
-require "core.portmanager".register_service("console", {
+module:add_item("net-provider", {
+	name = "console";
 	listener = console_listener;
 	default_port = 5582;
 	private = true;
-- 
cgit v1.2.3


From 904d33cce45285aeb581b4b22cea418204ccbbed Mon Sep 17 00:00:00 2001
From: Matthew Wild <mwild1@gmail.com>
Date: Tue, 24 Apr 2012 16:02:30 +0100
Subject: mod_http: Fix specifying method in app route keys

---
 plugins/mod_http.lua | 7 ++++---
 1 file changed, 4 insertions(+), 3 deletions(-)

(limited to 'plugins')

diff --git a/plugins/mod_http.lua b/plugins/mod_http.lua
index 3dcb14d7..6da4db24 100644
--- a/plugins/mod_http.lua
+++ b/plugins/mod_http.lua
@@ -19,10 +19,11 @@ end
 
 local function get_http_event(host, app_path, key)
 	local method, path = key:match("^(%S+)%s+(.+)$");
-	if not method and key:sub(1,1) == "/" then
+	if not method then
+		if key:sub(1,1) ~= "/" then
+			return nil;
+		end
 		method, path = "GET", key;
-	else
-		return nil;
 	end
 	path = normalize_path(path);
 	return method:upper().." "..host..app_path..path;
-- 
cgit v1.2.3


From 6f6e05cf7436075c999eee0e5165795311a25406 Mon Sep 17 00:00:00 2001
From: Matthew Wild <mwild1@gmail.com>
Date: Tue, 24 Apr 2012 18:50:22 +0100
Subject: mod_proxy65: Port to portmanager, make a shared module

---
 plugins/mod_proxy65.lua | 184 ++++++++++++++++++++++--------------------------
 1 file changed, 86 insertions(+), 98 deletions(-)

(limited to 'plugins')

diff --git a/plugins/mod_proxy65.lua b/plugins/mod_proxy65.lua
index d02f3b58..155cb60d 100644
--- a/plugins/mod_proxy65.lua
+++ b/plugins/mod_proxy65.lua
@@ -6,35 +6,21 @@
 -- This project is MIT/X11 licensed. Please see the
 -- COPYING file in the source package for more information.
 --
---[[
-* to restart the proxy in the console: e.g.
-module:unload("proxy65");
-> server.removeserver(<proxy65_port>);
-module:load("proxy65", <proxy65_jid>);
-]]--
 
+module:set_global();
 
-local module = module;
-local tostring = tostring;
 local jid_compare, jid_prep = require "util.jid".compare, require "util.jid".prep;
 local st = require "util.stanza";
-local connlisteners = require "net.connlisteners";
 local sha1 = require "util.hashes".sha1;
-local server = require "net.server";
 local b64 = require "util.encodings".base64.encode;
+local server = require "net.server";
 
-local host, name = module:get_host(), "SOCKS5 Bytestreams Service";
-local sessions, transfers = {}, {};
-
-local proxy_port = module:get_option("proxy65_port") or 5000;
-local proxy_interface = module:get_option("proxy65_interface") or "*";
-local proxy_address = module:get_option("proxy65_address") or (proxy_interface ~= "*" and proxy_interface) or host;
-local proxy_acl = module:get_option("proxy65_acl");
+local sessions, transfers = module:shared("sessions", "transfers");
 local max_buffer_size = 4096;
 
-local connlistener = { default_port = proxy_port, default_interface = proxy_interface, default_mode = "*a" };
+local listener = {};
 
-function connlistener.onincoming(conn, data)
+function listener.onincoming(conn, data)
 	local session = sessions[conn] or {};
 
 	local transfer = transfers[session.sha];
@@ -84,7 +70,7 @@ function connlistener.onincoming(conn, data)
 	end
 end
 
-function connlistener.ondisconnect(conn, err)
+function listener.ondisconnect(conn, err)
 	local session = sessions[conn];
 	if session then
 		if transfers[session.sha] then
@@ -101,88 +87,90 @@ function connlistener.ondisconnect(conn, err)
 	end
 end
 
-module:add_identity("proxy", "bytestreams", name);
-module:add_feature("http://jabber.org/protocol/bytestreams");
-
-module:hook("iq-get/host/http://jabber.org/protocol/disco#info:query", function(event)
-	local origin, stanza = event.origin, event.stanza;
-	origin.send(st.reply(stanza):query("http://jabber.org/protocol/disco#info")
-		:tag("identity", {category='proxy', type='bytestreams', name=name}):up()
-		:tag("feature", {var="http://jabber.org/protocol/bytestreams"}) );
-	return true;
-end, -1);
-
-module:hook("iq-get/host/http://jabber.org/protocol/disco#items:query", function(event)
-	local origin, stanza = event.origin, event.stanza;
-	origin.send(st.reply(stanza):query("http://jabber.org/protocol/disco#items"));
-	return true;
-end, -1);
+function module.add_host(module)
+	local host, name = module:get_host(), module:get_option_string("name", "SOCKS5 Bytestreams Service");
+	
+	local proxy_address = module:get_option("proxy65_address", host);
+	local proxy_port = module:get_option_number("proxy65_port", next(portmanager.get_active_services():search("proxy65", nil)[1]));
+	local proxy_acl = module:get_option("proxy65_acl");
 
-module:hook("iq-get/host/http://jabber.org/protocol/bytestreams:query", function(event)
-	local origin, stanza = event.origin, event.stanza;
+	module:add_identity("proxy", "bytestreams", name);
+	module:add_feature("http://jabber.org/protocol/bytestreams");
+	
+	module:hook("iq-get/host/http://jabber.org/protocol/disco#info:query", function(event)
+		local origin, stanza = event.origin, event.stanza;
+		origin.send(st.reply(stanza):query("http://jabber.org/protocol/disco#info")
+			:tag("identity", {category='proxy', type='bytestreams', name=name}):up()
+			:tag("feature", {var="http://jabber.org/protocol/bytestreams"}) );
+		return true;
+	end, -1);
+	
+	module:hook("iq-get/host/http://jabber.org/protocol/disco#items:query", function(event)
+		local origin, stanza = event.origin, event.stanza;
+		origin.send(st.reply(stanza):query("http://jabber.org/protocol/disco#items"));
+		return true;
+	end, -1);
 	
-	-- check ACL
-	while proxy_acl and #proxy_acl > 0 do -- using 'while' instead of 'if' so we can break out of it
-		local jid = stanza.attr.from;
-		for _, acl in ipairs(proxy_acl) do
-			if jid_compare(jid, acl) then break; end
+	module:hook("iq-get/host/http://jabber.org/protocol/bytestreams:query", function(event)
+		local origin, stanza = event.origin, event.stanza;
+		
+		-- check ACL
+		while proxy_acl and #proxy_acl > 0 do -- using 'while' instead of 'if' so we can break out of it
+			local jid = stanza.attr.from;
+			for _, acl in ipairs(proxy_acl) do
+				if jid_compare(jid, acl) then break; end
+			end
+			module:log("warn", "Denying use of proxy for %s", tostring(stanza.attr.from));
+			origin.send(st.error_reply(stanza, "auth", "forbidden"));
+			return true;
 		end
-		module:log("warn", "Denying use of proxy for %s", tostring(stanza.attr.from));
-		origin.send(st.error_reply(stanza, "auth", "forbidden"));
+	
+		local sid = stanza.tags[1].attr.sid;
+		origin.send(st.reply(stanza):tag("query", {xmlns="http://jabber.org/protocol/bytestreams", sid=sid})
+			:tag("streamhost", {jid=host, host=proxy_address, port=proxy_port}));
 		return true;
-	end
-
-	local sid = stanza.tags[1].attr.sid;
-	origin.send(st.reply(stanza):tag("query", {xmlns="http://jabber.org/protocol/bytestreams", sid=sid})
-		:tag("streamhost", {jid=host, host=proxy_address, port=proxy_port}));
-	return true;
-end);
-
-module.unload = function()
-	connlisteners.deregister(module.host .. ':proxy65');
-end
-
-module:hook("iq-set/host/http://jabber.org/protocol/bytestreams:query", function(event)
-	local origin, stanza = event.origin, event.stanza;
-
-	local query = stanza.tags[1];
-	local sid = query.attr.sid;
-	local from = stanza.attr.from;
-	local to = query:get_child_text("activate");
-	local prepped_to = jid_prep(to);
-
-	local info = "sid: "..tostring(sid)..", initiator: "..tostring(from)..", target: "..tostring(prepped_to or to);
-	if prepped_to and sid then
-		local sha = sha1(sid .. from .. prepped_to, true);
-		if not transfers[sha] then
-			module:log("debug", "Activation request has unknown session id; activation failed (%s)", info);
-			origin.send(st.error_reply(stanza, "modify", "item-not-found"));
-		elseif not transfers[sha].initiator then
-			module:log("debug", "The sender was not connected to the proxy; activation failed (%s)", info);
-			origin.send(st.error_reply(stanza, "cancel", "not-allowed", "The sender (you) is not connected to the proxy"));
-		--elseif not transfers[sha].target then -- can't happen, as target is set when a transfer object is created
-		--	module:log("debug", "The recipient was not connected to the proxy; activation failed (%s)", info);
-		--	origin.send(st.error_reply(stanza, "cancel", "not-allowed", "The recipient is not connected to the proxy"));
-		else -- if transfers[sha].initiator ~= nil and transfers[sha].target ~= nil then
-			module:log("debug", "Transfer activated (%s)", info);
-			transfers[sha].activated = true;
-			transfers[sha].target:resume();
-			transfers[sha].initiator:resume();
-			origin.send(st.reply(stanza));
+	end);
+	
+	module:hook("iq-set/host/http://jabber.org/protocol/bytestreams:query", function(event)
+		local origin, stanza = event.origin, event.stanza;
+	
+		local query = stanza.tags[1];
+		local sid = query.attr.sid;
+		local from = stanza.attr.from;
+		local to = query:get_child_text("activate");
+		local prepped_to = jid_prep(to);
+	
+		local info = "sid: "..tostring(sid)..", initiator: "..tostring(from)..", target: "..tostring(prepped_to or to);
+		if prepped_to and sid then
+			local sha = sha1(sid .. from .. prepped_to, true);
+			if not transfers[sha] then
+				module:log("debug", "Activation request has unknown session id; activation failed (%s)", info);
+				origin.send(st.error_reply(stanza, "modify", "item-not-found"));
+			elseif not transfers[sha].initiator then
+				module:log("debug", "The sender was not connected to the proxy; activation failed (%s)", info);
+				origin.send(st.error_reply(stanza, "cancel", "not-allowed", "The sender (you) is not connected to the proxy"));
+			--elseif not transfers[sha].target then -- can't happen, as target is set when a transfer object is created
+			--	module:log("debug", "The recipient was not connected to the proxy; activation failed (%s)", info);
+			--	origin.send(st.error_reply(stanza, "cancel", "not-allowed", "The recipient is not connected to the proxy"));
+			else -- if transfers[sha].initiator ~= nil and transfers[sha].target ~= nil then
+				module:log("debug", "Transfer activated (%s)", info);
+				transfers[sha].activated = true;
+				transfers[sha].target:resume();
+				transfers[sha].initiator:resume();
+				origin.send(st.reply(stanza));
+			end
+		elseif to and sid then
+			module:log("debug", "Malformed activation jid; activation failed (%s)", info);
+			origin.send(st.error_reply(stanza, "modify", "jid-malformed"));
+		else
+			module:log("debug", "Bad request; activation failed (%s)", info);
+			origin.send(st.error_reply(stanza, "modify", "bad-request"));
 		end
-	elseif to and sid then
-		module:log("debug", "Malformed activation jid; activation failed (%s)", info);
-		origin.send(st.error_reply(stanza, "modify", "jid-malformed"));
-	else
-		module:log("debug", "Bad request; activation failed (%s)", info);
-		origin.send(st.error_reply(stanza, "modify", "bad-request"));
-	end
-	return true;
-end);
-
-if not connlisteners.register(module.host .. ':proxy65', connlistener) then
-	module:log("error", "mod_proxy65: Could not establish a connection listener. Check your configuration please.");
-	module:log("error", "Possibly two proxy65 components are configured to share the same port.");
+		return true;
+	end);
 end
 
-connlisteners.start(module.host .. ':proxy65');
+module:provides("net", {
+	default_port = 5000;
+	listener = listener;
+});
-- 
cgit v1.2.3