Coverage Report

Created: 2026-09-01 06:28

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