Changeset

3012:6d86e26f0923

Merge configmanager->trunk
author Matthew Wild <mwild1@gmail.com>
date Wed, 05 May 2010 19:01:14 +0100
parents 3010:52146b82f295 (diff) 3011:1189a29cd846 (current diff)
children 3013:518e3f6f9946
files core/configmanager.lua
diffstat 17 files changed, 568 insertions(+), 387 deletions(-) [+]
line wrap: on
line diff
--- a/.hgtags	Wed Apr 14 12:59:46 2010 +0100
+++ b/.hgtags	Wed May 05 19:01:14 2010 +0100
@@ -36,3 +36,4 @@
 5ae3209fefa2c8dc1c53d08c2c1caa340b8ec542 0.5.2
 1a99a3bf3ce6dbdfb362b7fd101d761fb3cc10af 0.6.0
 81b4e738e4d321b78274132f63a9aec7007e64eb 0.6.1
+0395f2f34bd55a01ec7276884fb9a4e0051b0e7a 0.6.2
--- a/core/configmanager.lua	Wed Apr 14 12:59:46 2010 +0100
+++ b/core/configmanager.lua	Wed May 05 19:01:14 2010 +0100
@@ -30,10 +30,11 @@
 -- When key not found in section, check key in global's section
 function section_mt(section_name)
 	return { __index = 	function (t, k)
-									local section = rawget(global_config, section_name);
-									if not section then return nil; end
-									return section[k];
-							end };
+					local section = rawget(global_config, section_name);
+					if not section then return nil; end
+					return section[k];
+				end
+	};
 end
 
 function getconfig()
@@ -112,16 +113,20 @@
 	function parsers.lua.load(data, filename)
 		local env;
 		-- The ' = true' are needed so as not to set off __newindex when we assign the functions below
-		env = setmetatable({ Host = true, host = true, VirtualHost = true, Component = true, component = true,
-							Include = true, include = true, RunScript = dofile }, { __index = function (t, k)
-												return rawget(_G, k) or
-														function (settings_table)
-															config[__currenthost or "*"][k] = settings_table;
-														end;
-										end,
-								__newindex = function (t, k, v)
-											set(env.__currenthost or "*", "core", k, v);
-										end});
+		env = setmetatable({
+			Host = true, host = true, VirtualHost = true,
+			Component = true, component = true,
+			Include = true, include = true, RunScript = dofile }, {
+				__index = function (t, k)
+					return rawget(_G, k) or
+						function (settings_table)
+							config[__currenthost or "*"][k] = settings_table;
+						end;
+				end,
+				__newindex = function (t, k, v)
+					set(env.__currenthost or "*", "core", k, v);
+				end
+		});
 		
 		rawset(env, "__currenthost", "*") -- Default is global
 		function env.VirtualHost(name)
--- a/core/eventmanager.lua	Wed Apr 14 12:59:46 2010 +0100
+++ b/core/eventmanager.lua	Wed May 05 19:01:14 2010 +0100
@@ -10,24 +10,18 @@
 local t_insert = table.insert;
 local ipairs = ipairs;
 
+local events = _G.prosody.events;
+
 module "eventmanager"
 
 local event_handlers = {};
 
 function add_event_hook(name, handler)
-	if not event_handlers[name] then
-		event_handlers[name] = {};
-	end
-	t_insert(event_handlers[name] , handler);
+	return events.add_handler(name, handler);
 end
 
 function fire_event(name, ...)
-	local event_handlers = event_handlers[name];
-	if event_handlers then
-		for name, handler in ipairs(event_handlers) do
-			handler(...);
-		end
-	end
+	return events.fire_event(name, ...);
 end
 
-return _M;
\ No newline at end of file
+return _M;
--- a/core/hostmanager.lua	Wed Apr 14 12:59:46 2010 +0100
+++ b/core/hostmanager.lua	Wed May 05 19:01:14 2010 +0100
@@ -42,7 +42,7 @@
 	end
 	
 	if not activated_any_host then
-		log("error", "No hosts defined in the config file. This may cause unexpected behaviour as no modules will be loaded.");
+		log("error", "No active VirtualHost entries in the config file. This may cause unexpected behaviour as no modules will be loaded.");
 	end
 	
 	eventmanager.fire_event("hosts-activated", defined_hosts);
@@ -60,8 +60,8 @@
 			dialback_secret = configmanager.get(host, "core", "dialback_secret") or uuid_gen();
 	              };
 	for option_name in pairs(host_config.core) do
-		if option_name:match("_ports$") then
-			log("warn", "%s: Option '%s' has no effect for virtual hosts - put it in global Host \"*\" instead", host, option_name);
+		if option_name:match("_ports$") or option_name:match("_interface$") then
+			log("warn", "%s: Option '%s' has no effect for virtual hosts - put it in the server-wide section instead", host, option_name);
 		end
 	end
 	
--- a/core/modulemanager.lua	Wed Apr 14 12:59:46 2010 +0100
+++ b/core/modulemanager.lua	Wed May 05 19:01:14 2010 +0100
@@ -19,7 +19,7 @@
 local hosts = hosts;
 local prosody = prosody;
 
-local loadfile, pcall = loadfile, pcall;
+local loadfile, pcall, xpcall = loadfile, pcall, xpcall;
 local setmetatable, setfenv, getfenv = setmetatable, setfenv, getfenv;
 local pairs, ipairs = pairs, ipairs;
 local t_insert, t_concat = table.insert, table.concat;
@@ -29,6 +29,14 @@
 local error = error;
 local tostring, tonumber = tostring, tonumber;
 
+local debug_traceback = debug.traceback;
+local unpack, select = unpack, select;
+pcall = function(f, ...)
+	local n = select("#", ...);
+	local params = {...};
+	return xpcall(function() f(unpack(params, 1, n)) end, function(e) return tostring(e).."\n"..debug_traceback(); end);
+end
+
 local array, set = require "util.array", require "util.set";
 
 local autoload_modules = {"presence", "message", "iq"};
