Changeset

862:b3b80ccddb0c

Merged with main tip.
author Tobias Markmann <tm@ayena.de>
date Tue, 03 Mar 2009 17:48:04 +0100
parents 861:2a5373897128 (current diff) 856:946d0f91bd38 (diff)
children 863:fee8a700e92e
files
diffstat 17 files changed, 466 insertions(+), 192 deletions(-) [+]
line wrap: on
line diff
--- a/core/componentmanager.lua	Sun Feb 22 20:57:57 2009 +0100
+++ b/core/componentmanager.lua	Tue Mar 03 17:48:04 2009 +0100
@@ -7,20 +7,20 @@
 --
 
 
-
-
+
+
 local log = require "util.logger".init("componentmanager");
 local configmanager = require "core.configmanager";
 local eventmanager = require "core.eventmanager";
-local modulemanager = require "core.modulemanager";
-local jid_split = require "util.jid".split;
+local modulemanager = require "core.modulemanager";
+local jid_split = require "util.jid".split;
 local hosts = hosts;
 
-local pairs, type, tostring = pairs, type, tostring;
-
-local components = {};
-
-module "componentmanager"
+local pairs, type, tostring = pairs, type, tostring;
+
+local components = {};
+
+module "componentmanager"
 
 function load_enabled_components(config)
 	local defined_hosts = config or configmanager.getconfig();
@@ -39,34 +39,40 @@
 end
 
 eventmanager.add_event_hook("server-starting", load_enabled_components);
-
-function handle_stanza(origin, stanza)
-	local node, host = jid_split(stanza.attr.to);
+
+function handle_stanza(origin, stanza)
+	local node, host = jid_split(stanza.attr.to);
 	local component = nil;
 	if not component then component = components[stanza.attr.to]; end -- hack to allow hooking node@server/resource and server/resource
-	if not component then component = components[node.."@"..host]; end -- hack to allow hooking node@server
+	if not component then component = components[node.."@"..host]; end -- hack to allow hooking node@server
 	if not component then component = components[host]; end
-	if component then
-		log("debug", "stanza being handled by component: "..host);
-		component(origin, stanza, hosts[host]);
-	else
-		log("error", "Component manager recieved a stanza for a non-existing component: " .. stanza.attr.to);
-	end
-end
-
-function register_component(host, component)
-	if not hosts[host] or (hosts[host].type == 'component' and not hosts[host].connected) then
-		-- TODO check for host well-formedness
-		components[host] = component;
-		hosts[host] = { type = "component", host = host, connected = true, s2sout = {} };
+	if component then
+		log("debug", "stanza being handled by component: "..host);
+		component(origin, stanza, hosts[host]);
+	else
+		log("error", "Component manager recieved a stanza for a non-existing component: " .. stanza.attr.to);
+	end
+end
+
+function create_component(host, component)
+		-- TODO check for host well-formedness
+		session = session or { type = "component", host = host, connected = true, s2sout = {}, send = component };
+		return session;
+end
+
+function register_component(host, component, session)
+	if not hosts[host] or (hosts[host].type == 'component' and not hosts[host].connected) then
+		components[host] = component;
+		hosts[host] = session or create_component(host, component);
+		
 		-- FIXME only load for a.b.c if b.c has dialback, and/or check in config
-		modulemanager.load(host, "dialback");
-		log("debug", "component added: "..host);
-		return hosts[host];
-	else
-		log("error", "Attempt to set component for existing host: "..host);
-	end
-end
+		modulemanager.load(host, "dialback");
+		log("debug", "component added: "..host);
+		return session or hosts[host];
+	else
+		log("error", "Attempt to set component for existing host: "..host);
+	end
+end
 
 function deregister_component(host)
 	if components[host] then
@@ -79,5 +85,5 @@
 		log("error", "Attempt to remove component for non-existing host: "..host);
 	end
 end
-
-return _M;
+
+return _M;
--- a/core/presencemanager.lua	Sun Feb 22 20:57:57 2009 +0100
+++ b/core/presencemanager.lua	Tue Mar 03 17:48:04 2009 +0100
@@ -95,13 +95,16 @@
 		end
 		origin.priority = 0;
 		if stanza.attr.type == "unavailable" then
-			origin.presence = nil;
-			if origin.directed then
-				for jid in pairs(origin.directed) do
-					stanza.attr.to = jid;
-					core_route_stanza(origin, stanza);
-				end
-				origin.directed = nil;
+			origin.presence = nil;
+			if origin.directed then
+				local old_from = stanza.attr.from;
+				stanza.attr.from = origin.full_jid;
+				for jid in pairs(origin.directed) do
+					stanza.attr.to = jid;
+					core_route_stanza(origin, stanza);
+				end
+				stanza.attr.from = old_from;
+				origin.directed = nil;
 			end
 		else
 			origin.presence = stanza;
--- a/core/s2smanager.lua	Sun Feb 22 20:57:57 2009 +0100
+++ b/core/s2smanager.lua	Tue Mar 03 17:48:04 2009 +0100
@@ -206,22 +206,19 @@
 	session.version = 0; --tonumber(attr.version) or 0;
 	
 	if session.version >= 1.0 and not (attr.to and attr.from) then
