Coverage Report

Created: 2026-06-09 06:49

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/dovecot/src/lib-storage/mail.c
Line
Count
Source
1
/* Copyright (c) 2002-2018 Dovecot authors, see the included COPYING file */
2
3
#include "lib.h"
4
#include "ioloop.h"
5
#include "buffer.h"
6
#include "hash.h"
7
#include "hex-binary.h"
8
#include "crc32.h"
9
#include "sha1.h"
10
#include "hostpid.h"
11
#include "istream.h"
12
#include "mail-cache.h"
13
#include "mail-storage-private.h"
14
#include "message-id.h"
15
#include "message-part-data.h"
16
#include "imap-bodystructure.h"
17
18
#include <time.h>
19
20
struct mail *mail_alloc(struct mailbox_transaction_context *t,
21
      enum mail_fetch_field wanted_fields,
22
      struct mailbox_header_lookup_ctx *wanted_headers)
23
0
{
24
0
  struct mail *mail;
25
26
0
  i_assert(wanted_headers == NULL || wanted_headers->box == t->box);
27
28
0
  T_BEGIN {
29
0
    mail = t->box->v.mail_alloc(t, wanted_fields, wanted_headers);
30
0
    hook_mail_allocated(mail);
31
0
  } T_END;
32
33
0
  return mail;
34
0
}
35
36
void mail_free(struct mail **mail)
37
0
{
38
0
  struct mail_private *p = (struct mail_private *)*mail;
39
40
  /* make sure mailbox_search_*() users don't try to free the mail
41
     directly */
42
0
  i_assert(!p->search_mail);
43
44
0
  p->v.free(*mail);
45
0
  *mail = NULL;
46
0
}
47
48
void mail_set_seq(struct mail *mail, uint32_t seq)
49
0
{
50
0
  struct mail_private *p = (struct mail_private *)mail;
51
52
0
  T_BEGIN {
53
0
    p->v.set_seq(mail, seq, FALSE);
54
0
  } T_END;
55
0
}
56
57
void mail_set_seq_saving(struct mail *mail, uint32_t seq)
58
0
{
59
0
  struct mail_private *p = (struct mail_private *)mail;
60
61
0
  T_BEGIN {
62
0
    p->v.set_seq(mail, seq, TRUE);
63
0
  } T_END;
64
0
}
65
66
bool mail_set_uid(struct mail *mail, uint32_t uid)
67
0
{
68
0
  struct mail_private *p = (struct mail_private *)mail;
69
0
  bool ret;
70
71
0
  T_BEGIN {
72
0
    ret = p->v.set_uid(mail, uid);
73
0
  } T_END;
74
0
  return ret;
75
0
}
76
77
bool mail_prefetch(struct mail *mail)
78
0
{
79
0
  struct mail_private *p = (struct mail_private *)mail;
80
0
  bool ret;
81
82
0
  T_BEGIN {
83
0
    ret = p->v.prefetch(mail);
84
0
  } T_END;
85
0
  return ret;
86
0
}
87
88
void mail_add_temp_wanted_fields(struct mail *mail,
89
         enum mail_fetch_field fields,
90
         struct mailbox_header_lookup_ctx *headers)
