/* Copyright (c) 2002-2018 Dovecot authors, see the included COPYING file */
#include "lib.h"
#include "array.h"
#include "hash.h"
#include "mail-search.h"
struct mail_search_simplify_prev_arg {
struct {
bool match_not;
bool fuzzy;
} bin_mask;
const char *hdr_field_name_mask;
const char *str_mask;
};
struct mail_search_simplify_ctx {
/* arg mask => prev_arg */
HASH_TABLE(struct mail_search_simplify_prev_arg *,
bool parent_and;
bool removals;
bool initialized;
};
static int
const struct mail_search_simplify_prev_arg *arg2)
{
int ret;
if (ret == 0)
if (ret == 0)
return ret;
}
static unsigned int
{
unsigned int hash;
return hash;
}
struct mail_search_simplify_prev_arg *mask_r)
{
}
static struct mail_search_arg **
const struct mail_search_simplify_prev_arg *mask)
{
}
}
static bool
struct mail_search_arg *args,
const struct mail_search_simplify_prev_arg *mask)
{
return FALSE;
}
if (ctx->initialized)
/* a && !a = 0 */
if (ctx->initialized)
}
/* duplicate keyword. */
return TRUE;
}
struct mail_search_arg *args)
{
}
static bool
struct mail_search_arg *args)
{
}
{
unsigned int count;
/* invert the set to drop the NOT. Note that (uint32_t)-1
matches the last existing mail, which we don't know at this
point. lib-imap/imap-seqset.c has similar code that
disallows using (uint32_t)-1 as a real UID. */
return;
}
/* 1:* is the same as ALL. */
} else if (count == 0) {
/* empty set is the same as NOT ALL. this is mainly coming
from mail_search_args_merge_set() intersection. */
}
}
struct mail_search_arg *args)
{
/* "*" used - can't simplify it */
return FALSE;
}
return FALSE;
} else if (ctx->parent_and) {
return TRUE;
} else {
return TRUE;
}
}
struct mail_search_arg *args)
{
return FALSE;
}
case SEARCH_BEFORE:
if (ctx->parent_and) {
/* prev_arg < 5 AND arg < 10 */
} else {
/* prev_arg < 10 AND arg < 5 */
}
} else {
/* prev_arg < 5 OR arg < 10 */
} else {
/* prev_arg < 10 OR arg < 5 */
}
}
return TRUE;
case SEARCH_ON:
return TRUE;
return FALSE;
case SEARCH_SINCE:
if (ctx->parent_and) {
/* prev_arg >= 5 AND arg >= 10 */
} else {
/* prev_arg >= 10 AND arg >= 5 */
}
} else {
/* prev_arg >= 5 OR arg >= 10 */
} else {
/* prev_arg >= 10 OR arg >= 5 */
}
}
return TRUE;
default:
break;
}
return FALSE;
}
struct mail_search_arg *args)
{
return FALSE;
}
case SEARCH_SMALLER:
if (ctx->parent_and) {
/* prev_arg < 5 AND arg < 10 */
} else {
/* prev_arg < 10 AND arg < 5 */
}
} else {
/* prev_arg < 5 OR arg < 10 */
} else {
/* prev_arg < 10 OR arg < 5 */
}
}
return TRUE;
case SEARCH_LARGER:
if (ctx->parent_and) {
/* prev_arg >= 5 AND arg >= 10 */
} else {
/* prev_arg >= 10 AND arg >= 5 */
}
} else {
/* prev_arg >= 5 OR arg >= 10 */
} else {
/* prev_arg >= 10 OR arg >= 5 */
}
}
return TRUE;
default:
break;
}
return FALSE;
}
struct mail_search_arg *args)
{
}
static bool
const struct mail_search_arg *wanted_arg)
{
return TRUE;
}
return FALSE;
}
static bool
struct mail_search_arg **argsp,
const struct mail_search_arg *wanted_arg,
bool check_subs)
{
if (all_args->init_refcount > 0)
} else if (check_subs) {
/* we already verified that this should have
existed. */
i_unreached();
}
else
} else {
}
}
return found;
}
static bool
const struct mail_search_arg *wanted_args)
{
return FALSE;
}
return TRUE;
}
static unsigned int
{
unsigned int count;
return count;
}
static bool
struct mail_search_arg **argsp,
bool and_arg)
{
return FALSE;
/* find the arg which has the lowest number of child args */
lowest_arg = &one_arg;
break;
}
if (count < lowest_count) {
}
}
/* if there are any args that include lowest_arg, drop the arg since
it's redundant. (non-SUB duplicates are dropped elsewhere.) */
if (all_args->init_refcount > 0)
} else {
}
}
return ret;
}
static bool
struct mail_search_arg **argsp,
{
/* Simple SUB example:
(a AND b) OR (a AND c) -> a AND (b OR c)
More complicated example:
(c1 AND c2 AND u1 AND u2) OR (c1 AND c2 AND u3 AND u4) ->
c1 AND c2 AND ((u1 AND u2) OR (u3 AND u4))
Similarly for ORs:
(a OR b) AND (a OR c) -> a OR (b AND c)
(c1 OR c2 OR u1 OR u2) AND (c1 OR c2 OR u3 OR u4) ->
c1 OR c2 OR ((u1 OR u2) AND (u3 OR u4))
*/
/* single arg, nothing to extract */
return FALSE;
}
/* find the first arg with child_subargs_type */
break;
}
return FALSE;
/* check if sub_arg is found from all the args */
/* the whole arg matches */
/* exists as subarg */
} else {
break;
}
}
continue;
/* extract the arg and put it to common_args */
}
if (common_args == NULL)
return FALSE;
/* there are only common args */
} else {
/* replace OR arg with AND(OR(non_common_args), common_args)
or
replace AND arg with OR(AND(non_common_args), common_args) */
}
return TRUE;
}
static bool
{
bool merged;
/* neg(p and q and ..) == neg(p) or neg(q) or ..
neg(p or q or ..) == neg(p) and neg(q) and .. */
do {
}
/* p and (q and ..) == p and q and ..
p or (q or ..) == p or q or ..
(p) = p */
continue;
}
}
}
/* try to merge arguments */
case SEARCH_ALL: {
/* this arg has no siblings - no merging */
break;
}
/* .. AND ALL ..
.. OR NOT ALL ..
This arg is irrelevant -> drop */
break;
}
/* .. AND NOT ALL ..
.. OR ALL ..
The other args are irrelevant -> drop them */
break;
}
case SEARCH_FLAGS:
break;
case SEARCH_KEYWORDS:
break;
case SEARCH_SEQSET:
case SEARCH_UIDSET:
break;
case SEARCH_BEFORE:
case SEARCH_ON:
case SEARCH_SINCE:
break;
case SEARCH_SMALLER:
case SEARCH_LARGER:
break;
case SEARCH_BODY:
case SEARCH_TEXT:
/* BODY "" and TEXT "" matches everything */
break;
}
/* fall through */
case SEARCH_HEADER:
case SEARCH_HEADER_ADDRESS:
break;
default:
break;
}
if (merged) {
continue;
}
}
}
static bool
bool parent_and)
{
/* ignore non-flags */
/* can't merge these flags args */
} else if (prev_flags == NULL) {
/* first flags arg */
prev_flags = args;
} else {
/* merge to previous arg */
continue;
}
}
return removals;
}
static bool
struct mail_search_arg **argp,
bool parent_inthreads, bool parent_and)
{
case SEARCH_SUB:
case SEARCH_OR:
} else {
}
break;
case SEARCH_INTHREAD:
/* children converted to SEARCH_INTHREADs */
}
break;
default:
break;
}
}
return FALSE;
/* put all non-INTHREADs under a single INTHREAD */
}
}
if (!parent_and) {
/* We want to OR the args */
}
return TRUE;
}
{
bool removals;
/* we may have added some extra SUBs that could be dropped */
}
do {
if (removals)
/* do the flag merging into a single arg only at the end.
up until then they're treated as any other search args,
which simplifies their handling. after the flags merging is
done, further simplifications are still possible. */
} while (removals);
}