summaryrefslogtreecommitdiffstats
path: root/src/lib-index/mail-index-transaction-finish.c
diff options
context:
space:
mode:
Diffstat (limited to '')
-rw-r--r--src/lib-index/mail-index-transaction-finish.c350
1 files changed, 350 insertions, 0 deletions
diff --git a/src/lib-index/mail-index-transaction-finish.c b/src/lib-index/mail-index-transaction-finish.c
new file mode 100644
index 0000000..360e597
--- /dev/null
+++ b/src/lib-index/mail-index-transaction-finish.c
@@ -0,0 +1,350 @@
+/* Copyright (c) 2003-2018 Dovecot authors, see the included COPYING file */
+
+#include "lib.h"
+#include "array.h"
+#include "ioloop.h"
+#include "mail-index-private.h"
+#include "mail-index-modseq.h"
+#include "mail-index-transaction-private.h"
+
+int mail_transaction_expunge_guid_cmp(const struct mail_transaction_expunge_guid *e1,
+ const struct mail_transaction_expunge_guid *e2)
+{
+ if (e1->uid < e2->uid)
+ return -1;
+ else if (e1->uid > e2->uid)
+ return 1;
+ else
+ return 0;
+}
+
+void mail_index_transaction_sort_expunges(struct mail_index_transaction *t)
+{
+ if (!t->expunges_nonsorted)
+ return;
+
+ array_sort(&t->expunges, mail_transaction_expunge_guid_cmp);
+ t->expunges_nonsorted = FALSE;
+}
+
+static void
+ext_reset_update_atomic(struct mail_index_transaction *t,
+ uint32_t ext_id, uint32_t expected_reset_id)
+{
+ const struct mail_index_ext *map_ext;
+ struct mail_transaction_ext_reset *reset;
+ uint32_t idx, reset_id;
+
+ if (!mail_index_map_get_ext_idx(t->view->index->map, ext_id, &idx)) {
+ /* new extension */
+ reset_id = 1;
+ } else {
+ map_ext = array_idx(&t->view->index->map->extensions, idx);
+ reset_id = map_ext->reset_id + 1;
+ }
+ if (reset_id != expected_reset_id) {
+ /* ignore this extension update */
+ mail_index_ext_set_reset_id(t, ext_id, 0);
+ return;
+ }
+
+ if (reset_id == 0)
+ reset_id++;
+
+ array_idx_set(&t->ext_reset_ids, ext_id, &reset_id);
+
+ /* reseting existing data is optional */
+ if (array_is_created(&t->ext_resets)) {
+ reset = array_idx_modifiable(&t->ext_resets, ext_id);
+ if (reset->new_reset_id == (uint32_t)-1)
+ reset->new_reset_id = reset_id;
+ }
+}
+
+static void
+transaction_update_atomic_reset_ids(struct mail_index_transaction *t)
+{
+ const uint32_t *expected_reset_ids;
+ unsigned int ext_id, count;
+
+ if (!array_is_created(&t->ext_reset_atomic))
+ return;
+
+ expected_reset_ids = array_get(&t->ext_reset_atomic, &count);
+ for (ext_id = 0; ext_id < count; ext_id++) {
+ if (expected_reset_ids[ext_id] != 0) {
+ ext_reset_update_atomic(t, ext_id,
+ expected_reset_ids[ext_id]);
+ }
+ }
+}
+
+static unsigned int
+mail_transaction_drop_range(struct mail_index_transaction *t,
+ struct mail_index_flag_update update,
+ unsigned int update_idx,
+ ARRAY_TYPE(seq_range) *keeps)
+{
+ const struct seq_range *keep_range;
+ unsigned int i, keep_count;
+
+ keep_range = array_get(keeps, &keep_count);
+ if (keep_count == 1 &&
+ update.uid1 == keep_range[0].seq1 &&
+ update.uid2 == keep_range[0].seq2) {
+ /* everything is kept */
+ return update_idx + 1;
+ }
+
+ array_delete(&t->updates, update_idx, 1);
+
+ /* add back all the updates we want to keep */
+ for (i = 0; i < keep_count; i++, update_idx++) {
+ update.uid1 = keep_range[i].seq1;
+ update.uid2 = keep_range[i].seq2;
+ array_insert(&t->updates, update_idx, &update, 1);
+ }
+ return update_idx;
+}
+
+static void
+mail_index_transaction_finish_flag_updates(struct mail_index_transaction *t)
+{
+ const struct mail_index_flag_update *updates, *u;
+ const struct mail_index_record *rec;
+ unsigned int i, count;
+ ARRAY_TYPE(seq_range) keeps;
+ uint32_t seq;
+
+ if (!t->drop_unnecessary_flag_updates || !array_is_created(&t->updates))
+ return;
+
+ t_array_init(&keeps, 64);
+ updates = array_get(&t->updates, &count);
+ for (i = 0; i < count; ) {
+ /* first get the list of changes to drop */
+ u = &updates[i];
+ array_clear(&keeps);
+ for (seq = u->uid1; seq <= u->uid2; seq++) {
+ rec = mail_index_lookup(t->view, seq);
+ if ((rec->flags & u->add_flags) != u->add_flags ||
+ (rec->flags & u->remove_flags) != 0) {
+ /* keep this change */
+ seq_range_array_add(&keeps, seq);
+ }
+ }
+ i = mail_transaction_drop_range(t, updates[i], i, &keeps);
+ updates = array_get(&t->updates, &count);
+ }
+
+ if (array_count(&t->updates) == 0)
+ array_free(&t->updates);
+}
+
+static void
+mail_index_transaction_check_conflicts(struct mail_index_transaction *t)
+{
+ uint32_t seq;
+ bool ret1, ret2;
+
+ i_assert(t->max_modseq != 0);
+ i_assert(t->conflict_seqs != NULL);
+
+ if (t->max_modseq == mail_index_modseq_get_highest(t->view)) {
+ /* no conflicts possible */
+ return;
+ }
+ if (t->min_flagupdate_seq == 0) {
+ /* no flag updates */
+ return;
+ }
+
+ for (seq = t->min_flagupdate_seq; seq <= t->max_flagupdate_seq; seq++) {
+ if (mail_index_modseq_lookup(t->view, seq) > t->max_modseq) {
+ ret1 = mail_index_cancel_flag_updates(t, seq);
+ ret2 = mail_index_cancel_keyword_updates(t, seq);
+ if (ret1 || ret2) {
+ seq_range_array_add_with_init(t->conflict_seqs,
+ 16, seq);
+ }
+ }
+ }
+ mail_index_transaction_set_log_updates(t);
+}
+
+static uint32_t
+mail_index_transaction_get_uid(struct mail_index_transaction *t, uint32_t seq)
+{
+ const struct mail_index_record *rec;
+
+ i_assert(seq > 0);
+
+ if (seq >= t->first_new_seq)
+ rec = mail_index_transaction_lookup(t, seq);
+ else {
+ i_assert(seq <= t->view->map->hdr.messages_count);
+ rec = MAIL_INDEX_REC_AT_SEQ(t->view->map, seq);
+ }
+ i_assert(rec->uid != 0);
+ return rec->uid;
+}
+
+static void
+mail_index_convert_to_uids(struct mail_index_transaction *t,
+ ARRAY_TYPE(seq_array) *array)
+{
+ uint32_t *seq;
+ unsigned int i, count;
+
+ if (!array_is_created(array))
+ return;
+
+ count = array_count(array);
+ for (i = 0; i < count; i++) {
+ seq = array_idx_modifiable(array, i);
+ *seq = mail_index_transaction_get_uid(t, *seq);
+ }
+}
+
+static uint32_t
+get_nonexpunged_uid2(struct mail_index_transaction *t,
+ uint32_t uid1, uint32_t seq1)
+{
+ seq1++;
+
+ while (mail_index_transaction_get_uid(t, seq1) == uid1 + 1) {
+ seq1++;
+ uid1++;
+ }
+ return uid1;
+}
+
+void mail_index_transaction_seq_range_to_uid(struct mail_index_transaction *t,
+ ARRAY_TYPE(seq_range) *array)
+{
+ struct seq_range *range, *new_range;
+ unsigned int i, count;
+ uint32_t uid1, uid2, prev_uid = 0;
+
+ if (!array_is_created(array))
+ return;
+
+ count = array_count(array);
+ for (i = 0; i < count; i++) {
+ range = array_idx_modifiable(array, i);
+
+ uid1 = mail_index_transaction_get_uid(t, range->seq1);
+ uid2 = mail_index_transaction_get_uid(t, range->seq2);
+ i_assert(uid1 > prev_uid);
+ if (uid2 - uid1 == range->seq2 - range->seq1) {
+ /* simple conversion */
+ range->seq1 = uid1;
+ range->seq2 = uid2;
+ prev_uid = uid2;
+ } else {
+ /* remove expunged UIDs */
+ new_range = array_insert_space(array, i);
+ range = array_idx_modifiable(array, i + 1);
+ count++;
+
+ memcpy(new_range, range, array->arr.element_size);
+ new_range->seq1 = uid1;
+ new_range->seq2 = get_nonexpunged_uid2(t, uid1,
+ range->seq1);
+ i_assert(new_range->seq2 < uid2);
+
+ /* continue the range without the inserted seqs */
+ range->seq1 += new_range->seq2 - new_range->seq1 + 1;
+ prev_uid = new_range->seq2;
+ }
+ }
+}
+
+static void keyword_updates_convert_to_uids(struct mail_index_transaction *t)
+{
+ struct mail_index_transaction_keyword_update *update;
+
+ if (!array_is_created(&t->keyword_updates))
+ return;
+
+ array_foreach_modifiable(&t->keyword_updates, update) {
+ mail_index_transaction_seq_range_to_uid(t, &update->add_seq);
+ mail_index_transaction_seq_range_to_uid(t, &update->remove_seq);
+ }
+}
+
+static void expunges_convert_to_uids(struct mail_index_transaction *t)
+{
+ struct mail_transaction_expunge_guid *expunges;
+ unsigned int src, dest, count;
+
+ if (!array_is_created(&t->expunges))
+ return;
+
+ mail_index_transaction_sort_expunges(t);
+
+ expunges = array_get_modifiable(&t->expunges, &count);
+ if (count == 0)
+ return;
+
+ /* convert uids and drop duplicates */
+ expunges[0].uid = mail_index_transaction_get_uid(t, expunges[0].uid);
+ for (src = dest = 1; src < count; src++) {
+ expunges[dest].uid =
+ mail_index_transaction_get_uid(t, expunges[src].uid);
+ if (expunges[dest-1].uid != expunges[dest].uid) {
+ if (dest != src) {
+ memcpy(expunges[dest].guid_128, expunges[src].guid_128,
+ sizeof(expunges[dest].guid_128));
+ }
+ dest++;
+ }
+ }
+ array_delete(&t->expunges, dest, count-dest);
+}
+
+static void
+mail_index_transaction_convert_to_uids(struct mail_index_transaction *t)
+{
+ ARRAY_TYPE(seq_array) *update;
+
+ if (array_is_created(&t->ext_rec_updates)) {
+ array_foreach_modifiable(&t->ext_rec_updates, update)
+ mail_index_convert_to_uids(t, update);
+ }
+ if (array_is_created(&t->ext_rec_atomics)) {
+ array_foreach_modifiable(&t->ext_rec_atomics, update)
+ mail_index_convert_to_uids(t, update);
+ }
+
+ keyword_updates_convert_to_uids(t);
+ expunges_convert_to_uids(t);
+ mail_index_convert_to_uids(t, (void *)&t->modseq_updates);
+ mail_index_transaction_seq_range_to_uid(t, (void *)&t->updates);
+}
+
+void mail_index_transaction_finish_so_far(struct mail_index_transaction *t)
+{
+ if (array_is_created(&t->appends))
+ mail_index_transaction_sort_appends(t);
+ mail_index_transaction_finish_flag_updates(t);
+ if (t->max_modseq != 0)
+ mail_index_transaction_check_conflicts(t);
+}
+
+void mail_index_transaction_finish(struct mail_index_transaction *t)
+{
+ mail_index_transaction_finish_so_far(t);
+
+ if (array_is_created(&t->appends))
+ mail_index_update_day_headers(t, ioloop_time);
+ if (array_is_created(&t->ext_reset_atomic))
+ transaction_update_atomic_reset_ids(t);
+ /* finally convert all sequences to UIDs before we write them,
+ but after we've checked and removed conflicts */
+ mail_index_transaction_convert_to_uids(t);
+
+ /* and kind of ugly way to update highest modseq */
+ if (t->min_highest_modseq != 0)
+ mail_index_update_modseq(t, 0, t->min_highest_modseq);
+}