1 /* Copyright (c) 2011-2018 Dovecot authors, see the included COPYING file */
2 
3 #include "lib.h"
4 #include "str.h"
5 #include "hex-binary.h"
6 #include "sha1.h"
7 #include "istream.h"
8 #include "message-part-data.h"
9 #include "imap-envelope.h"
10 #include "imapc-msgmap.h"
11 #include "imapc-mail.h"
12 #include "imapc-storage.h"
13 
14 static bool imapc_mail_get_cached_guid(struct mail *_mail);
15 
16 struct mail *
imapc_mail_alloc(struct mailbox_transaction_context * t,enum mail_fetch_field wanted_fields,struct mailbox_header_lookup_ctx * wanted_headers)17 imapc_mail_alloc(struct mailbox_transaction_context *t,
18 		 enum mail_fetch_field wanted_fields,
19 		 struct mailbox_header_lookup_ctx *wanted_headers)
20 {
21 	struct imapc_mail *mail;
22 	pool_t pool;
23 
24 	pool = pool_alloconly_create("mail", 2048);
25 	mail = p_new(pool, struct imapc_mail, 1);
26 	mail->fd = -1;
27 
28 	index_mail_init(&mail->imail, t, wanted_fields, wanted_headers, pool, NULL);
29 	return &mail->imail.mail.mail;
30 }
31 
imapc_mail_is_expunged(struct mail * _mail)32 static bool imapc_mail_is_expunged(struct mail *_mail)
33 {
34 	struct imapc_mailbox *mbox = IMAPC_MAILBOX(_mail->box);
35 	struct imapc_msgmap *msgmap;
36 	uint32_t lseq, rseq;
37 
38 	if (!mbox->initial_sync_done) {
39 		/* unknown at this point */
40 		return FALSE;
41 	}
42 
43 	if (mbox->sync_view != NULL) {
44 		/* check if another session has already expunged it */
45 		if (!mail_index_lookup_seq(mbox->sync_view, _mail->uid, &lseq))
46 			return TRUE;
47 	}
48 
49 	/* check if we've received EXPUNGE for it */
50 	msgmap = imapc_client_mailbox_get_msgmap(mbox->client_box);
51 	if (!imapc_msgmap_uid_to_rseq(msgmap, _mail->uid, &rseq))
52 		return TRUE;
53 
54 	/* we may be running against a server that hasn't bothered sending
55 	   us an EXPUNGE. see if NOOP sends it. */
56 	imapc_mailbox_noop(mbox);
57 	if (!mbox->initial_sync_done) {
58 		/* NOOP caused a reconnection and desync */
59 		return FALSE;
60 	}
61 
62 	return !imapc_msgmap_uid_to_rseq(msgmap, _mail->uid, &rseq);
63 }
64 
imapc_mail_failed(struct mail * mail,const char * field)65 static int imapc_mail_failed(struct mail *mail, const char *field)
66 {
67 	struct imapc_mail *imail = IMAPC_MAIL(mail);
68 	struct imapc_mailbox *mbox = IMAPC_MAILBOX(mail->box);
69 	bool fix_broken_mail = FALSE;
70 
71 	if (mail->expunged || imapc_mail_is_expunged(mail)) {
72 		mail_set_expunged(mail);
73 	} else if (!imapc_client_mailbox_is_opened(mbox->client_box)) {
74 		/* we've already logged a disconnection error */
75 		mail_storage_set_internal_error(mail->box->storage);
76 	} else {
77 		/* By default we'll assume that this is a critical failure,
78 		   because we don't want to lose any data. We can be here
79 		   either because it's a temporary failure on the server or
80 		   it's a permanent failure. Unfortunately we can't know
81 		   which case it is, so permanent failures need to be worked
82 		   around by setting imapc_features=fetch-fix-broken-mails.
83 
84 		   One reason for permanent failures was that earlier Exchange
85 		   versions failed to return any data for messages in Calendars
86 		   mailbox. This seems to be fixed in newer versions.
87 		   */
88 		fix_broken_mail = imail->fetch_ignore_if_missing;
89 		mail_set_critical(mail,
90 			"imapc: Remote server didn't send %s%s (FETCH replied: %s)",
91 			field, fix_broken_mail ? " - treating it as empty" : "",
92 			imail->last_fetch_reply);
93 	}
94 	return fix_broken_mail ? 0 : -1;
95 }
96 
imapc_mail_get_modseq(struct mail * _mail)97 static uint64_t imapc_mail_get_modseq(struct mail *_mail)
98 {
99 	struct imapc_mailbox *mbox = IMAPC_MAILBOX(_mail->box);
100 	struct imapc_msgmap *msgmap;
101 	const uint64_t *modseqs;
102 	unsigned int count;
103 	uint32_t rseq;
104 
105 	if (!imapc_mailbox_has_modseqs(mbox))
106 		return index_mail_get_modseq(_mail);
107 
108 	msgmap = imapc_client_mailbox_get_msgmap(mbox->client_box);
109 	if (imapc_msgmap_uid_to_rseq(msgmap, _mail->uid, &rseq)) {
110 		modseqs = array_get(&mbox->rseq_modseqs, &count);
111 		if (rseq <= count)
112 			return modseqs[rseq-1];
113 	}
114 	return 1; /* unknown modseq */
115 }
116 
imapc_mail_get_received_date(struct mail * _mail,time_t * date_r)117 static int imapc_mail_get_received_date(struct mail *_mail, time_t *date_r)
118 {
119 	struct index_mail *mail = INDEX_MAIL(_mail);
120 	struct index_mail_data *data = &mail->data;
121 
122 	if (index_mail_get_received_date(_mail, date_r) == 0)
123 		return 0;
124 
125 	if (data->received_date == (time_t)-1) {
126 		if (imapc_mail_fetch(_mail, MAIL_FETCH_RECEIVED_DATE, NULL) < 0)
127 			return -1;
128 		if (data->received_date == (time_t)-1) {
129 			if (imapc_mail_failed(_mail, "INTERNALDATE") < 0)
130 				return -1;
131 			/* assume that the server never returns INTERNALDATE
132 			   for this mail (see BODY[] failure handling) */
133 			data->received_date = 0;
134 		}
135 	}
136 	*date_r = data->received_date;
137 	return 0;
138 }
139 
imapc_mail_get_save_date(struct mail * _mail,time_t * date_r)140 static int imapc_mail_get_save_date(struct mail *_mail, time_t *date_r)
141 {
142 	struct imapc_mailbox *mbox = IMAPC_MAILBOX(_mail->box);
143 	struct index_mail *mail = INDEX_MAIL(_mail);
144 	struct index_mail_data *data = &mail->data;
145 
146 	if (data->save_date != 0 && index_mail_get_save_date(_mail, date_r) > 0)
147 		return 1;
148 
149 	if (HAS_NO_BITS(mbox->capabilities, IMAPC_CAPABILITY_SAVEDATE)) {
150 		data->save_date = 0;
151 	} else if (data->save_date == (time_t)-1) {
152 		if (imapc_mail_fetch(_mail, MAIL_FETCH_SAVE_DATE, NULL) < 0)
153 			return -1;
154 		if (data->save_date == (time_t)-1 &&
155 		    imapc_mail_failed(_mail, "SAVEDATE") < 0)
156 			return -1;
157 	}
158 	if (data->save_date == (time_t)-1 || data->save_date == 0) {
159 		if (imapc_mail_get_received_date(_mail, date_r) < 0)
160 			return -1;
161 		return 0;
162 	}
163 	*date_r = data->save_date;
164 	return 1;
165 }
166 
imapc_mail_get_physical_size(struct mail * _mail,uoff_t * size_r)167 static int imapc_mail_get_physical_size(struct mail *_mail, uoff_t *size_r)
168 {
169 	struct imapc_mailbox *mbox = IMAPC_MAILBOX(_mail->box);
170 	struct index_mail *mail = INDEX_MAIL(_mail);
171 	struct index_mail_data *data = &mail->data;
172 	struct istream *input;
173 	uoff_t old_offset;
174 	int ret;
175 
176 	if (data->physical_size == UOFF_T_MAX)
177 		(void)index_mail_get_physical_size(_mail, size_r);
178 	if (data->physical_size != UOFF_T_MAX) {
179 		*size_r = data->physical_size;
180 		return 0;
181 	}
182 
183 	if (IMAPC_BOX_HAS_FEATURE(mbox, IMAPC_FEATURE_RFC822_SIZE) &&
184 	    data->stream == NULL) {
185 		/* Trust RFC822.SIZE to be correct enough to present to the
186 		   IMAP client. However, it can be wrong in some implementation
187 		   so try not to trust it too much. */
188 		if (imapc_mail_fetch(_mail, MAIL_FETCH_PHYSICAL_SIZE, NULL) < 0)
189 			return -1;
190 		if (data->physical_size == UOFF_T_MAX) {
191 			if (imapc_mail_failed(_mail, "RFC822.SIZE") < 0)
192 				return -1;
193 			/* assume that the server never returns RFC822.SIZE
194 			   for this mail (see BODY[] failure handling) */
195 			data->physical_size = 0;
196 		}
197 		*size_r = data->physical_size;
198 		return 0;
199 	}
200 
201 	old_offset = data->stream == NULL ? 0 : data->stream->v_offset;
202 	if (mail_get_stream(_mail, NULL, NULL, &input) < 0)
203 		return -1;
204 	i_assert(data->stream != NULL);
205 	i_stream_seek(data->stream, old_offset);
206 
207 	ret = i_stream_get_size(data->stream, TRUE,
208 				&data->physical_size);
209 	if (ret <= 0) {
210 		i_assert(ret != 0);
211 		mail_set_critical(_mail, "imapc: stat(%s) failed: %m",
212 				  i_stream_get_name(data->stream));
213 		return -1;
214 	}
215 	*size_r = data->physical_size;
216 	return 0;
217 }
218 
imapc_mail_get_virtual_size(struct mail * _mail,uoff_t * size_r)219 static int imapc_mail_get_virtual_size(struct mail *_mail, uoff_t *size_r)
220 {
221 	struct index_mail *mail = INDEX_MAIL(_mail);
222 	struct index_mail_data *data = &mail->data;
223 
224 	if (imapc_mail_get_physical_size(_mail, size_r) < 0)
225 		return -1;
226 	data->virtual_size = data->physical_size;
227 	return 0;
228 }
229 
230 static int
imapc_mail_get_header_stream(struct mail * _mail,struct mailbox_header_lookup_ctx * headers,struct istream ** stream_r)231 imapc_mail_get_header_stream(struct mail *_mail,
232 			     struct mailbox_header_lookup_ctx *headers,
233 			     struct istream **stream_r)
234 {
235 	struct imapc_mail *mail = IMAPC_MAIL(_mail);
236 	struct imapc_mailbox *mbox = IMAPC_MAILBOX(_mail->box);
237 	enum mail_lookup_abort old_abort = _mail->lookup_abort;
238 	int ret;
239 
240 	if (mail->imail.data.access_part != 0 ||
241 	    !IMAPC_BOX_HAS_FEATURE(mbox, IMAPC_FEATURE_FETCH_HEADERS)) {
242 		/* we're going to be reading the header/body anyway */
243 		return index_mail_get_header_stream(_mail, headers, stream_r);
244 	}
245 
246 	/* see if the wanted headers are already in cache */
247 	_mail->lookup_abort = MAIL_LOOKUP_ABORT_READ_MAIL;
248 	ret = index_mail_get_header_stream(_mail, headers, stream_r);
249 	_mail->lookup_abort = old_abort;
250 	if (ret == 0)
251 		return 0;
252 
253 	/* fetch only the wanted headers */
254 	if (imapc_mail_fetch(_mail, 0, headers->name) < 0)
255 		return -1;
256 	/* the headers should cached now. */
257 	return index_mail_get_header_stream(_mail, headers, stream_r);
258 }
259 
260 static int
imapc_mail_get_headers(struct mail * _mail,const char * field,bool decode_to_utf8,const char * const ** value_r)261 imapc_mail_get_headers(struct mail *_mail, const char *field,
262 		       bool decode_to_utf8, const char *const **value_r)
263 {
264 	struct mailbox_header_lookup_ctx *headers;
265 	const char *header_names[2];
266 	const unsigned char *data;
267 	size_t size;
268 	struct istream *input;
269 	int ret;
270 
271 	header_names[0] = field;
272 	header_names[1] = NULL;
273 	headers = mailbox_header_lookup_init(_mail->box, header_names);
274 	ret = mail_get_header_stream(_mail, headers, &input);
275 	mailbox_header_lookup_unref(&headers);
276 	if (ret < 0)
277 		return -1;
278 
279 	while (i_stream_read_more(input, &data, &size) > 0)
280 		i_stream_skip(input, size);
281 	/* the header should cached now. */
282 	return index_mail_get_headers(_mail, field, decode_to_utf8, value_r);
283 }
284 
285 static int
imapc_mail_get_first_header(struct mail * _mail,const char * field,bool decode_to_utf8,const char ** value_r)286 imapc_mail_get_first_header(struct mail *_mail, const char *field,
287 			    bool decode_to_utf8, const char **value_r)
288 {
289 	const char *const *values;
290 	int ret;
291 
292 	ret = imapc_mail_get_headers(_mail, field, decode_to_utf8, &values);
293 	if (ret <= 0)
294 		return ret;
295 	*value_r = values[0];
296 	return 1;
297 }
298 
299 static int
imapc_mail_get_stream(struct mail * _mail,bool get_body,struct message_size * hdr_size,struct message_size * body_size,struct istream ** stream_r)300 imapc_mail_get_stream(struct mail *_mail, bool get_body,
301 		      struct message_size *hdr_size,
302 		      struct message_size *body_size, struct istream **stream_r)
303 {
304 	struct imapc_mail *mail = IMAPC_MAIL(_mail);
305 	struct index_mail_data *data = &mail->imail.data;
306 	enum mail_fetch_field fetch_field;
307 
308 	if (get_body && !mail->body_fetched &&
309 	    mail->imail.data.stream != NULL) {
310 		/* we've fetched the header, but we need the body now too */
311 		index_mail_close_streams(&mail->imail);
312 		/* don't re-use any cached header sizes. we may be
313 		   intentionally downloading the full body because the header
314 		   wasn't returned correctly (e.g. pop3-migration does this) */
315 		data->hdr_size_set = FALSE;
316 	}
317 
318 	/* See if we can get it from cache. If the wanted_fields/headers are
319 	   set properly, this is usually already done by prefetching. */
320 	imapc_mail_try_init_stream_from_cache(mail);
321 
322 	if (data->stream == NULL) {
323 		if (!data->initialized) {
324 			/* coming here from mail_set_seq() */
325 			mail_set_aborted(_mail);
326 			return -1;
327 		}
328 		if (_mail->expunged) {
329 			/* We already detected that the mail is expunged.
330 			   Don't spend time trying to FETCH it again. */
331 			mail_set_expunged(_mail);
332 			return -1;
333 		}
334 		fetch_field = get_body ||
335 			(data->access_part & READ_BODY) != 0 ?
336 			MAIL_FETCH_STREAM_BODY : MAIL_FETCH_STREAM_HEADER;
337 		if (imapc_mail_fetch(_mail, fetch_field, NULL) < 0)
338 			return -1;
339 
340 		if (data->stream == NULL) {
341 			if (imapc_mail_failed(_mail, "BODY[]") < 0)
342 				return -1;
343 			i_assert(data->stream == NULL);
344 
345 			/* return the broken email as empty */
346 			mail->body_fetched = TRUE;
347 			data->stream = i_stream_create_from_data(NULL, 0);
348 			imapc_mail_init_stream(mail);
349 		}
350 	}
351 
352 	return index_mail_init_stream(&mail->imail, hdr_size, body_size,
353 				      stream_r);
354 }
355 
imapc_mail_has_headers_in_cache(struct index_mail * mail,struct mailbox_header_lookup_ctx * headers)356 bool imapc_mail_has_headers_in_cache(struct index_mail *mail,
357 				     struct mailbox_header_lookup_ctx *headers)
358 {
359 	struct mail *_mail = &mail->mail.mail;
360 	unsigned int i;
361 
362 	for (i = 0; i < headers->count; i++) {
363 		if (mail_cache_field_exists(_mail->transaction->cache_view,
364 					    _mail->seq, headers->idx[i]) <= 0)
365 			return FALSE;
366 	}
367 	return TRUE;
368 }
369 
imapc_mail_update_access_parts(struct index_mail * mail)370 void imapc_mail_update_access_parts(struct index_mail *mail)
371 {
372 	struct mail *_mail = &mail->mail.mail;
373 	struct imapc_mailbox *mbox = IMAPC_MAILBOX(_mail->box);
374 	struct index_mail_data *data = &mail->data;
375 	struct mailbox_header_lookup_ctx *header_ctx;
376 	const char *str;
377 	time_t date;
378 	uoff_t size;
379 
380 	if ((data->wanted_fields & MAIL_FETCH_RECEIVED_DATE) != 0)
381 		(void)index_mail_get_received_date(_mail, &date);
382 	if ((data->wanted_fields & MAIL_FETCH_SAVE_DATE) != 0) {
383 		if (index_mail_get_save_date(_mail, &date) < 0 &&
384 		    HAS_NO_BITS(mbox->capabilities,
385 				IMAPC_CAPABILITY_SAVEDATE)) {
386 			(void)index_mail_get_received_date(_mail, &date);
387 			data->save_date = data->received_date;
388 		}
389 	}
390 	if ((data->wanted_fields & (MAIL_FETCH_PHYSICAL_SIZE |
391 				    MAIL_FETCH_VIRTUAL_SIZE)) != 0) {
392 		if (index_mail_get_physical_size(_mail, &size) < 0 &&
393 		    !IMAPC_BOX_HAS_FEATURE(mbox, IMAPC_FEATURE_RFC822_SIZE))
394 			data->access_part |= READ_HDR | READ_BODY;
395 	}
396 	if ((data->wanted_fields & MAIL_FETCH_GUID) != 0)
397 		(void)imapc_mail_get_cached_guid(_mail);
398 	if ((data->wanted_fields & MAIL_FETCH_IMAP_BODY) != 0)
399 		(void)index_mail_get_cached_body(mail, &str);
400 	if ((data->wanted_fields & MAIL_FETCH_IMAP_BODYSTRUCTURE) != 0)
401 		(void)index_mail_get_cached_bodystructure(mail, &str);
402 
403 	if (data->access_part == 0 && data->wanted_headers != NULL &&
404 	    !IMAPC_BOX_HAS_FEATURE(mbox, IMAPC_FEATURE_FETCH_HEADERS)) {
405 		/* see if all wanted headers exist in cache */
406 		if (!imapc_mail_has_headers_in_cache(mail, data->wanted_headers))
407 			data->access_part |= PARSE_HDR;
408 	}
409 	if (data->access_part == 0 &&
410 	    (data->wanted_fields & MAIL_FETCH_IMAP_ENVELOPE) != 0 &&
411 	    !IMAPC_BOX_HAS_FEATURE(mbox, IMAPC_FEATURE_FETCH_HEADERS)) {
412 		/* the common code already checked this partially,
413 		   but we need a guaranteed correct answer */
414 		header_ctx = mailbox_header_lookup_init(_mail->box,
415 							message_part_envelope_headers);
416 		if (!imapc_mail_has_headers_in_cache(mail, header_ctx))
417 			data->access_part |= PARSE_HDR;
418 		mailbox_header_lookup_unref(&header_ctx);
419 	}
420 }
421 
imapc_mail_set_seq(struct mail * _mail,uint32_t seq,bool saving)422 static void imapc_mail_set_seq(struct mail *_mail, uint32_t seq, bool saving)
423 {
424 	struct imapc_mail *imail = IMAPC_MAIL(_mail);
425 	struct index_mail *mail = &imail->imail;
426 	struct imapc_mailbox *mbox = (struct imapc_mailbox *)_mail->box;
427 
428 	index_mail_set_seq(_mail, seq, saving);
429 	if (IMAPC_BOX_HAS_FEATURE(mbox, IMAPC_FEATURE_RFC822_SIZE)) {
430 		/* RFC822.SIZE may be read from vsize record or cache. It may
431 		   not be exactly correct. */
432 		mail->data.inexact_total_sizes = TRUE;
433 	}
434 
435 	/* searching code handles prefetching internally,
436 	   elsewhere we want to do it immediately */
437 	if (!mail->mail.search_mail && !_mail->saving)
438 		(void)imapc_mail_prefetch(_mail);
439 }
440 
441 static void
imapc_mail_add_temp_wanted_fields(struct mail * _mail,enum mail_fetch_field fields,struct mailbox_header_lookup_ctx * headers)442 imapc_mail_add_temp_wanted_fields(struct mail *_mail,
443 				  enum mail_fetch_field fields,
444 				  struct mailbox_header_lookup_ctx *headers)
445 {
446 	struct index_mail *mail = INDEX_MAIL(_mail);
447 
448 	index_mail_add_temp_wanted_fields(_mail, fields, headers);
449 	if (_mail->seq != 0)
450 		imapc_mail_update_access_parts(mail);
451 }
452 
imapc_mail_close(struct mail * _mail)453 static void imapc_mail_close(struct mail *_mail)
454 {
455 	struct imapc_mail *mail = IMAPC_MAIL(_mail);
456 	struct imapc_mailbox *mbox = IMAPC_MAILBOX(_mail->box);
457 	struct imapc_mail_cache *cache = &mbox->prev_mail_cache;
458 
459 	if (mail->fetch_count > 0) {
460 		imapc_mail_fetch_flush(mbox);
461 		while (mail->fetch_count > 0)
462 			imapc_mailbox_run_nofetch(mbox);
463 	}
464 
465 	index_mail_close(_mail);
466 
467 	mail->fetching_headers = NULL;
468 	if (mail->body_fetched) {
469 		imapc_mail_cache_free(cache);
470 		cache->uid = _mail->uid;
471 		if (mail->fd != -1) {
472 			cache->fd = mail->fd;
473 			mail->fd = -1;
474 		} else {
475 			cache->buf = mail->body;
476 			mail->body = NULL;
477 		}
478 	}
479 	i_close_fd(&mail->fd);
480 	buffer_free(&mail->body);
481 	mail->header_fetched = FALSE;
482 	mail->body_fetched = FALSE;
483 
484 	i_assert(mail->fetch_count == 0);
485 }
486 
imapc_mail_get_hdr_hash(struct index_mail * imail)487 static int imapc_mail_get_hdr_hash(struct index_mail *imail)
488 {
489 	struct istream *input;
490 	const unsigned char *data;
491 	size_t size;
492 	uoff_t old_offset;
493 	struct sha1_ctxt sha1_ctx;
494 	unsigned char sha1_output[SHA1_RESULTLEN];
495 	const char *sha1_str;
496 
497 	sha1_init(&sha1_ctx);
498 	old_offset = imail->data.stream == NULL ? 0 :
499 		imail->data.stream->v_offset;
500 	if (mail_get_hdr_stream(&imail->mail.mail, NULL, &input) < 0)
501 		return -1;
502 	i_assert(imail->data.stream != NULL);
503 	while (i_stream_read_more(input, &data, &size) > 0) {
504 		sha1_loop(&sha1_ctx, data, size);
505 		i_stream_skip(input, size);
506 	}
507 	i_stream_seek(imail->data.stream, old_offset);
508 	sha1_result(&sha1_ctx, sha1_output);
509 
510 	sha1_str = binary_to_hex(sha1_output, sizeof(sha1_output));
511 	imail->data.guid = p_strdup(imail->mail.data_pool, sha1_str);
512 	return 0;
513 }
514 
imapc_mail_get_cached_guid(struct mail * _mail)515 static bool imapc_mail_get_cached_guid(struct mail *_mail)
516 {
517 	struct index_mail *imail = INDEX_MAIL(_mail);
518 	const enum index_cache_field cache_idx =
519 		imail->ibox->cache_fields[MAIL_CACHE_GUID].idx;
520 	string_t *str;
521 
522 	if (imail->data.guid != NULL) {
523 		if (mail_cache_field_can_add(_mail->transaction->cache_trans,
524 					     _mail->seq, cache_idx)) {
525 			/* GUID was prefetched - add to cache */
526 			index_mail_cache_add_idx(imail, cache_idx,
527 				imail->data.guid, strlen(imail->data.guid));
528 		}
529 		return TRUE;
530 	}
531 
532 	str = str_new(imail->mail.data_pool, 64);
533 	if (mail_cache_lookup_field(_mail->transaction->cache_view,
534 				    str, imail->mail.mail.seq, cache_idx) > 0) {
535 		imail->data.guid = str_c(str);
536 		return TRUE;
537 	}
538 	return FALSE;
539 }
540 
imapc_mail_get_guid(struct mail * _mail,const char ** value_r)541 static int imapc_mail_get_guid(struct mail *_mail, const char **value_r)
542 {
543 	struct index_mail *imail = INDEX_MAIL(_mail);
544 	struct imapc_mailbox *mbox = IMAPC_MAILBOX(_mail->box);
545 	const enum index_cache_field cache_idx =
546 		imail->ibox->cache_fields[MAIL_CACHE_GUID].idx;
547 
548 	if (imapc_mail_get_cached_guid(_mail)) {
549 		*value_r = imail->data.guid;
550 		return 0;
551 	}
552 
553 	/* GUID not in cache, fetch it */
554 	if (mbox->guid_fetch_field_name != NULL) {
555 		if (imapc_mail_fetch(_mail, MAIL_FETCH_GUID, NULL) < 0)
556 			return -1;
557 		if (imail->data.guid == NULL) {
558 			(void)imapc_mail_failed(_mail, mbox->guid_fetch_field_name);
559 			return -1;
560 		}
561 	} else {
562 		/* use hash of message headers as the GUID */
563 		if (imapc_mail_get_hdr_hash(imail) < 0)
564 			return -1;
565 	}
566 
567 	index_mail_cache_add_idx(imail, cache_idx,
568 				 imail->data.guid, strlen(imail->data.guid));
569 	*value_r = imail->data.guid;
570 	return 0;
571 }
572 
573 static int
imapc_mail_get_special(struct mail * _mail,enum mail_fetch_field field,const char ** value_r)574 imapc_mail_get_special(struct mail *_mail, enum mail_fetch_field field,
575 		       const char **value_r)
576 {
577 	struct imapc_mailbox *mbox = IMAPC_MAILBOX(_mail->box);
578 	struct index_mail *imail = INDEX_MAIL(_mail);
579 	uint64_t num;
580 
581 	switch (field) {
582 	case MAIL_FETCH_GUID:
583 		if (!IMAPC_BOX_HAS_FEATURE(mbox, IMAPC_FEATURE_GUID_FORCED) &&
584 		    mbox->guid_fetch_field_name == NULL) {
585 			/* GUIDs not supported by server */
586 			break;
587 		}
588 		*value_r = "";
589 		return imapc_mail_get_guid(_mail, value_r);
590 	case MAIL_FETCH_UIDL_BACKEND:
591 		if (!IMAPC_BOX_HAS_FEATURE(mbox, IMAPC_FEATURE_GMAIL_MIGRATION))
592 			break;
593 		if (imapc_mail_get_guid(_mail, value_r) < 0)
594 			return -1;
595 		if (str_to_uint64(*value_r, &num) < 0) {
596 			mail_set_critical(_mail,
597 				"X-GM-MSGID not 64bit integer as expected for POP3 UIDL generation: %s", *value_r);
598 			return -1;
599 		}
600 
601 		*value_r = p_strdup_printf(imail->mail.data_pool,
602 					   "GmailId%"PRIx64, num);
603 		return 0;
604 	case MAIL_FETCH_IMAP_BODY:
605 		if (!IMAPC_BOX_HAS_FEATURE(mbox, IMAPC_FEATURE_FETCH_BODYSTRUCTURE))
606 			break;
607 
608 		if (index_mail_get_cached_body(imail, value_r))
609 			return 0;
610 		if (imapc_mail_fetch(_mail, field, NULL) < 0)
611 			return -1;
612 		if (imail->data.body == NULL) {
613 			(void)imapc_mail_failed(_mail, "BODY");
614 			return -1;
615 		}
616 		*value_r = imail->data.body;
617 		return 0;
618 	case MAIL_FETCH_IMAP_BODYSTRUCTURE:
619 		if (!IMAPC_BOX_HAS_FEATURE(mbox, IMAPC_FEATURE_FETCH_BODYSTRUCTURE))
620 			break;
621 
622 		if (index_mail_get_cached_bodystructure(imail, value_r))
623 			return 0;
624 		if (imapc_mail_fetch(_mail, field, NULL) < 0)
625 			return -1;
626 		if (imail->data.bodystructure == NULL) {
627 			(void)imapc_mail_failed(_mail, "BODYSTRUCTURE");
628 			return -1;
629 		}
630 		*value_r = imail->data.bodystructure;
631 		return 0;
632 	default:
633 		break;
634 	}
635 
636 	return index_mail_get_special(_mail, field, value_r);
637 }
638 
639 struct mail_vfuncs imapc_mail_vfuncs = {
640 	imapc_mail_close,
641 	index_mail_free,
642 	imapc_mail_set_seq,
643 	index_mail_set_uid,
644 	index_mail_set_uid_cache_updates,
645 	imapc_mail_prefetch,
646 	index_mail_precache,
647 	imapc_mail_add_temp_wanted_fields,
648 
649 	index_mail_get_flags,
650 	index_mail_get_keywords,
651 	index_mail_get_keyword_indexes,
652 	imapc_mail_get_modseq,
653 	index_mail_get_pvt_modseq,
654 	index_mail_get_parts,
655 	index_mail_get_date,
656 	imapc_mail_get_received_date,
657 	imapc_mail_get_save_date,
658 	imapc_mail_get_virtual_size,
659 	imapc_mail_get_physical_size,
660 	imapc_mail_get_first_header,
661 	imapc_mail_get_headers,
662 	imapc_mail_get_header_stream,
663 	imapc_mail_get_stream,
664 	index_mail_get_binary_stream,
665 	imapc_mail_get_special,
666 	index_mail_get_backend_mail,
667 	index_mail_update_flags,
668 	index_mail_update_keywords,
669 	index_mail_update_modseq,
670 	index_mail_update_pvt_modseq,
671 	NULL,
672 	index_mail_expunge,
673 	index_mail_set_cache_corrupted,
674 	index_mail_opened,
675 };
676