1 /* Copyright (c) 2003-2018 Dovecot authors, see the included COPYING file */
2
3 #include "lib.h"
4 #include "array.h"
5 #include "ioloop.h"
6 #include "mail-index-private.h"
7 #include "mail-index-modseq.h"
8 #include "mail-index-transaction-private.h"
9
mail_transaction_expunge_guid_cmp(const struct mail_transaction_expunge_guid * e1,const struct mail_transaction_expunge_guid * e2)10 int mail_transaction_expunge_guid_cmp(const struct mail_transaction_expunge_guid *e1,
11 const struct mail_transaction_expunge_guid *e2)
12 {
13 if (e1->uid < e2->uid)
14 return -1;
15 else if (e1->uid > e2->uid)
16 return 1;
17 else
18 return 0;
19 }
20
mail_index_transaction_sort_expunges(struct mail_index_transaction * t)21 void mail_index_transaction_sort_expunges(struct mail_index_transaction *t)
22 {
23 if (!t->expunges_nonsorted)
24 return;
25
26 array_sort(&t->expunges, mail_transaction_expunge_guid_cmp);
27 t->expunges_nonsorted = FALSE;
28 }
29
30 static void
ext_reset_update_atomic(struct mail_index_transaction * t,uint32_t ext_id,uint32_t expected_reset_id)31 ext_reset_update_atomic(struct mail_index_transaction *t,
32 uint32_t ext_id, uint32_t expected_reset_id)
33 {
34 const struct mail_index_ext *map_ext;
35 struct mail_transaction_ext_reset *reset;
36 uint32_t idx, reset_id;
37
38 if (!mail_index_map_get_ext_idx(t->view->index->map, ext_id, &idx)) {
39 /* new extension */
40 reset_id = 1;
41 } else {
42 map_ext = array_idx(&t->view->index->map->extensions, idx);
43 reset_id = map_ext->reset_id + 1;
44 }
45 if (reset_id != expected_reset_id) {
46 /* ignore this extension update */
47 mail_index_ext_set_reset_id(t, ext_id, 0);
48 return;
49 }
50
51 if (reset_id == 0)
52 reset_id++;
53
54 array_idx_set(&t->ext_reset_ids, ext_id, &reset_id);
55
56 /* reseting existing data is optional */
57 if (array_is_created(&t->ext_resets)) {
58 reset = array_idx_modifiable(&t->ext_resets, ext_id);
59 if (reset->new_reset_id == (uint32_t)-1)
60 reset->new_reset_id = reset_id;
61 }
62 }
63
64 static void
transaction_update_atomic_reset_ids(struct mail_index_transaction * t)65 transaction_update_atomic_reset_ids(struct mail_index_transaction *t)
66 {
67 const uint32_t *expected_reset_ids;
68 unsigned int ext_id, count;
69
70 if (!array_is_created(&t->ext_reset_atomic))
71 return;
72
73 expected_reset_ids = array_get(&t->ext_reset_atomic, &count);
74 for (ext_id = 0; ext_id < count; ext_id++) {
75 if (expected_reset_ids[ext_id] != 0) {
76 ext_reset_update_atomic(t, ext_id,
77 expected_reset_ids[ext_id]);
78 }
79 }
80 }
81
82 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)83 mail_transaction_drop_range(struct mail_index_transaction *t,
84 struct mail_index_flag_update update,
85 unsigned int update_idx,
86 ARRAY_TYPE(seq_range) *keeps)
87 {
88 const struct seq_range *keep_range;
89 unsigned int i, keep_count;
90
91 keep_range = array_get(keeps, &keep_count);
92 if (keep_count == 1 &&
93 update.uid1 == keep_range[0].seq1 &&
94 update.uid2 == keep_range[0].seq2) {
95 /* everything is kept */
96 return update_idx + 1;
97 }
98
99 array_delete(&t->updates, update_idx, 1);
100
101 /* add back all the updates we want to keep */
102 for (i = 0; i < keep_count; i++, update_idx++) {
103 update.uid1 = keep_range[i].seq1;
104 update.uid2 = keep_range[i].seq2;
105 array_insert(&t->updates, update_idx, &update, 1);
106 }
107 return update_idx;
108 }
109
110 static void
mail_index_transaction_finish_flag_updates(struct mail_index_transaction * t)111 mail_index_transaction_finish_flag_updates(struct mail_index_transaction *t)
112 {
113 const struct mail_index_flag_update *updates, *u;
114 const struct mail_index_record *rec;
115 unsigned int i, count;
116 ARRAY_TYPE(seq_range) keeps;
117 uint32_t seq;
118
119 if (!t->drop_unnecessary_flag_updates || !array_is_created(&t->updates))
120 return;
121
122 t_array_init(&keeps, 64);
123 updates = array_get(&t->updates, &count);
124 for (i = 0; i < count; ) {
125 /* first get the list of changes to drop */
126 u = &updates[i];
127 array_clear(&keeps);
128 for (seq = u->uid1; seq <= u->uid2; seq++) {
129 rec = mail_index_lookup(t->view, seq);
130 if ((rec->flags & u->add_flags) != u->add_flags ||
131 (rec->flags & u->remove_flags) != 0) {
132 /* keep this change */
133 seq_range_array_add(&keeps, seq);
134 }
135 }
136 i = mail_transaction_drop_range(t, updates[i], i, &keeps);
137 updates = array_get(&t->updates, &count);
138 }
139
140 if (array_count(&t->updates) == 0)
141 array_free(&t->updates);
142 }
143
144 static void
mail_index_transaction_check_conflicts(struct mail_index_transaction * t)145 mail_index_transaction_check_conflicts(struct mail_index_transaction *t)
146 {
147 uint32_t seq;
148 bool ret1, ret2;
149
150 i_assert(t->max_modseq != 0);
151 i_assert(t->conflict_seqs != NULL);
152
153 if (t->max_modseq == mail_index_modseq_get_highest(t->view)) {
154 /* no conflicts possible */
155 return;
156 }
157 if (t->min_flagupdate_seq == 0) {
158 /* no flag updates */
159 return;
160 }
161
162 for (seq = t->min_flagupdate_seq; seq <= t->max_flagupdate_seq; seq++) {
163 if (mail_index_modseq_lookup(t->view, seq) > t->max_modseq) {
164 ret1 = mail_index_cancel_flag_updates(t, seq);
165 ret2 = mail_index_cancel_keyword_updates(t, seq);
166 if (ret1 || ret2) {
167 seq_range_array_add_with_init(t->conflict_seqs,
168 16, seq);
169 }
170 }
171 }
172 mail_index_transaction_set_log_updates(t);
173 }
174
175 static uint32_t
mail_index_transaction_get_uid(struct mail_index_transaction * t,uint32_t seq)176 mail_index_transaction_get_uid(struct mail_index_transaction *t, uint32_t seq)
177 {
178 const struct mail_index_record *rec;
179
180 i_assert(seq > 0);
181
182 if (seq >= t->first_new_seq)
183 rec = mail_index_transaction_lookup(t, seq);
184 else {
185 i_assert(seq <= t->view->map->hdr.messages_count);
186 rec = MAIL_INDEX_REC_AT_SEQ(t->view->map, seq);
187 }
188 i_assert(rec->uid != 0);
189 return rec->uid;
190 }
191
192 static void
mail_index_convert_to_uids(struct mail_index_transaction * t,ARRAY_TYPE (seq_array)* array)193 mail_index_convert_to_uids(struct mail_index_transaction *t,
194 ARRAY_TYPE(seq_array) *array)
195 {
196 uint32_t *seq;
197 unsigned int i, count;
198
199 if (!array_is_created(array))
200 return;
201
202 count = array_count(array);
203 for (i = 0; i < count; i++) {
204 seq = array_idx_modifiable(array, i);
205 *seq = mail_index_transaction_get_uid(t, *seq);
206 }
207 }
208
209 static uint32_t
get_nonexpunged_uid2(struct mail_index_transaction * t,uint32_t uid1,uint32_t seq1)210 get_nonexpunged_uid2(struct mail_index_transaction *t,
211 uint32_t uid1, uint32_t seq1)
212 {
213 seq1++;
214
215 while (mail_index_transaction_get_uid(t, seq1) == uid1 + 1) {
216 seq1++;
217 uid1++;
218 }
219 return uid1;
220 }
221
mail_index_transaction_seq_range_to_uid(struct mail_index_transaction * t,ARRAY_TYPE (seq_range)* array)222 void mail_index_transaction_seq_range_to_uid(struct mail_index_transaction *t,
223 ARRAY_TYPE(seq_range) *array)
224 {
225 struct seq_range *range, *new_range;
226 unsigned int i, count;
227 uint32_t uid1, uid2, prev_uid = 0;
228
229 if (!array_is_created(array))
230 return;
231
232 count = array_count(array);
233 for (i = 0; i < count; i++) {
234 range = array_idx_modifiable(array, i);
235
236 uid1 = mail_index_transaction_get_uid(t, range->seq1);
237 uid2 = mail_index_transaction_get_uid(t, range->seq2);
238 i_assert(uid1 > prev_uid);
239 if (uid2 - uid1 == range->seq2 - range->seq1) {
240 /* simple conversion */
241 range->seq1 = uid1;
242 range->seq2 = uid2;
243 prev_uid = uid2;
244 } else {
245 /* remove expunged UIDs */
246 new_range = array_insert_space(array, i);
247 range = array_idx_modifiable(array, i + 1);
248 count++;
249
250 memcpy(new_range, range, array->arr.element_size);
251 new_range->seq1 = uid1;
252 new_range->seq2 = get_nonexpunged_uid2(t, uid1,
253 range->seq1);
254 i_assert(new_range->seq2 < uid2);
255
256 /* continue the range without the inserted seqs */
257 range->seq1 += new_range->seq2 - new_range->seq1 + 1;
258 prev_uid = new_range->seq2;
259 }
260 }
261 }
262
keyword_updates_convert_to_uids(struct mail_index_transaction * t)263 static void keyword_updates_convert_to_uids(struct mail_index_transaction *t)
264 {
265 struct mail_index_transaction_keyword_update *update;
266
267 if (!array_is_created(&t->keyword_updates))
268 return;
269
270 array_foreach_modifiable(&t->keyword_updates, update) {
271 mail_index_transaction_seq_range_to_uid(t, &update->add_seq);
272 mail_index_transaction_seq_range_to_uid(t, &update->remove_seq);
273 }
274 }
275
expunges_convert_to_uids(struct mail_index_transaction * t)276 static void expunges_convert_to_uids(struct mail_index_transaction *t)
277 {
278 struct mail_transaction_expunge_guid *expunges;
279 unsigned int src, dest, count;
280
281 if (!array_is_created(&t->expunges))
282 return;
283
284 mail_index_transaction_sort_expunges(t);
285
286 expunges = array_get_modifiable(&t->expunges, &count);
287 if (count == 0)
288 return;
289
290 /* convert uids and drop duplicates */
291 expunges[0].uid = mail_index_transaction_get_uid(t, expunges[0].uid);
292 for (src = dest = 1; src < count; src++) {
293 expunges[dest].uid =
294 mail_index_transaction_get_uid(t, expunges[src].uid);
295 if (expunges[dest-1].uid != expunges[dest].uid) {
296 if (dest != src) {
297 memcpy(expunges[dest].guid_128, expunges[src].guid_128,
298 sizeof(expunges[dest].guid_128));
299 }
300 dest++;
301 }
302 }
303 array_delete(&t->expunges, dest, count-dest);
304 }
305
306 static void
mail_index_transaction_convert_to_uids(struct mail_index_transaction * t)307 mail_index_transaction_convert_to_uids(struct mail_index_transaction *t)
308 {
309 ARRAY_TYPE(seq_array) *update;
310
311 if (array_is_created(&t->ext_rec_updates)) {
312 array_foreach_modifiable(&t->ext_rec_updates, update)
313 mail_index_convert_to_uids(t, update);
314 }
315 if (array_is_created(&t->ext_rec_atomics)) {
316 array_foreach_modifiable(&t->ext_rec_atomics, update)
317 mail_index_convert_to_uids(t, update);
318 }
319
320 keyword_updates_convert_to_uids(t);
321 expunges_convert_to_uids(t);
322 mail_index_convert_to_uids(t, (void *)&t->modseq_updates);
323 mail_index_transaction_seq_range_to_uid(t, (void *)&t->updates);
324 }
325
mail_index_transaction_finish_so_far(struct mail_index_transaction * t)326 void mail_index_transaction_finish_so_far(struct mail_index_transaction *t)
327 {
328 if (array_is_created(&t->appends))
329 mail_index_transaction_sort_appends(t);
330 mail_index_transaction_finish_flag_updates(t);
331 if (t->max_modseq != 0)
332 mail_index_transaction_check_conflicts(t);
333 }
334
mail_index_transaction_finish(struct mail_index_transaction * t)335 void mail_index_transaction_finish(struct mail_index_transaction *t)
336 {
337 mail_index_transaction_finish_so_far(t);
338
339 if (array_is_created(&t->appends))
340 mail_index_update_day_headers(t, ioloop_time);
341 if (array_is_created(&t->ext_reset_atomic))
342 transaction_update_atomic_reset_ids(t);
343 /* finally convert all sequences to UIDs before we write them,
344 but after we've checked and removed conflicts */
345 mail_index_transaction_convert_to_uids(t);
346
347 /* and kind of ugly way to update highest modseq */
348 if (t->min_highest_modseq != 0)
349 mail_index_update_modseq(t, 0, t->min_highest_modseq);
350 }
351