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 =
return ret;
}
+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,
/* 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)
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;
}
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)
}
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,
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)
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));
}
--- /dev/null
+/* 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);
+ }
+}
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 {
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 */