plugins/archive.lua

1
-- This implements XEP-0313: Message Archive Management
2
-- http://xmpp.org/extensions/xep-0313.html
3
-- (ie not XEP-0136)
4
 
5
local verse = require "verse";
6
local st = require "prosody.util.stanza";
7
local xmlns_mam = "urn:xmpp:mam:2"
8
local xmlns_forward = "urn:xmpp:forward:0";
9
local xmlns_delay = "urn:xmpp:delay";
10
local new_id = require "prosody.util.id".short;
11
local parse_datetime = require "prosody.util.datetime".parse;
12
local datetime = require "prosody.util.datetime".datetime;
13
local dataform = require"prosody.util.dataforms".new;
14
local rsm = require "prosody.util.rsm";
15
local NULL = {};
16
 
17
local query_form = dataform {
18
	{ name = "FORM_TYPE"; type = "hidden"; value = xmlns_mam; };
19
	{ name = "with"; type = "jid-single"; };
20
	{ name = "start"; type = "text-single" };
21
	{ name = "end"; type = "text-single"; };
22
};
23
 
24
function verse.plugins.archive(stream)
25
	function stream:query_archive(where, query_params, callback)
26
		local queryid = new_id();
27
		local query_st = st.iq{ id = queryid, type="set", to = where }
28
			:tag("query", { xmlns = xmlns_mam, queryid = queryid });
29
 
30
 
31
		local qstart, qend = tonumber(query_params["start"]), tonumber(query_params["end"]);
32
		query_params["start"] = qstart and datetime(qstart);
33
		query_params["end"] = qend and datetime(qend);
34
 
35
		query_st:add_child(query_form:form(query_params, "submit"));
36
		-- query_st:up();
37
		query_st:add_child(rsm.generate(query_params));
38
 
39
		local results = {};
40
		local function handle_archived_message(message)
41
 
42
			local result_tag = message:get_child("result", xmlns_mam);
43
			if result_tag and result_tag.attr.queryid == queryid then
44
				local forwarded = result_tag:get_child("forwarded", xmlns_forward);
45
 
46
				local id = result_tag.attr.id;
47
				local delay = forwarded:get_child("delay", xmlns_delay);
48
				local stamp = delay and parse_datetime(delay.attr.stamp) or nil;
49
 
50
				local message = forwarded:get_child("message", "jabber:client")
51
 
52
				results[#results+1] = { id = id, stamp = stamp, message = message };
53
				return true
54
			end
55
		end
56
 
57
		self:hook("message", handle_archived_message, 1);
58
		self:send_iq(query_st, function(reply)
59
			self:unhook("message", handle_archived_message);
60
			if reply.attr.type == "error" then
61
				self:warn(table.concat({reply:get_error()}, " "))
62
				callback(false, reply:get_error())
63
				return true;
64
			end
65
			local finished = reply:get_child("fin", xmlns_mam)
66
			if finished then
67
				results.complete = finished.attr.complete == "true" or finished.attr.complete == "1";
68
				local rset = rsm.get(finished);
69
				for k,v in pairs(rset or NULL) do results[k]=v; end
70
			end
71
			callback(results);
72
			return true
73
		end);
74
	end
75
 
76
	local default_attrs = {
77
		always = true, [true] = "always",
78
		never = false, [false] = "never",
79
		roster = "roster",
80
	}
81
 
82
	local function prefs_decode(stanza) -- from XML
83
		local prefs = {};
84
		local default = stanza.attr.default;
85
 
86
		if default then
87
			prefs[false] = default_attrs[default];
88
		end
89
 
90
		local always = stanza:get_child("always");
91
		if always then
92
			for rule in always:childtags("jid") do
93
				local jid = rule:get_text();
94
				prefs[jid] = true;
95
			end
96
		end
97
 
98
		local never = stanza:get_child("never");
99
		if never then
100
			for rule in never:childtags("jid") do
101
				local jid = rule:get_text();
102
				prefs[jid] = false;
103
			end
104
		end
105
		return prefs;
106
	end
107
 
108
	local function prefs_encode(prefs) -- into XML
109
		local default
110
		default, prefs[false] = prefs[false], nil;
111
		if default ~= nil then
112
			default = default_attrs[default];
113
		end
114
		local reply = st.stanza("prefs", { xmlns = xmlns_mam, default = default })
115
		local always = st.stanza("always");
116
		local never = st.stanza("never");
117
		for k,v in pairs(prefs) do
118
			(v and always or never):tag("jid"):text(k):up();
119
		end
120
		return reply:add_child(always):add_child(never);
121
	end
122
 
123
	function stream:archive_prefs_get(callback)
124
		self:send_iq(st.iq{ type="get" }:tag("prefs", { xmlns = xmlns_mam }),
125
		function(result)
126
			if result and result.attr.type == "result" and result.tags[1] then
127
				local prefs = prefs_decode(result.tags[1]);
128
				callback(prefs, result);
129
			else
130
				callback(nil, result);
131
			end
132
		end);
133
	end
134
 
135
	function stream:archive_prefs_set(prefs, callback)
136
		self:send_iq(st.iq{ type="set" }:add_child(prefs_encode(prefs)), callback);
137
	end
138
end