Mercurial > prosody-modules
comparison mod_mam/mod_mam.lua @ 1484:53a3a19d6093
mod_mam: Update to version 0.3 of XEP-0313
| author | Kim Alvefur <zash@zash.se> |
|---|---|
| date | Fri, 15 Aug 2014 17:43:23 +0200 |
| parents | 6b3db167374a |
| children | 3ac2b835c7b3 |
comparison
equal
deleted
inserted
replaced
| 1483:90fe03e65d2c | 1484:53a3a19d6093 |
|---|---|
| 1 -- XEP-0313: Message Archive Management for Prosody | 1 -- XEP-0313: Message Archive Management for Prosody |
| 2 -- Copyright (C) 2011-2014 Kim Alvefur | 2 -- Copyright (C) 2011-2014 Kim Alvefur |
| 3 -- | 3 -- |
| 4 -- This file is MIT/X11 licensed. | 4 -- This file is MIT/X11 licensed. |
| 5 | 5 |
| 6 local xmlns_mam = "urn:xmpp:mam:tmp"; | 6 local xmlns_mam = "urn:xmpp:mam:0"; |
| 7 local xmlns_delay = "urn:xmpp:delay"; | 7 local xmlns_delay = "urn:xmpp:delay"; |
| 8 local xmlns_forward = "urn:xmpp:forward:0"; | 8 local xmlns_forward = "urn:xmpp:forward:0"; |
| 9 | 9 |
| 10 local st = require "util.stanza"; | 10 local st = require "util.stanza"; |
| 11 local rsm = module:require "rsm"; | 11 local rsm = module:require "rsm"; |
| 13 local set_prefs = module:require"mamprefs".set; | 13 local set_prefs = module:require"mamprefs".set; |
| 14 local prefs_to_stanza = module:require"mamprefsxml".tostanza; | 14 local prefs_to_stanza = module:require"mamprefsxml".tostanza; |
| 15 local prefs_from_stanza = module:require"mamprefsxml".fromstanza; | 15 local prefs_from_stanza = module:require"mamprefsxml".fromstanza; |
| 16 local jid_bare = require "util.jid".bare; | 16 local jid_bare = require "util.jid".bare; |
| 17 local jid_split = require "util.jid".split; | 17 local jid_split = require "util.jid".split; |
| 18 local jid_prep = require "util.jid".prep; | 18 local dataform = require "util.dataforms".new; |
| 19 local host = module.host; | 19 local host = module.host; |
| 20 | 20 |
| 21 local rm_load_roster = require "core.rostermanager".load_roster; | 21 local rm_load_roster = require "core.rostermanager".load_roster; |
| 22 | 22 |
| 23 local getmetatable = getmetatable; | 23 local getmetatable = getmetatable; |
| 59 end | 59 end |
| 60 return origin.send(st.reply(stanza)); | 60 return origin.send(st.reply(stanza)); |
| 61 end | 61 end |
| 62 end); | 62 end); |
| 63 | 63 |
| 64 local query_form = dataform { | |
| 65 { name = "FORM_TYPE"; type = "hidden"; value = xmlns_mam; }; | |
| 66 { name = "with"; type = "jid-single"; }; | |
| 67 { name = "start"; type = "text-single" }; | |
| 68 { name = "end"; type = "text-single"; }; | |
| 69 }; | |
| 70 | |
| 71 -- Serve form | |
| 72 module:hook("iq-get/self/"..xmlns_mam..":query", function(event) | |
| 73 local origin, stanza = event.origin, event.stanza; | |
| 74 return origin.send(st.reply(stanza):add_child(query_form:form())); | |
| 75 end); | |
| 76 | |
| 64 -- Handle archive queries | 77 -- Handle archive queries |
| 65 module:hook("iq-get/self/"..xmlns_mam..":query", function(event) | 78 module:hook("iq-set/self/"..xmlns_mam..":query", function(event) |
| 66 local origin, stanza = event.origin, event.stanza; | 79 local origin, stanza = event.origin, event.stanza; |
| 67 local query = stanza.tags[1]; | 80 local query = stanza.tags[1]; |
| 68 local qid = query.attr.queryid; | 81 local qid = query.attr.queryid; |
| 69 | 82 |
| 70 -- Search query parameters | 83 -- Search query parameters |
| 71 local qwith = query:get_child_text("with"); | 84 local qwith, qstart, qend; |
| 72 local qstart = query:get_child_text("start"); | 85 local form = query:get_child("x", "jabber:x:data"); |
| 73 local qend = query:get_child_text("end"); | 86 if form then |
| 74 module:log("debug", "Archive query, id %s with %s from %s until %s)", | 87 local err; |
| 75 tostring(qid), qwith or "anyone", qstart or "the dawn of time", qend or "now"); | 88 form, err = query_form:data(form); |
| 89 if err then | |
| 90 return origin.send(st.error_reply(stanza, "modify", "bad-request", select(2, next(err)))) | |
| 91 end | |
| 92 qwith, qstart, qend = form["with"], form["start"], form["end"]; | |
| 93 qwith = qwith and jid_bare(qwith); -- dataforms does jidprep | |
| 94 end | |
| 76 | 95 |
| 77 if qstart or qend then -- Validate timestamps | 96 if qstart or qend then -- Validate timestamps |
| 78 local vstart, vend = (qstart and timestamp_parse(qstart)), (qend and timestamp_parse(qend)) | 97 local vstart, vend = (qstart and timestamp_parse(qstart)), (qend and timestamp_parse(qend)) |
| 79 if (qstart and not vstart) or (qend and not vend) then | 98 if (qstart and not vstart) or (qend and not vend) then |
| 80 origin.send(st.error_reply(stanza, "modify", "bad-request", "Invalid timestamp")) | 99 origin.send(st.error_reply(stanza, "modify", "bad-request", "Invalid timestamp")) |
| 81 return true | 100 return true |
| 82 end | 101 end |
| 83 qstart, qend = vstart, vend; | 102 qstart, qend = vstart, vend; |
| 84 end | 103 end |
| 85 | 104 |
| 86 if qwith then -- Validate the 'with' jid | 105 module:log("debug", "Archive query, id %s with %s from %s until %s)", |
| 87 local pwith = qwith and jid_prep(qwith); | 106 tostring(qid), qwith or "anyone", qstart or "the dawn of time", qend or "now"); |
| 88 if pwith and not qwith then -- it failed prepping | |
| 89 origin.send(st.error_reply(stanza, "modify", "bad-request", "Invalid JID")) | |
| 90 return true | |
| 91 end | |
| 92 qwith = jid_bare(pwith); | |
| 93 end | |
| 94 | 107 |
| 95 -- RSM stuff | 108 -- RSM stuff |
| 96 local qset = rsm.get(query); | 109 local qset = rsm.get(query); |
| 97 local qmax = m_min(qset and qset.max or default_max_items, max_max_items); | 110 local qmax = m_min(qset and qset.max or default_max_items, max_max_items); |
| 98 local reverse = qset and qset.before or false; | 111 local reverse = qset and qset.before or false; |
| 113 if not data then | 126 if not data then |
| 114 return origin.send(st.error_reply(stanza, "cancel", "internal-server-error", err)); | 127 return origin.send(st.error_reply(stanza, "cancel", "internal-server-error", err)); |
| 115 end | 128 end |
| 116 local count = err; | 129 local count = err; |
| 117 | 130 |
| 131 origin.send(st.reply(stanza)) | |
| 118 local msg_reply_attr = { to = stanza.attr.from, from = stanza.attr.to }; | 132 local msg_reply_attr = { to = stanza.attr.from, from = stanza.attr.to }; |
| 119 | 133 |
| 120 -- Wrap it in stuff and deliver | 134 -- Wrap it in stuff and deliver |
| 121 local fwd_st, first, last; | 135 local fwd_st, first, last; |
| 122 for id, item, when in data do | 136 for id, item, when in data do |
| 138 end | 152 end |
| 139 -- That's all folks! | 153 -- That's all folks! |
| 140 module:log("debug", "Archive query %s completed", tostring(qid)); | 154 module:log("debug", "Archive query %s completed", tostring(qid)); |
| 141 | 155 |
| 142 if reverse then first, last = last, first; end | 156 if reverse then first, last = last, first; end |
| 143 return origin.send(st.reply(stanza) | 157 return origin.send(st.message(msg_reply_attr) |
| 144 :query(xmlns_mam):add_child(rsm.generate { | 158 :tag("fin", { xmlns = xmlns_mam, queryid = qid }) |
| 145 first = first, last = last, count = count })); | 159 :add_child(rsm.generate { |
| 160 first = first, last = last, count = count })); | |
| 146 end); | 161 end); |
| 147 | 162 |
| 148 local function has_in_roster(user, who) | 163 local function has_in_roster(user, who) |
| 149 local roster = rm_load_roster(user, host); | 164 local roster = rm_load_roster(user, host); |
| 150 module:log("debug", "%s has %s in roster? %s", user, who, roster[who] and "yes" or "no"); | 165 module:log("debug", "%s has %s in roster? %s", user, who, roster[who] and "yes" or "no"); |
| 201 -- Check with the users preferences | 216 -- Check with the users preferences |
| 202 if shall_store(store_user, with) then | 217 if shall_store(store_user, with) then |
| 203 module:log("debug", "Archiving stanza: %s", stanza:top_tag()); | 218 module:log("debug", "Archiving stanza: %s", stanza:top_tag()); |
| 204 | 219 |
| 205 -- And stash it | 220 -- And stash it |
| 206 local ok, id = archive:append(store_user, time_now(), with, stanza); | 221 local ok, id = archive:append(store_user, nil, time_now(), with, stanza); |
| 207 if ok then | |
| 208 stanza:tag("archived", { xmlns = xmlns_mam, by = store_user.."@"..host, id = id }):up(); | |
| 209 end | |
| 210 else | 222 else |
| 211 module:log("debug", "Not archiving stanza: %s (prefs)", stanza:top_tag()); | 223 module:log("debug", "Not archiving stanza: %s (prefs)", stanza:top_tag()); |
| 212 end | 224 end |
| 213 end | 225 end |
| 214 | 226 |
| 221 module:hook("pre-message/full", c2s_message_handler, 2); | 233 module:hook("pre-message/full", c2s_message_handler, 2); |
| 222 -- Stanszas to local clients | 234 -- Stanszas to local clients |
| 223 module:hook("message/bare", message_handler, 2); | 235 module:hook("message/bare", message_handler, 2); |
| 224 module:hook("message/full", message_handler, 2); | 236 module:hook("message/full", message_handler, 2); |
| 225 | 237 |
| 226 local function post_carbons_handler(event) | |
| 227 event.stanza:maptags(function(tag) | |
| 228 if not ( tag.attr.xmlns == xmlns_mam and tag.name == "archived" ) then | |
| 229 return tag; | |
| 230 end | |
| 231 end); | |
| 232 end | |
| 233 | |
| 234 module:hook("pre-message/bare", post_carbons_handler, 0.9); | |
| 235 module:hook("pre-message/full", post_carbons_handler, 0.9); | |
| 236 | |
| 237 module:add_feature(xmlns_mam); | 238 module:add_feature(xmlns_mam); |
| 238 | 239 |
