Comparison

mod_mam_archive/mod_mam_archive.lua @ 1476:08ca6dd36e39

mod_mam_archive: Fixing issues noted in code review for 153df603f73d3b69c434f2790cff0270de14bb75
author syn@syn.im
date Wed, 30 Jul 2014 21:27:56 +0200
parent 1471:153df603f73d
child 1477:db870913e1cf
comparison
equal deleted inserted replaced
1475:58d48afca54d 1476:08ca6dd36e39
13 local st = require "util.stanza"; 13 local st = require "util.stanza";
14 local archive_store = "archive2"; 14 local archive_store = "archive2";
15 local archive = module:open_store(archive_store, "archive"); 15 local archive = module:open_store(archive_store, "archive");
16 local global_default_policy = module:get_option("default_archive_policy", false); 16 local global_default_policy = module:get_option("default_archive_policy", false);
17 local default_max_items, max_max_items = 20, module:get_option_number("max_archive_query_results", 50); 17 local default_max_items, max_max_items = 20, module:get_option_number("max_archive_query_results", 50);
18 local conversation_interval = module:get_option_number("archive_conversation_interval", 86400); 18 local conversation_interval = tonumber(module:get_option_number("archive_conversation_interval", 86400));
19 19
20 -- Feature discovery 20 -- Feature discovery
21 local xmlns_archive = "urn:xmpp:archive" 21 local xmlns_archive = "urn:xmpp:archive"
22 local feature_archive = st.stanza('feature', {xmlns=xmlns_archive}); 22 local feature_archive = st.stanza("feature", {xmlns=xmlns_archive}):tag("optional");
23 feature_archive:tag('optional');
24 if(global_default_policy) then 23 if(global_default_policy) then
25 feature_archive:tag('default'); 24 feature_archive:tag("default");
26 end 25 end
27 module:add_extension(feature_archive); 26 module:add_extension(feature_archive);
28 module:add_feature("urn:xmpp:archive:auto"); 27 module:add_feature("urn:xmpp:archive:auto");
29 module:add_feature("urn:xmpp:archive:manage"); 28 module:add_feature("urn:xmpp:archive:manage");
30 module:add_feature("urn:xmpp:archive:pref"); 29 module:add_feature("urn:xmpp:archive:pref");
31 module:add_feature("http://jabber.org/protocol/rsm"); 30 module:add_feature("http://jabber.org/protocol/rsm");
32 -- -------------------------------------------------- 31 -- --------------------------------------------------
33 32
34 local function os_date()
35 return os.date("!*t");
36 end
37 local function date_format(s) 33 local function date_format(s)
38 return os.date("%Y-%m-%dT%H:%M:%SZ", s); 34 return os.date("%Y-%m-%dT%H:%M:%SZ", s);
39 end 35 end
40 36
41 local function prefs_to_stanza(prefs) 37 local function prefs_to_stanza(prefs)
42 local prefstanza = st.stanza("pref", { xmlns='urn:xmpp:archive' }); 38 local prefstanza = st.stanza("pref", { xmlns="urn:xmpp:archive" });
43 local default = prefs[false] ~= nil and prefs[false] or global_default_policy; 39 local default = prefs[false] ~= nil and prefs[false] or global_default_policy;
44 40
45 prefstanza:tag('default', {otr='oppose', save=default and 'true' or 'false'}):up(); 41 prefstanza:tag("default", {otr="oppose", save=default and "true" or "false"}):up();
46 prefstanza:tag('method', {type='auto', use='concede'}):up(); 42 prefstanza:tag("method", {type="auto", use="concede"}):up();
47 prefstanza:tag('method', {type='local', use='concede'}):up(); 43 prefstanza:tag("method", {type="local", use="concede"}):up();
48 prefstanza:tag('method', {type='manual', use='concede'}):up(); 44 prefstanza:tag("method", {type="manual", use="concede"}):up();
49 45
50 for jid, choice in pairs(prefs) do 46 for jid, choice in pairs(prefs) do
51 if jid then 47 if jid then
52 prefstanza:tag('item', {jid=jid, otr='prefer', save=choice and 'message' or 'false' }):up() 48 prefstanza:tag("item", {jid=jid, otr="prefer", save=choice and "message" or "false" }):up()
53 end 49 end
54 end 50 end
55 51
56 return prefstanza; 52 return prefstanza;
57 end 53 end
58 local function prefs_from_stanza(stanza) 54 local function prefs_from_stanza(stanza, username)
59 local current_prefs = get_prefs(origin.username); 55 local current_prefs = get_prefs(username);
60 56
61 -- "default" | "item" | "session" | "method" 57 -- "default" | "item" | "session" | "method"
62 for elem in stanza:children() do 58 for elem in stanza:children() do
63 if elem.name == "default" then 59 if elem.name == "default" then
64 current_prefs[false] = elem.attr['save'] == 'true'; 60 current_prefs[false] = elem.attr["save"] == "true";
65 elseif elem.name == "item" then 61 elseif elem.name == "item" then
66 current_prefs[elem.attr['jid']] = not elem.attr['save'] == 'false'; 62 current_prefs[elem.attr["jid"]] = not elem.attr["save"] == "false";
67 elseif elem.name == "session" then 63 elseif elem.name == "session" then
68 module:log("info", "element is not supported: " .. tostring(elem)); 64 module:log("info", "element is not supported: " .. tostring(elem));
69 -- local found = false; 65 -- local found = false;
70 -- for child in data:children() do 66 -- for child in data:children() do
71 -- if child.name == elem.name and child.attr["thread"] == elem.attr["thread"] then 67 -- if child.name == elem.name and child.attr["thread"] == elem.attr["thread"] then
113 end 109 end
114 if stanza.attr.type == "set" then 110 if stanza.attr.type == "set" then
115 local new_prefs = stanza:get_child("pref", xmlns_archive); 111 local new_prefs = stanza:get_child("pref", xmlns_archive);
116 if not new_prefs then return false; end 112 if not new_prefs then return false; end
117 113
118 local prefs = prefs_from_stanza(stanza); 114 local prefs = prefs_from_stanza(stanza, origin.username);
119 local ok, err = set_prefs(user, prefs); 115 local ok, err = set_prefs(user, prefs);
120 116
121 if not ok then 117 if not ok then
122 return origin.send(st.error_reply(stanza, "cancel", "internal-server-error", "Error storing preferences: "..tostring(err))); 118 return origin.send(st.error_reply(stanza, "cancel", "internal-server-error", "Error storing preferences: "..tostring(err)));
123 end 119 end
124 end 120 end
125 return origin.send(reply); 121 return origin.send(reply);
126 end 122 end
127 local function auto_handler(event) 123 local function auto_handler(event)
128 local origin, stanza = event.origin, event.stanza; 124 local origin, stanza = event.origin, event.stanza;
129 if not stanza.attr['type'] == 'set' then return false; end 125 if not stanza.attr["type"] == "set" then return false; end
130 126
131 local user = origin.username; 127 local user = origin.username;
132 local prefs = get_prefs(user); 128 local prefs = get_prefs(user);
133 local auto = stanza:get_child('auto', xmlns_archive); 129 local auto = stanza:get_child("auto", xmlns_archive);
134 130
135 prefs[false] = auto.attr['save'] ~= nil and auto.attr['save'] == 'true' or false; 131 prefs[false] = auto.attr["save"] ~= nil and auto.attr["save"] == "true" or false;
136 set_prefs(user, prefs); 132 set_prefs(user, prefs);
137 133
138 return origin.send(st.reply(stanza)); 134 return origin.send(st.reply(stanza));
139 end 135 end
140 136
161 ------------------------------------------------------------ 157 ------------------------------------------------------------
162 local function list_stanza_to_query(origin, list_el) 158 local function list_stanza_to_query(origin, list_el)
163 local sql = "SELECT `with`, `when` / ".. conversation_interval .." as `day`, COUNT(0) FROM `prosodyarchive` WHERE `host`=? AND `user`=? AND `store`=? "; 159 local sql = "SELECT `with`, `when` / ".. conversation_interval .." as `day`, COUNT(0) FROM `prosodyarchive` WHERE `host`=? AND `user`=? AND `store`=? ";
164 local args = {origin.host, origin.username, archive_store}; 160 local args = {origin.host, origin.username, archive_store};
165 161
166 local with = list_el.attr['with']; 162 local with = list_el.attr["with"];
167 if with ~= nil then 163 if with ~= nil then
168 sql = sql .. "AND `with` = ? "; 164 sql = sql .. "AND `with` = ? ";
169 table.insert(args, jid_bare(with)); 165 table.insert(args, jid_bare(with));
170 end 166 end
171 167
172 local after = list_el.attr['start']; 168 local after = list_el.attr["start"];
173 if after ~= nil then 169 if after ~= nil then
174 sql = sql .. "AND `when` >= ?"; 170 sql = sql .. "AND `when` >= ?";
175 table.insert(args, date_parse(after)); 171 table.insert(args, date_parse(after));
176 end 172 end
177 173
178 local before = list_el.attr['end']; 174 local before = list_el.attr["end"];
179 if before ~= nil then 175 if before ~= nil then
180 sql = sql .. "AND `when` <= ? "; 176 sql = sql .. "AND `when` <= ? ";
181 table.insert(args, date_parse(before)); 177 table.insert(args, date_parse(before));
182 end 178 end
183 179
184 sql = sql .. "GROUP BY `with`, `when` / ".. conversation_interval .." ORDER BY `when` / ".. conversation_interval .." ASC "; 180 sql = sql .. "GROUP BY `with`, `when` / ".. conversation_interval .." ORDER BY `when` / ".. conversation_interval .." ASC ";
185 181
186 local qset = rsm.get(list_el); 182 local qset = rsm.get(list_el);
187 local limit = math.min(qset and qset.max or default_max_items, max_max_items); 183 local limit = math.min(qset and qset.max or default_max_items, max_max_items);
188 sql = sql..'LIMIT ?'; 184 sql = sql.."LIMIT ?";
189 table.insert(args, limit); 185 table.insert(args, limit);
190 186
191 table.insert(args, 1, sql); 187 table.insert(args, 1, sql);
192 return args; 188 return args;
193 end 189 end
195 local db = get_db(); 191 local db = get_db();
196 local origin, stanza = event.origin, event.stanza; 192 local origin, stanza = event.origin, event.stanza;
197 local reply = st.reply(stanza); 193 local reply = st.reply(stanza);
198 194
199 local query = list_stanza_to_query(origin, stanza.tags[1]); 195 local query = list_stanza_to_query(origin, stanza.tags[1]);
200 local list = reply:tag('list', {xmlns=xmlns_archive}); 196 local list = reply:tag("list", {xmlns=xmlns_archive});
201 197
202 for row in db:select(unpack(query)) do 198 for row in db:select(unpack(query)) do
203 list:tag('chat', { 199 list:tag("chat", {
204 xmlns=xmlns_archive, 200 xmlns=xmlns_archive,
205 with=row[1], 201 with=row[1],
206 start=date_format(row[2] * conversation_interval), 202 start=date_format(row[2] * conversation_interval),
207 version=row[3] 203 version=row[3]
208 }):up(); 204 }):up();
218 214
219 local function retrieve_handler(event) 215 local function retrieve_handler(event)
220 local origin, stanza = event.origin, event.stanza; 216 local origin, stanza = event.origin, event.stanza;
221 local reply = st.reply(stanza); 217 local reply = st.reply(stanza);
222 218
223 local retrieve = stanza:get_child('retrieve', xmlns_archive); 219 local retrieve = stanza:get_child("retrieve", xmlns_archive);
224 220
225 local qwith = retrieve.attr['with']; 221 local qwith = retrieve.attr["with"];
226 local qstart = retrieve.attr['start']; 222 local qstart = retrieve.attr["start"];
227 223
228 module:log("debug", "Archive query, with %s from %s)", 224 module:log("debug", "Archive query, with %s from %s)",
229 qwith or "anyone", qstart or "the dawn of time"); 225 qwith or "anyone", qstart or "the dawn of time");
230 226
231 if qstart then -- Validate timestamps 227 if qstart then -- Validate timestamps
235 return true 231 return true
236 end 232 end
237 qstart = vstart; 233 qstart = vstart;
238 end 234 end
239 235
240 if qwith then -- Validate the 'with' jid 236 if qwith then -- Validate the "with" jid
241 local pwith = qwith and jid_prep(qwith); 237 local pwith = qwith and jid_prep(qwith);
242 if pwith and not qwith then -- it failed prepping 238 if pwith and not qwith then -- it failed prepping
243 origin.send(st.error_reply(stanza, "modify", "bad-request", "Invalid JID")) 239 origin.send(st.error_reply(stanza, "modify", "bad-request", "Invalid JID"))
244 return true 240 return true
245 end 241 end
266 if not data then 262 if not data then
267 return origin.send(st.error_reply(stanza, "cancel", "internal-server-error", err)); 263 return origin.send(st.error_reply(stanza, "cancel", "internal-server-error", err));
268 end 264 end
269 local count = err; 265 local count = err;
270 266
271 local chat = reply:tag('chat', {xmlns=xmlns_archive, with=qwith, start=date_format(qstart), version=count}); 267 local chat = reply:tag("chat", {xmlns=xmlns_archive, with=qwith, start=date_format(qstart), version=count});
272 268
273 module:log("debug", 'Count '..count); 269 module:log("debug", "Count "..count);
274 for id, item, when in data do 270 for id, item, when in data do
275 local tag = jid_bare(item['attr']['from']) == jid_bare(origin.full_jid) and 'from' or 'to'; 271 local tag = jid_bare(item["attr"]["from"]) == jid_bare(origin.full_jid) and "from" or "to";
276 tag = chat:tag(tag, {secs = when - qstart}); 272 tag = chat:tag(tag, {secs = when - qstart});
277 tag:tag('body'):text(item[2][1]):up():up(); 273 tag:tag("body"):text(item[2][1]):up():up();
278 end 274 end
279 275
280 origin.send(reply); 276 origin.send(reply);
281 return true; 277 return true;
282 end 278 end
283 279
284 local function not_implemented(event) 280 local function not_implemented(event)
285 local origin, stanza = event.origin, event.stanza; 281 local origin, stanza = event.origin, event.stanza;
286 local reply = st.reply(stanza):tag('error', {type='cancel'}); 282 local reply = st.reply(stanza):tag("error", {type="cancel"});
287 reply:tag('feature-not-implemented', {xmlns='urn:ietf:params:xml:ns:xmpp-stanzas'}):up(); 283 reply:tag("feature-not-implemented", {xmlns="urn:ietf:params:xml:ns:xmpp-stanzas"}):up();
288 origin.send(reply); 284 origin.send(reply);
289 end 285 end
290 286
291 -- Preferences 287 -- Preferences
292 module:hook("iq/self/urn:xmpp:archive:pref", preferences_handler); 288 module:hook("iq/self/urn:xmpp:archive:pref", preferences_handler);