-		--print("to: "..tostring(attr.to).." from: "..tostring(attr.from));
 		log("warn", (session.to_host or "(unknown)").." failed to specify 'to' or 'from' hostname as per RFC");
 	end
 	
 	if session.direction == "incoming" then
 		-- Send a reply stream header
-		
-		--for k,v in pairs(attr) do print("", tostring(k), ":::", tostring(v)); end
-		
 		session.to_host = attr.to;
 		session.from_host = attr.from;
 	
 		session.streamid = uuid_gen();
 		(session.log or log)("debug", "incoming s2s received <stream:stream>");
 		send("<?xml version='1.0'?>");
-		send(stanza("stream:stream", { xmlns='jabber:server', ["xmlns:db"]='jabber:server:dialback', ["xmlns:stream"]='http://etherx.jabber.org/streams', id=session.streamid, from=session.to_host }):top_tag());
+		send(stanza("stream:stream", { xmlns='jabber:server', ["xmlns:db"]='jabber:server:dialback', 
+				["xmlns:stream"]='http://etherx.jabber.org/streams', id=session.streamid, from=session.to_host }):top_tag());
 		if session.to_host and not hosts[session.to_host] then
 			-- Attempting to connect to a host we don't serve
 			session:close({ condition = "host-unknown"; text = "This host does not serve "..session.to_host });
--- a/core/stanza_router.lua	Sun Feb 22 20:57:57 2009 +0100
+++ b/core/stanza_router.lua	Tue Mar 03 17:48:04 2009 +0100
@@ -217,6 +217,9 @@
 								session.send(stanza);
 							end
 						end
+					elseif resource and stanza.attr.type == 'groupchat' then
+						-- Groupchat message sent to offline resource
+						origin.send(st.error_reply(stanza, "cancel", "service-unavailable"));
 					else
 						local priority = 0;
 						local recipients = {};
@@ -263,10 +266,14 @@
 					if stanza.attr.type == "chat" or stanza.attr.type == "normal" or not stanza.attr.type then
 						offlinemanager.store(node, host, stanza);
 						-- FIXME don't store messages with only chat state notifications
+					elseif stanza.attr.type == "groupchat" then
+						local reply = st.error_reply(stanza, "cancel", "service-unavailable");
+						reply.attr.from = to;
+						origin.send(reply);
 					end
 					-- TODO allow configuration of offline storage
 					-- TODO send error if not storing offline
-				elseif stanza.name == "iq" then
+				elseif stanza.name == "iq" and (stanza.attr.type == "get" or stanza.attr.type == "set") then
 					origin.send(st.error_reply(stanza, "cancel", "service-unavailable"));
 				end
 			else -- user does not exist
@@ -277,7 +284,7 @@
 						origin.send(st.presence({from = to_bare, to = from_bare, type = "unsubscribed"}));
 					end
 					-- else ignore
-				else
+				elseif stanza.attr.type ~= "error" and (stanza.name ~= "iq" or stanza.attr.type ~= "result") then
 					origin.send(st.error_reply(stanza, "cancel", "service-unavailable"));
 				end
 			end
--- a/core/xmlhandlers.lua	Sun Feb 22 20:57:57 2009 +0100
+++ b/core/xmlhandlers.lua	Tue Mar 03 17:48:04 2009 +0100
@@ -121,17 +121,19 @@
 					cb_error(session, "parse-error", "unexpected-element-close", name);
 				end
 			end
-			if stanza and #chardata > 0 then
-				-- We have some character data in the buffer
-				stanza:text(t_concat(chardata));
-				chardata = {};
-			end
-			-- Complete stanza
-			if #stanza.last_add == 0 then
-				cb_handlestanza(session, stanza);
-				stanza = nil;
-			else
-				stanza:up();
+			if stanza then
+				if #chardata > 0 then
+					-- We have some character data in the buffer
+					stanza:text(t_concat(chardata));
+					chardata = {};
+				end
+				-- Complete stanza
+				if #stanza.last_add == 0 then
+					cb_handlestanza(session, stanza);
+					stanza = nil;
+				else
+					stanza:up();
+				end
 			end
 		end
 	return xml_handlers;
--- a/net/server.lua	Sun Feb 22 20:57:57 2009 +0100
+++ b/net/server.lua	Tue Mar 03 17:48:04 2009 +0100
@@ -369,7 +369,9 @@
         end
         bufferqueuelen = bufferqueuelen + 1
         bufferqueue[ bufferqueuelen ] = data
-        _writetimes[ handler ] = _writetimes[ handler ] or _currenttime
+        if handler then
+        	_writetimes[ handler ] = _writetimes[ handler ] or _currenttime
+        end
         return true
     end
     handler.write = write
@@ -436,7 +438,7 @@
             --out_put( "server.lua: read data '", buffer, "', error: ", err )
             return dispatch( handler, buffer, err )
         else    -- connections was closed or fatal error
