index-search.c revision 7fc0f80480063a9d4cb9e8c07b50db2a5627799e
e8058322725ba050014777ee2484f7e833ab1e3aLukas Slebodnik/* Copyright (c) 2002-2010 Dovecot authors, see the included COPYING file */
b699c4d7f85a5404be1d1ee9450331aea869b886Stef Walter
b699c4d7f85a5404be1d1ee9450331aea869b886Stef Walter#include "lib.h"
b699c4d7f85a5404be1d1ee9450331aea869b886Stef Walter#include "ioloop.h"
b699c4d7f85a5404be1d1ee9450331aea869b886Stef Walter#include "array.h"
b699c4d7f85a5404be1d1ee9450331aea869b886Stef Walter#include "istream.h"
b699c4d7f85a5404be1d1ee9450331aea869b886Stef Walter#include "utc-offset.h"
b699c4d7f85a5404be1d1ee9450331aea869b886Stef Walter#include "str.h"
b699c4d7f85a5404be1d1ee9450331aea869b886Stef Walter#include "time-util.h"
b699c4d7f85a5404be1d1ee9450331aea869b886Stef Walter#include "imap-match.h"
b699c4d7f85a5404be1d1ee9450331aea869b886Stef Walter#include "message-address.h"
b699c4d7f85a5404be1d1ee9450331aea869b886Stef Walter#include "message-date.h"
b699c4d7f85a5404be1d1ee9450331aea869b886Stef Walter#include "message-search.h"
b699c4d7f85a5404be1d1ee9450331aea869b886Stef Walter#include "message-parser.h"
b699c4d7f85a5404be1d1ee9450331aea869b886Stef Walter#include "mail-index-modseq.h"
b699c4d7f85a5404be1d1ee9450331aea869b886Stef Walter#include "index-storage.h"
b699c4d7f85a5404be1d1ee9450331aea869b886Stef Walter#include "index-mail.h"
b699c4d7f85a5404be1d1ee9450331aea869b886Stef Walter#include "index-sort.h"
b699c4d7f85a5404be1d1ee9450331aea869b886Stef Walter#include "mail-search.h"
b699c4d7f85a5404be1d1ee9450331aea869b886Stef Walter#include "mailbox-search-result-private.h"
b699c4d7f85a5404be1d1ee9450331aea869b886Stef Walter
b699c4d7f85a5404be1d1ee9450331aea869b886Stef Walter#include <stdlib.h>
b699c4d7f85a5404be1d1ee9450331aea869b886Stef Walter#include <ctype.h>
b699c4d7f85a5404be1d1ee9450331aea869b886Stef Walter
b699c4d7f85a5404be1d1ee9450331aea869b886Stef Walter#define TXT_UNKNOWN_CHARSET "[BADCHARSET] Unknown charset"
b699c4d7f85a5404be1d1ee9450331aea869b886Stef Walter#define TXT_INVALID_SEARCH_KEY "Invalid search key"
b699c4d7f85a5404be1d1ee9450331aea869b886Stef Walter
b699c4d7f85a5404be1d1ee9450331aea869b886Stef Walter#define SEARCH_NOTIFY_INTERVAL_SECS 10
b699c4d7f85a5404be1d1ee9450331aea869b886Stef Walter
b699c4d7f85a5404be1d1ee9450331aea869b886Stef Walter#define SEARCH_COST_DENTRY 3ULL
b699c4d7f85a5404be1d1ee9450331aea869b886Stef Walter#define SEARCH_COST_ATTR 1ULL
b699c4d7f85a5404be1d1ee9450331aea869b886Stef Walter#define SEARCH_COST_FILES_READ 25ULL
b699c4d7f85a5404be1d1ee9450331aea869b886Stef Walter#define SEARCH_COST_KBYTE 15ULL
b699c4d7f85a5404be1d1ee9450331aea869b886Stef Walter#define SEARCH_COST_CACHE 1ULL
b699c4d7f85a5404be1d1ee9450331aea869b886Stef Walter
b699c4d7f85a5404be1d1ee9450331aea869b886Stef Walter#define SEARCH_MIN_NONBLOCK_USECS 200000
b699c4d7f85a5404be1d1ee9450331aea869b886Stef Walter#define SEARCH_MAX_NONBLOCK_USECS 250000
b699c4d7f85a5404be1d1ee9450331aea869b886Stef Walter#define SEARCH_INITIAL_MAX_COST 30000
b699c4d7f85a5404be1d1ee9450331aea869b886Stef Walter#define SEARCH_RECALC_MIN_USECS 50000
b699c4d7f85a5404be1d1ee9450331aea869b886Stef Walter
b699c4d7f85a5404be1d1ee9450331aea869b886Stef Walterstruct index_search_context {
b699c4d7f85a5404be1d1ee9450331aea869b886Stef Walter struct mail_search_context mail_ctx;
b699c4d7f85a5404be1d1ee9450331aea869b886Stef Walter struct mail_index_view *view;
b699c4d7f85a5404be1d1ee9450331aea869b886Stef Walter struct mailbox *box;
b699c4d7f85a5404be1d1ee9450331aea869b886Stef Walter
b699c4d7f85a5404be1d1ee9450331aea869b886Stef Walter uint32_t seq1, seq2;
b699c4d7f85a5404be1d1ee9450331aea869b886Stef Walter struct mail *mail;
b699c4d7f85a5404be1d1ee9450331aea869b886Stef Walter struct index_mail *imail;
b699c4d7f85a5404be1d1ee9450331aea869b886Stef Walter struct mail_thread_context *thread_ctx;
b699c4d7f85a5404be1d1ee9450331aea869b886Stef Walter
b699c4d7f85a5404be1d1ee9450331aea869b886Stef Walter const char *error;
b699c4d7f85a5404be1d1ee9450331aea869b886Stef Walter
b699c4d7f85a5404be1d1ee9450331aea869b886Stef Walter struct timeval search_start_time, last_notify;
b699c4d7f85a5404be1d1ee9450331aea869b886Stef Walter struct timeval last_nonblock_timeval;
b699c4d7f85a5404be1d1ee9450331aea869b886Stef Walter unsigned long long cost, next_time_check_cost;
b699c4d7f85a5404be1d1ee9450331aea869b886Stef Walter
b699c4d7f85a5404be1d1ee9450331aea869b886Stef Walter unsigned int failed:1;
b699c4d7f85a5404be1d1ee9450331aea869b886Stef Walter unsigned int sorted:1;
b699c4d7f85a5404be1d1ee9450331aea869b886Stef Walter unsigned int have_seqsets:1;
b699c4d7f85a5404be1d1ee9450331aea869b886Stef Walter unsigned int have_index_args:1;
b699c4d7f85a5404be1d1ee9450331aea869b886Stef Walter unsigned int have_mailbox_args:1;
b699c4d7f85a5404be1d1ee9450331aea869b886Stef Walter};
b699c4d7f85a5404be1d1ee9450331aea869b886Stef Walter
b699c4d7f85a5404be1d1ee9450331aea869b886Stef Walterstruct search_header_context {
b699c4d7f85a5404be1d1ee9450331aea869b886Stef Walter struct index_search_context *index_context;
b699c4d7f85a5404be1d1ee9450331aea869b886Stef Walter struct mail_search_arg *args;
b699c4d7f85a5404be1d1ee9450331aea869b886Stef Walter
b699c4d7f85a5404be1d1ee9450331aea869b886Stef Walter struct message_header_line *hdr;
b699c4d7f85a5404be1d1ee9450331aea869b886Stef Walter
b699c4d7f85a5404be1d1ee9450331aea869b886Stef Walter unsigned int parse_headers:1;
b699c4d7f85a5404be1d1ee9450331aea869b886Stef Walter unsigned int custom_header:1;
b699c4d7f85a5404be1d1ee9450331aea869b886Stef Walter unsigned int threading:1;
b699c4d7f85a5404be1d1ee9450331aea869b886Stef Walter};
b699c4d7f85a5404be1d1ee9450331aea869b886Stef Walter
b699c4d7f85a5404be1d1ee9450331aea869b886Stef Walterstruct search_body_context {
b699c4d7f85a5404be1d1ee9450331aea869b886Stef Walter struct index_search_context *index_ctx;
b699c4d7f85a5404be1d1ee9450331aea869b886Stef Walter struct istream *input;
b699c4d7f85a5404be1d1ee9450331aea869b886Stef Walter struct message_part *part;
b699c4d7f85a5404be1d1ee9450331aea869b886Stef Walter};
b699c4d7f85a5404be1d1ee9450331aea869b886Stef Walter
b699c4d7f85a5404be1d1ee9450331aea869b886Stef Walterstatic const enum message_header_parser_flags hdr_parser_flags =
b699c4d7f85a5404be1d1ee9450331aea869b886Stef Walter MESSAGE_HEADER_PARSER_FLAG_CLEAN_ONELINE;
b699c4d7f85a5404be1d1ee9450331aea869b886Stef Walter
b699c4d7f85a5404be1d1ee9450331aea869b886Stef Walterstatic void search_parse_msgset_args(unsigned int messages_count,
b699c4d7f85a5404be1d1ee9450331aea869b886Stef Walter struct mail_search_arg *args,
b699c4d7f85a5404be1d1ee9450331aea869b886Stef Walter uint32_t *seq1_r, uint32_t *seq2_r);
b699c4d7f85a5404be1d1ee9450331aea869b886Stef Walter
b699c4d7f85a5404be1d1ee9450331aea869b886Stef Walterstatic void search_init_arg(struct mail_search_arg *arg,
b699c4d7f85a5404be1d1ee9450331aea869b886Stef Walter struct index_search_context *ctx)
b699c4d7f85a5404be1d1ee9450331aea869b886Stef Walter{
b699c4d7f85a5404be1d1ee9450331aea869b886Stef Walter uint8_t guid[MAIL_GUID_128_SIZE];
b699c4d7f85a5404be1d1ee9450331aea869b886Stef Walter bool match;
b699c4d7f85a5404be1d1ee9450331aea869b886Stef Walter
dff909d473f43a6bd0f0286fa2d279c0ebe945c6Stef Walter switch (arg->type) {
dff909d473f43a6bd0f0286fa2d279c0ebe945c6Stef Walter case SEARCH_SEQSET:
b699c4d7f85a5404be1d1ee9450331aea869b886Stef Walter ctx->have_seqsets = TRUE;
b699c4d7f85a5404be1d1ee9450331aea869b886Stef Walter break;
b699c4d7f85a5404be1d1ee9450331aea869b886Stef Walter case SEARCH_UIDSET:
dff909d473f43a6bd0f0286fa2d279c0ebe945c6Stef Walter case SEARCH_INTHREAD:
dff909d473f43a6bd0f0286fa2d279c0ebe945c6Stef Walter case SEARCH_FLAGS:
dff909d473f43a6bd0f0286fa2d279c0ebe945c6Stef Walter case SEARCH_KEYWORDS:
dff909d473f43a6bd0f0286fa2d279c0ebe945c6Stef Walter case SEARCH_MODSEQ:
dff909d473f43a6bd0f0286fa2d279c0ebe945c6Stef Walter if (arg->type == SEARCH_MODSEQ)
dff909d473f43a6bd0f0286fa2d279c0ebe945c6Stef Walter mail_index_modseq_enable(ctx->box->index);
dff909d473f43a6bd0f0286fa2d279c0ebe945c6Stef Walter ctx->have_index_args = TRUE;
dff909d473f43a6bd0f0286fa2d279c0ebe945c6Stef Walter break;
dff909d473f43a6bd0f0286fa2d279c0ebe945c6Stef Walter case SEARCH_MAILBOX_GUID:
dff909d473f43a6bd0f0286fa2d279c0ebe945c6Stef Walter if (mailbox_get_guid(ctx->box, guid) < 0) {
dff909d473f43a6bd0f0286fa2d279c0ebe945c6Stef Walter /* result will be unknown */
dff909d473f43a6bd0f0286fa2d279c0ebe945c6Stef Walter break;
dff909d473f43a6bd0f0286fa2d279c0ebe945c6Stef Walter }
dff909d473f43a6bd0f0286fa2d279c0ebe945c6Stef Walter
dff909d473f43a6bd0f0286fa2d279c0ebe945c6Stef Walter match = strcmp(mail_guid_128_to_string(guid),
dff909d473f43a6bd0f0286fa2d279c0ebe945c6Stef Walter arg->value.str) == 0;
dff909d473f43a6bd0f0286fa2d279c0ebe945c6Stef Walter if (match != arg->not)
dff909d473f43a6bd0f0286fa2d279c0ebe945c6Stef Walter arg->match_always = TRUE;
1319e71fd1680ca4864afe0b1aca2b8c8e4a1ee4Stef Walter else
1319e71fd1680ca4864afe0b1aca2b8c8e4a1ee4Stef Walter arg->nonmatch_always = TRUE;
b699c4d7f85a5404be1d1ee9450331aea869b886Stef Walter break;
dff909d473f43a6bd0f0286fa2d279c0ebe945c6Stef Walter case SEARCH_MAILBOX:
dff909d473f43a6bd0f0286fa2d279c0ebe945c6Stef Walter case SEARCH_MAILBOX_GLOB:
dff909d473f43a6bd0f0286fa2d279c0ebe945c6Stef Walter ctx->have_mailbox_args = TRUE;
dff909d473f43a6bd0f0286fa2d279c0ebe945c6Stef Walter break;
dff909d473f43a6bd0f0286fa2d279c0ebe945c6Stef Walter case SEARCH_ALL:
dff909d473f43a6bd0f0286fa2d279c0ebe945c6Stef Walter if (!arg->not)
dff909d473f43a6bd0f0286fa2d279c0ebe945c6Stef Walter arg->match_always = TRUE;
dff909d473f43a6bd0f0286fa2d279c0ebe945c6Stef Walter else
dff909d473f43a6bd0f0286fa2d279c0ebe945c6Stef Walter arg->nonmatch_always = TRUE;
dff909d473f43a6bd0f0286fa2d279c0ebe945c6Stef Walter break;
dff909d473f43a6bd0f0286fa2d279c0ebe945c6Stef Walter default:
dff909d473f43a6bd0f0286fa2d279c0ebe945c6Stef Walter break;
dff909d473f43a6bd0f0286fa2d279c0ebe945c6Stef Walter }
dff909d473f43a6bd0f0286fa2d279c0ebe945c6Stef Walter}
dff909d473f43a6bd0f0286fa2d279c0ebe945c6Stef Walter
dff909d473f43a6bd0f0286fa2d279c0ebe945c6Stef Walterstatic void search_seqset_arg(struct mail_search_arg *arg,
b699c4d7f85a5404be1d1ee9450331aea869b886Stef Walter struct index_search_context *ctx)
1319e71fd1680ca4864afe0b1aca2b8c8e4a1ee4Stef Walter{
1319e71fd1680ca4864afe0b1aca2b8c8e4a1ee4Stef Walter if (arg->type == SEARCH_SEQSET) {
1319e71fd1680ca4864afe0b1aca2b8c8e4a1ee4Stef Walter if (seq_range_exists(&arg->value.seqset, ctx->mail_ctx.seq))
1319e71fd1680ca4864afe0b1aca2b8c8e4a1ee4Stef Walter ARG_SET_RESULT(arg, 1);
1319e71fd1680ca4864afe0b1aca2b8c8e4a1ee4Stef Walter else
b699c4d7f85a5404be1d1ee9450331aea869b886Stef Walter ARG_SET_RESULT(arg, 0);
b699c4d7f85a5404be1d1ee9450331aea869b886Stef Walter }
b699c4d7f85a5404be1d1ee9450331aea869b886Stef Walter}
b699c4d7f85a5404be1d1ee9450331aea869b886Stef Walter
b699c4d7f85a5404be1d1ee9450331aea869b886Stef Walterstatic int search_arg_match_keywords(struct index_search_context *ctx,
b699c4d7f85a5404be1d1ee9450331aea869b886Stef Walter struct mail_search_arg *arg)
dff909d473f43a6bd0f0286fa2d279c0ebe945c6Stef Walter{
dff909d473f43a6bd0f0286fa2d279c0ebe945c6Stef Walter ARRAY_TYPE(keyword_indexes) keyword_indexes_arr;
dff909d473f43a6bd0f0286fa2d279c0ebe945c6Stef Walter const struct mail_keywords *search_kws = arg->value.keywords;
dff909d473f43a6bd0f0286fa2d279c0ebe945c6Stef Walter const unsigned int *keyword_indexes;
b699c4d7f85a5404be1d1ee9450331aea869b886Stef Walter unsigned int i, j, count;
b699c4d7f85a5404be1d1ee9450331aea869b886Stef Walter
dff909d473f43a6bd0f0286fa2d279c0ebe945c6Stef Walter t_array_init(&keyword_indexes_arr, 128);
dff909d473f43a6bd0f0286fa2d279c0ebe945c6Stef Walter mail_index_lookup_keywords(ctx->view, ctx->mail_ctx.seq,
dff909d473f43a6bd0f0286fa2d279c0ebe945c6Stef Walter &keyword_indexes_arr);
dff909d473f43a6bd0f0286fa2d279c0ebe945c6Stef Walter keyword_indexes = array_get(&keyword_indexes_arr, &count);
dff909d473f43a6bd0f0286fa2d279c0ebe945c6Stef Walter
dff909d473f43a6bd0f0286fa2d279c0ebe945c6Stef Walter /* there probably aren't many keywords, so O(n*m) for now */
dff909d473f43a6bd0f0286fa2d279c0ebe945c6Stef Walter for (i = 0; i < search_kws->count; i++) {
dff909d473f43a6bd0f0286fa2d279c0ebe945c6Stef Walter for (j = 0; j < count; j++) {
dff909d473f43a6bd0f0286fa2d279c0ebe945c6Stef Walter if (search_kws->idx[i] == keyword_indexes[j])
dff909d473f43a6bd0f0286fa2d279c0ebe945c6Stef Walter break;
b699c4d7f85a5404be1d1ee9450331aea869b886Stef Walter }
b699c4d7f85a5404be1d1ee9450331aea869b886Stef Walter if (j == count)
b699c4d7f85a5404be1d1ee9450331aea869b886Stef Walter return 0;
b699c4d7f85a5404be1d1ee9450331aea869b886Stef Walter }
b699c4d7f85a5404be1d1ee9450331aea869b886Stef Walter return 1;
b699c4d7f85a5404be1d1ee9450331aea869b886Stef Walter}
b699c4d7f85a5404be1d1ee9450331aea869b886Stef Walter
b699c4d7f85a5404be1d1ee9450331aea869b886Stef Walter/* Returns >0 = matched, 0 = not matched, -1 = unknown */
b699c4d7f85a5404be1d1ee9450331aea869b886Stef Walterstatic int search_arg_match_index(struct index_search_context *ctx,
1319e71fd1680ca4864afe0b1aca2b8c8e4a1ee4Stef Walter struct mail_search_arg *arg,
1319e71fd1680ca4864afe0b1aca2b8c8e4a1ee4Stef Walter const struct mail_index_record *rec)
1319e71fd1680ca4864afe0b1aca2b8c8e4a1ee4Stef Walter{
b699c4d7f85a5404be1d1ee9450331aea869b886Stef Walter enum mail_flags flags;
b699c4d7f85a5404be1d1ee9450331aea869b886Stef Walter uint64_t modseq;
b699c4d7f85a5404be1d1ee9450331aea869b886Stef Walter int ret;
b699c4d7f85a5404be1d1ee9450331aea869b886Stef Walter
b699c4d7f85a5404be1d1ee9450331aea869b886Stef Walter switch (arg->type) {
b699c4d7f85a5404be1d1ee9450331aea869b886Stef Walter case SEARCH_UIDSET:
b699c4d7f85a5404be1d1ee9450331aea869b886Stef Walter case SEARCH_INTHREAD:
b699c4d7f85a5404be1d1ee9450331aea869b886Stef Walter return seq_range_exists(&arg->value.seqset, rec->uid);
b699c4d7f85a5404be1d1ee9450331aea869b886Stef Walter case SEARCH_FLAGS:
b699c4d7f85a5404be1d1ee9450331aea869b886Stef Walter /* recent flag shouldn't be set, but indexes from v1.0.x
b699c4d7f85a5404be1d1ee9450331aea869b886Stef Walter may contain it. */
b699c4d7f85a5404be1d1ee9450331aea869b886Stef Walter flags = rec->flags & ~MAIL_RECENT;
b699c4d7f85a5404be1d1ee9450331aea869b886Stef Walter if ((arg->value.flags & MAIL_RECENT) != 0 &&
b699c4d7f85a5404be1d1ee9450331aea869b886Stef Walter index_mailbox_is_recent(ctx->box, rec->uid))
90e04eae7e54ec892a6f239783df94dab5d1ed9aJakub Hrozek flags |= MAIL_RECENT;
90e04eae7e54ec892a6f239783df94dab5d1ed9aJakub Hrozek return (flags & arg->value.flags) == arg->value.flags;
90e04eae7e54ec892a6f239783df94dab5d1ed9aJakub Hrozek case SEARCH_KEYWORDS:
90e04eae7e54ec892a6f239783df94dab5d1ed9aJakub Hrozek T_BEGIN {
90e04eae7e54ec892a6f239783df94dab5d1ed9aJakub Hrozek ret = search_arg_match_keywords(ctx, arg);
90e04eae7e54ec892a6f239783df94dab5d1ed9aJakub Hrozek } T_END;
90e04eae7e54ec892a6f239783df94dab5d1ed9aJakub Hrozek return ret;
90e04eae7e54ec892a6f239783df94dab5d1ed9aJakub Hrozek case SEARCH_MODSEQ: {
90e04eae7e54ec892a6f239783df94dab5d1ed9aJakub Hrozek if (arg->value.flags != 0) {
90e04eae7e54ec892a6f239783df94dab5d1ed9aJakub Hrozek modseq = mail_index_modseq_lookup_flags(ctx->view,
b699c4d7f85a5404be1d1ee9450331aea869b886Stef Walter arg->value.flags, ctx->mail_ctx.seq);
b699c4d7f85a5404be1d1ee9450331aea869b886Stef Walter } else if (arg->value.keywords != NULL) {
b699c4d7f85a5404be1d1ee9450331aea869b886Stef Walter modseq = mail_index_modseq_lookup_keywords(ctx->view,
b699c4d7f85a5404be1d1ee9450331aea869b886Stef Walter arg->value.keywords, ctx->mail_ctx.seq);
b699c4d7f85a5404be1d1ee9450331aea869b886Stef Walter } else {
b699c4d7f85a5404be1d1ee9450331aea869b886Stef Walter modseq = mail_index_modseq_lookup(ctx->view,
b699c4d7f85a5404be1d1ee9450331aea869b886Stef Walter ctx->mail_ctx.seq);
b699c4d7f85a5404be1d1ee9450331aea869b886Stef Walter }
b699c4d7f85a5404be1d1ee9450331aea869b886Stef Walter return modseq >= arg->value.modseq->modseq;
b699c4d7f85a5404be1d1ee9450331aea869b886Stef Walter }
b699c4d7f85a5404be1d1ee9450331aea869b886Stef Walter default:
b699c4d7f85a5404be1d1ee9450331aea869b886Stef Walter return -1;
b699c4d7f85a5404be1d1ee9450331aea869b886Stef Walter }
b699c4d7f85a5404be1d1ee9450331aea869b886Stef Walter}
dff909d473f43a6bd0f0286fa2d279c0ebe945c6Stef Walter
b699c4d7f85a5404be1d1ee9450331aea869b886Stef Walterstatic void search_index_arg(struct mail_search_arg *arg,
b699c4d7f85a5404be1d1ee9450331aea869b886Stef Walter struct index_search_context *ctx)
dff909d473f43a6bd0f0286fa2d279c0ebe945c6Stef Walter{
dff909d473f43a6bd0f0286fa2d279c0ebe945c6Stef Walter const struct mail_index_record *rec;
dff909d473f43a6bd0f0286fa2d279c0ebe945c6Stef Walter
dff909d473f43a6bd0f0286fa2d279c0ebe945c6Stef Walter rec = mail_index_lookup(ctx->view, ctx->mail_ctx.seq);
dff909d473f43a6bd0f0286fa2d279c0ebe945c6Stef Walter switch (search_arg_match_index(ctx, arg, rec)) {
dff909d473f43a6bd0f0286fa2d279c0ebe945c6Stef Walter case -1:
dff909d473f43a6bd0f0286fa2d279c0ebe945c6Stef Walter /* unknown */
dff909d473f43a6bd0f0286fa2d279c0ebe945c6Stef Walter break;
dff909d473f43a6bd0f0286fa2d279c0ebe945c6Stef Walter case 0:
dff909d473f43a6bd0f0286fa2d279c0ebe945c6Stef Walter ARG_SET_RESULT(arg, 0);
dff909d473f43a6bd0f0286fa2d279c0ebe945c6Stef Walter break;
dff909d473f43a6bd0f0286fa2d279c0ebe945c6Stef Walter default:
dff909d473f43a6bd0f0286fa2d279c0ebe945c6Stef Walter ARG_SET_RESULT(arg, 1);
dff909d473f43a6bd0f0286fa2d279c0ebe945c6Stef Walter break;
dff909d473f43a6bd0f0286fa2d279c0ebe945c6Stef Walter }
dff909d473f43a6bd0f0286fa2d279c0ebe945c6Stef Walter}
dff909d473f43a6bd0f0286fa2d279c0ebe945c6Stef Walter
dff909d473f43a6bd0f0286fa2d279c0ebe945c6Stef Walter/* Returns >0 = matched, 0 = not matched, -1 = unknown */
dff909d473f43a6bd0f0286fa2d279c0ebe945c6Stef Walterstatic int search_arg_match_mailbox(struct index_search_context *ctx,
dff909d473f43a6bd0f0286fa2d279c0ebe945c6Stef Walter struct mail_search_arg *arg)
dff909d473f43a6bd0f0286fa2d279c0ebe945c6Stef Walter{
dff909d473f43a6bd0f0286fa2d279c0ebe945c6Stef Walter const char *str;
dff909d473f43a6bd0f0286fa2d279c0ebe945c6Stef Walter
dff909d473f43a6bd0f0286fa2d279c0ebe945c6Stef Walter switch (arg->type) {
dff909d473f43a6bd0f0286fa2d279c0ebe945c6Stef Walter case SEARCH_MAILBOX:
dff909d473f43a6bd0f0286fa2d279c0ebe945c6Stef Walter if (mail_get_special(ctx->mail, MAIL_FETCH_MAILBOX_NAME,
dff909d473f43a6bd0f0286fa2d279c0ebe945c6Stef Walter &str) < 0)
dff909d473f43a6bd0f0286fa2d279c0ebe945c6Stef Walter return -1;
dff909d473f43a6bd0f0286fa2d279c0ebe945c6Stef Walter
dff909d473f43a6bd0f0286fa2d279c0ebe945c6Stef Walter if (strcasecmp(str, "INBOX") == 0)
dff909d473f43a6bd0f0286fa2d279c0ebe945c6Stef Walter return strcasecmp(arg->value.str, "INBOX") == 0;
dff909d473f43a6bd0f0286fa2d279c0ebe945c6Stef Walter return strcmp(str, arg->value.str) == 0;
dff909d473f43a6bd0f0286fa2d279c0ebe945c6Stef Walter case SEARCH_MAILBOX_GLOB:
90e04eae7e54ec892a6f239783df94dab5d1ed9aJakub Hrozek if (mail_get_special(ctx->mail, MAIL_FETCH_MAILBOX_NAME,
90e04eae7e54ec892a6f239783df94dab5d1ed9aJakub Hrozek &str) < 0)
90e04eae7e54ec892a6f239783df94dab5d1ed9aJakub Hrozek return -1;
1319e71fd1680ca4864afe0b1aca2b8c8e4a1ee4Stef Walter return imap_match(arg->value.mailbox_glob, str) == IMAP_MATCH_YES;
dff909d473f43a6bd0f0286fa2d279c0ebe945c6Stef Walter default:
dff909d473f43a6bd0f0286fa2d279c0ebe945c6Stef Walter return -1;
dff909d473f43a6bd0f0286fa2d279c0ebe945c6Stef Walter }
dff909d473f43a6bd0f0286fa2d279c0ebe945c6Stef Walter}
dff909d473f43a6bd0f0286fa2d279c0ebe945c6Stef Walter
dff909d473f43a6bd0f0286fa2d279c0ebe945c6Stef Walterstatic void search_mailbox_arg(struct mail_search_arg *arg,
dff909d473f43a6bd0f0286fa2d279c0ebe945c6Stef Walter struct index_search_context *ctx)
dff909d473f43a6bd0f0286fa2d279c0ebe945c6Stef Walter{
dff909d473f43a6bd0f0286fa2d279c0ebe945c6Stef Walter switch (search_arg_match_mailbox(ctx, arg)) {
dff909d473f43a6bd0f0286fa2d279c0ebe945c6Stef Walter case -1:
dff909d473f43a6bd0f0286fa2d279c0ebe945c6Stef Walter /* unknown */
dff909d473f43a6bd0f0286fa2d279c0ebe945c6Stef Walter break;
dff909d473f43a6bd0f0286fa2d279c0ebe945c6Stef Walter case 0:
dff909d473f43a6bd0f0286fa2d279c0ebe945c6Stef Walter ARG_SET_RESULT(arg, 0);
dff909d473f43a6bd0f0286fa2d279c0ebe945c6Stef Walter break;
dff909d473f43a6bd0f0286fa2d279c0ebe945c6Stef Walter default:
dff909d473f43a6bd0f0286fa2d279c0ebe945c6Stef Walter ARG_SET_RESULT(arg, 1);
dff909d473f43a6bd0f0286fa2d279c0ebe945c6Stef Walter break;
dff909d473f43a6bd0f0286fa2d279c0ebe945c6Stef Walter }
dff909d473f43a6bd0f0286fa2d279c0ebe945c6Stef Walter}
dff909d473f43a6bd0f0286fa2d279c0ebe945c6Stef Walter
dff909d473f43a6bd0f0286fa2d279c0ebe945c6Stef Walter/* Returns >0 = matched, 0 = not matched, -1 = unknown */
dff909d473f43a6bd0f0286fa2d279c0ebe945c6Stef Walterstatic int search_arg_match_cached(struct index_search_context *ctx,
dff909d473f43a6bd0f0286fa2d279c0ebe945c6Stef Walter struct mail_search_arg *arg)
dff909d473f43a6bd0f0286fa2d279c0ebe945c6Stef Walter{
dff909d473f43a6bd0f0286fa2d279c0ebe945c6Stef Walter const char *str;
dff909d473f43a6bd0f0286fa2d279c0ebe945c6Stef Walter struct tm *tm;
dff909d473f43a6bd0f0286fa2d279c0ebe945c6Stef Walter uoff_t virtual_size;
dff909d473f43a6bd0f0286fa2d279c0ebe945c6Stef Walter time_t date;
dff909d473f43a6bd0f0286fa2d279c0ebe945c6Stef Walter int tz_offset;
dff909d473f43a6bd0f0286fa2d279c0ebe945c6Stef Walter bool have_tz_offset;
66277b21179d95f6e96abed01a20ccbccf27ce99Pavel Březina
dff909d473f43a6bd0f0286fa2d279c0ebe945c6Stef Walter switch (arg->type) {
dff909d473f43a6bd0f0286fa2d279c0ebe945c6Stef Walter /* internal dates */
dff909d473f43a6bd0f0286fa2d279c0ebe945c6Stef Walter case SEARCH_BEFORE:
dff909d473f43a6bd0f0286fa2d279c0ebe945c6Stef Walter case SEARCH_ON:
dff909d473f43a6bd0f0286fa2d279c0ebe945c6Stef Walter case SEARCH_SINCE:
dff909d473f43a6bd0f0286fa2d279c0ebe945c6Stef Walter have_tz_offset = FALSE; tz_offset = 0; date = (time_t)-1;
dff909d473f43a6bd0f0286fa2d279c0ebe945c6Stef Walter switch (arg->value.date_type) {
dff909d473f43a6bd0f0286fa2d279c0ebe945c6Stef Walter case MAIL_SEARCH_DATE_TYPE_SENT:
4f7f714e118e95896fac5239c7a8b529c39a4758Jakub Hrozek if (mail_get_date(ctx->mail, &date, &tz_offset) < 0)
4f7f714e118e95896fac5239c7a8b529c39a4758Jakub Hrozek return -1;
4f7f714e118e95896fac5239c7a8b529c39a4758Jakub Hrozek have_tz_offset = TRUE;
4f7f714e118e95896fac5239c7a8b529c39a4758Jakub Hrozek break;
4f7f714e118e95896fac5239c7a8b529c39a4758Jakub Hrozek case MAIL_SEARCH_DATE_TYPE_RECEIVED:
4f7f714e118e95896fac5239c7a8b529c39a4758Jakub Hrozek if (mail_get_received_date(ctx->mail, &date) < 0)
4f7f714e118e95896fac5239c7a8b529c39a4758Jakub Hrozek return -1;
4f7f714e118e95896fac5239c7a8b529c39a4758Jakub Hrozek break;
4f7f714e118e95896fac5239c7a8b529c39a4758Jakub Hrozek case MAIL_SEARCH_DATE_TYPE_SAVED:
4f7f714e118e95896fac5239c7a8b529c39a4758Jakub Hrozek if (mail_get_save_date(ctx->mail, &date) < 0)
4f7f714e118e95896fac5239c7a8b529c39a4758Jakub Hrozek return -1;
4f7f714e118e95896fac5239c7a8b529c39a4758Jakub Hrozek break;
4f7f714e118e95896fac5239c7a8b529c39a4758Jakub Hrozek }
4f7f714e118e95896fac5239c7a8b529c39a4758Jakub Hrozek
4f7f714e118e95896fac5239c7a8b529c39a4758Jakub Hrozek if ((arg->value.search_flags &
1319e71fd1680ca4864afe0b1aca2b8c8e4a1ee4Stef Walter MAIL_SEARCH_ARG_FLAG_USE_TZ) == 0) {
1319e71fd1680ca4864afe0b1aca2b8c8e4a1ee4Stef Walter if (!have_tz_offset) {
90e04eae7e54ec892a6f239783df94dab5d1ed9aJakub Hrozek tm = localtime(&date);
90e04eae7e54ec892a6f239783df94dab5d1ed9aJakub Hrozek tz_offset = utc_offset(tm, date);
90e04eae7e54ec892a6f239783df94dab5d1ed9aJakub Hrozek }
90e04eae7e54ec892a6f239783df94dab5d1ed9aJakub Hrozek date += tz_offset * 60;
90e04eae7e54ec892a6f239783df94dab5d1ed9aJakub Hrozek }
90e04eae7e54ec892a6f239783df94dab5d1ed9aJakub Hrozek
1319e71fd1680ca4864afe0b1aca2b8c8e4a1ee4Stef Walter switch (arg->type) {
4f7f714e118e95896fac5239c7a8b529c39a4758Jakub Hrozek case SEARCH_BEFORE:
4f7f714e118e95896fac5239c7a8b529c39a4758Jakub Hrozek return date < arg->value.time;
4f7f714e118e95896fac5239c7a8b529c39a4758Jakub Hrozek case SEARCH_ON:
1319e71fd1680ca4864afe0b1aca2b8c8e4a1ee4Stef Walter return date >= arg->value.time &&
90e04eae7e54ec892a6f239783df94dab5d1ed9aJakub Hrozek date < arg->value.time + 3600*24;
90e04eae7e54ec892a6f239783df94dab5d1ed9aJakub Hrozek case SEARCH_SINCE:
1319e71fd1680ca4864afe0b1aca2b8c8e4a1ee4Stef Walter return date >= arg->value.time;
1319e71fd1680ca4864afe0b1aca2b8c8e4a1ee4Stef Walter default:
66277b21179d95f6e96abed01a20ccbccf27ce99Pavel Březina /* unreachable */
90e04eae7e54ec892a6f239783df94dab5d1ed9aJakub Hrozek break;
90e04eae7e54ec892a6f239783df94dab5d1ed9aJakub Hrozek }
1319e71fd1680ca4864afe0b1aca2b8c8e4a1ee4Stef Walter
1319e71fd1680ca4864afe0b1aca2b8c8e4a1ee4Stef Walter /* sizes */
90e04eae7e54ec892a6f239783df94dab5d1ed9aJakub Hrozek case SEARCH_SMALLER:
58229439447d5617913a5a2e173b78105c694842Pavel Březina case SEARCH_LARGER:
58229439447d5617913a5a2e173b78105c694842Pavel Březina if (mail_get_virtual_size(ctx->mail, &virtual_size) < 0)
5de968e80ade1c02d1907834dcff95e9fc9ad10aJakub Hrozek return -1;
5de968e80ade1c02d1907834dcff95e9fc9ad10aJakub Hrozek
58229439447d5617913a5a2e173b78105c694842Pavel Březina if (arg->type == SEARCH_SMALLER)
58229439447d5617913a5a2e173b78105c694842Pavel Březina return virtual_size < arg->value.size;
90e04eae7e54ec892a6f239783df94dab5d1ed9aJakub Hrozek else
90e04eae7e54ec892a6f239783df94dab5d1ed9aJakub Hrozek return virtual_size > arg->value.size;
90e04eae7e54ec892a6f239783df94dab5d1ed9aJakub Hrozek
90e04eae7e54ec892a6f239783df94dab5d1ed9aJakub Hrozek case SEARCH_GUID:
1319e71fd1680ca4864afe0b1aca2b8c8e4a1ee4Stef Walter if (mail_get_special(ctx->mail, MAIL_FETCH_GUID, &str) < 0)
1319e71fd1680ca4864afe0b1aca2b8c8e4a1ee4Stef Walter return -1;
4f7f714e118e95896fac5239c7a8b529c39a4758Jakub Hrozek return strcmp(str, arg->value.str) == 0;
4f7f714e118e95896fac5239c7a8b529c39a4758Jakub Hrozek default:
4f7f714e118e95896fac5239c7a8b529c39a4758Jakub Hrozek return -1;
4f7f714e118e95896fac5239c7a8b529c39a4758Jakub Hrozek }
4f7f714e118e95896fac5239c7a8b529c39a4758Jakub Hrozek}
4f7f714e118e95896fac5239c7a8b529c39a4758Jakub Hrozek
4f7f714e118e95896fac5239c7a8b529c39a4758Jakub Hrozekstatic void search_cached_arg(struct mail_search_arg *arg,
4f7f714e118e95896fac5239c7a8b529c39a4758Jakub Hrozek struct index_search_context *ctx)
4f7f714e118e95896fac5239c7a8b529c39a4758Jakub Hrozek{
4f7f714e118e95896fac5239c7a8b529c39a4758Jakub Hrozek switch (search_arg_match_cached(ctx, arg)) {
4f7f714e118e95896fac5239c7a8b529c39a4758Jakub Hrozek case -1:
4f7f714e118e95896fac5239c7a8b529c39a4758Jakub Hrozek /* unknown */
4f7f714e118e95896fac5239c7a8b529c39a4758Jakub Hrozek break;
4f7f714e118e95896fac5239c7a8b529c39a4758Jakub Hrozek case 0:
4f7f714e118e95896fac5239c7a8b529c39a4758Jakub Hrozek ARG_SET_RESULT(arg, 0);
4f7f714e118e95896fac5239c7a8b529c39a4758Jakub Hrozek break;
4f7f714e118e95896fac5239c7a8b529c39a4758Jakub Hrozek default:
4f7f714e118e95896fac5239c7a8b529c39a4758Jakub Hrozek ARG_SET_RESULT(arg, 1);
4f7f714e118e95896fac5239c7a8b529c39a4758Jakub Hrozek break;
4f7f714e118e95896fac5239c7a8b529c39a4758Jakub Hrozek }
4f7f714e118e95896fac5239c7a8b529c39a4758Jakub Hrozek}
4f7f714e118e95896fac5239c7a8b529c39a4758Jakub Hrozek
4f7f714e118e95896fac5239c7a8b529c39a4758Jakub Hrozekstatic int search_sent(enum mail_search_arg_type type, time_t search_time,
4f7f714e118e95896fac5239c7a8b529c39a4758Jakub Hrozek const unsigned char *sent_value, size_t sent_value_len)
4f7f714e118e95896fac5239c7a8b529c39a4758Jakub Hrozek{
4f7f714e118e95896fac5239c7a8b529c39a4758Jakub Hrozek time_t sent_time;
4f7f714e118e95896fac5239c7a8b529c39a4758Jakub Hrozek int timezone_offset;
4f7f714e118e95896fac5239c7a8b529c39a4758Jakub Hrozek
4f7f714e118e95896fac5239c7a8b529c39a4758Jakub Hrozek if (sent_value == NULL)
4f7f714e118e95896fac5239c7a8b529c39a4758Jakub Hrozek return 0;
4f7f714e118e95896fac5239c7a8b529c39a4758Jakub Hrozek
4f7f714e118e95896fac5239c7a8b529c39a4758Jakub Hrozek /* NOTE: RFC-3501 specifies that timezone is ignored
4f7f714e118e95896fac5239c7a8b529c39a4758Jakub Hrozek in searches. sent_time is returned as UTC, so change it. */
4f7f714e118e95896fac5239c7a8b529c39a4758Jakub Hrozek if (!message_date_parse(sent_value, sent_value_len,
4f7f714e118e95896fac5239c7a8b529c39a4758Jakub Hrozek &sent_time, &timezone_offset))
4f7f714e118e95896fac5239c7a8b529c39a4758Jakub Hrozek return 0;
4f7f714e118e95896fac5239c7a8b529c39a4758Jakub Hrozek sent_time += timezone_offset * 60;
4f7f714e118e95896fac5239c7a8b529c39a4758Jakub Hrozek
4f7f714e118e95896fac5239c7a8b529c39a4758Jakub Hrozek switch (type) {
66277b21179d95f6e96abed01a20ccbccf27ce99Pavel Březina case SEARCH_BEFORE:
4f7f714e118e95896fac5239c7a8b529c39a4758Jakub Hrozek return sent_time < search_time;
4f7f714e118e95896fac5239c7a8b529c39a4758Jakub Hrozek case SEARCH_ON:
4f7f714e118e95896fac5239c7a8b529c39a4758Jakub Hrozek return sent_time >= search_time &&
4f7f714e118e95896fac5239c7a8b529c39a4758Jakub Hrozek sent_time < search_time + 3600*24;
4f7f714e118e95896fac5239c7a8b529c39a4758Jakub Hrozek case SEARCH_SINCE:
4f7f714e118e95896fac5239c7a8b529c39a4758Jakub Hrozek return sent_time >= search_time;
4f7f714e118e95896fac5239c7a8b529c39a4758Jakub Hrozek default:
4f7f714e118e95896fac5239c7a8b529c39a4758Jakub Hrozek i_unreached();
4f7f714e118e95896fac5239c7a8b529c39a4758Jakub Hrozek }
4f7f714e118e95896fac5239c7a8b529c39a4758Jakub Hrozek}
4f7f714e118e95896fac5239c7a8b529c39a4758Jakub Hrozek
4f7f714e118e95896fac5239c7a8b529c39a4758Jakub Hrozekstatic struct message_search_context *
4f7f714e118e95896fac5239c7a8b529c39a4758Jakub Hrozekmsg_search_arg_context(struct index_search_context *ctx,
4f7f714e118e95896fac5239c7a8b529c39a4758Jakub Hrozek struct mail_search_arg *arg)
4f7f714e118e95896fac5239c7a8b529c39a4758Jakub Hrozek{
4f7f714e118e95896fac5239c7a8b529c39a4758Jakub Hrozek struct message_search_context *arg_ctx = arg->context;
4f7f714e118e95896fac5239c7a8b529c39a4758Jakub Hrozek enum message_search_flags flags;
4f7f714e118e95896fac5239c7a8b529c39a4758Jakub Hrozek int ret;
4f7f714e118e95896fac5239c7a8b529c39a4758Jakub Hrozek
4f7f714e118e95896fac5239c7a8b529c39a4758Jakub Hrozek if (arg_ctx != NULL)
4f7f714e118e95896fac5239c7a8b529c39a4758Jakub Hrozek return arg_ctx;
4f7f714e118e95896fac5239c7a8b529c39a4758Jakub Hrozek
4f7f714e118e95896fac5239c7a8b529c39a4758Jakub Hrozek flags = (arg->type == SEARCH_BODY || arg->type == SEARCH_BODY_FAST) ?
4f7f714e118e95896fac5239c7a8b529c39a4758Jakub Hrozek MESSAGE_SEARCH_FLAG_SKIP_HEADERS : 0;
4f7f714e118e95896fac5239c7a8b529c39a4758Jakub Hrozek
4f7f714e118e95896fac5239c7a8b529c39a4758Jakub Hrozek ret = message_search_init(arg->value.str,
4f7f714e118e95896fac5239c7a8b529c39a4758Jakub Hrozek ctx->mail_ctx.args->charset, flags,
dff909d473f43a6bd0f0286fa2d279c0ebe945c6Stef Walter &arg_ctx);
dff909d473f43a6bd0f0286fa2d279c0ebe945c6Stef Walter if (ret > 0) {
dff909d473f43a6bd0f0286fa2d279c0ebe945c6Stef Walter arg->context = arg_ctx;
dff909d473f43a6bd0f0286fa2d279c0ebe945c6Stef Walter return arg_ctx;
dff909d473f43a6bd0f0286fa2d279c0ebe945c6Stef Walter }
dff909d473f43a6bd0f0286fa2d279c0ebe945c6Stef Walter if (ret == 0)
dff909d473f43a6bd0f0286fa2d279c0ebe945c6Stef Walter ctx->error = TXT_UNKNOWN_CHARSET;
dff909d473f43a6bd0f0286fa2d279c0ebe945c6Stef Walter else
dff909d473f43a6bd0f0286fa2d279c0ebe945c6Stef Walter ctx->error = TXT_INVALID_SEARCH_KEY;
1319e71fd1680ca4864afe0b1aca2b8c8e4a1ee4Stef Walter return NULL;
dff909d473f43a6bd0f0286fa2d279c0ebe945c6Stef Walter}
dff909d473f43a6bd0f0286fa2d279c0ebe945c6Stef Walter
dff909d473f43a6bd0f0286fa2d279c0ebe945c6Stef Walterstatic void compress_lwsp(string_t *dest, const unsigned char *src,
dff909d473f43a6bd0f0286fa2d279c0ebe945c6Stef Walter unsigned int src_len)
dff909d473f43a6bd0f0286fa2d279c0ebe945c6Stef Walter{
dff909d473f43a6bd0f0286fa2d279c0ebe945c6Stef Walter unsigned int i;
dff909d473f43a6bd0f0286fa2d279c0ebe945c6Stef Walter bool prev_lwsp = TRUE;
90e04eae7e54ec892a6f239783df94dab5d1ed9aJakub Hrozek
90e04eae7e54ec892a6f239783df94dab5d1ed9aJakub Hrozek for (i = 0; i < src_len; i++) {
90e04eae7e54ec892a6f239783df94dab5d1ed9aJakub Hrozek if (IS_LWSP(src[i])) {
90e04eae7e54ec892a6f239783df94dab5d1ed9aJakub Hrozek if (!prev_lwsp) {
90e04eae7e54ec892a6f239783df94dab5d1ed9aJakub Hrozek prev_lwsp = TRUE;
90e04eae7e54ec892a6f239783df94dab5d1ed9aJakub Hrozek str_append_c(dest, ' ');
90e04eae7e54ec892a6f239783df94dab5d1ed9aJakub Hrozek }
90e04eae7e54ec892a6f239783df94dab5d1ed9aJakub Hrozek } else {
90e04eae7e54ec892a6f239783df94dab5d1ed9aJakub Hrozek prev_lwsp = FALSE;
90e04eae7e54ec892a6f239783df94dab5d1ed9aJakub Hrozek str_append_c(dest, src[i]);
90e04eae7e54ec892a6f239783df94dab5d1ed9aJakub Hrozek }
90e04eae7e54ec892a6f239783df94dab5d1ed9aJakub Hrozek }
90e04eae7e54ec892a6f239783df94dab5d1ed9aJakub Hrozek}
90e04eae7e54ec892a6f239783df94dab5d1ed9aJakub Hrozek
90e04eae7e54ec892a6f239783df94dab5d1ed9aJakub Hrozekstatic void search_header_arg(struct mail_search_arg *arg,
90e04eae7e54ec892a6f239783df94dab5d1ed9aJakub Hrozek struct search_header_context *ctx)
90e04eae7e54ec892a6f239783df94dab5d1ed9aJakub Hrozek{
90e04eae7e54ec892a6f239783df94dab5d1ed9aJakub Hrozek struct message_search_context *msg_search_ctx;
90e04eae7e54ec892a6f239783df94dab5d1ed9aJakub Hrozek struct message_block block;
90e04eae7e54ec892a6f239783df94dab5d1ed9aJakub Hrozek struct message_header_line hdr;
90e04eae7e54ec892a6f239783df94dab5d1ed9aJakub Hrozek int ret;
dff909d473f43a6bd0f0286fa2d279c0ebe945c6Stef Walter
dff909d473f43a6bd0f0286fa2d279c0ebe945c6Stef Walter /* first check that the field name matches to argument. */
dff909d473f43a6bd0f0286fa2d279c0ebe945c6Stef Walter switch (arg->type) {
dff909d473f43a6bd0f0286fa2d279c0ebe945c6Stef Walter case SEARCH_BEFORE:
dff909d473f43a6bd0f0286fa2d279c0ebe945c6Stef Walter case SEARCH_ON:
dff909d473f43a6bd0f0286fa2d279c0ebe945c6Stef Walter case SEARCH_SINCE:
dff909d473f43a6bd0f0286fa2d279c0ebe945c6Stef Walter if (arg->value.date_type != MAIL_SEARCH_DATE_TYPE_SENT)
dff909d473f43a6bd0f0286fa2d279c0ebe945c6Stef Walter return;
dff909d473f43a6bd0f0286fa2d279c0ebe945c6Stef Walter
dff909d473f43a6bd0f0286fa2d279c0ebe945c6Stef Walter /* date is handled differently than others */
dff909d473f43a6bd0f0286fa2d279c0ebe945c6Stef Walter if (strcasecmp(ctx->hdr->name, "Date") == 0) {
dff909d473f43a6bd0f0286fa2d279c0ebe945c6Stef Walter if (ctx->hdr->continues) {
dff909d473f43a6bd0f0286fa2d279c0ebe945c6Stef Walter ctx->hdr->use_full_value = TRUE;
dff909d473f43a6bd0f0286fa2d279c0ebe945c6Stef Walter return;
dff909d473f43a6bd0f0286fa2d279c0ebe945c6Stef Walter }
dff909d473f43a6bd0f0286fa2d279c0ebe945c6Stef Walter ret = search_sent(arg->type, arg->value.time,
dff909d473f43a6bd0f0286fa2d279c0ebe945c6Stef Walter ctx->hdr->full_value,
dff909d473f43a6bd0f0286fa2d279c0ebe945c6Stef Walter ctx->hdr->full_value_len);
dff909d473f43a6bd0f0286fa2d279c0ebe945c6Stef Walter ARG_SET_RESULT(arg, ret);
dff909d473f43a6bd0f0286fa2d279c0ebe945c6Stef Walter }
dff909d473f43a6bd0f0286fa2d279c0ebe945c6Stef Walter return;
dff909d473f43a6bd0f0286fa2d279c0ebe945c6Stef Walter
dff909d473f43a6bd0f0286fa2d279c0ebe945c6Stef Walter case SEARCH_HEADER:
dff909d473f43a6bd0f0286fa2d279c0ebe945c6Stef Walter case SEARCH_HEADER_ADDRESS:
dff909d473f43a6bd0f0286fa2d279c0ebe945c6Stef Walter case SEARCH_HEADER_COMPRESS_LWSP:
dff909d473f43a6bd0f0286fa2d279c0ebe945c6Stef Walter ctx->custom_header = TRUE;
dff909d473f43a6bd0f0286fa2d279c0ebe945c6Stef Walter
dff909d473f43a6bd0f0286fa2d279c0ebe945c6Stef Walter if (strcasecmp(ctx->hdr->name, arg->hdr_field_name) != 0)
dff909d473f43a6bd0f0286fa2d279c0ebe945c6Stef Walter return;
dff909d473f43a6bd0f0286fa2d279c0ebe945c6Stef Walter break;
dff909d473f43a6bd0f0286fa2d279c0ebe945c6Stef Walter default:
b699c4d7f85a5404be1d1ee9450331aea869b886Stef Walter return;
b699c4d7f85a5404be1d1ee9450331aea869b886Stef Walter }
b699c4d7f85a5404be1d1ee9450331aea869b886Stef Walter
b699c4d7f85a5404be1d1ee9450331aea869b886Stef Walter if (arg->value.str[0] == '\0') {
b699c4d7f85a5404be1d1ee9450331aea869b886Stef Walter /* we're just testing existence of the field. always matches. */
b699c4d7f85a5404be1d1ee9450331aea869b886Stef Walter ARG_SET_RESULT(arg, 1);
dff909d473f43a6bd0f0286fa2d279c0ebe945c6Stef Walter return;
b699c4d7f85a5404be1d1ee9450331aea869b886Stef Walter }
b699c4d7f85a5404be1d1ee9450331aea869b886Stef Walter
b699c4d7f85a5404be1d1ee9450331aea869b886Stef Walter if (ctx->hdr->continues) {
b699c4d7f85a5404be1d1ee9450331aea869b886Stef Walter ctx->hdr->use_full_value = TRUE;
b699c4d7f85a5404be1d1ee9450331aea869b886Stef Walter return;
b699c4d7f85a5404be1d1ee9450331aea869b886Stef Walter }
b699c4d7f85a5404be1d1ee9450331aea869b886Stef Walter
b699c4d7f85a5404be1d1ee9450331aea869b886Stef Walter memset(&block, 0, sizeof(block));
b699c4d7f85a5404be1d1ee9450331aea869b886Stef Walter
b699c4d7f85a5404be1d1ee9450331aea869b886Stef Walter /* We're searching only for values, so drop header name and middle
dff909d473f43a6bd0f0286fa2d279c0ebe945c6Stef Walter parts. We use header searching so that MIME words will be decoded. */
dff909d473f43a6bd0f0286fa2d279c0ebe945c6Stef Walter hdr = *ctx->hdr;
dff909d473f43a6bd0f0286fa2d279c0ebe945c6Stef Walter hdr.name = ""; hdr.name_len = 0;
b699c4d7f85a5404be1d1ee9450331aea869b886Stef Walter hdr.middle_len = 0;
b699c4d7f85a5404be1d1ee9450331aea869b886Stef Walter block.hdr = &hdr;
b699c4d7f85a5404be1d1ee9450331aea869b886Stef Walter
b699c4d7f85a5404be1d1ee9450331aea869b886Stef Walter msg_search_ctx = msg_search_arg_context(ctx->index_context, arg);
b699c4d7f85a5404be1d1ee9450331aea869b886Stef Walter if (msg_search_ctx == NULL)
b699c4d7f85a5404be1d1ee9450331aea869b886Stef Walter return;
b699c4d7f85a5404be1d1ee9450331aea869b886Stef Walter
b699c4d7f85a5404be1d1ee9450331aea869b886Stef Walter T_BEGIN {
b699c4d7f85a5404be1d1ee9450331aea869b886Stef Walter struct message_address *addr;
b699c4d7f85a5404be1d1ee9450331aea869b886Stef Walter string_t *str;
b699c4d7f85a5404be1d1ee9450331aea869b886Stef Walter
b699c4d7f85a5404be1d1ee9450331aea869b886Stef Walter switch (arg->type) {
b699c4d7f85a5404be1d1ee9450331aea869b886Stef Walter case SEARCH_HEADER:
b699c4d7f85a5404be1d1ee9450331aea869b886Stef Walter /* simple match */
fcd8093c58638dc7c4f9cddfc97f273b94ce2eadStef Walter break;
dff909d473f43a6bd0f0286fa2d279c0ebe945c6Stef Walter case SEARCH_HEADER_ADDRESS:
dff909d473f43a6bd0f0286fa2d279c0ebe945c6Stef Walter /* we have to match against normalized address */
dff909d473f43a6bd0f0286fa2d279c0ebe945c6Stef Walter addr = message_address_parse(pool_datastack_create(),
dff909d473f43a6bd0f0286fa2d279c0ebe945c6Stef Walter ctx->hdr->full_value,
b699c4d7f85a5404be1d1ee9450331aea869b886Stef Walter ctx->hdr->full_value_len,
b699c4d7f85a5404be1d1ee9450331aea869b886Stef Walter (unsigned int)-1, TRUE);
b699c4d7f85a5404be1d1ee9450331aea869b886Stef Walter str = t_str_new(ctx->hdr->value_len);
b699c4d7f85a5404be1d1ee9450331aea869b886Stef Walter message_address_write(str, addr);
b699c4d7f85a5404be1d1ee9450331aea869b886Stef Walter hdr.value = hdr.full_value = str_data(str);
b699c4d7f85a5404be1d1ee9450331aea869b886Stef Walter hdr.value_len = hdr.full_value_len = str_len(str);
b699c4d7f85a5404be1d1ee9450331aea869b886Stef Walter break;
b699c4d7f85a5404be1d1ee9450331aea869b886Stef Walter case SEARCH_HEADER_COMPRESS_LWSP:
b699c4d7f85a5404be1d1ee9450331aea869b886Stef Walter /* convert LWSP to single spaces */
b699c4d7f85a5404be1d1ee9450331aea869b886Stef Walter str = t_str_new(hdr.full_value_len);
b699c4d7f85a5404be1d1ee9450331aea869b886Stef Walter compress_lwsp(str, hdr.full_value, hdr.full_value_len);
b699c4d7f85a5404be1d1ee9450331aea869b886Stef Walter hdr.value = hdr.full_value = str_data(str);
b699c4d7f85a5404be1d1ee9450331aea869b886Stef Walter hdr.value_len = hdr.full_value_len = str_len(str);
b699c4d7f85a5404be1d1ee9450331aea869b886Stef Walter break;
b699c4d7f85a5404be1d1ee9450331aea869b886Stef Walter default:
b699c4d7f85a5404be1d1ee9450331aea869b886Stef Walter i_unreached();
b699c4d7f85a5404be1d1ee9450331aea869b886Stef Walter }
b699c4d7f85a5404be1d1ee9450331aea869b886Stef Walter ret = message_search_more(msg_search_ctx, &block) ? 1 : 0;
b699c4d7f85a5404be1d1ee9450331aea869b886Stef Walter } T_END;
b699c4d7f85a5404be1d1ee9450331aea869b886Stef Walter
b699c4d7f85a5404be1d1ee9450331aea869b886Stef Walter /* there may be multiple headers. don't mark this failed yet. */
b699c4d7f85a5404be1d1ee9450331aea869b886Stef Walter if (ret > 0)
b699c4d7f85a5404be1d1ee9450331aea869b886Stef Walter ARG_SET_RESULT(arg, 1);
b699c4d7f85a5404be1d1ee9450331aea869b886Stef Walter}
b699c4d7f85a5404be1d1ee9450331aea869b886Stef Walter
b699c4d7f85a5404be1d1ee9450331aea869b886Stef Walterstatic void search_header_unmatch(struct mail_search_arg *arg,
90e04eae7e54ec892a6f239783df94dab5d1ed9aJakub Hrozek void *context ATTR_UNUSED)
b699c4d7f85a5404be1d1ee9450331aea869b886Stef Walter{
b699c4d7f85a5404be1d1ee9450331aea869b886Stef Walter switch (arg->type) {
b699c4d7f85a5404be1d1ee9450331aea869b886Stef Walter case SEARCH_BEFORE:
b699c4d7f85a5404be1d1ee9450331aea869b886Stef Walter case SEARCH_ON:
1319e71fd1680ca4864afe0b1aca2b8c8e4a1ee4Stef Walter case SEARCH_SINCE:
b699c4d7f85a5404be1d1ee9450331aea869b886Stef Walter if (arg->value.date_type != MAIL_SEARCH_DATE_TYPE_SENT)
b699c4d7f85a5404be1d1ee9450331aea869b886Stef Walter break;
b699c4d7f85a5404be1d1ee9450331aea869b886Stef Walter
b699c4d7f85a5404be1d1ee9450331aea869b886Stef Walter if (arg->not) {
b699c4d7f85a5404be1d1ee9450331aea869b886Stef Walter /* date header not found, so we match only for
b699c4d7f85a5404be1d1ee9450331aea869b886Stef Walter NOT searches */
b699c4d7f85a5404be1d1ee9450331aea869b886Stef Walter ARG_SET_RESULT(arg, 0);
b699c4d7f85a5404be1d1ee9450331aea869b886Stef Walter }
90e04eae7e54ec892a6f239783df94dab5d1ed9aJakub Hrozek break;
90e04eae7e54ec892a6f239783df94dab5d1ed9aJakub Hrozek case SEARCH_HEADER:
90e04eae7e54ec892a6f239783df94dab5d1ed9aJakub Hrozek case SEARCH_HEADER_ADDRESS:
90e04eae7e54ec892a6f239783df94dab5d1ed9aJakub Hrozek case SEARCH_HEADER_COMPRESS_LWSP:
90e04eae7e54ec892a6f239783df94dab5d1ed9aJakub Hrozek ARG_SET_RESULT(arg, 0);
90e04eae7e54ec892a6f239783df94dab5d1ed9aJakub Hrozek break;
90e04eae7e54ec892a6f239783df94dab5d1ed9aJakub Hrozek default:
90e04eae7e54ec892a6f239783df94dab5d1ed9aJakub Hrozek break;
b699c4d7f85a5404be1d1ee9450331aea869b886Stef Walter }
b699c4d7f85a5404be1d1ee9450331aea869b886Stef Walter}
b699c4d7f85a5404be1d1ee9450331aea869b886Stef Walter
b699c4d7f85a5404be1d1ee9450331aea869b886Stef Walterstatic void search_header(struct message_header_line *hdr,
b699c4d7f85a5404be1d1ee9450331aea869b886Stef Walter struct search_header_context *ctx)
b699c4d7f85a5404be1d1ee9450331aea869b886Stef Walter{
b699c4d7f85a5404be1d1ee9450331aea869b886Stef Walter if (hdr == NULL) {
b699c4d7f85a5404be1d1ee9450331aea869b886Stef Walter /* end of headers, mark all unknown SEARCH_HEADERs unmatched */
b699c4d7f85a5404be1d1ee9450331aea869b886Stef Walter mail_search_args_foreach(ctx->args, search_header_unmatch, ctx);
b699c4d7f85a5404be1d1ee9450331aea869b886Stef Walter return;
b699c4d7f85a5404be1d1ee9450331aea869b886Stef Walter }
b699c4d7f85a5404be1d1ee9450331aea869b886Stef Walter
b699c4d7f85a5404be1d1ee9450331aea869b886Stef Walter if (hdr->eoh)
b699c4d7f85a5404be1d1ee9450331aea869b886Stef Walter return;
b699c4d7f85a5404be1d1ee9450331aea869b886Stef Walter
b699c4d7f85a5404be1d1ee9450331aea869b886Stef Walter if (ctx->parse_headers)
b699c4d7f85a5404be1d1ee9450331aea869b886Stef Walter index_mail_parse_header(NULL, hdr, ctx->index_context->imail);
b699c4d7f85a5404be1d1ee9450331aea869b886Stef Walter
b699c4d7f85a5404be1d1ee9450331aea869b886Stef Walter if (ctx->custom_header || strcasecmp(hdr->name, "Date") == 0) {
b699c4d7f85a5404be1d1ee9450331aea869b886Stef Walter ctx->hdr = hdr;
b699c4d7f85a5404be1d1ee9450331aea869b886Stef Walter
b699c4d7f85a5404be1d1ee9450331aea869b886Stef Walter ctx->custom_header = FALSE;
b699c4d7f85a5404be1d1ee9450331aea869b886Stef Walter mail_search_args_foreach(ctx->args, search_header_arg, ctx);
4f7f714e118e95896fac5239c7a8b529c39a4758Jakub Hrozek }
b699c4d7f85a5404be1d1ee9450331aea869b886Stef Walter}
4f7f714e118e95896fac5239c7a8b529c39a4758Jakub Hrozek
4f7f714e118e95896fac5239c7a8b529c39a4758Jakub Hrozekstatic void search_body(struct mail_search_arg *arg,
b699c4d7f85a5404be1d1ee9450331aea869b886Stef Walter struct search_body_context *ctx)
b699c4d7f85a5404be1d1ee9450331aea869b886Stef Walter{
fcd8093c58638dc7c4f9cddfc97f273b94ce2eadStef Walter struct message_search_context *msg_search_ctx;
b699c4d7f85a5404be1d1ee9450331aea869b886Stef Walter int ret;
b699c4d7f85a5404be1d1ee9450331aea869b886Stef Walter
b699c4d7f85a5404be1d1ee9450331aea869b886Stef Walter if (ctx->index_ctx->error != NULL)
b699c4d7f85a5404be1d1ee9450331aea869b886Stef Walter return;
b699c4d7f85a5404be1d1ee9450331aea869b886Stef Walter
b699c4d7f85a5404be1d1ee9450331aea869b886Stef Walter switch (arg->type) {
b699c4d7f85a5404be1d1ee9450331aea869b886Stef Walter case SEARCH_BODY:
b699c4d7f85a5404be1d1ee9450331aea869b886Stef Walter case SEARCH_BODY_FAST:
fcd8093c58638dc7c4f9cddfc97f273b94ce2eadStef Walter case SEARCH_TEXT:
fcd8093c58638dc7c4f9cddfc97f273b94ce2eadStef Walter case SEARCH_TEXT_FAST:
b699c4d7f85a5404be1d1ee9450331aea869b886Stef Walter break;
90e04eae7e54ec892a6f239783df94dab5d1ed9aJakub Hrozek default:
90e04eae7e54ec892a6f239783df94dab5d1ed9aJakub Hrozek return;
dff909d473f43a6bd0f0286fa2d279c0ebe945c6Stef Walter }
b699c4d7f85a5404be1d1ee9450331aea869b886Stef Walter
b699c4d7f85a5404be1d1ee9450331aea869b886Stef Walter msg_search_ctx = msg_search_arg_context(ctx->index_ctx, arg);
b699c4d7f85a5404be1d1ee9450331aea869b886Stef Walter if (msg_search_ctx == NULL) {
b699c4d7f85a5404be1d1ee9450331aea869b886Stef Walter ARG_SET_RESULT(arg, 0);
b699c4d7f85a5404be1d1ee9450331aea869b886Stef Walter return;
b699c4d7f85a5404be1d1ee9450331aea869b886Stef Walter }
b699c4d7f85a5404be1d1ee9450331aea869b886Stef Walter
b699c4d7f85a5404be1d1ee9450331aea869b886Stef Walter i_stream_seek(ctx->input, 0);
b699c4d7f85a5404be1d1ee9450331aea869b886Stef Walter ret = message_search_msg(msg_search_ctx, ctx->input, ctx->part);
b699c4d7f85a5404be1d1ee9450331aea869b886Stef Walter if (ret < 0 && ctx->input->stream_errno == 0) {
b699c4d7f85a5404be1d1ee9450331aea869b886Stef Walter /* try again without cached parts */
b699c4d7f85a5404be1d1ee9450331aea869b886Stef Walter mail_set_cache_corrupted(ctx->index_ctx->mail,
b699c4d7f85a5404be1d1ee9450331aea869b886Stef Walter MAIL_FETCH_MESSAGE_PARTS);
b699c4d7f85a5404be1d1ee9450331aea869b886Stef Walter
4f7f714e118e95896fac5239c7a8b529c39a4758Jakub Hrozek i_stream_seek(ctx->input, 0);
4f7f714e118e95896fac5239c7a8b529c39a4758Jakub Hrozek ret = message_search_msg(msg_search_ctx, ctx->input, NULL);
4f7f714e118e95896fac5239c7a8b529c39a4758Jakub Hrozek i_assert(ret >= 0 || ctx->input->stream_errno != 0);
b699c4d7f85a5404be1d1ee9450331aea869b886Stef Walter }
b699c4d7f85a5404be1d1ee9450331aea869b886Stef Walter
b699c4d7f85a5404be1d1ee9450331aea869b886Stef Walter ARG_SET_RESULT(arg, ret > 0);
90e04eae7e54ec892a6f239783df94dab5d1ed9aJakub Hrozek}
90e04eae7e54ec892a6f239783df94dab5d1ed9aJakub Hrozek
dff909d473f43a6bd0f0286fa2d279c0ebe945c6Stef Walterstatic int search_arg_match_text(struct mail_search_arg *args,
dff909d473f43a6bd0f0286fa2d279c0ebe945c6Stef Walter struct index_search_context *ctx, int ret)
dff909d473f43a6bd0f0286fa2d279c0ebe945c6Stef Walter{
dff909d473f43a6bd0f0286fa2d279c0ebe945c6Stef Walter struct istream *input;
dff909d473f43a6bd0f0286fa2d279c0ebe945c6Stef Walter struct mailbox_header_lookup_ctx *headers_ctx;
dff909d473f43a6bd0f0286fa2d279c0ebe945c6Stef Walter struct mail_search_arg *arg;
dff909d473f43a6bd0f0286fa2d279c0ebe945c6Stef Walter const char *const *headers;
dff909d473f43a6bd0f0286fa2d279c0ebe945c6Stef Walter bool have_headers, have_body;
dff909d473f43a6bd0f0286fa2d279c0ebe945c6Stef Walter
fcd8093c58638dc7c4f9cddfc97f273b94ce2eadStef Walter /* first check what we need to use */
fcd8093c58638dc7c4f9cddfc97f273b94ce2eadStef Walter headers = mail_search_args_analyze(args, &have_headers, &have_body);
fcd8093c58638dc7c4f9cddfc97f273b94ce2eadStef Walter if (!have_headers && !have_body)
fcd8093c58638dc7c4f9cddfc97f273b94ce2eadStef Walter return ret;
fcd8093c58638dc7c4f9cddfc97f273b94ce2eadStef Walter
fcd8093c58638dc7c4f9cddfc97f273b94ce2eadStef Walter if (have_headers) {
fcd8093c58638dc7c4f9cddfc97f273b94ce2eadStef Walter struct search_header_context hdr_ctx;
fcd8093c58638dc7c4f9cddfc97f273b94ce2eadStef Walter
dff909d473f43a6bd0f0286fa2d279c0ebe945c6Stef Walter if (have_body &&
90e04eae7e54ec892a6f239783df94dab5d1ed9aJakub Hrozek ctx->mail->lookup_abort == MAIL_LOOKUP_ABORT_NEVER) {
90e04eae7e54ec892a6f239783df94dab5d1ed9aJakub Hrozek /* just open the mail bypassing any caching, since
fcd8093c58638dc7c4f9cddfc97f273b94ce2eadStef Walter we're going to read through the body anyway */
fcd8093c58638dc7c4f9cddfc97f273b94ce2eadStef Walter headers = NULL;
fcd8093c58638dc7c4f9cddfc97f273b94ce2eadStef Walter }
c2cc119de8eac712c040b3993f41c967ff2278deStef Walter
c2cc119de8eac712c040b3993f41c967ff2278deStef Walter if (headers == NULL) {
c2cc119de8eac712c040b3993f41c967ff2278deStef Walter headers_ctx = NULL;
c2cc119de8eac712c040b3993f41c967ff2278deStef Walter if (mail_get_stream(ctx->mail, NULL, NULL, &input) < 0)
c2cc119de8eac712c040b3993f41c967ff2278deStef Walter return -1;
c2cc119de8eac712c040b3993f41c967ff2278deStef Walter } else {
c2cc119de8eac712c040b3993f41c967ff2278deStef Walter /* FIXME: do this once in init */
c2cc119de8eac712c040b3993f41c967ff2278deStef Walter i_assert(*headers != NULL);
c2cc119de8eac712c040b3993f41c967ff2278deStef Walter headers_ctx =
c2cc119de8eac712c040b3993f41c967ff2278deStef Walter mailbox_header_lookup_init(ctx->box, headers);
c2cc119de8eac712c040b3993f41c967ff2278deStef Walter if (mail_get_header_stream(ctx->mail, headers_ctx,
b699c4d7f85a5404be1d1ee9450331aea869b886Stef Walter &input) < 0) {
b699c4d7f85a5404be1d1ee9450331aea869b886Stef Walter mailbox_header_lookup_unref(&headers_ctx);
b699c4d7f85a5404be1d1ee9450331aea869b886Stef Walter return -1;
b699c4d7f85a5404be1d1ee9450331aea869b886Stef Walter }
b699c4d7f85a5404be1d1ee9450331aea869b886Stef Walter }
b699c4d7f85a5404be1d1ee9450331aea869b886Stef Walter
b699c4d7f85a5404be1d1ee9450331aea869b886Stef Walter memset(&hdr_ctx, 0, sizeof(hdr_ctx));
b699c4d7f85a5404be1d1ee9450331aea869b886Stef Walter hdr_ctx.index_context = ctx;
b699c4d7f85a5404be1d1ee9450331aea869b886Stef Walter hdr_ctx.custom_header = TRUE;
b699c4d7f85a5404be1d1ee9450331aea869b886Stef Walter hdr_ctx.args = args;
b699c4d7f85a5404be1d1ee9450331aea869b886Stef Walter hdr_ctx.parse_headers = headers == NULL &&
c2cc119de8eac712c040b3993f41c967ff2278deStef Walter index_mail_want_parse_headers(ctx->imail);
c2cc119de8eac712c040b3993f41c967ff2278deStef Walter
c2cc119de8eac712c040b3993f41c967ff2278deStef Walter if (hdr_ctx.parse_headers)
c2cc119de8eac712c040b3993f41c967ff2278deStef Walter index_mail_parse_header_init(ctx->imail, headers_ctx);
c2cc119de8eac712c040b3993f41c967ff2278deStef Walter message_parse_header(input, NULL, hdr_parser_flags,
c2cc119de8eac712c040b3993f41c967ff2278deStef Walter search_header, &hdr_ctx);
c2cc119de8eac712c040b3993f41c967ff2278deStef Walter if (headers_ctx != NULL)
c2cc119de8eac712c040b3993f41c967ff2278deStef Walter mailbox_header_lookup_unref(&headers_ctx);
c2cc119de8eac712c040b3993f41c967ff2278deStef Walter } else {
c2cc119de8eac712c040b3993f41c967ff2278deStef Walter struct message_size hdr_size;
fcd8093c58638dc7c4f9cddfc97f273b94ce2eadStef Walter
fcd8093c58638dc7c4f9cddfc97f273b94ce2eadStef Walter if (mail_get_stream(ctx->mail, &hdr_size, NULL, &input) < 0)
dff909d473f43a6bd0f0286fa2d279c0ebe945c6Stef Walter return -1;
fcd8093c58638dc7c4f9cddfc97f273b94ce2eadStef Walter
fcd8093c58638dc7c4f9cddfc97f273b94ce2eadStef Walter i_stream_seek(input, hdr_size.physical_size);
fcd8093c58638dc7c4f9cddfc97f273b94ce2eadStef Walter }
fcd8093c58638dc7c4f9cddfc97f273b94ce2eadStef Walter
fcd8093c58638dc7c4f9cddfc97f273b94ce2eadStef Walter if (have_body) {
dff909d473f43a6bd0f0286fa2d279c0ebe945c6Stef Walter struct search_body_context body_ctx;
fcd8093c58638dc7c4f9cddfc97f273b94ce2eadStef Walter
fcd8093c58638dc7c4f9cddfc97f273b94ce2eadStef Walter if (ctx->mail->lookup_abort != MAIL_LOOKUP_ABORT_NEVER)
dff909d473f43a6bd0f0286fa2d279c0ebe945c6Stef Walter return -1;
dff909d473f43a6bd0f0286fa2d279c0ebe945c6Stef Walter
dff909d473f43a6bd0f0286fa2d279c0ebe945c6Stef Walter memset(&body_ctx, 0, sizeof(body_ctx));
dff909d473f43a6bd0f0286fa2d279c0ebe945c6Stef Walter body_ctx.index_ctx = ctx;
dff909d473f43a6bd0f0286fa2d279c0ebe945c6Stef Walter body_ctx.input = input;
dff909d473f43a6bd0f0286fa2d279c0ebe945c6Stef Walter (void)mail_get_parts(ctx->mail, &body_ctx.part);
fcd8093c58638dc7c4f9cddfc97f273b94ce2eadStef Walter
fcd8093c58638dc7c4f9cddfc97f273b94ce2eadStef Walter ret = mail_search_args_foreach(args, search_body, &body_ctx);
fcd8093c58638dc7c4f9cddfc97f273b94ce2eadStef Walter } else {
90e04eae7e54ec892a6f239783df94dab5d1ed9aJakub Hrozek /* see if we have a decision */
fcd8093c58638dc7c4f9cddfc97f273b94ce2eadStef Walter ret = 1;
dff909d473f43a6bd0f0286fa2d279c0ebe945c6Stef Walter arg = ctx->mail_ctx.args->args;
dff909d473f43a6bd0f0286fa2d279c0ebe945c6Stef Walter for (; arg != NULL; arg = arg->next) {
fcd8093c58638dc7c4f9cddfc97f273b94ce2eadStef Walter if (arg->result == 0) {
b699c4d7f85a5404be1d1ee9450331aea869b886Stef Walter ret = 0;
b699c4d7f85a5404be1d1ee9450331aea869b886Stef Walter break;
b699c4d7f85a5404be1d1ee9450331aea869b886Stef Walter }
b699c4d7f85a5404be1d1ee9450331aea869b886Stef Walter if (arg->result < 0)
b699c4d7f85a5404be1d1ee9450331aea869b886Stef Walter ret = -1;
b699c4d7f85a5404be1d1ee9450331aea869b886Stef Walter }
b699c4d7f85a5404be1d1ee9450331aea869b886Stef Walter }
b699c4d7f85a5404be1d1ee9450331aea869b886Stef Walter return ret;
b699c4d7f85a5404be1d1ee9450331aea869b886Stef Walter}
b699c4d7f85a5404be1d1ee9450331aea869b886Stef Walter
b699c4d7f85a5404be1d1ee9450331aea869b886Stef Walterstatic bool search_msgset_fix_limits(unsigned int messages_count,
b699c4d7f85a5404be1d1ee9450331aea869b886Stef Walter ARRAY_TYPE(seq_range) *seqset, bool not)
b699c4d7f85a5404be1d1ee9450331aea869b886Stef Walter{
b699c4d7f85a5404be1d1ee9450331aea869b886Stef Walter struct seq_range *range;
b699c4d7f85a5404be1d1ee9450331aea869b886Stef Walter unsigned int count;
b699c4d7f85a5404be1d1ee9450331aea869b886Stef Walter
b699c4d7f85a5404be1d1ee9450331aea869b886Stef Walter i_assert(messages_count > 0);
b699c4d7f85a5404be1d1ee9450331aea869b886Stef Walter
b699c4d7f85a5404be1d1ee9450331aea869b886Stef Walter range = array_get_modifiable(seqset, &count);
b699c4d7f85a5404be1d1ee9450331aea869b886Stef Walter if (count > 0) {
b699c4d7f85a5404be1d1ee9450331aea869b886Stef Walter i_assert(range[0].seq1 != 0);
b699c4d7f85a5404be1d1ee9450331aea869b886Stef Walter if (range[count-1].seq2 == (uint32_t)-1) {
b699c4d7f85a5404be1d1ee9450331aea869b886Stef Walter /* "*" used, make sure the last message is in the range
b699c4d7f85a5404be1d1ee9450331aea869b886Stef Walter (e.g. with count+1:* we still want to include it) */
b699c4d7f85a5404be1d1ee9450331aea869b886Stef Walter seq_range_array_add(seqset, 0, messages_count);
b699c4d7f85a5404be1d1ee9450331aea869b886Stef Walter }
b699c4d7f85a5404be1d1ee9450331aea869b886Stef Walter /* remove all nonexistent messages */
b699c4d7f85a5404be1d1ee9450331aea869b886Stef Walter seq_range_array_remove_range(seqset, messages_count + 1,
b699c4d7f85a5404be1d1ee9450331aea869b886Stef Walter (uint32_t)-1);
b699c4d7f85a5404be1d1ee9450331aea869b886Stef Walter }
b699c4d7f85a5404be1d1ee9450331aea869b886Stef Walter if (!not)
b699c4d7f85a5404be1d1ee9450331aea869b886Stef Walter return array_count(seqset) > 0;
b699c4d7f85a5404be1d1ee9450331aea869b886Stef Walter else {
b699c4d7f85a5404be1d1ee9450331aea869b886Stef Walter /* if all messages are in the range, it can't match */
b699c4d7f85a5404be1d1ee9450331aea869b886Stef Walter range = array_get_modifiable(seqset, &count);
b699c4d7f85a5404be1d1ee9450331aea869b886Stef Walter return range[0].seq1 != 1 ||
b699c4d7f85a5404be1d1ee9450331aea869b886Stef Walter range[count-1].seq2 != messages_count;
b699c4d7f85a5404be1d1ee9450331aea869b886Stef Walter }
b699c4d7f85a5404be1d1ee9450331aea869b886Stef Walter}
b699c4d7f85a5404be1d1ee9450331aea869b886Stef Walter
b699c4d7f85a5404be1d1ee9450331aea869b886Stef Walterstatic void search_msgset_fix(unsigned int messages_count,
b699c4d7f85a5404be1d1ee9450331aea869b886Stef Walter ARRAY_TYPE(seq_range) *seqset,
b699c4d7f85a5404be1d1ee9450331aea869b886Stef Walter uint32_t *seq1_r, uint32_t *seq2_r, bool not)
b699c4d7f85a5404be1d1ee9450331aea869b886Stef Walter{
b699c4d7f85a5404be1d1ee9450331aea869b886Stef Walter const struct seq_range *range;
b699c4d7f85a5404be1d1ee9450331aea869b886Stef Walter unsigned int count;
b699c4d7f85a5404be1d1ee9450331aea869b886Stef Walter uint32_t min_seq, max_seq;
b699c4d7f85a5404be1d1ee9450331aea869b886Stef Walter
b699c4d7f85a5404be1d1ee9450331aea869b886Stef Walter if (!search_msgset_fix_limits(messages_count, seqset, not)) {
b699c4d7f85a5404be1d1ee9450331aea869b886Stef Walter *seq1_r = (uint32_t)-1;
b699c4d7f85a5404be1d1ee9450331aea869b886Stef Walter *seq2_r = 0;
b699c4d7f85a5404be1d1ee9450331aea869b886Stef Walter return;
b699c4d7f85a5404be1d1ee9450331aea869b886Stef Walter }
b699c4d7f85a5404be1d1ee9450331aea869b886Stef Walter
b699c4d7f85a5404be1d1ee9450331aea869b886Stef Walter range = array_get(seqset, &count);
b699c4d7f85a5404be1d1ee9450331aea869b886Stef Walter if (!not) {
b699c4d7f85a5404be1d1ee9450331aea869b886Stef Walter min_seq = range[0].seq1;
b699c4d7f85a5404be1d1ee9450331aea869b886Stef Walter max_seq = range[count-1].seq2;
b699c4d7f85a5404be1d1ee9450331aea869b886Stef Walter } else {
b699c4d7f85a5404be1d1ee9450331aea869b886Stef Walter min_seq = range[0].seq1 > 1 ? 1 : range[0].seq2 + 1;
b699c4d7f85a5404be1d1ee9450331aea869b886Stef Walter max_seq = range[count-1].seq2 < messages_count ?
b699c4d7f85a5404be1d1ee9450331aea869b886Stef Walter messages_count : range[count-1].seq1 - 1;
b699c4d7f85a5404be1d1ee9450331aea869b886Stef Walter if (min_seq > max_seq) {
b699c4d7f85a5404be1d1ee9450331aea869b886Stef Walter *seq1_r = (uint32_t)-1;
b699c4d7f85a5404be1d1ee9450331aea869b886Stef Walter *seq2_r = 0;
b699c4d7f85a5404be1d1ee9450331aea869b886Stef Walter return;
b699c4d7f85a5404be1d1ee9450331aea869b886Stef Walter }
b699c4d7f85a5404be1d1ee9450331aea869b886Stef Walter }
b699c4d7f85a5404be1d1ee9450331aea869b886Stef Walter
b699c4d7f85a5404be1d1ee9450331aea869b886Stef Walter if (*seq1_r < min_seq || *seq1_r == 0)
b699c4d7f85a5404be1d1ee9450331aea869b886Stef Walter *seq1_r = min_seq;
b699c4d7f85a5404be1d1ee9450331aea869b886Stef Walter if (*seq2_r > max_seq)
b699c4d7f85a5404be1d1ee9450331aea869b886Stef Walter *seq2_r = max_seq;
b699c4d7f85a5404be1d1ee9450331aea869b886Stef Walter}
b699c4d7f85a5404be1d1ee9450331aea869b886Stef Walter
b699c4d7f85a5404be1d1ee9450331aea869b886Stef Walterstatic void search_or_parse_msgset_args(unsigned int messages_count,
b699c4d7f85a5404be1d1ee9450331aea869b886Stef Walter struct mail_search_arg *args,
b699c4d7f85a5404be1d1ee9450331aea869b886Stef Walter uint32_t *seq1_r, uint32_t *seq2_r)
b699c4d7f85a5404be1d1ee9450331aea869b886Stef Walter{
b699c4d7f85a5404be1d1ee9450331aea869b886Stef Walter uint32_t seq1, seq2, min_seq1 = 0, max_seq2 = 0;
b699c4d7f85a5404be1d1ee9450331aea869b886Stef Walter
b699c4d7f85a5404be1d1ee9450331aea869b886Stef Walter for (; args != NULL; args = args->next) {
b699c4d7f85a5404be1d1ee9450331aea869b886Stef Walter seq1 = 1; seq2 = messages_count;
b699c4d7f85a5404be1d1ee9450331aea869b886Stef Walter
b699c4d7f85a5404be1d1ee9450331aea869b886Stef Walter switch (args->type) {
b699c4d7f85a5404be1d1ee9450331aea869b886Stef Walter case SEARCH_SUB:
b699c4d7f85a5404be1d1ee9450331aea869b886Stef Walter i_assert(!args->not);
b699c4d7f85a5404be1d1ee9450331aea869b886Stef Walter search_parse_msgset_args(messages_count,
b699c4d7f85a5404be1d1ee9450331aea869b886Stef Walter args->value.subargs,
b699c4d7f85a5404be1d1ee9450331aea869b886Stef Walter &seq1, &seq2);
b699c4d7f85a5404be1d1ee9450331aea869b886Stef Walter break;
b699c4d7f85a5404be1d1ee9450331aea869b886Stef Walter case SEARCH_OR:
b699c4d7f85a5404be1d1ee9450331aea869b886Stef Walter i_assert(!args->not);
b699c4d7f85a5404be1d1ee9450331aea869b886Stef Walter search_or_parse_msgset_args(messages_count,
b699c4d7f85a5404be1d1ee9450331aea869b886Stef Walter args->value.subargs,
b699c4d7f85a5404be1d1ee9450331aea869b886Stef Walter &seq1, &seq2);
b699c4d7f85a5404be1d1ee9450331aea869b886Stef Walter break;
b699c4d7f85a5404be1d1ee9450331aea869b886Stef Walter case SEARCH_SEQSET:
b699c4d7f85a5404be1d1ee9450331aea869b886Stef Walter search_msgset_fix(messages_count, &args->value.seqset,
b699c4d7f85a5404be1d1ee9450331aea869b886Stef Walter &seq1, &seq2, args->not);
b699c4d7f85a5404be1d1ee9450331aea869b886Stef Walter break;
b699c4d7f85a5404be1d1ee9450331aea869b886Stef Walter default:
b699c4d7f85a5404be1d1ee9450331aea869b886Stef Walter break;
b699c4d7f85a5404be1d1ee9450331aea869b886Stef Walter }
b699c4d7f85a5404be1d1ee9450331aea869b886Stef Walter
b699c4d7f85a5404be1d1ee9450331aea869b886Stef Walter if (min_seq1 == 0) {
b699c4d7f85a5404be1d1ee9450331aea869b886Stef Walter min_seq1 = seq1;
b699c4d7f85a5404be1d1ee9450331aea869b886Stef Walter max_seq2 = seq2;
b699c4d7f85a5404be1d1ee9450331aea869b886Stef Walter } else {
b699c4d7f85a5404be1d1ee9450331aea869b886Stef Walter if (seq1 < min_seq1)
b699c4d7f85a5404be1d1ee9450331aea869b886Stef Walter min_seq1 = seq1;
b699c4d7f85a5404be1d1ee9450331aea869b886Stef Walter if (seq2 > max_seq2)
b699c4d7f85a5404be1d1ee9450331aea869b886Stef Walter max_seq2 = seq2;
b699c4d7f85a5404be1d1ee9450331aea869b886Stef Walter }
b699c4d7f85a5404be1d1ee9450331aea869b886Stef Walter }
b699c4d7f85a5404be1d1ee9450331aea869b886Stef Walter i_assert(min_seq1 != 0);
b699c4d7f85a5404be1d1ee9450331aea869b886Stef Walter
b699c4d7f85a5404be1d1ee9450331aea869b886Stef Walter if (min_seq1 > *seq1_r)
b699c4d7f85a5404be1d1ee9450331aea869b886Stef Walter *seq1_r = min_seq1;
b699c4d7f85a5404be1d1ee9450331aea869b886Stef Walter if (max_seq2 < *seq2_r)
b699c4d7f85a5404be1d1ee9450331aea869b886Stef Walter *seq2_r = max_seq2;
b699c4d7f85a5404be1d1ee9450331aea869b886Stef Walter}
b699c4d7f85a5404be1d1ee9450331aea869b886Stef Walter
b699c4d7f85a5404be1d1ee9450331aea869b886Stef Walterstatic void search_parse_msgset_args(unsigned int messages_count,
b699c4d7f85a5404be1d1ee9450331aea869b886Stef Walter struct mail_search_arg *args,
b699c4d7f85a5404be1d1ee9450331aea869b886Stef Walter uint32_t *seq1_r, uint32_t *seq2_r)
b699c4d7f85a5404be1d1ee9450331aea869b886Stef Walter{
b699c4d7f85a5404be1d1ee9450331aea869b886Stef Walter for (; args != NULL; args = args->next) {
b699c4d7f85a5404be1d1ee9450331aea869b886Stef Walter switch (args->type) {
b699c4d7f85a5404be1d1ee9450331aea869b886Stef Walter case SEARCH_SUB:
b699c4d7f85a5404be1d1ee9450331aea869b886Stef Walter i_assert(!args->not);
b699c4d7f85a5404be1d1ee9450331aea869b886Stef Walter search_parse_msgset_args(messages_count,
b699c4d7f85a5404be1d1ee9450331aea869b886Stef Walter args->value.subargs,
b699c4d7f85a5404be1d1ee9450331aea869b886Stef Walter seq1_r, seq2_r);
b699c4d7f85a5404be1d1ee9450331aea869b886Stef Walter break;
b699c4d7f85a5404be1d1ee9450331aea869b886Stef Walter case SEARCH_OR:
b699c4d7f85a5404be1d1ee9450331aea869b886Stef Walter /* go through our children and use the widest seqset
b699c4d7f85a5404be1d1ee9450331aea869b886Stef Walter range */
b699c4d7f85a5404be1d1ee9450331aea869b886Stef Walter i_assert(!args->not);
b699c4d7f85a5404be1d1ee9450331aea869b886Stef Walter search_or_parse_msgset_args(messages_count,
b699c4d7f85a5404be1d1ee9450331aea869b886Stef Walter args->value.subargs,
b699c4d7f85a5404be1d1ee9450331aea869b886Stef Walter seq1_r, seq2_r);
b699c4d7f85a5404be1d1ee9450331aea869b886Stef Walter break;
b699c4d7f85a5404be1d1ee9450331aea869b886Stef Walter case SEARCH_SEQSET:
b699c4d7f85a5404be1d1ee9450331aea869b886Stef Walter search_msgset_fix(messages_count, &args->value.seqset,
b699c4d7f85a5404be1d1ee9450331aea869b886Stef Walter seq1_r, seq2_r, args->not);
b699c4d7f85a5404be1d1ee9450331aea869b886Stef Walter break;
b699c4d7f85a5404be1d1ee9450331aea869b886Stef Walter default:
b699c4d7f85a5404be1d1ee9450331aea869b886Stef Walter break;
b699c4d7f85a5404be1d1ee9450331aea869b886Stef Walter }
b699c4d7f85a5404be1d1ee9450331aea869b886Stef Walter }
b699c4d7f85a5404be1d1ee9450331aea869b886Stef Walter}
b699c4d7f85a5404be1d1ee9450331aea869b886Stef Walter
b699c4d7f85a5404be1d1ee9450331aea869b886Stef Walterstatic void search_limit_lowwater(struct index_search_context *ctx,
b699c4d7f85a5404be1d1ee9450331aea869b886Stef Walter uint32_t uid_lowwater, uint32_t *first_seq)
b699c4d7f85a5404be1d1ee9450331aea869b886Stef Walter{
b699c4d7f85a5404be1d1ee9450331aea869b886Stef Walter uint32_t seq1, seq2;
b699c4d7f85a5404be1d1ee9450331aea869b886Stef Walter
b699c4d7f85a5404be1d1ee9450331aea869b886Stef Walter if (uid_lowwater == 0)
b699c4d7f85a5404be1d1ee9450331aea869b886Stef Walter return;
b699c4d7f85a5404be1d1ee9450331aea869b886Stef Walter
dff909d473f43a6bd0f0286fa2d279c0ebe945c6Stef Walter mail_index_lookup_seq_range(ctx->view, uid_lowwater, (uint32_t)-1,
dff909d473f43a6bd0f0286fa2d279c0ebe945c6Stef Walter &seq1, &seq2);
b699c4d7f85a5404be1d1ee9450331aea869b886Stef Walter if (*first_seq < seq1)
b699c4d7f85a5404be1d1ee9450331aea869b886Stef Walter *first_seq = seq1;
b699c4d7f85a5404be1d1ee9450331aea869b886Stef Walter}
b699c4d7f85a5404be1d1ee9450331aea869b886Stef Walter
b699c4d7f85a5404be1d1ee9450331aea869b886Stef Walterstatic bool search_limit_by_flags(struct index_search_context *ctx,
b699c4d7f85a5404be1d1ee9450331aea869b886Stef Walter struct mail_search_arg *args,
b699c4d7f85a5404be1d1ee9450331aea869b886Stef Walter uint32_t *seq1, uint32_t *seq2)
b699c4d7f85a5404be1d1ee9450331aea869b886Stef Walter{
b699c4d7f85a5404be1d1ee9450331aea869b886Stef Walter const struct mail_index_header *hdr;
b699c4d7f85a5404be1d1ee9450331aea869b886Stef Walter
b699c4d7f85a5404be1d1ee9450331aea869b886Stef Walter hdr = mail_index_get_header(ctx->view);
b699c4d7f85a5404be1d1ee9450331aea869b886Stef Walter for (; args != NULL; args = args->next) {
b699c4d7f85a5404be1d1ee9450331aea869b886Stef Walter if (args->type != SEARCH_FLAGS) {
b699c4d7f85a5404be1d1ee9450331aea869b886Stef Walter if (args->type == SEARCH_ALL) {
b699c4d7f85a5404be1d1ee9450331aea869b886Stef Walter if (args->not)
fcd8093c58638dc7c4f9cddfc97f273b94ce2eadStef Walter return FALSE;
fcd8093c58638dc7c4f9cddfc97f273b94ce2eadStef Walter }
fcd8093c58638dc7c4f9cddfc97f273b94ce2eadStef Walter continue;
fcd8093c58638dc7c4f9cddfc97f273b94ce2eadStef Walter }
b699c4d7f85a5404be1d1ee9450331aea869b886Stef Walter if ((args->value.flags & MAIL_SEEN) != 0) {
b699c4d7f85a5404be1d1ee9450331aea869b886Stef Walter /* SEEN with 0 seen? */
b699c4d7f85a5404be1d1ee9450331aea869b886Stef Walter if (!args->not && hdr->seen_messages_count == 0)
b699c4d7f85a5404be1d1ee9450331aea869b886Stef Walter return FALSE;
b699c4d7f85a5404be1d1ee9450331aea869b886Stef Walter
b699c4d7f85a5404be1d1ee9450331aea869b886Stef Walter if (hdr->seen_messages_count == hdr->messages_count) {
b699c4d7f85a5404be1d1ee9450331aea869b886Stef Walter /* UNSEEN with all seen? */
b699c4d7f85a5404be1d1ee9450331aea869b886Stef Walter if (args->not)
b699c4d7f85a5404be1d1ee9450331aea869b886Stef Walter return FALSE;
b699c4d7f85a5404be1d1ee9450331aea869b886Stef Walter
b699c4d7f85a5404be1d1ee9450331aea869b886Stef Walter /* SEEN with all seen */
b699c4d7f85a5404be1d1ee9450331aea869b886Stef Walter args->match_always = TRUE;
b699c4d7f85a5404be1d1ee9450331aea869b886Stef Walter } else if (args->not) {
b699c4d7f85a5404be1d1ee9450331aea869b886Stef Walter /* UNSEEN with lowwater limiting */
b699c4d7f85a5404be1d1ee9450331aea869b886Stef Walter search_limit_lowwater(ctx,
b699c4d7f85a5404be1d1ee9450331aea869b886Stef Walter hdr->first_unseen_uid_lowwater, seq1);
b699c4d7f85a5404be1d1ee9450331aea869b886Stef Walter }
b699c4d7f85a5404be1d1ee9450331aea869b886Stef Walter }
b699c4d7f85a5404be1d1ee9450331aea869b886Stef Walter if ((args->value.flags & MAIL_DELETED) != 0) {
b699c4d7f85a5404be1d1ee9450331aea869b886Stef Walter /* DELETED with 0 deleted? */
b699c4d7f85a5404be1d1ee9450331aea869b886Stef Walter if (!args->not && hdr->deleted_messages_count == 0)
b699c4d7f85a5404be1d1ee9450331aea869b886Stef Walter return FALSE;
b699c4d7f85a5404be1d1ee9450331aea869b886Stef Walter
fcd8093c58638dc7c4f9cddfc97f273b94ce2eadStef Walter if (hdr->deleted_messages_count ==
b699c4d7f85a5404be1d1ee9450331aea869b886Stef Walter hdr->messages_count) {
b699c4d7f85a5404be1d1ee9450331aea869b886Stef Walter /* UNDELETED with all deleted? */
b699c4d7f85a5404be1d1ee9450331aea869b886Stef Walter if (args->not)
b699c4d7f85a5404be1d1ee9450331aea869b886Stef Walter return FALSE;
b699c4d7f85a5404be1d1ee9450331aea869b886Stef Walter
b699c4d7f85a5404be1d1ee9450331aea869b886Stef Walter /* DELETED with all deleted */
b699c4d7f85a5404be1d1ee9450331aea869b886Stef Walter args->match_always = TRUE;
b699c4d7f85a5404be1d1ee9450331aea869b886Stef Walter } else if (!args->not) {
b699c4d7f85a5404be1d1ee9450331aea869b886Stef Walter /* DELETED with lowwater limiting */
b699c4d7f85a5404be1d1ee9450331aea869b886Stef Walter search_limit_lowwater(ctx,
b699c4d7f85a5404be1d1ee9450331aea869b886Stef Walter hdr->first_deleted_uid_lowwater, seq1);
b699c4d7f85a5404be1d1ee9450331aea869b886Stef Walter }
b699c4d7f85a5404be1d1ee9450331aea869b886Stef Walter }
b699c4d7f85a5404be1d1ee9450331aea869b886Stef Walter }
b699c4d7f85a5404be1d1ee9450331aea869b886Stef Walter
return *seq1 <= *seq2;
}
static void search_get_seqset(struct index_search_context *ctx,
unsigned int messages_count,
struct mail_search_arg *args)
{
if (messages_count == 0) {
/* no messages, don't check sequence ranges. although we could
give error message then for FETCH, we shouldn't do it for
UID FETCH. */
ctx->seq1 = 1;
ctx->seq2 = 0;
return;
}
ctx->seq1 = 1;
ctx->seq2 = messages_count;
search_parse_msgset_args(messages_count, args, &ctx->seq1, &ctx->seq2);
if (ctx->seq1 == 0) {
ctx->seq1 = 1;
ctx->seq2 = messages_count;
}
if (ctx->seq1 > ctx->seq2) {
/* no matches */
return;
}
/* UNSEEN and DELETED in root search level may limit the range */
if (!search_limit_by_flags(ctx, args, &ctx->seq1, &ctx->seq2)) {
/* no matches */
ctx->seq1 = 1;
ctx->seq2 = 0;
}
}
static int search_build_subthread(struct mail_thread_iterate_context *iter,
ARRAY_TYPE(seq_range) *uids)
{
struct mail_thread_iterate_context *child_iter;
const struct mail_thread_child_node *node;
int ret = 0;
while ((node = mail_thread_iterate_next(iter, &child_iter)) != NULL) {
if (child_iter != NULL) {
if (search_build_subthread(child_iter, uids) < 0)
ret = -1;
}
seq_range_array_add(uids, 0, node->uid);
}
if (mail_thread_iterate_deinit(&iter) < 0)
ret = -1;
return ret;
}
static int search_build_inthread_result(struct index_search_context *ctx,
struct mail_search_arg *arg)
{
struct mail_thread_iterate_context *iter, *child_iter;
const struct mail_thread_child_node *node;
const ARRAY_TYPE(seq_range) *search_uids;
ARRAY_TYPE(seq_range) thread_uids;
int ret = 0;
/* mail_search_args_init() must have been called by now */
i_assert(arg->value.search_args != NULL);
p_array_init(&arg->value.seqset, ctx->mail_ctx.args->pool, 64);
if (mailbox_search_result_build(ctx->mail_ctx.transaction,
arg->value.search_args,
MAILBOX_SEARCH_RESULT_FLAG_UPDATE |
MAILBOX_SEARCH_RESULT_FLAG_QUEUE_SYNC,
&arg->value.search_result) < 0)
return -1;
if (ctx->thread_ctx == NULL) {
/* failed earlier */
return -1;
}
search_uids = mailbox_search_result_get(arg->value.search_result);
if (array_count(search_uids) == 0) {
/* search found nothing - no threads can match */
return 0;
}
t_array_init(&thread_uids, 128);
iter = mail_thread_iterate_init(ctx->thread_ctx,
arg->value.thread_type, FALSE);
while ((node = mail_thread_iterate_next(iter, &child_iter)) != NULL) {
seq_range_array_add(&thread_uids, 0, node->uid);
if (child_iter != NULL) {
if (search_build_subthread(child_iter,
&thread_uids) < 0)
ret = -1;
}
if (seq_range_array_have_common(&thread_uids, search_uids)) {
/* yes, we want this thread */
seq_range_array_merge(&arg->value.seqset, &thread_uids);
}
array_clear(&thread_uids);
}
if (mail_thread_iterate_deinit(&iter) < 0)
ret = -1;
return ret;
}
static int search_build_inthreads(struct index_search_context *ctx,
struct mail_search_arg *arg)
{
int ret = 0;
for (; arg != NULL; arg = arg->next) {
switch (arg->type) {
case SEARCH_OR:
case SEARCH_SUB:
if (search_build_inthreads(ctx, arg->value.subargs) < 0)
ret = -1;
break;
case SEARCH_INTHREAD:
if (search_build_inthread_result(ctx, arg) < 0)
ret = -1;
break;
default:
break;
}
}
return ret;
}
struct mail_search_context *
index_storage_search_init(struct mailbox_transaction_context *t,
struct mail_search_args *args,
const enum mail_sort_type *sort_program)
{
struct index_search_context *ctx;
struct mailbox_status status;
ctx = i_new(struct index_search_context, 1);
ctx->mail_ctx.transaction = t;
ctx->box = t->box;
ctx->view = t->view;
ctx->mail_ctx.args = args;
ctx->mail_ctx.sort_program = index_sort_program_init(t, sort_program);
ctx->next_time_check_cost = SEARCH_INITIAL_MAX_COST;
if (gettimeofday(&ctx->last_nonblock_timeval, NULL) < 0)
i_fatal("gettimeofday() failed: %m");
mailbox_get_status(t->box, STATUS_MESSAGES, &status);
ctx->mail_ctx.progress_max = status.messages;
i_array_init(&ctx->mail_ctx.results, 5);
array_create(&ctx->mail_ctx.module_contexts, default_pool,
sizeof(void *), 5);
mail_search_args_reset(ctx->mail_ctx.args->args, TRUE);
if (args->have_inthreads) {
if (mail_thread_init(t->box, NULL, &ctx->thread_ctx) < 0)
ctx->failed = TRUE;
if (search_build_inthreads(ctx, args->args) < 0)
ctx->failed = TRUE;
}
search_get_seqset(ctx, status.messages, args->args);
(void)mail_search_args_foreach(args->args, search_init_arg, ctx);
/* Need to reset results for match_always cases */
mail_search_args_reset(ctx->mail_ctx.args->args, FALSE);
return &ctx->mail_ctx;
}
static void search_arg_deinit(struct mail_search_arg *arg,
void *context ATTR_UNUSED)
{
struct message_search_context *search_ctx = arg->context;
if (search_ctx != NULL) {
message_search_deinit(&search_ctx);
arg->context = NULL;
}
}
int index_storage_search_deinit(struct mail_search_context *_ctx)
{
struct index_search_context *ctx = (struct index_search_context *)_ctx;
int ret;
ret = ctx->failed || ctx->error != NULL ? -1 : 0;
if (ctx->error != NULL) {
mail_storage_set_error(ctx->box->storage,
MAIL_ERROR_PARAMS, ctx->error);
}
mail_search_args_reset(ctx->mail_ctx.args->args, FALSE);
(void)mail_search_args_foreach(ctx->mail_ctx.args->args,
search_arg_deinit, NULL);
if (ctx->mail_ctx.sort_program != NULL)
index_sort_program_deinit(&ctx->mail_ctx.sort_program);
if (ctx->thread_ctx != NULL)
mail_thread_deinit(&ctx->thread_ctx);
array_free(&ctx->mail_ctx.results);
array_free(&ctx->mail_ctx.module_contexts);
i_free(ctx);
return ret;
}
static bool search_match_next(struct index_search_context *ctx)
{
static enum mail_lookup_abort cache_lookups[] = {
MAIL_LOOKUP_ABORT_NOT_IN_CACHE,
MAIL_LOOKUP_ABORT_READ_MAIL,
MAIL_LOOKUP_ABORT_NEVER
};
unsigned int i;
int ret = -1;
if (ctx->have_mailbox_args) {
ret = mail_search_args_foreach(ctx->mail_ctx.args->args,
search_mailbox_arg, ctx);
if (ret >= 0)
return ret > 0;
}
/* try to avoid doing extra work for as long as possible */
for (i = 0; i < N_ELEMENTS(cache_lookups) && ret < 0; i++) {
ctx->mail->lookup_abort = cache_lookups[i];
ret = mail_search_args_foreach(ctx->mail_ctx.args->args,
search_cached_arg, ctx);
if (ret >= 0)
break;
ret = search_arg_match_text(ctx->mail_ctx.args->args, ctx, ret);
if (ret >= 0)
break;
}
ctx->mail->lookup_abort = MAIL_LOOKUP_ABORT_NEVER;
return ret > 0;
}
static void index_storage_search_notify(struct mailbox *box,
struct index_search_context *ctx)
{
float percentage;
unsigned int msecs, secs;
if (ctx->last_notify.tv_sec == 0) {
/* set the search time in here, in case a plugin
already spent some time indexing the mailbox */
ctx->search_start_time = ioloop_timeval;
} else if (box->storage->callbacks.notify_ok != NULL &&
!ctx->mail_ctx.progress_hidden) {
percentage = ctx->mail_ctx.progress_cur * 100.0 /
ctx->mail_ctx.progress_max;
msecs = timeval_diff_msecs(&ioloop_timeval,
&ctx->search_start_time);
secs = (msecs / (percentage / 100.0) - msecs) / 1000;
T_BEGIN {
const char *text;
text = t_strdup_printf("Searched %d%% of the mailbox, "
"ETA %d:%02d", (int)percentage,
secs/60, secs%60);
box->storage->callbacks.
notify_ok(box, text,
box->storage->callback_context);
} T_END;
}
ctx->last_notify = ioloop_timeval;
}
static bool search_arg_is_static(struct mail_search_arg *arg)
{
struct mail_search_arg *subarg;
switch (arg->type) {
case SEARCH_OR:
case SEARCH_SUB:
/* they're static only if all subargs are static */
subarg = arg->value.subargs;
for (; subarg != NULL; subarg = subarg->next) {
if (!search_arg_is_static(subarg))
return FALSE;
}
return TRUE;
case SEARCH_SEQSET:
/* changes between syncs, but we can't really handle this
currently. seqsets should be converted to uidsets first. */
case SEARCH_FLAGS:
case SEARCH_KEYWORDS:
case SEARCH_MODSEQ:
case SEARCH_INTHREAD:
break;
case SEARCH_ALL:
case SEARCH_UIDSET:
case SEARCH_BEFORE:
case SEARCH_ON:
case SEARCH_SINCE:
case SEARCH_SMALLER:
case SEARCH_LARGER:
case SEARCH_HEADER:
case SEARCH_HEADER_ADDRESS:
case SEARCH_HEADER_COMPRESS_LWSP:
case SEARCH_BODY:
case SEARCH_TEXT:
case SEARCH_BODY_FAST:
case SEARCH_TEXT_FAST:
case SEARCH_GUID:
case SEARCH_MAILBOX:
case SEARCH_MAILBOX_GUID:
case SEARCH_MAILBOX_GLOB:
return TRUE;
}
return FALSE;
}
static void search_set_static_matches(struct mail_search_arg *arg)
{
for (; arg != NULL; arg = arg->next) {
if (search_arg_is_static(arg))
arg->result = 1;
}
}
static bool search_has_static_nonmatches(struct mail_search_arg *arg)
{
for (; arg != NULL; arg = arg->next) {
if (arg->result == 0 && search_arg_is_static(arg))
return TRUE;
}
return FALSE;
}
static unsigned long long search_mail_get_cost(struct mail_private *mail)
{
return mail->stats_open_lookup_count * SEARCH_COST_DENTRY +
mail->stats_stat_lookup_count * SEARCH_COST_DENTRY +
mail->stats_fstat_lookup_count * SEARCH_COST_ATTR +
mail->stats_cache_hit_count * SEARCH_COST_CACHE +
mail->stats_files_read_count * SEARCH_COST_FILES_READ +
(mail->stats_files_read_bytes/1024) * SEARCH_COST_KBYTE;
}
static bool search_would_block(struct index_search_context *ctx)
{
struct timeval now;
unsigned long long guess_cost;
long long usecs;
bool ret;
if (ctx->cost < ctx->next_time_check_cost)
return FALSE;
if (gettimeofday(&now, NULL) < 0)
i_fatal("gettimeofday() failed: %m");
usecs = timeval_diff_usecs(&now, &ctx->last_nonblock_timeval);
if (usecs < 0) {
/* clock moved backwards. */
ctx->last_nonblock_timeval = now;
ctx->next_time_check_cost = SEARCH_INITIAL_MAX_COST;
return TRUE;
} else if (usecs < SEARCH_MIN_NONBLOCK_USECS) {
/* not finished yet. estimate the next time lookup */
ret = FALSE;
} else {
/* done, or close enough anyway */
ctx->last_nonblock_timeval = now;
ret = TRUE;
}
guess_cost = ctx->cost *
(SEARCH_MAX_NONBLOCK_USECS / (double)usecs);
if (usecs < SEARCH_RECALC_MIN_USECS) {
/* the estimate may not be very good since we spent
so little time doing this search. don't allow huge changes
to the guess, but allow anyway large enough so that we can
move to right direction. */
if (guess_cost > ctx->next_time_check_cost*3)
guess_cost = ctx->next_time_check_cost*3;
else if (guess_cost < ctx->next_time_check_cost/3)
guess_cost = ctx->next_time_check_cost/3;
}
if (ret)
ctx->cost = 0;
ctx->next_time_check_cost = guess_cost;
return ret;
}
bool index_storage_search_next_nonblock(struct mail_search_context *_ctx,
struct mail *mail, bool *tryagain_r)
{
struct index_search_context *ctx = (struct index_search_context *)_ctx;
struct mailbox *box = _ctx->transaction->box;
struct mail_private *mail_private = (struct mail_private *)mail;
unsigned long long cost1, cost2;
bool old_stats_track, match = FALSE;
*tryagain_r = FALSE;
if (ctx->sorted) {
/* everything searched at this point already. just returning
matches from sort list */
if (!index_sort_list_next(ctx->mail_ctx.sort_program, mail))
return FALSE;
return TRUE;
}
if (search_would_block(ctx)) {
/* this lookup is useful when a large number of
messages match */
*tryagain_r = TRUE;
return FALSE;
}
ctx->mail = mail;
if (ioloop_time - ctx->last_notify.tv_sec >=
SEARCH_NOTIFY_INTERVAL_SECS)
index_storage_search_notify(box, ctx);
old_stats_track = mail_private->stats_track;
mail_private->stats_track = TRUE;
cost1 = search_mail_get_cost(mail_private);
while (box->v.search_next_update_seq(_ctx)) {
mail_set_seq(mail, _ctx->seq);
ctx->imail = (struct index_mail *)mail_get_real_mail(mail);
T_BEGIN {
match = search_match_next(ctx);
if (ctx->mail->expunged)
_ctx->seen_lost_data = TRUE;
if (!match &&
search_has_static_nonmatches(_ctx->args->args)) {
/* if there are saved search results remember
that this message never matches */
mailbox_search_results_never(_ctx, mail->uid);
}
} T_END;
cost2 = search_mail_get_cost(mail_private);
ctx->cost += cost2 - cost1;
cost1 = cost2;
mail_search_args_reset(_ctx->args->args, FALSE);
if (ctx->error != NULL)
ctx->failed = TRUE;
else if (match) {
if (_ctx->sort_program == NULL)
break;
index_sort_list_add(_ctx->sort_program, mail);
}
match = FALSE;
if (search_would_block(ctx)) {
*tryagain_r = TRUE;
break;
}
}
ctx->mail = NULL;
ctx->imail = NULL;
mail_private->stats_track = old_stats_track;
if (!match && _ctx->sort_program != NULL &&
!*tryagain_r && !ctx->failed) {
/* finished searching the messages. now sort them and start
returning the messages. */
ctx->sorted = TRUE;
index_sort_list_finish(_ctx->sort_program);
return index_storage_search_next_nonblock(_ctx, mail,
tryagain_r);
}
return !ctx->failed && match;
}
bool index_storage_search_next_update_seq(struct mail_search_context *_ctx)
{
struct index_search_context *ctx = (struct index_search_context *)_ctx;
uint32_t uid;
int ret;
if (_ctx->seq == 0) {
/* first time */
_ctx->seq = ctx->seq1;
} else {
_ctx->seq++;
}
if (!ctx->have_seqsets && !ctx->have_index_args &&
_ctx->update_result == NULL) {
ctx->mail_ctx.progress_cur = _ctx->seq;
return _ctx->seq <= ctx->seq2;
}
ret = 0;
while (_ctx->seq <= ctx->seq2) {
/* check if the sequence matches */
ret = mail_search_args_foreach(ctx->mail_ctx.args->args,
search_seqset_arg, ctx);
if (ret != 0 && ctx->have_index_args) {
/* check if flags/keywords match before anything else
is done. mail_set_seq() can be a bit slow. */
ret = mail_search_args_foreach(ctx->mail_ctx.args->args,
search_index_arg, ctx);
}
if (ret != 0 && _ctx->update_result != NULL) {
/* see if this message never matches */
mail_index_lookup_uid(ctx->view, _ctx->seq, &uid);
if (seq_range_exists(&_ctx->update_result->never_uids,
uid))
ret = 0;
}
if (ret != 0)
break;
/* doesn't, try next one */
_ctx->seq++;
mail_search_args_reset(ctx->mail_ctx.args->args, FALSE);
}
if (ret != 0 && _ctx->update_result != NULL) {
mail_index_lookup_uid(ctx->view, _ctx->seq, &uid);
if (seq_range_exists(&_ctx->update_result->uids, uid)) {
/* we already know that the static data
matches. mark it as such. */
search_set_static_matches(_ctx->args->args);
}
}
ctx->mail_ctx.progress_cur = _ctx->seq;
return ret != 0;
}