Coverage Report

Created: 2026-10-17 06:32

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/dovecot/src/lib-storage/index/index-search.c
Line
Count
Source
1
/* Copyright (c) Dovecot authors, see top-level COPYING file */
2
3
#include "lib.h"
4
#include "ioloop.h"
5
#include "array.h"
6
#include "istream.h"
7
#include "utc-offset.h"
8
#include "str.h"
9
#include "time-util.h"
10
#include "unichar.h"
11
#include "imap-match.h"
12
#include "master-service.h"
13
#include "message-address.h"
14
#include "message-date.h"
15
#include "message-search.h"
16
#include "message-parser.h"
17
#include "mail-index-modseq.h"
18
#include "index-storage.h"
19
#include "index-mail.h"
20
#include "index-sort.h"
21
#include "mail-search.h"
22
#include "mailbox-search-result-private.h"
23
#include "mailbox-recent-flags.h"
24
#include "index-search-private.h"
25
26
#include <ctype.h>
27
28
0
#define SEARCH_COST_DENTRY 3ULL
29
0
#define SEARCH_COST_ATTR 1ULL
30
0
#define SEARCH_COST_FILES_READ 25ULL
31
0
#define SEARCH_COST_KBYTE 15ULL
32
0
#define SEARCH_COST_CACHE 1ULL
33
34
0
#define SEARCH_MIN_NONBLOCK_USECS 200000
35
0
#define SEARCH_MAX_NONBLOCK_USECS 250000
36
0
#define SEARCH_INITIAL_MAX_COST 30000
37
0
#define SEARCH_RECALC_MIN_USECS 50000
38
39
/* If interrupt signal is received and search doesn't finish in this many
40
   milliseconds, fail the search with MAIL_ERRSTR_INTERRUPTED. */
41
0
#define SEARCH_INTERRUPT_DELAY_MSECS 2000
42
43
struct search_header_context {
44
        struct index_search_context *index_ctx;
45
        struct index_mail *imail;
46
  struct mail_search_arg *args;
47
48
  struct message_block decoded_block;
49
  bool decoded_block_set;
50
51
        struct message_header_line *hdr;
52
53
  bool parse_headers:1;
54
  bool custom_header:1;
55
  bool threading:1;
56
};
57
58
struct search_body_context {
59
        struct index_search_context *index_ctx;
60
  struct istream *input;
61
  struct message_part *part;
62
};
63
64
static void search_parse_msgset_args(unsigned int messages_count,
65
             struct mail_search_arg *args,
66
             uint32_t *seq1_r, uint32_t *seq2_r);
67
68
static void ATTR_NULL(2)
69
search_none(struct mail_search_arg *arg ATTR_UNUSED, void *ctx ATTR_UNUSED)
70
0
{
71
0
}
72
73
static void search_set_failed(struct index_search_context *ctx)
74
0
{
75
0
  if (ctx->failed)
76
0
    return;
77
78
  /* remember the first failure */
79
0
  mail_storage_last_error_push(ctx->box->storage);
80
0
  ctx->failed = TRUE;
81
0
}
82
83
static void search_cur_mail_failed(struct index_search_context *ctx)
84
0
{
85
0
  switch (mailbox_get_last_mail_error(ctx->cur_mail->box)) {
86
0
  case MAIL_ERROR_EXPUNGED:
87
0
    ctx->mail_ctx.seen_lost_data = TRUE;
88
0
    break;
89
0
  case MAIL_ERROR_LOOKUP_ABORTED:
90
    /* expected failure */
91
0
    break;
92
0
  default:
93
0
    search_set_failed(ctx);
94
0
    break;
95
0
  }
96
0
}
97
98
static void search_init_arg(struct mail_search_arg *arg,
99
          struct index_search_context *ctx)
100
0
{
101
0
  struct mailbox_metadata metadata;
102
0
  bool match;
103
104
0
  switch (arg->type) {
105
0
  case SEARCH_SEQSET:
106
0
    ctx->have_seqsets = TRUE;
107
0
    break;
108
0
  case SEARCH_UIDSET:
109
0
  case SEARCH_INTHREAD:
110
0
  case SEARCH_FLAGS:
111
0
  case SEARCH_KEYWORDS:
112
0
  case SEARCH_MODSEQ:
113
0
    if (arg->type == SEARCH_MODSEQ)
114
0
      mail_index_modseq_enable(ctx->box->index);
115
0
    ctx->have_index_args = TRUE;
116
0
    break;
117
0
  case SEARCH_MAILBOX_GUID:
118
0
    if (mailbox_get_metadata(ctx->box, MAILBOX_METADATA_GUID,
119
0
           &metadata) < 0) {
120
      /* result will be unknown */
121
0
      break;
122
0
    }
123
124
0
    match = strcmp(guid_128_to_string(metadata.guid),
125
0
             arg->value.str) == 0;
126
0
    if (match != arg->match_not)
127
0
      arg->match_always = TRUE;
128
0
    else {
129
0
      arg->nonmatch_always = TRUE;
130
0
      ctx->have_nonmatch_always = TRUE;
131
0
    }
132
0
    break;
133
0
  case SEARCH_MAILBOX:
134
0
  case SEARCH_MAILBOX_GLOB:
135
0
    ctx->have_mailbox_args = TRUE;
136
0
    break;
137
0
  case SEARCH_ALL:
138
0
    if (!arg->match_not)
139
0
      arg->match_always = TRUE;
140
0
    else {
141
0
      arg->nonmatch_always = TRUE;
142
0
      ctx->have_nonmatch_always = TRUE;
143
0
    }
144
0
    break;
145
0
  default:
146
0
    break;
147
0
  }
148
0
}
149
150
static void search_seqset_arg(struct mail_search_arg *arg,
151
            struct index_search_context *ctx)
152
0
{
153
0
  if (arg->type == SEARCH_SEQSET) {
154
0
    if (seq_range_exists(&arg->value.seqset, ctx->mail_ctx.seq))
155
0
      ARG_SET_RESULT(arg, 1);
156
0
    else
157
0
      ARG_SET_RESULT(arg, 0);
158
0
  }
159
0
}
160
161
static int search_arg_match_keywords(struct index_search_context *ctx,
162
             struct mail_search_arg *arg)
163
0
{
164
0
  ARRAY_TYPE(keyword_indexes) keyword_indexes_arr;
165
0
  const struct mail_keywords *search_kws = arg->initialized.keywords;
166
0
  const unsigned int *keyword_indexes;
167
0
  unsigned int i, j, count;
168
169
0
  i_assert(search_kws != NULL);
170
0
  if (search_kws->count == 0) {
171
    /* invalid keyword - never matches */
172
0
    return 0;
173
0
  }
174
175
0
  t_array_init(&keyword_indexes_arr, 128);
176
0
  mail_index_lookup_keywords(ctx->view, ctx->mail_ctx.seq,
177
0
           &keyword_indexes_arr);
178
0
  keyword_indexes = array_get(&keyword_indexes_arr, &count);
179
180
  /* there probably aren't many keywords, so O(n*m) for now */
181
0
  for (i = 0; i < search_kws->count; i++) {
182
0
    for (j = 0; j < count; j++) {
183
0
      if (search_kws->idx[i] == keyword_indexes[j])
184
0
        break;
185
0
    }
186
0
    if (j == count)
187
0
      return 0;
188
0
  }
189
0
  return 1;
190
0
}
191
192
static bool
193
index_search_get_pvt(struct index_search_context *ctx, uint32_t uid)
194
0
{
195
0
  index_transaction_init_pvt(ctx->mail_ctx.transaction);
196
197
0
  if (ctx->pvt_uid == uid)
198
0
    return ctx->pvt_seq != 0;
199
0
  ctx->pvt_uid = uid;
200
0
  return mail_index_lookup_seq(ctx->mail_ctx.transaction->view_pvt,
201
0
             uid, &ctx->pvt_seq);
202
0
}
203
204
/* Returns >0 = matched, 0 = not matched, -1 = unknown */
205
static int search_arg_match_index(struct index_search_context *ctx,
206
          struct mail_search_arg *arg,
207
          const struct mail_index_record *rec)
208
0
{
209
0
  enum mail_flags flags, pvt_flags_mask;
210
0
  uint64_t modseq;
211
0
  int ret;
212
213
0
  switch (arg->type) {
214
0
  case SEARCH_UIDSET:
215
0
  case SEARCH_INTHREAD:
216
0
    return seq_range_exists(&arg->value.seqset, rec->uid) ? 1 : 0;
217
0
  case SEARCH_FLAGS:
218
    /* recent flag shouldn't be set, but indexes from v1.0.x
219
       may contain it. */
220
0
    flags = rec->flags & ENUM_NEGATE(MAIL_RECENT);
221
0
    if ((arg->value.flags & MAIL_RECENT) != 0 &&
222
0
        mailbox_recent_flags_have_uid(ctx->box, rec->uid))
223
0
      flags |= MAIL_RECENT;
224
0
    if (ctx->box->view_pvt == NULL) {
225
      /* no private view (set by view syncing) ->
226
         no private flags */
227
0
    } else {
228
0
      pvt_flags_mask = mailbox_get_private_flags_mask(ctx->box);
229
0
      flags &= ENUM_NEGATE(pvt_flags_mask);
230
0
      if (index_search_get_pvt(ctx, rec->uid)) {
231
0
        rec = mail_index_lookup(ctx->mail_ctx.transaction->view_pvt,
232
0
              ctx->pvt_seq);
233
0
        flags |= rec->flags & pvt_flags_mask;
234
0
      }
235
0
    }
236
0
    return (flags & arg->value.flags) == arg->value.flags ? 1 : 0;
237
0
  case SEARCH_KEYWORDS:
238
0
    T_BEGIN {
239
0
      ret = search_arg_match_keywords(ctx, arg);
240
0
    } T_END;
241
0
    return ret;
242
0
  case SEARCH_MODSEQ: {
243
0
    modseq = mail_index_modseq_lookup(ctx->view, ctx->mail_ctx.seq);
244
0
    return modseq >= arg->value.modseq->modseq ? 1 : 0;
245
0
  }
246
0
  default:
247
0
    return -1;
248
0
  }
249
0
}
250
251
static void search_index_arg(struct mail_search_arg *arg,
252
           struct index_search_context *ctx)
253
0
{
254
0
  const struct mail_index_record *rec;
255
256
0
  rec = mail_index_lookup(ctx->view, ctx->mail_ctx.seq);
257
0
  switch (search_arg_match_index(ctx, arg, rec)) {
258
0
  case -1:
259
    /* unknown */
260
0
    break;
261
0
  case 0:
262
0
    ARG_SET_RESULT(arg, 0);
263
0
    break;
264
0
  default:
265
0
    ARG_SET_RESULT(arg, 1);
266
0
    break;
267
0
  }
268
0
}
269
270
/* Returns >0 = matched, 0 = not matched, -1 = unknown */
271
static int search_arg_match_mailbox(struct index_search_context *ctx,
272
            struct mail_search_arg *arg)