--- a/core/usermanager.lua	Wed Apr 14 12:59:46 2010 +0100
+++ b/core/usermanager.lua	Wed May 05 19:01:14 2010 +0100
@@ -16,82 +16,117 @@
 local config = require "core.configmanager";
 local hosts = hosts;
 
+local prosody = _G.prosody;
+
 module "usermanager"
 
+local new_default_provider;
+
+prosody.events.add_handler("host-activated", function (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
+			host_session.users = provider;
+		end
+	end);
+	host_session.events.add_handler("item-removed/auth-provider", function (provider)
+		if host_session.users == provider then
+			host_session.users = new_default_provider(host);
+		end
+	end);
+	host_session.users = new_default_provider(host); -- Start with the default usermanager provider
+end);
+
 local function is_cyrus(host) return config.get(host, "core", "sasl_backend") == "cyrus"; end
 
-function validate_credentials(host, username, password, method)
-	log("debug", "User '%s' is being validated", username);
-	if is_cyrus(host) then return nil, "Legacy auth not supported with Cyrus SASL."; end
-	local credentials = datamanager.load(username, host, "accounts") or {};
-
-	if method == nil then method = "PLAIN"; end
-	if method == "PLAIN" and credentials.password then -- PLAIN, do directly
+function new_default_provider(host)
+	local provider = { name = "default" };
+	
+	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 {};
+	
 		if password == credentials.password then
 			return true;
 		else
 			return nil, "Auth failed. Invalid username or password.";
 		end
-  end
-	-- must do md5
-	-- make credentials md5
-	local pwd = credentials.password;
-	if not pwd then pwd = credentials.md5; else pwd = hashes.md5(pwd, true); end
-	-- make password md5
-	if method == "PLAIN" then
-		password = hashes.md5(password or "", true);
-	elseif method ~= "DIGEST-MD5" then
-		return nil, "Unsupported auth method";
+	end
+
+	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)
+		if is_cyrus(host) then return nil, "Passwords unavailable for Cyrus SASL."; end
+		local account = datamanager.load(username, host, "accounts");
+		if account then
+			account.password = password;
+			return datamanager.store(username, host, "accounts", account);
+		end
+		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
+	end
+
+	function provider.create_user(username, password)
+		if is_cyrus(host) then return nil, "Account creation/modification not available with Cyrus SASL."; end
+		return datamanager.store(username, host, "accounts", {password = password});
 	end
-	-- compare
-	if password == pwd then
-		return true;
-	else
-		return nil, "Auth failed. Invalid username or password.";
+
+	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)
+		host = host or "*";
+		local admins = config.get(host, "core", "admins");
+		if host ~= "*" and admins == config.get("*", "core", "admins") then
+			return nil;
+		end
+		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("warn", "Option 'admins' for host '%s' is not a table", host);
+		end
+		return nil;
+	end
+	return provider;
+end
+
+function validate_credentials(host, username, password, method)
+	return hosts[host].users.test_password(username, password);
 end
 
 function get_password(username, host)
-	if is_cyrus(host) then return nil, "Passwords unavailable for Cyrus SASL."; end
-	return (datamanager.load(username, host, "accounts") or {}).password
+	return hosts[host].users.get_password(username);
 end
+
 function set_password(username, host, password)
-	if is_cyrus(host) then return nil, "Passwords unavailable for Cyrus SASL."; end
-	local account = datamanager.load(username, host, "accounts");
-	if account then
-		account.password = password;
-		return datamanager.store(username, host, "accounts", account);
-	end
-	return nil, "Account not available.";
+	return hosts[host].users.set_password(username, password);
 end
 
 function user_exists(username, host)
-	if is_cyrus(host) then return true; end
-	return datamanager.load(username, host, "accounts") ~= nil; -- FIXME also check for empty credentials
+	return hosts[host].users.user_exists(username);
 end
 
 function create_user(username, password, host)
-	if is_cyrus(host) then return nil, "Account creation/modification not available with Cyrus SASL."; end
-	return datamanager.store(username, host, "accounts", {password = password});
+	return hosts[host].users.create_user(username, password);
 end
 
 function get_supported_methods(host)
-	return {["PLAIN"] = true, ["DIGEST-MD5"] = true}; -- TODO this should be taken from the config
+	return hosts[host].users.get_supported_methods();
 end
 
 function is_admin(jid, host)
-	host = host or "*";
-	local admins = config.get(host, "core", "admins");
-	if host ~= "*" and admins == config.get("*", "core", "admins") then
-		return nil;
-	end
-	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("warn", "Option 'admins' for host '%s' is not a table", host); end
-	return nil;
+	return hosts[host].users.is_admin(jid);
 end
 
 return _M;
--- a/net/server_event.lua	Wed Apr 14 12:59:46 2010 +0100
+++ b/net/server_event.lua	Wed May 05 19:01:14 2010 +0100
@@ -161,7 +161,7 @@
 				self:_lock( false,  false, false )
 				--vdebug( "start listening on client socket with id:", self.id )
 				self.eventread = addevent( base, self.conn, EV_READ, self.readcallback, cfg.READ_TIMEOUT );  -- register callback
-				(self.onconnect or self.onincoming)(self)
+				self:onconnect()
 				self.eventsession = nil
 				return -1
 			end
@@ -282,8 +282,21 @@
 			return nointerface, noreading, nowriting
 	end
 	
+	--TODO: Deprecate
 	function interface_mt:lock_read(switch)
-		return self:_lock(self.nointerface, switch, self.nowriting);
+		if switch then
+			return self:pause();
+		else
+			return self:resume();
+		end
+	end
+
+	function interface_mt:pause()
+		return self:_lock(self.nointerface, true, self.nowriting);
+	end
+
+	function interface_mt:resume()
+		return self:_lock(self.nointerface, false, self.nowriting);
 	end
 
 	function interface_mt:counter(c)
@@ -389,6 +402,13 @@
 			self.starttls = false; -- prevent starttls()
 		end
 	end
+
+	function interface_mt:set_mode(pattern)
+		if pattern then
+			self._pattern = pattern;
+		end
+		return self._pattern;
+	end
 	
 	function interface_mt:set_send(new_send)
 		-- No-op, we always use the underlying connection's send
