Changeset

251:5b6dec537914

Merge from waqas
author Matthew Wild <mwild1@gmail.com>
date Thu, 13 Nov 2008 16:58:29 +0000
parents 245:5dc6ae7b5ce8 (current diff) 250:9f66ede8deeb (diff)
children 252:a698993bd49b
files util/stanza.lua
diffstat 5 files changed, 150 insertions(+), 9 deletions(-) [+]
line wrap: on
line diff
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/core/offlinemanager.lua	Thu Nov 13 16:58:29 2008 +0000
@@ -0,0 +1,32 @@
+
+local datamanager = require "util.datamanager";
+local st = require "util.stanza";
+local datetime = require "util.datetime";
+local ipairs = ipairs;
+
+module "offlinemanager"
+
+function store(node, host, stanza)
+	stanza.attr.stamp = datetime.datetime();
+	stanza.attr.stamp_legacy = datetime.legacy();
+	return datamanager.list_append(node, host, "offline", st.preserialize(stanza));
+end
+
+function load(node, host)
+	local data = datamanager.list_load(node, host, "offline");
+	if not data then return; end
+	for k, v in ipairs(data) do
+		stanza = st.deserialize(v);
+		stanza:tag("delay", {xmlns = "urn:xmpp:delay", from = host, stamp = stanza.attr.stamp}):up(); -- XEP-0203
+		stanza:tag("x", {xmlns = "jabber:x:delay", from = host, stamp = stanza.attr.stamp_legacy}):up(); -- XEP-0091 (deprecated)
+		stanza.attr.stamp, stanza.attr.stamp_legacy = nil, nil;
+		data[k] = stanza;
+	end
+	return data;
+end
+
+function deleteAll(node, host)
+	return datamanager.list_store(node, host, "offline", nil);
+end
+
+return _M;
--- a/core/stanza_router.lua	Thu Nov 13 16:47:16 2008 +0000
+++ b/core/stanza_router.lua	Thu Nov 13 16:58:29 2008 +0000
@@ -13,6 +13,7 @@
 
 local rostermanager = require "core.rostermanager";
 local sessionmanager = require "core.sessionmanager";
+local offlinemanager = require "core.offlinemanager";
 
 local s2s_verify_dialback = require "core.s2smanager".verify_dialback;
 local s2s_make_authenticated = require "core.s2smanager".make_authenticated;
@@ -149,6 +150,10 @@
 							core_route_stanza(origin, request);
 						end
 					end
+					for _, msg in ipairs(offlinemanager.load(node, host) or {}) do
+						origin.send(msg); -- FIXME do we need to modify to/from in any way?
+					end
+					offlinemanager.deleteAll(node, host);
 				end
 				origin.priority = 0;
 				if stanza.attr.type == "unavailable" then
@@ -168,11 +173,16 @@
 				end
 				stanza.attr.to = nil; -- reset it
 			else
-				-- TODO error, bad type
+				log("warn", "Unhandled c2s presence: %s", tostring(stanza));
+				origin.send(st.error_reply(stanza, "cancel", "service-unavailable")); -- FIXME correct error?
 			end
+		else
+			log("warn", "Unhandled c2s stanza: %s", tostring(stanza));
+			origin.send(st.error_reply(stanza, "cancel", "service-unavailable")); -- FIXME correct error?
 		end -- TODO handle other stanzas
 	else
-		log("warn", "Unhandled origin: %s", origin.type); -- FIXME reply with error
+		log("warn", "Unhandled origin: %s", origin.type);
+		origin.send(st.error_reply(stanza, "cancel", "service-unavailable")); -- FIXME correct error?
 	end
 end
 
@@ -328,8 +338,14 @@
 							t_insert(recipients, session);
 						end
 					end
+					local count = 0;
 					for _, session in pairs(recipients) do
 						session.send(stanza);
+						count = count + 1;
+					end
+					if count == 0 then
+						offlinemanager.store(node, host, stanza);
+						-- TODO deal with storage errors
 					end
 				else
 					-- TODO send IQ error
@@ -349,7 +365,12 @@
 						-- TODO send unavailable presence or unsubscribed
 					end
 				elseif stanza.name == "message" then
-					-- TODO send message error, or store offline messages
+					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
+					end
+					-- TODO allow configuration of offline storage
+					-- TODO send error if not storing offline
 				elseif stanza.name == "iq" then
 					-- TODO send IQ error
 				end
