/* Copyright (c) 2006-2018 Dovecot authors, see the included COPYING file */
#include "lib.h"
#include "array.h"
#include "mail-index-modseq.h"
#include "mailbox-list-index-storage.h"
#include "mailbox-list-index.h"
#define CACHED_STATUS_ITEMS \
struct index_list_changes {
bool rec_changed;
bool msgs_changed;
bool hmodseq_changed;
bool vsize_changed;
bool first_saved_changed;
};
/* Should the STATUS information for this mailbox not be written to the
mailbox list index? */
static int
{
int ret;
return 0;
return -1;
/* mailbox not found */
return 0;
}
/* mailbox_list_index_refresh_later() was called.
Can't trust the index's contents. */
ret = 1;
/* our in-memory tree is out of sync */
ret = 1;
} else if (!status_check) {
/* this operation doesn't need the index to be up-to-date */
ret = 0;
} else T_BEGIN {
} T_END;
if (ret != 0) {
/* error / mailbox has changed. we'll need to sync it. */
if (ret < 0)
else
return ret < 0 ? -1 : 0;
}
return 1;
}
static int
enum mailbox_existence *existence_r)
{
int ret;
/* failure / not found. fallback to the real storage check
just in case to see if the mailbox was just created. */
}
if ((flags & MAILBOX_LIST_INDEX_FLAG_NONEXISTENT) != 0)
else if ((flags & MAILBOX_LIST_INDEX_FLAG_NOSELECT) != 0)
else
return 0;
}
struct mail_index_view *view,
struct mailbox_status *status_r,
struct mailbox_index_vsize *vsize_r)
{
const void *data;
bool expunged;
else {
if ((items & STATUS_UIDVALIDITY) != 0 &&
rec->uid_validity == 0)
else
if (mailbox_guid != NULL)
}
}
STATUS_RECENT | STATUS_UIDNEXT)) != 0) {
else {
}
}
if ((items & STATUS_HIGHESTMODSEQ) != 0) {
else
}
else
}
return ret;
}
static int
enum mailbox_status_items items,
struct mailbox_status *status_r)
{
int ret;
if (items == 0)
return 1;
if ((items & STATUS_UNSEEN) != 0 &&
/* can't get UNSEEN from list index, since each user has
different \Seen flags */
return 0;
}
return ret;
return ret;
}
static int
struct mailbox_status *status_r)
{
return 0;
/* nonsynced / error, fallback to doing it the slow way */
}
}
static int
{
int ret;
/* syncing wants to know the GUID for a new mailbox. */
return 0;
}
return ret;
ret = 0;
return ret;
}
{
int ret;
return ret;
/* mailbox is empty. its size has to be zero, regardless of
what the vsize header says. */
/* out of date vsize info */
ret = 0;
}
if (ret > 0)
return ret;
}
static int
struct mailbox_index_first_saved *first_saved_r)
{
const void *data;
bool expunged;
int ret;
return ret;
/* mailbox was empty the last time we updated this.
we'll need to verify if it still is. */
first_saved_r->timestamp = 0;
}
}
static int
enum mailbox_metadata_items items,
struct mailbox_metadata *metadata_r)
{
int ret;
/* if mailbox is already opened, don't bother using the values
in mailbox list index. they have a higher chance of being
wrong. */
return 0;
}
/* see if we have a chance of fulfilling this without opening
the mailbox. */
if ((noncached_items & MAILBOX_METADATA_PHYSICAL_SIZE) != 0 &&
if (noncached_items != 0)
return 0;
if ((items & MAILBOX_METADATA_GUID) != 0) {
return ret;
}
if ((items & (MAILBOX_METADATA_VIRTUAL_SIZE |
MAILBOX_METADATA_PHYSICAL_SIZE)) != 0) {
return ret;
if ((items & MAILBOX_METADATA_PHYSICAL_SIZE) != 0)
}
if ((items & MAILBOX_METADATA_FIRST_SAVE_DATE) != 0) {
/* start writing first_saved to mailbox list index if it wasn't
there already. */
return ret;
}
return 1;
}
static int
enum mailbox_metadata_items items,
struct mailbox_metadata *metadata_r)
{
return 0;
}
static void
struct mail_index_view *view,
struct index_list_changes *changes_r)
{
const void *data;
}
static bool
struct mail_index_view *list_view,
struct index_list_changes *changes_r)
{
return FALSE;
return FALSE;
/* get STATUS info using the latest data in index.
note that for shared mailboxes (with private indexes) this
also means that the unseen count is always the owner's
count, not what exists in the private index. */
else
/* modseqs not enabled yet, but we can't return 0 */
}
return TRUE;
}
static void
struct mail_index_view *list_view,
struct index_list_changes *changes)
{
const void *data;
bool expunged;
else
/* it's not in the index yet. we'll set it only if we've
just called MAILBOX_METADATA_FIRST_SAVE_DATE. */
} else {
}
}
static bool
struct index_list_changes *changes)
{
i_zero(&old_status);
if (MAILBOX_IS_NEVER_IN_INDEX(box)) {
/* check only UIDVALIDITY and GUID changes for INBOX */
return changes->rec_changed;
}
/* update highest-modseq only if they're ever been used */
} else {
}
}
static void
struct mail_index_transaction *list_trans,
const struct index_list_changes *changes)
{
struct mailbox_transaction_context *t;
int ret = 0;
break;
}
ret = -1;
break;
}
}
(void)mailbox_transaction_commit(&t);
}
if (ret == 0) {
&first_saved, NULL);
}
}
static void
struct mail_index_transaction *list_trans,
const struct index_list_changes *changes)
{
if (changes->rec_changed) {
const void *old_data;
bool expunged;
}
if (changes->msgs_changed) {
}
if (changes->hmodseq_changed) {
}
if (changes->vsize_changed) {
}
if (changes->first_saved_changed)
}
{
int ret;
return 0;
/* don't update status info while mailbox is being deleted.
especially not a good idea if we're rolling back a created
mailbox that somebody else had just created */
return 0;
}
/* refresh the mailbox list index once. we can't do this again after
locking, because it could trigger list syncing. */
/* first do a quick check while unlocked to see if anything changes */
ret = -1;
ret = 1;
else {
/* if backend state changed on the last check, update it here
now. we probably don't need to bother checking again if the
state had changed? */
}
if (ret <= 0) {
if (ret < 0)
return 0;
}
/* looks like there are some changes. now lock the list index and do
the whole thing all over again while locked. this guarantees
that we'll always write the latest state of the mailbox. */
&list_view, &list_trans, 0) < 0) {
return -1;
}
/* refresh to latest state of the mailbox now that we're locked */
return -1;
}
else {
}
}
if (mail_index_sync_commit(&list_sync_ctx) < 0) {
return -1;
}
return 0;
}
const struct mailbox_update *update)
{
int ret;
/* update the mailbox list index even if it has some other pending
changes. */
return;
mailbox_guid, NULL);
if (update->uid_validity != 0) {
}
guid_changed = TRUE;
}
if (guid_changed ||
update->uid_validity != 0 ||
update->min_next_uid != 0 ||
update->min_first_recent_uid != 0 ||
update->min_highest_modseq != 0) {
/* reset status counters to 0. let the syncing later figure out
their correct values. */
}
(void)mail_index_transaction_commit(&list_trans);
}
{
}
{
/* List index isn't open and sync changed nothing.
Don't bother opening the list index. */
return;
}
but might as well do it. */
(void)index_list_update_mailbox(box);
}
static int
struct mail_transaction_commit_changes *changes_r)
{
return -1;
t = NULL;
/* check all changes here, because e.g. vsize update is _OTHERS */
if (changes_r->changes_mask == 0)
return 0;
/* this transaction commit may have been done in error handling path
and the caller still wants to access the current error. make sure
that whatever we do here won't change the error. */
(void)index_list_update_mailbox(box);
return 0;
}
enum mailbox_info_flags *flags)
{
int ret;
/* our in-memory tree is out of sync */
ret = 1;
} else T_BEGIN {
} T_END;
if (ret != 0) {
/* error / not up to date. don't waste time with it. */
return;
}
*flags |= MAILBOX_MARKED;
else
*flags |= MAILBOX_UNMARKED;
}
{
v->exists = index_list_exists;
}
{
sizeof(struct mailbox_list_index_msgs_record),
sizeof(uint32_t));
sizeof(struct mailbox_index_vsize), sizeof(uint64_t));
sizeof(struct mailbox_index_first_saved), sizeof(uint32_t));
}