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