summaryrefslogtreecommitdiffstats
path: root/src/lib-storage/mailbox-get.c
blob: 84a14ed00e65f6a0f177860b5049f7aa5a1a1fa7 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
/* Copyright (c) 2002-2018 Dovecot authors, see the included COPYING file */

#include "lib.h"
#include "array.h"
#include "mail-index-modseq.h"
#include "mail-storage-private.h"

void mailbox_get_seq_range(struct mailbox *box, uint32_t uid1, uint32_t uid2,
			   uint32_t *seq1_r, uint32_t *seq2_r)
{
	(void)mail_index_lookup_seq_range(box->view, uid1, uid2, seq1_r, seq2_r);
}

void mailbox_get_uid_range(struct mailbox *box,
			   const ARRAY_TYPE(seq_range) *seqs,
			   ARRAY_TYPE(seq_range) *uids)
{
	const struct seq_range *range;
	unsigned int i, count;
	uint32_t seq, uid, uid2;

	range = array_get(seqs, &count);
	for (i = 0; i < count; i++) {
		if (range[i].seq2 == (uint32_t)-1) {
			i_assert(i == count-1);
			mail_index_lookup_uid(box->view,
				mail_index_view_get_messages_count(box->view),
				&uid2);
			if (range[i].seq1 == (uint32_t)-1)
				uid = uid2;
			else
				mail_index_lookup_uid(box->view, range[i].seq1, &uid);
			seq_range_array_add_range(uids, uid, uid2);
			break;
		}
		for (seq = range[i].seq1; seq <= range[i].seq2; seq++) {
			mail_index_lookup_uid(box->view, seq, &uid);
			seq_range_array_add(uids, uid);
		}
	}
}

static void
add_expunges(ARRAY_TYPE(seq_range) *expunged_uids, uint32_t min_uid,
	     const struct mail_transaction_expunge *src, size_t src_size)
{
	const struct mail_transaction_expunge *end;

	end = src + src_size / sizeof(*src);
	for (; src != end; src++) {
		if (src->uid2 >= min_uid) {
			seq_range_array_add_range(expunged_uids,
						  src->uid1, src->uid2);
		}
	}
}

static void
add_guid_expunges(ARRAY_TYPE(seq_range) *expunged_uids, uint32_t min_uid,
		  const struct mail_transaction_expunge_guid *src,
		  size_t src_size)
{
	const struct mail_transaction_expunge_guid *end;

	end = src + src_size / sizeof(*src);
	for (; src != end; src++) {
		if (src->uid >= min_uid)
			seq_range_array_add(expunged_uids, src->uid);
	}
}

static int
mailbox_get_expunges_init(struct mailbox *box, uint64_t prev_modseq,
			  struct mail_transaction_log_view **log_view_r,
			  bool *modseq_too_old_r)
{
	struct mail_transaction_log_view *log_view;
	uint32_t log_seq, tail_seq;
	uoff_t log_offset;
	const char *reason;
	bool reset;
	int ret;

	*modseq_too_old_r = FALSE;

	if (!mail_index_modseq_get_next_log_offset(box->view, prev_modseq,
						   &log_seq, &log_offset)) {
		log_seq = 1;
		log_offset = 0;
		*modseq_too_old_r = TRUE;
	}
	if (log_seq > box->view->log_file_head_seq ||
	    (log_seq == box->view->log_file_head_seq &&
	     log_offset >= box->view->log_file_head_offset)) {
		/* we haven't seen this high expunges at all */
		return 1;
	}

	log_view = mail_transaction_log_view_open(box->index->log);
	ret = mail_transaction_log_view_set(log_view, log_seq, log_offset,
					    box->view->log_file_head_seq,
					    box->view->log_file_head_offset,
					    &reset, &reason);
	if (ret == 0) {
		mail_transaction_log_get_tail(box->index->log, &tail_seq);
		if (tail_seq <= box->view->log_file_head_seq) {
			i_assert(tail_seq > log_seq);
			ret = mail_transaction_log_view_set(log_view, tail_seq, 0,
							    box->view->log_file_head_seq,
							    box->view->log_file_head_offset,
							    &reset, &reason);
		}
		*modseq_too_old_r = TRUE;
	}
	if (ret <= 0) {
		mail_transaction_log_view_close(&log_view);
		return -1;
	}

	*log_view_r = log_view;
	return 0;
}

