changeset 603:efc9d88b70ab

merged.
author Marco Cirillo <maranda@lightwitch.org>
date Thu, 09 Feb 2012 00:54:39 +0000
parents 00590d492a5b (current diff) 072b05999b4b (diff)
children 17e879822700
files
diffstat 3 files changed, 86 insertions(+), 9 deletions(-) [+]
line wrap: on
line diff
--- a/mod_carbons/mod_carbons.lua	Thu Feb 09 00:53:32 2012 +0000
+++ b/mod_carbons/mod_carbons.lua	Thu Feb 09 00:54:39 2012 +0000
@@ -54,14 +54,20 @@
 		user_sessions = host_sessions[username];
 		if resource then
 			no_carbon_to[resource] = true;
-		else
+		elseif user_sessions then
 			local top_resources = user_sessions.top_resources;
-			for _, session in ipairs(top_resources) do
-				no_carbon_to[session.resource] = true;
+			if top_resources then
+				for _, session in ipairs(top_resources) do
+					no_carbon_to[session.resource] = true;
+				end
 			end
 		end
 	end
 
+	if not user_sessions then
+		return -- No use in sending carbons to an offline user
+	end
+
 	if not c2s and stanza:get_child("private", xmlns_carbons) then
 		stanza:maptags(function(tag)
 			return tag.attr.xmlns == xmlns_carbons
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/mod_conformance_restricted/mod_conformance_restricted.lua	Thu Feb 09 00:54:39 2012 +0000
@@ -0,0 +1,34 @@
+-- Prosody IM
+-- Copyright (C) 2012 Florian Zeitz
+-- 
+-- This project is MIT/X11 licensed. Please see the
+-- COPYING file in the source package for more information.
+--
+
+local st = require "util.stanza";
+
+module:hook("message/host", function (event)
+	local origin, stanza = event.origin, event.stanza;
+	local node, host, resource = jid.split(stanza.attr.to);
+	local body = stanza:get_child_text("body");
+	
+	if resource ~= "conformance" then
+		return; -- Not interop testing
+	end
+
+	if body == "PI" then
+		origin.send("<?testing this='out'?>");
+	elseif body == "comment" then
+		origin.send("<!-- no comment -->");
+	elseif body == "DTD" then
+		origin.send("<!DOCTYPE greeting [\n<!ENTITY test 'You should not see this'>\n]>");
+	elseif body == "entity" then
+		origin.send("<message type='chat' to='"..stanza.attr.from.."'><body>&test;</body></message>");
+	else
+		local reply = st.reply(stanza);
+		reply:body("Send me one of: PI, comment, DTD, or entity");
+		origin.send(reply);
+	end
+	
+	return true;
+end);
--- a/mod_smacks/mod_smacks.lua	Thu Feb 09 00:53:32 2012 +0000
+++ b/mod_smacks/mod_smacks.lua	Thu Feb 09 00:54:39 2012 +0000
@@ -7,9 +7,11 @@
 local tonumber, tostring = tonumber, tostring;
 local add_filter = require "util.filters".add_filter;
 local timer = require "util.timer";
+local datetime = require "util.datetime";
 
 local xmlns_sm = "urn:xmpp:sm:2";
 local xmlns_errors = "urn:ietf:params:xml:ns:xmpp-stanzas";
+local xmlns_delay = "urn:xmpp:delay";
 
 local sm_attr = { xmlns = xmlns_sm };
 
@@ -19,23 +21,38 @@
 
 local session_registry = {};
 
+local function can_do_smacks(session, advertise_only)
+	if session.smacks then return false, "unexpected-request", "Stream management is already enabled"; end
+	
+	local session_type = session.type;
+	if session_type == "c2s" then
+		if not(advertise_only) and not(session.resource) then -- Fail unless we're only advertising sm
+			return false, "unexpected-request", "Client must bind a resource before enabling stream management";
+		end
+		return true;
+	elseif s2s_smacks and (session_type == "s2sin" or session_type == "s2sout") then
+		return true;
+	end
+	return false, "service-unavailable", "Stream management is not available for this stream";
+end
+
 module:hook("stream-features",
 		function (event)
-			event.features:tag("sm", sm_attr):tag("optional"):up():up();
+			if can_do_smacks(event.origin, true) then
+				event.features:tag("sm", sm_attr):tag("optional"):up():up();
+			end
 		end);
 
 module:hook("s2s-stream-features",
 		function (event)
-			local origin = event.origin;
-			if s2s_smacks and (origin.type == "s2sin" or origin.type == "s2sout") then
+			if can_do_smacks(event.origin, true) then
 				event.features:tag("sm", sm_attr):tag("optional"):up():up();
 			end
 		end);
 
 module:hook_stanza("http://etherx.jabber.org/streams", "features",
 		function (session, stanza)
-			if s2s_smacks and (session.type == "s2sin" or session.type == "s2sout")
-					and not session.smacks and stanza:get_child("sm", xmlns_sm) then
+			if can_do_smacks(session) and stanza:get_child("sm", xmlns_sm) then
 				session.sends2s(st.stanza("enable", sm_attr));
 			end
 end);
@@ -55,7 +72,13 @@
 	local function new_send(stanza)
 		local attr = stanza.attr;
 		if attr and not attr.xmlns then -- Stanza in default stream namespace
-			queue[#queue+1] = st.clone(stanza);
+			local cached_stanza = st.clone(stanza);
+			
+			if cached_stanza and cached_stanza:get_child("delay", xmlns_delay) == nil then
+				cached_stanza = cached_stanza:tag("delay", { xmlns = xmlns_delay, from = session.host, stamp = datetime.datetime()});
+			end
+			
+			queue[#queue+1] = cached_stanza;
 		end
 		local ok, err = _send(stanza);
 		if ok and #queue > max_unacked_stanzas and not session.awaiting_ack then
@@ -86,6 +109,13 @@
 end
 
 module:hook_stanza(xmlns_sm, "enable", function (session, stanza)
+	local ok, err, err_text = can_do_smacks(session);
+	if not ok then
+		session.log("warn", "Failed to enable smacks: %s", err_text); -- TODO: XEP doesn't say we can send error text, should it?
+		session.send(st.stanza("failed", { xmlns = xmlns_sm }):tag(err, { xmlns = xmlns_errors}));
+		return true;
+	end
+
 	module:log("debug", "Enabling stream management");
 	session.smacks = true;
 	
@@ -181,10 +211,12 @@
 				handle_unacked_stanzas(session);
 			end
 		else
+			session.log("debug", "mod_smacks hibernating session for up to %d seconds", resume_timeout);
 			local hibernate_time = os_time(); -- Track the time we went into hibernation
 			session.hibernating = hibernate_time;
 			local resumption_token = session.resumption_token;
 			timer.add_task(resume_timeout, function ()
+				session.log("debug", "mod_smacks hibernation timeout reached...");
 				-- We need to check the current resumption token for this resource
 				-- matches the smacks session this timer is for in case it changed
 				-- (for example, the client may have bound a new resource and
@@ -194,11 +226,14 @@
 				-- Check the hibernate time still matches what we think it is,
 				-- otherwise the session resumed and re-hibernated.
 				and session.hibernating == hibernate_time then
+					session.log("debug", "Destroying session for hibernating too long");
 					session_registry[session.resumption_token] = nil;
 					session.resumption_token = nil;
 					-- This recursion back into our destroy handler is to
 					-- make sure we still handle any queued stanzas
 					sessionmanager.destroy_session(session);
+				else
+					session.log("debug", "Session resumed before hibernation timeout, all is well")
 				end
 			end);
 			return; -- Postpone destruction for now
@@ -212,11 +247,13 @@
 	local id = stanza.attr.previd;
 	local original_session = session_registry[id];
 	if not original_session then
+		session.log("debug", "Tried to resume non-existent session with id %s", id);
 		session.send(st.stanza("failed", sm_attr)
 			:tag("item-not-found", { xmlns = xmlns_errors })
 		);
 	elseif session.username == original_session.username
 	and session.host == original_session.host then
+		session.log("debug", "mod_smacks resuming existing session...");
 		-- TODO: All this should move to sessionmanager (e.g. session:replace(new_session))
 		original_session.ip = session.ip;
 		original_session.conn = session.conn;