summaryrefslogtreecommitdiffstats
path: root/src/lib-storage/index/index-status.c
diff options
context:
space:
mode:
authorDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-28 09:51:24 +0000
committerDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-28 09:51:24 +0000
commitf7548d6d28c313cf80e6f3ef89aed16a19815df1 (patch)
treea3f6f2a3f247293bee59ecd28e8cd8ceb6ca064a /src/lib-storage/index/index-status.c
parentInitial commit. (diff)
downloaddovecot-upstream.tar.xz
dovecot-upstream.zip
Adding upstream version 1:2.3.19.1+dfsg1.upstream/1%2.3.19.1+dfsg1upstream
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'src/lib-storage/index/index-status.c')
-rw-r--r--src/lib-storage/index/index-status.c344
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;
+}