--- a/util/datamanager.lua	Thu Nov 13 16:47:16 2008 +0000
+++ b/util/datamanager.lua	Thu Nov 13 16:58:29 2008 +0000
@@ -1,6 +1,6 @@
 local format = string.format;
 local setmetatable, type = setmetatable, type;
-local pairs = pairs;
+local pairs, ipairs = pairs, ipairs;
 local char = string.char;
 local loadfile, setfenv, pcall = loadfile, setfenv, pcall;
 local log = log;
@@ -9,6 +9,7 @@
 local tostring = tostring;
 local error = error;
 local next = next;
+local t_insert = table.insert;
 
 local indent = function(f, i)
 	for n = 1, i do
@@ -69,13 +70,14 @@
 
 ------- API -------------
 
-function getpath(username, host, datastore)
+function getpath(username, host, datastore, ext)
+	ext = ext or "dat";
 	if username then
-		return format("data/%s/%s/%s.dat", encode(host), datastore, encode(username));
+		return format("data/%s/%s/%s.%s", encode(host), datastore, encode(username), ext);
 	elseif host then
-		return format("data/%s/%s.dat", encode(host), datastore);
+		return format("data/%s/%s.%s", encode(host), datastore, ext);
 	else
-		return format("data/%s.dat", datastore);
+		return format("data/%s.%s", datastore, ext);
 	end
 end
 
@@ -115,4 +117,59 @@
 	return true;
 end
 
-return _M;
\ No newline at end of file
+function list_append(username, host, datastore, data)
+	if not data then return; end
+	-- save the datastore
+	local f, msg = io_open(getpath(username, host, datastore, "list"), "a+");
+	if not f then
+		log("error", "Unable to write to "..datastore.." storage ('"..msg.."') for user: "..(username or "nil").."@"..(host or "nil"));
+		return;
+	end
+	f:write("item(");
+	simplesave(f, data, 1);
+	f:write(");\n");
+	f:close();
+	return true;
+end
+
+function list_store(username, host, datastore, data)
+	if not data then
+		data = {};
+	end
+	-- save the datastore
+	local f, msg = io_open(getpath(username, host, datastore, "list"), "w+");
+	if not f then
+		log("error", "Unable to write to "..datastore.." storage ('"..msg.."') for user: "..(username or "nil").."@"..(host or "nil"));
+		return;
+	end
+	for _, d in ipairs(data) do
+		f:write("item(");
+		simplesave(f, d, 1);
+		f:write(");\n");
+	end
+	f:close();
+	if not next(data) then -- try to delete empty datastore
+		os_remove(getpath(username, host, datastore, "list"));
+	end
+	-- we write data even when we are deleting because lua doesn't have a
+	-- platform independent way of checking for non-exisitng files
+	return true;
+end
+
+function list_load(username, host, datastore)
+	local data, ret = loadfile(getpath(username, host, datastore, "list"));
+	if not data then
+		log("warn", "Failed to load "..datastore.." storage ('"..ret.."') for user: "..(username or "nil").."@"..(host or "nil"));
+		return nil;
+	end
+	local items = {};
+	setfenv(data, {item = function(i) t_insert(items, i); end});
+	local success, ret = pcall(data);
+	if not success then
+		log("error", "Unable to load "..datastore.." storage ('"..ret.."') for user: "..(username or "nil").."@"..(host or "nil"));
+		return nil;
+	end
+	return items;
+end
+
+return _M;
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/util/datetime.lua	Thu Nov 13 16:58:29 2008 +0000
@@ -0,0 +1,28 @@
+-- XEP-0082: XMPP Date and Time Profiles
+
+local os_date = os.date;
+local error = error;
+
+module "datetime"
+
+function date()
+	return os_date("!%Y-%m-%d");
+end
+
+function datetime()
+	return os_date("!%Y-%m-%dT%H:%M:%SZ");
+end
+
+function time()
+	return os_date("!%H:%M:%S");
+end
+
+function legacy()
+	return os_date("!%Y%m%dT%H:%M:%S");
+end
+
+function parse(s)
+	error("datetime.parse: Not implemented"); -- TODO
+end
+
+return _M;
--- a/util/stanza.lua	Thu Nov 13 16:47:16 2008 +0000
+++ b/util/stanza.lua	Thu Nov 13 16:58:29 2008 +0000
@@ -157,6 +157,9 @@
 			end
 			stanza.tags = tags;
 		end
+		if not stanza.last_add then
+			stanza.last_add = {};
+		end
 	end
 	
 	return stanza;