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