@@ -437,6 +457,7 @@
 	
 	-- Stub handlers
 	function interface_mt:onconnect()
+		return self:onincoming(nil);
 	end
 	function interface_mt:onincoming()
 	end
@@ -444,6 +465,8 @@
 	end
 	function interface_mt:ontimeout()
 	end
+	function interface_mt:ondrain()
+	end
 	function interface_mt:onstatus()
 		debug("server.lua: Dummy onstatus()")
 	end
@@ -524,6 +547,7 @@
 				if succ then  -- writing succesful
 					interface.writebuffer = ""
 					interface.writebufferlen = 0
+					interface:ondrain();
 					if interface.fatalerror then
 						debug "closing client after writing"
 						interface:_close()  -- close interface if needed
@@ -585,7 +609,7 @@
 						interface.eventreadtimeout = nil
 					end
 				end
-				local buffer, err, part = interface.conn:receive( pattern )  -- receive buffer with "pattern"
+				local buffer, err, part = interface.conn:receive( interface._pattern )  -- receive buffer with "pattern"
 				--vdebug( "read data:", tostring(buffer), "error:", tostring(err), "part:", tostring(part) )
 				buffer = buffer or part or ""
 				local len = string_len( buffer )
@@ -821,11 +845,32 @@
 	return signal_events[signal_num];
 end
 
