1 /* Copyright (c) 2003-2018 Dovecot authors, see the included COPYING file */
2
3 /* Inside transaction we keep messages stored in sequences in uid fields.
4 Before they're written to transaction log the sequences are changed to
5 UIDs. */
6
7 #include "lib.h"
8 #include "array.h"
9 #include "time-util.h"
10 #include "mail-index-private.h"
11 #include "mail-index-transaction-private.h"
12
13 static bool
14 mail_index_transaction_has_ext_changes(struct mail_index_transaction *t);
15
16 struct mail_index_record *
mail_index_transaction_lookup(struct mail_index_transaction * t,uint32_t seq)17 mail_index_transaction_lookup(struct mail_index_transaction *t, uint32_t seq)
18 {
19 i_assert(seq >= t->first_new_seq && seq <= t->last_new_seq);
20
21 return array_idx_modifiable(&t->appends, seq - t->first_new_seq);
22 }
23
mail_index_transaction_reset_v(struct mail_index_transaction * t)24 void mail_index_transaction_reset_v(struct mail_index_transaction *t)
25 {
26 ARRAY_TYPE(seq_array) *rec;
27 struct mail_index_transaction_ext_hdr_update *ext_hdr;
28
29 if (array_is_created(&t->ext_rec_updates)) {
30 array_foreach_modifiable(&t->ext_rec_updates, rec) {
31 if (array_is_created(rec))
32 array_free(rec);
33 }
34 array_free(&t->ext_rec_updates);
35 }
36 if (array_is_created(&t->ext_rec_atomics)) {
37 array_foreach_modifiable(&t->ext_rec_atomics, rec) {
38 if (array_is_created(rec))
39 array_free(rec);
40 }
41 array_free(&t->ext_rec_atomics);
42 }
43 if (array_is_created(&t->ext_hdr_updates)) {
44 array_foreach_modifiable(&t->ext_hdr_updates, ext_hdr) {
45 i_free(ext_hdr->data);
46 i_free(ext_hdr->mask);
47 }
48 array_free(&t->ext_hdr_updates);
49 }
50
51 if (array_is_created(&t->keyword_updates)) {
52 struct mail_index_transaction_keyword_update *u;
53
54 array_foreach_modifiable(&t->keyword_updates, u) {
55 if (array_is_created(&u->add_seq))
56 array_free(&u->add_seq);
57 if (array_is_created(&u->remove_seq))
58 array_free(&u->remove_seq);
59 }
60 array_free(&t->keyword_updates);
61 }
62
63 if (array_is_created(&t->appends))
64 array_free(&t->appends);
65 if (array_is_created(&t->modseq_updates))
66 array_free(&t->modseq_updates);
67 if (array_is_created(&t->expunges))
68 array_free(&t->expunges);
69 if (array_is_created(&t->updates))
70 array_free(&t->updates);
71 if (array_is_created(&t->ext_resizes))
72 array_free(&t->ext_resizes);
73 if (array_is_created(&t->ext_resets))
74 array_free(&t->ext_resets);
75 if (array_is_created(&t->ext_reset_ids))
76 array_free(&t->ext_reset_ids);
77 if (array_is_created(&t->ext_reset_atomic))
78 array_free(&t->ext_reset_atomic);
79 buffer_free(&t->attribute_updates);
80 buffer_free(&t->attribute_updates_suffix);
81
82 t->first_new_seq = mail_index_view_get_messages_count(t->view)+1;
83 t->last_new_seq = 0;
84 t->last_update_idx = 0;
85 t->min_flagupdate_seq = 0;
86 t->max_flagupdate_seq = 0;
87 t->min_highest_modseq = 0;
88
89 memset(t->pre_hdr_mask, 0, sizeof(t->pre_hdr_mask));
90 memset(t->post_hdr_mask, 0, sizeof(t->post_hdr_mask));
91
92 t->appends_nonsorted = FALSE;
93 t->expunges_nonsorted = FALSE;
94 t->drop_unnecessary_flag_updates = FALSE;
95 t->pre_hdr_changed = FALSE;
96 t->post_hdr_changed = FALSE;
97 t->reset = FALSE;
98 t->index_deleted = FALSE;
99 t->index_undeleted = FALSE;
100 t->log_updates = FALSE;
101 t->log_ext_updates = FALSE;
102 t->tail_offset_changed = FALSE;
103 }
104
mail_index_transaction_set_log_updates(struct mail_index_transaction * t)105 void mail_index_transaction_set_log_updates(struct mail_index_transaction *t)
106 {
107 /* flag updates aren't included in log_updates */
108 t->log_updates = array_is_created(&t->appends) ||
109 array_is_created(&t->modseq_updates) ||
110 array_is_created(&t->expunges) ||
111 array_is_created(&t->keyword_updates) ||
112 t->attribute_updates != NULL ||
113 t->pre_hdr_changed || t->post_hdr_changed ||
114 t->min_highest_modseq != 0;
115 }
116
mail_index_update_day_headers(struct mail_index_transaction * t,time_t day_stamp)117 void mail_index_update_day_headers(struct mail_index_transaction *t,
118 time_t day_stamp)
119 {
120 struct mail_index_header hdr;
121 const struct mail_index_record *rec;
122 const int max_days = N_ELEMENTS(hdr.day_first_uid);
123 time_t stamp;
124 int i, days;
125
126 hdr = *mail_index_get_header(t->view);
127 rec = array_front(&t->appends);
128
129 stamp = time_to_local_day_start(day_stamp);
130 if ((time_t)hdr.day_stamp >= stamp)
131 return;
132
133 /* get number of days since last message */
134 days = (stamp - hdr.day_stamp) / (3600*24);
135 if (days > max_days)
136 days = max_days;
137
138 /* @UNSAFE: move days forward and fill the missing days with old
139 day_first_uid[0]. */
140 if (days > 0 && days < max_days)
141 memmove(hdr.day_first_uid + days, hdr.day_first_uid,
142 (max_days - days) * sizeof(hdr.day_first_uid[0]));
143 for (i = 1; i < days; i++)
144 hdr.day_first_uid[i] = hdr.day_first_uid[0];
145
146 hdr.day_stamp = stamp;
147 hdr.day_first_uid[0] = rec->uid;
148
149 mail_index_update_header(t,
150 offsetof(struct mail_index_header, day_stamp),
151 &hdr.day_stamp, sizeof(hdr.day_stamp), FALSE);
152 mail_index_update_header(t,
153 offsetof(struct mail_index_header, day_first_uid),
154 hdr.day_first_uid, sizeof(hdr.day_first_uid), FALSE);
155 }
156
mail_index_append(struct mail_index_transaction * t,uint32_t uid,uint32_t * seq_r)157 void mail_index_append(struct mail_index_transaction *t, uint32_t uid,
158 uint32_t *seq_r)
159 {
160 struct mail_index_record *rec;
161
162 i_assert(!t->no_appends);
163
164 t->log_updates = TRUE;
165
166 if (!array_is_created(&t->appends))
167 i_array_init(&t->appends, 32);
168
169 /* sequence number is visible only inside given view,
170 so let it generate it */
171 if (t->last_new_seq != 0)
172 *seq_r = ++t->last_new_seq;
173 else
174 *seq_r = t->last_new_seq = t->first_new_seq;
175
176 rec = array_append_space(&t->appends);
177 if (uid != 0) {
178 rec->uid = uid;
179 if (!t->appends_nonsorted &&
180 t->last_new_seq != t->first_new_seq) {
181 /* if previous record's UID is larger than this one,
182 we'll have to sort the appends later */
183 rec = mail_index_transaction_lookup(t, *seq_r - 1);
184 if (rec->uid > uid)
185 t->appends_nonsorted = TRUE;
186 else if (rec->uid == uid)
187 i_panic("Duplicate UIDs added in transaction");
188 }
189 if (t->highest_append_uid < uid)
190 t->highest_append_uid = uid;
191 }
192 }
193
mail_index_append_finish_uids(struct mail_index_transaction * t,uint32_t first_uid,ARRAY_TYPE (seq_range)* uids_r)194 void mail_index_append_finish_uids(struct mail_index_transaction *t,
195 uint32_t first_uid,
196 ARRAY_TYPE(seq_range) *uids_r)
197 {
198 return mail_index_append_finish_uids_full(t, first_uid, first_uid, uids_r);
199 }
200
mail_index_append_finish_uids_full(struct mail_index_transaction * t,uint32_t min_allowed_uid,uint32_t first_new_uid,ARRAY_TYPE (seq_range)* uids_r)201 void mail_index_append_finish_uids_full(struct mail_index_transaction *t,
202 uint32_t min_allowed_uid,
203 uint32_t first_new_uid,
204 ARRAY_TYPE(seq_range) *uids_r)
205 {
206 struct mail_index_record *recs;
207 unsigned int i, count;
208 struct seq_range *range;
209 uint32_t next_uid;
210
211 if (!array_is_created(&t->appends))
212 return;
213
214 i_assert(min_allowed_uid <= first_new_uid);
215 i_assert(first_new_uid < (uint32_t)-1);
216
217 /* first find the highest assigned uid */
218 recs = array_get_modifiable(&t->appends, &count);
219 i_assert(count > 0);
220
221 next_uid = first_new_uid;
222 for (i = 0; i < count; i++) {
223 if (next_uid <= recs[i].uid)
224 next_uid = recs[i].uid + 1;
225 }
226 i_assert(next_uid > 0 && next_uid < (uint32_t)-1);
227
228 /* assign missing uids */
229 for (i = 0; i < count; i++) {
230 if (recs[i].uid == 0 || recs[i].uid < min_allowed_uid) {
231 i_assert(next_uid < (uint32_t)-1);
232 recs[i].uid = next_uid++;
233 if (t->highest_append_uid < recs[i].uid)
234 t->highest_append_uid = recs[i].uid;
235 } else {
236 t->appends_nonsorted = TRUE;
237 }
238 }
239
240 /* write the saved uids range */
241 array_clear(uids_r);
242 range = array_append_space(uids_r);
243 range->seq1 = range->seq2 = recs[0].uid;
244 for (i = 1; i < count; i++) {
245 if (range->seq2 + 1 == recs[i].uid)
246 range->seq2++;
247 else {
248 range = array_append_space(uids_r);
249 range->seq1 = range->seq2 = recs[i].uid;
250 }
251 }
252 }
253
mail_index_update_modseq(struct mail_index_transaction * t,uint32_t seq,uint64_t min_modseq)254 void mail_index_update_modseq(struct mail_index_transaction *t, uint32_t seq,
255 uint64_t min_modseq)
256 {
257 struct mail_transaction_modseq_update *u;
258
259 /* modseq=1 is the minimum always and it's only for mails that were
260 created/modified before modseqs were enabled. */
261 if (min_modseq <= 1)
262 return;
263
264 if (!array_is_created(&t->modseq_updates))
265 i_array_init(&t->modseq_updates, 32);
266
267 u = array_append_space(&t->modseq_updates);
268 u->uid = seq;
269 u->modseq_low32 = min_modseq & 0xffffffff;
270 u->modseq_high32 = min_modseq >> 32;
271
272 t->log_updates = TRUE;
273 }
274
mail_index_update_highest_modseq(struct mail_index_transaction * t,uint64_t min_modseq)275 void mail_index_update_highest_modseq(struct mail_index_transaction *t,
276 uint64_t min_modseq)
277 {
278 /* modseq=1 is the minimum always and it's only for mails that were
279 created/modified before modseqs were enabled. */
280 if (min_modseq <= 1)
281 return;
282
283 if (t->min_highest_modseq < min_modseq)
284 t->min_highest_modseq = min_modseq;
285
286 t->log_updates = TRUE;
287 }
288
289 static void
mail_index_revert_ext(ARRAY_TYPE (seq_array_array)* ext_updates,uint32_t seq)290 mail_index_revert_ext(ARRAY_TYPE(seq_array_array) *ext_updates,
291 uint32_t seq)
292 {
293 ARRAY_TYPE(seq_array) *seqs;
294 unsigned int idx;
295
296 if (!array_is_created(ext_updates))
297 return;
298
299 array_foreach_modifiable(ext_updates, seqs) {
300 if (array_is_created(seqs) &&
301 mail_index_seq_array_lookup(seqs, seq, &idx))
302 array_delete(seqs, idx, 1);
303 }
304 }
305
306 static void
mail_index_revert_changes_common(struct mail_index_transaction * t,uint32_t seq)307 mail_index_revert_changes_common(struct mail_index_transaction *t, uint32_t seq)
308 {
309 struct mail_index_transaction_keyword_update *kw_update;
310 unsigned int i;
311
312 /* remove extension updates */
313 mail_index_revert_ext(&t->ext_rec_updates, seq);
314 mail_index_revert_ext(&t->ext_rec_atomics, seq);
315 t->log_ext_updates = mail_index_transaction_has_ext_changes(t);
316
317 /* remove keywords */
318 if (array_is_created(&t->keyword_updates)) {
319 array_foreach_modifiable(&t->keyword_updates, kw_update) {
320 if (array_is_created(&kw_update->add_seq)) {
321 seq_range_array_remove(&kw_update->add_seq,
322 seq);
323 }
324 if (array_is_created(&kw_update->remove_seq)) {
325 seq_range_array_remove(&kw_update->remove_seq,
326 seq);
327 }
328 }
329 }
330 /* remove modseqs */
331 if (array_is_created(&t->modseq_updates) &&
332 mail_index_seq_array_lookup((void *)&t->modseq_updates, seq, &i))
333 array_delete(&t->modseq_updates, i, 1);
334 }
335
mail_index_revert_changes(struct mail_index_transaction * t,uint32_t seq)336 void mail_index_revert_changes(struct mail_index_transaction *t, uint32_t seq)
337 {
338 mail_index_revert_changes_common(t, seq);
339 (void)mail_index_cancel_flag_updates(t, seq);
340 }
341
342 static void
mail_index_expunge_last_append(struct mail_index_transaction * t,uint32_t seq)343 mail_index_expunge_last_append(struct mail_index_transaction *t, uint32_t seq)
344 {
345 i_assert(seq == t->last_new_seq);
346
347 mail_index_revert_changes_common(t, seq);
348
349 /* and finally remove the append itself */
350 array_delete(&t->appends, seq - t->first_new_seq, 1);
351 t->last_new_seq--;
352 if (t->first_new_seq > t->last_new_seq) {
353 t->last_new_seq = 0;
354 t->appends_nonsorted = FALSE;
355 array_free(&t->appends);
356 }
357 mail_index_transaction_set_log_updates(t);
358 }
359
mail_index_expunge(struct mail_index_transaction * t,uint32_t seq)360 void mail_index_expunge(struct mail_index_transaction *t, uint32_t seq)
361 {
362 static guid_128_t null_guid =
363 { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 };
364 mail_index_expunge_guid(t, seq, null_guid);
365 }
366
mail_index_expunge_guid(struct mail_index_transaction * t,uint32_t seq,const guid_128_t guid_128)367 void mail_index_expunge_guid(struct mail_index_transaction *t, uint32_t seq,
368 const guid_128_t guid_128)
369 {
370 const struct mail_transaction_expunge_guid *expunges;
371 struct mail_transaction_expunge_guid *expunge;
372 unsigned int count;
373
374 i_assert(seq > 0);
375 if (seq >= t->first_new_seq) {
376 /* we can handle only the last append. otherwise we'd have to
377 renumber sequences and that gets tricky. for now this is
378 enough, since we typically want to expunge all the
379 appends. */
380 mail_index_expunge_last_append(t, seq);
381 } else {
382 t->log_updates = TRUE;
383
384 /* ignore duplicates here. drop them when committing. */
385 if (!array_is_created(&t->expunges))
386 i_array_init(&t->expunges, 64);
387 else if (!t->expunges_nonsorted) {
388 /* usually expunges are added in increasing order. */
389 expunges = array_get(&t->expunges, &count);
390 if (count > 0 && seq < expunges[count-1].uid)
391 t->expunges_nonsorted = TRUE;
392 }
393 expunge = array_append_space(&t->expunges);
394 expunge->uid = seq;
395 memcpy(expunge->guid_128, guid_128, sizeof(expunge->guid_128));
396 }
397 }
398
update_minmax_flagupdate_seq(struct mail_index_transaction * t,uint32_t seq1,uint32_t seq2)399 static void update_minmax_flagupdate_seq(struct mail_index_transaction *t,
400 uint32_t seq1, uint32_t seq2)
401 {
402 if (t->min_flagupdate_seq == 0) {
403 t->min_flagupdate_seq = seq1;
404 t->max_flagupdate_seq = seq2;
405 } else {
406 if (t->min_flagupdate_seq > seq1)
407 t->min_flagupdate_seq = seq1;
408 if (t->max_flagupdate_seq < seq2)
409 t->max_flagupdate_seq = seq2;
410 }
411 }
412
413 unsigned int
mail_index_transaction_get_flag_update_pos(struct mail_index_transaction * t,unsigned int left_idx,unsigned int right_idx,uint32_t seq)414 mail_index_transaction_get_flag_update_pos(struct mail_index_transaction *t,
415 unsigned int left_idx,
416 unsigned int right_idx,
417 uint32_t seq)
418 {
419 const struct mail_index_flag_update *updates;
420 unsigned int idx, count;
421
422 updates = array_get(&t->updates, &count);
423 i_assert(left_idx <= right_idx && right_idx <= count);
424 i_assert(count < INT_MAX);
425
426 /* find the first update with either overlapping range,
427 or the update which will come after our insert */
428 idx = left_idx;
429 while (left_idx < right_idx) {
430 idx = (left_idx + right_idx) / 2;
431
432 if (updates[idx].uid2 < seq)
433 left_idx = idx+1;
434 else if (updates[idx].uid1 > seq)
435 right_idx = idx;
436 else
437 break;
438 }
439 if (left_idx > idx)
440 idx++;
441 return idx;
442 }
443
444 static void
mail_index_insert_flag_update(struct mail_index_transaction * t,struct mail_index_flag_update u,unsigned int idx)445 mail_index_insert_flag_update(struct mail_index_transaction *t,
446 struct mail_index_flag_update u,
447 unsigned int idx)
448 {
449 struct mail_index_flag_update *updates, tmp_update;
450 unsigned int count, first_idx, max;
451
452 updates = array_get_modifiable(&t->updates, &count);
453
454 /* overlapping ranges, split/merge them */
455 i_assert(idx == 0 || updates[idx-1].uid2 < u.uid1);
456 i_assert(idx == count || updates[idx].uid2 >= u.uid1);
457
458 /* first we'll just add the changes without trying to merge anything */
459 first_idx = idx;
460 for (; idx < count && u.uid2 >= updates[idx].uid1; idx++) {
461 i_assert(u.uid1 <= updates[idx].uid2);
462 if (u.uid1 != updates[idx].uid1 &&
463 (updates[idx].add_flags != u.add_flags ||
464 updates[idx].remove_flags != u.remove_flags)) {
465 if (u.uid1 < updates[idx].uid1) {
466 /* insert new update */
467 tmp_update = u;
468 tmp_update.uid2 = updates[idx].uid1 - 1;
469 } else {
470 /* split existing update from beginning */
471 tmp_update = updates[idx];
472 tmp_update.uid2 = u.uid1 - 1;
473 updates[idx].uid1 = u.uid1;
474 }
475
476 i_assert(tmp_update.uid1 <= tmp_update.uid2);
477 i_assert(updates[idx].uid1 <= updates[idx].uid2);
478
479 array_insert(&t->updates, idx, &tmp_update, 1);
480 updates = array_get_modifiable(&t->updates, &count);
481 idx++;
482 } else if (u.uid1 < updates[idx].uid1) {
483 updates[idx].uid1 = u.uid1;
484 }
485
486 if (u.uid2 < updates[idx].uid2 &&
487 (updates[idx].add_flags != u.add_flags ||
488 updates[idx].remove_flags != u.remove_flags)) {
489 /* split existing update from end */
490 tmp_update = updates[idx];
491 tmp_update.uid2 = u.uid2;
492 updates[idx].uid1 = u.uid2 + 1;
493
494 i_assert(tmp_update.uid1 <= tmp_update.uid2);
495 i_assert(updates[idx].uid1 <= updates[idx].uid2);
496
497 array_insert(&t->updates, idx, &tmp_update, 1);
498 updates = array_get_modifiable(&t->updates, &count);
499 }
500
501 updates[idx].add_flags =
502 (updates[idx].add_flags | u.add_flags) &
503 ENUM_NEGATE(u.remove_flags);
504 updates[idx].remove_flags =
505 (updates[idx].remove_flags | u.remove_flags) &
506 ENUM_NEGATE(u.add_flags);
507 u.uid1 = updates[idx].uid2 + 1;
508
509 if (updates[idx].add_flags == 0 &&
510 updates[idx].remove_flags == 0) {
511 /* we can remove this update completely */
512 array_delete(&t->updates, idx, 1);
513 updates = array_get_modifiable(&t->updates, &count);
514 }
515
516 if (u.uid1 > u.uid2) {
517 /* break here before idx++ so last_update_idx is set
518 correctly */
519 break;
520 }
521 }
522 i_assert(idx <= count);
523
524 if (u.uid1 <= u.uid2) {
525 i_assert(idx == 0 || updates[idx-1].uid2 < u.uid1);
526 i_assert(idx == count || updates[idx].uid1 > u.uid2);
527 array_insert(&t->updates, idx, &u, 1);
528 }
529 updates = array_get_modifiable(&t->updates, &count);
530 t->last_update_idx = idx == count ? count-1 : idx;
531
532 /* merge everything */
533 idx = first_idx == 0 ? 0 : first_idx - 1;
534 max = count == 0 ? 0 : I_MIN(t->last_update_idx + 1, count-1);
535 for (; idx < max; ) {
536 if (updates[idx].uid2 + 1 == updates[idx+1].uid1 &&
537 updates[idx].add_flags == updates[idx+1].add_flags &&
538 updates[idx].remove_flags == updates[idx+1].remove_flags) {
539 /* merge */
540 updates[idx].uid2 = updates[idx+1].uid2;
541 array_delete(&t->updates, idx + 1, 1);
542 max--;
543 if (t->last_update_idx > idx)
544 t->last_update_idx--;
545 updates = array_get_modifiable(&t->updates, &count);
546 } else {
547 idx++;
548 }
549 }
550 }
551
mail_index_record_modify_flags(struct mail_index_record * rec,enum modify_type modify_type,enum mail_flags flags)552 static void mail_index_record_modify_flags(struct mail_index_record *rec,
553 enum modify_type modify_type,
554 enum mail_flags flags)
555 {
556 switch (modify_type) {
557 case MODIFY_REPLACE:
558 rec->flags = flags;
559 break;
560 case MODIFY_ADD:
561 rec->flags |= flags;
562 break;
563 case MODIFY_REMOVE:
564 rec->flags &= ENUM_NEGATE(flags);
565 break;
566 }
567 }
568
mail_index_update_flags_range(struct mail_index_transaction * t,uint32_t seq1,uint32_t seq2,enum modify_type modify_type,enum mail_flags flags)569 void mail_index_update_flags_range(struct mail_index_transaction *t,
570 uint32_t seq1, uint32_t seq2,
571 enum modify_type modify_type,
572 enum mail_flags flags)
573 {
574 struct mail_index_record *rec;
575 struct mail_index_flag_update u, *last_update;
576 unsigned int idx, first_idx, count;
577
578 update_minmax_flagupdate_seq(t, seq1, seq2);
579 if (seq2 >= t->first_new_seq) {
580 /* updates for appended messages, modify them directly */
581 uint32_t seq;
582
583 for (seq = I_MAX(t->first_new_seq, seq1); seq <= seq2; seq++) {
584 rec = mail_index_transaction_lookup(t, seq);
585 mail_index_record_modify_flags(rec, modify_type, flags);
586 }
587 if (seq1 >= t->first_new_seq)
588 return;
589
590 /* range contains also existing messages. update them next. */
591 seq2 = t->first_new_seq - 1;
592 }
593
594 i_assert(seq1 <= seq2 && seq1 > 0);
595 i_assert(seq2 <= mail_index_view_get_messages_count(t->view));
596
597 if ((t->flags & MAIL_INDEX_TRANSACTION_FLAG_AVOID_FLAG_UPDATES) != 0)
598 t->drop_unnecessary_flag_updates = TRUE;
599
600 i_zero(&u);
601 u.uid1 = seq1;
602 u.uid2 = seq2;
603
604 switch (modify_type) {
605 case MODIFY_REPLACE:
606 u.add_flags = flags;
607 u.remove_flags = ENUM_NEGATE(flags) & MAIL_INDEX_FLAGS_MASK;
608 break;
609 case MODIFY_ADD:
610 if (flags == 0)
611 return;
612 u.add_flags = flags;
613 break;
614 case MODIFY_REMOVE:
615 if (flags == 0)
616 return;
617 u.remove_flags = flags;
618 break;
619 }
620
621 if (!array_is_created(&t->updates)) {
622 i_array_init(&t->updates, 256);
623 array_push_back(&t->updates, &u);
624 return;
625 }
626
627 last_update = array_get_modifiable(&t->updates, &count);
628 if (t->last_update_idx < count) {
629 /* fast path - hopefully we're updating the next message,
630 or a message that is to be appended as last update */
631 last_update += t->last_update_idx;
632 if (seq1 - 1 == last_update->uid2) {
633 if (u.add_flags == last_update->add_flags &&
634 u.remove_flags == last_update->remove_flags &&
635 (t->last_update_idx + 1 == count ||
636 last_update[1].uid1 > seq2)) {
637 /* we can just update the UID range */
638 last_update->uid2 = seq2;
639 return;
640 }
641 } else if (seq1 > last_update->uid2) {
642 /* hopefully we can just append it */
643 t->last_update_idx++;
644 last_update++;
645 }
646 }
647
648 if (t->last_update_idx == count)
649 array_push_back(&t->updates, &u);
650 else {
651 i_assert(t->last_update_idx < count);
652
653 /* slow path */
654 if (seq1 > last_update->uid2) {
655 /* added after this */
656 first_idx = t->last_update_idx + 1;
657 } else {
658 /* added before this or on top of this */
659 first_idx = 0;
660 count = t->last_update_idx + 1;
661 }
662 idx = mail_index_transaction_get_flag_update_pos(t, first_idx,
663 count, u.uid1);
664 mail_index_insert_flag_update(t, u, idx);
665 }
666 }
667
mail_index_update_flags(struct mail_index_transaction * t,uint32_t seq,enum modify_type modify_type,enum mail_flags flags)668 void mail_index_update_flags(struct mail_index_transaction *t, uint32_t seq,
669 enum modify_type modify_type,
670 enum mail_flags flags)
671 {
672 mail_index_update_flags_range(t, seq, seq, modify_type, flags);
673 }
674
675 static void
mail_index_attribute_set_full(struct mail_index_transaction * t,const char * key,bool pvt,char prefix,time_t timestamp,uint32_t value_len)676 mail_index_attribute_set_full(struct mail_index_transaction *t,
677 const char *key, bool pvt, char prefix,
678 time_t timestamp, uint32_t value_len)
679 {
680 uint32_t ts = timestamp;
681
682 if (t->attribute_updates == NULL) {
683 t->attribute_updates = buffer_create_dynamic(default_pool, 64);
684 t->attribute_updates_suffix = buffer_create_dynamic(default_pool, 64);
685 }
686 buffer_append_c(t->attribute_updates, prefix);
687 buffer_append_c(t->attribute_updates, pvt ? 'p' : 's');
688 buffer_append(t->attribute_updates, key, strlen(key)+1);
689
690 buffer_append(t->attribute_updates_suffix, &ts, sizeof(ts));
691 if (prefix == '+') {
692 buffer_append(t->attribute_updates_suffix,
693 &value_len, sizeof(value_len));
694 }
695 t->log_updates = TRUE;
696 }
697
mail_index_attribute_set(struct mail_index_transaction * t,bool pvt,const char * key,time_t timestamp,uint32_t value_len)698 void mail_index_attribute_set(struct mail_index_transaction *t,
699 bool pvt, const char *key,
700 time_t timestamp, uint32_t value_len)
701 {
702 mail_index_attribute_set_full(t, key, pvt, '+', timestamp, value_len);
703 }
704
mail_index_attribute_unset(struct mail_index_transaction * t,bool pvt,const char * key,time_t timestamp)705 void mail_index_attribute_unset(struct mail_index_transaction *t,
706 bool pvt, const char *key,
707 time_t timestamp)
708 {
709 mail_index_attribute_set_full(t, key, pvt, '-', timestamp, 0);
710 }
711
mail_index_update_header(struct mail_index_transaction * t,size_t offset,const void * data,size_t size,bool prepend)712 void mail_index_update_header(struct mail_index_transaction *t,
713 size_t offset, const void *data, size_t size,
714 bool prepend)
715 {
716 i_assert(offset < sizeof(t->pre_hdr_change));
717 i_assert(size <= sizeof(t->pre_hdr_change) - offset);
718
719 t->log_updates = TRUE;
720
721 if (prepend) {
722 t->pre_hdr_changed = TRUE;
723 memcpy(t->pre_hdr_change + offset, data, size);
724 for (; size > 0; size--)
725 t->pre_hdr_mask[offset++] = 1;
726 } else {
727 t->post_hdr_changed = TRUE;
728 memcpy(t->post_hdr_change + offset, data, size);
729 for (; size > 0; size--)
730 t->post_hdr_mask[offset++] = 1;
731 }
732 }
733
734 static void
mail_index_ext_rec_updates_resize(struct mail_index_transaction * t,uint32_t ext_id,uint16_t new_record_size)735 mail_index_ext_rec_updates_resize(struct mail_index_transaction *t,
736 uint32_t ext_id, uint16_t new_record_size)
737 {
738 ARRAY_TYPE(seq_array) *array, old_array;
739 unsigned int i;
740
741 if (!array_is_created(&t->ext_rec_updates))
742 return;
743 array = array_idx_modifiable(&t->ext_rec_updates, ext_id);
744 if (!array_is_created(array))
745 return;
746
747 old_array = *array;
748 i_zero(array);
749 mail_index_seq_array_alloc(array, new_record_size);
750
751 /* copy the records' beginnings. leave the end zero-filled. */
752 for (i = 0; i < array_count(&old_array); i++) {
753 const void *old_record = array_idx(&old_array, i);
754
755 memcpy(array_append_space(array), old_record,
756 old_array.arr.element_size);
757 }
758 array_free(&old_array);
759 }
760
mail_index_ext_resize(struct mail_index_transaction * t,uint32_t ext_id,uint32_t hdr_size,uint16_t record_size,uint16_t record_align)761 void mail_index_ext_resize(struct mail_index_transaction *t, uint32_t ext_id,
762 uint32_t hdr_size, uint16_t record_size,
763 uint16_t record_align)
764 {
765 const struct mail_index_registered_ext *rext;
766 const struct mail_transaction_ext_intro *resizes;
767 unsigned int resizes_count;
768 struct mail_transaction_ext_intro intro;
769 uint32_t old_record_size = 0, old_record_align, old_header_size;
770
771 i_zero(&intro);
772 rext = array_idx(&t->view->index->extensions, ext_id);
773
774 /* get ext_id from transaction's map if it's there */
775 if (!mail_index_map_get_ext_idx(t->view->map, ext_id, &intro.ext_id)) {
776 /* have to create it */
777 intro.ext_id = (uint32_t)-1;
778 old_record_align = rext->record_align;
779 old_header_size = rext->hdr_size;
780 } else {
781 const struct mail_index_ext *ext;
782
783 ext = array_idx(&t->view->map->extensions, intro.ext_id);
784 old_record_align = ext->record_align;
785 old_header_size = ext->hdr_size;
786 }
787
788 /* get the record size. if there are any existing record updates,
789 they're using the registered size, not the map's existing
790 record_size. */
791 if (array_is_created(&t->ext_resizes))
792 resizes = array_get(&t->ext_resizes, &resizes_count);
793 else {
794 resizes = NULL;
795 resizes_count = 0;
796 }
797 if (ext_id < resizes_count && resizes[ext_id].name_size != 0) {
798 /* already resized once. use the resized value. */
799 old_record_size = resizes[ext_id].record_size;
800 } else {
801 /* use the registered values. */
802 old_record_size = rext->record_size;
803 }
804
805 if (record_size != old_record_size && record_size != (uint16_t)-1) {
806 /* if record_size grows, we'll just resize the existing
807 ext_rec_updates array. it's not possible to shrink
808 record_size without data loss. */
809 i_assert(record_size > old_record_size);
810 mail_index_ext_rec_updates_resize(t, ext_id, record_size);
811 }
812
813 t->log_ext_updates = TRUE;
814
815 if (!array_is_created(&t->ext_resizes))
816 i_array_init(&t->ext_resizes, ext_id + 2);
817
818 intro.hdr_size = hdr_size != (uint32_t)-1 ? hdr_size : old_header_size;
819 if (record_size != (uint16_t)-1) {
820 i_assert(record_align != (uint16_t)-1);
821 intro.record_size = record_size;
822 intro.record_align = record_align;
823 } else {
824 i_assert(record_align == (uint16_t)-1);
825 intro.record_size = old_record_size;
826 intro.record_align = old_record_align;
827 }
828 intro.name_size = 1;
829 array_idx_set(&t->ext_resizes, ext_id, &intro);
830 }
831
mail_index_ext_resize_hdr(struct mail_index_transaction * t,uint32_t ext_id,uint32_t hdr_size)832 void mail_index_ext_resize_hdr(struct mail_index_transaction *t,
833 uint32_t ext_id, uint32_t hdr_size)
834 {
835 mail_index_ext_resize(t, ext_id, hdr_size, (uint16_t)-1, (uint16_t)-1);
836 }
837
mail_index_ext_reset(struct mail_index_transaction * t,uint32_t ext_id,uint32_t reset_id,bool clear_data)838 void mail_index_ext_reset(struct mail_index_transaction *t, uint32_t ext_id,
839 uint32_t reset_id, bool clear_data)
840 {
841 struct mail_transaction_ext_reset reset;
842
843 i_assert(reset_id != 0);
844
845 i_zero(&reset);
846 reset.new_reset_id = reset_id;
847 reset.preserve_data = clear_data ? 0 : 1;
848
849 mail_index_ext_set_reset_id(t, ext_id, reset_id);
850
851 if (!array_is_created(&t->ext_resets))
852 i_array_init(&t->ext_resets, ext_id + 2);
853 array_idx_set(&t->ext_resets, ext_id, &reset);
854 t->log_ext_updates = TRUE;
855 }
856
mail_index_ext_reset_inc(struct mail_index_transaction * t,uint32_t ext_id,uint32_t prev_reset_id,bool clear_data)857 void mail_index_ext_reset_inc(struct mail_index_transaction *t, uint32_t ext_id,
858 uint32_t prev_reset_id, bool clear_data)
859 {
860 uint32_t expected_reset_id = prev_reset_id + 1;
861
862 mail_index_ext_reset(t, ext_id, (uint32_t)-1, clear_data);
863
864 if (!array_is_created(&t->ext_reset_atomic))
865 i_array_init(&t->ext_reset_atomic, ext_id + 2);
866 array_idx_set(&t->ext_reset_atomic, ext_id, &expected_reset_id);
867 }
868
869 static bool
mail_index_transaction_has_ext_updates(const ARRAY_TYPE (seq_array_array)* arr)870 mail_index_transaction_has_ext_updates(const ARRAY_TYPE(seq_array_array) *arr)
871 {
872 const ARRAY_TYPE(seq_array) *array;
873
874 if (array_is_created(arr)) {
875 array_foreach(arr, array) {
876 if (array_is_created(array))
877 return TRUE;
878 }
879 }
880 return FALSE;
881 }
882
883 static bool
mail_index_transaction_has_ext_changes(struct mail_index_transaction * t)884 mail_index_transaction_has_ext_changes(struct mail_index_transaction *t)
885 {
886 if (mail_index_transaction_has_ext_updates(&t->ext_rec_updates))
887 return TRUE;
888 if (mail_index_transaction_has_ext_updates(&t->ext_rec_atomics))
889 return TRUE;
890
891 if (array_is_created(&t->ext_hdr_updates)) {
892 const struct mail_index_transaction_ext_hdr_update *hdr;
893
894 array_foreach(&t->ext_hdr_updates, hdr) {
895 if (hdr->alloc_size > 0)
896 return TRUE;
897 }
898 }
899 if (array_is_created(&t->ext_resets)) {
900 const struct mail_transaction_ext_reset *reset;
901
902 array_foreach(&t->ext_resets, reset) {
903 if (reset->new_reset_id != 0)
904 return TRUE;
905 }
906 }
907 if (array_is_created(&t->ext_resizes)) {
908 const struct mail_transaction_ext_intro *resize;
909
910 array_foreach(&t->ext_resizes, resize) {
911 if (resize->name_size > 0)
912 return TRUE;
913 }
914 }
915 return FALSE;
916 }
917
918 static void
mail_index_ext_update_reset(ARRAY_TYPE (seq_array_array)* arr,uint32_t ext_id)919 mail_index_ext_update_reset(ARRAY_TYPE(seq_array_array) *arr, uint32_t ext_id)
920 {
921 if (array_is_created(arr) && ext_id < array_count(arr)) {
922 /* if extension records have been updated, clear them */
923 ARRAY_TYPE(seq_array) *array;
924
925 array = array_idx_modifiable(arr, ext_id);
926 if (array_is_created(array))
927 array_clear(array);
928 }
929 }
930
931 static void
mail_index_ext_reset_changes(struct mail_index_transaction * t,uint32_t ext_id)932 mail_index_ext_reset_changes(struct mail_index_transaction *t, uint32_t ext_id)
933 {
934 mail_index_ext_update_reset(&t->ext_rec_updates, ext_id);
935 mail_index_ext_update_reset(&t->ext_rec_atomics, ext_id);
936 if (array_is_created(&t->ext_hdr_updates) &&
937 ext_id < array_count(&t->ext_hdr_updates)) {
938 /* if extension headers have been updated, clear them */
939 struct mail_index_transaction_ext_hdr_update *hdr;
940
941 hdr = array_idx_modifiable(&t->ext_hdr_updates, ext_id);
942 if (hdr->alloc_size > 0) {
943 i_free_and_null(hdr->mask);
944 i_free_and_null(hdr->data);
945 }
946 hdr->alloc_size = 0;
947 }
948 if (array_is_created(&t->ext_resets) &&
949 ext_id < array_count(&t->ext_resets)) {
950 /* clear resets */
951 array_idx_clear(&t->ext_resets, ext_id);
952 }
953 if (array_is_created(&t->ext_resizes) &&
954 ext_id < array_count(&t->ext_resizes)) {
955 /* clear resizes */
956 array_idx_clear(&t->ext_resizes, ext_id);
957 }
958
959 t->log_ext_updates = mail_index_transaction_has_ext_changes(t);
960 }
961
mail_index_ext_using_reset_id(struct mail_index_transaction * t,uint32_t ext_id,uint32_t reset_id)962 void mail_index_ext_using_reset_id(struct mail_index_transaction *t,
963 uint32_t ext_id, uint32_t reset_id)
964 {
965 uint32_t *reset_id_p;
966 bool changed;
967
968 if (!array_is_created(&t->ext_reset_ids))
969 i_array_init(&t->ext_reset_ids, ext_id + 2);
970 reset_id_p = array_idx_get_space(&t->ext_reset_ids, ext_id);
971 changed = *reset_id_p != reset_id && *reset_id_p != 0;
972 *reset_id_p = reset_id;
973 if (changed) {
974 /* reset_id changed, clear existing changes */
975 mail_index_ext_reset_changes(t, ext_id);
976 }
977 }
978
mail_index_ext_set_reset_id(struct mail_index_transaction * t,uint32_t ext_id,uint32_t reset_id)979 void mail_index_ext_set_reset_id(struct mail_index_transaction *t,
980 uint32_t ext_id, uint32_t reset_id)
981 {
982 mail_index_ext_using_reset_id(t, ext_id, reset_id);
983 /* make sure the changes get reset, even if reset_id doesn't change */
984 mail_index_ext_reset_changes(t, ext_id);
985 }
986
mail_index_update_header_ext(struct mail_index_transaction * t,uint32_t ext_id,size_t offset,const void * data,size_t size)987 void mail_index_update_header_ext(struct mail_index_transaction *t,
988 uint32_t ext_id, size_t offset,
989 const void *data, size_t size)
990 {
991 struct mail_index_transaction_ext_hdr_update *hdr;
992 size_t new_size;
993
994 i_assert(offset <= (uint32_t)-1 && size <= (uint32_t)-1 &&
995 offset + size <= (uint32_t)-1);
996
997 if (!array_is_created(&t->ext_hdr_updates))
998 i_array_init(&t->ext_hdr_updates, ext_id + 2);
999
1000 hdr = array_idx_get_space(&t->ext_hdr_updates, ext_id);
1001 if (hdr->alloc_size < offset || hdr->alloc_size - offset < size) {
1002 i_assert(size < SIZE_MAX - offset);
1003 new_size = nearest_power(offset + size);
1004 hdr->mask = i_realloc(hdr->mask, hdr->alloc_size, new_size);
1005 hdr->data = i_realloc(hdr->data, hdr->alloc_size, new_size);
1006 hdr->alloc_size = new_size;
1007 }
1008 memset(hdr->mask + offset, 1, size);
1009 memcpy(hdr->data + offset, data, size);
1010
1011 t->log_ext_updates = TRUE;
1012 }
1013
mail_index_update_ext(struct mail_index_transaction * t,uint32_t seq,uint32_t ext_id,const void * data,void * old_data_r)1014 void mail_index_update_ext(struct mail_index_transaction *t, uint32_t seq,
1015 uint32_t ext_id, const void *data, void *old_data_r)
1016 {
1017 struct mail_index *index = t->view->index;
1018 const struct mail_index_registered_ext *rext;
1019 const struct mail_transaction_ext_intro *intro;
1020 uint16_t record_size;
1021 ARRAY_TYPE(seq_array) *array;
1022 unsigned int count;
1023
1024 i_assert(seq > 0 &&
1025 (seq <= mail_index_view_get_messages_count(t->view) ||
1026 seq <= t->last_new_seq));
1027 i_assert(ext_id < array_count(&index->extensions));
1028
1029 t->log_ext_updates = TRUE;
1030
1031 if (!array_is_created(&t->ext_resizes)) {
1032 intro = NULL;
1033 count = 0;
1034 } else {
1035 intro = array_get(&t->ext_resizes, &count);
1036 }
1037 if (ext_id < count && intro[ext_id].name_size != 0) {
1038 /* resized record */
1039 record_size = intro[ext_id].record_size;
1040 } else {
1041 rext = array_idx(&index->extensions, ext_id);
1042 record_size = rext->record_size;
1043 }
1044 i_assert(record_size > 0);
1045
1046 if (!array_is_created(&t->ext_rec_updates))
1047 i_array_init(&t->ext_rec_updates, ext_id + 2);
1048 array = array_idx_get_space(&t->ext_rec_updates, ext_id);
1049
1050 /* @UNSAFE */
1051 if (!mail_index_seq_array_add(array, seq, data, record_size,
1052 old_data_r)) {
1053 /* not found, clear old_data if it was given */
1054 if (old_data_r != NULL)
1055 memset(old_data_r, 0, record_size);
1056 }
1057 }
1058
mail_index_atomic_inc_ext(struct mail_index_transaction * t,uint32_t seq,uint32_t ext_id,int diff)1059 int mail_index_atomic_inc_ext(struct mail_index_transaction *t,
1060 uint32_t seq, uint32_t ext_id, int diff)
1061 {
1062 ARRAY_TYPE(seq_array) *array;
1063 int32_t old_diff32, diff32 = diff;
1064
1065 i_assert(seq > 0 &&
1066 (seq <= mail_index_view_get_messages_count(t->view) ||
1067 seq <= t->last_new_seq));
1068 i_assert(ext_id < array_count(&t->view->index->extensions));
1069 /* currently non-external transactions can be applied multiple times,
1070 causing multiple increments. FIXME: we need this now and it doesn't
1071 actually seem to be a real problem at least right now - why? */
1072 /*i_assert((t->flags & MAIL_INDEX_TRANSACTION_FLAG_EXTERNAL) != 0);*/
1073
1074 t->log_ext_updates = TRUE;
1075 if (!array_is_created(&t->ext_rec_atomics))
1076 i_array_init(&t->ext_rec_atomics, ext_id + 2);
1077 array = array_idx_get_space(&t->ext_rec_atomics, ext_id);
1078 if (mail_index_seq_array_add(array, seq, &diff32, sizeof(diff32),
1079 &old_diff32)) {
1080 /* already incremented this sequence in this transaction */
1081 diff32 += old_diff32;
1082 (void)mail_index_seq_array_add(array, seq, &diff32,
1083 sizeof(diff32), NULL);
1084 }
1085 return diff32;
1086 }
1087
1088 static bool
keyword_update_has_changes(struct mail_index_transaction * t,uint32_t seq,enum modify_type modify_type,struct mail_keywords * keywords)1089 keyword_update_has_changes(struct mail_index_transaction *t, uint32_t seq,
1090 enum modify_type modify_type,
1091 struct mail_keywords *keywords)
1092 {
1093 ARRAY_TYPE(keyword_indexes) existing;
1094 const unsigned int *existing_idx;
1095 unsigned int i, j, existing_count;
1096 bool found;
1097
1098 t_array_init(&existing, 32);
1099 if (seq < t->first_new_seq)
1100 mail_index_transaction_lookup_latest_keywords(t, seq, &existing);
1101 existing_idx = array_get(&existing, &existing_count);
1102
1103 if (modify_type == MODIFY_REPLACE && existing_count != keywords->count)
1104 return TRUE;
1105
1106 for (i = 0; i < keywords->count; i++) {
1107 found = FALSE;
1108 for (j = 0; j < existing_count; j++) {
1109 if (existing_idx[j] == keywords->idx[i]) {
1110 found = TRUE;
1111 break;
1112 }
1113 }
1114 switch (modify_type) {
1115 case MODIFY_ADD:
1116 case MODIFY_REPLACE:
1117 if (!found)
1118 return TRUE;
1119 break;
1120 case MODIFY_REMOVE:
1121 if (found)
1122 return TRUE;
1123 break;
1124 }
1125 }
1126 return FALSE;
1127 }
1128
1129 static struct mail_keywords *
keyword_update_remove_existing(struct mail_index_transaction * t,uint32_t seq)1130 keyword_update_remove_existing(struct mail_index_transaction *t, uint32_t seq)
1131 {
1132 ARRAY_TYPE(keyword_indexes) keywords;
1133 uint32_t i, keywords_count;
1134
1135 t_array_init(&keywords, 32);
1136 if (t->view->v.lookup_full == NULL) {
1137 /* syncing is saving a list of changes into this transaction.
1138 the seq is actual an uid, so we can't lookup the existing
1139 keywords. we shouldn't get here unless we're reading
1140 pre-v2.2 keyword-reset records from .log files. so we don't
1141 really care about performance that much here, */
1142 keywords_count = array_count(&t->view->index->keywords);
1143 for (i = 0; i < keywords_count; i++)
1144 array_push_back(&keywords, &i);
1145 } else {
1146 mail_index_transaction_lookup_latest_keywords(t, seq, &keywords);
1147 }
1148 if (array_count(&keywords) == 0)
1149 return NULL;
1150 return mail_index_keywords_create_from_indexes(t->view->index,
1151 &keywords);
1152 }
1153
mail_index_update_keywords(struct mail_index_transaction * t,uint32_t seq,enum modify_type modify_type,struct mail_keywords * keywords)1154 void mail_index_update_keywords(struct mail_index_transaction *t, uint32_t seq,
1155 enum modify_type modify_type,
1156 struct mail_keywords *keywords)
1157 {
1158 struct mail_index_transaction_keyword_update *u;
1159 struct mail_keywords *add_keywords = NULL, *remove_keywords = NULL;
1160 struct mail_keywords *unref_keywords = NULL;
1161 unsigned int i;
1162 bool changed;
1163
1164 i_assert(seq > 0 &&
1165 (seq <= mail_index_view_get_messages_count(t->view) ||
1166 seq <= t->last_new_seq));
1167 i_assert(keywords->index == t->view->index);
1168
1169 if (keywords->count == 0 && modify_type != MODIFY_REPLACE)
1170 return;
1171
1172 update_minmax_flagupdate_seq(t, seq, seq);
1173
1174 if (!array_is_created(&t->keyword_updates)) {
1175 uint32_t max_idx = keywords->count == 0 ? 3 :
1176 keywords->idx[keywords->count-1];
1177
1178 i_array_init(&t->keyword_updates, max_idx + 1);
1179 }
1180
1181 if ((t->flags & MAIL_INDEX_TRANSACTION_FLAG_AVOID_FLAG_UPDATES) != 0) {
1182 T_BEGIN {
1183 changed = keyword_update_has_changes(t, seq,
1184 modify_type,
1185 keywords);
1186 } T_END;
1187 if (!changed)
1188 return;
1189 }
1190
1191 switch (modify_type) {
1192 case MODIFY_REPLACE:
1193 /* split this into add+remove. remove all existing keywords not
1194 included in the keywords list */
1195 if (seq < t->first_new_seq) {
1196 /* remove the ones currently in index */
1197 remove_keywords = keyword_update_remove_existing(t, seq);
1198 unref_keywords = remove_keywords;
1199 }
1200 /* remove from all changes we've done in this transaction */
1201 array_foreach_modifiable(&t->keyword_updates, u)
1202 seq_range_array_remove(&u->add_seq, seq);
1203 add_keywords = keywords;
1204 break;
1205 case MODIFY_ADD:
1206 add_keywords = keywords;
1207 break;
1208 case MODIFY_REMOVE:
1209 remove_keywords = keywords;
1210 break;
1211 }
1212
1213 /* Update add_seq and remove_seq arrays which describe the keyword
1214 changes. First do the removes, since replace removes everything
1215 first. */
1216 if (remove_keywords != NULL) {
1217 for (i = 0; i < remove_keywords->count; i++) {
1218 u = array_idx_get_space(&t->keyword_updates,
1219 remove_keywords->idx[i]);
1220 seq_range_array_remove(&u->add_seq, seq);
1221 /* Don't bother updating remove_seq for new messages,
1222 since their initial state is "no keyword" anyway */
1223 if (seq < t->first_new_seq) {
1224 seq_range_array_add_with_init(&u->remove_seq,
1225 16, seq);
1226 }
1227 }
1228 }
1229 if (add_keywords != NULL) {
1230 for (i = 0; i < add_keywords->count; i++) {
1231 u = array_idx_get_space(&t->keyword_updates,
1232 add_keywords->idx[i]);
1233 seq_range_array_add_with_init(&u->add_seq, 16, seq);
1234 seq_range_array_remove(&u->remove_seq, seq);
1235 }
1236 }
1237 if (unref_keywords != NULL)
1238 mail_index_keywords_unref(&unref_keywords);
1239
1240 t->log_updates = TRUE;
1241 }
1242
mail_index_cancel_flag_updates(struct mail_index_transaction * t,uint32_t seq)1243 bool mail_index_cancel_flag_updates(struct mail_index_transaction *t,
1244 uint32_t seq)
1245 {
1246 struct mail_index_flag_update *updates, tmp_update;
1247 unsigned int i, count;
1248
1249 if (!array_is_created(&t->updates))
1250 return FALSE;
1251
1252 updates = array_get_modifiable(&t->updates, &count);
1253 i = mail_index_transaction_get_flag_update_pos(t, 0, count, seq);
1254 if (i == count)
1255 return FALSE;
1256 else {
1257 i_assert(seq <= updates[i].uid2);
1258 if (seq < updates[i].uid1)
1259 return FALSE;
1260 }
1261
1262 /* exists */
1263 if (updates[i].uid1 == seq) {
1264 if (updates[i].uid2 != seq)
1265 updates[i].uid1++;
1266 else if (count > 1)
1267 array_delete(&t->updates, i, 1);
1268 else
1269 array_free(&t->updates);
1270 } else if (updates[i].uid2 == seq) {
1271 updates[i].uid2--;
1272 } else {
1273 /* need to split it in two */
1274 tmp_update = updates[i];
1275 tmp_update.uid1 = seq+1;
1276 updates[i].uid2 = seq-1;
1277 array_insert(&t->updates, i + 1, &tmp_update, 1);
1278 }
1279 return TRUE;
1280 }
1281
mail_index_cancel_array(ARRAY_TYPE (seq_range)* array,uint32_t seq)1282 static bool mail_index_cancel_array(ARRAY_TYPE(seq_range) *array, uint32_t seq)
1283 {
1284 if (array_is_created(array)) {
1285 if (seq_range_array_remove(array, seq)) {
1286 if (array_count(array) == 0)
1287 array_free(array);
1288 return TRUE;
1289 }
1290 }
1291 return FALSE;
1292 }
1293
mail_index_cancel_keyword_updates(struct mail_index_transaction * t,uint32_t seq)1294 bool mail_index_cancel_keyword_updates(struct mail_index_transaction *t,
1295 uint32_t seq)
1296 {
1297 struct mail_index_transaction_keyword_update *kw;
1298 bool ret = FALSE, have_kw_changes = FALSE;
1299
1300 if (!array_is_created(&t->keyword_updates))
1301 return FALSE;
1302
1303 array_foreach_modifiable(&t->keyword_updates, kw) {
1304 if (mail_index_cancel_array(&kw->add_seq, seq))
1305 ret = TRUE;
1306 if (mail_index_cancel_array(&kw->remove_seq, seq))
1307 ret = TRUE;
1308 if (array_is_created(&kw->add_seq) ||
1309 array_is_created(&kw->remove_seq))
1310 have_kw_changes = TRUE;
1311 }
1312 if (!have_kw_changes)
1313 array_free(&t->keyword_updates);
1314 return ret;
1315 }
1316
mail_index_transaction_reset(struct mail_index_transaction * t)1317 void mail_index_transaction_reset(struct mail_index_transaction *t)
1318 {
1319 t->v.reset(t);
1320 }
1321
mail_index_reset(struct mail_index_transaction * t)1322 void mail_index_reset(struct mail_index_transaction *t)
1323 {
1324 mail_index_transaction_reset(t);
1325
1326 t->reset = TRUE;
1327 }
1328
mail_index_unset_fscked(struct mail_index_transaction * t)1329 void mail_index_unset_fscked(struct mail_index_transaction *t)
1330 {
1331 struct mail_index_header new_hdr =
1332 *mail_index_get_header(t->view);
1333
1334 i_assert(t->view->index->log_sync_locked);
1335
1336 /* remove fsck'd-flag if it exists. */
1337 if ((new_hdr.flags & MAIL_INDEX_HDR_FLAG_FSCKD) != 0) {
1338 new_hdr.flags &= ENUM_NEGATE(MAIL_INDEX_HDR_FLAG_FSCKD);
1339 mail_index_update_header(t,
1340 offsetof(struct mail_index_header, flags),
1341 &new_hdr.flags, sizeof(new_hdr.flags), FALSE);
1342 }
1343 }
1344
mail_index_set_deleted(struct mail_index_transaction * t)1345 void mail_index_set_deleted(struct mail_index_transaction *t)
1346 {
1347 i_assert(!t->index_undeleted);
1348
1349 t->index_deleted = TRUE;
1350 }
1351
mail_index_set_undeleted(struct mail_index_transaction * t)1352 void mail_index_set_undeleted(struct mail_index_transaction *t)
1353 {
1354 i_assert(!t->index_deleted);
1355
1356 t->index_undeleted = TRUE;
1357 }
1358
mail_index_transaction_set_max_modseq(struct mail_index_transaction * t,uint64_t max_modseq,ARRAY_TYPE (seq_range)* seqs)1359 void mail_index_transaction_set_max_modseq(struct mail_index_transaction *t,
1360 uint64_t max_modseq,
1361 ARRAY_TYPE(seq_range) *seqs)
1362 {
1363 i_assert(array_is_created(seqs));
1364
1365 t->max_modseq = max_modseq;
1366 t->conflict_seqs = seqs;
1367 }
1368