273
0
{
274
0
  struct mailbox *box = ctx->cur_mail->box;
275
0
  const char *str;
276
277
0
  switch (arg->type) {
278
0
  case SEARCH_MAILBOX:
279
    /* first try to match the mailbox name itself. this is
280
       important when using "mailbox virtual/foo" parameter foin
281
       doveadm's search query, otherwise we can never fetch
282
       anything with doveadm from virtual mailboxes because the
283
       mailbox parameter is compared to the mail's backend
284
       mailbox. */
285
0
    if (strcmp(box->vname, arg->value.str) == 0)
286
0
      return 1;
287
0
    if (mail_get_special(ctx->cur_mail, MAIL_FETCH_MAILBOX_NAME,
288
0
             &str) < 0) {
289
0
      search_cur_mail_failed(ctx);
290
0
      return -1;
291
0
    }
292
293
0
    if (strcasecmp(str, "INBOX") == 0)
294
0
      return strcasecmp(arg->value.str, "INBOX") == 0 ? 1 : 0;
295
0
    return strcmp(str, arg->value.str) == 0 ? 1 : 0;
296
0
  case SEARCH_MAILBOX_GLOB:
297
0
    if (imap_match(arg->initialized.mailbox_glob, box->vname) == IMAP_MATCH_YES)
298
0
      return 1;
299
0
    if (mail_get_special(ctx->cur_mail, MAIL_FETCH_MAILBOX_NAME,
300
0
             &str) < 0) {
301
0
      search_cur_mail_failed(ctx);
302
0
      return -1;
303
0
    }
304
0
    return imap_match(arg->initialized.mailbox_glob, str) == IMAP_MATCH_YES ? 1 : 0;
305
0
  default:
306
0
    return -1;
307
0
  }
308
0
}
309
310
static void search_mailbox_arg(struct mail_search_arg *arg,
311
             struct index_search_context *ctx)
312
0
{
313
0
  switch (search_arg_match_mailbox(ctx, arg)) {
314
0
  case -1:
315
    /* unknown */
316
0
    break;
317
0
  case 0:
318
0
    ARG_SET_RESULT(arg, 0);
319
0
    break;
320
0
  default:
321
0
    ARG_SET_RESULT(arg, 1);
322
0
    break;
323
0
  }
324
0
}
325
326
/* Returns >0 = matched, 0 = not matched, -1 = unknown */
327
static int search_arg_match_cached(struct index_search_context *ctx,
328
           struct mail_search_arg *arg)
329
0
{
330
0
  const char *str;
331
0
  struct tm *tm;
332
0
  uoff_t virtual_size;
333
0
  time_t date;
334
0
  int tz_offset;
335
0
  bool have_tz_offset;
336
0
  int ret;
337
338
0
  switch (arg->type) {
339
  /* internal dates */
340
0
  case SEARCH_BEFORE:
341
0
  case SEARCH_ON:
342
0
  case SEARCH_SINCE:
343
0
    have_tz_offset = FALSE; tz_offset = 0; date = (time_t)-1;
344
0
    switch (arg->value.date_type) {
345
0
    case MAIL_SEARCH_DATE_TYPE_SENT:
346
0
      if (mail_get_date(ctx->cur_mail, &date, &tz_offset) < 0) {
347
0
        search_cur_mail_failed(ctx);
348
0
        return -1;
349
0
      }
350
0
      have_tz_offset = TRUE;
351
0
      break;
352
0
    case MAIL_SEARCH_DATE_TYPE_RECEIVED:
353
0
      if (mail_get_received_date(ctx->cur_mail, &date) < 0) {
354
0
        search_cur_mail_failed(ctx);
355
0
        return -1;
356
0
      }
357
0
      break;
358
0
    case MAIL_SEARCH_DATE_TYPE_SAVED:
359
0
      if (mail_get_save_date(ctx->cur_mail, &date) < 0) {
360
0
        search_cur_mail_failed(ctx);
361
0
        return -1;
362
0
      }
363
0
      break;
364
0
    }
365
366
0
    if ((arg->value.search_flags &
367
0
         MAIL_SEARCH_ARG_FLAG_UTC_TIMES) == 0) {
368
0
      if (!have_tz_offset) {
369
0
        tm = localtime(&date);
370
0
        tz_offset = utc_offset(tm, date);
371
0
      }
372
0
      date += tz_offset * 60;
373
0
    }
374
375
0
    switch (arg->type) {
376
0
    case SEARCH_BEFORE:
377
0
      return date < arg->value.time ? 1 : 0;
378
0
    case SEARCH_ON:
379
0
      return (date >= arg->value.time &&
380
0
        date < arg->value.time + 3600*24) ? 1 : 0;
381
0
    case SEARCH_SINCE:
382
0
      return date >= arg->value.time ? 1 : 0;
383
0
    default:
384
0
      i_unreached();
385
0
    }
386
387
  /* save date attribute */
388
0
  case SEARCH_SAVEDATESUPPORTED:
389
0
    ret = mail_get_save_date(ctx->cur_mail, &date);
390
0
    if (ret < 0) {
391
0
      search_cur_mail_failed(ctx);
392
0
      return -1;
393
0
    }
394
0
    return ret;
395
396
  /* sizes */
397
0
  case SEARCH_SMALLER:
398
0
  case SEARCH_LARGER:
399
0
    if (mail_get_virtual_size(ctx->cur_mail, &virtual_size) < 0) {
400
0
      search_cur_mail_failed(ctx);
401
0
      return -1;
402
0
    }
403
404
0
    if (arg->type == SEARCH_SMALLER)
405
0
      return virtual_size < arg->value.size ? 1 : 0;
406
0
    else
407
0
      return virtual_size > arg->value.size ? 1 : 0;
408
409
0
  case SEARCH_GUID:
410
0
    if (mail_get_special(ctx->cur_mail, MAIL_FETCH_GUID, &str) < 0) {
411
0
      search_cur_mail_failed(ctx);
412
0
      return -1;
413
0
    }
414
0
    return strcmp(str, arg->value.str) == 0 ? 1 : 0;
415
0
  case SEARCH_REAL_UID: {
416
0
    struct mail *real_mail;
417
418
0
    if (mail_get_backend_mail(ctx->cur_mail, &real_mail) < 0) {
419
0
      search_cur_mail_failed(ctx);
420
0
      return -1;
421
0
    }
422
0
    return seq_range_exists(&arg->value.seqset, real_mail->uid) ? 1 : 0;
423
0
  }
424
0
  default:
425
0
    return -1;
426
0
  }
427
0
}
428
429
static void search_cached_arg(struct mail_search_arg *arg,
430
            struct index_search_context *ctx)
431
0
{
432
0
  switch (search_arg_match_cached(ctx, arg)) {
433
0
  case -1:
434
    /* unknown */
435
0
    break;
436
0
  case 0:
437
0
    ARG_SET_RESULT(arg, 0);
438
0
    break;
439
0
  default:
440
0
    ARG_SET_RESULT(arg, 1);
441
0
    break;
442
0
  }
443
0
}
444
445
static int search_sent(enum mail_search_arg_type type, time_t search_time,
446
           const unsigned char *sent_value, size_t sent_value_len)
447
0
{
448
0
  time_t sent_time;
449
0
  int timezone_offset;
450
451
0
  if (sent_value == NULL)
452
0
    return 0;
453
454
  /* NOTE: RFC-3501 specifies that timezone is ignored
455
     in searches. sent_time is returned as UTC, so change it. */
456
0
  if (!message_date_parse(sent_value, sent_value_len,
457
0
        &sent_time, &timezone_offset))
458
0
    return 0;
459
0
  sent_time += timezone_offset * 60;
460
461
0
  switch (type) {
462
0
  case SEARCH_BEFORE:
463
0
    return sent_time < search_time ? 1 : 0;
464
0
  case SEARCH_ON:
465
0
    return (sent_time >= search_time &&
466
0
      sent_time < search_time + 3600*24) ? 1 : 0;
467
0
  case SEARCH_SINCE:
468
0
    return sent_time >= search_time ? 1 : 0;
469
0
  default:
470
0
                i_unreached();
471
0
  }
472
0
}
473
474
static struct message_search_context *
475
msg_search_arg_context(struct index_search_context *ctx,
476
           struct mail_search_arg *arg)
477
0
{
478
0
  enum message_search_flags flags = 0;
479
480
0
  if (arg->context == NULL) T_BEGIN {
481
0
    string_t *dtc = t_str_new(128);
482
483
0
    if (ctx->mail_ctx.normalizer(arg->value.str,
484
0
               strlen(arg->value.str), dtc) < 0)
485
0
      i_panic("search key not utf8: %s", arg->value.str);
486
487
0
    if (arg->type == SEARCH_BODY)
488
0
      flags |= MESSAGE_SEARCH_FLAG_SKIP_HEADERS;
489
    /* we don't get here if arg is "", but dtc can be "" if it
490
       only contains characters that we need to ignore. handle
491
       those searches by returning them as non-matched. */
492
0
    if (str_len(dtc) > 0) {
493
0
      arg->context =
494
0
        message_search_init(str_c(dtc),
495
0
                ctx->mail_ctx.normalizer,
496
0
                flags);
497
0
    }
498
0
  } T_END;
499
0
  return arg->context;
500
0
}
501
502
static void compress_lwsp(string_t *dest, const unsigned char *src,
503
        size_t src_len)
504
0
{
505
0
  size_t i;
506
0
  bool prev_lwsp = TRUE;
507
508
0
  for (i = 0; i < src_len; i++) {
509
0
    if (IS_LWSP(src[i])) {
510
0
      if (!prev_lwsp) {
511
0
        prev_lwsp = TRUE;
512
0
        str_append_c(dest, ' ');
513
0
      }
514
0
    } else {
515
0
      prev_lwsp = FALSE;
516
0
      str_append_c(dest, src[i]);
517
0
    }
518
0
  }
519
0
}
520
521
static pool_t
522
search_context_temp_pool(struct index_search_context *ctx, size_t size)
523
0
{
524
0
  if (ctx->temp_pool != NULL) {
525
0
    p_clear(ctx->temp_pool);
526
0
    return ctx->temp_pool;
527
0
  }
528
529
0
  if (size <= 1024)
530
0
    return pool_datastack_create();
531
  /* This could just as well be allocated from data stack, but it
532
     makes it more difficult to track bad data_stack_grow events.
533
     Use a temporary memory pool instead. */
534
0
  ctx->temp_pool = pool_alloconly_create("search context temp", size);
535
0
  return ctx->temp_pool;
536
0
}
537
538
static void search_header_arg(struct mail_search_arg *arg,
539
            struct search_header_context *ctx)
