Mercurial > 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 |
| parents | 153df603f73d |
| children | 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); |
