Software /
code /
prosody-modules
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); |