Comparison

mod_mam/mod_mam.lua @ 523:eff140d53b83

mod_mam: Add experimental implementation of the Message Archive Management ProtoXEP
author Kim Alvefur <zash@zash.se>
date Fri, 06 Jan 2012 16:50:12 +0100
child 558:66de25ffc8d9
comparison
equal deleted inserted replaced
522:0c756f62758d 523:eff140d53b83
1 -- XEP-xxxx: Message Archive Management for Prosody
2 -- Copyright (C) 2011-2012 Kim Alvefur
3 --
4 -- This file is MIT/X11 licensed.
5 --
6 -- Based on MAM ProtoXEP Version 0.1 (2010-07-01)
7
8 local st = require "util.stanza";
9 local jid_bare = require "util.jid".bare;
10 local jid_split = require "util.jid".split;
11 local xmlns_mam = "urn:xmpp:mam:tmp";
12 local xmlns_delay = "urn:xmpp:delay";
13 local xmlns_forward = "urn:xmpp:forward:0";
14 local host_sessions = hosts[module.host].sessions;
15 local dm_list_load = require "util.datamanager".list_load
16 local dm_list_append = require "util.datamanager".list_append
17 local time_now = os.time;
18 local timestamp, timestamp_parse = require "util.datetime".datetime, require "util.datetime".parse;
19 local uuid = require "util.uuid".generate;
20
21 -- TODO This, and appropritate filtering in message_handler()
22 module:hook("iq/self/"..xmlns_mam..":prefs", function(event)
23 local origin, stanza = event.origin, event.stanza;
24 if stanza.attr.type == "get" then
25 -- Not implemented yet, hardcoded to store everything.
26 origin.send(st.reply(stanza)
27 :tag("prefs", { xmlns = xmlns_mam, default = "always" }));
28 return true
29 else -- type == "set"
30 -- TODO
31 end
32 end);
33
34 module:hook("iq/self/"..xmlns_mam..":query", function(event)
35 local origin, stanza = event.origin, event.stanza;
36 local query = stanza.tags[1];
37 if stanza.attr.type == "get" then
38 local qid = query.attr.queryid;
39
40 -- Search query parameters
41 local qwith = query:get_child_text("with");
42 local qstart = query:get_child_text("start");
43 local qend = query:get_child_text("end");
44 module:log("debug", "Archive query, id %s with %s from %s until %s)",
45 tostring(qid), qwith or "anyone", qstart or "the dawn of time", qend or "now");
46
47 local qwith = qwith and jid_bare(qwith); -- FIXME Later, full vs bare query.
48 qstart, qend = (qstart and timestamp_parse(qstart)), (qend and timestamp_parse(qend))
49
50 -- Load all the data!
51 local data, err = dm_list_load(origin.username, origin.host, "archive2"); --FIXME Decide storage name. achive2, [sm]am, archive_ng, for_fra_and_nsa
52 module:log("debug", "Loaded %d items, about to filter", #(data or {}));
53 for i=1,#data do
54 local item = data[i];
55 local when, with = item.when, item.with_bare;
56 local ts = item.timestamp;
57 -- FIXME Premature optimization: Bare JIDs only
58 --module:log("debug", "message with %s when %s", with, when or "???");
59 -- Apply query filter
60 if (not qwith or qwith == with)
61 and (not qstart or when >= qstart)
62 and (not qend or when <= qend) then
63 -- Optimizable? Do this when archiving?
64 --module:log("debug", "sending");
65 local fwd_st = st.message{ to = origin.full_jid }
66 :tag("result", { xmlns = xmlns_mam, queryid = qid }):up()
67 :tag("forwarded", { xmlns = xmlns_forward })
68 :tag("delay", { xmlns = xmlns_delay, stamp = ts or timestamp(when) }):up();
69 local orig_stanza = st.deserialize(item.stanza);
70 orig_stanza.attr.xmlns = "jabber:client";
71 fwd_st:add_child(orig_stanza);
72 origin.send(fwd_st);
73 end
74 end
75 -- That's all folks!
76 module:log("debug", "Archive query %s completed", tostring(qid));
77 origin.send(st.reply(stanza));
78 return true
79 end
80 end);
81
82 local function message_handler(event, c2s)
83 local origin, stanza = event.origin, event.stanza;
84 local orig_type = stanza.attr.type or "normal";
85 local orig_to = stanza.attr.to;
86 local orig_from = stanza.attr.from;
87
88 if not orig_from and c2s then
89 orig_from = origin.full_jid;
90 end
91
92 -- Don't store messages of these types
93 if orig_type == "error"
94 or orig_type == "headline"
95 or orig_type == "groupchat" then
96 return;
97 -- TODO Maybe headlines should be configurable?
98 -- TODO Write a mod_mam_muc for groupchat messages.
99 end
100
101 -- Stamp "We archived this" on the message
102 stanza:tag("archived", { xmlns = xmlns_mam, by = module.host, id = uuid() });
103 local store_user, store_host = jid_split(c2s and orig_from or orig_to);
104
105 local when = time_now();
106 -- And stash it
107 dm_list_append(store_user, store_host, "archive2", {
108 when = when, -- This might be an UNIX timestamp. Probably.
109 timestamp = timestamp(when), -- Textual timestamp. But I'll assume that comparing numbers is faster and less annoying in case of timezones.
110 with = c2s and orig_to or orig_from,
111 with_bare = jid_bare(c2s and orig_to or orig_from), -- Premature optimization, to avoid loads of jid_bare() calls when filtering.
112 stanza = st.preserialize(stanza)
113 });
114 end
115
116 local function c2s_message_handler(event)
117 return message_handler(event, true);
118 end
119
120 -- Stanzas sent by local clients
121 module:hook("pre-message/bare", c2s_message_handler, 1);
122 module:hook("pre-message/full", c2s_message_handler, 1);
123 -- Stanszas to local clients
124 module:hook("message/bare", message_handler, 1);
125 module:hook("message/full", message_handler, 1);
126
127 module:add_feature(xmlns_mam);