91
0
{
92
0
  struct mail_private *p = (struct mail_private *)mail;
93
94
0
  i_assert(headers == NULL || headers->box == mail->box);
95
96
0
  p->v.add_temp_wanted_fields(mail, fields, headers);
97
0
}
98
99
static bool index_mail_get_age_days(struct mail *mail, int *days_r)
100
0
{
101
0
  int age_days;
102
0
  const struct mail_index_header *hdr =
103
0
    mail_index_get_header(mail->transaction->view);
104
0
  int n_days = N_ELEMENTS(hdr->day_first_uid);
105
106
0
  for (age_days = 0; age_days < n_days; age_days++) {
107
0
    if (mail->uid >= hdr->day_first_uid[age_days])
108
0
      break;
109
0
  }
110
111
0
  if (age_days == n_days) {
112
    /* mail is too old, cannot determine its age from
113
       day_first_uid[]. */
114
0
    return FALSE;
115
0
  }
116
117
0
  if (hdr->day_stamp != 0) {
118
    /* offset for hdr->day_stamp */
119
0
    age_days += (ioloop_time - hdr->day_stamp) / (3600 * 24);
120
0
  }
121
0
  *days_r = age_days;
122
0
  return TRUE;
123
0
}
124
125
void mail_event_create(struct mail *mail)
126
0
{
127
0
  struct mail_private *p = (struct mail_private *)mail;
128
0
  int age_days;
129
130
0
  if (p->_event != NULL)
131
0
    return;
132
0
  p->_event = event_create(mail->box->event);
133
0
  event_add_category(p->_event, &event_category_mail);
134
0
  event_add_int(p->_event, "seq", mail->seq);
135
0
  event_add_int(p->_event, "uid", mail->uid);
136
  /* Add mail age field to event. */
137
0
  if (index_mail_get_age_days(mail, &age_days))
138
0
    event_add_int(p->_event, "mail_age_days", age_days);
139
140
0
  T_BEGIN {
141
0
    char uid_buf[MAX_INT_STRLEN];
142
0
    const char *prefix;
143
0
    if (p->mail.uid == 0) {
144
0
      i_assert(p->mail.saving);
145
0
      prefix = "Saving mail: ";
146
0
    } else {
147
0
      prefix = t_strconcat(
148
0
        p->mail.saving ? "Saving mail UID " : "UID ",
149
0
        dec2str_buf(uid_buf, p->mail.uid),
150
0
        ": ",
151
0
        NULL);
152
0
    }
153
0
    event_set_append_log_prefix(p->_event, prefix);
154
0
  } T_END;
155
0
}
156
157
struct event *mail_event(struct mail *mail)
158
0
{
159
0
  struct mail_private *p = (struct mail_private *)mail;
160
161
0
  mail_event_create(mail);
162
0
  return p->_event;
163
0
}
164
165
enum mail_flags mail_get_flags(struct mail *mail)
166
0
{
167
0
  struct mail_private *p = (struct mail_private *)mail;
168
169
0
  return p->v.get_flags(mail);
170
0
}
171
172
uint64_t mail_get_modseq(struct mail *mail)
173
0
{
174
0
  struct mail_private *p = (struct mail_private *)mail;
175
176
0
  return p->v.get_modseq(mail);
177
0
}
178
179
uint64_t mail_get_pvt_modseq(struct mail *mail)
180
0
{
181
0
  struct mail_private *p = (struct mail_private *)mail;
182
183
0
  return p->v.get_pvt_modseq(mail);
184
0
}
185
186
const char *const *mail_get_keywords(struct mail *mail)
187
0
{
188
0
  struct mail_private *p = (struct mail_private *)mail;
189
190
0
  return p->v.get_keywords(mail);
191
0
}
192
193
const ARRAY_TYPE(keyword_indexes) *mail_get_keyword_indexes(struct mail *mail)
194
0
{
195
0
  struct mail_private *p = (struct mail_private *)mail;
196
197
0
  return p->v.get_keyword_indexes(mail);
198
0
}
199
200
int mail_get_parts(struct mail *mail, struct message_part **parts_r)
201
0
{
202
0
  struct mail_private *p = (struct mail_private *)mail;
203
0
  int ret;
204
205
0
  T_BEGIN {
206
0
    ret = p->v.get_parts(mail, parts_r);
207
0
  } T_END;
208
0
  return ret;
209
0
}
210
211
int mail_get_date(struct mail *mail, time_t *date_r, int *timezone_r)
212
0
{
213
0
  struct mail_private *p = (struct mail_private *)mail;
214
0
  int ret;
215
216
0
  T_BEGIN {
217
0
    ret = p->v.get_date(mail, date_r, timezone_r);
218
0
  } T_END;
219
0
  return ret;
220
0
}
221
222
int mail_get_received_date(struct mail *mail, time_t *date_r)
223
0
{
224
0
  struct mail_private *p = (struct mail_private *)mail;
225
0
  int ret;
226
227
0
  T_BEGIN {
228
0
    ret = p->v.get_received_date(mail, date_r);
229
0
  } T_END;
230
0
  return ret;
231
0
}
232
233
int mail_get_save_date(struct mail *mail, time_t *date_r)
234
0
{
235
0
  struct mail_private *p = (struct mail_private *)mail;
236
0
  int ret;
237
238
0
  T_BEGIN {
239
0
    ret = p->v.get_save_date(mail, date_r);
240
0
  } T_END;
241
0
  return ret;
242
0
}
243
244
int mail_get_virtual_size(struct mail *mail, uoff_t *size_r)
245
0
{
246
0
  struct mail_private *p = (struct mail_private *)mail;
247
0
  int ret;
248
249
0
  T_BEGIN {
250
0
    ret = p->v.get_virtual_size(mail, size_r);
251
0
  } T_END;
252
0
  return ret;
253
0
}
254
255
int mail_get_physical_size(struct mail *mail, uoff_t *size_r)
256
0
{
257
0
  struct mail_private *p = (struct mail_private *)mail;
258
0
  int ret;
259
260
0
  T_BEGIN {
261
0
    ret = p->v.get_physical_size(mail, size_r);
262
0
  } T_END;
263
0
  return ret;
264
0
}
265
266
int mail_get_first_header(struct mail *mail, const char *field,
267
        const char **value_r)
