index-search.c revision 4b0e91d0e48374c7ad23d58ed07070858f6ac086
00e7c3010f7da4a49881a7feb05e413af353af0aTimo Sirainen/* Copyright (c) 2002-2008 Dovecot authors, see the included COPYING file */
00e7c3010f7da4a49881a7feb05e413af353af0aTimo Sirainen#define TXT_UNKNOWN_CHARSET "[BADCHARSET] Unknown charset"
00e7c3010f7da4a49881a7feb05e413af353af0aTimo Sirainen#define TXT_INVALID_SEARCH_KEY "Invalid search key"
7ca63fa4166f89fee900b7c14d87d53fbac47242Timo Sirainen struct timeval search_start_time, last_notify;
00e7c3010f7da4a49881a7feb05e413af353af0aTimo Sirainenstatic const enum message_header_parser_flags hdr_parser_flags =
00e7c3010f7da4a49881a7feb05e413af353af0aTimo Sirainenstatic void search_parse_msgset_args(const struct mail_index_header *hdr,
00e7c3010f7da4a49881a7feb05e413af353af0aTimo Sirainenstatic void search_init_arg(struct mail_search_arg *arg,
00e7c3010f7da4a49881a7feb05e413af353af0aTimo Sirainenstatic void search_seqset_arg(struct mail_search_arg *arg,
7ca63fa4166f89fee900b7c14d87d53fbac47242Timo Sirainen if (seq_range_exists(&arg->value.seqset, ctx->mail_ctx.seq))
7ca63fa4166f89fee900b7c14d87d53fbac47242Timo Sirainenstatic int search_arg_match_keywords(struct index_search_context *ctx,
7ca63fa4166f89fee900b7c14d87d53fbac47242Timo Sirainen ARRAY_TYPE(keyword_indexes) keyword_indexes_arr;
7ca63fa4166f89fee900b7c14d87d53fbac47242Timo Sirainen const struct mail_keywords *search_kws = arg->value.keywords;
00e7c3010f7da4a49881a7feb05e413af353af0aTimo Sirainen const unsigned int *keyword_indexes;
00e7c3010f7da4a49881a7feb05e413af353af0aTimo Sirainen unsigned int i, j, count;
7ca63fa4166f89fee900b7c14d87d53fbac47242Timo Sirainen mail_index_lookup_keywords(ctx->view, ctx->mail_ctx.seq,
00e7c3010f7da4a49881a7feb05e413af353af0aTimo Sirainen keyword_indexes = array_get(&keyword_indexes_arr, &count);
00e7c3010f7da4a49881a7feb05e413af353af0aTimo Sirainen /* there probably aren't many keywords, so O(n*m) for now */
7ca63fa4166f89fee900b7c14d87d53fbac47242Timo Sirainen for (j = 0; j < count; j++) {
7ca63fa4166f89fee900b7c14d87d53fbac47242Timo Sirainen/* Returns >0 = matched, 0 = not matched, -1 = unknown */
7ca63fa4166f89fee900b7c14d87d53fbac47242Timo Sirainenstatic int search_arg_match_index(struct index_search_context *ctx,
00e7c3010f7da4a49881a7feb05e413af353af0aTimo Sirainen return seq_range_exists(&arg->value.seqset, rec->uid);
00e7c3010f7da4a49881a7feb05e413af353af0aTimo Sirainen /* recent flag shouldn't be set, but indexes from v1.0.x
00e7c3010f7da4a49881a7feb05e413af353af0aTimo Sirainen may contain it. */
00e7c3010f7da4a49881a7feb05e413af353af0aTimo Sirainen return (flags & arg->value.flags) == arg->value.flags;
00e7c3010f7da4a49881a7feb05e413af353af0aTimo Sirainen modseq = mail_index_modseq_lookup_flags(ctx->view,
00e7c3010f7da4a49881a7feb05e413af353af0aTimo Sirainen modseq = mail_index_modseq_lookup_keywords(ctx->view,
00e7c3010f7da4a49881a7feb05e413af353af0aTimo Sirainenstatic void search_index_arg(struct mail_search_arg *arg,
00e7c3010f7da4a49881a7feb05e413af353af0aTimo Sirainen rec = mail_index_lookup(ctx->view, ctx->mail_ctx.seq);
00e7c3010f7da4a49881a7feb05e413af353af0aTimo Sirainen switch (search_arg_match_index(ctx, arg, rec)) {
00e7c3010f7da4a49881a7feb05e413af353af0aTimo Sirainen /* unknown */
00e7c3010f7da4a49881a7feb05e413af353af0aTimo Sirainen/* Returns >0 = matched, 0 = not matched, -1 = unknown */
00e7c3010f7da4a49881a7feb05e413af353af0aTimo Sirainenstatic int search_arg_match_cached(struct index_search_context *ctx,
00e7c3010f7da4a49881a7feb05e413af353af0aTimo Sirainen const char *str;
00e7c3010f7da4a49881a7feb05e413af353af0aTimo Sirainen /* internal dates */
00e7c3010f7da4a49881a7feb05e413af353af0aTimo Sirainen if (mail_get_received_date(ctx->mail, &date) < 0)
00e7c3010f7da4a49881a7feb05e413af353af0aTimo Sirainen /* unreachable */
00e7c3010f7da4a49881a7feb05e413af353af0aTimo Sirainen /* sent dates */
00e7c3010f7da4a49881a7feb05e413af353af0aTimo Sirainen /* NOTE: RFC-3501 specifies that timezone is ignored
00e7c3010f7da4a49881a7feb05e413af353af0aTimo Sirainen in searches. date is returned as UTC, so change it. */
00e7c3010f7da4a49881a7feb05e413af353af0aTimo Sirainen if (mail_get_date(ctx->mail, &date, &timezone_offset) < 0)
00e7c3010f7da4a49881a7feb05e413af353af0aTimo Sirainen /* unreachable */
00e7c3010f7da4a49881a7feb05e413af353af0aTimo Sirainen if (mail_get_virtual_size(ctx->mail, &virtual_size) < 0)
00e7c3010f7da4a49881a7feb05e413af353af0aTimo Sirainen if (mail_get_special(ctx->mail, MAIL_FETCH_GUID, &str) < 0)
00e7c3010f7da4a49881a7feb05e413af353af0aTimo Sirainen if (mail_get_special(ctx->mail, MAIL_FETCH_MAILBOX_NAME,
00e7c3010f7da4a49881a7feb05e413af353af0aTimo Sirainen return strcasecmp(arg->value.str, "INBOX") == 0;
00e7c3010f7da4a49881a7feb05e413af353af0aTimo Sirainenstatic void search_cached_arg(struct mail_search_arg *arg,
00e7c3010f7da4a49881a7feb05e413af353af0aTimo Sirainen /* unknown */
7ca63fa4166f89fee900b7c14d87d53fbac47242Timo Sirainenstatic int search_sent(enum mail_search_arg_type type, time_t search_time,
7ca63fa4166f89fee900b7c14d87d53fbac47242Timo Sirainen const unsigned char *sent_value, size_t sent_value_len)
00e7c3010f7da4a49881a7feb05e413af353af0aTimo Sirainen /* NOTE: RFC-3501 specifies that timezone is ignored
00e7c3010f7da4a49881a7feb05e413af353af0aTimo Sirainen in searches. sent_time is returned as UTC, so change it. */
00e7c3010f7da4a49881a7feb05e413af353af0aTimo Sirainen if (!message_date_parse(sent_value, sent_value_len,
00e7c3010f7da4a49881a7feb05e413af353af0aTimo Sirainenmsg_search_arg_context(struct index_search_context *ctx,
0a53eb0283d7ec28c6105f61e118b96fce8ecb95Timo Sirainen struct message_search_context *arg_ctx = arg->context;
00e7c3010f7da4a49881a7feb05e413af353af0aTimo Sirainen flags = (arg->type == SEARCH_BODY || arg->type == SEARCH_BODY_FAST) ?
00e7c3010f7da4a49881a7feb05e413af353af0aTimo Sirainenstatic void compress_lwsp(string_t *dest, const unsigned char *src,
00e7c3010f7da4a49881a7feb05e413af353af0aTimo Sirainen unsigned int src_len)
00e7c3010f7da4a49881a7feb05e413af353af0aTimo Sirainen unsigned int i;
7ca63fa4166f89fee900b7c14d87d53fbac47242Timo Sirainen for (i = 0; i < src_len; i++) {
00e7c3010f7da4a49881a7feb05e413af353af0aTimo Sirainenstatic void search_header_arg(struct mail_search_arg *arg,
00e7c3010f7da4a49881a7feb05e413af353af0aTimo Sirainen struct message_search_context *msg_search_ctx;
00e7c3010f7da4a49881a7feb05e413af353af0aTimo Sirainen /* first check that the field name matches to argument. */
00e7c3010f7da4a49881a7feb05e413af353af0aTimo Sirainen /* date is handled differently than others */
00e7c3010f7da4a49881a7feb05e413af353af0aTimo Sirainen if (strcasecmp(ctx->hdr->name, "Date") == 0) {
7ca63fa4166f89fee900b7c14d87d53fbac47242Timo Sirainen if (strcasecmp(ctx->hdr->name, arg->hdr_field_name) != 0)
7ca63fa4166f89fee900b7c14d87d53fbac47242Timo Sirainen /* we're just testing existence of the field. always matches. */
0a53eb0283d7ec28c6105f61e118b96fce8ecb95Timo Sirainen /* We're searching only for values, so drop header name and middle
7ca63fa4166f89fee900b7c14d87d53fbac47242Timo Sirainen parts. We use header searching so that MIME words will be decoded. */
7ca63fa4166f89fee900b7c14d87d53fbac47242Timo Sirainen msg_search_ctx = msg_search_arg_context(ctx->index_context, arg);
00e7c3010f7da4a49881a7feb05e413af353af0aTimo Sirainen /* simple match */
00e7c3010f7da4a49881a7feb05e413af353af0aTimo Sirainen /* we have to match against normalized address */
00e7c3010f7da4a49881a7feb05e413af353af0aTimo Sirainen addr = message_address_parse(pool_datastack_create(),
00e7c3010f7da4a49881a7feb05e413af353af0aTimo Sirainen hdr.value_len = hdr.full_value_len = str_len(str);
00e7c3010f7da4a49881a7feb05e413af353af0aTimo Sirainen /* convert LWSP to single spaces */
00e7c3010f7da4a49881a7feb05e413af353af0aTimo Sirainen compress_lwsp(str, hdr.full_value, hdr.full_value_len);
00e7c3010f7da4a49881a7feb05e413af353af0aTimo Sirainen hdr.value_len = hdr.full_value_len = str_len(str);
00e7c3010f7da4a49881a7feb05e413af353af0aTimo Sirainen ret = message_search_more(msg_search_ctx, &block) ? 1 : 0;
0a53eb0283d7ec28c6105f61e118b96fce8ecb95Timo Sirainenstatic void search_header_unmatch(struct mail_search_arg *arg,
00e7c3010f7da4a49881a7feb05e413af353af0aTimo Sirainen /* date header not found, so we match only for
7ca63fa4166f89fee900b7c14d87d53fbac47242Timo Sirainen NOT searches */
7ca63fa4166f89fee900b7c14d87d53fbac47242Timo Sirainenstatic void search_header(struct message_header_line *hdr,
7ca63fa4166f89fee900b7c14d87d53fbac47242Timo Sirainen /* end of headers, mark all unknown SEARCH_HEADERs unmatched */
7ca63fa4166f89fee900b7c14d87d53fbac47242Timo Sirainen mail_search_args_foreach(ctx->args, search_header_unmatch, ctx);
0a53eb0283d7ec28c6105f61e118b96fce8ecb95Timo Sirainen index_mail_parse_header(NULL, hdr, ctx->index_context->imail);
0a53eb0283d7ec28c6105f61e118b96fce8ecb95Timo Sirainen if (ctx->custom_header || strcasecmp(hdr->name, "Date") == 0) {
0a53eb0283d7ec28c6105f61e118b96fce8ecb95Timo Sirainen mail_search_args_foreach(ctx->args, search_header_arg, ctx);
6c961309a97ee0c86b85fa336f5f51662bd3d515Timo Sirainenstatic void search_body(struct mail_search_arg *arg,
6c961309a97ee0c86b85fa336f5f51662bd3d515Timo Sirainen struct message_search_context *msg_search_ctx;
7ca63fa4166f89fee900b7c14d87d53fbac47242Timo Sirainen msg_search_ctx = msg_search_arg_context(ctx->index_ctx, arg);
00e7c3010f7da4a49881a7feb05e413af353af0aTimo Sirainen ret = message_search_msg(msg_search_ctx, ctx->input, ctx->part);
00e7c3010f7da4a49881a7feb05e413af353af0aTimo Sirainen if (ret < 0 && ctx->input->stream_errno == 0) {
00e7c3010f7da4a49881a7feb05e413af353af0aTimo Sirainen /* try again without cached parts */
00e7c3010f7da4a49881a7feb05e413af353af0aTimo Sirainen mail_set_cache_corrupted(ctx->index_ctx->mail,
00e7c3010f7da4a49881a7feb05e413af353af0aTimo Sirainen ret = message_search_msg(msg_search_ctx, ctx->input, NULL);
00e7c3010f7da4a49881a7feb05e413af353af0aTimo Sirainen i_assert(ret >= 0 || ctx->input->stream_errno != 0);
00e7c3010f7da4a49881a7feb05e413af353af0aTimo Sirainenstatic bool search_arg_match_text(struct mail_search_arg *args,
00e7c3010f7da4a49881a7feb05e413af353af0aTimo Sirainen struct mailbox_header_lookup_ctx *headers_ctx;
00e7c3010f7da4a49881a7feb05e413af353af0aTimo Sirainen const char *const *headers;
00e7c3010f7da4a49881a7feb05e413af353af0aTimo Sirainen /* first check what we need to use */
7ca63fa4166f89fee900b7c14d87d53fbac47242Timo Sirainen headers = mail_search_args_analyze(args, &have_headers, &have_body);
00e7c3010f7da4a49881a7feb05e413af353af0aTimo Sirainen if (mail_get_stream(ctx->mail, NULL, NULL, &input) < 0)
7ca63fa4166f89fee900b7c14d87d53fbac47242Timo Sirainen /* FIXME: do this once in init */
00e7c3010f7da4a49881a7feb05e413af353af0aTimo Sirainen if (mail_get_header_stream(ctx->mail, headers_ctx,
return FALSE;
if (have_body) {
return TRUE;
unsigned int count;
if (count > 0) {
if (!not)
unsigned int count;
*seq2_r = 0;
if (!not) {
*seq2_r = 0;
case SEARCH_SUB:
case SEARCH_OR:
case SEARCH_SEQSET:
if (min_seq1 == 0) {
case SEARCH_SUB:
case SEARCH_OR:
case SEARCH_SEQSET:
if (uid_lowwater == 0)
return FALSE;
return FALSE;
return FALSE;
return FALSE;
return FALSE;
int ret = 0;
return ret;
int ret = 0;
&thread_uids) < 0)
return ret;
int ret = 0;
case SEARCH_OR:
case SEARCH_SUB:
case SEARCH_INTHREAD:
return ret;
struct mail_search_context *
struct index_transaction_context *t =
int ret;
return ret;
int ret;
if (ret >= 0)
return ret > 0;
return FALSE;
return FALSE;
return TRUE;
float percentage;
T_BEGIN {
const char *text;
} T_END;
case SEARCH_OR:
case SEARCH_SUB:
return FALSE;
return TRUE;
case SEARCH_SEQSET:
case SEARCH_FLAGS:
case SEARCH_KEYWORDS:
case SEARCH_MODSEQ:
case SEARCH_INTHREAD:
case SEARCH_ALL:
case SEARCH_UIDSET:
case SEARCH_BEFORE:
case SEARCH_ON:
case SEARCH_SINCE:
case SEARCH_SENTBEFORE:
case SEARCH_SENTON:
case SEARCH_SENTSINCE:
case SEARCH_SMALLER:
case SEARCH_LARGER:
case SEARCH_HEADER:
case SEARCH_HEADER_ADDRESS:
case SEARCH_BODY:
case SEARCH_TEXT:
case SEARCH_BODY_FAST:
case SEARCH_TEXT_FAST:
case SEARCH_GUID:
case SEARCH_MAILBOX:
return TRUE;
return FALSE;
return TRUE;
return FALSE;
unsigned int count = 0;
T_BEGIN {
if (!match &&
} T_END;
else if (match) {
int ret;
ret = 0;
uid))
ret = 0;
if (ret != 0)
return ret != 0;