Merge compression fix

Sat, 29 May 2010 02:08:56 +0100

author
Matthew Wild <mwild1@gmail.com>
date
Sat, 29 May 2010 02:08:56 +0100
changeset 3128
2eac6dadd9a8
parent 3126
9095eb464260 (diff)
parent 3127
94639081f327 (current diff)
child 3129
125f03db0b1a

Merge compression fix

--- a/configure	Sat May 29 02:08:11 2010 +0100
+++ b/configure	Sat May 29 02:08:56 2010 +0100
@@ -61,7 +61,7 @@
 
 while [ "$1" ]
 do
-   value="`echo $1 | sed 's/.*=\(.*\)/\1/'`"
+   value="`echo $1 | sed 's/[^=]*=\(.*\)/\1/'`"
    if echo "$value" | grep -q "~"
    then
       echo
--- a/core/rostermanager.lua	Sat May 29 02:08:11 2010 +0100
+++ b/core/rostermanager.lua	Sat May 29 02:08:56 2010 +0100
@@ -93,15 +93,18 @@
 	else -- Attempt to load roster for non-loaded user
 		log("debug", "load_roster: loading for offline user: "..username.."@"..host);
 	end
-	roster = datamanager.load(username, host, "roster") or {};
+	local data, err = datamanager.load(username, host, "roster");
+	roster = data or {};
 	if user then user.roster = roster; end
-	if not roster[false] then roster[false] = { }; end
+	if not roster[false] then roster[false] = { broken = err or nil }; end
 	if roster[jid] then
 		roster[jid] = nil;
 		log("warn", "roster for "..jid.." has a self-contact");
 	end
-	hosts[host].events.fire_event("roster-load", username, host, roster);
-	return roster;
+	if not err then
+		hosts[host].events.fire_event("roster-load", username, host, roster);
+	end
+	return roster, err;
 end
 
 function save_roster(username, host, roster)
@@ -122,6 +125,7 @@
 		if metadata.version ~= true then
 			metadata.version = (metadata.version or 0) + 1;
 		end
+		if roster[false].broken then return nil, "Not saving broken roster" end
 		return datamanager.store(username, host, "roster", roster);
 	end
 	log("warn", "save_roster: user had no roster to save");
@@ -187,9 +191,9 @@
 end
 
 function is_contact_subscribed(username, host, jid)
-	local roster = load_roster(username, host);
+	local roster, err = load_roster(username, host);
 	local item = roster[jid];
-	return item and (item.subscription == "from" or item.subscription == "both");
+	return item and (item.subscription == "from" or item.subscription == "both"), err;
 end
 
 function is_contact_pending_in(username, host, jid)
--- a/core/sessionmanager.lua	Sat May 29 02:08:11 2010 +0100
+++ b/core/sessionmanager.lua	Sat May 29 02:08:56 2010 +0100
@@ -136,7 +136,7 @@
 		local sessions = hosts[session.host].sessions[session.username].sessions;
 		local limit = config_get(session.host, "core", "max_resources") or 10;
 		if #sessions >= limit then
-			return nil, "cancel", "conflict", "Resource limit reached; only "..limit.." resources allowed";
+			return nil, "cancel", "resource-constraint", "Resource limit reached; only "..limit.." resources allowed";
 		end
 		if sessions[resource] then
 			-- Resource conflict
@@ -174,7 +174,19 @@
 	hosts[session.host].sessions[session.username].sessions[resource] = session;
 	full_sessions[session.full_jid] = session;
 	
-	session.roster = rm_load_roster(session.username, session.host);
+	local err;
+	session.roster, err = rm_load_roster(session.username, session.host);
+	if err then
+		full_sessions[session.full_jid] = nil;
+		hosts[session.host].sessions[session.username].sessions[resource] = nil;
+		session.full_jid = nil;
+		session.resource = nil;
+		if next(bare_sessions[session.username..'@'..session.host].sessions) == nil then
+			bare_sessions[session.username..'@'..session.host] = nil;
+			hosts[session.host].sessions[session.username] = nil;
+		end
+		return nil, "cancel", "internal-server-error", "Error loading roster";
+	end
 	
 	hosts[session.host].events.fire_event("resource-bind", {session=session});
 	
--- a/core/usermanager.lua	Sat May 29 02:08:11 2010 +0100
+++ b/core/usermanager.lua	Sat May 29 02:08:56 2010 +0100
@@ -16,13 +16,15 @@
 local config = require "core.configmanager";
 local hosts = hosts;
 
+local require_provisioning = config.get("*", "core", "cyrus_require_provisioning") or false;
+
 local prosody = _G.prosody;
 
 module "usermanager"
 
 local new_default_provider;
 
-prosody.events.add_handler("host-activated", function (host)
+local function host_handler(host)
 	local host_session = hosts[host];
 	host_session.events.add_handler("item-added/auth-provider", function (provider)
 		if config.get(host, "core", "authentication") == provider.name then
@@ -35,14 +37,16 @@
 		end
 	end);
 	host_session.users = new_default_provider(host); -- Start with the default usermanager provider
-end);
+end
+prosody.events.add_handler("host-activated", host_handler);
+prosody.events.add_handler("component-activated", host_handler);
 
 local function is_cyrus(host) return config.get(host, "core", "sasl_backend") == "cyrus"; end
 
 function new_default_provider(host)
 	local provider = { name = "default" };
 	
-	function provider.test_password(username, password)
+	function provider:test_password(username, password)
 		if is_cyrus(host) then return nil, "Legacy auth not supported with Cyrus SASL."; end
 		local credentials = datamanager.load(username, host, "accounts") or {};
 	
