diff options
Diffstat (limited to '')
-rw-r--r-- | src/lib-storage/mailbox-get.c | 239 |
1 files changed, 239 insertions, 0 deletions
diff --git a/src/lib-storage/mailbox-get.c b/src/lib-storage/mailbox-get.c new file mode 100644 index 0000000..84a14ed --- /dev/null +++ b/src/lib-storage/mailbox-get.c @@ -0,0 +1,239 @@ +/* Copyright (c) 2002-2018 Dovecot authors, see the included COPYING file */ + +#include "lib.h" +#include "array.h" +#include "mail-index-modseq.h" +#include "mail-storage-private.h" + +void mailbox_get_seq_range(struct mailbox *box, uint32_t uid1, uint32_t uid2, + uint32_t *seq1_r, uint32_t *seq2_r) +{ + (void)mail_index_lookup_seq_range(box->view, uid1, uid2, seq1_r, seq2_r); +} + +void mailbox_get_uid_range(struct mailbox *box, + const ARRAY_TYPE(seq_range) *seqs, + ARRAY_TYPE(seq_range) *uids) +{ + const struct seq_range *range; + unsigned int i, count; + uint32_t seq, uid, uid2; + + range = array_get(seqs, &count); + for (i = 0; i < count; i++) { + if (range[i].seq2 == (uint32_t)-1) { + i_assert(i == count-1); + mail_index_lookup_uid(box->view, + mail_index_view_get_messages_count(box->view), + &uid2); + if (range[i].seq1 == (uint32_t)-1) + uid = uid2; + else + mail_index_lookup_uid(box->view, range[i].seq1, &uid); + seq_range_array_add_range(uids, uid, uid2); + break; + } + for (seq = range[i].seq1; seq <= range[i].seq2; seq++) { + mail_index_lookup_uid(box->view, seq, &uid); + seq_range_array_add(uids, uid); + } + } +} + +static void +add_expunges(ARRAY_TYPE(seq_range) *expunged_uids, uint32_t min_uid, + const struct mail_transaction_expunge *src, size_t src_size) +{ + const struct mail_transaction_expunge *end; + + end = src + src_size / sizeof(*src); + for (; src != end; src++) { + if (src->uid2 >= min_uid) { + seq_range_array_add_range(expunged_uids, + src->uid1, src->uid2); + } + } +} + +static void +add_guid_expunges(ARRAY_TYPE(seq_range) *expunged_uids, uint32_t min_uid, + const struct mail_transaction_expunge_guid *src, + size_t src_size) +{ + const struct mail_transaction_expunge_guid *end; + + end = src + src_size / sizeof(*src); + for (; src != end; src++) { + if (src->uid >= min_uid) + seq_range_array_add(expunged_uids, src->uid); + } +} + +static int +mailbox_get_expunges_init(struct mailbox *box, uint64_t prev_modseq, + struct mail_transaction_log_view **log_view_r, + bool *modseq_too_old_r) +{ + struct mail_transaction_log_view *log_view; + uint32_t log_seq, tail_seq; + uoff_t log_offset; + const char *reason; + bool reset; + int ret; + + *modseq_too_old_r = FALSE; + + if (!mail_index_modseq_get_next_log_offset(box->view, prev_modseq, + &log_seq, &log_offset)) { + log_seq = 1; + log_offset = 0; + *modseq_too_old_r = TRUE; + } + if (log_seq > box->view->log_file_head_seq || + (log_seq == box->view->log_file_head_seq && + log_offset >= box->view->log_file_head_offset)) { + /* we haven't seen this high expunges at all */ + return 1; + } + + log_view = mail_transaction_log_view_open(box->index->log); + ret = mail_transaction_log_view_set(log_view, log_seq, log_offset, + box->view->log_file_head_seq, + box->view->log_file_head_offset, + &reset, &reason); + if (ret == 0) { + mail_transaction_log_get_tail(box->index->log, &tail_seq); + if (tail_seq <= box->view->log_file_head_seq) { + i_assert(tail_seq > log_seq); + ret = mail_transaction_log_view_set(log_view, tail_seq, 0, + box->view->log_file_head_seq, + box->view->log_file_head_offset, + &reset, &reason); + } + *modseq_too_old_r = TRUE; + } + if (ret <= 0) { + mail_transaction_log_view_close(&log_view); + return -1; + } + + *log_view_r = log_view; + return 0; +} + +static void +mailbox_get_expunged_guids(struct mail_transaction_log_view *log_view, + ARRAY_TYPE(seq_range) *expunged_uids, + ARRAY_TYPE(mailbox_expunge_rec) *expunges) +{ + const struct mail_transaction_header *thdr; + const void *tdata; + const struct mail_transaction_expunge_guid *rec, *end; + struct mailbox_expunge_rec *expunge; + struct seq_range_iter iter; + unsigned int n; + uint32_t uid; + + while (mail_transaction_log_view_next(log_view, &thdr, &tdata) > 0) { + if ((thdr->type & MAIL_TRANSACTION_TYPE_MASK) != + MAIL_TRANSACTION_EXPUNGE_GUID) + continue; + + rec = tdata; + end = rec + thdr->size / sizeof(*rec); + for (; rec != end; rec++) { + if (!seq_range_exists(expunged_uids, rec->uid)) + continue; + seq_range_array_remove(expunged_uids, rec->uid); + + expunge = array_append_space(expunges); + expunge->uid = rec->uid; + memcpy(expunge->guid_128, rec->guid_128, + sizeof(expunge->guid_128)); + } + } + + /* everything left in expunged_uids didn't get a GUID */ + seq_range_array_iter_init(&iter, expunged_uids); n = 0; + while (seq_range_array_iter_nth(&iter, n++, &uid)) { + expunge = array_append_space(expunges); + expunge->uid = uid; + } +} + +static bool ATTR_NULL(4, 5) +mailbox_get_expunges_full(struct mailbox *box, uint64_t prev_modseq, + const ARRAY_TYPE(seq_range) *uids_filter, + ARRAY_TYPE(seq_range) *expunged_uids, + ARRAY_TYPE(mailbox_expunge_rec) *expunges) +{ + struct mail_transaction_log_view *log_view; + ARRAY_TYPE(seq_range) tmp_expunged_uids = ARRAY_INIT; + const struct mail_transaction_header *thdr; + const struct seq_range *range; + const void *tdata; + uint32_t min_uid; + bool modseq_too_old; + int ret; + + i_assert(array_count(uids_filter) > 0); + i_assert(expunged_uids == NULL || expunges == NULL); + + ret = mailbox_get_expunges_init(box, prev_modseq, &log_view, &modseq_too_old); + if (ret != 0) + return ret > 0; + + range = array_front(uids_filter); + min_uid = range->seq1; + + /* first get UIDs of all actual expunges */ + if (expunged_uids == NULL) { + i_array_init(&tmp_expunged_uids, 64); + expunged_uids = &tmp_expunged_uids; + } + mail_transaction_log_view_mark(log_view); + while ((ret = mail_transaction_log_view_next(log_view, + &thdr, &tdata)) > 0) { + if ((thdr->type & MAIL_TRANSACTION_EXTERNAL) == 0) { + /* skip expunge requests */ + continue; + } + switch (thdr->type & MAIL_TRANSACTION_TYPE_MASK) { + case MAIL_TRANSACTION_EXPUNGE: + add_expunges(expunged_uids, min_uid, tdata, thdr->size); + break; + case MAIL_TRANSACTION_EXPUNGE_GUID: + add_guid_expunges(expunged_uids, min_uid, + tdata, thdr->size); + break; + } + } + mail_transaction_log_view_rewind(log_view); + + /* drop UIDs that don't match the filter */ + seq_range_array_intersect(expunged_uids, uids_filter); + + if (expunges != NULL) { + mailbox_get_expunged_guids(log_view, expunged_uids, expunges); + array_free(&tmp_expunged_uids); + } + + mail_transaction_log_view_close(&log_view); + return ret < 0 || modseq_too_old ? FALSE : TRUE; +} + +bool mailbox_get_expunges(struct mailbox *box, uint64_t prev_modseq, + const ARRAY_TYPE(seq_range) *uids_filter, + ARRAY_TYPE(mailbox_expunge_rec) *expunges) +{ + return mailbox_get_expunges_full(box, prev_modseq, + uids_filter, NULL, expunges); +} + +bool mailbox_get_expunged_uids(struct mailbox *box, uint64_t prev_modseq, + const ARRAY_TYPE(seq_range) *uids_filter, + ARRAY_TYPE(seq_range) *expunged_uids) +{ + return mailbox_get_expunges_full(box, prev_modseq, + uids_filter, expunged_uids, NULL); +} |