+local function link(sender, receiver, buffersize)
+	sender:set_mode(buffersize);
+	local sender_locked;
+	
+	function receiver:ondrain()
+		if sender_locked then
+			sender:resume();
+			sender_locked = nil;
+		end
+	end
+	
+	function sender:onincoming(data)
+		receiver:write(data);
+		if receiver.writebufferlen >= buffersize then
+			sender_locked = true;
+			sender:pause();
+		end
+	end
+end
+
 return {
 
 	cfg = cfg,
 	base = base,
 	loop = loop,
+	link = link,
 	event = event,
 	event_base = base,
 	addevent = newevent,
--- a/net/server_select.lua	Wed Apr 14 12:59:46 2010 +0100
+++ b/net/server_select.lua	Wed May 05 19:01:14 2010 +0100
@@ -252,6 +252,7 @@
 	local dispatch = listeners.onincoming
 	local status = listeners.onstatus
 	local disconnect = listeners.ondisconnect
+	local drain = listeners.ondrain
 
 	local bufferqueue = { } -- buffer array
 	local bufferqueuelen = 0	-- end of buffer array
@@ -284,6 +285,7 @@
 		dispatch = listeners.onincoming
 		disconnect = listeners.ondisconnect
 		status = listeners.onstatus
+		drain = listeners.ondrain
 	end
 	handler.getstats = function( )
 		return readtraffic, sendtraffic
@@ -379,7 +381,7 @@
 	handler.socket = function( self )
 		return socket
 	end
-	handler.pattern = function( self, new )
+	handler.set_mode = function( self, new )
 		pattern = new or pattern
 		return pattern
 	end
@@ -392,6 +394,7 @@
 		maxreadlen = readlen or maxreadlen
 		return bufferlen, maxreadlen, maxsendlen
 	end
+	--TODO: Deprecate
 	handler.lock_read = function (self, switch)
 		if switch == true then
 			local tmp = _readlistlen
@@ -409,6 +412,12 @@
 		end
 		return noread
 	end
+	handler.pause = function (self)
+		return self:lock_read(true);
+	end
+	handler.resume = function (self)
+		return self:lock_read(false);
+	end
 	handler.lock = function( self, switch )
 		handler.lock_read (switch)
 		if switch == true then
@@ -430,7 +439,7 @@
 	end
 	local _readbuffer = function( ) -- this function reads data
 		local buffer, err, part = receive( socket, pattern )	-- receive buffer with "pattern"
-		if not err or (err == "wantread" or err == "timeout") or (part and string_len(part) > 0) then -- received something
+		if not err or (err == "wantread" or err == "timeout") then -- received something
 			local buffer = buffer or part or ""
 			local len = string_len( buffer )
 			if len > maxreadlen then
@@ -472,6 +481,9 @@
 			_sendlistlen = removesocket( _sendlist, socket, _sendlistlen ) -- delete socket from writelist
 			_ = needtls and handler:starttls(nil, true)
 			_writetimes[ handler ] = nil
+			if drain then
+				drain(handler)
+			end
 			_ = toclose and handler:close( )
 			return true
 		elseif byte and ( err == "timeout" or err == "wantwrite" ) then -- want write
@@ -508,6 +520,9 @@
 						out_put( "server.lua: ssl handshake done" )
 						handler.readbuffer = _readbuffer	-- when handshake is done, replace the handshake function with regular functions
 						handler.sendbuffer = _sendbuffer
+						out_put ("server.lua: compression used: "..tostring(client:compression()))
+						out_put ("server.lua: finished: "..tostring(client:getfinished()):sub(1, 300):gsub("[\r\n]+", " "):gsub("[%z\1-\31]", "_"))
+						out_put ("server.lua: peer finished: "..tostring(client:getpeerfinished()):sub(1, 300):gsub("[\r\n]+", " "):gsub("[%z\1-\31]", "_"))
 						_ = status and status( handler, "ssl-handshake-complete" )
 						_readlistlen = addsocket(_readlist, client, _readlistlen)
 						return true
@@ -617,7 +632,7 @@
 			listeners.onconnect(handler);
 			handler.sendbuffer = _sendbuffer;
 			if bufferqueuelen > 0 then
-				return _senddbuffer();
+				return _sendbuffer();
 			end
 		end
 	end
@@ -663,6 +678,28 @@
 	--mem_free( )
 end
 
+local function link(sender, receiver, buffersize)
+	sender:set_mode(buffersize);
+	local sender_locked;
+	local _sendbuffer = receiver.sendbuffer;
+	function receiver.sendbuffer()
+		_sendbuffer();
+		if sender_locked and receiver.bufferlen() < buffersize then
+			sender:lock_read(false); -- Unlock now
+			sender_locked = nil;
+		end
+	end
+	
+	local _readbuffer = sender.readbuffer;
+	function sender.readbuffer()
+		_readbuffer();
+		if not sender_locked and receiver.bufferlen() >= buffersize then
+			sender_locked = true;
+			sender:lock_read(true);
+		end
+	end
+end
+
 ----------------------------------// PUBLIC //--
 
 addserver = function( addr, port, listeners, pattern, sslctx ) -- this function provides a way for other scripts to reg a server
@@ -886,6 +923,7 @@
 	wrapclient = wrapclient,
 	
 	loop = loop,
+	link = link,
 	stats = stats,
 	closeall = closeall,
 	addtimer = addtimer,
--- a/plugins/mod_console.lua	Wed Apr 14 12:59:46 2010 +0100
+++ b/plugins/mod_console.lua	Wed May 05 19:01:14 2010 +0100
@@ -53,76 +53,76 @@
 
 local sessions = {};
 
+function console_listener.onconnect(conn)
+	-- Handle new connection
+	local session = console:new_session(conn);
+	sessions[conn] = session;
+	printbanner(session);
+end
+
 function console_listener.onincoming(conn, data)
 	local session = sessions[conn];
-	
-	if not session then
-		-- Handle new connection
-		session = console:new_session(conn);
-		sessions[conn] = session;
-		printbanner(session);
-	end
-	if data then
-		-- Handle data
-		(function(session, data)
-			local useglobalenv;
-			
-			if data:match("^>") then
-				data = data:gsub("^>", "");
-				useglobalenv = true;
-			elseif data == "\004" then
-				commands["bye"](session, data);
-				return;
-			else
-				local command = data:lower();
-				command = data:match("^%w+") or data:match("%p");
-				if commands[command] then
-					commands[command](session, data);
-					return;
-				end
-			end
 
-			session.env._ = data;
-			
-			local chunk, err = loadstring("return "..data);
-			if not chunk then
-				chunk, err = loadstring(data);
-				if not chunk then
-					err = err:gsub("^%[string .-%]:%d+: ", "");
-					err = err:gsub("^:%d+: ", "");
-					err = err:gsub("'<eof>'", "the end of the line");
-					session.print("Sorry, I couldn't understand that... "..err);
-					return;
-				end
-			end
-			
-			setfenv(chunk, (useglobalenv and redirect_output(_G, session)) or session.env or nil);
-			
-			local ranok, taskok, message = pcall(chunk);
-			
-			if not (ranok or message or useglobalenv) and commands[data:lower()] then
-				commands[data:lower()](session, data);
+	-- Handle data
+	(function(session, data)
+		local useglobalenv;
+		
+		if data:match("^>") then
+			data = data:gsub("^>", "");
+			useglobalenv = true;
+		elseif data == "\004" then
+			commands["bye"](session, data);
+			return;
+		else
+			local command = data:lower();
+			command = data:match("^%w+") or data:match("%p");
+			if commands[command] then
+				commands[command](session, data);
 				return;
 			end
-			
-			if not ranok then
-				session.print("Fatal error while running command, it did not complete");
-				session.print("Error: "..taskok);
+		end
+
+		session.env._ = data;
+		
+		local chunk, err = loadstring("return "..data);
+		if not chunk then
+			chunk, err = loadstring(data);
+			if not chunk then
+				err = err:gsub("^%[string .-%]:%d+: ", "");
+				err = err:gsub("^:%d+: ", "");
+				err = err:gsub("'<eof>'", "the end of the line");
+				session.print("Sorry, I couldn't understand that... "..err);
 				return;
 			end
-			
-			if not message then
-				session.print("Result: "..tostring(taskok));
-				return;
-			elseif (not taskok) and message then
-				session.print("Command completed with a problem");
-				session.print("Message: "..tostring(message));
-				return;
-			end
-			
-			session.print("OK: "..tostring(message));
-		end)(session, data);
-	end
+		end
+		
+		setfenv(chunk, (useglobalenv and redirect_output(_G, session)) or session.env or nil);
+		
+		local ranok, taskok, message = pcall(chunk);
+		
+		if not (ranok or message or useglobalenv) and commands[data:lower()] then
+			commands[data:lower()](session, data);
+			return;
+		end
+		
+		if not ranok then
+			session.print("Fatal error while running command, it did not complete");
+			session.print("Error: "..taskok);
+			return;
+		end
+		
+		if not message then
+			session.print("Result: "..tostring(taskok));
+			return;
+		elseif (not taskok) and message then
+			session.print("Command completed with a problem");
+			session.print("Message: "..tostring(message));
+			return;
+		end
+		
+		session.print("OK: "..tostring(message));
+	end)(session, data);
+	
 	session.send(string.char(0));
 end
 
--- a/plugins/mod_proxy65.lua	Wed Apr 14 12:59:46 2010 +0100
+++ b/plugins/mod_proxy65.lua	Wed May 05 19:01:14 2010 +0100
@@ -20,6 +20,7 @@
 local config_get = require "core.configmanager".get;
 local connlisteners = require "net.connlisteners";
 local sha1 = require "util.hashes".sha1;
+local server = require "net.server";
 
 local host, name = module:get_host(), "SOCKS5 Bytestreams Service";
 local sessions, transfers, component, replies_cache = {}, {}, nil, {};
@@ -28,6 +29,7 @@
 local proxy_interface = config_get(host, "core", "proxy65_interface") or "*";
 local proxy_address = config_get(host, "core", "proxy65_address") or (proxy_interface ~= "*" and proxy_interface) or host;
 local proxy_acl = config_get(host, "core", "proxy65_acl");
+local max_buffer_size = 4096;
 
 local connlistener = { default_port = proxy_port, default_interface = proxy_interface, default_mode = "*a" };
 
@@ -84,8 +86,8 @@
 				transfers[sha].initiator = conn;
 				session.sha = sha;
 				module:log("debug", "initiator connected ... ");
-				throttle_sending(conn, transfers[sha].target);
-				throttle_sending(transfers[sha].target, conn);
+				server.link(conn, transfers[sha].target, max_buffer_size);
+				server.link(transfers[sha].target, conn, max_buffer_size);
 			end
 			conn:write(string.char(5, 0, 0, 3, sha:len()) .. sha .. string.char(0, 0)); -- VER, REP, RSV, ATYP, BND.ADDR (sha), BND.PORT (2 Byte)
 			conn:lock_read(true)
@@ -234,8 +236,12 @@
 			elseif xmlns == "http://jabber.org/protocol/bytestreams" then
 				origin.send(get_stream_host(origin, stanza));
 				return true;
+			else
+				origin.send(st.error_reply(stanza, "cancel", "service-unavailable"));
+				return true;
 			end
 		elseif stanza.name == "iq" and type == "set" then
+			module:log("debug", "Received activation request from %s", stanza.attr.from);
 			local reply, from, to, sid = set_activation(stanza);
 			if reply ~= nil and from ~= nil and to ~= nil and sid ~= nil then
 				local sha = sha1(sid .. from .. to, true);
@@ -246,6 +252,15 @@
 					transfers[sha].activated = true;
 					transfers[sha].target:lock_read(false);
 					transfers[sha].initiator:lock_read(false);
+				else
+					module:log("debug", "Both parties were not yet connected");
+					local message = "Neither party is connected to the proxy";
+					if transfers[sha].initiator then
+						message = "The recipient is not connected to the proxy";
+					elseif transfers[sha].target then
+						message = "The sender (you) is not connected to the proxy";
+					end
+					origin.send(st.error_reply(stanza, "cancel", "not-allowed", message));
 				end
 			else
 				module:log("error", "activation failed: sid: %s, initiator: %s, target: %s", tostring(sid), tostring(from), tostring(to));
@@ -262,25 +277,3 @@
 
 connlisteners.start(module.host .. ':proxy65');
 component = componentmanager.register_component(host, handle_to_domain);
-local sender_lock_threshold = 4096;
-function throttle_sending(sender, receiver)
-	sender:pattern(sender_lock_threshold);
-	local sender_locked;
-	local _sendbuffer = receiver.sendbuffer;
-	function receiver.sendbuffer()
-		_sendbuffer();
-		if sender_locked and receiver.bufferlen() < sender_lock_threshold then
-			sender:lock_read(false); -- Unlock now
-			sender_locked = nil;
-		end
-	end
-	
-	local _readbuffer = sender.readbuffer;
-	function sender.readbuffer()
-		_readbuffer();
-		if not sender_locked and receiver.bufferlen() >= sender_lock_threshold then
-			sender_locked = true;
-			sender:lock_read(true);
-		end
-	end
-end
--- a/prosody	Wed Apr 14 12:59:46 2010 +0100
+++ b/prosody	Wed May 05 19:01:14 2010 +0100
@@ -29,6 +29,10 @@
 	end
 end
 
+-- Global 'prosody' object
+prosody = { events = require "util.events".new(); };
+local prosody = prosody;
+
 -- Load the config-parsing module
 config = require "core.configmanager"
 
@@ -120,15 +124,34 @@
 	end
 end
 
+function set_function_metatable()
+	local mt = {};
+	function mt.__index(f, upvalue)
+		local i, name, value = 0;
+		repeat
+			i = i + 1;
+			name, value = debug.getupvalue(f, i);
+		until name == upvalue or name == nil;
+		return value;
+	end
+	function mt.__newindex(f, upvalue, value)
+		local i, name = 0;
+		repeat
+			i = i + 1;
+			name = debug.getupvalue(f, i);
+		until name == upvalue or name == nil;
+		if name then
+			debug.setupvalue(f, i, value);
+		end
+	end
+	debug.setmetatable(function() end, mt);
+end
+
 function init_global_state()
 	bare_sessions = {};
 	full_sessions = {};
 	hosts = {};
 
-	-- Global 'prosody' object
-	prosody = {};
-	local prosody = prosody;
-	
 	prosody.bare_sessions = bare_sessions;
 	prosody.full_sessions = full_sessions;
 	prosody.hosts = hosts;
@@ -138,8 +161,6 @@
 	
 	prosody.arg = _G.arg;
 
-	prosody.events = require "util.events".new();
-	
 	prosody.platform = "unknown";
 	if os.getenv("WINDIR") then
 		prosody.platform = "windows";
@@ -170,7 +191,6 @@
 	-- Function to reopen logfiles
 	function prosody.reopen_logfiles()
 		log("info", "Re-opening log files");
-		eventmanager.fire_event("reopen-log-files"); -- Handled by appropriate log sinks
 		prosody.events.fire_event("reopen-log-files");
 	end
 
@@ -263,7 +283,6 @@
 	require "util.import"
 	require "core.xmlhandlers"
 	require "core.rostermanager"
-	require "core.eventmanager"
 	require "core.hostmanager"
 	require "core.modulemanager"
 	require "core.usermanager"
@@ -307,7 +326,6 @@
 function prepare_to_start()
 	log("info", "Prosody is using the %s backend for connection handling", server.get_backend());
 	-- Signal to modules that we are ready to start
-	eventmanager.fire_event("server-starting");
 	prosody.events.fire_event("server-starting");
 
 	-- start listening on sockets
@@ -415,6 +433,7 @@
 init_logging();
 check_dependencies();
 sandbox_require();
+set_function_metatable();
 load_libraries();
 init_global_state();
 read_version();
@@ -424,14 +443,12 @@
 init_global_protection();
 prepare_to_start();
 
-eventmanager.fire_event("server-started");
 prosody.events.fire_event("server-started");
 
 loop();
 
 log("info", "Shutting down...");
 cleanup();
-eventmanager.fire_event("server-stopped");
 prosody.events.fire_event("server-stopped");
 log("info", "Shutdown complete");
 
--- a/prosody.cfg.lua.dist	Wed Apr 14 12:59:46 2010 +0100
+++ b/prosody.cfg.lua.dist	Wed May 05 19:01:14 2010 +0100
@@ -1,138 +1,127 @@
 -- Prosody Example Configuration File
---
--- If it wasn't already obvious, -- starts a comment, and all
--- text after it on a line is ignored by Prosody.
---
--- The config is split into sections, a global section, and one
--- for each defined host that we serve. You can add as many host
--- sections as you like.
---
--- Lists are written { "like", "this", "one" }
--- Lists can also be of { 1, 2, 3 } numbers, and other things.
--- Either commas, or semi-colons; may be used
--- as seperators.
+-- 
+-- Information on configuring Prosody can be found on our
+-- website at http://prosody.im/doc/configure
+-- 
+-- Tip: You can check that the syntax of this file is correct
+-- when you have finished by running: luac -p prosody.cfg.lua
+-- If there are any errors, it will let you know what and where
+-- they are, otherwise it will keep quiet.
 --
--- A table is a list of values, except each value has a name. An
--- example table would be:
---
--- ssl = { key = "keyfile.key", certificate = "certificate.cert" }
---
--- Whitespace (that is tabs, spaces, line breaks) is mostly
--- insignificant, so
--- can
--- be placed anywhere that      you deem fitting.
---
--- Tip: You can check that the syntax of this file is correct when you
--- have finished by running: luac -p prosody.cfg.lua
--- If there are any errors, it will let you know what and where they
--- are, otherwise it will keep quiet.
---
--- The only thing left to do is rename this file to remove the .dist
--- ending, and fill in the
+-- The only thing left to do is rename this file to remove the .dist ending, and fill in the
 -- blanks. Good luck, and happy Jabbering!
 
--- Server-wide settings go in this section
-Host "*"
+
+---------- Server-wide settings ----------
+-- Settings in this section apply to the whole server and are the default settings
+-- for any virtual hosts
 
-	-- This is a (by default, empty) list of accounts that are admins for the
-	-- server. Note that you must create the accounts separately (see
-	-- http://prosody.im/doc/creating_accounts)
-	-- Example: admins = { "user1@example.com", "user2@example.net" }
-	admins = { }
+-- This is a (by default, empty) list of accounts that are admins
+-- for the server. Note that you must create the accounts separately
+-- (see http://prosody.im/doc/creating_accounts for info)
+-- Example: admins = { "user1@example.com", "user2@example.net" }
+admins = { }
+
+-- Enable use of libevent for better performance under high load
+-- For more information see: http://prosody.im/doc/libevent
+--use_libevent = true;
+
+-- This is the list of modules Prosody will load on startup.
+-- It looks for mod_modulename.lua in the plugins folder, so make sure that exists too.
+-- Documentation on modules can be found at: http://prosody.im/doc/modules
+modules_enabled = {
 
-	-- Enable use of libevent for better performance under high load
-	-- For more information see: http://prosody.im/doc/libevent
-	--use_libevent = true;
+	-- Generally required
+		"roster"; -- Allow users to have a roster. Recommended ;)
+		"saslauth"; -- Authentication for clients and servers. Recommended if you want to log in.
+		"tls"; -- Add support for secure TLS on c2s/s2s connections
+		"dialback"; -- s2s dialback support
+		"disco"; -- Service discovery
 
-	-- This is the list of modules Prosody will load on startup. It looks for
-	-- mod_modulename.lua in the plugins folder, so make sure that exists too.
-	-- Documentation on modules can be found at: http://prosody.im/doc/modules
-	modules_enabled = {
-		-- Generally required
-		"roster";   -- Allow users to have a roster. Recommended ;)
-		"saslauth"; -- Authentication for clients and servers. Recommended if
-		            -- you want to log in.
-		"dialback"; -- s2s dialback support
-		"disco";    -- Service discovery
-		"posix";    -- POSIX functionality, daemonizes, enables syslog, etc.
+	-- Not essential, but recommended
+		"private"; -- Private XML storage (for room bookmarks, etc.)
+		"vcard"; -- Allow users to set vCards
+		--"privacy"; -- Support privacy lists
+		--"compression"; -- Stream compression
+
+	-- Nice to have
+		"legacyauth"; -- Legacy authentication. Only used by some old clients and bots.
+		"version"; -- Replies to server version requests
+		"uptime"; -- Report how long server has been running
+		"time"; -- Let others know the time here on this server
+		"ping"; -- Replies to XMPP pings with pongs
+		"pep"; -- Enables users to publish their mood, activity, playing music and more
+		"register"; -- Allow users to register on this server using a client and change passwords
 
-		-- Not essential, but recommended
-		"private";       -- Private XML storage (for room bookmarks, etc.)
-		"vcard";         -- Allow users to set vCards
-		"tls";           -- Support for secure TLS on c2s/s2s connections
-		--"privacy";     -- Support privacy lists
-		--"compression"; -- Stream compression for client-to-server streams
+	-- Other specific functionality
+		--"posix"; -- POSIX functionality, sends server to background, enables syslog, etc.
+		--"console"; -- Opens admin telnet interface on localhost port 5582
+		--"bosh"; -- Enable BOSH clients, aka "Jabber over HTTP"
+		--"httpserver"; -- Serve static files from a directory over HTTP
+		--"groups"; -- Shared roster support
+		--"announce"; -- Send announcement to all online users
+		--"welcome"; -- Welcome users who register accounts
+		--"watchregistrations"; -- Alert admins of registrations
+};
+
+-- These modules are auto-loaded, should you
+-- for (for some mad reason) want to disable
+-- them then uncomment them below
+modules_disabled = {
+	-- "presence";
+	-- "message";
+	-- "iq";
+};
 
-		-- Nice to have
-		"legacyauth"; -- Legacy authentication. Only used by some old
-		              -- clients and bots.
-		"version";    -- Replies to server version requests
-		"uptime";     -- Report how long server has been running
-		"time";       -- Let others know the time here on this server
-		"ping";       -- Replies to XMPP pings with pongs
-		"pep";        -- Enables users to publish their mood, activity, playing
-		              -- music and more
-		"register";   -- Allow users to register on this server using a client
-		              -- and change passwords
+-- Disable account creation by default, for security
+-- For more information see http://prosody.im/doc/creating_accounts
+allow_registration = false;
+	
+-- These are the SSL/TLS-related settings. If you don't want
+-- to use SSL/TLS, you may comment or remove this
+ssl = {
+	key = "certs/localhost.key";
+	certificate = "certs/localhost.cert";
+}
+
+-- Require encryption on client/server connections?
+--c2s_require_encryption = false
+--s2s_require_encryption = false
 
-		-- Other specific functionality
-		--"console";            -- telnet to port 5582
-		                        -- (needs console_enabled = true)
-		--"bosh";               -- Enable BOSH clients, aka "Jabber over HTTP"
-		--"httpserver";         -- Serve static files from a directory over
-		                        -- HTTP
-		--"groups";             -- Shared roster support
-		--"announce";           -- Send announcement to all online users
-		--"welcome";            -- Welcome users who register accounts
-		--"watchregistrations"; -- Alert admins of registrations
+-- Logging configuration
+-- For advanced logging see http://prosody.im/doc/logging
+log = "prosody.log";
+debug = false; -- Log debug messages?
+
+----------- Virtual hosts -----------
+-- You need to add a VirtualHost entry for each domain you wish Prosody to serve.
+-- Settings under each VirtualHost entry apply *only* to that host.
+
+VirtualHost "localhost"
+
+VirtualHost "example.com"
+	enabled = false -- Remove this line to enable this host
+
+	-- Assign this host a certificate for TLS, otherwise it would use the one
+	-- set in the global section (if any).
+	-- Note that old-style SSL on port 5223 only supports one certificate, and will always
+	-- use the global one.
+	ssl = { 
+		key = "certs/example.com.key";
+		certificate = "certs/example.com.crt";
 	}
 
-	-- These modules are auto-loaded, should you for (for some mad
-	-- reason) want to disable them then uncomment them below.
-	modules_disabled = {
-		--"presence";
-		--"message";
-		--"iq";
-	}
-
-	-- Disable account creation by default, for security
-	-- For more information see http://prosody.im/doc/creating_accounts
-	allow_registration = false;
-
-	--These are the SSL/TLS-related settings.
-	--ssl = {
-	--    key = "certs/localhost.key";
-	--    certificate = "certs/localhost.cert";
-	--}
-
-	-- Require encryption on client/server connections?
-	--c2s_require_encryption = false
-	--s2s_require_encryption = false
+------ Components ------
+-- You can specify components to add hosts that provide special services,
+-- like multi-user conferences, and transports.
+-- For more information on components, see http://prosody.im/doc/components
 
-	-- Logging configuration
-	-- For advanced logging see http://prosody.im/doc/logging
-	log = "prosody.log";
-	debug = false; -- Log debug messages?
-
--- This allows clients to connect to localhost. No harm in it.
-Host "localhost"
-
--- Section for example.com
--- (replace example.com with your domain name)
-Host "example.com"
-	enabled = false -- This will disable the host, preserving the config, but
-	                -- denying connections (remove to enable!)
-
-	-- Assign this host a certificate for TLS, otherwise it would use the one
-	-- set in the global section (if any). Note that old-style SSL on port 5223
-	-- only supports one certificate, and will always use the global one.
-	--ssl = {
-	--    key = "certs/example.com.key";
-	--    certificate = "certs/example.com.crt";
-	--}
-
--- Set up a MUC (multi-user chat) room server on conference.example.com:
+---Set up a MUC (multi-user chat) room server on conference.example.com:
 --Component "conference.example.com" "muc"
 
 -- Set up a SOCKS5 bytestream proxy for server-proxied file transfers:
 --Component "proxy.example.com" "proxy65"
+
+---Set up an external component (default component port is 5347)
+--Component "gateway.example.com"
+--	component_secret = "password"
--- a/util/sasl.lua	Wed Apr 14 12:59:46 2010 +0100
+++ b/util/sasl.lua	Wed May 05 19:01:14 2010 +0100
@@ -41,27 +41,6 @@
 state = false : disabled
 state = true : enabled
 state = nil : non-existant
-
-plain:
-	function(username, realm)
-		return password, state;
-	end
-
-plain-test:
-	function(username, realm, password)
-		return true or false, state;
-	end
-
-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:
-	function(username, domain, realm, encoding, digesthash)
-		return true or false, state;
-	end
 ]]
 
 local method = {};
