Mercurial > prosody-modules
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 |
| parents | |
| children | 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); |
