index-search.c revision 7fc0f80480063a9d4cb9e8c07b50db2a5627799e
e8058322725ba050014777ee2484f7e833ab1e3aLukas Slebodnik/* Copyright (c) 2002-2010 Dovecot authors, see the included COPYING file */
b699c4d7f85a5404be1d1ee9450331aea869b886Stef Walter#define TXT_UNKNOWN_CHARSET "[BADCHARSET] Unknown charset"
b699c4d7f85a5404be1d1ee9450331aea869b886Stef Walter#define TXT_INVALID_SEARCH_KEY "Invalid search key"
b699c4d7f85a5404be1d1ee9450331aea869b886Stef Walter const char *error;
b699c4d7f85a5404be1d1ee9450331aea869b886Stef Walterstatic const enum message_header_parser_flags hdr_parser_flags =
b699c4d7f85a5404be1d1ee9450331aea869b886Stef Walterstatic void search_parse_msgset_args(unsigned int messages_count,
b699c4d7f85a5404be1d1ee9450331aea869b886Stef Walterstatic void search_init_arg(struct mail_search_arg *arg,
dff909d473f43a6bd0f0286fa2d279c0ebe945c6Stef Walter /* result will be unknown */
dff909d473f43a6bd0f0286fa2d279c0ebe945c6Stef Walterstatic void search_seqset_arg(struct mail_search_arg *arg,
1319e71fd1680ca4864afe0b1aca2b8c8e4a1ee4Stef Walter if (seq_range_exists(&arg->value.seqset, ctx->mail_ctx.seq))
b699c4d7f85a5404be1d1ee9450331aea869b886Stef Walterstatic int search_arg_match_keywords(struct index_search_context *ctx,
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;
dff909d473f43a6bd0f0286fa2d279c0ebe945c6Stef Walter mail_index_lookup_keywords(ctx->view, ctx->mail_ctx.seq,
dff909d473f43a6bd0f0286fa2d279c0ebe945c6Stef Walter keyword_indexes = array_get(&keyword_indexes_arr, &count);
dff909d473f43a6bd0f0286fa2d279c0ebe945c6Stef Walter /* there probably aren't many keywords, so O(n*m) for now */
dff909d473f43a6bd0f0286fa2d279c0ebe945c6Stef Walter for (j = 0; j < count; j++) {
b699c4d7f85a5404be1d1ee9450331aea869b886Stef Walter/* Returns >0 = matched, 0 = not matched, -1 = unknown */
b699c4d7f85a5404be1d1ee9450331aea869b886Stef Walterstatic int search_arg_match_index(struct index_search_context *ctx,
b699c4d7f85a5404be1d1ee9450331aea869b886Stef Walter return seq_range_exists(&arg->value.seqset, rec->uid);
b699c4d7f85a5404be1d1ee9450331aea869b886Stef Walter /* recent flag shouldn't be set, but indexes from v1.0.x
b699c4d7f85a5404be1d1ee9450331aea869b886Stef Walter may contain it. */
90e04eae7e54ec892a6f239783df94dab5d1ed9aJakub Hrozek return (flags & arg->value.flags) == arg->value.flags;
90e04eae7e54ec892a6f239783df94dab5d1ed9aJakub Hrozek modseq = mail_index_modseq_lookup_flags(ctx->view,
b699c4d7f85a5404be1d1ee9450331aea869b886Stef Walter modseq = mail_index_modseq_lookup_keywords(ctx->view,
b699c4d7f85a5404be1d1ee9450331aea869b886Stef Walterstatic void search_index_arg(struct mail_search_arg *arg,
dff909d473f43a6bd0f0286fa2d279c0ebe945c6Stef Walter rec = mail_index_lookup(ctx->view, ctx->mail_ctx.seq);
dff909d473f43a6bd0f0286fa2d279c0ebe945c6Stef Walter switch (search_arg_match_index(ctx, arg, rec)) {
dff909d473f43a6bd0f0286fa2d279c0ebe945c6Stef Walter /* unknown */
dff909d473f43a6bd0f0286fa2d279c0ebe945c6Stef Walter/* Returns >0 = matched, 0 = not matched, -1 = unknown */
dff909d473f43a6bd0f0286fa2d279c0ebe945c6Stef Walterstatic int search_arg_match_mailbox(struct index_search_context *ctx,
dff909d473f43a6bd0f0286fa2d279c0ebe945c6Stef Walter const char *str;
dff909d473f43a6bd0f0286fa2d279c0ebe945c6Stef Walter if (mail_get_special(ctx->mail, MAIL_FETCH_MAILBOX_NAME,
dff909d473f43a6bd0f0286fa2d279c0ebe945c6Stef Walter return strcasecmp(arg->value.str, "INBOX") == 0;
90e04eae7e54ec892a6f239783df94dab5d1ed9aJakub Hrozek if (mail_get_special(ctx->mail, MAIL_FETCH_MAILBOX_NAME,
1319e71fd1680ca4864afe0b1aca2b8c8e4a1ee4Stef Walter return imap_match(arg->value.mailbox_glob, str) == IMAP_MATCH_YES;
dff909d473f43a6bd0f0286fa2d279c0ebe945c6Stef Walterstatic void search_mailbox_arg(struct mail_search_arg *arg,
dff909d473f43a6bd0f0286fa2d279c0ebe945c6Stef Walter /* unknown */
dff909d473f43a6bd0f0286fa2d279c0ebe945c6Stef Walter/* Returns >0 = matched, 0 = not matched, -1 = unknown */
dff909d473f43a6bd0f0286fa2d279c0ebe945c6Stef Walterstatic int search_arg_match_cached(struct index_search_context *ctx,
dff909d473f43a6bd0f0286fa2d279c0ebe945c6Stef Walter const char *str;
dff909d473f43a6bd0f0286fa2d279c0ebe945c6Stef Walter /* internal dates */
dff909d473f43a6bd0f0286fa2d279c0ebe945c6Stef Walter have_tz_offset = FALSE; tz_offset = 0; date = (time_t)-1;
4f7f714e118e95896fac5239c7a8b529c39a4758Jakub Hrozek if (mail_get_date(ctx->mail, &date, &tz_offset) < 0)
4f7f714e118e95896fac5239c7a8b529c39a4758Jakub Hrozek if (mail_get_received_date(ctx->mail, &date) < 0)
66277b21179d95f6e96abed01a20ccbccf27ce99Pavel Březina /* unreachable */
58229439447d5617913a5a2e173b78105c694842Pavel Březina if (mail_get_virtual_size(ctx->mail, &virtual_size) < 0)
1319e71fd1680ca4864afe0b1aca2b8c8e4a1ee4Stef Walter if (mail_get_special(ctx->mail, MAIL_FETCH_GUID, &str) < 0)
4f7f714e118e95896fac5239c7a8b529c39a4758Jakub Hrozekstatic void search_cached_arg(struct mail_search_arg *arg,
4f7f714e118e95896fac5239c7a8b529c39a4758Jakub Hrozek /* unknown */
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 /* 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 Hrozekmsg_search_arg_context(struct index_search_context *ctx,
4f7f714e118e95896fac5239c7a8b529c39a4758Jakub Hrozek struct message_search_context *arg_ctx = arg->context;
4f7f714e118e95896fac5239c7a8b529c39a4758Jakub Hrozek flags = (arg->type == SEARCH_BODY || arg->type == SEARCH_BODY_FAST) ?
dff909d473f43a6bd0f0286fa2d279c0ebe945c6Stef Walterstatic void compress_lwsp(string_t *dest, const unsigned char *src,
dff909d473f43a6bd0f0286fa2d279c0ebe945c6Stef Walter unsigned int src_len)
dff909d473f43a6bd0f0286fa2d279c0ebe945c6Stef Walter unsigned int i;
90e04eae7e54ec892a6f239783df94dab5d1ed9aJakub Hrozek for (i = 0; i < src_len; i++) {
90e04eae7e54ec892a6f239783df94dab5d1ed9aJakub Hrozekstatic void search_header_arg(struct mail_search_arg *arg,
dff909d473f43a6bd0f0286fa2d279c0ebe945c6Stef Walter /* first check that the field name matches to argument. */
dff909d473f43a6bd0f0286fa2d279c0ebe945c6Stef Walter if (arg->value.date_type != MAIL_SEARCH_DATE_TYPE_SENT)
dff909d473f43a6bd0f0286fa2d279c0ebe945c6Stef Walter /* date is handled differently than others */
dff909d473f43a6bd0f0286fa2d279c0ebe945c6Stef Walter if (strcasecmp(ctx->hdr->name, arg->hdr_field_name) != 0)
b699c4d7f85a5404be1d1ee9450331aea869b886Stef Walter /* we're just testing existence of the field. always matches. */
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. */
b699c4d7f85a5404be1d1ee9450331aea869b886Stef Walter msg_search_ctx = msg_search_arg_context(ctx->index_context, arg);
b699c4d7f85a5404be1d1ee9450331aea869b886Stef Walter /* simple match */
dff909d473f43a6bd0f0286fa2d279c0ebe945c6Stef Walter /* we have to match against normalized address */
dff909d473f43a6bd0f0286fa2d279c0ebe945c6Stef Walter addr = message_address_parse(pool_datastack_create(),
b699c4d7f85a5404be1d1ee9450331aea869b886Stef Walter hdr.value_len = hdr.full_value_len = str_len(str);
b699c4d7f85a5404be1d1ee9450331aea869b886Stef Walter /* convert LWSP to single spaces */
b699c4d7f85a5404be1d1ee9450331aea869b886Stef Walter compress_lwsp(str, hdr.full_value, hdr.full_value_len);
b699c4d7f85a5404be1d1ee9450331aea869b886Stef Walter hdr.value_len = hdr.full_value_len = str_len(str);
b699c4d7f85a5404be1d1ee9450331aea869b886Stef Walter ret = message_search_more(msg_search_ctx, &block) ? 1 : 0;
b699c4d7f85a5404be1d1ee9450331aea869b886Stef Walter /* there may be multiple headers. don't mark this failed yet. */
b699c4d7f85a5404be1d1ee9450331aea869b886Stef Walterstatic void search_header_unmatch(struct mail_search_arg *arg,
b699c4d7f85a5404be1d1ee9450331aea869b886Stef Walter if (arg->value.date_type != MAIL_SEARCH_DATE_TYPE_SENT)
b699c4d7f85a5404be1d1ee9450331aea869b886Stef Walter /* date header not found, so we match only for
b699c4d7f85a5404be1d1ee9450331aea869b886Stef Walter NOT searches */
b699c4d7f85a5404be1d1ee9450331aea869b886Stef Walterstatic void search_header(struct message_header_line *hdr,
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 index_mail_parse_header(NULL, hdr, ctx->index_context->imail);
b699c4d7f85a5404be1d1ee9450331aea869b886Stef Walter if (ctx->custom_header || strcasecmp(hdr->name, "Date") == 0) {
b699c4d7f85a5404be1d1ee9450331aea869b886Stef Walter mail_search_args_foreach(ctx->args, search_header_arg, ctx);
4f7f714e118e95896fac5239c7a8b529c39a4758Jakub Hrozekstatic void search_body(struct mail_search_arg *arg,
b699c4d7f85a5404be1d1ee9450331aea869b886Stef Walter msg_search_ctx = msg_search_arg_context(ctx->index_ctx, arg);
b699c4d7f85a5404be1d1ee9450331aea869b886Stef Walter ret = message_search_msg(msg_search_ctx, ctx->input, ctx->part);
b699c4d7f85a5404be1d1ee9450331aea869b886Stef Walter /* try again without cached parts */
4f7f714e118e95896fac5239c7a8b529c39a4758Jakub Hrozek ret = message_search_msg(msg_search_ctx, ctx->input, NULL);
4f7f714e118e95896fac5239c7a8b529c39a4758Jakub Hrozek i_assert(ret >= 0 || ctx->input->stream_errno != 0);
dff909d473f43a6bd0f0286fa2d279c0ebe945c6Stef Walterstatic int search_arg_match_text(struct mail_search_arg *args,
dff909d473f43a6bd0f0286fa2d279c0ebe945c6Stef Walter const char *const *headers;
fcd8093c58638dc7c4f9cddfc97f273b94ce2eadStef Walter /* first check what we need to use */
fcd8093c58638dc7c4f9cddfc97f273b94ce2eadStef Walter headers = mail_search_args_analyze(args, &have_headers, &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 */
c2cc119de8eac712c040b3993f41c967ff2278deStef Walter if (mail_get_stream(ctx->mail, NULL, NULL, &input) < 0)
c2cc119de8eac712c040b3993f41c967ff2278deStef Walter /* FIXME: do this once in init */
c2cc119de8eac712c040b3993f41c967ff2278deStef Walter if (mail_get_header_stream(ctx->mail, headers_ctx,
c2cc119de8eac712c040b3993f41c967ff2278deStef Walter index_mail_parse_header_init(ctx->imail, headers_ctx);
c2cc119de8eac712c040b3993f41c967ff2278deStef Walter message_parse_header(input, NULL, hdr_parser_flags,
fcd8093c58638dc7c4f9cddfc97f273b94ce2eadStef Walter if (mail_get_stream(ctx->mail, &hdr_size, NULL, &input) < 0)
fcd8093c58638dc7c4f9cddfc97f273b94ce2eadStef Walter if (ctx->mail->lookup_abort != MAIL_LOOKUP_ABORT_NEVER)
dff909d473f43a6bd0f0286fa2d279c0ebe945c6Stef Walter (void)mail_get_parts(ctx->mail, &body_ctx.part);
fcd8093c58638dc7c4f9cddfc97f273b94ce2eadStef Walter ret = mail_search_args_foreach(args, search_body, &body_ctx);
90e04eae7e54ec892a6f239783df94dab5d1ed9aJakub Hrozek /* see if we have a decision */
b699c4d7f85a5404be1d1ee9450331aea869b886Stef Walterstatic bool search_msgset_fix_limits(unsigned int messages_count,
b699c4d7f85a5404be1d1ee9450331aea869b886Stef Walter unsigned int count;
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 /* remove all nonexistent messages */
b699c4d7f85a5404be1d1ee9450331aea869b886Stef Walter seq_range_array_remove_range(seqset, messages_count + 1,
b699c4d7f85a5404be1d1ee9450331aea869b886Stef Walter /* if all messages are in the range, it can't match */
b699c4d7f85a5404be1d1ee9450331aea869b886Stef Walterstatic void search_msgset_fix(unsigned int messages_count,
b699c4d7f85a5404be1d1ee9450331aea869b886Stef Walter unsigned int count;
b699c4d7f85a5404be1d1ee9450331aea869b886Stef Walter if (!search_msgset_fix_limits(messages_count, seqset, not)) {
b699c4d7f85a5404be1d1ee9450331aea869b886Stef Walter min_seq = range[0].seq1 > 1 ? 1 : range[0].seq2 + 1;
b699c4d7f85a5404be1d1ee9450331aea869b886Stef Walter max_seq = range[count-1].seq2 < messages_count ?
b699c4d7f85a5404be1d1ee9450331aea869b886Stef Walterstatic void search_or_parse_msgset_args(unsigned int messages_count,
b699c4d7f85a5404be1d1ee9450331aea869b886Stef Walter uint32_t seq1, seq2, min_seq1 = 0, max_seq2 = 0;
b699c4d7f85a5404be1d1ee9450331aea869b886Stef Walter search_msgset_fix(messages_count, &args->value.seqset,
b699c4d7f85a5404be1d1ee9450331aea869b886Stef Walterstatic void search_parse_msgset_args(unsigned int messages_count,
b699c4d7f85a5404be1d1ee9450331aea869b886Stef Walter /* go through our children and use the widest seqset
b699c4d7f85a5404be1d1ee9450331aea869b886Stef Walter search_msgset_fix(messages_count, &args->value.seqset,
b699c4d7f85a5404be1d1ee9450331aea869b886Stef Walterstatic void search_limit_lowwater(struct index_search_context *ctx,
dff909d473f43a6bd0f0286fa2d279c0ebe945c6Stef Walter mail_index_lookup_seq_range(ctx->view, uid_lowwater, (uint32_t)-1,
b699c4d7f85a5404be1d1ee9450331aea869b886Stef Walterstatic bool search_limit_by_flags(struct index_search_context *ctx,
b699c4d7f85a5404be1d1ee9450331aea869b886Stef Walter /* SEEN with 0 seen? */
b699c4d7f85a5404be1d1ee9450331aea869b886Stef Walter if (!args->not && hdr->seen_messages_count == 0)
b699c4d7f85a5404be1d1ee9450331aea869b886Stef Walter if (hdr->seen_messages_count == hdr->messages_count) {
b699c4d7f85a5404be1d1ee9450331aea869b886Stef Walter /* UNSEEN with all seen? */
b699c4d7f85a5404be1d1ee9450331aea869b886Stef Walter /* SEEN with all seen */
b699c4d7f85a5404be1d1ee9450331aea869b886Stef Walter /* UNSEEN with lowwater limiting */
b699c4d7f85a5404be1d1ee9450331aea869b886Stef Walter /* DELETED with 0 deleted? */
b699c4d7f85a5404be1d1ee9450331aea869b886Stef Walter if (!args->not && hdr->deleted_messages_count == 0)
b699c4d7f85a5404be1d1ee9450331aea869b886Stef Walter /* UNDELETED with all deleted? */
b699c4d7f85a5404be1d1ee9450331aea869b886Stef Walter /* DELETED with all deleted */
b699c4d7f85a5404be1d1ee9450331aea869b886Stef Walter /* DELETED with lowwater limiting */
unsigned int messages_count,
if (messages_count == 0) {
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 *
int ret;
return ret;
if (ret >= 0)
return ret > 0;
if (ret >= 0)
if (ret >= 0)
return ret > 0;
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_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:
case SEARCH_MAILBOX_GUID:
case SEARCH_MAILBOX_GLOB:
return TRUE;
return FALSE;
return TRUE;
return FALSE;
unsigned long long guess_cost;
long long usecs;
bool ret;
return FALSE;
if (usecs < 0) {
return TRUE;
if (ret)
return ret;
return FALSE;
return TRUE;
return FALSE;
T_BEGIN {
if (!match &&
} T_END;
else if (match) {
int ret;
ret = 0;
uid))
ret = 0;
if (ret != 0)
return ret != 0;