268
0
{
269
0
  struct mail_private *p = (struct mail_private *)mail;
270
0
  int ret;
271
272
0
  T_BEGIN {
273
0
    ret = p->v.get_first_header(mail, field, FALSE, value_r);
274
0
  } T_END;
275
0
  return ret;
276
0
}
277
278
int mail_get_first_header_utf8(struct mail *mail, const char *field,
279
             const char **value_r)
280
0
{
281
0
  struct mail_private *p = (struct mail_private *)mail;
282
0
  int ret;
283
284
0
  T_BEGIN {
285
0
    ret = p->v.get_first_header(mail, field, TRUE, value_r);
286
0
  } T_END;
287
0
  return ret;
288
0
}
289
290
int mail_get_headers(struct mail *mail, const char *field,
291
         const char *const **value_r)
292
0
{
293
0
  struct mail_private *p = (struct mail_private *)mail;
294
0
  int ret;
295
296
0
  T_BEGIN {
297
0
    ret = p->v.get_headers(mail, field, FALSE, value_r);
298
0
  } T_END;
299
0
  return ret;
300
0
}
301
302
int mail_get_headers_utf8(struct mail *mail, const char *field,
303
        const char *const **value_r)
304
0
{
305
0
  struct mail_private *p = (struct mail_private *)mail;
306
0
  int ret;
307
308
0
  T_BEGIN {
309
0
    ret = p->v.get_headers(mail, field, TRUE, value_r);
310
0
  } T_END;
311
0
  return ret;
312
0
}
313
314
int mail_get_header_stream(struct mail *mail,
315
         struct mailbox_header_lookup_ctx *headers,
316
         struct istream **stream_r)
317
0
{
318
0
  struct mail_private *p = (struct mail_private *)mail;
319
0
  int ret;
320
321
0
  i_assert(headers->count > 0);
322
0
  i_assert(headers->box == mail->box);
323
324
0
  T_BEGIN {
325
0
    ret = p->v.get_header_stream(mail, headers, stream_r);
326
0
  } T_END;
327
0
  return ret;
328
0
}
329
330
void mail_set_aborted(struct mail *mail)
331
0
{
332
0
  mail_storage_set_error(mail->box->storage, MAIL_ERROR_LOOKUP_ABORTED,
333
0
             "Mail field not cached");
334
0
}
335
336
int mail_get_stream(struct mail *mail, struct message_size *hdr_size,
337
        struct message_size *body_size, struct istream **stream_r)
338
0
{
339
0
  return mail_get_stream_because(mail, hdr_size, body_size,
340
0
               "mail stream", stream_r);
341
0
}
342
343
int mail_get_stream_because(struct mail *mail, struct message_size *hdr_size,
344
          struct message_size *body_size,
345
          const char *reason, struct istream **stream_r)