540
0
{
541
0
        struct message_search_context *msg_search_ctx;
542
0
  struct message_block block;
543
0
  struct message_header_line hdr;
544
0
  int ret;
545
546
  /* first check that the field name matches to argument. */
547
0
  switch (arg->type) {
548
0
  case SEARCH_BEFORE:
549
0
  case SEARCH_ON:
550
0
  case SEARCH_SINCE:
551
0
    if (arg->value.date_type != MAIL_SEARCH_DATE_TYPE_SENT)
552
0
      return;
553
554
    /* date is handled differently than others */
555
0
    if (strcasecmp(ctx->hdr->name, "Date") == 0) {
556
0
      if (ctx->hdr->continues) {
557
0
        ctx->hdr->use_full_value = TRUE;
558
0
        return;
559
0
      }
560
0
      ret = search_sent(arg->type, arg->value.time,
561
0
            ctx->hdr->full_value,
562
0
            ctx->hdr->full_value_len);
563
0
      ARG_SET_RESULT(arg, ret);
564
0
    }
565
0
    return;
566
567
0
  case SEARCH_HEADER:
568
0
  case SEARCH_HEADER_ADDRESS:
569
0
  case SEARCH_HEADER_COMPRESS_LWSP:
570
0
    ctx->custom_header = TRUE;
571
572
0
    if (strcasecmp(ctx->hdr->name, arg->hdr_field_name) != 0)
573
0
      return;
574
0
    break;
575
0
  default:
576
0
    return;
577
0
  }
578
579
0
  if (arg->value.str[0] == '\0') {
580
    /* we're just testing existence of the field. always matches. */
581
0
    ARG_SET_RESULT(arg, 1);
582
0
    return;
583
0
  }
584
585
0
  if (ctx->hdr->continues) {
586
0
    ctx->hdr->use_full_value = TRUE;
587
0
    return;
588
0
  }
589
590
0
  i_zero(&block);
591
592
  /* We're searching only for values, so drop header name and middle
593
     parts. We use header searching so that MIME words will be decoded. */
594
0
  hdr = *ctx->hdr;
595
0
  hdr.name = ""; hdr.name_len = 0;
596
0
  hdr.middle_len = 0;
597
0
  block.hdr = &hdr;
598
599
0
  msg_search_ctx = msg_search_arg_context(ctx->index_ctx, arg);
600
0
  if (msg_search_ctx == NULL)
601
0
    return;
602
603
0
  if (!ctx->decoded_block_set) { T_BEGIN {
604
0
    struct message_address *addr;
605
0
    string_t *str;
606
607
0
    switch (arg->type) {
608
0
    case SEARCH_HEADER:
609
      /* simple match */
610
0
      break;
611
0
    case SEARCH_HEADER_ADDRESS: {
612
      /* we have to match against normalized address */
613
0
      pool_t pool = search_context_temp_pool(ctx->index_ctx,
614
0
            ctx->hdr->full_value_len);
615
0
      addr = message_address_parse(pool,
616
0
                 ctx->hdr->full_value,
617
0
                 ctx->hdr->full_value_len,
618
0
                 UINT_MAX,
619
0
                 MESSAGE_ADDRESS_PARSE_FLAG_FILL_MISSING);
620
0
      str = str_new(pool, ctx->hdr->value_len);
621
0
      message_address_write(str, addr);
622
0
      hdr.value = hdr.full_value = str_data(str);
623
0
      hdr.value_len = hdr.full_value_len = str_len(str);
624
0
      break;
625
0
    }
626
0
    case SEARCH_HEADER_COMPRESS_LWSP: {
627
      /* convert LWSP to single spaces */
628
0
      pool_t pool = search_context_temp_pool(ctx->index_ctx,
629
0
            hdr.full_value_len);
630
0
      str = str_new(pool, hdr.full_value_len);
631
0
      compress_lwsp(str, hdr.full_value, hdr.full_value_len);
632
0
      hdr.value = hdr.full_value = str_data(str);
633
0
      hdr.value_len = hdr.full_value_len = str_len(str);
634
0
      break;
635
0
    }
636
0
    default:
637
0
      i_unreached();
638
0
    }
639
0
    ret = message_search_more_get_decoded(msg_search_ctx, &block,
640
0
                  &ctx->decoded_block) ? 1 : 0;
641
0
    ctx->decoded_block_set = TRUE;
642
0
  } T_END; } else {
643
    /* this block was already decoded and saved by an earlier
644
       search arg. use the already-decoded block to avoid
645
       duplicating work. */
646
0
    ret = message_search_more_decoded(msg_search_ctx,
647
0
              &ctx->decoded_block) ? 1 : 0;
648
0
  }
649
650
  /* there may be multiple headers. don't mark this failed yet. */
651
0
  if (ret > 0)
652
0
    ARG_SET_RESULT(arg, 1);
653
0
}
654
655
static void search_header_unmatch(struct mail_search_arg *arg,
656
          struct search_header_context *ctx ATTR_UNUSED)
657
0
{
658
0
  switch (arg->type) {
659
0
  case SEARCH_BEFORE:
660
0
  case SEARCH_ON:
661
0
  case SEARCH_SINCE:
662
0
    if (arg->value.date_type != MAIL_SEARCH_DATE_TYPE_SENT)
663
0
      break;
664
665
0
    if (arg->match_not) {
666
      /* date header not found, so we match only for
667
         NOT searches */
668
0
      ARG_SET_RESULT(arg, 0);
669
0
    }
670
0
    break;
671
0
  case SEARCH_HEADER:
672
0
  case SEARCH_HEADER_ADDRESS:
673
0
  case SEARCH_HEADER_COMPRESS_LWSP:
674
0
    ARG_SET_RESULT(arg, 0);
675
0
    break;
676
0
  default:
677
0
    break;
678
0
  }
679
0
}
680
681
static void search_header(struct message_header_line *hdr,
682
        struct search_header_context *ctx)
683
0
{
684
0
  if (ctx->parse_headers)
685
0
    index_mail_parse_header(NULL, hdr, ctx->imail);
686
687
0
  if (hdr == NULL) {
688
    /* end of headers, mark all unknown SEARCH_HEADERs unmatched */
689
0
    (void)mail_search_args_foreach(ctx->args, search_header_unmatch,
690
0
                 ctx);
691
0
    return;
692
0
  }
693
694
0
  if (hdr->eoh)
695
0
    return;
696
697
0
  if (ctx->custom_header || strcasecmp(hdr->name, "Date") == 0) {
698
0
    ctx->hdr = hdr;
699
700
0
    ctx->decoded_block_set = FALSE;
701
0
    ctx->custom_header = FALSE;
702
0
    (void)mail_search_args_foreach(ctx->args, search_header_arg, ctx);
703
0
  }
704
0
}
705
706
static void search_body(struct mail_search_arg *arg,
707
      struct search_body_context *ctx)
708
0
{
709
0
  struct message_search_context *msg_search_ctx;
710
0
  const char *error;
711
0
  int ret;
712
713
0
  switch (arg->type) {
714
0
  case SEARCH_BODY:
715
0
  case SEARCH_TEXT:
716
0
    break;
717
0
  default:
718
0
    return;
719
0
  }
720
721
0
  msg_search_ctx = msg_search_arg_context(ctx->index_ctx, arg);
722
0
  if (msg_search_ctx == NULL) {
723
0
    ARG_SET_RESULT(arg, 0);
724
0
    return;
725
0
  }
726
727
0
  i_stream_seek(ctx->input, 0);
728
0
  ret = message_search_msg(msg_search_ctx, ctx->input, ctx->part, &error);
729
0
  if (ret < 0 && ctx->input->stream_errno == 0) {
730
    /* try again without cached parts */
731
0
    index_mail_set_message_parts_corrupted(ctx->index_ctx->cur_mail, error);
732
733
0
    i_stream_seek(ctx->input, 0);
734
0
    ret = message_search_msg(msg_search_ctx, ctx->input, NULL, &error);
735
0
    i_assert(ret >= 0 || ctx->input->stream_errno != 0);
736
0
  }
737
0
  if (ctx->input->stream_errno != 0) {
738
0
    mailbox_set_critical(ctx->index_ctx->box,
739
0
      "read(%s) failed: %s", i_stream_get_name(ctx->input),
740
0
      i_stream_get_error(ctx->input));
741
0
  }
742
743
0
  ARG_SET_RESULT(arg, ret);
744
0
}
745
746
static int search_arg_match_text(struct mail_search_arg *args,
747
         struct index_search_context *ctx)
