diff options
author | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-28 09:51:24 +0000 |
---|---|---|
committer | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-28 09:51:24 +0000 |
commit | f7548d6d28c313cf80e6f3ef89aed16a19815df1 (patch) | |
tree | a3f6f2a3f247293bee59ecd28e8cd8ceb6ca064a /src/lib-index/mail-index-transaction-view.c | |
parent | Initial commit. (diff) | |
download | dovecot-f7548d6d28c313cf80e6f3ef89aed16a19815df1.tar.xz dovecot-f7548d6d28c313cf80e6f3ef89aed16a19815df1.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-index/mail-index-transaction-view.c')
-rw-r--r-- | src/lib-index/mail-index-transaction-view.c | 534 |
1 files changed, 534 insertions, 0 deletions
diff --git a/src/lib-index/mail-index-transaction-view.c b/src/lib-index/mail-index-transaction-view.c new file mode 100644 index 0000000..240c7fe --- /dev/null +++ b/src/lib-index/mail-index-transaction-view.c @@ -0,0 +1,534 @@ +/* Copyright (c) 2004-2018 Dovecot authors, see the included COPYING file */ + +#include "lib.h" +#include "array.h" +#include "buffer.h" +#include "seq-range-array.h" +#include "mail-index-private.h" +#include "mail-index-view-private.h" +#include "mail-index-transaction-private.h" + +struct mail_index_view_transaction { + struct mail_index_view view; + struct mail_index_view_vfuncs *super; + struct mail_index_transaction *t; + + struct mail_index_map *lookup_map; + struct mail_index_header hdr; + + buffer_t *lookup_return_data; + uint32_t lookup_prev_seq; + + unsigned int record_size; + unsigned int recs_count; + void *recs; + ARRAY(void *) all_recs; +}; + +static void tview_close(struct mail_index_view *view) +{ + struct mail_index_view_transaction *tview = + (struct mail_index_view_transaction *)view; + struct mail_index_transaction *t = tview->t; + void **recs; + unsigned int i, count; + + if (tview->lookup_map != NULL) + mail_index_unmap(&tview->lookup_map); + buffer_free(&tview->lookup_return_data); + + if (array_is_created(&tview->all_recs)) { + recs = array_get_modifiable(&tview->all_recs, &count); + for (i = 0; i < count; i++) + i_free(recs[i]); + array_free(&tview->all_recs); + } + + tview->super->close(view); + mail_index_transaction_unref(&t); +} + +static uint32_t tview_get_message_count(struct mail_index_view *view) +{ + struct mail_index_view_transaction *tview = + (struct mail_index_view_transaction *)view; + + return view->map->hdr.messages_count + + (tview->t->last_new_seq == 0 ? 0 : + tview->t->last_new_seq - tview->t->first_new_seq + 1); +} + +static const struct mail_index_header * +tview_get_header(struct mail_index_view *view) +{ + struct mail_index_view_transaction *tview = + (struct mail_index_view_transaction *)view; + const struct mail_index_header *hdr; + uint32_t next_uid; + + /* FIXME: header counters may not be correct */ + hdr = tview->super->get_header(view); + + next_uid = mail_index_transaction_get_next_uid(tview->t); + if (next_uid != hdr->next_uid) { + tview->hdr = *hdr; + tview->hdr.next_uid = next_uid; + hdr = &tview->hdr; + } + return hdr; +} + +static const struct mail_index_record * +tview_apply_flag_updates(struct mail_index_view_transaction *tview, + struct mail_index_map *map, + const struct mail_index_record *rec, uint32_t seq) +{ + struct mail_index_transaction *t = tview->t; + const struct mail_index_flag_update *updates; + struct mail_index_record *trec; + unsigned int idx, count; + + /* see if there are any flag updates */ + if (seq < t->min_flagupdate_seq || seq > t->max_flagupdate_seq || + !array_is_created(&t->updates)) + return rec; + + updates = array_get(&t->updates, &count); + idx = mail_index_transaction_get_flag_update_pos(t, 0, count, seq); + if (seq < updates[idx].uid1 || seq > updates[idx].uid2) + return rec; + + /* yes, we have flag updates. since we can't modify rec directly and + we want to be able to handle multiple mail_index_lookup() calls + without the second one overriding the first one's data, we'll + create a records array and return data from there. + + it's also possible that the record size increases, so we potentially + have to create multiple arrays. they all get eventually freed when + the view gets freed. */ + if (map->hdr.record_size > tview->record_size) { + if (!array_is_created(&tview->all_recs)) + i_array_init(&tview->all_recs, 4); + tview->recs_count = t->first_new_seq; + tview->record_size = I_MAX(map->hdr.record_size, + tview->view.map->hdr.record_size); + tview->recs = i_malloc(MALLOC_MULTIPLY(tview->record_size, + tview->recs_count)); + array_push_back(&tview->all_recs, &tview->recs); + } + i_assert(tview->recs_count == t->first_new_seq); + i_assert(seq > 0 && seq <= tview->recs_count); + + trec = PTR_OFFSET(tview->recs, (seq-1) * tview->record_size); + memcpy(trec, rec, map->hdr.record_size); + trec->flags |= updates[idx].add_flags & 0xff; + trec->flags &= ENUM_NEGATE(updates[idx].remove_flags); + return trec; +} + +static const struct mail_index_record * +tview_lookup_full(struct mail_index_view *view, uint32_t seq, + struct mail_index_map **map_r, bool *expunged_r) +{ + struct mail_index_view_transaction *tview = + (struct mail_index_view_transaction *)view; + const struct mail_index_record *rec; + + if (seq >= tview->t->first_new_seq) { + /* FIXME: is this right to return index map..? + it's not there yet. */ + *map_r = view->index->map; + if (expunged_r != NULL) + *expunged_r = FALSE; + return mail_index_transaction_lookup(tview->t, seq); + } + + rec = tview->super->lookup_full(view, seq, map_r, expunged_r); + rec = tview_apply_flag_updates(tview, *map_r, rec, seq); + + if (expunged_r != NULL && + mail_index_transaction_is_expunged(tview->t, seq)) + *expunged_r = TRUE; + return rec; +} + +static void +tview_lookup_uid(struct mail_index_view *view, uint32_t seq, uint32_t *uid_r) +{ + struct mail_index_view_transaction *tview = + (struct mail_index_view_transaction *)view; + + if (seq >= tview->t->first_new_seq) + *uid_r = mail_index_transaction_lookup(tview->t, seq)->uid; + else + tview->super->lookup_uid(view, seq, uid_r); +} + +static void tview_lookup_seq_range(struct mail_index_view *view, + uint32_t first_uid, uint32_t last_uid, + uint32_t *first_seq_r, uint32_t *last_seq_r) +{ + struct mail_index_view_transaction *tview = + (struct mail_index_view_transaction *)view; + const struct mail_index_record *rec; + uint32_t seq; + + if (!tview->t->reset) { + tview->super->lookup_seq_range(view, first_uid, last_uid, + first_seq_r, last_seq_r); + } else { + /* index is being reset. we never want to return old + sequences. */ + *first_seq_r = *last_seq_r = 0; + } + if (tview->t->last_new_seq == 0) { + /* no new messages, the results are final. */ + return; + } + + rec = mail_index_transaction_lookup(tview->t, tview->t->first_new_seq); + if (rec->uid == 0) { + /* new messages don't have UIDs */ + return; + } + if (last_uid < rec->uid) { + /* all wanted messages were existing */ + return; + } + + /* at least some of the wanted messages are newly created */ + if (*first_seq_r == 0) { + seq = tview->t->first_new_seq; + for (; seq <= tview->t->last_new_seq; seq++) { + rec = mail_index_transaction_lookup(tview->t, seq); + if (first_uid <= rec->uid) + break; + } + if (seq > tview->t->last_new_seq || rec->uid > last_uid) { + /* no messages in range */ + return; + } + *first_seq_r = seq; + + if (rec->uid == last_uid) { + /* one seq in range */ + *last_seq_r = seq; + return; + } + } + + seq = tview->t->last_new_seq; + for (; seq >= tview->t->first_new_seq; seq--) { + rec = mail_index_transaction_lookup(tview->t, seq); + if (rec->uid <= last_uid) { + *last_seq_r = seq; + break; + } + } + i_assert(seq >= tview->t->first_new_seq); +} + +static void tview_lookup_first(struct mail_index_view *view, + enum mail_flags flags, uint8_t flags_mask, + uint32_t *seq_r) +{ + struct mail_index_view_transaction *tview = + (struct mail_index_view_transaction *)view; + const struct mail_index_record *rec; + unsigned int append_count; + uint32_t seq, message_count; + + if (!tview->t->reset) { + tview->super->lookup_first(view, flags, flags_mask, seq_r); + if (*seq_r != 0) + return; + } else { + *seq_r = 0; + } + + rec = array_get(&tview->t->appends, &append_count); + seq = tview->t->first_new_seq; + message_count = tview->t->last_new_seq; + i_assert(append_count == message_count - seq + 1); + + for (; seq <= message_count; seq++, rec++) { + if ((rec->flags & flags_mask) == (uint8_t)flags) { + *seq_r = seq; + break; + } + } +} + +static void keyword_index_add(ARRAY_TYPE(keyword_indexes) *keywords, + unsigned int idx) +{ + const unsigned int *indexes; + unsigned int i, count; + + indexes = array_get(keywords, &count); + for (i = 0; i < count; i++) { + if (indexes[i] == idx) + return; + } + array_push_back(keywords, &idx); +} + +static void keyword_index_remove(ARRAY_TYPE(keyword_indexes) *keywords, + unsigned int idx) +{ + const unsigned int *indexes; + unsigned int i, count; + + indexes = array_get(keywords, &count); + for (i = 0; i < count; i++) { + if (indexes[i] == idx) { + array_delete(keywords, i, 1); + break; + } + } +} + +static void tview_lookup_keywords(struct mail_index_view *view, uint32_t seq, + ARRAY_TYPE(keyword_indexes) *keyword_idx) +{ + struct mail_index_view_transaction *tview = + (struct mail_index_view_transaction *)view; + struct mail_index_transaction *t = tview->t; + const struct mail_index_transaction_keyword_update *updates; + unsigned int i, count; + + tview->super->lookup_keywords(view, seq, keyword_idx); + + if (seq < t->min_flagupdate_seq || seq > t->max_flagupdate_seq) { + /* no keyword updates for this sequence */ + return; + } + + if (array_is_created(&t->keyword_updates)) + updates = array_get(&t->keyword_updates, &count); + else { + updates = NULL; + count = 0; + } + for (i = 0; i < count; i++) { + if (array_is_created(&updates[i].add_seq) && + seq_range_exists(&updates[i].add_seq, seq)) + keyword_index_add(keyword_idx, i); + else if (array_is_created(&updates[i].remove_seq) && + seq_range_exists(&updates[i].remove_seq, seq)) + keyword_index_remove(keyword_idx, i); + } +} + +static const void * +tview_return_updated_ext(struct mail_index_view_transaction *tview, + uint32_t seq, const void *data, uint32_t ext_id) +{ + const struct mail_index_ext *ext; + const struct mail_index_registered_ext *rext; + const struct mail_transaction_ext_intro *intro; + unsigned int record_align, record_size; + uint32_t ext_idx; + size_t pos; + + /* data begins with a 32bit sequence, followed by the actual + extension data */ + data = CONST_PTR_OFFSET(data, sizeof(uint32_t)); + + if (!mail_index_map_get_ext_idx(tview->lookup_map, ext_id, &ext_idx)) { + /* we're adding the extension now. */ + rext = array_idx(&tview->view.index->extensions, ext_id); + record_align = rext->record_align; + record_size = rext->record_size; + } else { + ext = array_idx(&tview->lookup_map->extensions, ext_idx); + record_align = ext->record_align; + record_size = ext->record_size; + } + + /* see if the extension has been resized within this transaction */ + if (array_is_created(&tview->t->ext_resizes) && + ext_id < array_count(&tview->t->ext_resizes)) { + intro = array_idx(&tview->t->ext_resizes, ext_id); + if (intro[ext_id].name_size != 0) { + record_align = intro->record_align; + record_size = intro->record_size; + } + } + + if (record_align <= sizeof(uint32_t)) { + /* data is 32bit aligned already */ + return data; + } else { + /* assume we want 64bit alignment - copy the data to + temporary buffer and return it */ + if (tview->lookup_return_data == NULL) { + tview->lookup_return_data = + buffer_create_dynamic(default_pool, + record_size + 64); + } else if (seq != tview->lookup_prev_seq) { + /* clear the buffer between lookups for different + messages */ + buffer_set_used_size(tview->lookup_return_data, 0); + } + tview->lookup_prev_seq = seq; + pos = tview->lookup_return_data->used; + buffer_append(tview->lookup_return_data, data, record_size); + return CONST_PTR_OFFSET(tview->lookup_return_data->data, pos); + } +} + +static bool +tview_is_ext_reset(struct mail_index_view_transaction *tview, uint32_t ext_id) +{ + const struct mail_transaction_ext_reset *resets; + unsigned int count; + + if (!array_is_created(&tview->t->ext_resets)) + return FALSE; + + resets = array_get(&tview->t->ext_resets, &count); + return ext_id < count && resets[ext_id].new_reset_id != 0; +} + +static bool +tview_lookup_ext_update(struct mail_index_view_transaction *tview, uint32_t seq, + uint32_t ext_id, struct mail_index_map **map_r, + const void **data_r) +{ + const ARRAY_TYPE(seq_array) *ext_buf; + const void *data; + unsigned int idx; + uint32_t map_ext_idx; + + ext_buf = array_idx(&tview->t->ext_rec_updates, ext_id); + if (!array_is_created(ext_buf) || + !mail_index_seq_array_lookup(ext_buf, seq, &idx)) + return FALSE; + + if (tview->lookup_map == NULL) { + tview->lookup_map = + mail_index_map_clone(tview->view.index->map); + } + if (!mail_index_map_get_ext_idx(tview->lookup_map, ext_id, &map_ext_idx)) { + /* extension doesn't yet exist in the map. add it there with + the preliminary information (mainly its size) so if caller + looks it up, it's going to be found. */ + const struct mail_index_registered_ext *rext = + array_idx(&tview->view.index->extensions, ext_id); + struct mail_index_ext_header ext_hdr; + + i_zero(&ext_hdr); + ext_hdr.hdr_size = rext->hdr_size; + ext_hdr.record_size = ext_buf->arr.element_size - sizeof(uint32_t); + ext_hdr.record_align = rext->record_align; + + mail_index_map_register_ext(tview->lookup_map, rext->name, + (uint32_t)-1, &ext_hdr); + } + + data = array_idx(ext_buf, idx); + *map_r = tview->lookup_map; + *data_r = tview_return_updated_ext(tview, seq, data, ext_id); + return TRUE; +} + +static void +tview_lookup_ext_full(struct mail_index_view *view, uint32_t seq, + uint32_t ext_id, struct mail_index_map **map_r, + const void **data_r, bool *expunged_r) +{ + struct mail_index_view_transaction *tview = + (struct mail_index_view_transaction *)view; + + i_assert(ext_id < array_count(&view->index->extensions)); + + if (expunged_r != NULL) + *expunged_r = FALSE; + + if (array_is_created(&tview->t->ext_rec_updates) && + ext_id < array_count(&tview->t->ext_rec_updates)) { + /* there are some ext updates in transaction. + see if there's any for this sequence. */ + if (tview_lookup_ext_update(tview, seq, ext_id, map_r, data_r)) + return; + } + + /* not updated, return the existing value, unless ext was + already reset */ + if (seq < tview->t->first_new_seq && + !tview_is_ext_reset(tview, ext_id)) { + tview->super->lookup_ext_full(view, seq, ext_id, + map_r, data_r, expunged_r); + } else { + *map_r = view->index->map; + *data_r = NULL; + } +} + +static void tview_get_header_ext(struct mail_index_view *view, + struct mail_index_map *map, uint32_t ext_id, + const void **data_r, size_t *data_size_r) +{ + struct mail_index_view_transaction *tview = + (struct mail_index_view_transaction *)view; + + /* FIXME: check updates */ + tview->super->get_header_ext(view, map, ext_id, data_r, data_size_r); +} + +static bool tview_ext_get_reset_id(struct mail_index_view *view, + struct mail_index_map *map, + uint32_t ext_id, uint32_t *reset_id_r) +{ + struct mail_index_view_transaction *tview = + (struct mail_index_view_transaction *)view; + const uint32_t *reset_id_p; + + if (array_is_created(&tview->t->ext_reset_ids) && + ext_id < array_count(&tview->t->ext_reset_ids) && + map == tview->lookup_map) { + reset_id_p = array_idx(&tview->t->ext_reset_ids, ext_id); + *reset_id_r = *reset_id_p; + return TRUE; + } + + return tview->super->ext_get_reset_id(view, map, ext_id, reset_id_r); +} + +static struct mail_index_view_vfuncs trans_view_vfuncs = { + tview_close, + tview_get_message_count, + tview_get_header, + tview_lookup_full, + tview_lookup_uid, + tview_lookup_seq_range, + tview_lookup_first, + tview_lookup_keywords, + tview_lookup_ext_full, + tview_get_header_ext, + tview_ext_get_reset_id +}; + +struct mail_index_view * +mail_index_transaction_open_updated_view(struct mail_index_transaction *t) +{ + struct mail_index_view_transaction *tview; + + if (t->view->syncing) { + /* transaction view is being synced. while it's done, it's not + possible to add new messages, but the view itself might + change. so we can't make a copy of the view. */ + mail_index_view_ref(t->view); + return t->view; + } + + tview = i_new(struct mail_index_view_transaction, 1); + mail_index_view_clone(&tview->view, t->view); + tview->view.v = trans_view_vfuncs; + tview->super = &t->view->v; + tview->t = t; + + mail_index_transaction_ref(t); + return &tview->view; +} |