Mercurial > dovecot > core-2.2
changeset 19421:82e6a3baa001
Added mailbox { autoexpunge } setting.
This can be used to automatically expunge mails from specified mailboxes
after they're old enough. The expunges are done when the user is being
deinitialized. mailbox_list_index=yes should be enabled to have the best
performance with this setting.
Example:
namespace inbox {
mailbox Spam {
auto = create
special_use = \Junk
autoexpunge = 30d
}
}
author | Timo Sirainen <tss@iki.fi> |
---|---|
date | Fri, 27 Nov 2015 13:59:22 +0200 |
parents | db90e76f44dc |
children | 1217e5610865 |
files | src/lib-storage/Makefile.am src/lib-storage/list/mailbox-list-index-status.c src/lib-storage/list/mailbox-list-index.h src/lib-storage/mail-autoexpunge.c src/lib-storage/mail-autoexpunge.h src/lib-storage/mail-storage-private.h src/lib-storage/mail-storage-settings.c src/lib-storage/mail-storage-settings.h src/lib-storage/mail-storage.h src/lib-storage/mail-user.c |
diffstat | 10 files changed, 237 insertions(+), 6 deletions(-) [+] |
line wrap: on
line diff
--- a/src/lib-storage/Makefile.am Fri Nov 27 13:06:01 2015 +0200 +++ b/src/lib-storage/Makefile.am Fri Nov 27 13:59:22 2015 +0200 @@ -24,6 +24,7 @@ fail-mailbox.c \ fail-mail.c \ mail.c \ + mail-autoexpunge.c \ mail-copy.c \ mail-error.c \ mail-namespace.c \
--- a/src/lib-storage/list/mailbox-list-index-status.c Fri Nov 27 13:06:01 2015 +0200 +++ b/src/lib-storage/list/mailbox-list-index-status.c Fri Nov 27 13:59:22 2015 +0200 @@ -15,11 +15,13 @@ guid_128_t guid; uint32_t seq; struct mailbox_index_vsize vsize; + uint32_t first_uid; bool rec_changed; bool msgs_changed; bool hmodseq_changed; bool vsize_changed; + bool first_saved_changed; }; struct index_list_storage_module index_list_storage_module = @@ -268,6 +270,30 @@ } static int +index_list_get_cached_first_saved(struct mailbox *box, + struct mailbox_index_first_saved *first_saved_r) +{ + struct mailbox_list_index *ilist = INDEX_LIST_CONTEXT(box->list); + struct mail_index_view *view; + const void *data; + bool expunged; + uint32_t seq; + int ret; + + memset(first_saved_r, 0, sizeof(*first_saved_r)); + + if ((ret = index_list_open_view(box, &view, &seq)) <= 0) + return ret; + + mail_index_lookup_ext(view, seq, ilist->first_saved_ext_id, + &data, &expunged); + if (data != NULL) + memcpy(first_saved_r, data, sizeof(*first_saved_r)); + mail_index_view_close(&view); + return first_saved_r->timestamp != 0 ? 1 : 0; +} + +static int index_list_try_get_metadata(struct mailbox *box, enum mailbox_metadata_items items, struct mailbox_metadata *metadata_r) @@ -286,7 +312,8 @@ /* see if we have a chance of fulfilling this without opening the mailbox. */ noncached_items = items & ~(MAILBOX_METADATA_GUID | - MAILBOX_METADATA_VIRTUAL_SIZE); + MAILBOX_METADATA_VIRTUAL_SIZE | + MAILBOX_METADATA_FIRST_SAVE_DATE); if ((noncached_items & MAILBOX_METADATA_PHYSICAL_SIZE) != 0 && box->mail_vfuncs->get_physical_size == box->mail_vfuncs->get_virtual_size) @@ -306,6 +333,15 @@ if ((items & MAILBOX_METADATA_PHYSICAL_SIZE) != 0) metadata_r->physical_size = metadata_r->virtual_size; } + if ((items & MAILBOX_METADATA_FIRST_SAVE_DATE) != 0) { + struct mailbox_index_first_saved first_saved; + + if ((ret = index_list_get_cached_first_saved(box, &first_saved)) <= 0) + return ret; + metadata_r->first_save_date = + first_saved.timestamp == (uint32_t)-1 ? (time_t)-1 : + first_saved.timestamp; + } return 1; } @@ -386,6 +422,33 @@ return TRUE; } +static void +index_list_first_saved_update_changes(struct mailbox *box, + struct mail_index_view *list_view, + struct index_list_changes *changes) +{ + struct mailbox_list_index *ilist = INDEX_LIST_CONTEXT(box->list); + struct mailbox_index_first_saved first_saved; + const void *data; + bool expunged; + + mail_index_lookup_ext(list_view, changes->seq, + ilist->first_saved_ext_id, &data, &expunged); + if (data == NULL) + memset(&first_saved, 0, sizeof(first_saved)); + else + memcpy(&first_saved, data, sizeof(first_saved)); + if (mail_index_view_get_messages_count(box->view) > 0) + mail_index_lookup_uid(box->view, 1, &changes->first_uid); + if (first_saved.uid == 0 && first_saved.timestamp == 0) { + /* first time setting this */ + changes->first_saved_changed = TRUE; + } else { + changes->first_saved_changed = + changes->first_uid != first_saved.uid; + } +} + static bool index_list_has_changed(struct mailbox *box, struct mail_index_view *list_view, struct index_list_changes *changes) @@ -428,12 +491,57 @@ } if (memcmp(&old_vsize, &changes->vsize, sizeof(old_vsize)) != 0) changes->vsize_changed = TRUE; + index_list_first_saved_update_changes(box, list_view, changes); return changes->rec_changed || changes->msgs_changed || - changes->hmodseq_changed || changes->vsize_changed; + changes->hmodseq_changed || changes->vsize_changed || + changes->first_saved_changed; } static void +index_list_update_first_saved(struct mailbox *box, + struct mail_index_transaction *list_trans, + const struct index_list_changes *changes) +{ + struct mailbox_list_index *ilist = INDEX_LIST_CONTEXT(box->list); + struct mailbox_transaction_context *t; + struct mail *mail; + struct mailbox_index_first_saved first_saved; + uint32_t seq, messages_count; + time_t save_date; + int ret = 0; + + memset(&first_saved, 0, sizeof(first_saved)); + first_saved.uid = changes->first_uid; + first_saved.timestamp = (uint32_t)-1; + + if (changes->first_uid != 0) { + t = mailbox_transaction_begin(box, 0); + mail = mail_alloc(t, MAIL_FETCH_SAVE_DATE, NULL); + messages_count = mail_index_view_get_messages_count(box->view); + for (seq = 1; seq <= messages_count; seq++) { + mail_set_seq(mail, seq); + if (mail_get_save_date(mail, &save_date) == 0) { + first_saved.timestamp = save_date; + break; + } + if (mailbox_get_last_mail_error(box) != MAIL_ERROR_EXPUNGED) { + ret = -1; + break; + } + } + mail_free(&mail); + (void)mailbox_transaction_commit(&t); + } + if (ret == 0) { + mail_index_update_ext(list_trans, changes->seq, + ilist->first_saved_ext_id, + &first_saved, NULL); + } +} + + +static void index_list_update(struct mailbox *box, struct mail_index_view *list_view, struct mail_index_transaction *list_trans, const struct index_list_changes *changes) @@ -480,6 +588,8 @@ ilist->vsize_ext_id, &changes->vsize, NULL); } + if (changes->first_saved_changed) + index_list_update_first_saved(box, list_trans, changes); } static int index_list_update_mailbox(struct mailbox *box) @@ -705,4 +815,7 @@ ilist->vsize_ext_id = mail_index_ext_register(ilist->index, "vsize", 0, sizeof(struct mailbox_index_vsize), sizeof(uint64_t)); + ilist->first_saved_ext_id = + mail_index_ext_register(ilist->index, "1saved", 0, + sizeof(struct mailbox_index_first_saved), sizeof(uint32_t)); }
--- a/src/lib-storage/list/mailbox-list-index.h Fri Nov 27 13:06:01 2015 +0200 +++ b/src/lib-storage/list/mailbox-list-index.h Fri Nov 27 13:59:22 2015 +0200 @@ -89,7 +89,7 @@ const char *path; struct mail_index *index; uint32_t ext_id, msgs_ext_id, hmodseq_ext_id, subs_hdr_ext_id; - uint32_t vsize_ext_id; + uint32_t vsize_ext_id, first_saved_ext_id; struct timeval last_refresh_timeval; pool_t mailbox_pool;
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/lib-storage/mail-autoexpunge.c Fri Nov 27 13:59:22 2015 +0200 @@ -0,0 +1,93 @@ +/* Copyright (c) 2015 Dovecot authors, see the included COPYING file */ + +#include "lib.h" +#include "ioloop.h" +#include "mail-storage-private.h" +#include "mail-namespace.h" +#include "mail-user.h" +#include "mail-autoexpunge.h" + +static int mailbox_autoexpunge(struct mailbox *box, time_t expire_time) +{ + struct mailbox_transaction_context *t; + struct mail *mail; + struct mailbox_metadata metadata; + const struct mail_index_header *hdr; + uint32_t seq; + time_t timestamp; + int ret = 0; + + /* first try to check quickly from mailbox list index if we should + bother opening this mailbox. */ + if (mailbox_get_metadata(box, MAILBOX_METADATA_FIRST_SAVE_DATE, + &metadata) == 0) { + if (metadata.first_save_date == (time_t)-1 || + metadata.first_save_date > expire_time) + return 0; + } + + if (mailbox_sync(box, MAILBOX_SYNC_FLAG_FAST) < 0) { + if (mailbox_get_last_mail_error(box) == MAIL_ERROR_NOTFOUND) { + /* autocreated mailbox doesn't exist yet */ + return 0; + } + return -1; + } + + t = mailbox_transaction_begin(box, 0); + mail = mail_alloc(t, 0, NULL); + + hdr = mail_index_get_header(box->view); + for (seq = 1; seq <= hdr->messages_count; seq++) { + mail_set_seq(mail, seq); + if (mail_get_save_date(mail, ×tamp) == 0) { + if (timestamp > expire_time) + break; + mail_expunge(mail); + } else if (mailbox_get_last_mail_error(box) == MAIL_ERROR_EXPUNGED) { + /* already expunged */ + } else { + /* failed */ + ret = -1; + break; + } + } + mail_free(&mail); + if (mailbox_transaction_commit(&t) < 0) + ret = -1; + return ret; +} + +static void mail_namespace_autoexpunge(struct mail_namespace *ns) +{ + struct mailbox_settings *const *box_set; + struct mailbox *box; + time_t expire_time; + + if (!array_is_created(&ns->set->mailboxes)) + return; + + array_foreach(&ns->set->mailboxes, box_set) { + if ((*box_set)->autoexpunge == 0 || + ioloop_time < (*box_set)->autoexpunge) + continue; + expire_time = ioloop_time - (*box_set)->autoexpunge; + box = mailbox_alloc(ns->list, (*box_set)->name, 0); + if (mailbox_autoexpunge(box, expire_time) < 0) { + i_error("Failed to autoexpunge mailbox '%s': %s", + mailbox_get_vname(box), + mailbox_get_last_error(box, NULL)); + } + mailbox_free(&box); + } +} + +void mail_user_autoexpunge(struct mail_user *user) +{ + struct mail_namespace *ns; + + for (ns = user->namespaces; ns != NULL; ns = ns->next) { + if (ns->alias_for == NULL) + mail_namespace_autoexpunge(ns); + } +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/lib-storage/mail-autoexpunge.h Fri Nov 27 13:59:22 2015 +0200 @@ -0,0 +1,6 @@ +#ifndef MAIL_AUTOEXPUNGE_H +#define MAIL_AUTOEXPUNGE_H + +void mail_user_autoexpunge(struct mail_user *user); + +#endif
--- a/src/lib-storage/mail-storage-private.h Fri Nov 27 13:06:01 2015 +0200 +++ b/src/lib-storage/mail-storage-private.h Fri Nov 27 13:59:22 2015 +0200 @@ -271,6 +271,11 @@ uint32_t message_count; }; +struct mailbox_index_first_saved { + uint32_t uid; + uint32_t timestamp; +}; + struct mailbox { const char *name; /* mailbox's virtual name (from mail_namespace_get_vname()) */
--- a/src/lib-storage/mail-storage-settings.c Fri Nov 27 13:06:01 2015 +0200 +++ b/src/lib-storage/mail-storage-settings.c Fri Nov 27 13:59:22 2015 +0200 @@ -126,6 +126,7 @@ DEF(SET_STR, special_use), DEF(SET_STR, driver), DEF(SET_STR, comment), + DEF(SET_TIME, autoexpunge), SETTING_DEFINE_LIST_END }; @@ -137,7 +138,8 @@ MAILBOX_SET_AUTO_SUBSCRIBE, .special_use = "", .driver = "", - .comment = "" + .comment = "", + .autoexpunge = 0 }; const struct setting_parser_info mailbox_setting_parser_info = {
--- a/src/lib-storage/mail-storage-settings.h Fri Nov 27 13:06:01 2015 +0200 +++ b/src/lib-storage/mail-storage-settings.h Fri Nov 27 13:59:22 2015 +0200 @@ -81,6 +81,7 @@ const char *special_use; const char *driver; const char *comment; + unsigned int autoexpunge; }; struct mail_user_settings {
--- a/src/lib-storage/mail-storage.h Fri Nov 27 13:06:01 2015 +0200 +++ b/src/lib-storage/mail-storage.h Fri Nov 27 13:59:22 2015 +0200 @@ -91,10 +91,12 @@ MAILBOX_METADATA_CACHE_FIELDS = 0x04, MAILBOX_METADATA_PRECACHE_FIELDS = 0x08, MAILBOX_METADATA_BACKEND_NAMESPACE = 0x10, - MAILBOX_METADATA_PHYSICAL_SIZE = 0x20 + MAILBOX_METADATA_PHYSICAL_SIZE = 0x20, + MAILBOX_METADATA_FIRST_SAVE_DATE = 0x40 /* metadata items that require mailbox to be synced at least once. */ #define MAILBOX_METADATA_SYNC_ITEMS \ - (MAILBOX_METADATA_VIRTUAL_SIZE | MAILBOX_METADATA_PHYSICAL_SIZE) + (MAILBOX_METADATA_VIRTUAL_SIZE | MAILBOX_METADATA_PHYSICAL_SIZE | \ + MAILBOX_METADATA_FIRST_SAVE_DATE) }; enum mailbox_search_result_flags { @@ -275,10 +277,15 @@ uint64_t virtual_size; /* sum of physical size of all messages in mailbox */ uint64_t physical_size; + /* timestamp of when the first message was saved. + (time_t)-1 if there are no mails in the mailbox. */ + time_t first_save_date; + /* Fields that have "temp" or "yes" caching decision. */ const ARRAY_TYPE(mailbox_cache_field) *cache_fields; /* Fields that should be precached */ enum mail_fetch_field precache_fields; + /* imapc backend returns this based on the remote NAMESPACE reply, while currently other backends return "" and type the same as the mailbox's real namespace type */
--- a/src/lib-storage/mail-user.c Fri Nov 27 13:06:01 2015 +0200 +++ b/src/lib-storage/mail-user.c Fri Nov 27 13:59:22 2015 +0200 @@ -21,6 +21,7 @@ #include "mail-storage-service.h" #include "mail-namespace.h" #include "mail-storage.h" +#include "mail-autoexpunge.h" #include "mail-user.h" @@ -163,6 +164,8 @@ return; } + mail_user_autoexpunge(user); + user->deinitializing = TRUE; /* call deinit() with refcount=1, otherwise we may assert-crash in