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;