mail-autoexpunge.c revision f5ddf3762f7fa7a389b91a6b8f5732a5d4a1b382
b9f30617c2c96d54acbc4f85ed17b939c4f28916Timo Sirainen/* Copyright (c) 2015-2017 Dovecot authors, see the included COPYING file */
a64adf62fa33f2463a86f990217b0c9078531a40Timo Sirainen
1358e2c58ce29231485a5cfa454756d429ad3d2cTimo Sirainen#include "lib.h"
a64adf62fa33f2463a86f990217b0c9078531a40Timo Sirainen#include "ioloop.h"
d176f84ce5ca2073f4dfbafb457b9c74f6bf0d76Timo Sirainen#include "file-create-locked.h"
a64adf62fa33f2463a86f990217b0c9078531a40Timo Sirainen#include "mailbox-list-iter.h"
a64adf62fa33f2463a86f990217b0c9078531a40Timo Sirainen#include "mail-storage-private.h"
a64adf62fa33f2463a86f990217b0c9078531a40Timo Sirainen#include "mail-namespace.h"
a64adf62fa33f2463a86f990217b0c9078531a40Timo Sirainen#include "mail-user.h"
a64adf62fa33f2463a86f990217b0c9078531a40Timo Sirainen#include "mail-autoexpunge.h"
d63b4241643b6014d49ff356f14e0f3ee43068a8Timo Sirainen
b215a8a123623782554a83f3025ef4e771bd8f01Timo Sirainen#define AUTOEXPUNGE_LOCK_FNAME "dovecot.autoexpunge.lock"
a64adf62fa33f2463a86f990217b0c9078531a40Timo Sirainen
a64adf62fa33f2463a86f990217b0c9078531a40Timo Sirainenstruct mailbox_autoexpunge_lock {
a64adf62fa33f2463a86f990217b0c9078531a40Timo Sirainen const char *path;
a64adf62fa33f2463a86f990217b0c9078531a40Timo Sirainen struct file_lock *lock;
a64adf62fa33f2463a86f990217b0c9078531a40Timo Sirainen int fd;
a64adf62fa33f2463a86f990217b0c9078531a40Timo Sirainen};
a64adf62fa33f2463a86f990217b0c9078531a40Timo Sirainen
a64adf62fa33f2463a86f990217b0c9078531a40Timo Sirainenstatic bool mailbox_autoexpunge_lock(struct mail_user *user,
a64adf62fa33f2463a86f990217b0c9078531a40Timo Sirainen struct mailbox_autoexpunge_lock *lock)
a64adf62fa33f2463a86f990217b0c9078531a40Timo Sirainen{
a64adf62fa33f2463a86f990217b0c9078531a40Timo Sirainen struct file_create_settings lock_set;
a64adf62fa33f2463a86f990217b0c9078531a40Timo Sirainen bool created;
02a0492affdff138f43903c19ca366363923044dTimo Sirainen const char *home, *error;
02a0492affdff138f43903c19ca366363923044dTimo Sirainen int ret;
a64adf62fa33f2463a86f990217b0c9078531a40Timo Sirainen
a64adf62fa33f2463a86f990217b0c9078531a40Timo Sirainen if (lock->fd != -1)
a64adf62fa33f2463a86f990217b0c9078531a40Timo Sirainen return TRUE;
a64adf62fa33f2463a86f990217b0c9078531a40Timo Sirainen
a64adf62fa33f2463a86f990217b0c9078531a40Timo Sirainen /* Try to lock the autoexpunging. If the lock already exists, another
a64adf62fa33f2463a86f990217b0c9078531a40Timo Sirainen process is already busy with expunging, so we don't have to do it.
a64adf62fa33f2463a86f990217b0c9078531a40Timo Sirainen The easiest place where to store the lock file to is the home
a64adf62fa33f2463a86f990217b0c9078531a40Timo Sirainen directory, but allow autoexpunging to work even if we can't get
a64adf62fa33f2463a86f990217b0c9078531a40Timo Sirainen it. The lock isn't really required; it 1) improves performance
a64adf62fa33f2463a86f990217b0c9078531a40Timo Sirainen so that multiple processes won't do the same work unnecessarily,
a64adf62fa33f2463a86f990217b0c9078531a40Timo Sirainen and 2) it helps to avoid duplicate mails being added with
a64adf62fa33f2463a86f990217b0c9078531a40Timo Sirainen lazy_expunge. */
a64adf62fa33f2463a86f990217b0c9078531a40Timo Sirainen if ((ret = mail_user_get_home(user, &home)) > 0) {
a64adf62fa33f2463a86f990217b0c9078531a40Timo Sirainen const struct mail_storage_settings *mail_set =
6c2ce1d5bf17b21e804a079eb0f973b7ab83e0d8Timo Sirainen mail_user_set_get_storage_set(user);
a64adf62fa33f2463a86f990217b0c9078531a40Timo Sirainen i_zero(&lock_set);
a64adf62fa33f2463a86f990217b0c9078531a40Timo Sirainen lock_set.lock_method = mail_set->parsed_lock_method,
a64adf62fa33f2463a86f990217b0c9078531a40Timo Sirainen lock->path = t_strdup_printf("%s/"AUTOEXPUNGE_LOCK_FNAME, home);
a64adf62fa33f2463a86f990217b0c9078531a40Timo Sirainen lock->fd = file_create_locked(lock->path, &lock_set,
a64adf62fa33f2463a86f990217b0c9078531a40Timo Sirainen &lock->lock, &created, &error);
a64adf62fa33f2463a86f990217b0c9078531a40Timo Sirainen if (lock->fd == -1) {
97db4761382024093f441e4bc78ba8b6a056504dTimo Sirainen if (errno == EAGAIN)
a64adf62fa33f2463a86f990217b0c9078531a40Timo Sirainen return FALSE;
a64adf62fa33f2463a86f990217b0c9078531a40Timo Sirainen if (errno != ENOENT)
a64adf62fa33f2463a86f990217b0c9078531a40Timo Sirainen i_error("autoexpunge: Couldn't lock %s: %s", lock->path, error);
a64adf62fa33f2463a86f990217b0c9078531a40Timo Sirainen return TRUE;
a64adf62fa33f2463a86f990217b0c9078531a40Timo Sirainen }
02a0492affdff138f43903c19ca366363923044dTimo Sirainen } else if (ret == 0) {
02a0492affdff138f43903c19ca366363923044dTimo Sirainen i_warning("autoexpunge: User has no home directory, can't lock");
a64adf62fa33f2463a86f990217b0c9078531a40Timo Sirainen }
a64adf62fa33f2463a86f990217b0c9078531a40Timo Sirainen return TRUE;
a64adf62fa33f2463a86f990217b0c9078531a40Timo Sirainen}
a64adf62fa33f2463a86f990217b0c9078531a40Timo Sirainen
a64adf62fa33f2463a86f990217b0c9078531a40Timo Sirainenstatic int
a64adf62fa33f2463a86f990217b0c9078531a40Timo Sirainenmailbox_autoexpunge(struct mailbox *box, unsigned int interval_time,
a64adf62fa33f2463a86f990217b0c9078531a40Timo Sirainen unsigned int max_mails)
a64adf62fa33f2463a86f990217b0c9078531a40Timo Sirainen{
a64adf62fa33f2463a86f990217b0c9078531a40Timo Sirainen struct mailbox_transaction_context *t;
a64adf62fa33f2463a86f990217b0c9078531a40Timo Sirainen struct mail *mail;
a64adf62fa33f2463a86f990217b0c9078531a40Timo Sirainen struct mailbox_metadata metadata;
a64adf62fa33f2463a86f990217b0c9078531a40Timo Sirainen const struct mail_index_header *hdr;
a64adf62fa33f2463a86f990217b0c9078531a40Timo Sirainen struct mailbox_status status;
a64adf62fa33f2463a86f990217b0c9078531a40Timo Sirainen uint32_t seq;
6c2ce1d5bf17b21e804a079eb0f973b7ab83e0d8Timo Sirainen time_t timestamp, expire_time, last_rename_stamp = 0;
a64adf62fa33f2463a86f990217b0c9078531a40Timo Sirainen const void *data;
a64adf62fa33f2463a86f990217b0c9078531a40Timo Sirainen size_t size;
a64adf62fa33f2463a86f990217b0c9078531a40Timo Sirainen int ret = 0;
1cfdcb36985904eff281fc6d7ea2d13b3c375980Timo Sirainen
a64adf62fa33f2463a86f990217b0c9078531a40Timo Sirainen if ((unsigned int)ioloop_time < interval_time)
a64adf62fa33f2463a86f990217b0c9078531a40Timo Sirainen expire_time = 0;
a64adf62fa33f2463a86f990217b0c9078531a40Timo Sirainen else
a64adf62fa33f2463a86f990217b0c9078531a40Timo Sirainen expire_time = ioloop_time - interval_time;
b215a8a123623782554a83f3025ef4e771bd8f01Timo Sirainen
383d0e8c24451468d6bea17e4b55d74de744abe6Timo Sirainen /* first try to check quickly from mailbox list index if we should
383d0e8c24451468d6bea17e4b55d74de744abe6Timo Sirainen bother opening this mailbox. */
383d0e8c24451468d6bea17e4b55d74de744abe6Timo Sirainen if (mailbox_get_status(box, STATUS_MESSAGES, &status) < 0) {
383d0e8c24451468d6bea17e4b55d74de744abe6Timo Sirainen if (mailbox_get_last_mail_error(box) == MAIL_ERROR_NOTFOUND) {
b215a8a123623782554a83f3025ef4e771bd8f01Timo Sirainen /* autocreated mailbox doesn't exist yet */
a64adf62fa33f2463a86f990217b0c9078531a40Timo Sirainen return 0;
a64adf62fa33f2463a86f990217b0c9078531a40Timo Sirainen }
7ceee001c11865a88a311c50c4430867b2d32ce6Timo Sirainen return -1;
7ceee001c11865a88a311c50c4430867b2d32ce6Timo Sirainen }
7ceee001c11865a88a311c50c4430867b2d32ce6Timo Sirainen if (interval_time == 0 && status.messages <= max_mails)
7ceee001c11865a88a311c50c4430867b2d32ce6Timo Sirainen return 0;
7ceee001c11865a88a311c50c4430867b2d32ce6Timo Sirainen
b215a8a123623782554a83f3025ef4e771bd8f01Timo Sirainen if (max_mails == 0 || status.messages <= max_mails) {
b215a8a123623782554a83f3025ef4e771bd8f01Timo Sirainen if (mailbox_get_metadata(box, MAILBOX_METADATA_FIRST_SAVE_DATE,
a64adf62fa33f2463a86f990217b0c9078531a40Timo Sirainen &metadata) < 0)
b215a8a123623782554a83f3025ef4e771bd8f01Timo Sirainen return -1;
b215a8a123623782554a83f3025ef4e771bd8f01Timo Sirainen if (metadata.first_save_date == (time_t)-1 ||
a64adf62fa33f2463a86f990217b0c9078531a40Timo Sirainen metadata.first_save_date > expire_time)
ce8a6f53ea3ce91b759a54b771e1779564de19a1Timo Sirainen return 0;
ce8a6f53ea3ce91b759a54b771e1779564de19a1Timo Sirainen }
a64adf62fa33f2463a86f990217b0c9078531a40Timo Sirainen
a64adf62fa33f2463a86f990217b0c9078531a40Timo Sirainen if (mailbox_sync(box, MAILBOX_SYNC_FLAG_FAST) < 0)
02a0492affdff138f43903c19ca366363923044dTimo Sirainen return -1;
02a0492affdff138f43903c19ca366363923044dTimo Sirainen
a64adf62fa33f2463a86f990217b0c9078531a40Timo Sirainen mail_index_get_header_ext(box->view, box->box_last_rename_stamp_ext_id,
a64adf62fa33f2463a86f990217b0c9078531a40Timo Sirainen &data, &size);
02a0492affdff138f43903c19ca366363923044dTimo Sirainen
02a0492affdff138f43903c19ca366363923044dTimo Sirainen if (size >= sizeof(uint32_t))
1cbc0c6372d24168962698c5f4d3e15df8943ebfTimo Sirainen last_rename_stamp = *(const uint32_t*)data;
1cbc0c6372d24168962698c5f4d3e15df8943ebfTimo Sirainen
1cbc0c6372d24168962698c5f4d3e15df8943ebfTimo Sirainen t = mailbox_transaction_begin(box, 0);
1cbc0c6372d24168962698c5f4d3e15df8943ebfTimo Sirainen mail = mail_alloc(t, 0, NULL);
a64adf62fa33f2463a86f990217b0c9078531a40Timo Sirainen
a64adf62fa33f2463a86f990217b0c9078531a40Timo Sirainen hdr = mail_index_get_header(box->view);
a64adf62fa33f2463a86f990217b0c9078531a40Timo Sirainen for (seq = 1; seq <= hdr->messages_count; seq++) {
1cbc0c6372d24168962698c5f4d3e15df8943ebfTimo Sirainen mail_set_seq(mail, seq);
b215a8a123623782554a83f3025ef4e771bd8f01Timo Sirainen if (max_mails > 0 && hdr->messages_count - seq + 1 > max_mails) {
b215a8a123623782554a83f3025ef4e771bd8f01Timo Sirainen /* max_mails is still being reached -> expunge.
b215a8a123623782554a83f3025ef4e771bd8f01Timo Sirainen don't even check saved-dates before we're
a64adf62fa33f2463a86f990217b0c9078531a40Timo Sirainen below max_mails. */
a64adf62fa33f2463a86f990217b0c9078531a40Timo Sirainen mail_expunge(mail);
1cbc0c6372d24168962698c5f4d3e15df8943ebfTimo Sirainen } else if (interval_time == 0) {
a64adf62fa33f2463a86f990217b0c9078531a40Timo Sirainen /* only max_mails is used. nothing further to do. */
a64adf62fa33f2463a86f990217b0c9078531a40Timo Sirainen break;
a64adf62fa33f2463a86f990217b0c9078531a40Timo Sirainen } else if (mail_get_save_date(mail, &timestamp) == 0) {
a64adf62fa33f2463a86f990217b0c9078531a40Timo Sirainen if (I_MAX(last_rename_stamp, timestamp) > expire_time)
97db4761382024093f441e4bc78ba8b6a056504dTimo Sirainen break;
a64adf62fa33f2463a86f990217b0c9078531a40Timo Sirainen mail_expunge(mail);
b215a8a123623782554a83f3025ef4e771bd8f01Timo Sirainen } else if (mailbox_get_last_mail_error(box) == MAIL_ERROR_EXPUNGED) {
a64adf62fa33f2463a86f990217b0c9078531a40Timo Sirainen /* already expunged */
a64adf62fa33f2463a86f990217b0c9078531a40Timo Sirainen } else {
97db4761382024093f441e4bc78ba8b6a056504dTimo Sirainen /* failed */
a64adf62fa33f2463a86f990217b0c9078531a40Timo Sirainen ret = -1;
a64adf62fa33f2463a86f990217b0c9078531a40Timo Sirainen break;
a64adf62fa33f2463a86f990217b0c9078531a40Timo Sirainen }
a64adf62fa33f2463a86f990217b0c9078531a40Timo Sirainen }
a64adf62fa33f2463a86f990217b0c9078531a40Timo Sirainen mail_free(&mail);
b215a8a123623782554a83f3025ef4e771bd8f01Timo Sirainen if (mailbox_transaction_commit(&t) < 0)
b215a8a123623782554a83f3025ef4e771bd8f01Timo Sirainen ret = -1;
b215a8a123623782554a83f3025ef4e771bd8f01Timo Sirainen return ret;
b215a8a123623782554a83f3025ef4e771bd8f01Timo Sirainen}
b215a8a123623782554a83f3025ef4e771bd8f01Timo Sirainen
b215a8a123623782554a83f3025ef4e771bd8f01Timo Sirainenstatic void
b215a8a123623782554a83f3025ef4e771bd8f01Timo Sirainenmailbox_autoexpunge_set(struct mail_namespace *ns, const char *vname,
b215a8a123623782554a83f3025ef4e771bd8f01Timo Sirainen unsigned int autoexpunge,
b215a8a123623782554a83f3025ef4e771bd8f01Timo Sirainen unsigned int autoexpunge_max_mails)
b215a8a123623782554a83f3025ef4e771bd8f01Timo Sirainen{
b215a8a123623782554a83f3025ef4e771bd8f01Timo Sirainen struct mailbox *box;
b215a8a123623782554a83f3025ef4e771bd8f01Timo Sirainen
b215a8a123623782554a83f3025ef4e771bd8f01Timo Sirainen /* autoexpunge is configured by admin, so we can safely ignore
b215a8a123623782554a83f3025ef4e771bd8f01Timo Sirainen any ACLs the user might normally have against expunging in
b215a8a123623782554a83f3025ef4e771bd8f01Timo Sirainen the mailbox. */
b215a8a123623782554a83f3025ef4e771bd8f01Timo Sirainen box = mailbox_alloc(ns->list, vname, MAILBOX_FLAG_IGNORE_ACLS);
a64adf62fa33f2463a86f990217b0c9078531a40Timo Sirainen if (mailbox_autoexpunge(box, autoexpunge, autoexpunge_max_mails) < 0) {
b215a8a123623782554a83f3025ef4e771bd8f01Timo Sirainen i_error("Failed to autoexpunge mailbox '%s': %s",
a64adf62fa33f2463a86f990217b0c9078531a40Timo Sirainen mailbox_get_vname(box),
97db4761382024093f441e4bc78ba8b6a056504dTimo Sirainen mailbox_get_last_error(box, NULL));
97db4761382024093f441e4bc78ba8b6a056504dTimo Sirainen }
97db4761382024093f441e4bc78ba8b6a056504dTimo Sirainen mailbox_free(&box);
97db4761382024093f441e4bc78ba8b6a056504dTimo Sirainen}
a64adf62fa33f2463a86f990217b0c9078531a40Timo Sirainen
1358e2c58ce29231485a5cfa454756d429ad3d2cTimo Sirainenstatic void
b215a8a123623782554a83f3025ef4e771bd8f01Timo Sirainenmailbox_autoexpunge_wildcards(struct mail_namespace *ns,
d176f84ce5ca2073f4dfbafb457b9c74f6bf0d76Timo Sirainen const struct mailbox_settings *set)
97db4761382024093f441e4bc78ba8b6a056504dTimo Sirainen{
a64adf62fa33f2463a86f990217b0c9078531a40Timo Sirainen struct mailbox_list_iterate_context *iter;
1358e2c58ce29231485a5cfa454756d429ad3d2cTimo Sirainen const struct mailbox_info *info;
7ceee001c11865a88a311c50c4430867b2d32ce6Timo Sirainen const char *iter_name;
1358e2c58ce29231485a5cfa454756d429ad3d2cTimo Sirainen
1358e2c58ce29231485a5cfa454756d429ad3d2cTimo Sirainen iter_name = t_strconcat(ns->prefix, set->name, NULL);
1358e2c58ce29231485a5cfa454756d429ad3d2cTimo Sirainen iter = mailbox_list_iter_init(ns->list, iter_name,
97db4761382024093f441e4bc78ba8b6a056504dTimo Sirainen MAILBOX_LIST_ITER_NO_AUTO_BOXES |
97db4761382024093f441e4bc78ba8b6a056504dTimo Sirainen MAILBOX_LIST_ITER_SKIP_ALIASES |
97db4761382024093f441e4bc78ba8b6a056504dTimo Sirainen MAILBOX_LIST_ITER_RETURN_NO_FLAGS);
97db4761382024093f441e4bc78ba8b6a056504dTimo Sirainen while ((info = mailbox_list_iter_next(iter)) != NULL) T_BEGIN {
97db4761382024093f441e4bc78ba8b6a056504dTimo Sirainen mailbox_autoexpunge_set(ns, info->vname, set->autoexpunge,
97db4761382024093f441e4bc78ba8b6a056504dTimo Sirainen set->autoexpunge_max_mails);
97db4761382024093f441e4bc78ba8b6a056504dTimo Sirainen } T_END;
97db4761382024093f441e4bc78ba8b6a056504dTimo Sirainen if (mailbox_list_iter_deinit(&iter) < 0) {
1358e2c58ce29231485a5cfa454756d429ad3d2cTimo Sirainen i_error("Failed to iterate autoexpunge mailboxes '%s': %s",
d176f84ce5ca2073f4dfbafb457b9c74f6bf0d76Timo Sirainen iter_name, mailbox_list_get_last_error(ns->list, NULL));
b215a8a123623782554a83f3025ef4e771bd8f01Timo Sirainen }
d176f84ce5ca2073f4dfbafb457b9c74f6bf0d76Timo Sirainen}
97db4761382024093f441e4bc78ba8b6a056504dTimo Sirainen
97db4761382024093f441e4bc78ba8b6a056504dTimo Sirainenstatic bool
97db4761382024093f441e4bc78ba8b6a056504dTimo Sirainenmail_namespace_autoexpunge(struct mail_namespace *ns,
97db4761382024093f441e4bc78ba8b6a056504dTimo Sirainen struct mailbox_autoexpunge_lock *lock)
a64adf62fa33f2463a86f990217b0c9078531a40Timo Sirainen{
struct mailbox_settings *const *box_set;
const char *vname;
if (!array_is_created(&ns->set->mailboxes))
return TRUE;
array_foreach(&ns->set->mailboxes, box_set) {
if ((*box_set)->autoexpunge == 0 &&
(*box_set)->autoexpunge_max_mails == 0)
continue;
if (!mailbox_autoexpunge_lock(ns->user, lock))
return FALSE;
if (strpbrk((*box_set)->name, "*?") != NULL)
mailbox_autoexpunge_wildcards(ns, *box_set);
else {
if ((*box_set)->name[0] == '\0' && ns->prefix_len > 0 &&
ns->prefix[ns->prefix_len-1] == mail_namespace_get_sep(ns))
vname = t_strndup(ns->prefix, ns->prefix_len - 1);
else
vname = t_strconcat(ns->prefix, (*box_set)->name, NULL);
mailbox_autoexpunge_set(ns, vname, (*box_set)->autoexpunge,
(*box_set)->autoexpunge_max_mails);
}
}
return TRUE;
}
void mail_user_autoexpunge(struct mail_user *user)
{
struct mailbox_autoexpunge_lock lock = { .fd = -1 };
struct mail_namespace *ns;
for (ns = user->namespaces; ns != NULL; ns = ns->next) {
if (ns->alias_for == NULL) {
if (!mail_namespace_autoexpunge(ns, &lock))
break;
}
}
if (lock.fd != -1) {
i_unlink(lock.path);
i_close_fd(&lock.fd);
file_lock_free(&lock.lock);
}
}