-            out_put( "server.lua: client ", ip, ":", clientport, " error: ", err )
+            out_put( "server.lua: client ", ip, ":", tostring(clientport), " error: ", tostring(err) )
             fatalerror = true
             disconnect( handler, err )
 	    _ = handler and handler.close( )
@@ -470,7 +472,7 @@
             out_put( "server.lua: client ", ip, ":", clientport, " error: ", err )
             fatalerror = true
             disconnect( handler, err )
-            handler.close( )
+            _ = handler and handler.close( )
             return false
         end
     end
@@ -478,16 +480,19 @@
     if sslctx then    -- ssl?
         ssl = true
         local wrote
+        local read
         local handshake = coroutine_wrap( function( client )    -- create handshake coroutine
                 local err
                 for i = 1, 10 do    -- 10 handshake attemps
+                    _sendlistlen = ( wrote and removesocket( _sendlist, socket, _sendlistlen ) ) or _sendlistlen
+                    _readlistlen = ( read and removesocket( _readlist, socket, _readlistlen ) ) or _readlistlen
+                    read, wrote = nil, nil
                     _, err = client:dohandshake( )
                     if not err then
-                        --out_put( "server.lua: ssl handshake done" )
-                        _sendlistlen = ( wrote and removesocket( _sendlist, socket, _sendlistlen ) ) or _sendlistlen
+                        out_put( "server.lua: ssl handshake done" )
                         handler.readbuffer = _readbuffer    -- when handshake is done, replace the handshake function with regular functions
                         handler.sendbuffer = _sendbuffer
-                        --return dispatch( handler )
+                        -- return dispatch( handler )
                         return true
                     else
                         out_put( "server.lua: error during ssl handshake: ", err )
@@ -495,12 +500,18 @@
                             _sendlistlen = _sendlistlen + 1
                             _sendlist[ _sendlistlen ] = client
                             wrote = true
+                        elseif err == "wantread" and not read then
+                                _readlistlen = _readlistlen + 1
+                                _readlist [ _readlistlen ] = client
+                                read = true
+                        else
+                        	break;
                         end
                         --coroutine_yield( handler, nil, err )    -- handshake not finished
                         coroutine_yield( )
                     end
                 end
-                disconnect( handler, "max handshake attemps exceeded" )
+                disconnect( handler, "ssl handshake failed" )
                 handler.close( true )    -- forced disconnect
                 return false    -- handshake failed
             end
@@ -556,8 +567,8 @@
                 handler.starttls = nil
                 needtls = nil
 
-                handler.receivedata = handler.handshake
-                handler.dispatchdata = handler.handshake
+                handler.readbuffer = handshake
+                handler.sendbuffer = handshake
                 handshake( socket )    -- do handshake
             end
             handler.readbuffer = _readbuffer
--- a/net/xmppclient_listener.lua	Sun Feb 22 20:57:57 2009 +0100
+++ b/net/xmppclient_listener.lua	Tue Mar 03 17:48:04 2009 +0100
@@ -29,9 +29,10 @@
 
 function stream_callbacks.error(session, error, data)
 	if error == "no-stream" then
+		session.log("debug", "Invalid opening stream header");
 		session:close("invalid-namespace");
-	else
-		session.log("debug", "Client XML parse error: %s", tostring(error));
+	elseif session.close then
+		(session.log or log)("debug", "Client XML parse error: %s", tostring(error));
 		session:close("xml-not-well-formed");
 	end
 end
--- a/plugins/mod_bosh.lua	Sun Feb 22 20:57:57 2009 +0100
+++ b/plugins/mod_bosh.lua	Tue Mar 03 17:48:04 2009 +0100
@@ -32,6 +32,19 @@
 local waiting_requests = {};
 function on_destroy_request(request)
 	waiting_requests[request] = nil;
+	local session = request.session;
+	if session then
+		local requests = session.requests;
+		for i,r in pairs(requests) do
+			if r == request then requests[i] = nil; break; end
+		end
+		
+		-- If this session now has no requests open, mark it as inactive
+		if #requests == 0 and session.bosh_max_inactive and not inactive_sessions[session] then
+			inactive_sessions[session] = os_time();
+			(session.log or log)("debug", "BOSH session marked as inactive at %d", inactive_sessions[session]);
+		end
+	end
 end
 
 function handle_request(method, body, request)
@@ -151,10 +164,6 @@
 				end
 			elseif s ~= "" then
 				log("debug", "Saved to send buffer because there are %d open requests", #r);
-				if session.bosh_max_inactive and not inactive_sessions[session] then
-					inactive_sessions[session] = os_time();
-					(session.log or log)("debug", "BOSH session marked as inactive at %d", inactive_sessions[session]);
-				end
 				-- Hmm, no requests are open :(
 				t_insert(session.send_buffer, tostring(s));
 				log("debug", "There are now %d things in the send_buffer", #session.send_buffer);
@@ -243,7 +252,6 @@
 				(session.log or log)("debug", "BOSH client inactive too long, destroying session at %d", now);
 				sessions[session.sid]  = nil;
 				inactive_sessions[session] = nil;
-				session.bosh_max_inactive = nil; -- Stop us marking this session as active during destroy
 				sm_destroy_session(session, "BOSH client silent for over "..session.bosh_max_inactive.." seconds");
 			end
 		else
--- a/plugins/mod_muc.lua	Sun Feb 22 20:57:57 2009 +0100
+++ b/plugins/mod_muc.lua	Tue Mar 03 17:48:04 2009 +0100
@@ -1,3 +1,10 @@
+-- Prosody IM v0.3
+-- Copyright (C) 2008-2009 Matthew Wild
+-- Copyright (C) 2008-2009 Waqas Hussain
+-- 
+-- This project is MIT/X11 licensed. Please see the
+-- COPYING file in the source package for more information.
+--
 
 
 local register_component = require "core.componentmanager".register_component;
@@ -62,7 +69,7 @@
 end
 local presence_filters = {["http://jabber.org/protocol/muc"]=true;["http://jabber.org/protocol/muc#user"]=true};
 function get_filtered_presence(stanza)
-	return filter_xmlns_from_stanza(st.deserialize(st.preserialize(stanza)), presence_filters);
+	return filter_xmlns_from_stanza(st.clone(stanza), presence_filters);
 end
 function getUsingPath(stanza, path, getText)
 	local tag = stanza;
@@ -116,60 +123,12 @@
 	if subject == "" then subject = nil; end
 	rooms_info:set(room, 'subject', subject);
 	save_room();
-	local msg = st.message({type='groupchat', from=from})
+	local msg = st.message({type='groupchat', from=current_nick})
 		:tag('subject'):text(subject):up();
 	broadcast_message_stanza(room, msg, false);
-	--broadcast_message(current_nick, room, subject or "", nil);
 	return true;
 end
 
-function broadcast_presence(type, from, room, code, newnick)
-	local data = rooms:get(room, from);
-	local stanza = st.presence({type=type, from=from})
-		:tag("x", {xmlns='http://jabber.org/protocol/muc#user'})
-		:tag("item", {affiliation=data.affiliation, role=data.role, nick = newnick}):up();
-	if code then
-		stanza:tag("status", {code=code}):up();
-	end
-	local me;
-	local r = rooms:get(room);
-	if r then
-		for occupant, o_data in pairs(r) do
-			if occupant ~= from then
-				stanza.attr.to = o_data.jid;
-				core_route_stanza(component, stanza);
-			else
-				me = o_data.jid;
-			end
-		end
-	end
-	if me then
-		stanza:tag("status", {code='110'});
-		stanza.attr.to = me;
-		core_route_stanza(component, stanza);
-	end
-end
-function broadcast_message(from, room, subject, body)
-	local stanza = st.message({type='groupchat', from=from});
-	if subject then stanza:tag('subject'):text(subject):up(); end
-	if body then stanza:tag('body'):text(body):up(); end
-	local r = rooms:get(room);
-	if r then
-		for occupant, o_data in pairs(r) do
-			stanza.attr.to = o_data.jid;
-			core_route_stanza(component, stanza);
-		end
-		if not subject and body then -- add to history
-			local history = rooms_info:get(room, 'history');
-			if not history then history = {}; rooms_info:set(room, 'history', history); end
-			-- stanza = st.deserialize(st.preserialize(stanza));
-			stanza:tag("delay", {xmlns = "urn:xmpp:delay", from = muc_domain, stamp = datetime.datetime()}):up(); -- XEP-0203
-			stanza:tag("x", {xmlns = "jabber:x:delay", from = muc_domain, stamp = datetime.legacy()}):up(); -- XEP-0091 (deprecated)
-			t_insert(history, st.preserialize(stanza));
-			while #history > history_length do t_remove(history, 1) end
-		end
-	end
-end
 function broadcast_message_stanza(room, stanza, historic)
 	local r = rooms:get(room);
 	if r then
@@ -182,10 +141,10 @@
 		if historic then -- add to history
 			local history = rooms_info:get(room, 'history');
 			if not history then history = {}; rooms_info:set(room, 'history', history); end
-			-- stanza = st.deserialize(st.preserialize(stanza));
+			-- stanza = st.clone(stanza);
 			stanza:tag("delay", {xmlns = "urn:xmpp:delay", from = muc_domain, stamp = datetime.datetime()}):up(); -- XEP-0203
 			stanza:tag("x", {xmlns = "jabber:x:delay", from = muc_domain, stamp = datetime.legacy()}):up(); -- XEP-0091 (deprecated)
-			t_insert(history, st.preserialize(stanza));
+			t_insert(history, st.clone(st.preserialize(stanza)));
 			while #history > history_length do t_remove(history, 1) end
 		end
 	end
@@ -226,57 +185,58 @@
 	local room = jid_bare(to);
 	local current_nick = jid_nick:get(from, room);
 	local type = stanza.attr.type;
+	log("debug", "room: %s, current_nick: %s, stanza: %s", room or "nil", current_nick or "nil", stanza:top_tag());
+	if (select(2, jid_split(from)) == muc_domain) then error("Presence from the MUC itself!!!"); end
 	if stanza.name == "presence" then
 		local pr = get_filtered_presence(stanza);
-		pr.attr.from = to;
+		pr.attr.from = current_nick;
 		if type == "error" then -- error, kick em out!
 			if current_nick then
-				local data = rooms:get(room, to);
-				data.role = 'none';
-				local pr = st.presence({type='unavailable', from=current_nick}):tag('status'):text('This participant is kicked from the room because he sent an error presence'):up()
-					:tag("x", {xmlns='http://jabber.org/protocol/muc#user'})
-					:tag("item", {affiliation=data.affiliation, role=data.role}):up();
-				broadcast_presence_stanza(room, pr);
-				--broadcast_presence('unavailable', to, room); -- TODO also add <status>This participant is kicked from the room because he sent an error presence: badformed error stanza</status>
-				rooms:remove(room, to);
-				jid_nick:remove(from, room);
+				log("debug", "kicking %s from %s", current_nick, room);
+				handle_to_occupant(origin, st.presence({type='unavailable', from=from, to=to}):tag('status'):text('This participant is kicked from the room because he sent an error presence')); -- send unavailable
 			end
 		elseif type == "unavailable" then -- unavailable
 			if current_nick then
-				local data = rooms:get(room, to);
+				log("debug", "%s leaving %s", current_nick, room);
+				local data = rooms:get(room, current_nick);
 				data.role = 'none';
 				broadcast_presence_stanza(room, pr);
-				--broadcast_presence('unavailable', to, room);
-				rooms:remove(room, to);
+				rooms:remove(room, current_nick);
 				jid_nick:remove(from, room);
 			end
 		elseif not type then -- available
 			if current_nick then
-				if current_nick == to then -- simple presence
-					broadcast_presence_stanza(room, pr);
-					-- FIXME check if something was filtered. if it was, then user may be rejoining
-				else -- change nick
-					if rooms:get(room, to) then
-						origin.send(st.error_reply(stanza, "cancel", "conflict"));
-					else
-						local data = rooms:get(room, current_nick);
-						local to_nick = select(3, jid_split(to));
-						if to_nick then
-							local p = st.presence({type='unavailable', from=current_nick});
-								--[[:tag('x', {xmlns='http://jabber.org/protocol/muc#user'})
-									:tag('item', {affiliation=data.affiliation, role=data.role, nick=to_nick}):up()
-									:tag('status', {code='303'});]]
-							broadcast_presence_stanza(room, p, '303', to_nick);
-							--broadcast_presence('unavailable', current_nick, room, '303', to_nick);
-							rooms:remove(room, current_nick);
-							rooms:set(room, to, data);
-							jid_nick:set(from, room, to);
-							broadcast_presence_stanza(room, pr);
-							--broadcast_presence(nil, to, room, nil);
+				if #pr == #stanza or current_nick ~= to then
+					if current_nick == to then -- simple presence
+						log("debug", "%s broadcasted presence", current_nick);
+						rooms:get(room, current_nick).sessions[from] = pr;
+						broadcast_presence_stanza(room, pr);
+					else -- change nick
+						if rooms:get(room, to) then
+							log("debug", "%s couldn't change nick", current_nick);
+							origin.send(st.error_reply(stanza, "cancel", "conflict"));
 						else
-							--TODO malformed-jid
+							local data = rooms:get(room, current_nick);
+							local to_nick = select(3, jid_split(to));
+							if to_nick then
+								log("debug", "%s (%s) changing nick to %s", current_nick, data.jid, to);
+								local p = st.presence({type='unavailable', from=current_nick});
+								broadcast_presence_stanza(room, p, '303', to_nick);
+								rooms:remove(room, current_nick);
+								rooms:set(room, to, data);
+								jid_nick:set(from, room, to);
+								pr.attr.from = to;
+								rooms:get(room, to).sessions[from] = pr;
+								broadcast_presence_stanza(room, pr);
+							else
+								--TODO malformed-jid
+							end
 						end
 					end
+				else -- possible rejoin
+					log("debug", "%s had connection replaced", current_nick);
+					handle_to_occupant(origin, st.presence({type='unavailable', from=from, to=to}):tag('status'):text('Replaced by new connection')); -- send unavailable
+					handle_to_occupant(origin, stanza); -- resend available
 				end
 			else -- enter room
 				local new_nick = to;
@@ -284,8 +244,10 @@
 					new_nick = nil;
 				end
 				if not new_nick then
+					log("debug", "%s couldn't join due to nick conflict: %s", from, to);
 					origin.send(st.error_reply(stanza, "cancel", "conflict"));
 				else
+					log("debug", "%s joining as %s", from, to);
 					local data;
 					if not rooms:get(room) and not rooms_info:get(room) then -- new room
 						data = {affiliation='owner', role='moderator', jid=from, sessions={[from]=get_filtered_presence(stanza)}};
@@ -298,19 +260,17 @@
 					local r = rooms:get(room);
 					if r then
 						for occupant, o_data in pairs(r) do
-							if occupant ~= from then
+							if occupant ~= to then
 								local pres = get_filtered_presence(o_data.sessions[o_data.jid]);
 								pres.attr.to, pres.attr.from = from, occupant;
-								pres
-								--local pres = st.presence({to=from, from=occupant})
-									:tag("x", {xmlns='http://jabber.org/protocol/muc#user'})
+								pres:tag("x", {xmlns='http://jabber.org/protocol/muc#user'})
 									:tag("item", {affiliation=o_data.affiliation, role=o_data.role}):up();
 								core_route_stanza(component, pres);
 							end
 						end
 					end
+					pr.attr.from = to;
 					broadcast_presence_stanza(room, pr);
-					--broadcast_presence(nil, to, room);
 					local history = rooms_info:get(room, 'history'); -- send discussion history
 					if history then
 						for _, msg in ipairs(history) do
@@ -327,24 +287,17 @@
 		elseif type ~= 'result' then -- bad type
 			origin.send(st.error_reply(stanza, "modify", "bad-request")); -- FIXME correct error?
 		end
-	elseif not current_nick then -- not in room
+	elseif not current_nick and type ~= "error" then -- not in room
 		origin.send(st.error_reply(stanza, "cancel", "not-acceptable"));
 	elseif stanza.name == "message" and type == "groupchat" then -- groupchat messages not allowed in PM
 		origin.send(st.error_reply(stanza, "modify", "bad-request"));
 	elseif stanza.name == "message" and type == "error" then
-		if current_nick then
-			local data = rooms:get(room, to);
-			data.role = 'none';
-			local pr = st.presence({type='unavailable', from=current_nick}):tag('status'):text('This participant is kicked from the room because he sent an error message to another occupant'):up()
-				:tag("x", {xmlns='http://jabber.org/protocol/muc#user'})
-				:tag("item", {affiliation=data.affiliation, role=data.role}):up();
-			broadcast_presence_stanza(room, pr);
-			rooms:remove(room, to);
-			jid_nick:remove(from, room);
-		end
+		log("debug", "%s kicked from %s for sending an error message", current_nick, room);
+		handle_to_occupant(origin, st.presence({type='unavailable', from=from, to=to}):tag('status'):text('This participant is kicked from the room because he sent an error message to another occupant')); -- send unavailable
 	else -- private stanza
 		local o_data = rooms:get(room, to);
 		if o_data then
+			log("debug", "%s sent private stanza to %s (%s)", from, to, o_data.jid);
 			local jid = o_data.jid;
 			if stanza.name=='iq' and type=='get' and stanza.tags[1].attr.xmlns == 'vcard-temp' then jid = jid_bare(jid); end
 			stanza.attr.to, stanza.attr.from = jid, current_nick;
@@ -379,7 +332,6 @@
 			if subject then
 				set_subject(current_nick, room, subject); -- TODO use broadcast_message_stanza
 			else
-				--broadcast_message(current_nick, room, nil, getText(stanza, {"body"}));
 				broadcast_message_stanza(room, stanza, true);
 			end
 		end
--- a/plugins/mod_register.lua	Sun Feb 22 20:57:57 2009 +0100
+++ b/plugins/mod_register.lua	Tue Mar 03 17:48:04 2009 +0100
@@ -44,7 +44,7 @@
 				datamanager.store(session.username, session.host, "vcard", nil);
 				datamanager.store(session.username, session.host, "private", nil);
 				datamanager.store(session.username, session.host, "offline", nil);
-				local bare = session.username.."@"..session.host;
+				--local bare = session.username.."@"..session.host;
 				for jid, item in pairs(roster) do
 					if jid ~= "pending" then
 						if item.subscription == "both" or item.subscription == "to" then
--- a/prosody	Sun Feb 22 20:57:57 2009 +0100
+++ b/prosody	Tue Mar 03 17:48:04 2009 +0100
@@ -7,14 +7,14 @@
 -- COPYING file in the source package for more information.
 --
 
--- Config here --
+-- Will be modified by configure script if run --
 
 CFG_SOURCEDIR=nil;
 CFG_CONFIGDIR=os.getenv("PROSODY_CFGDIR");
 CFG_PLUGINDIR=nil;
 CFG_DATADIR=os.getenv("PROSODY_DATADIR");
 
--- -- -- -- -- --
+-- -- -- -- -- -- -- ---- -- -- -- -- -- -- -- --
 
 if CFG_SOURCEDIR then
 	package.path = CFG_SOURCEDIR.."/?.lua;"..package.path
@@ -84,7 +84,9 @@
 require "core.sessionmanager"
 require "core.stanza_router"
 
---[[
+-- Commented to protect us from 
+-- the second kind of people
+--[[ 
 pcall(require, "remdebug.engine");
 if remdebug then remdebug.engine.start() end
 ]]
@@ -104,6 +106,7 @@
 
 ----------- End of out-of-place code --------------
 
+
 eventmanager.fire_event("server-starting");
 
 
@@ -148,4 +151,21 @@
 
 eventmanager.fire_event("server-started");
 
-server.loop();
+local quitting;
+while not quitting do
+	xpcall(server.loop, function (err)
+					if err:match("%d*: interrupted!$") then
+						quitting = true;
+						return;
+					end
+
+					log("error", "Top-level error, please report:\n%s", tostring(err));
+
+					local traceback = debug.traceback("", 2);
+					if traceback then
+						log("error", "%s", traceback);
+					end
+					
+					eventmanager.fire_event("very-bad-error", "*", err, traceback);
+				end);
+end
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/util/dataforms.lua	Tue Mar 03 17:48:04 2009 +0100
@@ -0,0 +1,70 @@
+
+module "dataforms"
+
+local xmlns_forms = 'jabber:x:data';
+
+local form_t = {};
+local form_mt = { __index = form_t };
+
+function new(layout)
+	return setmetatable(layout, form_mt);
+end
+
+local form_x_attr = { xmlns = xmlns_forms };
+
+function form_t.form(layout, data)
+	local form = st.tag("x", form_x_attr);
+	for n, field in ipairs(layout) do
+		local field_type = field.type;
+		-- Add field tag
+		form:tag("field", { type = field_type, var = field.name });
+
+		local value = data[field.name];
+		
+		-- Add value, depending on type
+		if field_type == "hidden" then
+			if type(value) == "table" then
+				-- Assume an XML snippet
+				form:add_child(value);
+			elseif value then
+				form:text(tostring(value));
+			end
+		elseif field_type == "boolean" then
+			form:text((value and "1") or "0");
+		elseif field_type == "fixed" then
+			
+		elseif field_type == "jid-multi" then
+			for _, jid in ipairs(value) do
+				form:tag("value"):text(jid):up();
+			end
+		elseif field_type == "jid-single" then
+			form:tag("value"):text(value):up();
+			
+		end
+		
+		-- Jump back up to list of fields
+		form:up();
+	end
+	return form;
+end
+
+function form_t.data(layout, stanza)
+	
+end
+
+
+
+--[[
+
+Layout:
+{
+
+	title = "MUC Configuration",
+	instructions = [[Use this form to configure options for this MUC room.]],
+
+	{ name = "FORM_TYPE", type = "hidden", required = true };
+	{ name = "field-name", type = "field-type", required = false };
+}
+
+
+--]]
--- a/util/dependencies.lua	Sun Feb 22 20:57:57 2009 +0100
+++ b/util/dependencies.lua	Tue Mar 03 17:48:04 2009 +0100
@@ -9,7 +9,7 @@
 
 local fatal;
 
-local function softreq(...) local ok, lib =  pcall(require, ...); if ok then return lib; else return nil; end end
+local function softreq(...) local ok, lib =  pcall(require, ...); if ok then return lib; else return nil, lib; end end
 
 local function missingdep(name, sources, msg)
 	print("");
@@ -51,19 +51,37 @@
 	end
 end
 
-local encodings = softreq "util.encodings"
+local encodings, err = softreq "util.encodings"
 if not encodings then
-	missingdep("util.encodings", { ["Windows"] = "Make sure you have encodings.dll from the Prosody distribution in util/";
+	if err:match("not found") then
+		missingdep("util.encodings", { ["Windows"] = "Make sure you have encodings.dll from the Prosody distribution in util/";
 	 				["GNU/Linux"] = "Run './configure' and 'make' in the Prosody source directory to build util/encodings.so";
 	 			});
+	else
+		print "***********************************"
+		print("util/encodings couldn't be loaded. Check that you have a recent version of libidn");
+		print ""
+		print("The full error was:");
+		print(err)
+		print "***********************************"
+	end
 	fatal = true;
 end
 
-local encodings = softreq "util.hashes"
-if not encodings then
-	missingdep("util.hashes", { ["Windows"] = "Make sure you have hashes.dll from the Prosody distribution in util/";
+local hashes, err = softreq "util.hashes"
+if not hashes then
+	if err:match("not found") then
+		missingdep("util.hashes", { ["Windows"] = "Make sure you have hashes.dll from the Prosody distribution in util/";
 	 				["GNU/Linux"] = "Run './configure' and 'make' in the Prosody source directory to build util/hashes.so";
 	 			});
+ 	else
+		print "***********************************"
+		print("util/hashes couldn't be loaded. Check that you have a recent version of OpenSSL (libcrypto in particular)");
+		print ""
+		print("The full error was:");
+		print(err)
+		print "***********************************"
+	end
 	fatal = true;
 end
 
--- a/util/multitable.lua	Sun Feb 22 20:57:57 2009 +0100
+++ b/util/multitable.lua	Tue Mar 03 17:48:04 2009 +0100
@@ -82,6 +82,53 @@
 end
 
 
+local function s(t, n, results, _end, ...)
+	if t == nil then return; end
+	local k = select(n, ...);
+	if n == _end then
+		if k == nil then
+			for _, v in pairs(t) do
+				t_insert(results, v);
+			end
+		else
+			t_insert(results, t[k]);
+		end
+		return;
+	end
+	if k then
+		v = t[k];
+		if v then
+			s(v, n+1, results, _end, ...);
+		end
+	else
+		for _,b in pairs(t) do
+			s(b, n+1, results, _end, ...);
+		end
+	end
+end
+
+-- Search for keys, nil == wildcard
+local function search(self, ...)
+	local _end = select('#', ...);
+	for n = _end,1 do
+		if select(n, ...) then _end = n; break; end
+	end
+	local results = {};
+	s(self.data, 1, results, _end, ...);
+	return results;
+end
+
+-- Append results to an existing list
+local function search_add(self, results, ...)
+	if not results then results = {}; end
+	local _end = select('#', ...);
+	for n = _end,1 do
+		if select(n, ...) then _end = n; break; end
+	end
+	s(self.data, 1, results, _end, ...);
+	return results;
+end
+
 function new()
 	return {
 		data = {};
@@ -89,6 +136,8 @@
 		add = add;
 		set = set;
 		remove = remove;
+		search = search;
+		search_add = search_add;
 	};
 end
 
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/util/pubsub.lua	Tue Mar 03 17:48:04 2009 +0100
@@ -0,0 +1,60 @@
+
+local ipairs, pairs, setmetatable, type = 
+        ipairs, pairs, setmetatable, type;
+
+module "pubsub"
+
+local pubsub_node_mt = { __index = _M };
+
+function new_node(name)
+	return setmetatable({ name = name, subscribers = {} }, pubsub_node_mt);
+end
+
+function set_subscribers(node, subscribers_list, list_type)
+	local subscribers = node.subscribers;
+	
+	if list_type == "array" then
+		for _, jid in ipairs(subscribers_list) do
+			if not subscribers[jid] then
+				node:add_subscriber(jid);
+			end
+		end
+	elseif (not list_type) or list_type == "set" then
+		for jid in pairs(subscribers_list) do
+			if type(jid) == "string" then
+				node:add_subscriber(jid);
+			end
+		end
+	end
+end
+
+function get_subscribers(node)
+	return node.subscribers;
+end
+
+function publish(node, item, dispatcher, data)
+	local subscribers = node.subscribers;
+	for i = 1,#subscribers do
+		item.attr.to = subscribers[i];
+		dispatcher(data, item);
+	end
+end
+
+function add_subscriber(node, jid)
+	local subscribers = node.subscribers;
+	if not subscribers[jid] then
+		local space = #subscribers;
+		subscribers[space] = jid;
+		subscribers[jid] = space;
+	end
+end
+
+function remove_subscriber(node, subscriber)
+	local subscribers = node.subscribers;
+	if subscribers[jid] then
+		subscribers[subscribers[jid]] = nil;
+		subscribers[jid] = nil;
+	end
+end
+
+return _M;
--- a/util/stanza.lua	Sun Feb 22 20:57:57 2009 +0100
+++ b/util/stanza.lua	Tue Mar 03 17:48:04 2009 +0100
@@ -12,9 +12,10 @@
 local t_remove      =  table.remove;
 local t_concat      =  table.concat;
 local s_format      = string.format;
-local s_match      = string.match;
+local s_match       =  string.match;
 local tostring      =      tostring;
 local setmetatable  =  setmetatable;
+local getmetatable  =  getmetatable;
 local pairs         =         pairs;
 local ipairs        =        ipairs;
 local type          =          type;
@@ -215,6 +216,24 @@
 	return stanza;
 end
 
+function clone(stanza)
+    local lookup_table = {};
+    local function _copy(object)
+        if type(object) ~= "table" then
+            return object;
+        elseif lookup_table[object] then
+            return lookup_table[object];
+        end
+        local new_table = {};
+        lookup_table[object] = new_table;
+        for index, value in pairs(object) do
+            new_table[_copy(index)] = _copy(value);
+        end
+        return setmetatable(new_table, getmetatable(object));
+    end
+    return _copy(stanza)
+end
+
 function message(attr, body)
 	if not body then
 		return stanza("message", attr);
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/util/timer.lua	Tue Mar 03 17:48:04 2009 +0100
@@ -0,0 +1,51 @@
+-- Prosody IM v0.3
+-- Copyright (C) 2008-2009 Matthew Wild
+-- Copyright (C) 2008-2009 Waqas Hussain
+-- 
+-- This project is MIT/X11 licensed. Please see the
+-- COPYING file in the source package for more information.
+--
+
+
+local ns_addtimer = require "net.server".addtimer;
+local get_time = os.time;
+local t_insert = table.insert;
+local t_remove = table.remove;
+local ipairs, pairs = ipairs, pairs;
+local type = type;
+
+local data = {};
+local new_data = {};
+
+module "timer"
+
+local function _add_task(delay, func)
+	local current_time = get_time();
+	delay = delay + current_time;
+	if delay >= current_time then
+		t_insert(new_data, {delay, func});
+	else func(); end
+end
+
+add_task = _add_task;
+
+ns_addtimer(function()
+	local current_time = get_time();
+	if #new_data > 0 then
+		for _, d in pairs(new_data) do
+			t_insert(data, d);
+		end
+		new_data = {};
+	end
+	
+	for i, d in pairs(data) do
+		local t, func = d[1], d[2];
+		if t <= current_time then
+			data[i] = nil;
+			local r = func();
+			if type(r) == "number" then _add_task(r, func); end
+		end
+	end
+end);
+
+return _M;