748
0
{
749
0
  const enum message_header_parser_flags hdr_parser_flags =
750
0
    MESSAGE_HEADER_PARSER_FLAG_CLEAN_ONELINE;
751
0
  struct index_mail *imail = INDEX_MAIL(ctx->cur_mail);
752
0
  struct mail *real_mail;
753
0
  struct istream *input = NULL;
754
0
  struct mailbox_header_lookup_ctx *headers_ctx;
755
0
  struct search_header_context hdr_ctx;
756
0
  struct search_body_context body_ctx;
757
0
  const char *const *headers;
758
0
  bool have_headers, have_body, failed = FALSE;
759
0
  int ret;
760
761
  /* first check what we need to use */
762
0
  headers = mail_search_args_analyze(args, &have_headers, &have_body);
763
0
  if (!have_headers && !have_body)
764
0
    return -1;
765
766
0
  i_zero(&hdr_ctx);
767
0
  hdr_ctx.index_ctx = ctx;
768
  /* hdr_ctx.imail is different from imail for mails in
769
     virtual mailboxes */
770
0
  if (mail_get_backend_mail(ctx->cur_mail, &real_mail) < 0) {
771
0
    search_cur_mail_failed(ctx);
772
0
    return -1;
773
0
  }
774
0
  hdr_ctx.imail = INDEX_MAIL(real_mail);
775
0
  hdr_ctx.custom_header = TRUE;
776
0
  hdr_ctx.args = args;
777
778
0
  headers_ctx = headers == NULL ? NULL :
779
0
    mailbox_header_lookup_init(ctx->box, headers);
780
0
  if (headers != NULL &&
781
0
      (!have_body ||
782
0
       ctx->cur_mail->lookup_abort != MAIL_LOOKUP_ABORT_NEVER)) {
783
    /* try to look up the specified headers from cache */
784
0
    i_assert(*headers != NULL);
785
786
0
    if (mail_get_header_stream(ctx->cur_mail, headers_ctx,
787
0
             &input) < 0) {
788
0
      search_cur_mail_failed(ctx);
789
0
      failed = TRUE;
790
0
    } else {
791
0
      message_parse_header(input, NULL, hdr_parser_flags,
792
0
               search_header, &hdr_ctx);
793
0
    }
794
0
    input = NULL;
795
0
  } else if (have_headers) {
796
    /* we need to read the entire header */
797
0
    ret = have_body ?
798
0
      mail_get_stream_because(ctx->cur_mail, NULL, NULL, "search", &input) :
799
0
      mail_get_hdr_stream_because(ctx->cur_mail, NULL, "search", &input);
800
0
    if (ret < 0) {
801
0
      search_cur_mail_failed(ctx);
802
0
      failed = TRUE;
803
0
    } else {
804
      /* FIXME: The header parsing here is an optimization to
805
         avoid parsing the header twice: First when checking
806
         whether the search matches, and secondly when
807
         generating wanted fields. However, if we already
808
         know that we want to generate a BODYSTRUCTURE reply,
809
         index_mail_parse_header() must have a non-NULL part
810
         parameter. That's not easily possible at this point
811
         without larger code changes, so for now we'll just
812
         disable this optimization for that case. */
813
0
      hdr_ctx.parse_headers =
814
0
        !hdr_ctx.imail->data.save_bodystructure_header &&
815
0
        index_mail_want_parse_headers(hdr_ctx.imail);
816
0
      if (hdr_ctx.parse_headers) {
817
0
        index_mail_parse_header_init(hdr_ctx.imail,
818
0
                   headers_ctx, TRUE);
819
0
      }
820
0
      message_parse_header(input, NULL, hdr_parser_flags,
821
0
               search_header, &hdr_ctx);
822
0
      if (input->stream_errno != 0) {
823
0
        mailbox_set_critical(ctx->box,
824
0
          "read(%s) failed: %s",
825
0
          i_stream_get_name(input),
826
0
          i_stream_get_error(input));
827
0
        failed = TRUE;
828
0
        search_set_failed(ctx);
829
0
      }
830
0
    }
831
0
  }
832
0
  mailbox_header_lookup_unref(&headers_ctx);
833
834
0
  if (failed) {
835
    /* opening mail failed. maybe because of lookup_abort.
836
       update access_parts for prefetching */
837
0
    if (have_body)
838
0
      imail->data.access_part |= READ_HDR | READ_BODY;
839
0
    else
840
0
      imail->data.access_part |= READ_HDR;
841
0
    return -1;
842
0
  }
843
844
0
  if (have_headers) {
845
    /* see if the header search succeeded in finishing the search */
846
0
    ret = mail_search_args_foreach(args, search_none, NULL);
847
0
    if (ret >= 0 || !have_body)
848
0
      return ret;
849
0
  }
850
851
0
  i_assert(have_body);
852
853
0
  if (ctx->cur_mail->lookup_abort != MAIL_LOOKUP_ABORT_NEVER) {
854
0
    imail->data.access_part |= READ_HDR | READ_BODY;
855
0
    return -1;
856
0
  }
857
858
0
  if (input == NULL) {
859
    /* we didn't search headers. */
860
0
    struct message_size hdr_size;
861
862
0
    if (mail_get_stream_because(ctx->cur_mail, &hdr_size, NULL, "search", &input) < 0) {
863
0
      search_cur_mail_failed(ctx);
864
0
      return -1;
865
0
    }
866
0
    i_stream_seek(input, hdr_size.physical_size);
867
0
  }
868
869
0
  i_zero(&body_ctx);
870
0
  body_ctx.index_ctx = ctx;
871
0
  body_ctx.input = input;
872
  /* Get parts if they already exist in cache. If they don't,
873
     message-search will parse the mail automatically. */
874
0
  ctx->cur_mail->lookup_abort = MAIL_LOOKUP_ABORT_NOT_IN_CACHE;
875
0
  (void)mail_get_parts(ctx->cur_mail, &body_ctx.part);
876
0
  ctx->cur_mail->lookup_abort = MAIL_LOOKUP_ABORT_NEVER;
877
878
0
  return mail_search_args_foreach(args, search_body, &body_ctx);
879
0
}
880
881
static bool
882
search_msgset_fix_limits(unsigned int messages_count,
883
       ARRAY_TYPE(seq_range) *seqset, bool match_not)
884
0
{
885
0
  struct seq_range *range;
886
0
  unsigned int count;
887
888
0
  i_assert(messages_count > 0);
889
890
0
  range = array_get_modifiable(seqset, &count);
891
0
  if (count > 0) {
892
0
    i_assert(range[0].seq1 != 0);
893
0
    if (range[count-1].seq2 == (uint32_t)-1) {
894
      /* "*" used, make sure the last message is in the range
895
         (e.g. with count+1:* we still want to include it) */
896
0
      seq_range_array_add(seqset, messages_count);
897
0
    }
898
    /* remove all nonexistent messages */
899
0
    seq_range_array_remove_range(seqset, messages_count + 1,
900
0
               (uint32_t)-1);
901
0
  }
902
0
  if (!match_not)
903
0
    return array_count(seqset) > 0;
904
0
  else {
905
    /* if all messages are in the range, it can't match */
906
0
    range = array_get_modifiable(seqset, &count);
907
0
    return count == 0 || range[0].seq1 != 1 ||
908
0
      range[count-1].seq2 != messages_count;
909
0
  }
910
0
}
911
912
static void
913
search_msgset_fix(unsigned int messages_count,
914
      ARRAY_TYPE(seq_range) *seqset,
915
      uint32_t *seq1_r, uint32_t *seq2_r, bool match_not)
916
0
{
917
0
  const struct seq_range *range;
918
0
  unsigned int count;
919
0
  uint32_t min_seq, max_seq;
920
921
0
  if (!search_msgset_fix_limits(messages_count, seqset, match_not)) {
922
0
    *seq1_r = (uint32_t)-1;
923
0
    *seq2_r = 0;
924
0
    return;
925
0
  }
926
927
0
  range = array_get(seqset, &count);
928
0
  if (!match_not) {
929
0
    min_seq = range[0].seq1;
930
0
    max_seq = range[count-1].seq2;
931
0
  } else if (count == 0) {
932
    /* matches all messages */
933
0
    min_seq = 1;
934
0
    max_seq = messages_count;
935
0
  } else {
936
0
    min_seq = range[0].seq1 > 1 ? 1 : range[0].seq2 + 1;
937
0
    max_seq = range[count-1].seq2 < messages_count ?
938
0
      messages_count : range[count-1].seq1 - 1;
939
0
    if (min_seq > max_seq) {
940
0
      *seq1_r = (uint32_t)-1;
941
0
      *seq2_r = 0;
942
0
      return;
943
0
    }
944
0
  }
945
946
0
  if (*seq1_r < min_seq || *seq1_r == 0)
947
0
    *seq1_r = min_seq;
948
0
  if (*seq2_r > max_seq)
949
0
    *seq2_r = max_seq;
950
0
}
951
952
static void search_or_parse_msgset_args(unsigned int messages_count,
953
          struct mail_search_arg *args,
954
          uint32_t *seq1_r, uint32_t *seq2_r)
955
0
{
956
0
  uint32_t seq1, seq2, min_seq1 = 0, max_seq2 = 0;
957
958
0
  for (; args != NULL; args = args->next) {
959
0
    seq1 = 1; seq2 = messages_count;
960
961
0
    switch (args->type) {
962
0
    case SEARCH_SUB:
963
0
      i_assert(!args->match_not);
964
0
      search_parse_msgset_args(messages_count,
965
0
             args->value.subargs,
966
0
             &seq1, &seq2);
967
0
      break;
968
0
    case SEARCH_OR:
969
0
      i_assert(!args->match_not);
970
0
      search_or_parse_msgset_args(messages_count,
971
0
                args->value.subargs,
972
0
                &seq1, &seq2);
973
0
      break;
974
0
    case SEARCH_SEQSET:
975
0
      search_msgset_fix(messages_count, &args->value.seqset,
976
0
            &seq1, &seq2, args->match_not);
977
0
      break;
978
0
    default:
979
0
      break;
980
0
    }
981
982
0
    if (min_seq1 == 0) {
983
0
      min_seq1 = seq1;
984
0
      max_seq2 = seq2;
985
0
    } else {
986
0
      if (seq1 < min_seq1)
987
0
        min_seq1 = seq1;
988
0
      if (seq2 > max_seq2)
989
0
        max_seq2 = seq2;
990
0
    }
991
0
  }
992
0
  i_assert(min_seq1 != 0);
993
994
0
  if (min_seq1 > *seq1_r)
995
0
    *seq1_r = min_seq1;
996
0
  if (max_seq2 < *seq2_r)
997
0
    *seq2_r = max_seq2;
998
0
}
999
1000
static void search_parse_msgset_args(unsigned int messages_count,
1001
             struct mail_search_arg *args,
1002
             uint32_t *seq1_r, uint32_t *seq2_r)
1003
0
{
1004
0
  for (; args != NULL; args = args->next) {
1005
0
    switch (args->type) {
1006
0
    case SEARCH_SUB:
1007
0
      i_assert(!args->match_not);
1008
0
      search_parse_msgset_args(messages_count,
1009
0
             args->value.subargs,
1010
0
             seq1_r, seq2_r);
1011
0
      break;
1012
0
    case SEARCH_OR:
1013
      /* go through our children and use the widest seqset
1014
         range */
1015
0
      i_assert(!args->match_not);
1016
0
      search_or_parse_msgset_args(messages_count,
1017
0
                args->value.subargs,
1018
0
                seq1_r, seq2_r);
1019
0
      break;
1020
0
    case SEARCH_SEQSET:
1021
0
      search_msgset_fix(messages_count, &args->value.seqset,
1022
0
            seq1_r, seq2_r, args->match_not);
1023
0
      break;
1024
0
    default:
1025
0
      break;
1026
0
    }
1027
0
  }
1028
0
}
1029
1030
static void search_limit_lowwater(struct index_search_context *ctx,
1031
          uint32_t uid_lowwater, uint32_t *first_seq)
1032
0
{
1033
0
  uint32_t seq1, seq2;
1034
1035
0
  if (uid_lowwater == 0)
1036
0
    return;
1037
1038
0
  (void)mail_index_lookup_seq_range(ctx->view, uid_lowwater, (uint32_t)-1,
1039
0
            &seq1, &seq2);
1040
0
  if (*first_seq < seq1)
1041
0
    *first_seq = seq1;
1042
0
}
1043
1044
/* Returns TRUE if the view's header counters (e.g.
1045
   deleted_messages_count) may be older than the flags that searching
1046
   actually matches. */
