Software /
code /
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 |
parent | 1403:6b3db167374a |
child | 1587: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 |