diff options
Diffstat (limited to 'src/lib-storage/index/index-status.c')
-rw-r--r-- | src/lib-storage/index/index-status.c | 344 |
1 files changed, 344 insertions, 0 deletions
diff --git a/src/lib-storage/index/index-status.c b/src/lib-storage/index/index-status.c new file mode 100644 index 0000000..9d751b9 --- /dev/null +++ b/src/lib-storage/index/index-status.c @@ -0,0 +1,344 @@ +/* Copyright (c) 2002-2018 Dovecot authors, see the included COPYING file */ + +#include "lib.h" +#include "array.h" +#include "mail-cache.h" +#include "mail-index-modseq.h" +#include "mailbox-recent-flags.h" +#include "index-storage.h" + +static void +get_last_cached_seq(struct mailbox *box, uint32_t *last_cached_seq_r) +{ + const struct mail_index_header *hdr; + struct mail_cache_view *cache_view; + uint32_t seq; + + *last_cached_seq_r = 0; + if (!mail_cache_exists(box->cache)) + return; + + cache_view = mail_cache_view_open(box->cache, box->view); + hdr = mail_index_get_header(box->view); + for (seq = hdr->messages_count; seq > 0; seq--) { + if (mail_cache_field_exists_any(cache_view, seq)) { + *last_cached_seq_r = seq; + break; + } + } + mail_cache_view_close(&cache_view); +} + +int index_storage_get_status(struct mailbox *box, + enum mailbox_status_items items, + struct mailbox_status *status_r) +{ + if (items == 0) { + /* caller could have wanted only e.g. mailbox_status.have_* + flags */ + return 0; + } + + if (!box->opened) { + if (mailbox_open(box) < 0) + return -1; + if (mailbox_sync(box, MAILBOX_SYNC_FLAG_FAST) < 0) + return -1; + } + index_storage_get_open_status(box, items, status_r); + return 0; +} + +static unsigned int index_storage_count_pvt_unseen(struct mailbox *box) +{ + const struct mail_index_record *pvt_rec; + uint32_t shared_seq, pvt_seq, shared_count, pvt_count; + uint32_t shared_uid; + unsigned int unseen_count = 0; + + /* we can't trust private index to be up to date. we'll need to go + through the shared index and for each existing mail lookup its + private flags. if a mail doesn't exist in private index then its + flags are 0. */ + shared_count = mail_index_view_get_messages_count(box->view); + pvt_count = mail_index_view_get_messages_count(box->view_pvt); + shared_seq = pvt_seq = 1; + while (shared_seq <= shared_count && pvt_seq <= pvt_count) { + mail_index_lookup_uid(box->view, shared_seq, &shared_uid); + pvt_rec = mail_index_lookup(box->view_pvt, pvt_seq); + + if (shared_uid == pvt_rec->uid) { + if ((pvt_rec->flags & MAIL_SEEN) == 0) + unseen_count++; + shared_seq++; pvt_seq++; + } else if (shared_uid < pvt_rec->uid) { + shared_seq++; + } else { + pvt_seq++; + } + } + unseen_count += (shared_count+1) - shared_seq; + return unseen_count; +} + +static uint32_t index_storage_find_first_pvt_unseen_seq(struct mailbox *box) +{ + const struct mail_index_header *pvt_hdr; + const struct mail_index_record *pvt_rec; + uint32_t pvt_seq, pvt_count, shared_seq, seq2; + + pvt_count = mail_index_view_get_messages_count(box->view_pvt); + mail_index_lookup_first(box->view_pvt, 0, MAIL_SEEN, &pvt_seq); + if (pvt_seq == 0) + pvt_seq = pvt_count+1; + for (; pvt_seq <= pvt_count; pvt_seq++) { + pvt_rec = mail_index_lookup(box->view_pvt, pvt_seq); + if ((pvt_rec->flags & MAIL_SEEN) == 0 && + mail_index_lookup_seq(box->view, pvt_rec->uid, &shared_seq)) + return shared_seq; + } + /* if shared index has any messages that don't exist in private index, + the first of them is the first unseen message */ + pvt_hdr = mail_index_get_header(box->view_pvt); + if (mail_index_lookup_seq_range(box->view, + pvt_hdr->next_uid, (uint32_t)-1, + &shared_seq, &seq2)) + return shared_seq; + return 0; +} + +void index_storage_get_open_status(struct mailbox *box, + enum mailbox_status_items items, + struct mailbox_status *status_r) +{ + const struct mail_index_header *hdr; + + /* we can get most of the status items without any trouble */ + hdr = mail_index_get_header(box->view); + status_r->messages = hdr->messages_count; + if ((items & STATUS_RECENT) != 0) { + if ((box->flags & MAILBOX_FLAG_DROP_RECENT) != 0) { + /* recent flags are set and dropped by the previous + sync while index was locked. if we updated the + recent flags here we'd have a race condition. */ + i_assert(box->synced); + } else { + /* make sure recent count is set, in case we haven't + synced yet */ + index_sync_update_recent_count(box); + } + status_r->recent = mailbox_recent_flags_count(box); + i_assert(status_r->recent <= status_r->messages); + } + if ((items & STATUS_UNSEEN) != 0) { + if (box->view_pvt == NULL || + (mailbox_get_private_flags_mask(box) & MAIL_SEEN) == 0) { + status_r->unseen = hdr->messages_count - + hdr->seen_messages_count; + } else { + status_r->unseen = index_storage_count_pvt_unseen(box); + } + } + status_r->uidvalidity = hdr->uid_validity; + status_r->uidnext = hdr->next_uid; + status_r->first_recent_uid = hdr->first_recent_uid; + if ((items & STATUS_HIGHESTMODSEQ) != 0) { + status_r->nonpermanent_modseqs = + mail_index_is_in_memory(box->index); + status_r->no_modseq_tracking = + !mail_index_have_modseq_tracking(box->index); + status_r->highest_modseq = + mail_index_modseq_get_highest(box->view); + if (status_r->highest_modseq == 0) { + /* modseqs not enabled yet, but we can't return 0 */ + status_r->highest_modseq = 1; + } + } + if ((items & STATUS_HIGHESTPVTMODSEQ) != 0 && box->view_pvt != NULL) { + status_r->highest_pvt_modseq = + mail_index_modseq_get_highest(box->view_pvt); + if (status_r->highest_pvt_modseq == 0) { + /* modseqs not enabled yet, but we can't return 0 */ + status_r->highest_pvt_modseq = 1; + } + } + + if ((items & STATUS_FIRST_UNSEEN_SEQ) != 0) { + if (box->view_pvt == NULL || + (mailbox_get_private_flags_mask(box) & MAIL_SEEN) == 0) { + mail_index_lookup_first(box->view, 0, MAIL_SEEN, + &status_r->first_unseen_seq); + } else { + status_r->first_unseen_seq = + index_storage_find_first_pvt_unseen_seq(box); + } + } + if ((items & STATUS_LAST_CACHED_SEQ) != 0) + get_last_cached_seq(box, &status_r->last_cached_seq); + + if ((items & STATUS_KEYWORDS) != 0) + status_r->keywords = mail_index_get_keywords(box->index); + if ((items & STATUS_PERMANENT_FLAGS) != 0) { + if (!mailbox_is_readonly(box)) { + status_r->permanent_flags = MAIL_FLAGS_NONRECENT; + status_r->permanent_keywords = TRUE; + status_r->allow_new_keywords = + !box->disallow_new_keywords; + } + status_r->flags = MAIL_FLAGS_NONRECENT; + } +} + +static void +get_metadata_cache_fields(struct mailbox *box, + struct mailbox_metadata *metadata_r) +{ + const struct mail_cache_field *fields; + enum mail_cache_decision_type dec; + ARRAY_TYPE(mailbox_cache_field) *cache_fields; + struct mailbox_cache_field *cf; + unsigned int i, count; + + fields = mail_cache_register_get_list(box->cache, + pool_datastack_create(), &count); + + cache_fields = t_new(ARRAY_TYPE(mailbox_cache_field), 1); + t_array_init(cache_fields, count); + for (i = 0; i < count; i++) { + dec = fields[i].decision & ENUM_NEGATE(MAIL_CACHE_DECISION_FORCED); + if (dec != MAIL_CACHE_DECISION_NO) { + cf = array_append_space(cache_fields); + cf->name = fields[i].name; + cf->decision = fields[i].decision; + cf->last_used = fields[i].last_used; + } + } + metadata_r->cache_fields = cache_fields; +} + +static void get_metadata_precache_fields(struct mailbox *box, + struct mailbox_metadata *metadata_r) +{ + const struct mail_cache_field *fields; + unsigned int i, count; + enum mail_fetch_field cache = 0; + + fields = mail_cache_register_get_list(box->cache, + pool_datastack_create(), &count); + for (i = 0; i < count; i++) { + const char *name = fields[i].name; + + if (str_begins(name, "hdr.") || + strcmp(name, "date.sent") == 0 || + strcmp(name, "imap.envelope") == 0) + cache |= MAIL_FETCH_STREAM_HEADER; + else if (strcmp(name, "mime.parts") == 0 || + strcmp(name, "binary.parts") == 0 || + strcmp(name, "imap.body") == 0 || + strcmp(name, "imap.bodystructure") == 0 || + strcmp(name, "body.snippet") == 0) + cache |= MAIL_FETCH_STREAM_BODY; + else if (strcmp(name, "date.received") == 0) + cache |= MAIL_FETCH_RECEIVED_DATE; + else if (strcmp(name, "date.save") == 0) + cache |= MAIL_FETCH_SAVE_DATE; + else if (strcmp(name, "size.virtual") == 0) + cache |= MAIL_FETCH_VIRTUAL_SIZE; + else if (strcmp(name, "size.physical") == 0) + cache |= MAIL_FETCH_PHYSICAL_SIZE; + else if (strcmp(name, "pop3.uidl") == 0) + cache |= MAIL_FETCH_UIDL_BACKEND; + else if (strcmp(name, "pop3.order") == 0) + cache |= MAIL_FETCH_POP3_ORDER; + else if (strcmp(name, "guid") == 0) + cache |= MAIL_FETCH_GUID; + else if (strcmp(name, "flags") == 0) { + /* just ignore for now at least.. */ + } else + e_debug(box->event, + "Ignoring unknown cache field: %s", name); + } + metadata_r->precache_fields = cache; +} + +static int +index_mailbox_get_first_save_date(struct mailbox *box, + struct mailbox_metadata *metadata_r) +{ + const struct mail_index_header *hdr; + struct mailbox_transaction_context *t; + struct mail *mail; + uint32_t seq; + int ret = -1; + + hdr = mail_index_get_header(box->view); + if (hdr->messages_count == 0) { + metadata_r->first_save_date = (time_t)-1; + return 0; + } + + t = mailbox_transaction_begin(box, 0, __func__); + mail = mail_alloc(t, 0, NULL); + for (seq = 1; seq <= hdr->messages_count; seq++) { + mail_set_seq(mail, seq); + if (mail_get_save_date(mail, &metadata_r->first_save_date) >= 0) { + ret = 0; + break; + } + if (mailbox_get_last_mail_error(box) != MAIL_ERROR_EXPUNGED) { + /* failed */ + break; + } + } + mail_free(&mail); + (void)mailbox_transaction_commit(&t); + if (seq > hdr->messages_count) { + /* all messages were expunged after all */ + metadata_r->first_save_date = (time_t)-1; + return 0; + } + return ret; +} + +int index_mailbox_get_metadata(struct mailbox *box, + enum mailbox_metadata_items items, + struct mailbox_metadata *metadata_r) +{ + /* handle items that don't require opening the mailbox */ + if ((items & MAILBOX_METADATA_BACKEND_NAMESPACE) != 0) { + metadata_r->backend_ns_prefix = ""; + metadata_r->backend_ns_type = + mailbox_list_get_namespace(box->list)->type; + items &= ENUM_NEGATE(MAILBOX_METADATA_BACKEND_NAMESPACE); + } + if (items == 0) + return 0; + + /* handle items that require opening the mailbox */ + if (!box->opened) { + if (mailbox_open(box) < 0) + return -1; + } + if (!box->synced && (items & MAILBOX_METADATA_SYNC_ITEMS) != 0) { + if (mailbox_sync(box, MAILBOX_SYNC_FLAG_FAST) < 0) + return -1; + } + + if ((items & MAILBOX_METADATA_VIRTUAL_SIZE) != 0) { + if (index_mailbox_get_virtual_size(box, metadata_r) < 0) + return -1; + } + if ((items & MAILBOX_METADATA_PHYSICAL_SIZE) != 0) { + if (index_mailbox_get_physical_size(box, metadata_r) < 0) + return -1; + } + if ((items & MAILBOX_METADATA_FIRST_SAVE_DATE) != 0) { + if (index_mailbox_get_first_save_date(box, metadata_r) < 0) + return -1; + } + if ((items & MAILBOX_METADATA_CACHE_FIELDS) != 0) + get_metadata_cache_fields(box, metadata_r); + if ((items & MAILBOX_METADATA_PRECACHE_FIELDS) != 0) + get_metadata_precache_fields(box, metadata_r); + return 0; +} |