@@ -53,12 +57,12 @@
 		end
 	end
 
-	function provider.get_password(username)
+	function provider:get_password(username)
 		if is_cyrus(host) then return nil, "Passwords unavailable for Cyrus SASL."; end
 		return (datamanager.load(username, host, "accounts") or {}).password;
 	end
 	
-	function provider.set_password(username, password)
+	function provider:set_password(username, password)
 		if is_cyrus(host) then return nil, "Passwords unavailable for Cyrus SASL."; end
 		local account = datamanager.load(username, host, "accounts");
 		if account then
@@ -68,29 +72,32 @@
 		return nil, "Account not available.";
 	end
 
-	function provider.user_exists(username)
-		if is_cyrus(host) then return true; end
-		return datamanager.load(username, host, "accounts") ~= nil; -- FIXME also check for empty credentials
+	function provider:user_exists(username)
+		if not(require_provisioning) and is_cyrus(host) then return true; end
+		local account, err = datamanager.load(username, host, "accounts") ~= nil; -- FIXME also check for empty credentials
+		return (account or err) ~= nil; -- FIXME also check for empty credentials
 	end
 
-	function provider.create_user(username, password)
-		if is_cyrus(host) then return nil, "Account creation/modification not available with Cyrus SASL."; end
+	function provider:create_user(username, password)
+		if not(require_provisioning) and is_cyrus(host) then return nil, "Account creation/modification not available with Cyrus SASL."; end
 		return datamanager.store(username, host, "accounts", {password = password});
 	end
 
-	function provider.get_supported_methods()
+	function provider:get_supported_methods()
 		return {["PLAIN"] = true, ["DIGEST-MD5"] = true}; -- TODO this should be taken from the config
 	end
 
-	function provider.is_admin(jid)
+	function provider:is_admin(jid)
 		local admins = config.get(host, "core", "admins");
-		if admins ~= config.get("*", "core", "admins") and type(admins) == "table" then
-			jid = jid_bare(jid);
-			for _,admin in ipairs(admins) do
-				if admin == jid then return true; end
+		if admins ~= config.get("*", "core", "admins") then
+			if type(admins) == "table" then
+				jid = jid_bare(jid);
+				for _,admin in ipairs(admins) do
+					if admin == jid then return true; end
+				end
+			elseif admins then
+				log("error", "Option 'admins' for host '%s' is not a table", host);
 			end
-		elseif admins then
-			log("error", "Option 'admins' for host '%s' is not a table", host);
 		end
 		return is_admin(jid); -- Test whether it's a global admin instead
 	end
@@ -98,32 +105,32 @@
 end
 
 function validate_credentials(host, username, password, method)
-	return hosts[host].users.test_password(username, password);
+	return hosts[host].users:test_password(username, password);
 end
 
 function get_password(username, host)
-	return hosts[host].users.get_password(username);
+	return hosts[host].users:get_password(username);
 end
 
 function set_password(username, host, password)
-	return hosts[host].users.set_password(username, password);
+	return hosts[host].users:set_password(username, password);
 end
 
 function user_exists(username, host)
-	return hosts[host].users.user_exists(username);
+	return hosts[host].users:user_exists(username);
 end
 
 function create_user(username, password, host)
-	return hosts[host].users.create_user(username, password);
+	return hosts[host].users:create_user(username, password);
 end
 
 function get_supported_methods(host)
-	return hosts[host].users.get_supported_methods();
+	return hosts[host].users:get_supported_methods();
 end
 
 function is_admin(jid, host)
 	if host and host ~= "*" then
-		return hosts[host].users.is_admin(jid);
+		return hosts[host].users:is_admin(jid);
 	else -- Test only whether this JID is a global admin
 		local admins = config.get("*", "core", "admins");
 		if type(admins) == "table" then
@@ -138,4 +145,6 @@
 	end
 end
 
+_M.new_default_provider = new_default_provider;
+
 return _M;
--- a/net/dns.lua	Sat May 29 02:08:11 2010 +0100
+++ b/net/dns.lua	Sat May 29 02:08:56 2010 +0100
@@ -851,7 +851,13 @@
 
 function resolver:lookup(qname, qtype, qclass)    -- - - - - - - - - -  lookup
 	self:query (qname, qtype, qclass)
-	while self:pulse() do socket.select(self.socket, nil, 4); end
+	while self:pulse() do
+           local recvt = {}
+           for i, s in ipairs(self.socket) do
+              recvt[i] = s
+           end
+           socket.select(recvt, nil, 4)
+        end
 	--print(self.cache);
 	return self:peek(qname, qtype, qclass);
 end
--- a/net/httpserver_listener.lua	Sat May 29 02:08:11 2010 +0100
+++ b/net/httpserver_listener.lua	Sat May 29 02:08:56 2010 +0100
@@ -29,7 +29,7 @@
 		end
 	end
 
-	if data then
+	if data and data ~= "" then
 		request_reader(request, data);
 	end
 end
--- a/plugins/mod_bosh.lua	Sat May 29 02:08:11 2010 +0100
+++ b/plugins/mod_bosh.lua	Sat May 29 02:08:56 2010 +0100
@@ -31,6 +31,8 @@
 local BOSH_DEFAULT_REQUESTS = tonumber(module:get_option("bosh_max_requests")) or 2;
 local BOSH_DEFAULT_MAXPAUSE = tonumber(module:get_option("bosh_max_pause")) or 300;
 
+local consider_bosh_secure = module:get_option_boolean("consider_bosh_secure");
+
 local default_headers = { ["Content-Type"] = "text/xml; charset=utf-8" };
 local session_close_reply = { headers = default_headers, body = st.stanza("body", { xmlns = xmlns_bosh, type = "terminate" }), attr = {} };
 
