1 /* Copyright (c) 2002-2018 Dovecot authors, see the included COPYING file */
2 
3 #include "lib.h"
4 #include "array.h"
5 #include "mail-cache.h"
6 #include "mail-index-modseq.h"
7 #include "mailbox-recent-flags.h"
8 #include "index-storage.h"
9 
10 static void
get_last_cached_seq(struct mailbox * box,uint32_t * last_cached_seq_r)11 get_last_cached_seq(struct mailbox *box, uint32_t *last_cached_seq_r)
12 {
13 	const struct mail_index_header *hdr;
14 	struct mail_cache_view *cache_view;
15 	uint32_t seq;
16 
17 	*last_cached_seq_r = 0;
18 	if (!mail_cache_exists(box->cache))
19 		return;
20 
21 	cache_view = mail_cache_view_open(box->cache, box->view);
22 	hdr = mail_index_get_header(box->view);
23 	for (seq = hdr->messages_count; seq > 0; seq--) {
24 		if (mail_cache_field_exists_any(cache_view, seq)) {
25 			*last_cached_seq_r = seq;
26 			break;
27 		}
28 	}
29 	mail_cache_view_close(&cache_view);
30 }
31 
index_storage_get_status(struct mailbox * box,enum mailbox_status_items items,struct mailbox_status * status_r)32 int index_storage_get_status(struct mailbox *box,
33 			     enum mailbox_status_items items,
34 			     struct mailbox_status *status_r)
35 {
36 	if (items == 0) {
37 		/* caller could have wanted only e.g. mailbox_status.have_*
38 		   flags */
39 		return 0;
40 	}
41 
42 	if (!box->opened) {
43 		if (mailbox_open(box) < 0)
44 			return -1;
45 		if (mailbox_sync(box, MAILBOX_SYNC_FLAG_FAST) < 0)
46 			return -1;
47 	}
48 	index_storage_get_open_status(box, items, status_r);
49 	return 0;
50 }
51 
index_storage_count_pvt_unseen(struct mailbox * box)52 static unsigned int index_storage_count_pvt_unseen(struct mailbox *box)
53 {
54 	const struct mail_index_record *pvt_rec;
55 	uint32_t shared_seq, pvt_seq, shared_count, pvt_count;
56 	uint32_t shared_uid;
57 	unsigned int unseen_count = 0;
58 
59 	/* we can't trust private index to be up to date. we'll need to go
60 	   through the shared index and for each existing mail lookup its
61 	   private flags. if a mail doesn't exist in private index then its
62 	   flags are 0. */
63 	shared_count = mail_index_view_get_messages_count(box->view);
64 	pvt_count = mail_index_view_get_messages_count(box->view_pvt);
65 	shared_seq = pvt_seq = 1;
66 	while (shared_seq <= shared_count && pvt_seq <= pvt_count) {
67 		mail_index_lookup_uid(box->view, shared_seq, &shared_uid);
68 		pvt_rec = mail_index_lookup(box->view_pvt, pvt_seq);
69 
70 		if (shared_uid == pvt_rec->uid) {
71 			if ((pvt_rec->flags & MAIL_SEEN) == 0)
72 				unseen_count++;
73 			shared_seq++; pvt_seq++;
74 		} else if (shared_uid < pvt_rec->uid) {
75 			shared_seq++;
76 		} else {
77 			pvt_seq++;
78 		}
79 	}
80 	unseen_count += (shared_count+1) - shared_seq;
81 	return unseen_count;
82 }
83 
index_storage_find_first_pvt_unseen_seq(struct mailbox * box)84 static uint32_t index_storage_find_first_pvt_unseen_seq(struct mailbox *box)
85 {
86 	const struct mail_index_header *pvt_hdr;
87 	const struct mail_index_record *pvt_rec;
88 	uint32_t pvt_seq, pvt_count, shared_seq, seq2;
89 
90 	pvt_count = mail_index_view_get_messages_count(box->view_pvt);
91 	mail_index_lookup_first(box->view_pvt, 0, MAIL_SEEN, &pvt_seq);
92 	if (pvt_seq == 0)
93 		pvt_seq = pvt_count+1;
94 	for (; pvt_seq <= pvt_count; pvt_seq++) {
95 		pvt_rec = mail_index_lookup(box->view_pvt, pvt_seq);
96 		if ((pvt_rec->flags & MAIL_SEEN) == 0 &&
97 		    mail_index_lookup_seq(box->view, pvt_rec->uid, &shared_seq))
98 			return shared_seq;
99 	}
100 	/* if shared index has any messages that don't exist in private index,
101 	   the first of them is the first unseen message */
102 	pvt_hdr = mail_index_get_header(box->view_pvt);
103 	if (mail_index_lookup_seq_range(box->view,
104 					pvt_hdr->next_uid, (uint32_t)-1,
105 					&shared_seq, &seq2))
106 		return shared_seq;
107 	return 0;
108 }
109 
index_storage_get_open_status(struct mailbox * box,enum mailbox_status_items items,struct mailbox_status * status_r)110 void index_storage_get_open_status(struct mailbox *box,
111 				   enum mailbox_status_items items,
112 				   struct mailbox_status *status_r)
113 {
114 	const struct mail_index_header *hdr;
115 
116 	/* we can get most of the status items without any trouble */
117 	hdr = mail_index_get_header(box->view);
118 	status_r->messages = hdr->messages_count;
119 	if ((items & STATUS_RECENT) != 0) {
120 		if ((box->flags & MAILBOX_FLAG_DROP_RECENT) != 0) {
121 			/* recent flags are set and dropped by the previous
122 			   sync while index was locked. if we updated the
123 			   recent flags here we'd have a race condition. */
124 			i_assert(box->synced);
125 		} else {
126 			/* make sure recent count is set, in case we haven't
127 			   synced yet */
128 			index_sync_update_recent_count(box);
129 		}
130 		status_r->recent = mailbox_recent_flags_count(box);
131 		i_assert(status_r->recent <= status_r->messages);
132 	}
133 	if ((items & STATUS_UNSEEN) != 0) {
134 		if (box->view_pvt == NULL ||
135 		    (mailbox_get_private_flags_mask(box) & MAIL_SEEN) == 0) {
136 			status_r->unseen = hdr->messages_count -
137 				hdr->seen_messages_count;
138 		} else {
139 			status_r->unseen = index_storage_count_pvt_unseen(box);
140 		}
141 	}
142 	status_r->uidvalidity = hdr->uid_validity;
143 	status_r->uidnext = hdr->next_uid;
144 	status_r->first_recent_uid = hdr->first_recent_uid;
145 	if ((items & STATUS_HIGHESTMODSEQ) != 0) {
146 		status_r->nonpermanent_modseqs =
147 			mail_index_is_in_memory(box->index);
148 		status_r->no_modseq_tracking =
149 			!mail_index_have_modseq_tracking(box->index);
150 		status_r->highest_modseq =
151 			mail_index_modseq_get_highest(box->view);
152 		if (status_r->highest_modseq == 0) {
153 			/* modseqs not enabled yet, but we can't return 0 */
154 			status_r->highest_modseq = 1;
155 		}
156 	}
157 	if ((items & STATUS_HIGHESTPVTMODSEQ) != 0 && box->view_pvt != NULL) {
158 		status_r->highest_pvt_modseq =
159 			mail_index_modseq_get_highest(box->view_pvt);
160 		if (status_r->highest_pvt_modseq == 0) {
161 			/* modseqs not enabled yet, but we can't return 0 */
162 			status_r->highest_pvt_modseq = 1;
163 		}
164 	}
165 
166 	if ((items & STATUS_FIRST_UNSEEN_SEQ) != 0) {
167 		if (box->view_pvt == NULL ||
168 		    (mailbox_get_private_flags_mask(box) & MAIL_SEEN) == 0) {
169 			mail_index_lookup_first(box->view, 0, MAIL_SEEN,
170 						&status_r->first_unseen_seq);
171 		} else {
172 			status_r->first_unseen_seq =
173 				index_storage_find_first_pvt_unseen_seq(box);
174 		}
175 	}
176 	if ((items & STATUS_LAST_CACHED_SEQ) != 0)
177 		get_last_cached_seq(box, &status_r->last_cached_seq);
178 
179 	if ((items & STATUS_KEYWORDS) != 0)
180 		status_r->keywords = mail_index_get_keywords(box->index);
181 	if ((items & STATUS_PERMANENT_FLAGS) != 0) {
182 		if (!mailbox_is_readonly(box)) {
183 			status_r->permanent_flags = MAIL_FLAGS_NONRECENT;
184 			status_r->permanent_keywords = TRUE;
185 			status_r->allow_new_keywords =
186 				!box->disallow_new_keywords;
187 		}
188 		status_r->flags = MAIL_FLAGS_NONRECENT;
189 	}
190 }
191 
192 static void
get_metadata_cache_fields(struct mailbox * box,struct mailbox_metadata * metadata_r)193 get_metadata_cache_fields(struct mailbox *box,
194 			  struct mailbox_metadata *metadata_r)
195 {
196 	const struct mail_cache_field *fields;
197 	enum mail_cache_decision_type dec;
198 	ARRAY_TYPE(mailbox_cache_field) *cache_fields;
199 	struct mailbox_cache_field *cf;
200 	unsigned int i, count;
201 
202 	fields = mail_cache_register_get_list(box->cache,
203 					      pool_datastack_create(), &count);
204 
205 	cache_fields = t_new(ARRAY_TYPE(mailbox_cache_field), 1);
206 	t_array_init(cache_fields, count);
207 	for (i = 0; i < count; i++) {
208 		dec = fields[i].decision & ENUM_NEGATE(MAIL_CACHE_DECISION_FORCED);
209 		if (dec != MAIL_CACHE_DECISION_NO) {
210 			cf = array_append_space(cache_fields);
211 			cf->name = fields[i].name;
212 			cf->decision = fields[i].decision;
213 			cf->last_used = fields[i].last_used;
214 		}
215 	}
216 	metadata_r->cache_fields = cache_fields;
217 }
218 
get_metadata_precache_fields(struct mailbox * box,struct mailbox_metadata * metadata_r)219 static void get_metadata_precache_fields(struct mailbox *box,
220 					 struct mailbox_metadata *metadata_r)
221 {
222 	const struct mail_cache_field *fields;
223 	unsigned int i, count;
224 	enum mail_fetch_field cache = 0;
225 
226 	fields = mail_cache_register_get_list(box->cache,
227 					      pool_datastack_create(), &count);
228 	for (i = 0; i < count; i++) {
229 		const char *name = fields[i].name;
230 
231 		if (str_begins(name, "hdr.") ||
232 		    strcmp(name, "date.sent") == 0 ||
233 		    strcmp(name, "imap.envelope") == 0)
234 			cache |= MAIL_FETCH_STREAM_HEADER;
235 		else if (strcmp(name, "mime.parts") == 0 ||
236 			 strcmp(name, "binary.parts") == 0 ||
237 			 strcmp(name, "imap.body") == 0 ||
238 			 strcmp(name, "imap.bodystructure") == 0 ||
239 			 strcmp(name, "body.snippet") == 0)
240 			cache |= MAIL_FETCH_STREAM_BODY;
241 		else if (strcmp(name, "date.received") == 0)
242 			cache |= MAIL_FETCH_RECEIVED_DATE;
243 		else if (strcmp(name, "date.save") == 0)
244 			cache |= MAIL_FETCH_SAVE_DATE;
245 		else if (strcmp(name, "size.virtual") == 0)
246 			cache |= MAIL_FETCH_VIRTUAL_SIZE;
247 		else if (strcmp(name, "size.physical") == 0)
248 			cache |= MAIL_FETCH_PHYSICAL_SIZE;
249 		else if (strcmp(name, "pop3.uidl") == 0)
250 			cache |= MAIL_FETCH_UIDL_BACKEND;
251 		else if (strcmp(name, "pop3.order") == 0)
252 			cache |= MAIL_FETCH_POP3_ORDER;
253 		else if (strcmp(name, "guid") == 0)
254 			cache |= MAIL_FETCH_GUID;
255 		else if (strcmp(name, "flags") == 0) {
256 			/* just ignore for now at least.. */
257 		} else
258 			e_debug(box->event,
259 				"Ignoring unknown cache field: %s", name);
260 	}
261 	metadata_r->precache_fields = cache;
262 }
263 
264 static int
index_mailbox_get_first_save_date(struct mailbox * box,struct mailbox_metadata * metadata_r)265 index_mailbox_get_first_save_date(struct mailbox *box,
266 				  struct mailbox_metadata *metadata_r)
267 {
268 	const struct mail_index_header *hdr;
269 	struct mailbox_transaction_context *t;
270 	struct mail *mail;
271 	uint32_t seq;
272 	int ret = -1;
273 
274 	hdr = mail_index_get_header(box->view);
275 	if (hdr->messages_count == 0) {
276 		metadata_r->first_save_date = (time_t)-1;
277 		return 0;
278 	}
279 
280 	t = mailbox_transaction_begin(box, 0, __func__);
281 	mail = mail_alloc(t, 0, NULL);
282 	for (seq = 1; seq <= hdr->messages_count; seq++) {
283 		mail_set_seq(mail, seq);
284 		if (mail_get_save_date(mail, &metadata_r->first_save_date) >= 0) {
285 			ret = 0;
286 			break;
287 		}
288 		if (mailbox_get_last_mail_error(box) != MAIL_ERROR_EXPUNGED) {
289 			/* failed */
290 			break;
291 		}
292 	}
293 	mail_free(&mail);
294 	(void)mailbox_transaction_commit(&t);
295 	if (seq > hdr->messages_count) {
296 		/* all messages were expunged after all */
297 		metadata_r->first_save_date = (time_t)-1;
298 		return 0;
299 	}
300 	return ret;
301 }
302 
index_mailbox_get_metadata(struct mailbox * box,enum mailbox_metadata_items items,struct mailbox_metadata * metadata_r)303 int index_mailbox_get_metadata(struct mailbox *box,
304 			       enum mailbox_metadata_items items,
305 			       struct mailbox_metadata *metadata_r)
306 {
307 	/* handle items that don't require opening the mailbox */
308 	if ((items & MAILBOX_METADATA_BACKEND_NAMESPACE) != 0) {
309 		metadata_r->backend_ns_prefix = "";
310 		metadata_r->backend_ns_type =
311 			mailbox_list_get_namespace(box->list)->type;
312 		items &= ENUM_NEGATE(MAILBOX_METADATA_BACKEND_NAMESPACE);
313 	}
314 	if (items == 0)
315 		return 0;
316 
317 	/* handle items that require opening the mailbox */
318 	if (!box->opened) {
319 		if (mailbox_open(box) < 0)
320 			return -1;
321 	}
322 	if (!box->synced && (items & MAILBOX_METADATA_SYNC_ITEMS) != 0) {
323 		if (mailbox_sync(box, MAILBOX_SYNC_FLAG_FAST) < 0)
324 			return -1;
325 	}
326 
327 	if ((items & MAILBOX_METADATA_VIRTUAL_SIZE) != 0) {
328 		if (index_mailbox_get_virtual_size(box, metadata_r) < 0)
329 			return -1;
330 	}
331 	if ((items & MAILBOX_METADATA_PHYSICAL_SIZE) != 0) {
332 		if (index_mailbox_get_physical_size(box, metadata_r) < 0)
333 			return -1;
334 	}
335 	if ((items & MAILBOX_METADATA_FIRST_SAVE_DATE) != 0) {
336 		if (index_mailbox_get_first_save_date(box, metadata_r) < 0)
337 			return -1;
338 	}
339 	if ((items & MAILBOX_METADATA_CACHE_FIELDS) != 0)
340 		get_metadata_cache_fields(box, metadata_r);
341 	if ((items & MAILBOX_METADATA_PRECACHE_FIELDS) != 0)
342 		get_metadata_precache_fields(box, metadata_r);
343 	return 0;
344 }
345