346
0
{
347
0
  struct mail_private *p = (struct mail_private *)mail;
348
0
  int ret;
349
350
0
  if (mail->lookup_abort != MAIL_LOOKUP_ABORT_NEVER) {
351
0
    mail_set_aborted(mail);
352
0
    return -1;
353
0
  }
354
0
  T_BEGIN {
355
0
    p->get_stream_reason = reason;
356
0
    ret = p->v.get_stream(mail, TRUE, hdr_size, body_size, stream_r);
357
0
    p->get_stream_reason = "";
358
0
  } T_END;
359
0
  i_assert(ret < 0 || (*stream_r)->blocking);
360
0
  return ret;
361
0
}
362
363
int mail_get_hdr_stream(struct mail *mail, struct message_size *hdr_size,
364
      struct istream **stream_r)
365
0
{
366
0
  return mail_get_hdr_stream_because(mail, hdr_size, "header stream", stream_r);
367
0
}
368
369
int mail_get_hdr_stream_because(struct mail *mail,
370
        struct message_size *hdr_size,
371
        const char *reason, struct istream **stream_r)
372
0
{
373
0
  struct mail_private *p = (struct mail_private *)mail;
374
0
  int ret;
375
376
0
  if (mail->lookup_abort != MAIL_LOOKUP_ABORT_NEVER) {
377
0
    mail_set_aborted(mail);
378
0
    return -1;
379
0
  }
380
0
  T_BEGIN {
381
0
    p->get_stream_reason = reason;
382
0
    ret = p->v.get_stream(mail, FALSE, hdr_size, NULL, stream_r);
383
0
    p->get_stream_reason = "";
384
0
  } T_END;
385
0
  i_assert(ret < 0 || (*stream_r)->blocking);
386
0
  return ret;
387
0
}
388
389
int mail_get_binary_stream(struct mail *mail, const struct message_part *part,
390
         bool include_hdr,
391
         struct mail_binary_properties *bprops_r,
392
         struct istream **stream_r)
393
0
{
394
0
  struct mail_private *p = (struct mail_private *)mail;
395
0
  int ret;
396
397
0
  if (bprops_r != NULL)
398
0
    i_zero(bprops_r);
399
400
0
  if (mail->lookup_abort != MAIL_LOOKUP_ABORT_NEVER) {
401
0
    mail_set_aborted(mail);
402
0
    return -1;
403
0
  }
404
0
  T_BEGIN {
405
0
    ret = p->v.get_binary_stream(mail, part, include_hdr,
406
0
               bprops_r, stream_r);
407
0
  } T_END;
408
0
  i_assert(ret < 0 || (*stream_r)->blocking);
409
0
  return ret;
410
0
}
411
412
int mail_get_binary_properties(struct mail *mail,
413
             const struct message_part *part,
414
             bool include_hdr,
415
             struct mail_binary_properties *bprops_r)
416
0
{
417
0
  struct mail_private *p = (struct mail_private *)mail;
418
0
  int ret;
419
420
0
  if (bprops_r != NULL)
421
0
    i_zero(bprops_r);
422
423
0
  T_BEGIN {
424
0
    ret = p->v.get_binary_stream(mail, part, include_hdr,
425
0
               bprops_r, NULL);
426
0
  } T_END;
427
0
  return ret;
428
0
}
429
430
int mail_get_special(struct mail *mail, enum mail_fetch_field field,
431
         const char **value_r)
432
0
{
433
0
  struct mail_private *p = (struct mail_private *)mail;
434
435
0
  if (p->v.get_special(mail, field, value_r) < 0)
436
0
    return -1;
437
0
  i_assert(*value_r != NULL);
438
0
  return 0;
439
0
}
440
441
int mail_get_backend_mail(struct mail *mail, struct mail **real_mail_r)
442
0
{
443
0
  struct mail_private *p = (struct mail_private *)mail;
444
0
  return p->v.get_backend_mail(mail, real_mail_r);
445
0
}
446
447
static int mail_get_message_id_full(struct mail *mail,
448
            const char **value_r,
449
            bool require_valid)