1047
static bool search_view_have_stale_hdr_counters(struct mail_index_view *view)
1048
0
{
1049
0
  const struct mail_index_modseq_header *view_modseq_hdr, *head_modseq_hdr;
1050
1051
0
  if (view->map == view->index->map)
1052
0
    return FALSE;
1053
1054
  /* The view's map is older than the index's head map. If their
1055
     highest_modseqs are equal, there are no flag, keyword, append or
1056
     expunge changes between the maps, so the view's header counters
1057
     are still valid. Compare the maps' raw modseq headers -
1058
     mail_index_map_modseq_get_highest() could fall back to returning
1059
     the log head's modseq for both maps, hiding their difference. */
1060
0
  view_modseq_hdr = mail_index_map_get_modseq_header(view->map);
1061
0
  head_modseq_hdr = mail_index_map_get_modseq_header(view->index->map);
1062
0
  return view_modseq_hdr == NULL || head_modseq_hdr == NULL ||
1063
0
    view_modseq_hdr->highest_modseq == 0 ||
1064
0
    view_modseq_hdr->highest_modseq != head_modseq_hdr->highest_modseq;
1065
0
}
1066
1067
static bool search_limit_by_hdr(struct index_search_context *ctx,
1068
        struct mail_search_arg *args,
1069
        uint32_t *seq1, uint32_t *seq2)
1070
0
{
1071
0
  const struct mail_index_header *hdr;
1072
0
  enum mail_flags pvt_flags_mask;
1073
0
  uint64_t highest_modseq;
1074
1075
0
  if (search_view_have_stale_hdr_counters(ctx->view)) {
1076
    /* Flag matching uses the latest flags from the index's head
1077
       map (see view_lookup_full()), but the view's header still
1078
       has the counters from the last sync. For example with a
1079
       pipelined UID STORE +FLAGS \Deleted followed by UID EXPUNGE
1080
       the view's header could still have deleted_messages_count=0,
1081
       even though the messages are already marked as \Deleted in
1082
       the head map. Don't do any optimizations based on the stale
1083
       header counters in that case. */
1084
0
    return *seq1 <= *seq2;
1085
0
  }
1086
1087
0
  hdr = mail_index_get_header(ctx->view);
1088
  /* we can't trust that private view's header is fully up to date,
1089
     so do this optimization only for non-private flags */
1090
0
  pvt_flags_mask = ctx->box->view_pvt == NULL ? 0 :
1091
0
    mailbox_get_private_flags_mask(ctx->box);
1092
1093
0
  for (; args != NULL; args = args->next) {
1094
0
    switch (args->type) {
1095
0
    case SEARCH_ALL:
1096
0
      if (args->match_not) {
1097
        /* NOT ALL - pointless noop query */
1098
0
        return FALSE;
1099
0
      }
1100
0
      continue;
1101
0
    case SEARCH_MODSEQ:
1102
      /* MODSEQ higher than current HIGHESTMODSEQ? */
1103
0
      highest_modseq = mail_index_modseq_get_highest(ctx->view);
1104
0
      if (args->value.modseq->modseq > highest_modseq)
1105
0
        return FALSE;
1106
0
      continue;
1107
0
    default:
1108
0
      continue;
1109
0
    case SEARCH_FLAGS:
1110
0
      break;
1111
0
    }
1112
0
    if ((args->value.flags & MAIL_SEEN) != 0 &&
1113
0
        (pvt_flags_mask & MAIL_SEEN) == 0) {
1114
      /* SEEN with 0 seen? */
1115
0
      if (!args->match_not && hdr->seen_messages_count == 0)
1116
0
        return FALSE;
1117
1118
0
      if (hdr->seen_messages_count == hdr->messages_count) {
1119
        /* UNSEEN with all seen? */
1120
0
        if (args->match_not)
1121
0
          return FALSE;
1122
0
      } else if (args->match_not) {
1123
        /* UNSEEN with lowwater limiting */
1124
0
        search_limit_lowwater(ctx,
1125
0
                                  hdr->first_unseen_uid_lowwater, seq1);
1126
0
      }
1127
0
    }
1128
0
    if ((args->value.flags & MAIL_DELETED) != 0 &&
1129
0
        (pvt_flags_mask & MAIL_DELETED) == 0) {
1130
      /* DELETED with 0 deleted? */
1131
0
      if (!args->match_not &&
1132
0
          hdr->deleted_messages_count == 0)
1133
0
        return FALSE;
1134
1135
0
      if (hdr->deleted_messages_count == hdr->messages_count) {
1136
        /* UNDELETED with all deleted? */
1137
0
        if (args->match_not)
1138
0
          return FALSE;
1139
0
      } else if (!args->match_not) {
1140
        /* DELETED with lowwater limiting */
1141
0
        search_limit_lowwater(ctx,
1142
0
                                  hdr->first_deleted_uid_lowwater, seq1);
1143
0
      }
1144
0
    }
1145
0
  }
1146
1147
0
  return *seq1 <= *seq2;
1148
0
}
1149
1150
static void search_get_seqset(struct index_search_context *ctx,
1151
            unsigned int messages_count,
1152
            struct mail_search_arg *args)
1153
0
{
1154
0
  if (messages_count == 0) {
1155
    /* no messages, don't check sequence ranges. although we could
1156
       give error message then for FETCH, we shouldn't do it for
1157
       UID FETCH. */
1158
0
    ctx->seq1 = 1;
1159
0
    ctx->seq2 = 0;
1160
0
    return;
1161
0
  }
1162
1163
0
  ctx->seq1 = 1;
1164
0
  ctx->seq2 = messages_count;
1165
1166
0
  search_parse_msgset_args(messages_count, args, &ctx->seq1, &ctx->seq2);
1167
0
  if (ctx->seq1 == 0) {
1168
0
    ctx->seq1 = 1;
1169
0
    ctx->seq2 = messages_count;
1170
0
  }
1171
0
  if (ctx->seq1 > ctx->seq2) {
1172
    /* no matches */
1173
0
    return;
1174
0
  }
1175
1176
  /* See if this search query can never match based on data in index's
1177
     header. We'll scan only the root level args, which is usually
1178
     enough. */
1179
0
  if (!search_limit_by_hdr(ctx, args, &ctx->seq1, &ctx->seq2)) {
1180
    /* no matches */
1181
0
    ctx->seq1 = 1;
1182
0
    ctx->seq2 = 0;
1183
0
  }
1184
0
}
1185
1186
static int search_build_subthread(struct mail_thread_iterate_context *iter,
1187
          ARRAY_TYPE(seq_range) *uids)
1188
0
{
1189
0
  struct mail_thread_iterate_context *child_iter;
1190
0
  const struct mail_thread_child_node *node;
1191
0
  int ret = 0;
1192
1193
0
  while ((node = mail_thread_iterate_next(iter, &child_iter)) != NULL) {
1194
0
    if (child_iter != NULL) {
1195
0
      if (search_build_subthread(child_iter, uids) < 0)
1196
0
        ret = -1;
1197
0
    }
1198
0
    seq_range_array_add(uids, node->uid);
1199
0
  }
1200
0
  if (mail_thread_iterate_deinit(&iter) < 0)
1201
0
    ret = -1;
1202
0
  return ret;
1203
0
}
1204
1205
static int search_build_inthread_result(struct index_search_context *ctx,
1206
          struct mail_search_arg *arg)
1207
0
{
1208
0
  struct mail_thread_iterate_context *iter, *child_iter;
1209
0
  const struct mail_thread_child_node *node;
1210
0
  const ARRAY_TYPE(seq_range) *search_uids;
1211
0
  ARRAY_TYPE(seq_range) thread_uids;
1212
0
  int ret = 0;
1213
1214
  /* mail_search_args_init() must have been called by now */
1215
0
  i_assert(arg->initialized.search_args != NULL);
1216
1217
0
  p_array_init(&arg->value.seqset, ctx->mail_ctx.args->pool, 64);
1218
0
  if (mailbox_search_result_build(ctx->mail_ctx.transaction,
1219
0
          arg->initialized.search_args,
1220
0
          MAILBOX_SEARCH_RESULT_FLAG_UPDATE |
1221
0
          MAILBOX_SEARCH_RESULT_FLAG_QUEUE_SYNC,
1222
0
          &arg->value.search_result) < 0)
1223
0
    return -1;
1224
0
  if (ctx->thread_ctx == NULL) {
1225
    /* failed earlier */
1226
0
    return -1;
1227
0
  }
1228
1229
0
  search_uids = mailbox_search_result_get(arg->value.search_result);
1230
0
  if (array_count(search_uids) == 0) {
1231
    /* search found nothing - no threads can match */
1232
0
    return 0;
1233
0
  }
1234
1235
0
  t_array_init(&thread_uids, 128);
1236
0
  iter = mail_thread_iterate_init(ctx->thread_ctx,
1237
0
          arg->value.thread_type, FALSE);
1238
0
  while ((node = mail_thread_iterate_next(iter, &child_iter)) != NULL) {
1239
0
    seq_range_array_add(&thread_uids, node->uid);
1240
0
    if (child_iter != NULL) {
1241
0
      if (search_build_subthread(child_iter,
1242
0
               &thread_uids) < 0)
1243
0
        ret = -1;
1244
0
    }
1245
0
    if (seq_range_array_have_common(&thread_uids, search_uids)) {
1246
      /* yes, we want this thread */
1247
0
      seq_range_array_merge(&arg->value.seqset, &thread_uids);
1248
0
    }
1249
0
    array_clear(&thread_uids);
1250
0
  }
1251
0
  if (mail_thread_iterate_deinit(&iter) < 0)
1252
0
    ret = -1;
1253
0
  return ret;
1254
0
}
1255
1256
static int search_build_inthreads(struct index_search_context *ctx,
1257
          struct mail_search_arg *arg)
1258
0
{
1259
0
  int ret = 0;
1260
1261
0
  for (; arg != NULL; arg = arg->next) {
1262
0
    switch (arg->type) {
1263
0
    case SEARCH_OR:
1264
0
    case SEARCH_SUB:
1265
0
      if (search_build_inthreads(ctx, arg->value.subargs) < 0)
1266
0
        ret = -1;
1267
0
      break;
1268
0
    case SEARCH_INTHREAD:
1269
      /* The INTHREAD's search key is searched separately
1270
         using the same thread context, so build the nested
1271
         INTHREADs' results first. */
1272
0
      if (search_build_inthreads(ctx, arg->value.subargs) < 0)
1273
0
        ret = -1;
1274
0
      if (search_build_inthread_result(ctx, arg) < 0)
1275
0
        ret = -1;
1276
0
      break;
1277
0
    default:
1278
0
      break;
1279
0
    }
1280
0
  }
1281
0
  return ret;
1282
0
}
1283
1284
static void
1285
wanted_sort_fields_get(struct mailbox *box,
1286
           const enum mail_sort_type *sort_program,
1287
           struct mailbox_header_lookup_ctx *wanted_headers,
1288
           enum mail_fetch_field *wanted_fields_r,
1289
           struct mailbox_header_lookup_ctx **headers_ctx_r)