--- a/util/sasl/anonymous.lua	Wed Apr 14 12:59:46 2010 +0100
+++ b/util/sasl/anonymous.lua	Wed May 05 19:01:14 2010 +0100
@@ -1,5 +1,5 @@
 -- sasl.lua v0.4
--- Copyright (C) 2008-2009 Tobias Markmann
+-- Copyright (C) 2008-2010 Tobias Markmann
 --
 --    All rights reserved.
 --
@@ -20,6 +20,16 @@
 
 --=========================
 --SASL ANONYMOUS according to RFC 4505
+
+--[[
+Supported Authentication Backends
+
+anonymous:
+	function(username, realm)
+		return true; --for normal usage just return true; if you don't like the supplied username you can return false.
+	end
+]]
+
 local function anonymous(self, message)
 	local username;
 	repeat
--- a/util/sasl/digest-md5.lua	Wed Apr 14 12:59:46 2010 +0100
+++ b/util/sasl/digest-md5.lua	Wed May 05 19:01:14 2010 +0100
@@ -1,5 +1,5 @@
 -- sasl.lua v0.4
--- Copyright (C) 2008-2009 Tobias Markmann
+-- Copyright (C) 2008-2010 Tobias Markmann
 --
 --    All rights reserved.
 --
@@ -29,6 +29,21 @@
 --=========================
 --SASL DIGEST-MD5 according to RFC 2831
 