static void
mailbox_get_expunged_guids(struct mail_transaction_log_view *log_view,
			   ARRAY_TYPE(seq_range) *expunged_uids,
			   ARRAY_TYPE(mailbox_expunge_rec) *expunges)
{
	const struct mail_transaction_header *thdr;
	const void *tdata;
	const struct mail_transaction_expunge_guid *rec, *end;
	struct mailbox_expunge_rec *expunge;
	struct seq_range_iter iter;
	unsigned int n;
	uint32_t uid;

	while (mail_transaction_log_view_next(log_view, &thdr, &tdata) > 0) {
		if ((thdr->type & MAIL_TRANSACTION_TYPE_MASK) !=
		    MAIL_TRANSACTION_EXPUNGE_GUID)
			continue;

		rec = tdata;
		end = rec + thdr->size / sizeof(*rec);
		for (; rec != end; rec++) {
			if (!seq_range_exists(expunged_uids, rec->uid))
				continue;
			seq_range_array_remove(expunged_uids, rec->uid);

			expunge = array_append_space(expunges);
			expunge->uid = rec->uid;
			memcpy(expunge->guid_128, rec->guid_128,
			       sizeof(expunge->guid_128));
		}
	}

	/* everything left in expunged_uids didn't get a GUID */
	seq_range_array_iter_init(&iter, expunged_uids); n = 0;
	while (seq_range_array_iter_nth(&iter, n++, &uid)) {
		expunge = array_append_space(expunges);
		expunge->uid = uid;
	}
}

static bool ATTR_NULL(4, 5)
mailbox_get_expunges_full(struct mailbox *box, uint64_t prev_modseq,
			  const ARRAY_TYPE(seq_range) *uids_filter,
			  ARRAY_TYPE(seq_range) *expunged_uids,
			  ARRAY_TYPE(mailbox_expunge_rec) *expunges)
{
	struct mail_transaction_log_view *log_view;
	ARRAY_TYPE(seq_range) tmp_expunged_uids = ARRAY_INIT;
	const struct mail_transaction_header *thdr;
	const struct seq_range *range;
	const void *tdata;
	uint32_t min_uid;
	bool modseq_too_old;
	int ret;

	i_assert(array_count(uids_filter) > 0);
	i_assert(expunged_uids == NULL || expunges == NULL);

	ret = mailbox_get_expunges_init(box, prev_modseq, &log_view, &modseq_too_old);
	if (ret != 0)
		return ret > 0;

	range = array_front(uids_filter);
	min_uid = range->seq1;

	/* first get UIDs of all actual expunges */
	if (expunged_uids == NULL) {
		i_array_init(&tmp_expunged_uids, 64);
		expunged_uids = &tmp_expunged_uids;
	}
	mail_transaction_log_view_mark(log_view);
	while ((ret = mail_transaction_log_view_next(log_view,
						     &thdr, &tdata)) > 0) {
		if ((thdr->type & MAIL_TRANSACTION_EXTERNAL) == 0) {
			/* skip expunge requests */
			continue;
		}
		switch (thdr->type & MAIL_TRANSACTION_TYPE_MASK) {
		case MAIL_TRANSACTION_EXPUNGE:
			add_expunges(expunged_uids, min_uid, tdata, thdr->size);
			break;
		case MAIL_TRANSACTION_EXPUNGE_GUID:
			add_guid_expunges(expunged_uids, min_uid,
					  tdata, thdr->size);
			break;
		}
	}
	mail_transaction_log_view_rewind(log_view);

	/* drop UIDs that don't match the filter */
	seq_range_array_intersect(expunged_uids, uids_filter);

	if (expunges != NULL) {
		mailbox_get_expunged_guids(log_view, expunged_uids, expunges);
		array_free(&tmp_expunged_uids);
	}

	mail_transaction_log_view_close(&log_view);
	return ret < 0 || modseq_too_old ? FALSE : TRUE;
}

bool mailbox_get_expunges(struct mailbox *box, uint64_t prev_modseq,
			  const ARRAY_TYPE(seq_range) *uids_filter,
			  ARRAY_TYPE(mailbox_expunge_rec) *expunges)
{
	return mailbox_get_expunges_full(box, prev_modseq,
					 uids_filter, NULL, expunges);
}

bool mailbox_get_expunged_uids(struct mailbox *box, uint64_t prev_modseq,
			       const ARRAY_TYPE(seq_range) *uids_filter,
			       ARRAY_TYPE(seq_range) *expunged_uids)
{
	return mailbox_get_expunges_full(box, prev_modseq,
					 uids_filter, expunged_uids, NULL);
}