mail-search.c revision 95f3ad1f505146ba2f4d2f4022244250a58b836f
/* Copyright (c) 2002-2008 Dovecot authors, see the included COPYING file */
#include "lib.h"
#include "array.h"
#include "buffer.h"
#include "mail-index.h"
#include "mail-search.h"
void mail_search_args_reset(struct mail_search_arg *args, bool full_reset)
{
while (args != NULL) {
if (args->type == SEARCH_OR || args->type == SEARCH_SUB)
mail_search_args_reset(args->value.subargs, full_reset);
if (!args->match_always)
args->result = -1;
else {
if (!full_reset)
args->result = 1;
else {
args->match_always = FALSE;
args->result = -1;
}
}
args = args->next;
}
}
static void search_arg_foreach(struct mail_search_arg *arg,
mail_search_foreach_callback_t *callback,
void *context)
{
struct mail_search_arg *subarg;
if (arg->result != -1)
return;
if (arg->type == SEARCH_SUB) {
/* sublist of conditions */
i_assert(arg->value.subargs != NULL);
arg->result = 1;
subarg = arg->value.subargs;
while (subarg != NULL) {
if (subarg->result == -1)
search_arg_foreach(subarg, callback, context);
if (subarg->result == -1)
arg->result = -1;
else if (subarg->result == 0) {
/* didn't match */
arg->result = 0;
break;
}
subarg = subarg->next;
}
if (arg->not && arg->result != -1)
arg->result = !arg->result;
} else if (arg->type == SEARCH_OR) {
/* OR-list of conditions */
i_assert(arg->value.subargs != NULL);
subarg = arg->value.subargs;
arg->result = 0;
while (subarg != NULL) {
if (subarg->result == -1)
search_arg_foreach(subarg, callback, context);
if (subarg->result == -1)
arg->result = -1;
else if (subarg->result > 0) {
/* matched */
arg->result = 1;
break;
}
subarg = subarg->next;
}
if (arg->not && arg->result != -1)
arg->result = !arg->result;
} else {
/* just a single condition */
callback(arg, context);
}
}
#undef mail_search_args_foreach
int mail_search_args_foreach(struct mail_search_arg *args,
mail_search_foreach_callback_t *callback,
void *context)
{
int result;
result = 1;
for (; args != NULL; args = args->next) {
search_arg_foreach(args, callback, context);
if (args->result == 0) {
/* didn't match */
return 0;
}
if (args->result == -1)
result = -1;
}
return result;
}
static void
search_arg_analyze(struct mail_search_arg *arg, buffer_t *headers,
bool *have_body, bool *have_text)
{
static const char *date_hdr = "Date";
struct mail_search_arg *subarg;
if (arg->result != -1)
return;
switch (arg->type) {
case SEARCH_OR:
case SEARCH_SUB:
subarg = arg->value.subargs;
while (subarg != NULL) {
if (subarg->result == -1) {
search_arg_analyze(subarg, headers,
have_body, have_text);
}
subarg = subarg->next;
}
break;
case SEARCH_SENTBEFORE:
case SEARCH_SENTON:
case SEARCH_SENTSINCE:
buffer_append(headers, &date_hdr, sizeof(const char *));
break;
case SEARCH_HEADER:
case SEARCH_HEADER_ADDRESS:
case SEARCH_HEADER_COMPRESS_LWSP:
buffer_append(headers, &arg->hdr_field_name,
sizeof(const char *));
break;
case SEARCH_BODY:
case SEARCH_BODY_FAST:
*have_body = TRUE;
break;
case SEARCH_TEXT:
case SEARCH_TEXT_FAST:
*have_text = TRUE;
*have_body = TRUE;
break;
default:
break;
}
}
const char *const *
mail_search_args_analyze(struct mail_search_arg *args,
bool *have_headers, bool *have_body)
{
const char *null = NULL;
buffer_t *headers;
bool have_text;
*have_headers = *have_body = have_text = FALSE;
headers = buffer_create_dynamic(pool_datastack_create(), 128);
for (; args != NULL; args = args->next)
search_arg_analyze(args, headers, have_body, &have_text);
*have_headers = have_text || headers->used != 0;
if (headers->used == 0 || have_text)
return NULL;
buffer_append(headers, &null, sizeof(const char *));
return buffer_get_data(headers, NULL);
}
static struct mail_keywords *
mail_search_keywords_merge(struct mail_keywords **_kw1,
struct mail_keywords **_kw2)
{
struct mail_keywords *kw1 = *_kw1, *kw2 = *_kw2;
struct mail_keywords *new_kw;
i_assert(kw1->index == kw2->index);
T_BEGIN {
ARRAY_TYPE(keyword_indexes) new_indexes;
unsigned int i, j;
t_array_init(&new_indexes, kw1->count + kw2->count + 1);
array_append(&new_indexes, kw1->idx, kw1->count);
for (i = 0; i < kw2->count; i++) {
/* don't add duplicates */
for (j = 0; j < kw1->count; j++) {
if (kw1->idx[j] == kw2->idx[i])
break;
}
if (j == kw1->count)
array_append(&new_indexes, kw2->idx+i, 1);
}
new_kw = mail_index_keywords_create_from_indexes(kw1->index,
&new_indexes);
} T_END;
mail_index_keywords_free(_kw1);
mail_index_keywords_free(_kw2);
return new_kw;
}
static void
mail_search_args_simplify_sub(struct mail_search_arg *args, bool parent_and)
{
struct mail_search_arg *sub, *prev = NULL;
struct mail_search_arg *prev_flags_arg, *prev_not_flags_arg;
struct mail_search_arg *prev_kw_arg, *prev_not_kw_arg;
prev_flags_arg = prev_not_flags_arg = NULL;
prev_kw_arg = prev_not_kw_arg = NULL;
for (; args != NULL;) {
if (args->not && (args->type == SEARCH_SUB ||
args->type == SEARCH_OR)) {
/* neg(p and q and ..) == neg(p) or neg(q) or ..
neg(p or q or ..) == neg(p) and neg(q) and .. */
args->type = args->type == SEARCH_SUB ?
SEARCH_OR : SEARCH_SUB;
args->not = FALSE;
sub = args->value.subargs;
for (; sub != NULL; sub = sub->next)
sub->not = !sub->not;
}
if ((args->type == SEARCH_SUB && parent_and) ||
(args->type == SEARCH_OR && !parent_and)) {
/* p and (q and ..) == p and q and ..
p or (q or ..) == p or q or .. */
sub = args->value.subargs;
for (; sub->next != NULL; sub = sub->next) ;
sub->next = args->next;
*args = *args->value.subargs;
continue;
}
if (args->type == SEARCH_SUB || args->type == SEARCH_OR) {
mail_search_args_simplify_sub(args->value.subargs,
args->type == SEARCH_SUB);
}
/* merge all flags arguments */
if (args->type == SEARCH_FLAGS && !args->not && parent_and) {
if (prev_flags_arg == NULL)
prev_flags_arg = args;
else {
prev_flags_arg->value.flags |=
args->value.flags;
prev->next = args->next;
args = args->next;
continue;
}
} else if (args->type == SEARCH_FLAGS && args->not &&
!parent_and) {
if (prev_not_flags_arg == NULL)
prev_not_flags_arg = args;
else {
prev_not_flags_arg->value.flags |=
args->value.flags;
prev->next = args->next;
args = args->next;
continue;
}
}
/* merge all keywords arguments */
if (args->type == SEARCH_KEYWORDS && !args->not && parent_and) {
if (prev_kw_arg == NULL)
prev_kw_arg = args;
else {
prev_kw_arg->value.keywords =
mail_search_keywords_merge(
&prev_kw_arg->value.keywords,
&args->value.keywords);
prev->next = args->next;
args = args->next;
continue;
}
} else if (args->type == SEARCH_KEYWORDS && args->not &&
!parent_and) {
if (prev_not_kw_arg == NULL)
prev_not_kw_arg = args;
else {
prev_not_kw_arg->value.keywords =
mail_search_keywords_merge(
&prev_not_kw_arg->value.keywords,
&args->value.keywords);
prev->next = args->next;
args = args->next;
continue;
}
}
prev = args;
args = args->next;
}
}
void mail_search_args_simplify(struct mail_search_arg *args)
{
mail_search_args_simplify_sub(args, TRUE);
}