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