@@ -63,8 +65,11 @@
 	local session = sessions[request.sid];
 	if session then
 		local requests = session.requests;
-		for i,r in pairs(requests) do
-			if r == request then requests[i] = nil; break; end
+		for i,r in ipairs(requests) do
+			if r == request then
+				t_remove(requests, i);
+				break;
+			end
 		end
 		
 		-- If this session now has no requests open, mark it as inactive
@@ -90,6 +95,8 @@
 	--log("debug", "Handling new request %s: %s\n----------", request.id, tostring(body));
 	request.notopen = true;
 	request.log = log;
+	request.on_destroy = on_destroy_request;
+	
 	local parser = lxp.new(init_xmlhandlers(request, stream_callbacks), "\1");
 	
 	parser:parse(body);
@@ -118,14 +125,21 @@
 			session.send(resp);
 		end
 		
-		if not request.destroyed and session.bosh_wait then
-			request.reply_before = os_time() + session.bosh_wait;
-			request.on_destroy = on_destroy_request;
-			waiting_requests[request] = true;
+		if not request.destroyed then
+			-- We're keeping this request open, to respond later
+			log("debug", "Have nothing to say, so leaving request unanswered for now");
+			if session.bosh_wait then
+				request.reply_before = os_time() + session.bosh_wait;
+				waiting_requests[request] = true;
+			end
+			if inactive_sessions[session] then
+				-- Session was marked as inactive, since we have
+				-- a request open now, unmark it
+				inactive_sessions[session] = nil;
+			end
 		end
 		
-		log("debug", "Have nothing to say, so leaving request unanswered for now");
-		return true;
+		return true; -- Inform httpserver we shall reply later
 	end
 end
 
@@ -162,10 +176,14 @@
 		
 		-- New session
 		sid = new_uuid();
-		local session = { type = "c2s_unauthed", conn = {}, sid = sid, rid = tonumber(attr.rid), host = attr.to, bosh_version = attr.ver, bosh_wait = attr.wait, streamid = sid, 
-						bosh_hold = BOSH_DEFAULT_HOLD, bosh_max_inactive = BOSH_DEFAULT_INACTIVITY,
-						requests = { }, send_buffer = {}, reset_stream = bosh_reset_stream, close = bosh_close_stream, 
-						dispatch_stanza = core_process_stanza, log = logger.init("bosh"..sid), secure = request.secure };
+		local session = {
+			type = "c2s_unauthed", conn = {}, sid = sid, rid = tonumber(attr.rid)-1, host = attr.to,
+			bosh_version = attr.ver, bosh_wait = attr.wait, streamid = sid,
+			bosh_hold = BOSH_DEFAULT_HOLD, bosh_max_inactive = BOSH_DEFAULT_INACTIVITY,
+			requests = { }, send_buffer = {}, reset_stream = bosh_reset_stream,
+			close = bosh_close_stream, dispatch_stanza = core_process_stanza,
+			log = logger.init("bosh"..sid),	secure = consider_bosh_secure or request.secure
+		};
 		sessions[sid] = session;
 		
 		log("info", "New BOSH session, assigned it sid '%s'", sid);
@@ -174,11 +192,6 @@
 		function session.send(s)
 			--log("debug", "Sending BOSH data: %s", tostring(s));
 			local oldest_request = r[1];
-			while oldest_request and oldest_request.destroyed do
-				t_remove(r, 1);
-				waiting_requests[oldest_request] = nil;
-				oldest_request = r[1];
-			end
 			if oldest_request then
 				log("debug", "We have an open request, so sending on that");
 				response.body = t_concat{"<body xmlns='http://jabber.org/protocol/httpbind' sid='", sid, "' xmlns:stream = 'http://etherx.jabber.org/streams'>", tostring(s), "</body>" };
@@ -193,7 +206,6 @@
 				else
 					log("debug", "Destroying the request now...");
 					oldest_request:destroy();