450
0
{
451
0
  const char *hdr_value, *msgid_bare;
452
0
  int ret;
453
454
0
  ret = mail_get_first_header(mail, "Message-ID", &hdr_value);
455
0
  if (ret <= 0) {
456
0
    *value_r = NULL;
457
0
    return ret;
458
0
  }
459
460
0
  *value_r = hdr_value; /* save it now as next function alters it */
461
0
  msgid_bare = message_id_get_next(&hdr_value);
462
463
0
  if (msgid_bare != NULL) {
464
    /* Complete the message ID with surrounding `<' and `>'. */
465
0
    *value_r = t_strconcat("<",  msgid_bare, ">", NULL);
466
0
    return 1;
467
0
  } else if (!require_valid) {
468
    /* *value_r already set above */
469
0
    return 1;
470
0
  } else {
471
0
    *value_r = NULL;
472
0
    return 0;
473
0
  }
474
0
}
475
476
int mail_get_message_id(struct mail *mail, const char **value_r)
477
0
{
478
0
  return mail_get_message_id_full(mail, value_r, TRUE/*require_valid*/);
479
0
}
480
481
int mail_get_message_id_no_validation(struct mail *mail, const char **value_r)
482
0
{
483
0
  return mail_get_message_id_full(mail, value_r, FALSE/*require_valid*/);
484
0
}
485
486
void mail_update_flags(struct mail *mail, enum modify_type modify_type,
487
           enum mail_flags flags)
488
0
{
489
0
  struct mail_private *p = (struct mail_private *)mail;
490
491
0
  p->v.update_flags(mail, modify_type, flags);
492
0
}
493
494
void mail_update_keywords(struct mail *mail, enum modify_type modify_type,
495
        struct mail_keywords *keywords)
496
0
{
497
0
  struct mail_private *p = (struct mail_private *)mail;
498
499
0
  p->v.update_keywords(mail, modify_type, keywords);
500
0
}
501
502
void mail_update_modseq(struct mail *mail, uint64_t min_modseq)
503
0
{
504
0
  struct mail_private *p = (struct mail_private *)mail;
505
506
0
  p->v.update_modseq(mail, min_modseq);
507
0
}
508
509
void mail_update_pvt_modseq(struct mail *mail, uint64_t min_pvt_modseq)
510
0
{
511
0
  struct mail_private *p = (struct mail_private *)mail;
512
513
0
  p->v.update_pvt_modseq(mail, min_pvt_modseq);
514
0
}
515
516
void mail_update_pop3_uidl(struct mail *mail, const char *uidl)
517
0
{
518
0
  struct mail_private *p = (struct mail_private *)mail;
519
520
0
  if (p->v.update_pop3_uidl != NULL)
521
0
    p->v.update_pop3_uidl(mail, uidl);
522
0
}
523
524
void mail_expunge(struct mail *mail)
525
0
{
526
0
  struct mail_private *p = (struct mail_private *)mail;
527
528
0
  T_BEGIN {
529
0
    p->v.expunge(mail);
530
0
    mail_expunge_requested_event(mail);
531
0
  } T_END;
532
0
}
533
534
void mail_autoexpunge(struct mail *mail)
535
0
{
536
0
  struct mail_private *p = (struct mail_private *)mail;
537
0
  p->autoexpunged = TRUE;
538
0
  mail_expunge(mail);
539
0
  p->autoexpunged = FALSE;
540
0
}
541
542
void mail_set_expunged(struct mail *mail)
543
0
{
544
0
  mail_storage_set_error(mail->box->storage, MAIL_ERROR_EXPUNGED,
545
0
             "Message was expunged");
546
0
  mail->expunged = TRUE;
547
0
}
548
549
int mail_precache(struct mail *mail)
550
0
{
551
0
  struct mail_private *p = (struct mail_private *)mail;
552
0
  int ret;
553
554
0
  T_BEGIN {
555
0
    ret = p->v.precache(mail);
556
0
  } T_END;
557
0
  return ret;
558
0
}
559
560
void mail_set_cache_corrupted(struct mail *mail,
561
            enum mail_fetch_field field,
562
            const char *reason)