+--[[
+Supported Authentication Backends
+
+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:
+	function(username, domain, realm, encoding, digesthash)
+		return true or false, state;
+	end
+]]
+
 local function digest(self, message)
 	--TODO complete support for authzid
 
--- a/util/sasl/plain.lua	Wed Apr 14 12:59:46 2010 +0100
+++ b/util/sasl/plain.lua	Wed May 05 19:01:14 2010 +0100
@@ -1,5 +1,5 @@
 -- sasl.lua v0.4
--- Copyright (C) 2008-2009 Tobias Markmann
+-- Copyright (C) 2008-2010 Tobias Markmann
 --
 --    All rights reserved.
 --
@@ -19,6 +19,26 @@
 
 -- ================================
 -- SASL PLAIN according to RFC 4616
+
+--[[
+Supported Authentication Backends
+
+plain:
+	function(username, realm)
+		return password, state;
+	end
+
+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)
 	if not message then
 		return "failure", "malformed-request";
@@ -46,6 +66,10 @@
 		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
@@ -61,7 +85,7 @@
 end
 
 function init(registerMechanism)
-	registerMechanism("PLAIN", {"plain", "plain_test"}, plain);
+	registerMechanism("PLAIN", {"plain", "plain_test", "plain_hashed"}, plain);
 end
 
 return _M;
--- a/util/sasl/scram.lua	Wed Apr 14 12:59:46 2010 +0100
+++ b/util/sasl/scram.lua	Wed May 05 19:01:14 2010 +0100
@@ -1,5 +1,5 @@
 -- sasl.lua v0.4
--- Copyright (C) 2008-2009 Tobias Markmann
+-- Copyright (C) 2008-2010 Tobias Markmann
 --
 --    All rights reserved.
 --
@@ -28,6 +28,16 @@
 
 --=========================
 --SASL SCRAM-SHA-1 according to draft-ietf-sasl-scram-10
+
+--[[
+Supported Authentication Backends
+
+scram-{MECH}:
+	function(username, realm)
+		return salted_password, iteration_count, salt, state;
+	end
+]]
+
 local default_i = 4096
 
 local function bp( b )
@@ -82,77 +92,95 @@
 	return username;
 end
 
-local function scram_sha_1(self, message)
-	if not self.state then self["state"] = {} 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 not self.state.name then
-		-- we are processing client_first_message
-		local client_first_message = message;
-		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=([^,]+)")
+		if not self.state.name then
+			-- we are processing client_first_message
+			local client_first_message = message;
+			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=([^,]+)")
 		
-		if not self.state.name or not self.state.clientnonce then
-			return "failure", "malformed-request";
-		end
+			if not self.state.name or not self.state.clientnonce then
+				return "failure", "malformed-request";
+			end
 		
-		self.state.name = validate_username(self.state.name);
-		if not self.state.name then
-			log("debug", "Username violates either SASLprep or contains forbidden character sequences.")
-			return "failure", "malformed-request", "Invalid username.";
-		end
-		
-		self.state["servernonce"] = generate_uuid();
-		self.state["salt"] = generate_uuid();
+			self.state.name = validate_username(self.state.name);
+			if not self.state.name then
+				log("debug", "Username violates either SASLprep or contains forbidden character sequences.")
+				return "failure", "malformed-request", "Invalid username.";
+			end
 		
-		local server_first_message = "r="..self.state.clientnonce..self.state.servernonce..",s="..base64.encode(self.state.salt)..",i="..default_i;
-		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["servernonce"] = generate_uuid();
+			
+			-- retreive credentials
+			if self.profile.plain then
+				password, state = self.profile.plain(self.state.name, self.realm)
+				if state == nil then return "failure", "not-authorized"
+				elseif state == false then return "failure", "account-disabled" end
+				
+				password = saslprep(password);
+				if not password then
+					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
+				salted_password, iteration_count, salt, state = self.profile["scram-"..hash_name](self.state.name, self.realm);
+				if state == nil then return "failure", "not-authorized"
+				elseif state == false then return "failure", "account-disabled" end
+				
+				self.state.salted_password = salted_password;
+				self.state.iteration_count = iteration_count;
+				self.state.salt = salt
+			end
 		
-		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=");
-		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
+			local server_first_message = "r="..self.state.clientnonce..self.state.servernonce..",s="..base64.encode(self.state.salt)..",i="..self.state.iteration_count;
+			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=");
+			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
 		
-		local password, state;
-		if self.profile.plain then
-			password, state = self.profile.plain(self.state.name, self.realm)
-			if state == nil then return "failure", "not-authorized"
-			elseif state == false then return "failure", "account-disabled" end
-			password = saslprep(password);
-			if not password then
-				log("debug", "Password violates SASLprep.");
-				return "failure", "not-authorized", "Invalid password."
+			local SaltedPassword = self.state.salted_password;
+			local ClientKey = HMAC_f(SaltedPassword, "Client Key")
+			local ServerKey = HMAC_f(SaltedPassword, "Server Key")
+			local StoredKey = H_f(ClientKey)
+			local AuthMessage = "n=" .. s_match(self.state.client_first_message,"n=(.+)") .. "," .. self.state.server_first_message .. "," .. s_match(client_final_message, "(.+),p=.+")
+			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;
+				return "success", server_final_message;
+			else
+				return "failure", "not-authorized", "The response provided by the client doesn't match the one we calculated.";
 			end
 		end
-		
-		local SaltedPassword = Hi(hmac_sha1, password, self.state.salt, default_i)
-		local ClientKey = hmac_sha1(SaltedPassword, "Client Key")
-		local ServerKey = hmac_sha1(SaltedPassword, "Server Key")
-		local StoredKey = sha1(ClientKey)
-		local AuthMessage = "n=" .. s_match(self.state.client_first_message,"n=(.+)") .. "," .. self.state.server_first_message .. "," .. s_match(client_final_message, "(.+),p=.+")
-		local ClientSignature = hmac_sha1(StoredKey, AuthMessage)
-		local ClientProof     = binaryXOR(ClientKey, ClientSignature)
-		local ServerSignature = hmac_sha1(ServerKey, AuthMessage)
-		
-		if base64.encode(ClientProof) == self.state.proof then
-			local server_final_message = "v="..base64.encode(ServerSignature);
-			self["username"] = self.state.name;
-			return "success", server_final_message;
-		else
-			return "failure", "not-authorized", "The response provided by the client doesn't match the one we calculated.";
-		end
 	end
+	return scram_hash;
 end
 
 function init(registerMechanism)
-	registerMechanism("SCRAM-SHA-1", {"plain"}, scram_sha_1);
+	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));
+	end
+	
+	registerSCRAMMechanism("SHA-1", sha1, hmac_sha1);
 end
 
 return _M;
\ No newline at end of file