1290
0
{
1291
0
  ARRAY_TYPE(const_string) headers;
1292
0
  const char *header;
1293
0
  unsigned int i;
1294
1295
0
  *wanted_fields_r = 0;
1296
0
  *headers_ctx_r = NULL;
1297
1298
0
  t_array_init(&headers, 8);
1299
0
  for (i = 0; sort_program[i] != MAIL_SORT_END; i++) {
1300
0
    header = NULL;
1301
1302
0
    switch (sort_program[i] & MAIL_SORT_MASK) {
1303
0
    case MAIL_SORT_ARRIVAL:
1304
0
      *wanted_fields_r |= MAIL_FETCH_RECEIVED_DATE;
1305
0
      break;
1306
0
    case MAIL_SORT_CC:
1307
0
      header = "Cc";
1308
0
      break;
1309
0
    case MAIL_SORT_DATE:
1310
0
      *wanted_fields_r |= MAIL_FETCH_DATE;
1311
0
      break;
1312
0
    case MAIL_SORT_FROM:
1313
0
      header = "From";
1314
0
      break;
1315
0
    case MAIL_SORT_SIZE:
1316
0
      *wanted_fields_r |= MAIL_FETCH_VIRTUAL_SIZE;
1317
0
      break;
1318
0
    case MAIL_SORT_SUBJECT:
1319
0
      header = "Subject";
1320
0
      break;
1321
0
    case MAIL_SORT_TO:
1322
0
      header = "To";
1323
0
      break;
1324
0
    }
1325
0
    if (header != NULL)
1326
0
      array_push_back(&headers, &header);
1327
0
  }
1328
1329
0
  if (wanted_headers != NULL) {
1330
0
    for (i = 0; wanted_headers->name[i] != NULL; i++)
1331
0
      array_push_back(&headers, &wanted_headers->name[i]);
1332
0
  }
1333
1334
0
  if (array_count(&headers) > 0) {
1335
0
    array_append_zero(&headers);
1336
0
    *headers_ctx_r = mailbox_header_lookup_init(box,
1337
0
              array_front(&headers));
1338
0
  }
1339
0
}
1340
1341
struct mail_search_context *
1342
index_storage_search_init(struct mailbox_transaction_context *t,
1343
        struct mail_search_args *args,
1344
        const enum mail_sort_type *sort_program,
1345
        enum mail_fetch_field wanted_fields,
1346
        struct mailbox_header_lookup_ctx *wanted_headers)
1347
0
{
1348
0
  struct index_search_context *ctx;
1349
0
  struct mailbox_status status;
1350
1351
0
  ctx = i_new(struct index_search_context, 1);
1352
0
  ctx->mail_ctx.transaction = t;
1353
0
  ctx->mail_ctx.normalizer = t->box->storage->user->default_normalizer;
1354
0
  ctx->box = t->box;
1355
0
  ctx->view = t->view;
1356
0
  ctx->mail_ctx.args = args;
1357
0
  ctx->mail_ctx.sort_program = index_sort_program_init(t, sort_program);
1358
1359
0
  ctx->mail_ctx.max_mails = t->box->storage->set->mail_prefetch_count + 1;
1360
0
  if (ctx->mail_ctx.max_mails == 0)
1361
0
    ctx->mail_ctx.max_mails = UINT_MAX;
1362
0
  ctx->next_time_check_cost = SEARCH_INITIAL_MAX_COST;
1363
0
  i_gettimeofday(&ctx->last_nonblock_timeval);
1364
1365
0
  mailbox_get_open_status(t->box, STATUS_MESSAGES, &status);
1366
0
  ctx->mail_ctx.progress_max = status.messages;
1367
1368
0
  i_array_init(&ctx->mail_ctx.results, 5);
1369
0
  array_create(&ctx->mail_ctx.module_contexts, default_pool,
1370
0
         sizeof(void *), 5);
1371
0
  i_array_init(&ctx->mail_ctx.mails, ctx->mail_ctx.max_mails);
1372
1373
0
  mail_search_args_reset(ctx->mail_ctx.args->args, TRUE);
1374
0
  if (args->have_inthreads) {
1375
0
    if (mail_thread_init(t->box, NULL, &ctx->thread_ctx) < 0)
1376
0
      search_set_failed(ctx);
1377
0
    if (search_build_inthreads(ctx, args->args) < 0)
1378
0
      search_set_failed(ctx);
1379
0
  }
1380
1381
0
  if (sort_program != NULL) {
1382
0
    wanted_sort_fields_get(ctx->box, sort_program, wanted_headers,
1383
0
               &ctx->mail_ctx.wanted_fields,
1384
0
               &ctx->mail_ctx.wanted_headers);
1385
0
  } else if (wanted_headers != NULL) {
1386
0
    ctx->mail_ctx.wanted_headers = wanted_headers;
1387
0
    mailbox_header_lookup_ref(wanted_headers);
1388
0
  }
1389
0
  ctx->mail_ctx.wanted_fields |= wanted_fields;
1390
1391
0
  search_get_seqset(ctx, status.messages, args->args);
1392
0
  (void)mail_search_args_foreach(args->args, search_init_arg, ctx);
1393
1394
  /* Need to reset results for match_always cases */
1395
0
  mail_search_args_reset(ctx->mail_ctx.args->args, FALSE);
1396
0
  return &ctx->mail_ctx;
1397
0
}
1398
1399
static void ATTR_NULL(2)
1400
search_arg_deinit(struct mail_search_arg *arg,
1401
      struct index_search_context *ctx)