563
0
{
564
0
  struct mail_private *p = (struct mail_private *)mail;
565
0
  p->v.set_cache_corrupted(mail, field, reason);
566
0
}
567
568
void mail_generate_guid_128_hash(const char *guid, guid_128_t guid_128_r)
569
0
{
570
0
  unsigned char sha1_sum[SHA1_RESULTLEN];
571
0
  buffer_t buf;
572
573
0
  if (guid_128_from_string(guid, guid_128_r) < 0) {
574
    /* not 128bit hex. use a hash of it instead. */
575
0
    buffer_create_from_data(&buf, guid_128_r, GUID_128_SIZE);
576
0
    buffer_set_used_size(&buf, 0);
577
0
    sha1_get_digest(guid, strlen(guid), sha1_sum);
578
#if SHA1_RESULTLEN < GUID_128_SIZE
579
#  error not possible
580
#endif
581
0
    buffer_append(&buf,
582
0
            sha1_sum + SHA1_RESULTLEN - GUID_128_SIZE,
583
0
            GUID_128_SIZE);
584
0
  }
585
0
}
586
587
static bool
588
mail_message_has_attachment(struct message_part *part,
589
          const struct message_part_attachment_settings *set)
590
0
{
591
0
  bool has_attachment = FALSE;
592
0
  for (; part != NULL && !has_attachment; part = part->next) T_BEGIN {
593
0
    has_attachment = message_part_is_attachment(part, set) ||
594
0
      mail_message_has_attachment(part->children, set);
595
0
  } T_END;
596
597
0
  return has_attachment;
598
0
}
599
600
bool mail_has_attachment_keywords(struct mail *mail)
601
0
{
602
0
  const char *const *kw = mail_get_keywords(mail);
603
0
  return (str_array_icase_find(kw, MAIL_KEYWORD_HAS_ATTACHMENT) !=
604
0
    str_array_icase_find(kw, MAIL_KEYWORD_HAS_NO_ATTACHMENT));
605
0
}
606
607
static int mail_parse_parts(struct mail *mail, struct message_part **parts_r)
608
0
{
609
0
  const char *structure, *error;
610
0
  struct mail_private *pmail = (struct mail_private*)mail;
611
612
  /* need to get bodystructure first */
613
0
  if (mail_get_special(mail, MAIL_FETCH_IMAP_BODYSTRUCTURE,
614
0
           &structure) < 0) {
615
    /* Don't bother logging an error. See
616
       mail_set_attachment_keywords(). */
617
0
    return -1;
618
0
  }
619
0
  if (imap_bodystructure_parse_full(structure, pmail->data_pool, parts_r,
620
0
            &error) < 0) {
621
0
    mail_set_cache_corrupted(mail, MAIL_FETCH_IMAP_BODYSTRUCTURE,
622
0
           error);
623
0
    return -1;
624
0
  }
625
0
  return 0;
626
0
}
627
628
int mail_set_attachment_keywords(struct mail *mail)
629
0
{
630
0
  int ret;
631
0
  const struct mail_storage_settings *mail_set =
632
0
    mail_storage_get_settings(mailbox_get_storage(mail->box));
633
634
0
  const char *const keyword_has_attachment[] = {
635
0
    MAIL_KEYWORD_HAS_ATTACHMENT,
636
0
    NULL,
637
0
  };
638
0
  const char *const keyword_has_no_attachment[] = {
639
0
    MAIL_KEYWORD_HAS_NO_ATTACHMENT,
640
0
    NULL
641
0
  };
642
0
  struct message_part_attachment_settings set = {
643
0
    .content_type_filter =
644
0
      mail_set->parsed_mail_attachment_content_type_filter,
645
0
    .exclude_inlined =
646
0
      mail_set->parsed_mail_attachment_exclude_inlined,
647
0
  };
648
0
  struct mail_keywords *kw_has = NULL, *kw_has_not = NULL;
649
650
  /* walk all parts and see if there is an attachment */
651
0
  struct message_part *parts;
652
0
  if (mail_get_parts(mail, &parts) < 0) {
653
    /* Callers don't really care about the exact error, and
654
       critical errors were already logged. Most importantly we
655
       don't want to log MAIL_ERROR_LOOKUP_ABORTED since that is
656
       an expected error. */
657
0
    ret = -1;
658
0
  } else if (parts->data == NULL &&
659
0
       mail_parse_parts(mail, &parts) < 0) {
660
0
    ret = -1;
661
0
  } else if (mailbox_keywords_create(mail->box, keyword_has_attachment, &kw_has) < 0 ||
662
0
       mailbox_keywords_create(mail->box, keyword_has_no_attachment, &kw_has_not) < 0) {
663
0
    mail_set_critical(mail, "Failed to add attachment keywords: "
664
0
          "mailbox_keywords_create(%s) failed: %s",
665
0
          mailbox_get_vname(mail->box),
666
0
          mail_storage_get_last_internal_error(mail->box->storage, NULL));
667
0
    ret = -1;
668
0
  } else {
669
0
    bool has_attachment = mail_message_has_attachment(parts, &set);
670
671
    /* make sure only one of the keywords gets set */
672
0
    mail_update_keywords(mail, MODIFY_REMOVE, has_attachment ? kw_has_not : kw_has);
673
0
    mail_update_keywords(mail, MODIFY_ADD, has_attachment ? kw_has : kw_has_not);
674
0
    ret = has_attachment ? 1 : 0;
675
0
  }
676
677
0
  if (kw_has != NULL)
678
0
    mailbox_keywords_unref(&kw_has);
679
0
  if (kw_has_not != NULL)
680
0
    mailbox_keywords_unref(&kw_has_not);
681
682
0
  return ret;
683
0
}
684
685
bool mail_stream_access_start(struct mail *mail)
686
0
{
687
0
  if (mail->lookup_abort != MAIL_LOOKUP_ABORT_NEVER) {
688
0
    mail_set_aborted(mail);
689
0
    return FALSE;
690
0
  }
691
0
  mail->mail_stream_accessed = TRUE;
692
0
  mail_event_create(mail);
693
0
  return TRUE;
694
0
}
695
696
bool mail_metadata_access_start(struct mail *mail)
697
0
{
698
0
  if (mail->lookup_abort >= MAIL_LOOKUP_ABORT_NOT_IN_CACHE) {
699
0
    mail_set_aborted(mail);
700
0
    return FALSE;
701
0
  }
702
0
  mail->mail_metadata_accessed = TRUE;
703
0
  mail_event_create(mail);
704
0
  return TRUE;
705
0
}
706
707
void mail_opened_event(struct mail *mail)
708
0
{
709
0
  struct mail_private *pmail =
710
0
    container_of(mail, struct mail_private, mail);
711
712
  /* If istream is opened twice for the same mail, count it as a single
713
     mail_opened event. Their cost is effectively the same, so having
714
     two events would just be confusing the statistics. */
715
0
  if (pmail->mail_opened_event_sent)
716
0
    return;
717
0
  pmail->mail_opened_event_sent = TRUE;
718
719
0
  struct event_passthrough *e =
720
0
    event_create_passthrough(mail_event(mail))->
721
0
    set_name("mail_opened")->
722
0
    add_str("reason", pmail->get_stream_reason);
723
0
  if (pmail->get_stream_reason != NULL)
724
0
    e_debug(e->event(), "Opened mail because: %s",
725
0
      pmail->get_stream_reason);
726
0
  else
727
0
    e_debug(e->event(), "Opened mail");
728
0
}
729
730
void mail_metadata_accessed_event(struct event *mail_event)
731
0
{
732
0
  struct event_passthrough *e =
733
0
    event_create_passthrough(mail_event)->
734
0
    set_name("mail_metadata_accessed");
735
0
  e_debug(e->event(), "Mail metadata accessed");
736
0
}
737
738
void mail_expunge_requested_event(struct mail *mail)
739
0
{
740
0
  struct event_passthrough *e =
741
0
    event_create_passthrough(mail_event(mail))->
742
0
    set_name("mail_expunge_requested")->
743
0
    add_int("uid", mail->uid)->
744
0
    add_int("seq", mail->seq);
745
0
  e_debug(e->event(), "Expunge requested");
746
0
}