-					t_remove(r, 1);
 				end
 			elseif s ~= "" then
 				log("debug", "Saved to send buffer because there are %d open requests", #r);
@@ -235,8 +247,9 @@
 			session.log("warn", "rid too large (means a request was lost). Last rid: %d New rid: %s", session.rid, attr.rid);
 		elseif diff <= 0 then
 			-- Repeated, ignore
-			session.log("debug", "rid repeated (on request %s), ignoring: %d", request.id, session.rid);
+			session.log("debug", "rid repeated (on request %s), ignoring: %s (diff %d)", request.id, session.rid, diff);
 			request.notopen = nil;
+			request.sid = sid;
 			t_insert(session.requests, request);
 			return;
 		end
@@ -250,12 +263,6 @@
 		return;
 	end
 	
-	-- If session was inactive, make sure it is now marked as not
-	if #session.requests == 0 then
-		(session.log or log)("debug", "BOSH client now active again at %d", os_time());
-		inactive_sessions[session] = nil;
-	end
-	
 	if session.notopen then
 		local features = st.stanza("stream:features");
 		hosts[session.host].events.fire_event("stream-features", { origin = session, features = features });
--- a/plugins/mod_groups.lua	Sat May 29 02:08:11 2010 +0100
+++ b/plugins/mod_groups.lua	Sat May 29 02:08:56 2010 +0100
@@ -18,7 +18,7 @@
 local module_host = module:get_host();
 
 function inject_roster_contacts(username, host, roster)
-	module:log("warn", "Injecting group members to roster");
+	--module:log("debug", "Injecting group members to roster");
 	local bare_jid = username.."@"..host;
 	if not members[bare_jid] and not members[false] then return; end -- Not a member of any groups
 	
@@ -41,7 +41,7 @@
 	-- Find groups this JID is a member of
 	if members[bare_jid] then
 		for _, group_name in ipairs(members[bare_jid]) do
-			module:log("debug", "Importing group %s", group_name);
+			--module:log("debug", "Importing group %s", group_name);
 			import_jids_to_roster(group_name);
 		end
 	end
@@ -49,7 +49,7 @@
 	-- Import public groups
 	if members[false] then
 		for _, group_name in ipairs(members[false]) do
-			module:log("debug", "Importing group %s", group_name);
+			--module:log("debug", "Importing group %s", group_name);
 			import_jids_to_roster(group_name);
 		end
 	end
@@ -67,7 +67,9 @@
 				new_roster[jid] = contact;
 			end
 		end
-		new_roster[false].version = nil; -- Version is void
+		if new_roster[false] then
+			new_roster[false].version = nil; -- Version is void
+		end
 		return username, host, datastore, new_roster;
 	end
 
--- a/plugins/mod_presence.lua	Sat May 29 02:08:11 2010 +0100
+++ b/plugins/mod_presence.lua	Sat May 29 02:08:56 2010 +0100
@@ -38,42 +38,23 @@
 	_core_route_stanza(origin, stanza);
 end
 
-local select_top_resources;
-local bare_message_delivery_policy = module:get_option("bare_message_delivery_policy") or "priority";
-if bare_message_delivery_policy == "broadcast" then
-	function select_top_resources(user)
-		local recipients = {};
-		for _, session in pairs(user.sessions) do -- find resources with non-negative priority
+local function select_top_resources(user)
+	local priority = 0;
+	local recipients = {};
+	for _, session in pairs(user.sessions) do -- find resource with greatest priority
+		if session.presence then
+			-- TODO check active privacy list for session
 			local p = session.priority;
-			if p and p >= 0 then
+			if p > priority then
+				priority = p;
+				recipients = {session};
+			elseif p == priority then
 				t_insert(recipients, session);
 			end
 		end
-		return recipients;
 	end
-else
-	if bare_message_delivery_policy ~= "priority" then
-		module:log("warn", "Invalid value for config option bare_message_delivery_policy");
-	end
-	function select_top_resources(user)
-		local priority = 0;
-		local recipients = {};
-		for _, session in pairs(user.sessions) do -- find resource with greatest priority
-			if session.presence then
-				-- TODO check active privacy list for session
-				local p = session.priority;
-				if p > priority then
-					priority = p;
-					recipients = {session};
-				elseif p == priority then
-					t_insert(recipients, session);
-				end
-			end
-		end
-		return recipients;
-	end
+	return recipients;
 end
-
 local function recalc_resource_map(user)
 	if user then
 		user.top_resources = select_top_resources(user);
@@ -81,7 +62,17 @@
 	end
 end
 
+local ignore_presence_priority = module:get_option("ignore_presence_priority");
+
 function handle_normal_presence(origin, stanza, core_route_stanza)
+	if ignore_presence_priority then
+		local priority = stanza:child_with_name("priority");
+		if priority and priority[1] ~= "0" then
+			for i=#priority.tags,1,-1 do priority.tags[i] = nil; end
+			for i=#priority,1,-1 do priority[i] = nil; end
+			priority[1] = "0";
+		end
+	end
 	if full_sessions[origin.full_jid] then -- if user is still connected
 		origin.send(stanza); -- reflect their presence back to them
 	end
@@ -236,16 +227,13 @@
 	stanza.attr.from, stanza.attr.to = from_bare, to_bare;
 	log("debug", "inbound presence "..stanza.attr.type.." from "..from_bare.." for "..to_bare);
 	
-	if not node then
-		log("debug", "dropping presence sent to host or invalid address '%s'", tostring(to_bare));
-	end
-	
 	if stanza.attr.type == "probe" then
-		if rostermanager.is_contact_subscribed(node, host, from_bare) then
+		local result, err = rostermanager.is_contact_subscribed(node, host, from_bare);
+		if result then
 			if 0 == send_presence_of_available_resources(node, host, st_from, origin, core_route_stanza) then
 				core_route_stanza(hosts[host], st.presence({from=to_bare, to=from_bare, type="unavailable"})); -- TODO send last activity
 			end
-		else
+		elseif not err then
 			core_route_stanza(hosts[host], st.presence({from=to_bare, to=from_bare, type="unsubscribed"}));
 		end
 	elseif stanza.attr.type == "subscribe" then
--- a/plugins/mod_privacy.lua	Sat May 29 02:08:11 2010 +0100
+++ b/plugins/mod_privacy.lua	Sat May 29 02:08:56 2010 +0100
@@ -13,7 +13,7 @@
 local bare_sessions, full_sessions = bare_sessions, full_sessions;
 local util_Jid = require "util.jid";
 local jid_bare = util_Jid.bare;
-local jid_split = util_Jid.split;
+local jid_split, jid_join = util_Jid.split, util_Jid.join;
 local load_roster = require "core.rostermanager".load_roster;
 local to_number = tonumber;
 
@@ -160,26 +160,7 @@
 			end
 		end
 		
-		if tmp.type == "group" then
-			local found = false;
-			local roster = load_roster(origin.username, origin.host);
-			for jid,item in pairs(roster) do
-				if item.groups ~= nil then
-					for group in pairs(item.groups) do
-						if group == tmp.value then
-							found = true;
-							break;
-						end
-					end
-					if found == true then
-						break;
-					end
-				end
-			end
-			if found == false then
-				return {"cancel", "item-not-found", "Specifed roster group not existing."};
-			end
-		elseif tmp.type == "subscription" then
+		if tmp.type == "subscription" then
 			if	tmp.value ~= "both" and
 				tmp.value ~= "to" and
 				tmp.value ~= "from" and
@@ -326,7 +307,7 @@
 	local is_to_user = bare_jid == jid_bare(to);
 	local is_from_user = bare_jid == jid_bare(from);
 	
-	module:log("debug", "stanza: %s, to: %s, from: %s", tostring(stanza.name), tostring(to), tostring(from));
+	--module:log("debug", "stanza: %s, to: %s, from: %s", tostring(stanza.name), tostring(to), tostring(from));
 	
 	if privacy_lists.lists == nil or
 		not (session.activePrivacyList or privacy_lists.default)
@@ -334,7 +315,7 @@
 		return; -- Nothing to block, default is Allow all
 	end
 	if is_from_user and is_to_user then
-		module:log("debug", "Not blocking communications between user's resources");
+		--module:log("debug", "Not blocking communications between user's resources");
 		return; -- from one of a user's resource to another => HANDS OFF!
 	end
 	
@@ -344,8 +325,8 @@
 		listname = privacy_lists.default; -- no active list selected, use default list
 	end
 	local list = privacy_lists.lists[listname];
-	if not list then
-		module:log("debug", "given privacy list not found. name: %s", listname);
+	if not list then -- should never happen
+		module:log("warn", "given privacy list not found. name: %s for user %s", listname, bare_jid);
 		return;
 	end
 	for _,item in ipairs(list.items) do
@@ -364,10 +345,10 @@
 			local evilJid = {};
 			apply = false;
 			if is_to_user then
-				module:log("debug", "evil jid is (from): %s", from);
+				--module:log("debug", "evil jid is (from): %s", from);
 				evilJid.node, evilJid.host, evilJid.resource = jid_split(from);
 			else
-				module:log("debug", "evil jid is (to): %s", to);
+				--module:log("debug", "evil jid is (to): %s", to);
 				evilJid.node, evilJid.host, evilJid.resource = jid_split(to);
 			end
 			if	item.type == "jid" and
@@ -379,17 +360,22 @@
 				block = (item.action == "deny");
 			elseif item.type == "group" then
 				local roster = load_roster(session.username, session.host);
-				local groups = roster[evilJid.node .. "@" .. evilJid.host].groups;
-				for group in pairs(groups) do
-					if group == item.value then
-						apply = true;
-						block = (item.action == "deny");
-						break;
+				local roster_entry = roster[jid_join(evilJid.node, evilJid.host)];
+				if roster_entry then
+					local groups = roster_entry.groups;
+					for group in pairs(groups) do
+						if group == item.value then
+							apply = true;
+							block = (item.action == "deny");
+							break;
+						end
 					end
 				end
-			elseif item.type == "subscription" and evilJid.node ~= nil and evilJid.host ~= nil then -- we need a valid bare evil jid
+			elseif item.type == "subscription" then -- we need a valid bare evil jid
 				local roster = load_roster(session.username, session.host);
-				if roster[evilJid.node .. "@" .. evilJid.host].subscription == item.value then
+				local roster_entry = roster[jid_join(evilJid.node, evilJid.host)];
+				if (not(roster_entry) and item.value == "none")
+				   or (roster_entry and roster_entry.subscription == item.value) then
 					apply = true;
 					block = (item.action == "deny");
 				end
@@ -408,7 +394,7 @@
 				end
 				return true; -- stanza blocked !
 			else
-				module:log("debug", "stanza explicitly allowed!")
+				--module:log("debug", "stanza explicitly allowed!")
 				return;
 			end
 		end
@@ -439,7 +425,7 @@
 		if session ~= nil then
 			return checkIfNeedToBeBlocked(e, session);
 		else
-			module:log("debug", "preCheckIncoming: Couldn't get session for jid: %s@%s/%s", tostring(node), tostring(host), tostring(resource));
+			--module:log("debug", "preCheckIncoming: Couldn't get session for jid: %s@%s/%s", tostring(node), tostring(host), tostring(resource));
 		end
 	end
 end
--- a/plugins/mod_private.lua	Sat May 29 02:08:11 2010 +0100
+++ b/plugins/mod_private.lua	Sat May 29 02:08:56 2010 +0100
@@ -26,7 +26,11 @@
 				if #query.tags == 1 then
 					local tag = query.tags[1];
 					local key = tag.name..":"..tag.attr.xmlns;
-					local data = datamanager.load(node, host, "private");
+					local data, err = datamanager.load(node, host, "private");
+					if err then
+						session.send(st.error_reply(stanza, "wait", "internal-server-error"));
+						return true;
+					end
 					if stanza.attr.type == "get" then
 						if data and data[key] then
 							session.send(st.reply(stanza):tag("query", {xmlns = "jabber:iq:private"}):add_child(st.deserialize(data[key])));
--- a/plugins/mod_saslauth.lua	Sat May 29 02:08:11 2010 +0100
+++ b/plugins/mod_saslauth.lua	Sat May 29 02:08:56 2010 +0100
@@ -28,6 +28,12 @@
 local secure_auth_only = module:get_option("c2s_require_encryption") or module:get_option("require_encryption");
 local sasl_backend = module:get_option("sasl_backend") or "builtin";
 
+-- Cyrus config options
+local require_provisioning = module:get_option("cyrus_require_provisioning") or false;
+local cyrus_service_realm = module:get_option("cyrus_service_realm");
+local cyrus_service_name = module:get_option("cyrus_service_name");
+local cyrus_application_name = module:get_option("cyrus_application_name");
+
 local log = module._log;
 
 local xmlns_sasl ='urn:ietf:params:xml:ns:xmpp-sasl';
@@ -45,7 +51,11 @@
 	if ok then
 		local cyrus_new = cyrus.new;
 		new_sasl = function(realm)
-			return cyrus_new(realm, module:get_option("cyrus_service_name") or "xmpp");
+			return cyrus_new(
+				cyrus_service_realm or realm,
+				cyrus_service_name or "xmpp",
+				cyrus_application_name or "prosody"
+			);
 		end
 	else
 		module:log("error", "Failed to load Cyrus SASL because: %s", cyrus);
@@ -94,7 +104,7 @@
 	return reply;
 end
 
-local function handle_status(session, status)
+local function handle_status(session, status, ret, err_msg)
 	if status == "failure" then
 		session.sasl_handler = session.sasl_handler:clean_clone();
 	elseif status == "success" then
@@ -103,12 +113,20 @@
 			module:log("warn", "SASL succeeded but we didn't get a username!");
 			session.sasl_handler = nil;
 			session:reset_stream();
-			return;
+			return status, ret, err_msg;
 		end
-		sm_make_authenticated(session, session.sasl_handler.username);
-		session.sasl_handler = nil;
-		session:reset_stream();
+
+		if not(require_provisioning) or usermanager_user_exists(username, session.host) then
+			sm_make_authenticated(session, session.sasl_handler.username);
+			session.sasl_handler = nil;
+			session:reset_stream();
+		else
+			module:log("warn", "SASL succeeded but we don't have an account provisioned for %s", username);
+			session.sasl_handler = session.sasl_handler:clean_clone();
+			return "failure", "not-authorized", "User authenticated successfully, but not provisioned for XMPP";
+		end
 	end
+	return status, ret, err_msg;
 end
 
 local function sasl_handler(session, stanza)
@@ -142,7 +160,7 @@
 		end
 	end
 	local status, ret, err_msg = session.sasl_handler:process(text);
-	handle_status(session, status);
+	status, ret, err_msg = handle_status(session, status, ret, err_msg);
 	local s = build_reply(status, ret, err_msg);
 	log("debug", "sasl reply: %s", tostring(s));
 	session.send(s);
--- a/prosodyctl	Sat May 29 02:08:11 2010 +0100
+++ b/prosodyctl	Sat May 29 02:08:56 2010 +0100
@@ -122,8 +122,12 @@
 
 hosts = prosody.hosts;
 
+local function make_host(hostname)
+	return { events = prosody.events, users = require "core.usermanager".new_default_provider(hostname) };
+end
+
 for hostname, config in pairs(config.getconfig()) do
-	hosts[hostname] = { events = prosody.events };
+	hosts[hostname] = make_host(hostname);
 end
 	
 require "core.modulemanager"
@@ -235,16 +239,23 @@
 		return 1;
 	end
 	
+	if not hosts[host] then
+		show_warning("The host '%s' is not listed in the configuration file (or is not enabled).", host)
+		show_warning("The user will not be able to log in until this is changed.");
+		hosts[host] = make_host(host);
+	elseif config.get(host, "core", "authentication")
+		and config.get(host, "core", "authentication") ~= "default" then
+		show_warning("The host '%s' is configured to use the '%s' authentication provider", host,
+			config.get(host, "core", "authentication"));
+		show_warning("prosodyctl currently only supports the default provider, sorry :(");
+		return 1;
+	end
+	
 	if prosodyctl.user_exists{ user = user, host = host } then
 		show_message [[That user already exists]];
 		return 1;
 	end
 	
-	if not hosts[host] then
-		show_warning("The host '%s' is not listed in the configuration file (or is not enabled).", host)
-		show_warning("The user will not be able to log in until this is changed.");
-	end
-	
 	local password = read_password();
 	if not password then return 1; end
 	
@@ -273,6 +284,18 @@
 		return 1;
 	end
 	
+	if not hosts[host] then
+		show_warning("The host '%s' is not listed in the configuration file (or is not enabled).", host)
+		show_warning("The user will not be able to log in until this is changed.");
+		hosts[host] = make_host(host);
+	elseif config.get(host, "core", "authentication")
+		and config.get(host, "core", "authentication") ~= "default" then
+		show_warning("The host '%s' is configured to use the '%s' authentication provider", host,
+			config.get(host, "core", "authentication"));
+		show_warning("prosodyctl currently only supports the default provider, sorry :(");
+		return 1;
+	end
+	
 	if not prosodyctl.user_exists { user = user, host = host } then
 		show_message [[That user does not exist, use prosodyctl adduser to create a new user]]
 		return 1;
@@ -306,6 +329,18 @@
 		return 1;
 	end
 	
+	if not hosts[host] then
+		show_warning("The host '%s' is not listed in the configuration file (or is not enabled).", host)
+		show_warning("The user will not be able to log in until this is changed.");
+		hosts[host] = make_host(host);
+	elseif config.get(host, "core", "authentication")
+		and config.get(host, "core", "authentication") ~= "default" then
+		show_warning("The host '%s' is configured to use the '%s' authentication provider", host,
+			config.get(host, "core", "authentication"));
+		show_warning("prosodyctl currently only supports the default provider, sorry :(");
+		return 1;
+	end
+
 	if not prosodyctl.user_exists { user = user, host = host } then
 		show_message [[That user does not exist on this server]]
 		return 1;
--- a/util/datamanager.lua	Sat May 29 02:08:11 2010 +0100
+++ b/util/datamanager.lua	Sat May 29 02:08:56 2010 +0100
@@ -21,12 +21,13 @@
 local t_insert = table.insert;
 local append = require "util.serialization".append;
 local path_separator = "/"; if os.getenv("WINDIR") then path_separator = "\\" end
+local lfs = require "lfs";
 local raw_mkdir;
 
 if prosody.platform == "posix" then
 	raw_mkdir = require "util.pposix".mkdir; -- Doesn't trample on umask
 else
-	raw_mkdir = require "lfs".mkdir;
+	raw_mkdir = lfs.mkdir;
 end
 
 module "datamanager"
@@ -111,14 +112,21 @@
 function load(username, host, datastore)
 	local data, ret = loadfile(getpath(username, host, datastore));
 	if not data then
-		log("debug", "Failed to load "..datastore.." storage ('"..ret.."') for user: "..(username or "nil").."@"..(host or "nil"));
-		return nil;
+		local mode = lfs.attributes(getpath(username, host, datastore), "mode");
+		if not mode then
+			log("debug", "Failed to load "..datastore.." storage ('"..ret.."') for user: "..(username or "nil").."@"..(host or "nil"));
+			return nil;
+		else -- file exists, but can't be read
+			-- TODO more detailed error checking and logging?
+			log("error", "Failed to load "..datastore.." storage ('"..ret.."') for user: "..(username or "nil").."@"..(host or "nil"));
+			return nil, "Error reading storage";
+		end
 	end
 	setfenv(data, {});
 	local success, ret = pcall(data);
 	if not success then
 		log("error", "Unable to load "..datastore.." storage ('"..ret.."') for user: "..(username or "nil").."@"..(host or "nil"));
-		return nil;
+		return nil, "Error reading storage";
 	end
 	return ret;
 end
@@ -137,7 +145,7 @@
 	local f, msg = io_open(getpath(username, host, datastore, nil, true), "w+");
 	if not f then
 		log("error", "Unable to write to "..datastore.." storage ('"..msg.."') for user: "..(username or "nil").."@"..(host or "nil"));
-		return;
+		return nil, "Error saving to storage";
 	end
 	f:write("return ");
 	append(f, data);
--- a/util/sasl/digest-md5.lua	Sat May 29 02:08:11 2010 +0100
+++ b/util/sasl/digest-md5.lua	Sat May 29 02:08:56 2010 +0100
@@ -32,13 +32,13 @@
 --[[
 Supported Authentication Backends
 
-digest-md5:
+digest_md5:
 	function(username, domain, realm, encoding) -- domain and realm are usually the same; for some broken
 												-- implementations it's not
 		return digesthash, state;
 	end
 
-digest-md5-test:
+digest_md5_test:
 	function(username, domain, realm, encoding, digesthash)
 		return true or false, state;
 	end
--- a/util/sasl/plain.lua	Sat May 29 02:08:11 2010 +0100
+++ b/util/sasl/plain.lua	Sat May 29 02:08:56 2010 +0100
@@ -28,15 +28,10 @@
 		return password, state;
 	end
 
-plain-test:
+plain_test:
 	function(username, realm, password)
 		return true or false, state;
 	end
-	
-plain-hashed:
-	function(username, realm)
-		return hashed_password, hash_function, state;
-	end
 ]]
 
 local function plain(self, message)
@@ -66,10 +61,6 @@
 		if correct_password == password then correct = true; else correct = false; end
 	elseif self.profile.plain_test then
 		correct, state = self.profile.plain_test(authentication, self.realm, password);
-	elseif self.profile.plain_hashed then
-		local hashed_password, hash_f;
-		hashed_password, hash_f, state = self.profile.plain_hashed(authentication, self.realm);
-		if hashed_password == hash_f(password) then correct = true; else correct = false; end
 	end
 
 	self.username = authentication
@@ -85,7 +76,7 @@
 end
 
 function init(registerMechanism)
-	registerMechanism("PLAIN", {"plain", "plain_test", "plain_hashed"}, plain);
+	registerMechanism("PLAIN", {"plain", "plain_test"}, plain);
 end
 
 return _M;
--- a/util/sasl/scram.lua	Sat May 29 02:08:11 2010 +0100
+++ b/util/sasl/scram.lua	Sat May 29 02:08:56 2010 +0100
@@ -1,15 +1,15 @@
 -- sasl.lua v0.4
 -- Copyright (C) 2008-2010 Tobias Markmann
 --
---    All rights reserved.
+--	  All rights reserved.
 --
---    Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:
+--	  Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:
 --
---        * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
---        * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution.
---        * Neither the name of Tobias Markmann nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission.
+--		  * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
+--		  * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution.
+--		  * Neither the name of Tobias Markmann nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission.
 --
---    THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+--	  THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 
 local s_match = string.match;
 local type = type
@@ -32,7 +32,8 @@
 --[[
 Supported Authentication Backends
 
-scram-{MECH}:
+scram_{MECH}:
+	-- MECH being a standard hash name (like those at IANA's hash registry) with '-' replaced with '_'
 	function(username, realm)
 		return salted_password, iteration_count, salt, state;
 	end
@@ -92,19 +93,45 @@
 	return username;
 end
 
+local function hashprep( hashname ) 
+	local hash = hashname:lower()
+	hash = hash:gsub("-", "_")
+	return hash
+end
+
+function saltedPasswordSHA1(password, salt, iteration_count)
+	local salted_password
+	if type(password) ~= "string" or type(salt) ~= "string" or type(iteration_count) ~= "number" then
+		return false, "inappropriate argument types"
+	end
+	if iteration_count < 4096 then
+		log("warning", "Iteration count < 4096 which is the suggested minimum according to RFC 5802.")
+	end
+
+	return true, Hi(hmac_sha1, password, salt, iteration_count);
+end
+
 local function scram_gen(hash_name, H_f, HMAC_f)
 	local function scram_hash(self, message)
 		if not self.state then self["state"] = {} end
 	
+		if type(message) ~= "string" or #message == 0 then return "failure", "malformed-request" end
 		if not self.state.name then
 			-- we are processing client_first_message
 			local client_first_message = message;
+			
+			-- TODO: fail if authzid is provided, since we don't support them yet
 			self.state["client_first_message"] = client_first_message;
-			self.state["name"] = client_first_message:match("n=(.+),r=")
-			self.state["clientnonce"] = client_first_message:match("r=([^,]+)")
-		
+			self.state["gs2_cbind_flag"], self.state["authzid"], self.state["name"], self.state["clientnonce"]
+				= client_first_message:match("^(%a),(.*),n=(.*),r=([^,]*).*");
+
+			-- we don't do any channel binding yet
+			if self.state.gs2_cbind_flag ~= "n" and self.state.gs2_cbind_flag ~= "y" then
+				return "failure", "malformed-request";
+			end
+
 			if not self.state.name or not self.state.clientnonce then
-				return "failure", "malformed-request";
+				return "failure", "malformed-request", "Channel binding isn't support at this time.";
 			end
 		
 			self.state.name = validate_username(self.state.name);
@@ -126,11 +153,18 @@
 					log("debug", "Password violates SASLprep.");
 					return "failure", "not-authorized", "Invalid password."
 				end
+
 				self.state.salt = generate_uuid();
 				self.state.iteration_count = default_i;
-				self.state.salted_password = Hi(HMAC_f, password, self.state.salt, default_i);
-			elseif self.profile["scram_"..hash_name] then
-				local salted_password, iteration_count, salt, state = self.profile["scram-"..hash_name](self.state.name, self.realm);
+
+				local succ = false;
+				succ, self.state.salted_password = saltedPasswordSHA1(password, self.state.salt, default_i, self.state.iteration_count);
+				if not succ then
+					log("error", "Generating salted password failed. Reason: %s", self.state.salted_password);
+					return "failure", "temporary-auth-failure";
+				end
+			elseif self.profile["scram_"..hashprep(hash_name)] then
+				local salted_password, iteration_count, salt, state = self.profile["scram_"..hashprep(hash_name)](self.state.name, self.realm);
 				if state == nil then return "failure", "not-authorized"
 				elseif state == false then return "failure", "account-disabled" end
 				
@@ -143,17 +177,19 @@
 			self.state["server_first_message"] = server_first_message;
 			return "challenge", server_first_message
 		else
-			if type(message) ~= "string" then return "failure", "malformed-request" end
 			-- we are processing client_final_message
 			local client_final_message = message;
-		
-			self.state["proof"] = client_final_message:match("p=(.+)");
-			self.state["nonce"] = client_final_message:match("r=(.+),p=");
-			self.state["channelbinding"] = client_final_message:match("c=(.+),r=");
+			
+			self.state["channelbinding"], self.state["nonce"], self.state["proof"] = client_final_message:match("^c=(.*),r=(.*),.*p=(.*)");
+	
 			if not self.state.proof or not self.state.nonce or not self.state.channelbinding then
 				return "failure", "malformed-request", "Missing an attribute(p, r or c) in SASL message.";
 			end
-		
+
+			if self.state.nonce ~= self.state.clientnonce..self.state.servernonce then
+				return "failure", "malformed-request", "Wrong nonce in client-final-message.";
+			end
+			
 			local SaltedPassword = self.state.salted_password;
 			local ClientKey = HMAC_f(SaltedPassword, "Client Key")
 			local ServerKey = HMAC_f(SaltedPassword, "Server Key")
@@ -162,7 +198,7 @@
 			local ClientSignature = HMAC_f(StoredKey, AuthMessage)
 			local ClientProof     = binaryXOR(ClientKey, ClientSignature)
 			local ServerSignature = HMAC_f(ServerKey, AuthMessage)
-		
+
 			if base64.encode(ClientProof) == self.state.proof then
 				local server_final_message = "v="..base64.encode(ServerSignature);
 				self["username"] = self.state.name;
@@ -177,10 +213,10 @@
 
 function init(registerMechanism)
 	local function registerSCRAMMechanism(hash_name, hash, hmac_hash)
-		registerMechanism("SCRAM-"..hash_name, {"plain", "scram_"..(hash_name:lower())}, scram_gen(hash_name:lower(), hash, hmac_hash));
+		registerMechanism("SCRAM-"..hash_name, {"plain", "scram_"..(hashprep(hash_name))}, scram_gen(hash_name:lower(), hash, hmac_hash));
 	end
-	
+
 	registerSCRAMMechanism("SHA-1", sha1, hmac_sha1);
 end
 
-return _M;
\ No newline at end of file
+return _M;
--- a/util/sasl_cyrus.lua	Sat May 29 02:08:11 2010 +0100
+++ b/util/sasl_cyrus.lua	Sat May 29 02:08:56 2010 +0100
@@ -45,10 +45,10 @@
 end
 
 -- create a new SASL object which can be used to authenticate clients
-function new(realm, service_name)
+function new(realm, service_name, app_name)
 	local sasl_i = {};
 
-	init(service_name);
+	init(app_name or service_name);
 
 	sasl_i.realm = realm;
 	sasl_i.service_name = service_name;

mercurial