Mercurial > prosody-hg
comparison plugins/mod_mam/mod_mam.lua @ 10411:db2a06b9ff98
Merge 0.11->trunk
| author | Kim Alvefur <zash@zash.se> |
|---|---|
| date | Sat, 16 Nov 2019 16:52:31 +0100 |
| parents | fd94721186b8 |
| children | cfc05e46b979 |
comparison
equal
deleted
inserted
replaced
| 10410:659b577f280c | 10411:db2a06b9ff98 |
|---|---|
| 38 local strip_tags = module:get_option_set("dont_archive_namespaces", { "http://jabber.org/protocol/chatstates" }); | 38 local strip_tags = module:get_option_set("dont_archive_namespaces", { "http://jabber.org/protocol/chatstates" }); |
| 39 | 39 |
| 40 local archive_store = module:get_option_string("archive_store", "archive"); | 40 local archive_store = module:get_option_string("archive_store", "archive"); |
| 41 local archive = module:open_store(archive_store, "archive"); | 41 local archive = module:open_store(archive_store, "archive"); |
| 42 | 42 |
| 43 local cleanup_after = module:get_option_string("archive_expires_after", "1w"); | |
| 44 local cleanup_interval = module:get_option_number("archive_cleanup_interval", 4 * 60 * 60); | |
| 45 local archive_item_limit = module:get_option_number("storage_archive_item_limit", archive.caps and archive.caps.quota or 1000); | |
| 43 if not archive.find then | 46 if not archive.find then |
| 44 error("mod_"..(archive._provided_by or archive.name and "storage_"..archive.name).." does not support archiving\n" | 47 error("mod_"..(archive._provided_by or archive.name and "storage_"..archive.name).." does not support archiving\n" |
| 45 .."See https://prosody.im/doc/storage and https://prosody.im/doc/archiving for more information"); | 48 .."See https://prosody.im/doc/storage and https://prosody.im/doc/archiving for more information"); |
| 46 end | 49 end |
| 47 local use_total = module:get_option_boolean("mam_include_total", true); | 50 local use_total = module:get_option_boolean("mam_include_total", true); |
| 115 return true; | 118 return true; |
| 116 end | 119 end |
| 117 qstart, qend = vstart, vend; | 120 qstart, qend = vstart, vend; |
| 118 end | 121 end |
| 119 | 122 |
| 120 module:log("debug", "Archive query, id %s with %s from %s until %s", | 123 module:log("debug", "Archive query by %s id=%s with=%s when=%s...%s", |
| 121 tostring(qid), qwith or "anyone", | 124 origin.username, |
| 122 qstart and timestamp(qstart) or "the dawn of time", | 125 qid or stanza.attr.id, |
| 123 qend and timestamp(qend) or "now"); | 126 qwith or "*", |
| 127 qstart and timestamp(qstart) or "", | |
| 128 qend and timestamp(qend) or ""); | |
| 124 | 129 |
| 125 -- RSM stuff | 130 -- RSM stuff |
| 126 local qset = rsm.get(query); | 131 local qset = rsm.get(query); |
| 127 local qmax = m_min(qset and qset.max or default_max_items, max_max_items); | 132 local qmax = m_min(qset and qset.max or default_max_items, max_max_items); |
| 128 local reverse = qset and qset.before or false; | 133 local reverse = qset and qset.before or false; |
| 129 local before, after = qset and qset.before, qset and qset.after; | 134 local before, after = qset and qset.before, qset and qset.after; |
| 130 if type(before) ~= "string" then before = nil; end | 135 if type(before) ~= "string" then before = nil; end |
| 136 if qset then | |
| 137 module:log("debug", "Archive query id=%s rsm=%q", qid or stanza.attr.id, qset); | |
| 138 end | |
| 131 | 139 |
| 132 -- Load all the data! | 140 -- Load all the data! |
| 133 local data, err = archive:find(origin.username, { | 141 local data, err = archive:find(origin.username, { |
| 134 start = qstart; ["end"] = qend; -- Time range | 142 start = qstart; ["end"] = qend; -- Time range |
| 135 with = qwith; | 143 with = qwith; |
| 138 reverse = reverse; | 146 reverse = reverse; |
| 139 total = use_total or qmax == 0; | 147 total = use_total or qmax == 0; |
| 140 }); | 148 }); |
| 141 | 149 |
| 142 if not data then | 150 if not data then |
| 143 origin.send(st.error_reply(stanza, "cancel", "internal-server-error", err)); | 151 module:log("debug", "Archive query id=%s failed: %s", qid or stanza.attr.id, err); |
| 152 if err == "item-not-found" then | |
| 153 origin.send(st.error_reply(stanza, "modify", "item-not-found")); | |
| 154 else | |
| 155 origin.send(st.error_reply(stanza, "cancel", "internal-server-error")); | |
| 156 end | |
| 144 return true; | 157 return true; |
| 145 end | 158 end |
| 146 local total = tonumber(err); | 159 local total = tonumber(err); |
| 147 | 160 |
| 148 local msg_reply_attr = { to = stanza.attr.from, from = stanza.attr.to }; | 161 local msg_reply_attr = { to = stanza.attr.from, from = stanza.attr.to }; |
| 187 origin.send(results[i]); | 200 origin.send(results[i]); |
| 188 end | 201 end |
| 189 first, last = last, first; | 202 first, last = last, first; |
| 190 end | 203 end |
| 191 | 204 |
| 192 -- That's all folks! | |
| 193 module:log("debug", "Archive query %s completed", tostring(qid)); | |
| 194 | |
| 195 origin.send(st.reply(stanza) | 205 origin.send(st.reply(stanza) |
| 196 :tag("fin", { xmlns = xmlns_mam, queryid = qid, complete = complete }) | 206 :tag("fin", { xmlns = xmlns_mam, queryid = qid, complete = complete }) |
| 197 :add_child(rsm.generate { | 207 :add_child(rsm.generate { |
| 198 first = first, last = last, count = total })); | 208 first = first, last = last, count = total })); |
| 209 | |
| 210 -- That's all folks! | |
| 211 module:log("debug", "Archive query id=%s completed, %d items returned", qid or stanza.attr.id, complete and count or count - 1); | |
| 199 return true; | 212 return true; |
| 200 end); | 213 end); |
| 201 | 214 |
| 202 local function has_in_roster(user, who) | 215 local function has_in_roster(user, who) |
| 203 local roster = rm_load_roster(user, host); | 216 local roster = rm_load_roster(user, host); |
| 211 module:log("debug", "%s@%s does not exist", user, host) | 224 module:log("debug", "%s@%s does not exist", user, host) |
| 212 return false; | 225 return false; |
| 213 end | 226 end |
| 214 local prefs = get_prefs(user); | 227 local prefs = get_prefs(user); |
| 215 local rule = prefs[who]; | 228 local rule = prefs[who]; |
| 216 module:log("debug", "%s's rule for %s is %s", user, who, tostring(rule)); | 229 module:log("debug", "%s's rule for %s is %s", user, who, rule); |
| 217 if rule ~= nil then | 230 if rule ~= nil then |
| 218 return rule; | 231 return rule; |
| 219 end | 232 end |
| 220 -- Below could be done by a metatable | 233 -- Below could be done by a metatable |
| 221 local default = prefs[false]; | 234 local default = prefs[false]; |
| 222 module:log("debug", "%s's default rule is %s", user, tostring(default)); | 235 module:log("debug", "%s's default rule is %s", user, default); |
| 223 if default == "roster" then | 236 if default == "roster" then |
| 224 return has_in_roster(user, who); | 237 return has_in_roster(user, who); |
| 225 end | 238 end |
| 226 return default; | 239 return default; |
| 227 end | 240 end |
| 295 -- Check with the users preferences | 308 -- Check with the users preferences |
| 296 if shall_store(store_user, with) then | 309 if shall_store(store_user, with) then |
| 297 log("debug", "Archiving stanza: %s", stanza:top_tag()); | 310 log("debug", "Archiving stanza: %s", stanza:top_tag()); |
| 298 | 311 |
| 299 -- And stash it | 312 -- And stash it |
| 300 local ok = archive:append(store_user, nil, clone_for_storage, time_now(), with); | 313 local time = time_now(); |
| 314 local ok, err = archive:append(store_user, nil, clone_for_storage, time, with); | |
| 315 if not ok and err == "quota-limit" then | |
| 316 if type(cleanup_after) == "number" then | |
| 317 module:log("debug", "User '%s' over quota, cleaning archive", store_user); | |
| 318 local cleaned = archive:delete(store_user, { | |
| 319 ["end"] = (os.time() - cleanup_after); | |
| 320 }); | |
| 321 if cleaned then | |
| 322 ok, err = archive:append(store_user, nil, clone_for_storage, time, with); | |
| 323 end | |
| 324 end | |
| 325 if not ok and (archive.caps and archive.caps.truncate) then | |
| 326 module:log("debug", "User '%s' over quota, truncating archive", store_user); | |
| 327 local truncated = archive:delete(store_user, { | |
| 328 truncate = archive_item_limit - 1; | |
| 329 }); | |
| 330 if truncated then | |
| 331 ok, err = archive:append(store_user, nil, clone_for_storage, time, with); | |
| 332 end | |
| 333 end | |
| 334 end | |
| 301 if ok then | 335 if ok then |
| 302 local clone_for_other_handlers = st.clone(stanza); | 336 local clone_for_other_handlers = st.clone(stanza); |
| 303 local id = ok; | 337 local id = ok; |
| 304 clone_for_other_handlers:tag("stanza-id", { xmlns = xmlns_st_id, by = store_user.."@"..host, id = id }):up(); | 338 clone_for_other_handlers:tag("stanza-id", { xmlns = xmlns_st_id, by = store_user.."@"..host, id = id }):up(); |
| 305 event.stanza = clone_for_other_handlers; | 339 event.stanza = clone_for_other_handlers; |
| 321 end | 355 end |
| 322 | 356 |
| 323 module:hook("pre-message/bare", strip_stanza_id_after_other_events, -1); | 357 module:hook("pre-message/bare", strip_stanza_id_after_other_events, -1); |
| 324 module:hook("pre-message/full", strip_stanza_id_after_other_events, -1); | 358 module:hook("pre-message/full", strip_stanza_id_after_other_events, -1); |
| 325 | 359 |
| 326 local cleanup_after = module:get_option_string("archive_expires_after", "1w"); | |
| 327 local cleanup_interval = module:get_option_number("archive_cleanup_interval", 4 * 60 * 60); | |
| 328 if cleanup_after ~= "never" then | 360 if cleanup_after ~= "never" then |
| 329 local cleanup_storage = module:open_store("archive_cleanup"); | 361 local cleanup_storage = module:open_store("archive_cleanup"); |
| 330 local cleanup_map = module:open_store("archive_cleanup", "map"); | 362 local cleanup_map = module:open_store("archive_cleanup", "map"); |
| 331 | 363 |
| 332 local day = 86400; | 364 local day = 86400; |
| 357 local ok = cleanup_map:set(date, username, true); | 389 local ok = cleanup_map:set(date, username, true); |
| 358 if ok then | 390 if ok then |
| 359 last_date:set(username, date); | 391 last_date:set(username, date); |
| 360 end | 392 end |
| 361 end | 393 end |
| 394 local cleanup_time = module:measure("cleanup", "times"); | |
| 362 | 395 |
| 363 cleanup_runner = require "util.async".runner(function () | 396 cleanup_runner = require "util.async".runner(function () |
| 397 local cleanup_done = cleanup_time(); | |
| 364 local users = {}; | 398 local users = {}; |
| 365 local cut_off = datestamp(os.time() - cleanup_after); | 399 local cut_off = datestamp(os.time() - cleanup_after); |
| 366 for date in cleanup_storage:users() do | 400 for date in cleanup_storage:users() do |
| 367 if date <= cut_off then | 401 if date <= cut_off then |
| 368 module:log("debug", "Messages from %q should be expired", date); | 402 module:log("debug", "Messages from %q should be expired", date); |
| 386 num_users = num_users + 1; | 420 num_users = num_users + 1; |
| 387 sum = sum + (tonumber(ok) or 0); | 421 sum = sum + (tonumber(ok) or 0); |
| 388 end | 422 end |
| 389 end | 423 end |
| 390 module:log("info", "Deleted %d expired messages for %d users", sum, num_users); | 424 module:log("info", "Deleted %d expired messages for %d users", sum, num_users); |
| 425 cleanup_done(); | |
| 391 end); | 426 end); |
| 392 | 427 |
| 393 cleanup_task = module:add_timer(1, function () | 428 cleanup_task = module:add_timer(1, function () |
| 394 cleanup_runner:run(true); | 429 cleanup_runner:run(true); |
| 395 return cleanup_interval; | 430 return cleanup_interval; |
