395
374
static struct message_search_context *
396
msg_search_arg_context(struct index_search_context *ctx,
397
struct mail_search_arg *arg)
375
msg_search_arg_context(struct mail_search_arg *arg)
399
struct message_search_context *arg_ctx = arg->context;
400
enum message_search_flags flags;
406
flags = (arg->type == SEARCH_BODY || arg->type == SEARCH_BODY_FAST) ?
407
MESSAGE_SEARCH_FLAG_SKIP_HEADERS : 0;
409
ret = message_search_init(arg->value.str,
410
ctx->mail_ctx.args->charset, flags,
413
arg->context = arg_ctx;
417
ctx->error = TXT_UNKNOWN_CHARSET;
419
ctx->error = TXT_INVALID_SEARCH_KEY;
377
enum message_search_flags flags = MESSAGE_SEARCH_FLAG_DTCASE;
379
if (arg->context == NULL) T_BEGIN {
380
string_t *dtc = t_str_new(128);
382
if (uni_utf8_to_decomposed_titlecase(arg->value.str,
383
strlen(arg->value.str),
385
i_panic("search key not utf8: %s", arg->value.str);
387
if (arg->type == SEARCH_BODY)
388
flags |= MESSAGE_SEARCH_FLAG_SKIP_HEADERS;
389
/* we don't get here if arg is "", but dtc can be "" if it
390
only contains characters that we need to ignore. handle
391
those searches by returning them as non-matched. */
392
if (str_len(dtc) > 0)
393
arg->context = message_search_init(str_c(dtc), flags);
423
398
static void compress_lwsp(string_t *dest, const unsigned char *src,
633
603
static int search_arg_match_text(struct mail_search_arg *args,
634
struct index_search_context *ctx, int ret)
604
struct index_search_context *ctx)
636
struct istream *input;
606
const enum message_header_parser_flags hdr_parser_flags =
607
MESSAGE_HEADER_PARSER_FLAG_CLEAN_ONELINE;
608
struct index_mail *imail = (struct index_mail *)ctx->cur_mail;
609
struct istream *input = NULL;
637
610
struct mailbox_header_lookup_ctx *headers_ctx;
638
struct mail_search_arg *arg;
611
struct search_header_context hdr_ctx;
612
struct search_body_context body_ctx;
639
613
const char *const *headers;
640
bool have_headers, have_body;
614
bool have_headers, have_body, failed = FALSE;
642
617
/* first check what we need to use */
643
618
headers = mail_search_args_analyze(args, &have_headers, &have_body);
644
619
if (!have_headers && !have_body)
622
memset(&hdr_ctx, 0, sizeof(hdr_ctx));
623
/* hdr_ctx.imail is different from imail for mails in
625
hdr_ctx.imail = (struct index_mail *)mail_get_real_mail(ctx->cur_mail);
626
hdr_ctx.custom_header = TRUE;
629
headers_ctx = headers == NULL ? NULL :
630
mailbox_header_lookup_init(ctx->box, headers);
631
if (headers != NULL &&
633
ctx->cur_mail->lookup_abort == MAIL_LOOKUP_ABORT_NEVER)) {
634
/* try to look up the specified headers from cache */
635
i_assert(*headers != NULL);
637
if (mail_get_header_stream(ctx->cur_mail, headers_ctx,
641
message_parse_header(input, NULL, hdr_parser_flags,
642
search_header, &hdr_ctx);
645
} else if (have_headers) {
646
/* we need to read the entire header */
647
if (mail_get_hdr_stream(ctx->cur_mail, NULL, &input) < 0)
650
hdr_ctx.parse_headers =
651
index_mail_want_parse_headers(hdr_ctx.imail);
652
if (hdr_ctx.parse_headers) {
653
index_mail_parse_header_init(hdr_ctx.imail,
656
message_parse_header(input, NULL, hdr_parser_flags,
657
search_header, &hdr_ctx);
660
if (headers_ctx != NULL)
661
mailbox_header_lookup_unref(&headers_ctx);
664
/* opening mail failed. maybe because of lookup_abort.
665
update access_parts for prefetching */
667
imail->data.access_part |= READ_HDR | READ_BODY;
669
imail->data.access_part |= READ_HDR;
647
673
if (have_headers) {
648
struct search_header_context hdr_ctx;
651
ctx->mail->lookup_abort == MAIL_LOOKUP_ABORT_NEVER) {
652
/* just open the mail bypassing any caching, since
653
we're going to read through the body anyway */
657
if (headers == NULL) {
659
if (mail_get_stream(ctx->mail, NULL, NULL, &input) < 0)
662
/* FIXME: do this once in init */
663
i_assert(*headers != NULL);
665
mailbox_header_lookup_init(ctx->box, headers);
666
if (mail_get_header_stream(ctx->mail, headers_ctx,
668
mailbox_header_lookup_unref(&headers_ctx);
673
memset(&hdr_ctx, 0, sizeof(hdr_ctx));
674
hdr_ctx.index_context = ctx;
675
hdr_ctx.custom_header = TRUE;
677
hdr_ctx.parse_headers = headers == NULL &&
678
index_mail_want_parse_headers(ctx->imail);
680
if (hdr_ctx.parse_headers)
681
index_mail_parse_header_init(ctx->imail, headers_ctx);
682
message_parse_header(input, NULL, hdr_parser_flags,
683
search_header, &hdr_ctx);
684
if (headers_ctx != NULL)
685
mailbox_header_lookup_unref(&headers_ctx);
674
/* see if the header search succeeded in finishing the search */
675
ret = mail_search_args_foreach(args, search_none, NULL);
676
if (ret >= 0 || !have_body)
682
if (ctx->cur_mail->lookup_abort != MAIL_LOOKUP_ABORT_NEVER) {
683
imail->data.access_part |= READ_HDR | READ_BODY;
688
/* we didn't search headers. */
687
689
struct message_size hdr_size;
689
if (mail_get_stream(ctx->mail, &hdr_size, NULL, &input) < 0)
691
if (mail_get_stream(ctx->cur_mail, &hdr_size, NULL, &input) < 0)
692
693
i_stream_seek(input, hdr_size.physical_size);
696
struct search_body_context body_ctx;
698
if (ctx->mail->lookup_abort != MAIL_LOOKUP_ABORT_NEVER)
701
memset(&body_ctx, 0, sizeof(body_ctx));
702
body_ctx.index_ctx = ctx;
703
body_ctx.input = input;
704
(void)mail_get_parts(ctx->mail, &body_ctx.part);
706
ret = mail_search_args_foreach(args, search_body, &body_ctx);
708
/* see if we have a decision */
710
arg = ctx->mail_ctx.args->args;
711
for (; arg != NULL; arg = arg->next) {
712
if (arg->result == 0) {
696
memset(&body_ctx, 0, sizeof(body_ctx));
697
body_ctx.index_ctx = ctx;
698
body_ctx.input = input;
699
(void)mail_get_parts(ctx->cur_mail, &body_ctx.part);
701
return mail_search_args_foreach(args, search_body, &body_ctx);
723
static bool search_msgset_fix_limits(unsigned int messages_count,
724
ARRAY_TYPE(seq_range) *seqset, bool not)
705
search_msgset_fix_limits(unsigned int messages_count,
706
ARRAY_TYPE(seq_range) *seqset, bool match_not)
726
708
struct seq_range *range;
727
709
unsigned int count;
1053
wanted_sort_fields_get(struct mailbox *box,
1054
const enum mail_sort_type *sort_program,
1055
struct mailbox_header_lookup_ctx *wanted_headers,
1056
enum mail_fetch_field *wanted_fields_r,
1057
struct mailbox_header_lookup_ctx **headers_ctx_r)
1059
ARRAY_TYPE(const_string) headers;
1063
*wanted_fields_r = 0;
1064
*headers_ctx_r = NULL;
1066
t_array_init(&headers, 8);
1067
for (i = 0; sort_program[i] != MAIL_SORT_END; i++) {
1070
switch (sort_program[i] & MAIL_SORT_MASK) {
1071
case MAIL_SORT_ARRIVAL:
1072
*wanted_fields_r |= MAIL_FETCH_RECEIVED_DATE;
1077
case MAIL_SORT_DATE:
1078
*wanted_fields_r |= MAIL_FETCH_DATE;
1080
case MAIL_SORT_FROM:
1083
case MAIL_SORT_SIZE:
1084
*wanted_fields_r |= MAIL_FETCH_VIRTUAL_SIZE;
1086
case MAIL_SORT_SUBJECT:
1094
array_append(&headers, &header, 1);
1097
if (wanted_headers != NULL) {
1098
for (i = 0; wanted_headers->name[i] != NULL; i++)
1099
array_append(&headers, &wanted_headers->name[i], 1);
1102
if (array_count(&headers) > 0) {
1103
(void)array_append_space(&headers);
1104
*headers_ctx_r = mailbox_header_lookup_init(box,
1105
array_idx(&headers, 0));
1068
1109
struct mail_search_context *
1069
1110
index_storage_search_init(struct mailbox_transaction_context *t,
1070
1111
struct mail_search_args *args,
1071
const enum mail_sort_type *sort_program)
1112
const enum mail_sort_type *sort_program,
1113
enum mail_fetch_field wanted_fields,
1114
struct mailbox_header_lookup_ctx *wanted_headers)
1073
1116
struct index_search_context *ctx;
1074
1117
struct mailbox_status status;
1120
1178
int index_storage_search_deinit(struct mail_search_context *_ctx)
1122
1180
struct index_search_context *ctx = (struct index_search_context *)_ctx;
1181
struct mail **mailp;
1125
ret = ctx->failed || ctx->error != NULL ? -1 : 0;
1127
if (ctx->error != NULL) {
1128
mail_storage_set_error(ctx->box->storage,
1129
MAIL_ERROR_PARAMS, ctx->error);
1184
ret = ctx->failed ? -1 : 0;
1132
1186
mail_search_args_reset(ctx->mail_ctx.args->args, FALSE);
1133
1187
(void)mail_search_args_foreach(ctx->mail_ctx.args->args,
1134
1188
search_arg_deinit, NULL);
1190
if (ctx->mail_ctx.wanted_headers != NULL)
1191
mailbox_header_lookup_unref(&ctx->mail_ctx.wanted_headers);
1136
1192
if (ctx->mail_ctx.sort_program != NULL)
1137
1193
index_sort_program_deinit(&ctx->mail_ctx.sort_program);
1138
1194
if (ctx->thread_ctx != NULL)
1139
1195
mail_thread_deinit(&ctx->thread_ctx);
1140
1196
array_free(&ctx->mail_ctx.results);
1141
1197
array_free(&ctx->mail_ctx.module_contexts);
1199
array_foreach_modifiable(&ctx->mails, mailp) {
1200
struct index_mail *imail = (struct index_mail *)*mailp;
1202
imail->search_mail = FALSE;
1205
array_free(&ctx->mails);
1146
static bool search_match_next(struct index_search_context *ctx)
1210
static unsigned long long
1211
search_get_cost(struct mailbox_transaction_context *trans)
1213
return trans->stats.open_lookup_count * SEARCH_COST_DENTRY +
1214
trans->stats.stat_lookup_count * SEARCH_COST_DENTRY +
1215
trans->stats.fstat_lookup_count * SEARCH_COST_ATTR +
1216
trans->stats.cache_hit_count * SEARCH_COST_CACHE +
1217
trans->stats.files_read_count * SEARCH_COST_FILES_READ +
1218
(trans->stats.files_read_bytes/1024) * SEARCH_COST_KBYTE;
1221
static int search_match_once(struct index_search_context *ctx)
1225
ret = mail_search_args_foreach(ctx->mail_ctx.args->args,
1226
search_cached_arg, ctx);
1228
ret = search_arg_match_text(ctx->mail_ctx.args->args, ctx);
1232
static bool search_arg_is_static(struct mail_search_arg *arg)
1234
struct mail_search_arg *subarg;
1236
switch (arg->type) {
1239
/* they're static only if all subargs are static */
1240
subarg = arg->value.subargs;
1241
for (; subarg != NULL; subarg = subarg->next) {
1242
if (!search_arg_is_static(subarg))
1247
/* changes between syncs, but we can't really handle this
1248
currently. seqsets should be converted to uidsets first. */
1250
case SEARCH_KEYWORDS:
1252
case SEARCH_INTHREAD:
1259
case SEARCH_SMALLER:
1262
case SEARCH_HEADER_ADDRESS:
1263
case SEARCH_HEADER_COMPRESS_LWSP:
1267
case SEARCH_MAILBOX:
1268
case SEARCH_MAILBOX_GUID:
1269
case SEARCH_MAILBOX_GLOB:
1275
static void search_set_static_matches(struct mail_search_arg *arg)
1277
for (; arg != NULL; arg = arg->next) {
1278
if (search_arg_is_static(arg))
1283
static bool search_has_static_nonmatches(struct mail_search_arg *arg)
1285
for (; arg != NULL; arg = arg->next) {
1286
if (arg->result == 0 && search_arg_is_static(arg))
1292
static void search_match_finish(struct index_search_context *ctx, int match)
1294
if (ctx->cur_mail->expunged)
1295
ctx->mail_ctx.seen_lost_data = TRUE;
1298
search_has_static_nonmatches(ctx->mail_ctx.args->args)) {
1299
/* if there are saved search results remember
1300
that this message never matches */
1301
mailbox_search_results_never(&ctx->mail_ctx,
1302
ctx->cur_mail->uid);
1306
static int search_match_next(struct index_search_context *ctx)
1148
1308
static enum mail_lookup_abort cache_lookups[] = {
1149
1309
MAIL_LOOKUP_ABORT_NOT_IN_CACHE,
1150
1310
MAIL_LOOKUP_ABORT_READ_MAIL,
1151
1311
MAIL_LOOKUP_ABORT_NEVER
1313
unsigned int i, n = N_ELEMENTS(cache_lookups);
1156
1316
if (ctx->have_mailbox_args) {
1317
/* check that the mailbox name matches.
1318
this makes sense only with virtual mailboxes. */
1157
1319
ret = mail_search_args_foreach(ctx->mail_ctx.args->args,
1158
1320
search_mailbox_arg, ctx);
1163
/* try to avoid doing extra work for as long as possible */
1164
for (i = 0; i < N_ELEMENTS(cache_lookups) && ret < 0; i++) {
1165
ctx->mail->lookup_abort = cache_lookups[i];
1166
ret = mail_search_args_foreach(ctx->mail_ctx.args->args,
1167
search_cached_arg, ctx);
1171
ret = search_arg_match_text(ctx->mail_ctx.args->args, ctx, ret);
1175
ctx->mail->lookup_abort = MAIL_LOOKUP_ABORT_NEVER;
1323
/* avoid doing extra work for as long as possible */
1324
if (ctx->max_mails > 1) {
1325
/* we're doing prefetching. if we have to read the mail,
1326
do a prefetch first and the final search later */
1329
for (i = 0; i < n && ret < 0; i++) {
1330
ctx->cur_mail->lookup_abort = cache_lookups[i];
1331
ret = search_match_once(ctx);
1333
ctx->cur_mail->lookup_abort = MAIL_LOOKUP_ABORT_NEVER;
1334
search_match_finish(ctx, ret);
1179
1338
static void index_storage_search_notify(struct mailbox *box,
1208
1367
ctx->last_notify = ioloop_timeval;
1211
static bool search_arg_is_static(struct mail_search_arg *arg)
1213
struct mail_search_arg *subarg;
1215
switch (arg->type) {
1218
/* they're static only if all subargs are static */
1219
subarg = arg->value.subargs;
1220
for (; subarg != NULL; subarg = subarg->next) {
1221
if (!search_arg_is_static(subarg))
1226
/* changes between syncs, but we can't really handle this
1227
currently. seqsets should be converted to uidsets first. */
1229
case SEARCH_KEYWORDS:
1231
case SEARCH_INTHREAD:
1238
case SEARCH_SMALLER:
1241
case SEARCH_HEADER_ADDRESS:
1242
case SEARCH_HEADER_COMPRESS_LWSP:
1245
case SEARCH_BODY_FAST:
1246
case SEARCH_TEXT_FAST:
1248
case SEARCH_MAILBOX:
1249
case SEARCH_MAILBOX_GUID:
1250
case SEARCH_MAILBOX_GLOB:
1256
static void search_set_static_matches(struct mail_search_arg *arg)
1258
for (; arg != NULL; arg = arg->next) {
1259
if (search_arg_is_static(arg))
1264
static bool search_has_static_nonmatches(struct mail_search_arg *arg)
1266
for (; arg != NULL; arg = arg->next) {
1267
if (arg->result == 0 && search_arg_is_static(arg))
1273
static unsigned long long search_mail_get_cost(struct mail_private *mail)
1275
return mail->stats_open_lookup_count * SEARCH_COST_DENTRY +
1276
mail->stats_stat_lookup_count * SEARCH_COST_DENTRY +
1277
mail->stats_fstat_lookup_count * SEARCH_COST_ATTR +
1278
mail->stats_cache_hit_count * SEARCH_COST_CACHE +
1279
mail->stats_files_read_count * SEARCH_COST_FILES_READ +
1280
(mail->stats_files_read_bytes/1024) * SEARCH_COST_KBYTE;
1283
1370
static bool search_would_block(struct index_search_context *ctx)
1285
1372
struct timeval now;
1328
bool index_storage_search_next_nonblock(struct mail_search_context *_ctx,
1329
struct mail *mail, bool *tryagain_r)
1415
static int search_more_with_mail(struct index_search_context *ctx,
1331
struct index_search_context *ctx = (struct index_search_context *)_ctx;
1418
struct mail_search_context *_ctx = &ctx->mail_ctx;
1332
1419
struct mailbox *box = _ctx->transaction->box;
1333
struct mail_private *mail_private = (struct mail_private *)mail;
1420
struct index_mail *imail = (struct index_mail *)mail;
1334
1421
unsigned long long cost1, cost2;
1335
bool old_stats_track, match = FALSE;
1337
*tryagain_r = FALSE;
1340
/* everything searched at this point already. just returning
1341
matches from sort list */
1342
if (!index_sort_list_next(ctx->mail_ctx.sort_program, mail))
1347
1424
if (search_would_block(ctx)) {
1348
1425
/* this lookup is useful when a large number of
1349
1426
messages match */
1356
1430
if (ioloop_time - ctx->last_notify.tv_sec >=
1357
1431
SEARCH_NOTIFY_INTERVAL_SECS)
1358
1432
index_storage_search_notify(box, ctx);
1360
old_stats_track = mail_private->stats_track;
1361
mail_private->stats_track = TRUE;
1362
cost1 = search_mail_get_cost(mail_private);
1434
mail_search_args_reset(_ctx->args->args, FALSE);
1436
cost1 = search_get_cost(mail->transaction);
1363
1438
while (box->v.search_next_update_seq(_ctx)) {
1364
1439
mail_set_seq(mail, _ctx->seq);
1365
ctx->imail = (struct index_mail *)mail_get_real_mail(mail);
1441
ctx->cur_mail = mail;
1368
1443
match = search_match_next(ctx);
1370
if (ctx->mail->expunged)
1371
_ctx->seen_lost_data = TRUE;
1374
search_has_static_nonmatches(_ctx->args->args)) {
1375
/* if there are saved search results remember
1376
that this message never matches */
1377
mailbox_search_results_never(_ctx, mail->uid);
1380
cost2 = search_mail_get_cost(mail_private);
1445
ctx->cur_mail = NULL;
1447
i_assert(imail->data.search_results == NULL);
1449
/* result isn't known yet, do a prefetch and
1451
imail->data.search_results =
1452
buffer_create_dynamic(imail->data_pool, 64);
1453
mail_search_args_result_serialize(_ctx->args,
1454
imail->data.search_results);
1457
mail_search_args_reset(_ctx->args->args, FALSE);
1464
cost2 = search_get_cost(mail->transaction);
1381
1465
ctx->cost += cost2 - cost1;
1384
mail_search_args_reset(_ctx->args->args, FALSE);
1386
if (ctx->error != NULL)
1389
if (_ctx->sort_program == NULL)
1468
if (search_would_block(ctx)) {
1473
cost2 = search_get_cost(mail->transaction);
1474
ctx->cost += cost2 - cost1;
1478
struct mail *index_search_get_mail(struct index_search_context *ctx)
1480
struct index_mail *imail;
1481
struct mail *const *mails, *mail;
1484
if (ctx->unused_mail_idx == ctx->max_mails)
1487
mails = array_get(&ctx->mails, &count);
1488
if (ctx->unused_mail_idx < count)
1489
return mails[ctx->unused_mail_idx];
1491
mail = mail_alloc(ctx->mail_ctx.transaction,
1492
ctx->mail_ctx.wanted_fields,
1493
ctx->mail_ctx.wanted_headers);
1494
imail = (struct index_mail *)mail;
1495
imail->search_mail = TRUE;
1496
ctx->mail_ctx.transaction->stats_track = TRUE;
1498
array_append(&ctx->mails, &mail, 1);
1502
static int search_more_with_prefetching(struct index_search_context *ctx,
1503
struct mail **mail_r)
1505
struct mail *mail, *const *mails;
1509
while ((mail = index_search_get_mail(ctx)) != NULL) {
1510
ret = search_more_with_mail(ctx, mail);
1514
if (ctx->mail_ctx.sort_program != NULL) {
1515
/* don't prefetch when using a sort program,
1516
since the mails' access order will change */
1517
i_assert(ctx->unused_mail_idx == 0);
1521
if (mail_prefetch(mail) && ctx->unused_mail_idx == 0) {
1522
/* no prefetching done, return it immediately */
1526
ctx->unused_mail_idx++;
1535
if (ctx->unused_mail_idx == 0) {
1540
/* prefetch buffer is full. */
1543
/* return the next message */
1544
i_assert(ctx->unused_mail_idx > 0);
1546
mails = array_get(&ctx->mails, &count);
1548
if (--ctx->unused_mail_idx > 0) {
1549
array_delete(&ctx->mails, 0, 1);
1550
array_append(&ctx->mails, mail_r, 1);
1555
static bool search_finish_prefetch(struct index_search_context *ctx,
1556
struct index_mail *imail)
1560
i_assert(imail->mail.mail.lookup_abort == MAIL_LOOKUP_ABORT_NEVER);
1562
ctx->cur_mail = &imail->mail.mail;
1563
mail_search_args_result_deserialize(ctx->mail_ctx.args,
1564
imail->data.search_results->data,
1565
imail->data.search_results->used);
1566
ret = search_match_once(ctx);
1567
search_match_finish(ctx, ret);
1568
ctx->cur_mail = NULL;
1572
static int search_more(struct index_search_context *ctx,
1573
struct mail **mail_r)
1575
struct index_mail *imail;
1578
while ((ret = search_more_with_prefetching(ctx, mail_r)) > 0) {
1579
imail = (struct index_mail *)*mail_r;
1580
if (imail->data.search_results == NULL)
1583
/* searching wasn't finished yet */
1584
if (search_finish_prefetch(ctx, imail))
1586
/* search finished as non-match */
1591
bool index_storage_search_next_nonblock(struct mail_search_context *_ctx,
1592
struct mail **mail_r, bool *tryagain_r)
1594
struct index_search_context *ctx = (struct index_search_context *)_ctx;
1595
struct mail *mail, *const *mailp;
1599
*tryagain_r = FALSE;
1601
if (_ctx->sort_program == NULL) {
1602
ret = search_more(ctx, &mail);
1614
while ((ret = search_more(ctx, &mail)) > 0)
1392
1615
index_sort_list_add(_ctx->sort_program, mail);
1396
if (search_would_block(ctx)) {
1397
1618
*tryagain_r = TRUE;
1403
mail_private->stats_track = old_stats_track;
1405
if (!match && _ctx->sort_program != NULL &&
1406
!*tryagain_r && !ctx->failed) {
1407
1621
/* finished searching the messages. now sort them and start
1408
1622
returning the messages. */
1409
1623
ctx->sorted = TRUE;
1410
1624
index_sort_list_finish(_ctx->sort_program);
1411
return index_storage_search_next_nonblock(_ctx, mail,
1414
return !ctx->failed && match;
1629
/* everything searched at this point already. just returning
1630
matches from sort list */
1631
if (!index_sort_list_next(_ctx->sort_program, &seq))
1634
mailp = array_idx(&ctx->mails, 0);
1635
mail_set_seq(*mailp, seq);
1417
1640
bool index_storage_search_next_update_seq(struct mail_search_context *_ctx)