index-search.c revision 4b0e91d0e48374c7ad23d58ed07070858f6ac086
00e7c3010f7da4a49881a7feb05e413af353af0aTimo Sirainen/* Copyright (c) 2002-2008 Dovecot authors, see the included COPYING file */
00e7c3010f7da4a49881a7feb05e413af353af0aTimo Sirainen
00e7c3010f7da4a49881a7feb05e413af353af0aTimo Sirainen#include "lib.h"
00e7c3010f7da4a49881a7feb05e413af353af0aTimo Sirainen#include "ioloop.h"
00e7c3010f7da4a49881a7feb05e413af353af0aTimo Sirainen#include "array.h"
00e7c3010f7da4a49881a7feb05e413af353af0aTimo Sirainen#include "istream.h"
00e7c3010f7da4a49881a7feb05e413af353af0aTimo Sirainen#include "utc-offset.h"
7ca63fa4166f89fee900b7c14d87d53fbac47242Timo Sirainen#include "str.h"
7ca63fa4166f89fee900b7c14d87d53fbac47242Timo Sirainen#include "message-address.h"
7ca63fa4166f89fee900b7c14d87d53fbac47242Timo Sirainen#include "message-date.h"
7ca63fa4166f89fee900b7c14d87d53fbac47242Timo Sirainen#include "message-search.h"
7ca63fa4166f89fee900b7c14d87d53fbac47242Timo Sirainen#include "message-parser.h"
00e7c3010f7da4a49881a7feb05e413af353af0aTimo Sirainen#include "mail-index-modseq.h"
00e7c3010f7da4a49881a7feb05e413af353af0aTimo Sirainen#include "index-storage.h"
00e7c3010f7da4a49881a7feb05e413af353af0aTimo Sirainen#include "index-mail.h"
00e7c3010f7da4a49881a7feb05e413af353af0aTimo Sirainen#include "index-sort.h"
00e7c3010f7da4a49881a7feb05e413af353af0aTimo Sirainen#include "mail-search.h"
00e7c3010f7da4a49881a7feb05e413af353af0aTimo Sirainen#include "mailbox-search-result-private.h"
00e7c3010f7da4a49881a7feb05e413af353af0aTimo Sirainen
00e7c3010f7da4a49881a7feb05e413af353af0aTimo Sirainen#include <stdlib.h>
00e7c3010f7da4a49881a7feb05e413af353af0aTimo Sirainen#include <ctype.h>
00e7c3010f7da4a49881a7feb05e413af353af0aTimo Sirainen
00e7c3010f7da4a49881a7feb05e413af353af0aTimo Sirainen#define TXT_UNKNOWN_CHARSET "[BADCHARSET] Unknown charset"
00e7c3010f7da4a49881a7feb05e413af353af0aTimo Sirainen#define TXT_INVALID_SEARCH_KEY "Invalid search key"
00e7c3010f7da4a49881a7feb05e413af353af0aTimo Sirainen
00e7c3010f7da4a49881a7feb05e413af353af0aTimo Sirainen#define SEARCH_NONBLOCK_COUNT 20
00e7c3010f7da4a49881a7feb05e413af353af0aTimo Sirainen#define SEARCH_NOTIFY_INTERVAL_SECS 10
00e7c3010f7da4a49881a7feb05e413af353af0aTimo Sirainen
00e7c3010f7da4a49881a7feb05e413af353af0aTimo Sirainenstruct index_search_context {
7ca63fa4166f89fee900b7c14d87d53fbac47242Timo Sirainen struct mail_search_context mail_ctx;
7ca63fa4166f89fee900b7c14d87d53fbac47242Timo Sirainen struct mail_index_view *view;
7ca63fa4166f89fee900b7c14d87d53fbac47242Timo Sirainen struct index_mailbox *ibox;
00e7c3010f7da4a49881a7feb05e413af353af0aTimo Sirainen
00e7c3010f7da4a49881a7feb05e413af353af0aTimo Sirainen uint32_t seq1, seq2;
00e7c3010f7da4a49881a7feb05e413af353af0aTimo Sirainen struct mail *mail;
00e7c3010f7da4a49881a7feb05e413af353af0aTimo Sirainen struct index_mail *imail;
7ca63fa4166f89fee900b7c14d87d53fbac47242Timo Sirainen struct mail_thread_context *thread_ctx;
9dcb7a41eaaf832f641b7743060b5cf5ed7c80b3Timo Sirainen
0a53eb0283d7ec28c6105f61e118b96fce8ecb95Timo Sirainen const char *error;
7ca63fa4166f89fee900b7c14d87d53fbac47242Timo Sirainen
7ca63fa4166f89fee900b7c14d87d53fbac47242Timo Sirainen struct timeval search_start_time, last_notify;
7ca63fa4166f89fee900b7c14d87d53fbac47242Timo Sirainen
7ca63fa4166f89fee900b7c14d87d53fbac47242Timo Sirainen unsigned int failed:1;
7ca63fa4166f89fee900b7c14d87d53fbac47242Timo Sirainen unsigned int sorted:1;
7ca63fa4166f89fee900b7c14d87d53fbac47242Timo Sirainen unsigned int have_seqsets:1;
7ca63fa4166f89fee900b7c14d87d53fbac47242Timo Sirainen unsigned int have_index_args:1;
00e7c3010f7da4a49881a7feb05e413af353af0aTimo Sirainen};
00e7c3010f7da4a49881a7feb05e413af353af0aTimo Sirainen
00e7c3010f7da4a49881a7feb05e413af353af0aTimo Sirainenstruct search_header_context {
7ca63fa4166f89fee900b7c14d87d53fbac47242Timo Sirainen struct index_search_context *index_context;
00e7c3010f7da4a49881a7feb05e413af353af0aTimo Sirainen struct mail_search_arg *args;
00e7c3010f7da4a49881a7feb05e413af353af0aTimo Sirainen
00e7c3010f7da4a49881a7feb05e413af353af0aTimo Sirainen struct message_header_line *hdr;
00e7c3010f7da4a49881a7feb05e413af353af0aTimo Sirainen
00e7c3010f7da4a49881a7feb05e413af353af0aTimo Sirainen unsigned int parse_headers:1;
00e7c3010f7da4a49881a7feb05e413af353af0aTimo Sirainen unsigned int custom_header:1;
00e7c3010f7da4a49881a7feb05e413af353af0aTimo Sirainen unsigned int threading:1;
00e7c3010f7da4a49881a7feb05e413af353af0aTimo Sirainen};
00e7c3010f7da4a49881a7feb05e413af353af0aTimo Sirainen
00e7c3010f7da4a49881a7feb05e413af353af0aTimo Sirainenstruct search_body_context {
00e7c3010f7da4a49881a7feb05e413af353af0aTimo Sirainen struct index_search_context *index_ctx;
00e7c3010f7da4a49881a7feb05e413af353af0aTimo Sirainen struct istream *input;
00e7c3010f7da4a49881a7feb05e413af353af0aTimo Sirainen const struct message_part *part;
00e7c3010f7da4a49881a7feb05e413af353af0aTimo Sirainen};
00e7c3010f7da4a49881a7feb05e413af353af0aTimo Sirainen
00e7c3010f7da4a49881a7feb05e413af353af0aTimo Sirainenstatic const enum message_header_parser_flags hdr_parser_flags =
00e7c3010f7da4a49881a7feb05e413af353af0aTimo Sirainen MESSAGE_HEADER_PARSER_FLAG_CLEAN_ONELINE;
00e7c3010f7da4a49881a7feb05e413af353af0aTimo Sirainen
00e7c3010f7da4a49881a7feb05e413af353af0aTimo Sirainenstatic void search_parse_msgset_args(const struct mail_index_header *hdr,
00e7c3010f7da4a49881a7feb05e413af353af0aTimo Sirainen struct mail_search_arg *args,
00e7c3010f7da4a49881a7feb05e413af353af0aTimo Sirainen uint32_t *seq1_r, uint32_t *seq2_r);
00e7c3010f7da4a49881a7feb05e413af353af0aTimo Sirainen
00e7c3010f7da4a49881a7feb05e413af353af0aTimo Sirainenstatic void search_init_arg(struct mail_search_arg *arg,
00e7c3010f7da4a49881a7feb05e413af353af0aTimo Sirainen struct index_search_context *ctx)
00e7c3010f7da4a49881a7feb05e413af353af0aTimo Sirainen{
00e7c3010f7da4a49881a7feb05e413af353af0aTimo Sirainen switch (arg->type) {
00e7c3010f7da4a49881a7feb05e413af353af0aTimo Sirainen case SEARCH_SEQSET:
7ca63fa4166f89fee900b7c14d87d53fbac47242Timo Sirainen ctx->have_seqsets = TRUE;
00e7c3010f7da4a49881a7feb05e413af353af0aTimo Sirainen break;
00e7c3010f7da4a49881a7feb05e413af353af0aTimo Sirainen case SEARCH_UIDSET:
00e7c3010f7da4a49881a7feb05e413af353af0aTimo Sirainen case SEARCH_INTHREAD:
00e7c3010f7da4a49881a7feb05e413af353af0aTimo Sirainen case SEARCH_FLAGS:
00e7c3010f7da4a49881a7feb05e413af353af0aTimo Sirainen case SEARCH_KEYWORDS:
7ca63fa4166f89fee900b7c14d87d53fbac47242Timo Sirainen case SEARCH_MODSEQ:
00e7c3010f7da4a49881a7feb05e413af353af0aTimo Sirainen if (arg->type == SEARCH_MODSEQ)
7ca63fa4166f89fee900b7c14d87d53fbac47242Timo Sirainen mail_index_modseq_enable(ctx->ibox->index);
0a53eb0283d7ec28c6105f61e118b96fce8ecb95Timo Sirainen ctx->have_index_args = TRUE;
00e7c3010f7da4a49881a7feb05e413af353af0aTimo Sirainen break;
00e7c3010f7da4a49881a7feb05e413af353af0aTimo Sirainen case SEARCH_ALL:
00e7c3010f7da4a49881a7feb05e413af353af0aTimo Sirainen if (!arg->not)
00e7c3010f7da4a49881a7feb05e413af353af0aTimo Sirainen arg->match_always = TRUE;
00e7c3010f7da4a49881a7feb05e413af353af0aTimo Sirainen break;
7ca63fa4166f89fee900b7c14d87d53fbac47242Timo Sirainen default:
7ca63fa4166f89fee900b7c14d87d53fbac47242Timo Sirainen break;
00e7c3010f7da4a49881a7feb05e413af353af0aTimo Sirainen }
0a53eb0283d7ec28c6105f61e118b96fce8ecb95Timo Sirainen}
00e7c3010f7da4a49881a7feb05e413af353af0aTimo Sirainen
00e7c3010f7da4a49881a7feb05e413af353af0aTimo Sirainenstatic void search_seqset_arg(struct mail_search_arg *arg,
7ca63fa4166f89fee900b7c14d87d53fbac47242Timo Sirainen struct index_search_context *ctx)
7ca63fa4166f89fee900b7c14d87d53fbac47242Timo Sirainen{
7ca63fa4166f89fee900b7c14d87d53fbac47242Timo Sirainen if (arg->type == SEARCH_SEQSET) {
7ca63fa4166f89fee900b7c14d87d53fbac47242Timo Sirainen if (seq_range_exists(&arg->value.seqset, ctx->mail_ctx.seq))
7ca63fa4166f89fee900b7c14d87d53fbac47242Timo Sirainen ARG_SET_RESULT(arg, 1);
7ca63fa4166f89fee900b7c14d87d53fbac47242Timo Sirainen else
7ca63fa4166f89fee900b7c14d87d53fbac47242Timo Sirainen ARG_SET_RESULT(arg, 0);
7ca63fa4166f89fee900b7c14d87d53fbac47242Timo Sirainen }
7ca63fa4166f89fee900b7c14d87d53fbac47242Timo Sirainen}
7ca63fa4166f89fee900b7c14d87d53fbac47242Timo Sirainen
7ca63fa4166f89fee900b7c14d87d53fbac47242Timo Sirainenstatic int search_arg_match_keywords(struct index_search_context *ctx,
7ca63fa4166f89fee900b7c14d87d53fbac47242Timo Sirainen struct mail_search_arg *arg)
7ca63fa4166f89fee900b7c14d87d53fbac47242Timo Sirainen{
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;
00e7c3010f7da4a49881a7feb05e413af353af0aTimo Sirainen
00e7c3010f7da4a49881a7feb05e413af353af0aTimo Sirainen t_array_init(&keyword_indexes_arr, 128);
7ca63fa4166f89fee900b7c14d87d53fbac47242Timo Sirainen mail_index_lookup_keywords(ctx->view, ctx->mail_ctx.seq,
00e7c3010f7da4a49881a7feb05e413af353af0aTimo Sirainen &keyword_indexes_arr);
00e7c3010f7da4a49881a7feb05e413af353af0aTimo Sirainen keyword_indexes = array_get(&keyword_indexes_arr, &count);
00e7c3010f7da4a49881a7feb05e413af353af0aTimo Sirainen
00e7c3010f7da4a49881a7feb05e413af353af0aTimo Sirainen /* there probably aren't many keywords, so O(n*m) for now */
00e7c3010f7da4a49881a7feb05e413af353af0aTimo Sirainen for (i = 0; i < search_kws->count; i++) {
7ca63fa4166f89fee900b7c14d87d53fbac47242Timo Sirainen for (j = 0; j < count; j++) {
7ca63fa4166f89fee900b7c14d87d53fbac47242Timo Sirainen if (search_kws->idx[i] == keyword_indexes[j])
7ca63fa4166f89fee900b7c14d87d53fbac47242Timo Sirainen break;
7ca63fa4166f89fee900b7c14d87d53fbac47242Timo Sirainen }
7ca63fa4166f89fee900b7c14d87d53fbac47242Timo Sirainen if (j == count)
7ca63fa4166f89fee900b7c14d87d53fbac47242Timo Sirainen return 0;
7ca63fa4166f89fee900b7c14d87d53fbac47242Timo Sirainen }
7ca63fa4166f89fee900b7c14d87d53fbac47242Timo Sirainen return 1;
7ca63fa4166f89fee900b7c14d87d53fbac47242Timo Sirainen}
7ca63fa4166f89fee900b7c14d87d53fbac47242Timo Sirainen
7ca63fa4166f89fee900b7c14d87d53fbac47242Timo Sirainen/* Returns >0 = matched, 0 = not matched, -1 = unknown */
7ca63fa4166f89fee900b7c14d87d53fbac47242Timo Sirainenstatic int search_arg_match_index(struct index_search_context *ctx,
00e7c3010f7da4a49881a7feb05e413af353af0aTimo Sirainen struct mail_search_arg *arg,
00e7c3010f7da4a49881a7feb05e413af353af0aTimo Sirainen const struct mail_index_record *rec)
00e7c3010f7da4a49881a7feb05e413af353af0aTimo Sirainen{
00e7c3010f7da4a49881a7feb05e413af353af0aTimo Sirainen enum mail_flags flags;
00e7c3010f7da4a49881a7feb05e413af353af0aTimo Sirainen uint64_t modseq;
7ca63fa4166f89fee900b7c14d87d53fbac47242Timo Sirainen int ret;
00e7c3010f7da4a49881a7feb05e413af353af0aTimo Sirainen
00e7c3010f7da4a49881a7feb05e413af353af0aTimo Sirainen switch (arg->type) {
7ca63fa4166f89fee900b7c14d87d53fbac47242Timo Sirainen case SEARCH_UIDSET:
7ca63fa4166f89fee900b7c14d87d53fbac47242Timo Sirainen case SEARCH_INTHREAD:
00e7c3010f7da4a49881a7feb05e413af353af0aTimo Sirainen return seq_range_exists(&arg->value.seqset, rec->uid);
9dcb7a41eaaf832f641b7743060b5cf5ed7c80b3Timo Sirainen case SEARCH_FLAGS:
00e7c3010f7da4a49881a7feb05e413af353af0aTimo Sirainen /* recent flag shouldn't be set, but indexes from v1.0.x
00e7c3010f7da4a49881a7feb05e413af353af0aTimo Sirainen may contain it. */
9dcb7a41eaaf832f641b7743060b5cf5ed7c80b3Timo Sirainen flags = rec->flags & ~MAIL_RECENT;
00e7c3010f7da4a49881a7feb05e413af353af0aTimo Sirainen if ((arg->value.flags & MAIL_RECENT) != 0 &&
00e7c3010f7da4a49881a7feb05e413af353af0aTimo Sirainen index_mailbox_is_recent(ctx->ibox, rec->uid))
7ca63fa4166f89fee900b7c14d87d53fbac47242Timo Sirainen flags |= MAIL_RECENT;
00e7c3010f7da4a49881a7feb05e413af353af0aTimo Sirainen return (flags & arg->value.flags) == arg->value.flags;
00e7c3010f7da4a49881a7feb05e413af353af0aTimo Sirainen case SEARCH_KEYWORDS:
00e7c3010f7da4a49881a7feb05e413af353af0aTimo Sirainen T_BEGIN {
00e7c3010f7da4a49881a7feb05e413af353af0aTimo Sirainen ret = search_arg_match_keywords(ctx, arg);
00e7c3010f7da4a49881a7feb05e413af353af0aTimo Sirainen } T_END;
00e7c3010f7da4a49881a7feb05e413af353af0aTimo Sirainen return ret;
00e7c3010f7da4a49881a7feb05e413af353af0aTimo Sirainen case SEARCH_MODSEQ: {
7ca63fa4166f89fee900b7c14d87d53fbac47242Timo Sirainen if (arg->value.flags != 0) {
00e7c3010f7da4a49881a7feb05e413af353af0aTimo Sirainen modseq = mail_index_modseq_lookup_flags(ctx->view,
00e7c3010f7da4a49881a7feb05e413af353af0aTimo Sirainen arg->value.flags, ctx->mail_ctx.seq);
00e7c3010f7da4a49881a7feb05e413af353af0aTimo Sirainen } else if (arg->value.keywords != NULL) {
00e7c3010f7da4a49881a7feb05e413af353af0aTimo Sirainen modseq = mail_index_modseq_lookup_keywords(ctx->view,
00e7c3010f7da4a49881a7feb05e413af353af0aTimo Sirainen arg->value.keywords, ctx->mail_ctx.seq);
00e7c3010f7da4a49881a7feb05e413af353af0aTimo Sirainen } else {
00e7c3010f7da4a49881a7feb05e413af353af0aTimo Sirainen modseq = mail_index_modseq_lookup(ctx->view,
00e7c3010f7da4a49881a7feb05e413af353af0aTimo Sirainen ctx->mail_ctx.seq);
00e7c3010f7da4a49881a7feb05e413af353af0aTimo Sirainen }
00e7c3010f7da4a49881a7feb05e413af353af0aTimo Sirainen return modseq >= arg->value.modseq->modseq;
00e7c3010f7da4a49881a7feb05e413af353af0aTimo Sirainen }
00e7c3010f7da4a49881a7feb05e413af353af0aTimo Sirainen default:
00e7c3010f7da4a49881a7feb05e413af353af0aTimo Sirainen return -1;
00e7c3010f7da4a49881a7feb05e413af353af0aTimo Sirainen }
00e7c3010f7da4a49881a7feb05e413af353af0aTimo Sirainen}
00e7c3010f7da4a49881a7feb05e413af353af0aTimo Sirainen
00e7c3010f7da4a49881a7feb05e413af353af0aTimo Sirainenstatic void search_index_arg(struct mail_search_arg *arg,
00e7c3010f7da4a49881a7feb05e413af353af0aTimo Sirainen struct index_search_context *ctx)
00e7c3010f7da4a49881a7feb05e413af353af0aTimo Sirainen{
00e7c3010f7da4a49881a7feb05e413af353af0aTimo Sirainen const struct mail_index_record *rec;
00e7c3010f7da4a49881a7feb05e413af353af0aTimo Sirainen
00e7c3010f7da4a49881a7feb05e413af353af0aTimo Sirainen rec = mail_index_lookup(ctx->view, ctx->mail_ctx.seq);
00e7c3010f7da4a49881a7feb05e413af353af0aTimo Sirainen switch (search_arg_match_index(ctx, arg, rec)) {
00e7c3010f7da4a49881a7feb05e413af353af0aTimo Sirainen case -1:
00e7c3010f7da4a49881a7feb05e413af353af0aTimo Sirainen /* unknown */
00e7c3010f7da4a49881a7feb05e413af353af0aTimo Sirainen break;
00e7c3010f7da4a49881a7feb05e413af353af0aTimo Sirainen case 0:
00e7c3010f7da4a49881a7feb05e413af353af0aTimo Sirainen ARG_SET_RESULT(arg, 0);
00e7c3010f7da4a49881a7feb05e413af353af0aTimo Sirainen break;
00e7c3010f7da4a49881a7feb05e413af353af0aTimo Sirainen default:
00e7c3010f7da4a49881a7feb05e413af353af0aTimo Sirainen ARG_SET_RESULT(arg, 1);
00e7c3010f7da4a49881a7feb05e413af353af0aTimo Sirainen break;
00e7c3010f7da4a49881a7feb05e413af353af0aTimo Sirainen }
00e7c3010f7da4a49881a7feb05e413af353af0aTimo Sirainen}
00e7c3010f7da4a49881a7feb05e413af353af0aTimo Sirainen
00e7c3010f7da4a49881a7feb05e413af353af0aTimo Sirainen/* Returns >0 = matched, 0 = not matched, -1 = unknown */
00e7c3010f7da4a49881a7feb05e413af353af0aTimo Sirainenstatic int search_arg_match_cached(struct index_search_context *ctx,
00e7c3010f7da4a49881a7feb05e413af353af0aTimo Sirainen struct mail_search_arg *arg)
00e7c3010f7da4a49881a7feb05e413af353af0aTimo Sirainen{
00e7c3010f7da4a49881a7feb05e413af353af0aTimo Sirainen const char *str;
00e7c3010f7da4a49881a7feb05e413af353af0aTimo Sirainen struct tm *tm;
00e7c3010f7da4a49881a7feb05e413af353af0aTimo Sirainen uoff_t virtual_size;
00e7c3010f7da4a49881a7feb05e413af353af0aTimo Sirainen time_t date;
00e7c3010f7da4a49881a7feb05e413af353af0aTimo Sirainen int timezone_offset;
00e7c3010f7da4a49881a7feb05e413af353af0aTimo Sirainen
00e7c3010f7da4a49881a7feb05e413af353af0aTimo Sirainen switch (arg->type) {
00e7c3010f7da4a49881a7feb05e413af353af0aTimo Sirainen /* internal dates */
00e7c3010f7da4a49881a7feb05e413af353af0aTimo Sirainen case SEARCH_BEFORE:
00e7c3010f7da4a49881a7feb05e413af353af0aTimo Sirainen case SEARCH_ON:
00e7c3010f7da4a49881a7feb05e413af353af0aTimo Sirainen case SEARCH_SINCE:
00e7c3010f7da4a49881a7feb05e413af353af0aTimo Sirainen if (mail_get_received_date(ctx->mail, &date) < 0)
00e7c3010f7da4a49881a7feb05e413af353af0aTimo Sirainen return -1;
00e7c3010f7da4a49881a7feb05e413af353af0aTimo Sirainen
00e7c3010f7da4a49881a7feb05e413af353af0aTimo Sirainen if ((arg->value.search_flags &
00e7c3010f7da4a49881a7feb05e413af353af0aTimo Sirainen MAIL_SEARCH_ARG_FLAG_USE_TZ) == 0) {
00e7c3010f7da4a49881a7feb05e413af353af0aTimo Sirainen tm = localtime(&date);
00e7c3010f7da4a49881a7feb05e413af353af0aTimo Sirainen date += utc_offset(tm, date)*60;
00e7c3010f7da4a49881a7feb05e413af353af0aTimo Sirainen }
00e7c3010f7da4a49881a7feb05e413af353af0aTimo Sirainen
00e7c3010f7da4a49881a7feb05e413af353af0aTimo Sirainen switch (arg->type) {
00e7c3010f7da4a49881a7feb05e413af353af0aTimo Sirainen case SEARCH_BEFORE:
00e7c3010f7da4a49881a7feb05e413af353af0aTimo Sirainen return date < arg->value.time;
00e7c3010f7da4a49881a7feb05e413af353af0aTimo Sirainen case SEARCH_ON:
00e7c3010f7da4a49881a7feb05e413af353af0aTimo Sirainen return date >= arg->value.time &&
00e7c3010f7da4a49881a7feb05e413af353af0aTimo Sirainen date < arg->value.time + 3600*24;
00e7c3010f7da4a49881a7feb05e413af353af0aTimo Sirainen case SEARCH_SINCE:
00e7c3010f7da4a49881a7feb05e413af353af0aTimo Sirainen return date >= arg->value.time;
00e7c3010f7da4a49881a7feb05e413af353af0aTimo Sirainen default:
00e7c3010f7da4a49881a7feb05e413af353af0aTimo Sirainen /* unreachable */
00e7c3010f7da4a49881a7feb05e413af353af0aTimo Sirainen break;
00e7c3010f7da4a49881a7feb05e413af353af0aTimo Sirainen }
00e7c3010f7da4a49881a7feb05e413af353af0aTimo Sirainen
00e7c3010f7da4a49881a7feb05e413af353af0aTimo Sirainen /* sent dates */
00e7c3010f7da4a49881a7feb05e413af353af0aTimo Sirainen case SEARCH_SENTBEFORE:
00e7c3010f7da4a49881a7feb05e413af353af0aTimo Sirainen case SEARCH_SENTON:
00e7c3010f7da4a49881a7feb05e413af353af0aTimo Sirainen case SEARCH_SENTSINCE:
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 return -1;
00e7c3010f7da4a49881a7feb05e413af353af0aTimo Sirainen if ((arg->value.search_flags &
00e7c3010f7da4a49881a7feb05e413af353af0aTimo Sirainen MAIL_SEARCH_ARG_FLAG_USE_TZ) == 0)
00e7c3010f7da4a49881a7feb05e413af353af0aTimo Sirainen date += timezone_offset * 60;
00e7c3010f7da4a49881a7feb05e413af353af0aTimo Sirainen
00e7c3010f7da4a49881a7feb05e413af353af0aTimo Sirainen switch (arg->type) {
00e7c3010f7da4a49881a7feb05e413af353af0aTimo Sirainen case SEARCH_SENTBEFORE:
00e7c3010f7da4a49881a7feb05e413af353af0aTimo Sirainen return date < arg->value.time;
00e7c3010f7da4a49881a7feb05e413af353af0aTimo Sirainen case SEARCH_SENTON:
00e7c3010f7da4a49881a7feb05e413af353af0aTimo Sirainen return date >= arg->value.time &&
00e7c3010f7da4a49881a7feb05e413af353af0aTimo Sirainen date < arg->value.time + 3600*24;
00e7c3010f7da4a49881a7feb05e413af353af0aTimo Sirainen case SEARCH_SENTSINCE:
00e7c3010f7da4a49881a7feb05e413af353af0aTimo Sirainen return date >= arg->value.time;
00e7c3010f7da4a49881a7feb05e413af353af0aTimo Sirainen default:
00e7c3010f7da4a49881a7feb05e413af353af0aTimo Sirainen /* unreachable */
00e7c3010f7da4a49881a7feb05e413af353af0aTimo Sirainen break;
00e7c3010f7da4a49881a7feb05e413af353af0aTimo Sirainen }
00e7c3010f7da4a49881a7feb05e413af353af0aTimo Sirainen
00e7c3010f7da4a49881a7feb05e413af353af0aTimo Sirainen /* sizes */
00e7c3010f7da4a49881a7feb05e413af353af0aTimo Sirainen case SEARCH_SMALLER:
00e7c3010f7da4a49881a7feb05e413af353af0aTimo Sirainen case SEARCH_LARGER:
00e7c3010f7da4a49881a7feb05e413af353af0aTimo Sirainen if (mail_get_virtual_size(ctx->mail, &virtual_size) < 0)
00e7c3010f7da4a49881a7feb05e413af353af0aTimo Sirainen return -1;
00e7c3010f7da4a49881a7feb05e413af353af0aTimo Sirainen
00e7c3010f7da4a49881a7feb05e413af353af0aTimo Sirainen if (arg->type == SEARCH_SMALLER)
00e7c3010f7da4a49881a7feb05e413af353af0aTimo Sirainen return virtual_size < arg->value.size;
00e7c3010f7da4a49881a7feb05e413af353af0aTimo Sirainen else
00e7c3010f7da4a49881a7feb05e413af353af0aTimo Sirainen return virtual_size > arg->value.size;
00e7c3010f7da4a49881a7feb05e413af353af0aTimo Sirainen
00e7c3010f7da4a49881a7feb05e413af353af0aTimo Sirainen case SEARCH_GUID:
00e7c3010f7da4a49881a7feb05e413af353af0aTimo Sirainen if (mail_get_special(ctx->mail, MAIL_FETCH_GUID, &str) < 0)
00e7c3010f7da4a49881a7feb05e413af353af0aTimo Sirainen return -1;
00e7c3010f7da4a49881a7feb05e413af353af0aTimo Sirainen return strcmp(str, arg->value.str) == 0;
00e7c3010f7da4a49881a7feb05e413af353af0aTimo Sirainen case SEARCH_MAILBOX:
00e7c3010f7da4a49881a7feb05e413af353af0aTimo Sirainen if (mail_get_special(ctx->mail, MAIL_FETCH_MAILBOX_NAME,
00e7c3010f7da4a49881a7feb05e413af353af0aTimo Sirainen &str) < 0)
00e7c3010f7da4a49881a7feb05e413af353af0aTimo Sirainen return -1;
00e7c3010f7da4a49881a7feb05e413af353af0aTimo Sirainen
00e7c3010f7da4a49881a7feb05e413af353af0aTimo Sirainen if (strcasecmp(str, "INBOX") == 0)
00e7c3010f7da4a49881a7feb05e413af353af0aTimo Sirainen return strcasecmp(arg->value.str, "INBOX") == 0;
00e7c3010f7da4a49881a7feb05e413af353af0aTimo Sirainen return strcmp(str, arg->value.str) == 0;
00e7c3010f7da4a49881a7feb05e413af353af0aTimo Sirainen default:
00e7c3010f7da4a49881a7feb05e413af353af0aTimo Sirainen return -1;
00e7c3010f7da4a49881a7feb05e413af353af0aTimo Sirainen }
00e7c3010f7da4a49881a7feb05e413af353af0aTimo Sirainen}
00e7c3010f7da4a49881a7feb05e413af353af0aTimo Sirainen
00e7c3010f7da4a49881a7feb05e413af353af0aTimo Sirainenstatic void search_cached_arg(struct mail_search_arg *arg,
00e7c3010f7da4a49881a7feb05e413af353af0aTimo Sirainen struct index_search_context *ctx)
00e7c3010f7da4a49881a7feb05e413af353af0aTimo Sirainen{
00e7c3010f7da4a49881a7feb05e413af353af0aTimo Sirainen switch (search_arg_match_cached(ctx, arg)) {
00e7c3010f7da4a49881a7feb05e413af353af0aTimo Sirainen case -1:
00e7c3010f7da4a49881a7feb05e413af353af0aTimo Sirainen /* unknown */
7ca63fa4166f89fee900b7c14d87d53fbac47242Timo Sirainen break;
7ca63fa4166f89fee900b7c14d87d53fbac47242Timo Sirainen case 0:
00e7c3010f7da4a49881a7feb05e413af353af0aTimo Sirainen ARG_SET_RESULT(arg, 0);
7ca63fa4166f89fee900b7c14d87d53fbac47242Timo Sirainen break;
00e7c3010f7da4a49881a7feb05e413af353af0aTimo Sirainen default:
00e7c3010f7da4a49881a7feb05e413af353af0aTimo Sirainen ARG_SET_RESULT(arg, 1);
00e7c3010f7da4a49881a7feb05e413af353af0aTimo Sirainen break;
7ca63fa4166f89fee900b7c14d87d53fbac47242Timo Sirainen }
7ca63fa4166f89fee900b7c14d87d53fbac47242Timo Sirainen}
7ca63fa4166f89fee900b7c14d87d53fbac47242Timo Sirainen
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)
7ca63fa4166f89fee900b7c14d87d53fbac47242Timo Sirainen{
7ca63fa4166f89fee900b7c14d87d53fbac47242Timo Sirainen time_t sent_time;
7ca63fa4166f89fee900b7c14d87d53fbac47242Timo Sirainen int timezone_offset;
7ca63fa4166f89fee900b7c14d87d53fbac47242Timo Sirainen
7ca63fa4166f89fee900b7c14d87d53fbac47242Timo Sirainen if (sent_value == NULL)
7ca63fa4166f89fee900b7c14d87d53fbac47242Timo Sirainen return 0;
00e7c3010f7da4a49881a7feb05e413af353af0aTimo Sirainen
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 Sirainen &sent_time, &timezone_offset))
00e7c3010f7da4a49881a7feb05e413af353af0aTimo Sirainen return 0;
00e7c3010f7da4a49881a7feb05e413af353af0aTimo Sirainen sent_time += timezone_offset * 60;
00e7c3010f7da4a49881a7feb05e413af353af0aTimo Sirainen
00e7c3010f7da4a49881a7feb05e413af353af0aTimo Sirainen switch (type) {
00e7c3010f7da4a49881a7feb05e413af353af0aTimo Sirainen case SEARCH_SENTBEFORE:
00e7c3010f7da4a49881a7feb05e413af353af0aTimo Sirainen return sent_time < search_time;
00e7c3010f7da4a49881a7feb05e413af353af0aTimo Sirainen case SEARCH_SENTON:
7ca63fa4166f89fee900b7c14d87d53fbac47242Timo Sirainen return sent_time >= search_time &&
7ca63fa4166f89fee900b7c14d87d53fbac47242Timo Sirainen sent_time < search_time + 3600*24;
7ca63fa4166f89fee900b7c14d87d53fbac47242Timo Sirainen case SEARCH_SENTSINCE:
7ca63fa4166f89fee900b7c14d87d53fbac47242Timo Sirainen return sent_time >= search_time;
7ca63fa4166f89fee900b7c14d87d53fbac47242Timo Sirainen default:
7ca63fa4166f89fee900b7c14d87d53fbac47242Timo Sirainen i_unreached();
7ca63fa4166f89fee900b7c14d87d53fbac47242Timo Sirainen }
7ca63fa4166f89fee900b7c14d87d53fbac47242Timo Sirainen}
7ca63fa4166f89fee900b7c14d87d53fbac47242Timo Sirainen
00e7c3010f7da4a49881a7feb05e413af353af0aTimo Sirainenstatic struct message_search_context *
00e7c3010f7da4a49881a7feb05e413af353af0aTimo Sirainenmsg_search_arg_context(struct index_search_context *ctx,
00e7c3010f7da4a49881a7feb05e413af353af0aTimo Sirainen struct mail_search_arg *arg)
00e7c3010f7da4a49881a7feb05e413af353af0aTimo Sirainen{
0a53eb0283d7ec28c6105f61e118b96fce8ecb95Timo Sirainen struct message_search_context *arg_ctx = arg->context;
0a53eb0283d7ec28c6105f61e118b96fce8ecb95Timo Sirainen enum message_search_flags flags;
0a53eb0283d7ec28c6105f61e118b96fce8ecb95Timo Sirainen int ret;
0a53eb0283d7ec28c6105f61e118b96fce8ecb95Timo Sirainen
0a53eb0283d7ec28c6105f61e118b96fce8ecb95Timo Sirainen if (arg_ctx != NULL)
0a53eb0283d7ec28c6105f61e118b96fce8ecb95Timo Sirainen return arg_ctx;
0a53eb0283d7ec28c6105f61e118b96fce8ecb95Timo Sirainen
00e7c3010f7da4a49881a7feb05e413af353af0aTimo Sirainen flags = (arg->type == SEARCH_BODY || arg->type == SEARCH_BODY_FAST) ?
00e7c3010f7da4a49881a7feb05e413af353af0aTimo Sirainen MESSAGE_SEARCH_FLAG_SKIP_HEADERS : 0;
00e7c3010f7da4a49881a7feb05e413af353af0aTimo Sirainen
00e7c3010f7da4a49881a7feb05e413af353af0aTimo Sirainen ret = message_search_init(arg->value.str,
00e7c3010f7da4a49881a7feb05e413af353af0aTimo Sirainen ctx->mail_ctx.args->charset, flags,
00e7c3010f7da4a49881a7feb05e413af353af0aTimo Sirainen &arg_ctx);
00e7c3010f7da4a49881a7feb05e413af353af0aTimo Sirainen if (ret > 0) {
00e7c3010f7da4a49881a7feb05e413af353af0aTimo Sirainen arg->context = arg_ctx;
0a53eb0283d7ec28c6105f61e118b96fce8ecb95Timo Sirainen return arg_ctx;
00e7c3010f7da4a49881a7feb05e413af353af0aTimo Sirainen }
00e7c3010f7da4a49881a7feb05e413af353af0aTimo Sirainen if (ret == 0)
00e7c3010f7da4a49881a7feb05e413af353af0aTimo Sirainen ctx->error = TXT_UNKNOWN_CHARSET;
00e7c3010f7da4a49881a7feb05e413af353af0aTimo Sirainen else
00e7c3010f7da4a49881a7feb05e413af353af0aTimo Sirainen ctx->error = TXT_INVALID_SEARCH_KEY;
00e7c3010f7da4a49881a7feb05e413af353af0aTimo Sirainen return NULL;
00e7c3010f7da4a49881a7feb05e413af353af0aTimo Sirainen}
00e7c3010f7da4a49881a7feb05e413af353af0aTimo Sirainen
00e7c3010f7da4a49881a7feb05e413af353af0aTimo Sirainenstatic void compress_lwsp(string_t *dest, const unsigned char *src,
00e7c3010f7da4a49881a7feb05e413af353af0aTimo Sirainen unsigned int src_len)
00e7c3010f7da4a49881a7feb05e413af353af0aTimo Sirainen{
00e7c3010f7da4a49881a7feb05e413af353af0aTimo Sirainen unsigned int i;
00e7c3010f7da4a49881a7feb05e413af353af0aTimo Sirainen bool prev_lwsp = TRUE;
00e7c3010f7da4a49881a7feb05e413af353af0aTimo Sirainen
7ca63fa4166f89fee900b7c14d87d53fbac47242Timo Sirainen for (i = 0; i < src_len; i++) {
7ca63fa4166f89fee900b7c14d87d53fbac47242Timo Sirainen if (IS_LWSP(src[i])) {
7ca63fa4166f89fee900b7c14d87d53fbac47242Timo Sirainen if (!prev_lwsp) {
7ca63fa4166f89fee900b7c14d87d53fbac47242Timo Sirainen prev_lwsp = TRUE;
7ca63fa4166f89fee900b7c14d87d53fbac47242Timo Sirainen str_append_c(dest, ' ');
00e7c3010f7da4a49881a7feb05e413af353af0aTimo Sirainen }
00e7c3010f7da4a49881a7feb05e413af353af0aTimo Sirainen } else {
00e7c3010f7da4a49881a7feb05e413af353af0aTimo Sirainen prev_lwsp = FALSE;
00e7c3010f7da4a49881a7feb05e413af353af0aTimo Sirainen str_append_c(dest, src[i]);
7ca63fa4166f89fee900b7c14d87d53fbac47242Timo Sirainen }
7ca63fa4166f89fee900b7c14d87d53fbac47242Timo Sirainen }
00e7c3010f7da4a49881a7feb05e413af353af0aTimo Sirainen}
00e7c3010f7da4a49881a7feb05e413af353af0aTimo Sirainen
00e7c3010f7da4a49881a7feb05e413af353af0aTimo Sirainenstatic void search_header_arg(struct mail_search_arg *arg,
00e7c3010f7da4a49881a7feb05e413af353af0aTimo Sirainen struct search_header_context *ctx)
00e7c3010f7da4a49881a7feb05e413af353af0aTimo Sirainen{
00e7c3010f7da4a49881a7feb05e413af353af0aTimo Sirainen struct message_search_context *msg_search_ctx;
00e7c3010f7da4a49881a7feb05e413af353af0aTimo Sirainen struct message_block block;
00e7c3010f7da4a49881a7feb05e413af353af0aTimo Sirainen struct message_header_line hdr;
00e7c3010f7da4a49881a7feb05e413af353af0aTimo Sirainen int ret;
00e7c3010f7da4a49881a7feb05e413af353af0aTimo Sirainen
00e7c3010f7da4a49881a7feb05e413af353af0aTimo Sirainen /* first check that the field name matches to argument. */
00e7c3010f7da4a49881a7feb05e413af353af0aTimo Sirainen switch (arg->type) {
00e7c3010f7da4a49881a7feb05e413af353af0aTimo Sirainen case SEARCH_SENTBEFORE:
00e7c3010f7da4a49881a7feb05e413af353af0aTimo Sirainen case SEARCH_SENTON:
00e7c3010f7da4a49881a7feb05e413af353af0aTimo Sirainen case SEARCH_SENTSINCE:
00e7c3010f7da4a49881a7feb05e413af353af0aTimo Sirainen /* date is handled differently than others */
00e7c3010f7da4a49881a7feb05e413af353af0aTimo Sirainen if (strcasecmp(ctx->hdr->name, "Date") == 0) {
00e7c3010f7da4a49881a7feb05e413af353af0aTimo Sirainen if (ctx->hdr->continues) {
00e7c3010f7da4a49881a7feb05e413af353af0aTimo Sirainen ctx->hdr->use_full_value = TRUE;
00e7c3010f7da4a49881a7feb05e413af353af0aTimo Sirainen return;
00e7c3010f7da4a49881a7feb05e413af353af0aTimo Sirainen }
00e7c3010f7da4a49881a7feb05e413af353af0aTimo Sirainen ret = search_sent(arg->type, arg->value.time,
00e7c3010f7da4a49881a7feb05e413af353af0aTimo Sirainen ctx->hdr->full_value,
00e7c3010f7da4a49881a7feb05e413af353af0aTimo Sirainen ctx->hdr->full_value_len);
00e7c3010f7da4a49881a7feb05e413af353af0aTimo Sirainen ARG_SET_RESULT(arg, ret);
00e7c3010f7da4a49881a7feb05e413af353af0aTimo Sirainen }
00e7c3010f7da4a49881a7feb05e413af353af0aTimo Sirainen return;
00e7c3010f7da4a49881a7feb05e413af353af0aTimo Sirainen
00e7c3010f7da4a49881a7feb05e413af353af0aTimo Sirainen case SEARCH_HEADER:
00e7c3010f7da4a49881a7feb05e413af353af0aTimo Sirainen case SEARCH_HEADER_ADDRESS:
00e7c3010f7da4a49881a7feb05e413af353af0aTimo Sirainen case SEARCH_HEADER_COMPRESS_LWSP:
00e7c3010f7da4a49881a7feb05e413af353af0aTimo Sirainen ctx->custom_header = TRUE;
00e7c3010f7da4a49881a7feb05e413af353af0aTimo Sirainen
7ca63fa4166f89fee900b7c14d87d53fbac47242Timo Sirainen if (strcasecmp(ctx->hdr->name, arg->hdr_field_name) != 0)
7ca63fa4166f89fee900b7c14d87d53fbac47242Timo Sirainen return;
7ca63fa4166f89fee900b7c14d87d53fbac47242Timo Sirainen break;
7ca63fa4166f89fee900b7c14d87d53fbac47242Timo Sirainen default:
7ca63fa4166f89fee900b7c14d87d53fbac47242Timo Sirainen return;
7ca63fa4166f89fee900b7c14d87d53fbac47242Timo Sirainen }
7ca63fa4166f89fee900b7c14d87d53fbac47242Timo Sirainen
0a53eb0283d7ec28c6105f61e118b96fce8ecb95Timo Sirainen if (arg->value.str[0] == '\0') {
7ca63fa4166f89fee900b7c14d87d53fbac47242Timo Sirainen /* we're just testing existence of the field. always matches. */
7ca63fa4166f89fee900b7c14d87d53fbac47242Timo Sirainen ARG_SET_RESULT(arg, 1);
7ca63fa4166f89fee900b7c14d87d53fbac47242Timo Sirainen return;
7ca63fa4166f89fee900b7c14d87d53fbac47242Timo Sirainen }
7ca63fa4166f89fee900b7c14d87d53fbac47242Timo Sirainen
7ca63fa4166f89fee900b7c14d87d53fbac47242Timo Sirainen if (ctx->hdr->continues) {
7ca63fa4166f89fee900b7c14d87d53fbac47242Timo Sirainen ctx->hdr->use_full_value = TRUE;
7ca63fa4166f89fee900b7c14d87d53fbac47242Timo Sirainen return;
7ca63fa4166f89fee900b7c14d87d53fbac47242Timo Sirainen }
7ca63fa4166f89fee900b7c14d87d53fbac47242Timo Sirainen
0a53eb0283d7ec28c6105f61e118b96fce8ecb95Timo Sirainen memset(&block, 0, sizeof(block));
0a53eb0283d7ec28c6105f61e118b96fce8ecb95Timo Sirainen
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 hdr = *ctx->hdr;
7ca63fa4166f89fee900b7c14d87d53fbac47242Timo Sirainen hdr.name = ""; hdr.name_len = 0;
7ca63fa4166f89fee900b7c14d87d53fbac47242Timo Sirainen hdr.middle_len = 0;
7ca63fa4166f89fee900b7c14d87d53fbac47242Timo Sirainen block.hdr = &hdr;
7ca63fa4166f89fee900b7c14d87d53fbac47242Timo Sirainen
7ca63fa4166f89fee900b7c14d87d53fbac47242Timo Sirainen msg_search_ctx = msg_search_arg_context(ctx->index_context, arg);
0a53eb0283d7ec28c6105f61e118b96fce8ecb95Timo Sirainen if (msg_search_ctx == NULL)
7ca63fa4166f89fee900b7c14d87d53fbac47242Timo Sirainen return;
7ca63fa4166f89fee900b7c14d87d53fbac47242Timo Sirainen
00e7c3010f7da4a49881a7feb05e413af353af0aTimo Sirainen T_BEGIN {
00e7c3010f7da4a49881a7feb05e413af353af0aTimo Sirainen struct message_address *addr;
00e7c3010f7da4a49881a7feb05e413af353af0aTimo Sirainen string_t *str;
00e7c3010f7da4a49881a7feb05e413af353af0aTimo Sirainen
00e7c3010f7da4a49881a7feb05e413af353af0aTimo Sirainen switch (arg->type) {
00e7c3010f7da4a49881a7feb05e413af353af0aTimo Sirainen case SEARCH_HEADER:
00e7c3010f7da4a49881a7feb05e413af353af0aTimo Sirainen /* simple match */
00e7c3010f7da4a49881a7feb05e413af353af0aTimo Sirainen break;
00e7c3010f7da4a49881a7feb05e413af353af0aTimo Sirainen case SEARCH_HEADER_ADDRESS:
00e7c3010f7da4a49881a7feb05e413af353af0aTimo Sirainen /* we have to match against normalized address */
00e7c3010f7da4a49881a7feb05e413af353af0aTimo Sirainen addr = message_address_parse(pool_datastack_create(),
7ca63fa4166f89fee900b7c14d87d53fbac47242Timo Sirainen ctx->hdr->full_value,
7ca63fa4166f89fee900b7c14d87d53fbac47242Timo Sirainen ctx->hdr->full_value_len,
7ca63fa4166f89fee900b7c14d87d53fbac47242Timo Sirainen (unsigned int)-1, TRUE);
00e7c3010f7da4a49881a7feb05e413af353af0aTimo Sirainen str = t_str_new(ctx->hdr->value_len);
7ca63fa4166f89fee900b7c14d87d53fbac47242Timo Sirainen message_address_write(str, addr);
00e7c3010f7da4a49881a7feb05e413af353af0aTimo Sirainen hdr.value = hdr.full_value = str_data(str);
00e7c3010f7da4a49881a7feb05e413af353af0aTimo Sirainen hdr.value_len = hdr.full_value_len = str_len(str);
00e7c3010f7da4a49881a7feb05e413af353af0aTimo Sirainen break;
00e7c3010f7da4a49881a7feb05e413af353af0aTimo Sirainen case SEARCH_HEADER_COMPRESS_LWSP:
00e7c3010f7da4a49881a7feb05e413af353af0aTimo Sirainen /* convert LWSP to single spaces */
00e7c3010f7da4a49881a7feb05e413af353af0aTimo Sirainen str = t_str_new(hdr.full_value_len);
00e7c3010f7da4a49881a7feb05e413af353af0aTimo Sirainen compress_lwsp(str, hdr.full_value, hdr.full_value_len);
00e7c3010f7da4a49881a7feb05e413af353af0aTimo Sirainen hdr.value = hdr.full_value = str_data(str);
00e7c3010f7da4a49881a7feb05e413af353af0aTimo Sirainen hdr.value_len = hdr.full_value_len = str_len(str);
00e7c3010f7da4a49881a7feb05e413af353af0aTimo Sirainen break;
7ca63fa4166f89fee900b7c14d87d53fbac47242Timo Sirainen default:
00e7c3010f7da4a49881a7feb05e413af353af0aTimo Sirainen i_unreached();
00e7c3010f7da4a49881a7feb05e413af353af0aTimo Sirainen }
00e7c3010f7da4a49881a7feb05e413af353af0aTimo Sirainen ret = message_search_more(msg_search_ctx, &block) ? 1 : 0;
00e7c3010f7da4a49881a7feb05e413af353af0aTimo Sirainen } T_END;
00e7c3010f7da4a49881a7feb05e413af353af0aTimo Sirainen
00e7c3010f7da4a49881a7feb05e413af353af0aTimo Sirainen ARG_SET_RESULT(arg, ret);
00e7c3010f7da4a49881a7feb05e413af353af0aTimo Sirainen}
00e7c3010f7da4a49881a7feb05e413af353af0aTimo Sirainen
0a53eb0283d7ec28c6105f61e118b96fce8ecb95Timo Sirainenstatic void search_header_unmatch(struct mail_search_arg *arg,
0a53eb0283d7ec28c6105f61e118b96fce8ecb95Timo Sirainen void *context ATTR_UNUSED)
00e7c3010f7da4a49881a7feb05e413af353af0aTimo Sirainen{
7ca63fa4166f89fee900b7c14d87d53fbac47242Timo Sirainen switch (arg->type) {
00e7c3010f7da4a49881a7feb05e413af353af0aTimo Sirainen case SEARCH_SENTBEFORE:
00e7c3010f7da4a49881a7feb05e413af353af0aTimo Sirainen case SEARCH_SENTON:
00e7c3010f7da4a49881a7feb05e413af353af0aTimo Sirainen case SEARCH_SENTSINCE:
00e7c3010f7da4a49881a7feb05e413af353af0aTimo Sirainen if (arg->not) {
00e7c3010f7da4a49881a7feb05e413af353af0aTimo Sirainen /* date header not found, so we match only for
7ca63fa4166f89fee900b7c14d87d53fbac47242Timo Sirainen NOT searches */
00e7c3010f7da4a49881a7feb05e413af353af0aTimo Sirainen ARG_SET_RESULT(arg, 0);
7ca63fa4166f89fee900b7c14d87d53fbac47242Timo Sirainen }
00e7c3010f7da4a49881a7feb05e413af353af0aTimo Sirainen break;
7ca63fa4166f89fee900b7c14d87d53fbac47242Timo Sirainen case SEARCH_HEADER:
7ca63fa4166f89fee900b7c14d87d53fbac47242Timo Sirainen case SEARCH_HEADER_ADDRESS:
7ca63fa4166f89fee900b7c14d87d53fbac47242Timo Sirainen case SEARCH_HEADER_COMPRESS_LWSP:
7ca63fa4166f89fee900b7c14d87d53fbac47242Timo Sirainen ARG_SET_RESULT(arg, 0);
7ca63fa4166f89fee900b7c14d87d53fbac47242Timo Sirainen break;
7ca63fa4166f89fee900b7c14d87d53fbac47242Timo Sirainen default:
7ca63fa4166f89fee900b7c14d87d53fbac47242Timo Sirainen break;
7ca63fa4166f89fee900b7c14d87d53fbac47242Timo Sirainen }
7ca63fa4166f89fee900b7c14d87d53fbac47242Timo Sirainen}
7ca63fa4166f89fee900b7c14d87d53fbac47242Timo Sirainen
7ca63fa4166f89fee900b7c14d87d53fbac47242Timo Sirainenstatic void search_header(struct message_header_line *hdr,
7ca63fa4166f89fee900b7c14d87d53fbac47242Timo Sirainen struct search_header_context *ctx)
7ca63fa4166f89fee900b7c14d87d53fbac47242Timo Sirainen{
7ca63fa4166f89fee900b7c14d87d53fbac47242Timo Sirainen if (hdr == NULL) {
7ca63fa4166f89fee900b7c14d87d53fbac47242Timo Sirainen /* end of headers, mark all unknown SEARCH_HEADERs unmatched */
7ca63fa4166f89fee900b7c14d87d53fbac47242Timo Sirainen mail_search_args_foreach(ctx->args, search_header_unmatch, ctx);
7ca63fa4166f89fee900b7c14d87d53fbac47242Timo Sirainen return;
7ca63fa4166f89fee900b7c14d87d53fbac47242Timo Sirainen }
7ca63fa4166f89fee900b7c14d87d53fbac47242Timo Sirainen
7ca63fa4166f89fee900b7c14d87d53fbac47242Timo Sirainen if (hdr->eoh)
7ca63fa4166f89fee900b7c14d87d53fbac47242Timo Sirainen return;
00e7c3010f7da4a49881a7feb05e413af353af0aTimo Sirainen
0a53eb0283d7ec28c6105f61e118b96fce8ecb95Timo Sirainen if (ctx->parse_headers)
0a53eb0283d7ec28c6105f61e118b96fce8ecb95Timo Sirainen index_mail_parse_header(NULL, hdr, ctx->index_context->imail);
0a53eb0283d7ec28c6105f61e118b96fce8ecb95Timo Sirainen
0a53eb0283d7ec28c6105f61e118b96fce8ecb95Timo Sirainen if (ctx->custom_header || strcasecmp(hdr->name, "Date") == 0) {
0a53eb0283d7ec28c6105f61e118b96fce8ecb95Timo Sirainen ctx->hdr = hdr;
0a53eb0283d7ec28c6105f61e118b96fce8ecb95Timo Sirainen
0a53eb0283d7ec28c6105f61e118b96fce8ecb95Timo Sirainen ctx->custom_header = FALSE;
0a53eb0283d7ec28c6105f61e118b96fce8ecb95Timo Sirainen mail_search_args_foreach(ctx->args, search_header_arg, ctx);
0a53eb0283d7ec28c6105f61e118b96fce8ecb95Timo Sirainen }
0a53eb0283d7ec28c6105f61e118b96fce8ecb95Timo Sirainen}
0a53eb0283d7ec28c6105f61e118b96fce8ecb95Timo Sirainen
6c961309a97ee0c86b85fa336f5f51662bd3d515Timo Sirainenstatic void search_body(struct mail_search_arg *arg,
6c961309a97ee0c86b85fa336f5f51662bd3d515Timo Sirainen struct search_body_context *ctx)
0a53eb0283d7ec28c6105f61e118b96fce8ecb95Timo Sirainen{
6c961309a97ee0c86b85fa336f5f51662bd3d515Timo Sirainen struct message_search_context *msg_search_ctx;
0a53eb0283d7ec28c6105f61e118b96fce8ecb95Timo Sirainen int ret;
0a53eb0283d7ec28c6105f61e118b96fce8ecb95Timo Sirainen
0a53eb0283d7ec28c6105f61e118b96fce8ecb95Timo Sirainen if (ctx->index_ctx->error != NULL)
0a53eb0283d7ec28c6105f61e118b96fce8ecb95Timo Sirainen return;
7ca63fa4166f89fee900b7c14d87d53fbac47242Timo Sirainen
7ca63fa4166f89fee900b7c14d87d53fbac47242Timo Sirainen switch (arg->type) {
00e7c3010f7da4a49881a7feb05e413af353af0aTimo Sirainen case SEARCH_BODY:
00e7c3010f7da4a49881a7feb05e413af353af0aTimo Sirainen case SEARCH_BODY_FAST:
7ca63fa4166f89fee900b7c14d87d53fbac47242Timo Sirainen case SEARCH_TEXT:
7ca63fa4166f89fee900b7c14d87d53fbac47242Timo Sirainen case SEARCH_TEXT_FAST:
7ca63fa4166f89fee900b7c14d87d53fbac47242Timo Sirainen break;
7ca63fa4166f89fee900b7c14d87d53fbac47242Timo Sirainen default:
7ca63fa4166f89fee900b7c14d87d53fbac47242Timo Sirainen return;
7ca63fa4166f89fee900b7c14d87d53fbac47242Timo Sirainen }
7ca63fa4166f89fee900b7c14d87d53fbac47242Timo Sirainen
7ca63fa4166f89fee900b7c14d87d53fbac47242Timo Sirainen msg_search_ctx = msg_search_arg_context(ctx->index_ctx, arg);
0a53eb0283d7ec28c6105f61e118b96fce8ecb95Timo Sirainen if (msg_search_ctx == NULL) {
0a53eb0283d7ec28c6105f61e118b96fce8ecb95Timo Sirainen ARG_SET_RESULT(arg, 0);
0a53eb0283d7ec28c6105f61e118b96fce8ecb95Timo Sirainen return;
0a53eb0283d7ec28c6105f61e118b96fce8ecb95Timo Sirainen }
00e7c3010f7da4a49881a7feb05e413af353af0aTimo Sirainen
00e7c3010f7da4a49881a7feb05e413af353af0aTimo Sirainen i_stream_seek(ctx->input, 0);
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 MAIL_FETCH_MESSAGE_PARTS);
00e7c3010f7da4a49881a7feb05e413af353af0aTimo Sirainen
00e7c3010f7da4a49881a7feb05e413af353af0aTimo Sirainen i_stream_seek(ctx->input, 0);
00e7c3010f7da4a49881a7feb05e413af353af0aTimo Sirainen ret = message_search_msg(msg_search_ctx, ctx->input, NULL);
00e7c3010f7da4a49881a7feb05e413af353af0aTimo Sirainen i_assert(ret >= 0 || ctx->input->stream_errno != 0);
00e7c3010f7da4a49881a7feb05e413af353af0aTimo Sirainen }
0a53eb0283d7ec28c6105f61e118b96fce8ecb95Timo Sirainen
00e7c3010f7da4a49881a7feb05e413af353af0aTimo Sirainen ARG_SET_RESULT(arg, ret > 0);
00e7c3010f7da4a49881a7feb05e413af353af0aTimo Sirainen}
00e7c3010f7da4a49881a7feb05e413af353af0aTimo Sirainen
00e7c3010f7da4a49881a7feb05e413af353af0aTimo Sirainenstatic bool search_arg_match_text(struct mail_search_arg *args,
00e7c3010f7da4a49881a7feb05e413af353af0aTimo Sirainen struct index_search_context *ctx)
00e7c3010f7da4a49881a7feb05e413af353af0aTimo Sirainen{
00e7c3010f7da4a49881a7feb05e413af353af0aTimo Sirainen struct istream *input;
00e7c3010f7da4a49881a7feb05e413af353af0aTimo Sirainen struct mailbox_header_lookup_ctx *headers_ctx;
00e7c3010f7da4a49881a7feb05e413af353af0aTimo Sirainen const char *const *headers;
00e7c3010f7da4a49881a7feb05e413af353af0aTimo Sirainen bool have_headers, have_body;
00e7c3010f7da4a49881a7feb05e413af353af0aTimo Sirainen
00e7c3010f7da4a49881a7feb05e413af353af0aTimo Sirainen /* first check what we need to use */
7ca63fa4166f89fee900b7c14d87d53fbac47242Timo Sirainen headers = mail_search_args_analyze(args, &have_headers, &have_body);
7ca63fa4166f89fee900b7c14d87d53fbac47242Timo Sirainen if (!have_headers && !have_body)
7ca63fa4166f89fee900b7c14d87d53fbac47242Timo Sirainen return TRUE;
7ca63fa4166f89fee900b7c14d87d53fbac47242Timo Sirainen
7ca63fa4166f89fee900b7c14d87d53fbac47242Timo Sirainen if (have_headers) {
7ca63fa4166f89fee900b7c14d87d53fbac47242Timo Sirainen struct search_header_context hdr_ctx;
7ca63fa4166f89fee900b7c14d87d53fbac47242Timo Sirainen
7ca63fa4166f89fee900b7c14d87d53fbac47242Timo Sirainen if (have_body)
7ca63fa4166f89fee900b7c14d87d53fbac47242Timo Sirainen headers = NULL;
7ca63fa4166f89fee900b7c14d87d53fbac47242Timo Sirainen
7ca63fa4166f89fee900b7c14d87d53fbac47242Timo Sirainen if (headers == NULL) {
00e7c3010f7da4a49881a7feb05e413af353af0aTimo Sirainen headers_ctx = NULL;
00e7c3010f7da4a49881a7feb05e413af353af0aTimo Sirainen if (mail_get_stream(ctx->mail, NULL, NULL, &input) < 0)
00e7c3010f7da4a49881a7feb05e413af353af0aTimo Sirainen return FALSE;
00e7c3010f7da4a49881a7feb05e413af353af0aTimo Sirainen } else {
7ca63fa4166f89fee900b7c14d87d53fbac47242Timo Sirainen /* FIXME: do this once in init */
7ca63fa4166f89fee900b7c14d87d53fbac47242Timo Sirainen i_assert(*headers != NULL);
00e7c3010f7da4a49881a7feb05e413af353af0aTimo Sirainen headers_ctx =
00e7c3010f7da4a49881a7feb05e413af353af0aTimo Sirainen mailbox_header_lookup_init(&ctx->ibox->box,
00e7c3010f7da4a49881a7feb05e413af353af0aTimo Sirainen headers);
00e7c3010f7da4a49881a7feb05e413af353af0aTimo Sirainen if (mail_get_header_stream(ctx->mail, headers_ctx,
7ca63fa4166f89fee900b7c14d87d53fbac47242Timo Sirainen &input) < 0) {
7ca63fa4166f89fee900b7c14d87d53fbac47242Timo Sirainen mailbox_header_lookup_unref(&headers_ctx);
7ca63fa4166f89fee900b7c14d87d53fbac47242Timo Sirainen return FALSE;
00e7c3010f7da4a49881a7feb05e413af353af0aTimo Sirainen }
00e7c3010f7da4a49881a7feb05e413af353af0aTimo Sirainen }
00e7c3010f7da4a49881a7feb05e413af353af0aTimo Sirainen
00e7c3010f7da4a49881a7feb05e413af353af0aTimo Sirainen memset(&hdr_ctx, 0, sizeof(hdr_ctx));
7ca63fa4166f89fee900b7c14d87d53fbac47242Timo Sirainen hdr_ctx.index_context = ctx;
00e7c3010f7da4a49881a7feb05e413af353af0aTimo Sirainen hdr_ctx.custom_header = TRUE;
00e7c3010f7da4a49881a7feb05e413af353af0aTimo Sirainen hdr_ctx.args = args;
00e7c3010f7da4a49881a7feb05e413af353af0aTimo Sirainen hdr_ctx.parse_headers = headers == NULL &&
00e7c3010f7da4a49881a7feb05e413af353af0aTimo Sirainen index_mail_want_parse_headers(ctx->imail);
00e7c3010f7da4a49881a7feb05e413af353af0aTimo Sirainen
00e7c3010f7da4a49881a7feb05e413af353af0aTimo Sirainen if (hdr_ctx.parse_headers)
index_mail_parse_header_init(ctx->imail, headers_ctx);
message_parse_header(input, NULL, hdr_parser_flags,
search_header, &hdr_ctx);
if (headers_ctx != NULL)
mailbox_header_lookup_unref(&headers_ctx);
} else {
struct message_size hdr_size;
if (mail_get_stream(ctx->mail, &hdr_size, NULL, &input) < 0)
return FALSE;
i_stream_seek(input, hdr_size.physical_size);
}
if (have_body) {
struct search_body_context body_ctx;
memset(&body_ctx, 0, sizeof(body_ctx));
body_ctx.index_ctx = ctx;
body_ctx.input = input;
(void)mail_get_parts(ctx->mail, &body_ctx.part);
mail_search_args_foreach(args, search_body, &body_ctx);
}
return TRUE;
}
static bool search_msgset_fix_limits(const struct mail_index_header *hdr,
ARRAY_TYPE(seq_range) *seqset, bool not)
{
struct seq_range *range;
unsigned int count;
i_assert(hdr->messages_count > 0);
range = array_get_modifiable(seqset, &count);
if (count > 0) {
i_assert(range[0].seq1 != 0);
if (range[count-1].seq2 == (uint32_t)-1) {
/* "*" used, make sure the last message is in the range
(e.g. with count+1:* we still want to include it) */
seq_range_array_add(seqset, 0, hdr->messages_count);
}
/* remove all non-existing messages */
seq_range_array_remove_range(seqset, hdr->messages_count + 1,
(uint32_t)-1);
}
if (!not)
return array_count(seqset) > 0;
else {
/* if all messages are in the range, it can't match */
range = array_get_modifiable(seqset, &count);
return range[0].seq1 != 1 ||
range[count-1].seq2 != hdr->messages_count;
}
}
static void search_msgset_fix(const struct mail_index_header *hdr,
ARRAY_TYPE(seq_range) *seqset,
uint32_t *seq1_r, uint32_t *seq2_r, bool not)
{
const struct seq_range *range;
unsigned int count;
uint32_t min_seq, max_seq;
if (!search_msgset_fix_limits(hdr, seqset, not)) {
*seq1_r = (uint32_t)-1;
*seq2_r = 0;
return;
}
range = array_get(seqset, &count);
if (!not) {
min_seq = range[0].seq1;
max_seq = range[count-1].seq2;
} else {
min_seq = range[0].seq1 > 1 ? 1 : range[0].seq2 + 1;
max_seq = range[count-1].seq2 < hdr->messages_count ?
hdr->messages_count : range[count-1].seq1 - 1;
if (min_seq > max_seq) {
*seq1_r = (uint32_t)-1;
*seq2_r = 0;
return;
}
}
if (*seq1_r < min_seq || *seq1_r == 0)
*seq1_r = min_seq;
if (*seq2_r > max_seq)
*seq2_r = max_seq;
}
static void search_or_parse_msgset_args(const struct mail_index_header *hdr,
struct mail_search_arg *args,
uint32_t *seq1_r, uint32_t *seq2_r)
{
uint32_t seq1, seq2, min_seq1 = 0, max_seq2 = 0;
for (; args != NULL; args = args->next) {
seq1 = 1; seq2 = hdr->messages_count;
switch (args->type) {
case SEARCH_SUB:
i_assert(!args->not);
search_parse_msgset_args(hdr, args->value.subargs,
&seq1, &seq2);
break;
case SEARCH_OR:
i_assert(!args->not);
search_or_parse_msgset_args(hdr, args->value.subargs,
&seq1, &seq2);
break;
case SEARCH_SEQSET:
search_msgset_fix(hdr, &args->value.seqset,
&seq1, &seq2, args->not);
break;
default:
break;
}
if (min_seq1 == 0) {
min_seq1 = seq1;
max_seq2 = seq2;
} else {
if (seq1 < min_seq1)
min_seq1 = seq1;
if (seq2 > max_seq2)
max_seq2 = seq2;
}
}
i_assert(min_seq1 != 0);
if (min_seq1 > *seq1_r)
*seq1_r = min_seq1;
if (max_seq2 < *seq2_r)
*seq2_r = max_seq2;
}
static void search_parse_msgset_args(const struct mail_index_header *hdr,
struct mail_search_arg *args,
uint32_t *seq1_r, uint32_t *seq2_r)
{
for (; args != NULL; args = args->next) {
switch (args->type) {
case SEARCH_SUB:
i_assert(!args->not);
search_parse_msgset_args(hdr, args->value.subargs,
seq1_r, seq2_r);
break;
case SEARCH_OR:
/* go through our children and use the widest seqset
range */
i_assert(!args->not);
search_or_parse_msgset_args(hdr, args->value.subargs,
seq1_r, seq2_r);
break;
case SEARCH_SEQSET:
search_msgset_fix(hdr, &args->value.seqset,
seq1_r, seq2_r, args->not);
break;
default:
break;
}
}
}
static void search_limit_lowwater(struct index_search_context *ctx,
uint32_t uid_lowwater, uint32_t *first_seq)
{
uint32_t seq1, seq2;
if (uid_lowwater == 0)
return;
mail_index_lookup_seq_range(ctx->view, uid_lowwater, (uint32_t)-1,
&seq1, &seq2);
if (*first_seq < seq1)
*first_seq = seq1;
}
static bool search_limit_by_flags(struct index_search_context *ctx,
const struct mail_index_header *hdr,
struct mail_search_arg *args,
uint32_t *seq1, uint32_t *seq2)
{
for (; args != NULL; args = args->next) {
if (args->type != SEARCH_FLAGS) {
if (args->type == SEARCH_ALL) {
if (args->not)
return FALSE;
}
continue;
}
if ((args->value.flags & MAIL_SEEN) != 0) {
/* SEEN with 0 seen? */
if (!args->not && hdr->seen_messages_count == 0)
return FALSE;
if (hdr->seen_messages_count == hdr->messages_count) {
/* UNSEEN with all seen? */
if (args->not)
return FALSE;
/* SEEN with all seen */
args->match_always = TRUE;
} else if (args->not) {
/* UNSEEN with lowwater limiting */
search_limit_lowwater(ctx,
hdr->first_unseen_uid_lowwater, seq1);
}
}
if ((args->value.flags & MAIL_DELETED) != 0) {
/* DELETED with 0 deleted? */
if (!args->not && hdr->deleted_messages_count == 0)
return FALSE;
if (hdr->deleted_messages_count ==
hdr->messages_count) {
/* UNDELETED with all deleted? */
if (args->not)
return FALSE;
/* DELETED with all deleted */
args->match_always = TRUE;
} else if (!args->not) {
/* DELETED with lowwater limiting */
search_limit_lowwater(ctx,
hdr->first_deleted_uid_lowwater, seq1);
}
}
}
return *seq1 <= *seq2;
}
static void search_get_seqset(struct index_search_context *ctx,
struct mail_search_arg *args)
{
const struct mail_index_header *hdr;
hdr = mail_index_get_header(ctx->view);
if (hdr->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 = hdr->messages_count;
search_parse_msgset_args(hdr, args, &ctx->seq1, &ctx->seq2);
if (ctx->seq1 == 0) {
ctx->seq1 = 1;
ctx->seq2 = hdr->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, hdr, 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;
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_transaction_context *t =
(struct index_transaction_context *)_t;
struct index_search_context *ctx;
ctx = i_new(struct index_search_context, 1);
ctx->mail_ctx.transaction = _t;
ctx->ibox = t->ibox;
ctx->view = t->trans_view;
ctx->mail_ctx.args = args;
ctx->mail_ctx.sort_program = index_sort_program_init(_t, sort_program);
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, 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->ibox->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)
{
struct mail_search_arg *arg;
int ret;
/* next search only from cached arguments */
ret = mail_search_args_foreach(ctx->mail_ctx.args->args,
search_cached_arg, ctx);
if (ret >= 0)
return ret > 0;
/* open the mail file and check the rest */
if (!search_arg_match_text(ctx->mail_ctx.args->args, ctx))
return FALSE;
for (arg = ctx->mail_ctx.args->args; arg != NULL; arg = arg->next) {
if (arg->result != 1)
return FALSE;
}
return TRUE;
}
static void index_storage_search_notify(struct mailbox *box,
struct index_search_context *ctx)
{
const struct mail_index_header *hdr;
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) {
hdr = mail_index_get_header(ctx->ibox->view);
percentage = ctx->mail->seq * 100.0 / hdr->messages_count;
msecs = (ioloop_timeval.tv_sec -
ctx->search_start_time.tv_sec) * 1000 +
(ioloop_timeval.tv_usec -
ctx->search_start_time.tv_usec) / 1000;
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_SENTBEFORE:
case SEARCH_SENTON:
case SEARCH_SENTSINCE:
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:
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;
}
int 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 int count = 0;
bool 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 0;
return 1;
}
ctx->mail = mail;
if (ioloop_time - ctx->last_notify.tv_sec >=
SEARCH_NOTIFY_INTERVAL_SECS)
index_storage_search_notify(box, ctx);
while (box->v.search_next_update_seq(_ctx)) {
mail_set_seq(mail, _ctx->seq);
ctx->imail = mail_private->v.get_index_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;
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);
}
if (++count == SEARCH_NONBLOCK_COUNT) {
*tryagain_r = TRUE;
return 0;
}
}
ctx->mail = NULL;
ctx->imail = NULL;
if (!match && _ctx->sort_program != NULL && !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 ? -1 : (match ? 1 : 0);
}
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)
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);
}
}
return ret != 0;
}