diff mod_mam/mod_mam.lua @ 701:cc5805f83583

mod_mam: Implement support for Result Set Management in queries.
author Kim Alvefur <zash@zash.se>
date Fri, 08 Jun 2012 03:13:31 +0200
parents da33325453fb
children d94ee0848b27
line wrap: on
line diff
--- a/mod_mam/mod_mam.lua	Thu Jun 07 23:41:25 2012 +0200
+++ b/mod_mam/mod_mam.lua	Fri Jun 08 03:13:31 2012 +0200
@@ -8,6 +8,7 @@
 local xmlns_forward = "urn:xmpp:forward:0";
 
 local st = require "util.stanza";
+local rsm = module:require "rsm";
 local jid_bare = require "util.jid".bare;
 local jid_split = require "util.jid".split;
 local host = module.host;
@@ -62,7 +63,6 @@
 		local default = prefs[false];
 		default = default ~= nil and default_attrs[default] or global_default_policy;
 		local reply = st.reply(stanza):tag("prefs", { xmlns = xmlns_mam, default = default })
-		--module:log("debug", "get_prefs(%q) => %s", user, require"util.serialization".serialize(prefs));
 		local always = st.stanza("always");
 		local never = st.stanza("never");
 		for k,v in pairs(prefs) do
@@ -97,7 +97,6 @@
 			end
 		end
 
-		--module:log("debug", "set_prefs(%q, %s)", user, require"util.serialization".serialize(prefs));
 		local ok, err = set_prefs(user, prefs);
 		if not ok then
 			origin.send(st.error_reply(stanza, "cancel", "internal-server-error", "Error storing preferences: "..tostring(err)));
@@ -119,6 +118,7 @@
 		local qwith = query:get_child_text("with");
 		local qstart = query:get_child_text("start");
 		local qend = query:get_child_text("end");
+		local qset = rsm.get(query);
 		module:log("debug", "Archive query, id %s with %s from %s until %s)",
 			tostring(qid), qwith or "anyone", qstart or "the dawn of time", qend or "now");
 
@@ -136,19 +136,35 @@
 			return true
 		end
 
+		-- RSM stuff
+		local qset_matches = not (qset and qset.after);
+		local first, last, index;
+		local n = 0;
+		local start = qset and qset.index or 1;
+
 		module:log("debug", "Loaded %d items, about to filter", #data);
-		for i=1,#data do
+		for i=start,#data do
 			local item = data[i];
 			local when, with, with_bare = item.when, item.with, item.with_bare;
 			local ts = item.timestamp;
 			local id = item.id;
+			--module:log("debug", "id is %s", id);
+
+			-- RSM pre-send-checking
+			if qset then
+				if qset.before == id then
+					module:log("debug", "End of matching range found");
+					qset_matches = false;
+					break;
+				end
+			end
+
 			--module:log("debug", "message with %s at %s", with, when or "???");
 			-- Apply query filter
 			if (not qwith or ((qwith == with) or (qwith == with_bare)))
 					and (not qstart or when >= qstart)
-					and (not qend or when <= qend) then
-				-- Optimizable? Do this when archiving?
-				--module:log("debug", "sending");
+					and (not qend or when <= qend)
+					and (not qset or qset_matches) then
 				local fwd_st = st.message{ to = origin.full_jid }
 					:tag("result", { xmlns = xmlns_mam, queryid = qid, id = id }):up()
 					:tag("forwarded", { xmlns = xmlns_forward })
@@ -157,13 +173,32 @@
 				orig_stanza.attr.xmlns = "jabber:client";
 				fwd_st:add_child(orig_stanza);
 				origin.send(fwd_st);
-			elseif qend and when > qend then
+				if not first then
+					index = i;
+					first = id;
+				end
+				last = id;
+				n = n + 1;
+			elseif (qend and when > qend) then
+				module:log("debug", "We have passed into messages more recent than requested");
 				break -- We have passed into messages more recent than requested
 			end
+
+			-- RSM post-send-checking
+			if qset then
+				if qset.after == id then
+					module:log("debug", "Start of matching range found");
+					qset_matches = true;
+				end
+				if qset.max and n >= qset.max then
+					module:log("debug", "Max number of items matched");
+					break
+				end
+			end
 		end
 		-- That's all folks!
 		module:log("debug", "Archive query %s completed", tostring(qid));
-		origin.send(st.reply(stanza));
+		origin.send(st.reply(stanza):add_child(rsm.generate{first = { index = index; first }, last = last}));
 		return true
 	end
 end);
@@ -221,8 +256,6 @@
 	local target_jid = c2s and orig_to or orig_from;
 	local target_bare = jid_bare(target_jid);
 
-	assert(store_host == host, "This should not happen.");
-
 	if shall_store(store_user, target_bare) then
 		module:log("debug", "Archiving stanza: %s", stanza:top_tag());
 
@@ -232,8 +265,7 @@
 		local ok, err = dm_list_append(store_user, store_host, archive_store, {
 			-- WARNING This format may change.
 			id = id,
-			when = when, -- This might be an UNIX timestamp. Probably.
-			timestamp = timestamp(when), -- Textual timestamp. But I'll assume that comparing numbers is faster and less annoying in case of timezones.
+			when = when,
 			with = target_jid,
 			with_bare = target_bare, -- Optimization, to avoid loads of jid_bare() calls when filtering.
 			stanza = st.preserialize(stanza)