1402
0
{
1403
0
  switch (arg->type) {
1404
0
  case SEARCH_MIMEPART:
1405
0
    index_search_mime_arg_deinit(arg, ctx);
1406
0
    break;
1407
0
  default:
1408
0
    if (arg->context != NULL) {
1409
0
      struct message_search_context *search_ctx = arg->context;
1410
0
      message_search_deinit(&search_ctx);
1411
0
      arg->context = NULL;
1412
0
    }
1413
0
  }
1414
0
}
1415
1416
int index_storage_search_deinit(struct mail_search_context *_ctx)
1417
0
{
1418
0
        struct index_search_context *ctx = (struct index_search_context *)_ctx;
1419
0
  struct mail *mail;
1420
0
  int ret;
1421
1422
0
  ret = ctx->failed ? -1 : 0;
1423
1424
0
  mail_search_args_reset(ctx->mail_ctx.args->args, FALSE);
1425
0
  (void)mail_search_args_foreach(ctx->mail_ctx.args->args,
1426
0
               search_arg_deinit, ctx);
1427
1428
0
  mailbox_header_lookup_unref(&ctx->mail_ctx.wanted_headers);
1429
0
  if (ctx->mail_ctx.sort_program != NULL) {
1430
0
    if (index_sort_program_deinit(&ctx->mail_ctx.sort_program) < 0)
1431
0
      ret = -1;
1432
0
  }
1433
0
  if (ctx->thread_ctx != NULL)
1434
0
    mail_thread_deinit(&ctx->thread_ctx);
1435
0
  array_free(&ctx->mail_ctx.results);
1436
0
  array_free(&ctx->mail_ctx.module_contexts);
1437
1438
0
  array_foreach_elem(&ctx->mail_ctx.mails, mail) {
1439
0
    struct index_mail *imail = INDEX_MAIL(mail);
1440
1441
0
    imail->mail.search_mail = FALSE;
1442
0
    mail_free(&mail);
1443
0
  }
1444
1445
0
  if (ctx->failed)
1446
0
    mail_storage_last_error_pop(ctx->box->storage);
1447
0
  array_free(&ctx->mail_ctx.mails);
1448
0
  pool_unref(&ctx->temp_pool);
1449
0
  i_free(ctx);
1450
0
  return ret;
1451
0
}
1452
1453
static unsigned long long
1454
search_get_cost(struct mailbox_transaction_context *trans)
1455
0
{
1456
0
  return trans->stats.open_lookup_count * SEARCH_COST_DENTRY +
1457
0
    trans->stats.stat_lookup_count * SEARCH_COST_DENTRY +
1458
0
    trans->stats.fstat_lookup_count * SEARCH_COST_ATTR +
1459
0
    trans->stats.cache_hit_count * SEARCH_COST_CACHE +
1460
0
    trans->stats.files_read_count * SEARCH_COST_FILES_READ +
1461
0
    (trans->stats.files_read_bytes/1024) * SEARCH_COST_KBYTE;
1462
0
}
1463
1464
static int search_match_once(struct index_search_context *ctx)
1465
0
{
1466
0
  int ret;
1467
1468
0
  ret = mail_search_args_foreach(ctx->mail_ctx.args->args,
1469
0
               search_cached_arg, ctx);
1470
0
  if (ret < 0)
1471
0
    ret = search_arg_match_text(ctx->mail_ctx.args->args, ctx);
1472
0
  if (ret < 0)
1473
0
    ret = index_search_mime_arg_match(ctx->mail_ctx.args->args, ctx);
1474
0
  return ret;
1475
0
}
1476
1477
static bool search_arg_is_static(struct mail_search_arg *arg)
1478
0
{
1479
0
  struct mail_search_arg *subarg;
1480
1481
0
  switch (arg->type) {
1482
0
  case SEARCH_OR:
1483
0
  case SEARCH_SUB:
1484
    /* they're static only if all subargs are static */
1485
0
    subarg = arg->value.subargs;
1486
0
    for (; subarg != NULL; subarg = subarg->next) {
1487
0
      if (!search_arg_is_static(subarg))
1488
0
        return FALSE;
1489
0
    }
1490
0
    return TRUE;
1491
0
  case SEARCH_SEQSET:
1492
    /* changes between syncs, but we can't really handle this
1493
       currently. seqsets should be converted to uidsets first. */
1494
0
  case SEARCH_FLAGS:
1495
0
  case SEARCH_KEYWORDS:
1496
0
  case SEARCH_MODSEQ:
1497
0
  case SEARCH_INTHREAD:
1498
0
    break;
1499
0
  case SEARCH_ALL:
1500
0
  case SEARCH_UIDSET:
1501
0
  case SEARCH_BEFORE:
1502
0
  case SEARCH_ON:
1503
0
  case SEARCH_SINCE:
1504
0
  case SEARCH_SMALLER:
1505
0
  case SEARCH_LARGER:
1506
0
  case SEARCH_HEADER:
1507
0
  case SEARCH_HEADER_ADDRESS:
1508
0
  case SEARCH_HEADER_COMPRESS_LWSP:
1509
0
  case SEARCH_BODY:
1510
0
  case SEARCH_TEXT:
1511
0
  case SEARCH_SAVEDATESUPPORTED:
1512
0
  case SEARCH_GUID:
1513
0
  case SEARCH_MAILBOX:
1514
0
  case SEARCH_MAILBOX_GUID:
1515
0
  case SEARCH_MAILBOX_GLOB:
1516
0
  case SEARCH_REAL_UID:
1517
0
  case SEARCH_MIMEPART:
1518
0
    return TRUE;
1519
0
  }
1520
0
  return FALSE;
1521
0
}
1522
1523
static void search_set_static_matches(struct mail_search_arg *arg)
1524
0
{
1525
0
  for (; arg != NULL; arg = arg->next) {
1526
0
    if (search_arg_is_static(arg))
1527
0
      arg->result = 1;
1528
0
  }
1529
0
}
1530
1531
static bool search_has_static_nonmatches(struct mail_search_arg *arg)
1532
0
{
1533
0
  for (; arg != NULL; arg = arg->next) {
1534
0
    if (arg->result == 0 && search_arg_is_static(arg))
1535
0
      return TRUE;
1536
0
  }
1537
0
  return FALSE;
1538
0
}
1539
1540
static void search_match_finish(struct index_search_context *ctx, int match)
1541
0
{
1542
0
  if (match == 0 &&
1543
0
      search_has_static_nonmatches(ctx->mail_ctx.args->args)) {
1544
    /* if there are saved search results remember
1545
       that this message never matches */
1546
0
    mailbox_search_results_never(&ctx->mail_ctx,
1547
0
               ctx->cur_mail->uid);
1548
0
  }
1549
0
}
1550
1551
static int search_match_next(struct index_search_context *ctx)
1552
0
{
1553
0
  static enum mail_lookup_abort cache_lookups[] = {
1554
0
    MAIL_LOOKUP_ABORT_NOT_IN_CACHE,
1555
0
    MAIL_LOOKUP_ABORT_READ_MAIL,
1556
0
    MAIL_LOOKUP_ABORT_NEVER
1557
0
  };
1558
0
  unsigned int i, n = N_ELEMENTS(cache_lookups);
1559
0
  int ret = -1;
1560
1561
0
  if (ctx->have_mailbox_args) {
1562
    /* check that the mailbox name matches.
1563
       this makes sense only with virtual mailboxes. */
1564
0
    ret = mail_search_args_foreach(ctx->mail_ctx.args->args,
1565
0
                 search_mailbox_arg, ctx);
1566
0
  }
1567
1568
  /* avoid doing extra work for as long as possible */
1569
0
  if (ctx->mail_ctx.max_mails > 1) {
1570
    /* we're doing prefetching. if we have to read the mail,
1571
       do a prefetch first and the final search later */
1572
0
    n--;
1573
0
  }
1574
1575
0
  i_assert(ctx->cur_mail->lookup_abort == MAIL_LOOKUP_ABORT_NEVER);
1576
0
  for (i = 0; i < n && ret < 0; i++) {
1577
0
    ctx->cur_mail->lookup_abort = cache_lookups[i];
1578
0
    T_BEGIN {
1579
0
      ret = search_match_once(ctx);
1580
0
    } T_END;
1581
0
  }
1582
0
  ctx->cur_mail->lookup_abort = MAIL_LOOKUP_ABORT_NEVER;
1583
0
  search_match_finish(ctx, ret);
1584
0
  return ret;
1585
0
}
1586
1587
static bool search_would_block(struct index_search_context *ctx)
1588
0
{
1589
0
  struct timeval now;
1590
0
  unsigned long long guess_cost;
1591
0
  long long usecs;
1592
0
  bool ret;
1593
1594
0
  if (ctx->cost < ctx->next_time_check_cost)
1595
0
    return FALSE;
1596
1597
0
  i_gettimeofday(&now);
1598
1599
0
  usecs = timeval_diff_usecs(&now, &ctx->last_nonblock_timeval);
1600
0
  if (usecs < 0) {
1601
    /* clock moved backwards. */
1602
0
    ctx->last_nonblock_timeval = now;
1603
0
    ctx->next_time_check_cost = SEARCH_INITIAL_MAX_COST;
1604
0
    return TRUE;
1605
0
  } else if (usecs < SEARCH_MIN_NONBLOCK_USECS) {
1606
    /* not finished yet. estimate the next time lookup */
1607
0
    ret = FALSE;
1608
0
  } else {
1609
    /* done, or close enough anyway */
1610
0
    ctx->last_nonblock_timeval = now;
1611
0
    ret = TRUE;
1612
0
  }
1613
0
  guess_cost = ctx->cost *
1614
0
    (SEARCH_MAX_NONBLOCK_USECS / (double)usecs);
1615
0
  if (usecs < SEARCH_RECALC_MIN_USECS) {
1616
    /* the estimate may not be very good since we spent
1617
       so little time doing this search. don't allow huge changes
1618
       to the guess, but allow anyway large enough so that we can
1619
       move to right direction. */
1620
0
    if (guess_cost > ctx->next_time_check_cost*3)
1621
0
      guess_cost = ctx->next_time_check_cost*3;
1622
0
    else if (guess_cost < ctx->next_time_check_cost/3)
1623
0
      guess_cost = ctx->next_time_check_cost/3;
1624
0
  }
1625
0
  if (ret)
1626
0
    ctx->cost = 0;
1627
0
  ctx->next_time_check_cost = guess_cost;
1628
0
  return ret;
1629
0
}
1630
1631
int index_storage_search_next_match_mail(struct mail_search_context *_ctx,
1632
           struct mail *mail)
1633
0
{
1634
0
  struct index_search_context *ctx =
1635
0
    container_of(_ctx, struct index_search_context, mail_ctx);
1636
0
  struct index_mail *imail = INDEX_MAIL(mail);
1637
0
  int match;
1638
1639
0
  ctx->cur_mail = mail;
1640
  /* mail's access_type is SEARCH only while using it to process
1641
     the search query. afterwards the mail can still be accessed
1642
     for fetching. */
1643
0
  ctx->cur_mail->access_type = MAIL_ACCESS_TYPE_SEARCH;
1644
0
  T_BEGIN {
1645
0
    match = search_match_next(ctx);
1646
0
  } T_END;
1647
0
  ctx->cur_mail->access_type = MAIL_ACCESS_TYPE_DEFAULT;
1648
0
  ctx->cur_mail = NULL;
1649
1650
0
  i_assert(imail->data.search_results == NULL);
1651
0
  if (match < 0) {
1652
    /* result isn't known yet, do a prefetch and
1653
       finish later */
1654
0
    imail->data.search_results =
1655
0
      buffer_create_dynamic(imail->mail.data_pool, 64);
1656
0
    mail_search_args_result_serialize(_ctx->args,
1657
0
              imail->data.search_results);
1658
0
  }
1659
1660
0
  mail_search_args_reset(_ctx->args->args, FALSE);
1661
1662
0
  if (match != 0) {
1663
    /* either matched or result is still unknown.
1664
       anyway we're far enough now that we probably want
1665
       to update the access_parts. the only problem here is
1666
       if searching would want fewer access_parts than the
1667
       fetching part, but that's probably not a big problem
1668
       usually. */
1669
0
    index_mail_update_access_parts_pre(mail);
1670
0
    return 1;
1671
0
  }
1672
1673
  /* non-match */
1674
0
  if (_ctx->args->stop_on_nonmatch)
1675
0
    return -1;
1676
0
  return 0;
1677
0
}
1678
1679
static int search_more_with_mail(struct index_search_context *ctx,
1680
         struct mail *mail)
1681
0
{
1682
0
  struct mail_search_context *_ctx = &ctx->mail_ctx;
1683
0
  struct mailbox *box = _ctx->transaction->box;
1684
0
  unsigned long long cost1, cost2;
1685
0
  int ret;
1686
1687
0
  if (search_would_block(ctx)) {
1688
    /* this lookup is useful when a large number of
1689
       messages match */
1690
0
    return 0;
1691
0
  }
1692
1693
0
  mail_search_args_reset(_ctx->args->args, FALSE);
1694
1695
0
  cost1 = search_get_cost(mail->transaction);
1696
0
  ret = -1;
1697
0
  for (;;) {
1698
0
    bool more;
1699
0
    T_BEGIN {
1700
0
      more = box->v.search_next_update_seq(_ctx);
1701
0
    } T_END;
1702
0
    if (!more)
1703
0
      break;
1704
0
    mail_set_seq(mail, _ctx->seq);
1705
1706
0
    T_BEGIN {
1707
0
      ret = box->v.search_next_match_mail(_ctx, mail);
1708
0
    } T_END;
1709
0
    if (ret != 0)
1710
0
      break;
1711
1712
0
    cost2 = search_get_cost(mail->transaction);
1713
0
    ctx->cost += cost2 - cost1;
1714
0
    cost1 = cost2;
1715
1716
0
    if (search_would_block(ctx))
1717
0
      break;
1718
0
    ret = -1;
1719
0
  }
1720
0
  cost2 = search_get_cost(mail->transaction);
1721
0
  ctx->cost += cost2 - cost1;
1722
0
  return ret;
1723
0
}
1724
1725
struct mail *index_search_get_mail(struct index_search_context *ctx)
1726
0
{
1727
0
  struct index_mail *imail;
1728
0
  struct mail *const *mails, *mail;
1729
0
  unsigned int count;
1730
1731
0
  if (ctx->mail_ctx.unused_mail_idx == ctx->mail_ctx.max_mails)
1732
0
    return NULL;
1733
1734
0
  mails = array_get(&ctx->mail_ctx.mails, &count);
1735
0
  if (ctx->mail_ctx.unused_mail_idx < count)
1736
0
    return mails[ctx->mail_ctx.unused_mail_idx];
1737
1738
0
  mail = mail_alloc(ctx->mail_ctx.transaction,
1739
0
        ctx->mail_ctx.wanted_fields,
1740
0
        ctx->mail_ctx.wanted_headers);
1741
0
  imail = INDEX_MAIL(mail);
1742
0
  imail->mail.search_mail = TRUE;
1743
0
  ctx->mail_ctx.transaction->stats_track = TRUE;
1744
1745
0
  array_push_back(&ctx->mail_ctx.mails, &mail);
1746
0
  return mail;
1747
0
}
1748
1749
static int search_more_with_prefetching(struct index_search_context *ctx,
1750
          struct mail **mail_r)
