Working presence, presence probes and other fixes

Thu, 23 Oct 2008 00:46:38 +0500

author
Waqas Hussain <waqas20@gmail.com>
date
Thu, 23 Oct 2008 00:46:38 +0500
changeset 113
9026fdad1531
parent 112
df54cab4ff9c
child 114
bed2a8508cf5

Working presence, presence probes and other fixes

core/stanza_router.lua file | annotate | diff | comparison | revisions
--- a/core/stanza_router.lua	Wed Oct 22 21:20:47 2008 +0500
+++ b/core/stanza_router.lua	Thu Oct 23 00:46:38 2008 +0500
@@ -9,14 +9,20 @@
 
 local st = require "util.stanza";
 local send = require "core.sessionmanager".send_to_session;
+local user_exists = require "core.usermanager".user_exists;
 
 local jid_split = require "util.jid".split;
+local print = print;
 
 function core_process_stanza(origin, stanza)
 	log("debug", "Received: "..tostring(stanza))
 	-- TODO verify validity of stanza (as well as JID validity)
 	if stanza.name == "iq" and not(#stanza.tags == 1 and stanza.tags[1].attr.xmlns) then
-		error("Invalid IQ");
+		if stanza.attr.type == "set" or stanza.attr.type == "get" then
+			error("Invalid IQ");
+		elseif #stanza.tags > 1 or not(stanza.attr.type == "error" or stanza.attr.type == "result") then
+			error("Invalid IQ");
+		end
 	end
 
 	if origin.type == "c2s" and not origin.full_jid
@@ -25,13 +31,50 @@
 		error("Client MUST bind resource after auth");
 	end
 
-	
 	local to = stanza.attr.to;
-	stanza.attr.from = origin.full_jid -- quick fix to prevent impersonation
+	stanza.attr.from = origin.full_jid; -- quick fix to prevent impersonation (FIXME this would be incorrect when the origin is not c2s)
+	-- TODO also, stazas should be returned to their original state before the function ends
 	
-	if not to or (hosts[to] and hosts[to].type == "local") then
+	-- TODO presence subscriptions
+	if not to then
+		if stanza.name == "presence" and origin.roster then
+			if stanza.attr.type == nil or stanza.attr.type == "available" or stanza.attr.type == "unavailable" then
+				--stanza.attr.from = origin.full_jid;
+				for jid in pairs(origin.roster) do -- broadcast to all interested contacts
+					local subscription = origin.roster[jid].subscription;
+					if subscription == "both" or subscription == "from" then
+						stanza.attr.to = jid;
+						core_route_stanza(origin, stanza);
+					end
+				end
+				--[[local node, host = jid_split(stanza.attr.from);
+				for _, res in pairs(hosts[host].sessions[node].sessions) do -- broadcast to all resources
+					if res.full_jid then
+						res = user.sessions[k];
+						break;
+					end
+				end]]
+				if not origin.presence then -- presence probes on initial presence
+					local probe = st.presence({from = origin.full_jid, type = "probe"});
+					for jid in pairs(origin.roster) do
+						local subscription = origin.roster[jid].subscription;
+						if subscription == "both" or subscription == "to" then
+							probe.attr.to = jid;
+							core_route_stanza(origin, probe);
+						end
+					end
+				end
+				origin.presence = stanza;
+				stanza.attr.to = nil; -- reset it
+			else
+				-- TODO error, bad type
+			end
+		else
+			core_handle_stanza(origin, stanza);
+		end
+	elseif hosts[to] and hosts[to].type == "local" then
 		core_handle_stanza(origin, stanza);
-	elseif to and stanza.name == "iq" and not select(3, jid_split(to)) then
+	elseif stanza.name == "iq" and not select(3, jid_split(to)) then
 		core_handle_stanza(origin, stanza);
 	elseif origin.type == "c2s" then
 		core_route_stanza(origin, stanza);
@@ -42,7 +85,6 @@
 	-- Handlers
 	if origin.type == "c2s" or origin.type == "c2s_unauthed" then
 		local session = origin;
-		stanza.attr.from = session.full_jid;
 		
 		log("debug", "Routing stanza");
 		-- Stanza has no to attribute
@@ -55,12 +97,22 @@
 	end
 end
 
+function is_authorized_to_see_presence(origin, username, host)
+	local roster = datamanager.load(username, host, "roster") or {};
+	local item = roster[origin.username.."@"..origin.host];
+	return item and (item.subscription == "both" or item.subscription == "from");
+end
+
 function core_route_stanza(origin, stanza)
 	-- Hooks
 	--- ...later
 	
 	-- Deliver
-	local node, host, resource = jid_split(stanza.attr.to);
+	local to = stanza.attr.to;
+	local node, host, resource = jid_split(to);
+
+	if stanza.name == "presence" and stanza.attr.type == "probe" then resource = nil; end
+
 	local host_session = hosts[host]
 	if host_session and host_session.type == "local" then
 		-- Local host
@@ -70,13 +122,30 @@
 			if not res then
 				-- if we get here, resource was not specified or was unavailable
 				if stanza.name == "presence" then
-					for k in pairs(user.sessions) do -- presence broadcast to all user resources
-						if user.sessions[k].full_jid then
-							stanza.attr.to = user.sessions[k].full_jid;
-							send(user.sessions[k], stanza);
+					if stanza.attr.type == "probe" then
+						if is_authorized_to_see_presence(origin, node, host) then
+							for k in pairs(user.sessions) do -- return presence for all resources
+								if user.sessions[k].presence then
+									local pres = user.sessions[k].presence;
+									pres.attr.to = origin.full_jid;
+									pres.attr.from = user.sessions[k].full_jid;
+									send(origin, pres);
+									pres.attr.to = nil;
+									pres.attr.from = nil;
+								end
+							end
+						else
+							send(origin, st.presence({from = user.."@"..host, to = origin.username.."@"..origin.host, type = "unsubscribed"}));
+						end
+					else
+						for k in pairs(user.sessions) do -- presence broadcast to all user resources
+							if user.sessions[k].full_jid then
+								stanza.attr.to = user.sessions[k].full_jid;
+								send(user.sessions[k], stanza);
+							end
 						end
 					end
-				else if stanza.name == "message" then -- select a resource to recieve message
+				elseif stanza.name == "message" then -- select a resource to recieve message
 					for k in pairs(user.sessions) do
 						if user.sessions[k].full_jid then
 							res = user.sessions[k];
@@ -84,19 +153,39 @@
 						end
 					end
 					-- TODO find resource with greatest priority
+					send(res, stanza);
 				else
-					error("IQs should't get here");
+					-- TODO send IQ error
 				end
-			end
-			if res then
+			else
 				stanza.attr.to = res.full_jid;
 				send(res, stanza); -- Yay \o/
-			elseif stanza.name == "message" then
-				-- TODO return message error
 			end
 		else
-			-- user not found
-			send(origin, st.error_reply(stanza, "cancel", "service-unavailable"));
+			-- user not online
+			if user_exists(node, host) then
+				if stanza.name == "presence" then
+					if stanza.attr.type == "probe" and is_authorized_to_see_presence(origin, node, host) then -- FIXME what to do for not c2s?
+						-- TODO send last recieved unavailable presence
+					else
+						-- TODO send unavailable presence
+					end
+				elseif stanza.name == "message" then
+					-- TODO send message error, or store offline messages
+				elseif stanza.name == "iq" then
+					-- TODO send IQ error
+				end
+			else -- user does not exist
+				-- TODO we would get here for nodeless JIDs too. Do something fun maybe? Echo service? Let plugins use xmpp:server/resource addresses?
+				if stanza.name == "presence" then
+					if stanza.attr.type == "probe" then
+						send(origin, st.presence({from = user.."@"..host, to = origin.username.."@"..origin.host, type = "unsubscribed"}));
+					end
+					-- else ignore
+				else
+					send(origin, st.error_reply(stanza, "cancel", "service-unavailable"));
+				end
+			end
 		end
 	else
 		-- Remote host
@@ -106,6 +195,7 @@
 			-- Need to establish the connection
 		end
 	end
+	stanza.attr.to = to; -- reset
 end
 
 function handle_stanza_nodest(stanza)

mercurial