1751
0
{
1752
0
  struct mail *mail, *const *mails;
1753
0
  unsigned int count;
1754
0
  int ret = 0;
1755
1756
0
  while ((mail = index_search_get_mail(ctx)) != NULL) {
1757
0
    T_BEGIN {
1758
0
      ret = search_more_with_mail(ctx, mail);
1759
0
    } T_END;
1760
0
    if (ret <= 0)
1761
0
      break;
1762
1763
0
    if (ctx->mail_ctx.sort_program != NULL) {
1764
      /* don't prefetch when using a sort program,
1765
         since the mails' access order will change */
1766
0
      i_assert(ctx->mail_ctx.unused_mail_idx == 0);
1767
0
      *mail_r = mail;
1768
0
      return 1;
1769
0
    }
1770
0
    if (mail_prefetch(mail) && ctx->mail_ctx.unused_mail_idx == 0) {
1771
      /* no prefetching done, return it immediately */
1772
0
      *mail_r = mail;
1773
0
      return 1;
1774
0
    }
1775
0
    ctx->mail_ctx.unused_mail_idx++;
1776
0
  }
1777
1778
0
  if (mail != NULL) {
1779
0
    if (ret == 0) {
1780
      /* wait */
1781
0
      return 0;
1782
0
    }
1783
0
    i_assert(ret < 0);
1784
0
    if (ctx->mail_ctx.unused_mail_idx == 0) {
1785
      /* finished */
1786
0
      return -1;
1787
0
    }
1788
0
  } else {
1789
    /* prefetch buffer is full. */
1790
0
  }
1791
1792
  /* return the next message */
1793
0
  i_assert(ctx->mail_ctx.unused_mail_idx > 0);
1794
1795
0
  mails = array_get(&ctx->mail_ctx.mails, &count);
1796
0
  *mail_r = mails[0];
1797
0
  if (--ctx->mail_ctx.unused_mail_idx > 0) {
1798
0
    array_pop_front(&ctx->mail_ctx.mails);
1799
0
    array_push_back(&ctx->mail_ctx.mails, mail_r);
1800
0
  }
1801
0
  T_BEGIN {
1802
0
    index_mail_update_access_parts_post(*mail_r);
1803
0
  } T_END;
1804
0
  return 1;
1805
0
}
1806
1807
static bool search_finish_prefetch(struct index_search_context *ctx,
1808
           struct index_mail *imail)
1809
0
{
1810
0
  int ret;
1811
1812
0
  i_assert(imail->mail.mail.lookup_abort == MAIL_LOOKUP_ABORT_NEVER);
1813
1814
0
  ctx->cur_mail = &imail->mail.mail;
1815
0
  ctx->cur_mail->access_type = MAIL_ACCESS_TYPE_SEARCH;
1816
0
  mail_search_args_result_deserialize(ctx->mail_ctx.args,
1817
0
              imail->data.search_results->data,
1818
0
              imail->data.search_results->used);
1819
0
  T_BEGIN {
1820
0
    ret = search_match_once(ctx);
1821
0
    search_match_finish(ctx, ret);
1822
0
  } T_END;
1823
0
  ctx->cur_mail->access_type = MAIL_ACCESS_TYPE_DEFAULT;
1824
0
  ctx->cur_mail = NULL;
1825
0
  return ret > 0;
1826
0
}
1827
1828
static int search_more(struct index_search_context *ctx,
1829
           struct mail **mail_r)
1830
0
{
1831
0
  struct index_mail *imail;
1832
0
  int ret;
1833
1834
0
  while ((ret = search_more_with_prefetching(ctx, mail_r)) > 0) {
1835
0
    imail = INDEX_MAIL(*mail_r);
1836
0
    if (imail->data.search_results == NULL)
1837
0
      break;
1838
1839
    /* prefetch running - searching wasn't finished yet */
1840
0
    if (search_finish_prefetch(ctx, imail))
1841
0
      break;
1842
    /* search finished as non-match */
1843
0
    if (ctx->mail_ctx.args->stop_on_nonmatch) {
1844
0
      ret = -1;
1845
0
      break;
1846
0
    }
1847
0
    mailbox_search_notify(ctx->box, &ctx->mail_ctx);
1848
0
  }
1849
0
  return ret;
1850
0
}
1851
1852
static bool
1853
index_storage_search_is_interrupted(struct index_search_context *ctx)
1854
0
{
1855
0
  if (!master_service_is_killed(master_service))
1856
0
    return FALSE;
1857
1858
0
  struct timeval tv_now;
1859
0
  i_gettimeofday(&tv_now);
1860
1861
0
  if (ctx->interrupt_start_time.tv_sec == 0) {
1862
0
    master_service_get_kill_time(master_service,
1863
0
               &ctx->interrupt_start_time);
1864
0
    i_assert(ctx->interrupt_start_time.tv_sec > 0);
1865
0
  } else if (timeval_diff_msecs(&tv_now, &ctx->interrupt_start_time) >=
1866
0
     SEARCH_INTERRUPT_DELAY_MSECS) {
1867
0
    mail_storage_set_error(ctx->box->storage,
1868
0
               MAIL_ERROR_INTERRUPTED,
1869
0
               MAIL_ERRSTR_INTERRUPTED);
1870
0
    search_set_failed(ctx);
1871
0
    return TRUE;
1872
0
  }
1873
0
  return FALSE;
1874
0
}
1875
1876
bool index_storage_search_next_nonblock(struct mail_search_context *_ctx,
1877
          struct mail **mail_r, bool *tryagain_r)
1878
0
{
1879
0
        struct index_search_context *ctx = (struct index_search_context *)_ctx;
1880
0
  struct mail *mail, *const *mailp;
1881
0
  uint32_t seq;
1882
0
  int ret;
1883
1884
0
  *tryagain_r = FALSE;
1885
1886
0
  if (index_storage_search_is_interrupted(ctx))
1887
0
    return FALSE;
1888
1889
0
  if (_ctx->sort_program == NULL) {
1890
0
    ret = search_more(ctx, &mail);
1891
0
    if (ret == 0) {
1892
0
      *tryagain_r = TRUE;
1893
0
      return FALSE;
1894
0
    }
1895
0
    if (ret < 0)
1896
0
      return FALSE;
1897
0
    *mail_r = mail;
1898
0
    return TRUE;
1899
0
  }
1900
1901
0
  if (!ctx->sorted) {
1902
0
    while ((ret = search_more(ctx, &mail)) > 0) T_BEGIN {
1903
0
      index_sort_list_add(_ctx->sort_program, mail);
1904
0
    } T_END;
1905
1906
0
    if (ret == 0) {
1907
0
      *tryagain_r = TRUE;
1908
0
      return FALSE;
1909
0
    }
1910
    /* finished searching the messages. now sort them and start
1911
       returning the messages. */
1912
0
    ctx->sorted = TRUE;
1913
0
    index_sort_list_finish(_ctx->sort_program);
1914
0
  }
1915
1916
  /* everything searched at this point already. just returning
1917
     matches from sort list. FIXME: we could do prefetching here also. */
1918
0
  if (!index_sort_list_next(_ctx->sort_program, &seq))
1919
0
    return FALSE;
1920
1921
0
  mailp = array_front(&ctx->mail_ctx.mails);
1922
0
  mail_set_seq(*mailp, seq);
1923
0
  index_mail_update_access_parts_pre(*mailp);
1924
0
  index_mail_update_access_parts_post(*mailp);
1925
0
  *mail_r = *mailp;
1926
0
  return TRUE;
1927
0
}
1928
1929
bool index_storage_search_match_index_args(struct mail_search_context *_ctx)
1930
0
{
1931
0
  struct index_search_context *ctx =
1932
0
    container_of(_ctx, struct index_search_context, mail_ctx);
1933
0
  int ret;
1934
1935
  /* Match the args that can be looked up from the index for the
1936
     already set _ctx->seq. Args that need the mail itself are left
1937
     with an unknown result for the caller to handle. */
1938
0
  ret = mail_search_args_foreach(_ctx->args->args, search_seqset_arg, ctx);
1939
0
  if (ret != 0) {
1940
0
    ret = mail_search_args_foreach(_ctx->args->args,
1941
0
                 search_index_arg, ctx);
1942
0
  }
1943
0
  return ret != 0;
1944
0
}
1945
1946
bool index_storage_search_next_update_seq(struct mail_search_context *_ctx)
1947
0
{
1948
0
        struct index_search_context *ctx = (struct index_search_context *)_ctx;
1949
0
  uint32_t uid;
1950
0
  int ret;
1951
1952
0
  if (_ctx->seq == 0) {
1953
    /* first time */
1954
0
    _ctx->seq = ctx->seq1;
1955
0
  } else {
1956
0
    _ctx->seq++;
1957
0
  }
1958
1959
0
  if (!ctx->have_seqsets && !ctx->have_index_args &&
1960
0
      !ctx->have_nonmatch_always && _ctx->update_result == NULL) {
1961
0
    _ctx->progress_cur = _ctx->seq;
1962
0
    return _ctx->seq <= ctx->seq2;
1963
0
  }
1964
1965
0
  ret = 0;
1966
0
  while (_ctx->seq <= ctx->seq2) {
1967
    /* check if the sequence matches */
1968
0
    ret = mail_search_args_foreach(ctx->mail_ctx.args->args,
1969
0
                 search_seqset_arg, ctx);
1970
0
    if (ret != 0 && ctx->have_index_args) {
1971
      /* check if flags/keywords match before anything else
1972
         is done. mail_set_seq() can be a bit slow. */
1973
0
      ret = mail_search_args_foreach(ctx->mail_ctx.args->args,
1974
0
                   search_index_arg, ctx);
1975
0
    }
1976
0
    if (ret != 0 && _ctx->update_result != NULL) {
1977
      /* see if this message never matches */
1978
0
      mail_index_lookup_uid(ctx->view, _ctx->seq, &uid);
1979
0
      if (seq_range_exists(&_ctx->update_result->never_uids,
1980
0
               uid))
1981
0
        ret = 0;
1982
0
    }
1983
0
    if (ret != 0)
1984
0
      break;
1985
1986
    /* doesn't, try next one */
1987
0
    _ctx->seq++;
1988
0
    mail_search_args_reset(ctx->mail_ctx.args->args, FALSE);
1989
0
  }
1990
1991
0
  if (ret != 0 && _ctx->update_result != NULL) {
1992
0
    mail_index_lookup_uid(ctx->view, _ctx->seq, &uid);
1993
0
    if (seq_range_exists(&_ctx->update_result->uids, uid)) {
1994
      /* we already know that the static data
1995
         matches. mark it as such. */
1996
0
      search_set_static_matches(_ctx->args->args);
1997
0
    }
1998
0
  }
1999
0
  ctx->mail_ctx.progress_cur = _ctx->seq;
2000
0
  return ret != 0;
2001
0
}