mail-search-args-simplify.c revision 1270cb6b6139001b0a89f595ad0868b1f3a0af45
2e37d45867d081db150ab78dad303b9077aea24fTimo Sirainen/* Copyright (c) 2002-2017 Dovecot authors, see the included COPYING file */
2e78f05b11df23ec2731afaf8f19d5b5240cb29fTimo Sirainen /* arg mask => prev_arg */
2e78f05b11df23ec2731afaf8f19d5b5240cb29fTimo Sirainen HASH_TABLE(struct mail_search_simplify_prev_arg *,
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen struct mail_search_simplify_prev_arg *) prev_args;
660b99a7059824676b2b8d6f79b8e15d47df25a2Timo Sirainenmail_search_simplify_prev_arg_cmp(const struct mail_search_simplify_prev_arg *arg1,
660b99a7059824676b2b8d6f79b8e15d47df25a2Timo Sirainen const struct mail_search_simplify_prev_arg *arg2)
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen ret = memcmp(&arg1->bin_mask, &arg2->bin_mask, sizeof(arg1->bin_mask));
b9c76fe9d9ca194816606342da1ddbd9be6bc8abTimo Sirainen ret = null_strcmp(arg1->hdr_field_name_mask, arg2->hdr_field_name_mask);
b9c76fe9d9ca194816606342da1ddbd9be6bc8abTimo Sirainen ret = null_strcmp(arg1->str_mask, arg2->str_mask);
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainenstatic unsigned int
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainenmail_search_simplify_prev_arg_hash(const struct mail_search_simplify_prev_arg *arg)
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen unsigned int hash;
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen hash = mem_hash(&arg->bin_mask, sizeof(arg->bin_mask));
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainenstatic void mail_search_arg_get_base_mask(const struct mail_search_arg *arg,
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen mask_r->bin_mask.search_flags = arg->value.search_flags;
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainenstatic struct mail_search_arg **
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainenmail_search_args_simplify_get_prev_argp(struct mail_search_simplify_ctx *ctx,
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen const struct mail_search_simplify_prev_arg *mask)
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen struct mail_search_simplify_prev_arg *prev_arg;
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen prev_arg = hash_table_lookup(ctx->prev_args, mask);
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen prev_arg = p_new(ctx->pool, struct mail_search_simplify_prev_arg, 1);
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen p_strdup(ctx->pool, mask->hdr_field_name_mask);
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen hash_table_insert(ctx->prev_args, prev_arg, prev_arg);
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainenmail_search_args_merge_mask(struct mail_search_simplify_ctx *ctx,
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen const struct mail_search_simplify_prev_arg *mask)
2598b2f36365b52d9754b9348a5be29569293e46Timo Sirainen prev_argp = mail_search_args_simplify_get_prev_argp(ctx, mask);
1bf5c6c20f3d51f13d3240cfb46e471074c86276Timo Sirainen if ((*prev_argp)->match_not != args->match_not) {
1bf5c6c20f3d51f13d3240cfb46e471074c86276Timo Sirainen /* a && !a = 0 */
1bf5c6c20f3d51f13d3240cfb46e471074c86276Timo Sirainen /* duplicate keyword. */
e5acc283bf030b0b5c79ca4e52d315c516a299faPascal Volkstatic bool mail_search_args_merge_flags(struct mail_search_simplify_ctx *ctx,
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen return mail_search_args_merge_mask(ctx, args, &mask);
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainenmail_search_args_merge_keywords(struct mail_search_simplify_ctx *ctx,
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen return mail_search_args_merge_mask(ctx, args, &mask);
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainenstatic void mail_search_args_simplify_set(struct mail_search_arg *args)
b8afdaa1bffe2f27cd4b02bf3bfbd2d297c8e648Timo Sirainen unsigned int count;
b8afdaa1bffe2f27cd4b02bf3bfbd2d297c8e648Timo Sirainen /* invert the set to drop the NOT. Note that (uint32_t)-1
b8afdaa1bffe2f27cd4b02bf3bfbd2d297c8e648Timo Sirainen matches the last existing mail, which we don't know at this
b8afdaa1bffe2f27cd4b02bf3bfbd2d297c8e648Timo Sirainen point. lib-imap/imap-seqset.c has similar code that
b8afdaa1bffe2f27cd4b02bf3bfbd2d297c8e648Timo Sirainen disallows using (uint32_t)-1 as a real UID. */
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen if (seq_range_exists(&args->value.seqset, (uint32_t)-1))
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen seq_range_array_invert(&args->value.seqset, 1, (uint32_t)-2);
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen seqset = array_get(&args->value.seqset, &count);
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen if (count == 1 && seqset->seq1 == 1 && seqset->seq2 >= (uint32_t)-2) {
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen /* 1:* is the same as ALL. */
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen } else if (count == 0) {
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen /* empty set is the same as NOT ALL. this is mainly coming
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen from mail_search_args_merge_set() intersection. */
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainenstatic bool mail_search_args_merge_set(struct mail_search_simplify_ctx *ctx,
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen /* "*" used - can't simplify it */
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen prev_argp = mail_search_args_simplify_get_prev_argp(ctx, &mask);
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen seq_range_array_intersect(&(*prev_argp)->value.seqset,
53dfcefa9440a49d703e49193819a79be99c9ba6Timo Sirainen seq_range_array_merge(&(*prev_argp)->value.seqset,
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainenstatic bool mail_search_args_merge_time(struct mail_search_simplify_ctx *ctx,
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen struct mail_search_arg **prev_argp, *prev_arg;
75e46142d8fbac811df8f2ca58d9a2f48a75d65fTimo Sirainen mask.bin_mask.date_type = args->value.date_type;
75e46142d8fbac811df8f2ca58d9a2f48a75d65fTimo Sirainen prev_argp = mail_search_args_simplify_get_prev_argp(ctx, &mask);
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen if (prev_arg->value.time < args->value.time) {
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen /* prev_arg < 5 AND arg < 10 */
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen /* prev_arg < 10 AND arg < 5 */
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen if (prev_arg->value.time < args->value.time) {
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen /* prev_arg < 5 OR arg < 10 */
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen /* prev_arg < 10 OR arg < 5 */
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen if (prev_arg->value.time < args->value.time) {
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen /* prev_arg >= 5 AND arg >= 10 */
77f1da4b5e2b800197d8db548235497d5e9d6a4fTimo Sirainen /* prev_arg >= 10 AND arg >= 5 */
18f1bbf05980d3c53ecae81b62574212f0891522Timo Sirainen if (prev_arg->value.time < args->value.time) {
75e46142d8fbac811df8f2ca58d9a2f48a75d65fTimo Sirainen /* prev_arg >= 5 OR arg >= 10 */
77f1da4b5e2b800197d8db548235497d5e9d6a4fTimo Sirainen /* prev_arg >= 10 OR arg >= 5 */
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainenstatic bool mail_search_args_merge_size(struct mail_search_simplify_ctx *ctx,
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen struct mail_search_arg **prev_argp, *prev_arg;
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen prev_argp = mail_search_args_simplify_get_prev_argp(ctx, &mask);
04b7dc631f33bf61f273138c679da9bd0910fb6dTimo Sirainen if (prev_arg->value.size < args->value.size) {
04b7dc631f33bf61f273138c679da9bd0910fb6dTimo Sirainen /* prev_arg < 5 AND arg < 10 */
04b7dc631f33bf61f273138c679da9bd0910fb6dTimo Sirainen /* prev_arg < 10 AND arg < 5 */
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen if (prev_arg->value.size < args->value.size) {
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen /* prev_arg < 5 OR arg < 10 */
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen /* prev_arg < 10 OR arg < 5 */
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen if (prev_arg->value.size < args->value.size) {
53dfcefa9440a49d703e49193819a79be99c9ba6Timo Sirainen /* prev_arg >= 5 AND arg >= 10 */
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen /* prev_arg >= 10 AND arg >= 5 */
2598b2f36365b52d9754b9348a5be29569293e46Timo Sirainen if (prev_arg->value.size < args->value.size) {
2ef0e8ee48c9683f7bd6698798efa3328e4322d1Timo Sirainen /* prev_arg >= 5 OR arg >= 10 */
6303191abcb37164f435ccdc56e9dbddf1288851Timo Sirainen /* prev_arg >= 10 OR arg >= 5 */
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainenstatic bool mail_search_args_merge_text(struct mail_search_simplify_ctx *ctx,
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen mask.hdr_field_name_mask = args->hdr_field_name;
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen return mail_search_args_merge_mask(ctx, args, &mask);
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainenmail_search_args_have_equal(const struct mail_search_arg *args,
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen for (arg = args; arg != NULL; arg = arg->next) {
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen if (mail_search_arg_one_equals(arg, wanted_arg))
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainenmail_search_args_remove_equal(struct mail_search_args *all_args,
a75907609d7c410c9e17beedfafbf28b4439fa8aTimo Sirainen if (mail_search_arg_one_equals(*argp, wanted_arg)) {
a75907609d7c410c9e17beedfafbf28b4439fa8aTimo Sirainen if (!mail_search_args_remove_equal(all_args, &(*argp)->value.subargs, wanted_arg, FALSE)) {
decb23442f9e6cd5c4845a9cb162029b8c6d5f0fTimo Sirainen /* we already verified that this should have
a75907609d7c410c9e17beedfafbf28b4439fa8aTimo Sirainenmail_search_args_have_all_equal(struct mail_search_arg *parent_arg,
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen for (arg = wanted_args; arg != NULL; arg = arg->next) {
46ec792dd4ccf6c34706c4774228301fafde6aa9Timo Sirainen if (!mail_search_args_have_equal(parent_arg->value.subargs, arg))
46ec792dd4ccf6c34706c4774228301fafde6aa9Timo Sirainenstatic unsigned int
8bb360f9e5de1c25e4f875205bb06e8bf15dae14Timo Sirainenmail_search_args_count(const struct mail_search_arg *args)
46ec792dd4ccf6c34706c4774228301fafde6aa9Timo Sirainen unsigned int count;
a75907609d7c410c9e17beedfafbf28b4439fa8aTimo Sirainenmail_search_args_simplify_drop_redundant_args(struct mail_search_args *all_args,
4c6ddf2491104f917d00e6900e833e80ea02c7b6Timo Sirainen struct mail_search_arg *arg, **argp, one_arg, *lowest_arg = NULL;
4c6ddf2491104f917d00e6900e833e80ea02c7b6Timo Sirainen child_subargs_type = and_arg ? SEARCH_OR : SEARCH_SUB;
4c6ddf2491104f917d00e6900e833e80ea02c7b6Timo Sirainen /* find the arg which has the lowest number of child args */
4c6ddf2491104f917d00e6900e833e80ea02c7b6Timo Sirainen for (arg = *argsp; arg != NULL; arg = arg->next) {
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen count = mail_search_args_count(arg->value.subargs);
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen /* if there are any args that include lowest_arg, drop the arg since
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen it's redundant. (non-SUB duplicates are dropped elsewhere.) */
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen if (*argp != lowest_arg && (*argp)->type == child_subargs_type &&
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen mail_search_args_have_all_equal(*argp, lowest_arg)) {
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainenmail_search_args_simplify_extract_common(struct mail_search_args *all_args,
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen /* Simple SUB example:
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen (a AND b) OR (a AND c) -> a AND (b OR c)
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen More complicated example:
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen (c1 AND c2 AND u1 AND u2) OR (c1 AND c2 AND u3 AND u4) ->
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen c1 AND c2 AND ((u1 AND u2) OR (u3 AND u4))
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen Similarly for ORs:
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen (a OR b) AND (a OR c) -> a OR (b AND c)
660b99a7059824676b2b8d6f79b8e15d47df25a2Timo Sirainen (c1 OR c2 OR u1 OR u2) AND (c1 OR c2 OR u3 OR u4) ->
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen c1 OR c2 OR ((u1 OR u2) AND (u3 OR u4))
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen struct mail_search_arg *arg, *sub_arg, *sub_next;
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen struct mail_search_arg *new_arg, *child_arg, *common_args = NULL;
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen if (*argsp == NULL || (*argsp)->next == NULL) {
7a23d586f07ec376e28e8f6f3f3392a4ac8b83bbTimo Sirainen /* single arg, nothing to extract */
0348172a5278d1f5aa2440f30346c390ddc17318Timo Sirainen child_subargs_type = and_arg ? SEARCH_OR : SEARCH_SUB;
7a23d586f07ec376e28e8f6f3f3392a4ac8b83bbTimo Sirainen /* find the first arg with child_subargs_type */
7a23d586f07ec376e28e8f6f3f3392a4ac8b83bbTimo Sirainen for (arg = *argsp; arg != NULL; arg = arg->next) {
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen for (sub_arg = arg->value.subargs; sub_arg != NULL; sub_arg = sub_next) {
decb23442f9e6cd5c4845a9cb162029b8c6d5f0fTimo Sirainen /* check if sub_arg is found from all the args */
decb23442f9e6cd5c4845a9cb162029b8c6d5f0fTimo Sirainen for (arg = *argsp; arg != NULL; arg = arg->next) {
a75907609d7c410c9e17beedfafbf28b4439fa8aTimo Sirainen if (mail_search_arg_one_equals(arg, sub_arg)) {
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen /* the whole arg matches */
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen mail_search_args_have_equal(arg->value.subargs, sub_arg)) {
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen /* exists as subarg */
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen /* extract the arg and put it to common_args */
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen mail_search_args_remove_equal(all_args, argsp, sub_arg, TRUE);
d3a7d023b47d2a137f01109e7b38702dca3f11d3Timo Sirainen /* replace all the original args with a single new SUB/OR arg */
d3a7d023b47d2a137f01109e7b38702dca3f11d3Timo Sirainen new_arg = p_new(pool, struct mail_search_arg, 1);
d3a7d023b47d2a137f01109e7b38702dca3f11d3Timo Sirainen /* there are only common args */
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen /* replace OR arg with AND(OR(non_common_args), common_args)
5a250816ffc4cc5db203f9410ea99b6601c7b91aTimo Sirainen replace AND arg with OR(AND(non_common_args), common_args) */
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen child_arg = p_new(pool, struct mail_search_arg, 1);
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen child_arg->type = and_arg ? SEARCH_SUB : SEARCH_OR;
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainenmail_search_args_simplify_sub(struct mail_search_args *all_args, pool_t pool,
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen struct mail_search_arg **argsp, bool parent_and)
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen struct mail_search_arg *sub, **all_argsp = argsp;
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen ctx.initialized = all_args->init_refcount > 0;
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen ctx.pool = pool_alloconly_create("mail search args simplify", 1024);
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen hash_table_create(&ctx.prev_args, ctx.pool, 0,
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen if (args->match_not && (args->type == SEARCH_SUB ||
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen /* neg(p and q and ..) == neg(p) or neg(q) or ..
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen neg(p or q or ..) == neg(p) and neg(q) and .. */
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen if ((args->type == SEARCH_SUB && parent_and) ||
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen ((args->type == SEARCH_SUB || args->type == SEARCH_OR) &&
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen /* p and (q and ..) == p and q and ..
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen p or (q or ..) == p or q or ..
case SEARCH_ALL: {
case SEARCH_FLAGS:
case SEARCH_KEYWORDS:
case SEARCH_SEQSET:
case SEARCH_UIDSET:
case SEARCH_BEFORE:
case SEARCH_ON:
case SEARCH_SINCE:
case SEARCH_SMALLER:
case SEARCH_LARGER:
case SEARCH_BODY:
case SEARCH_TEXT:
case SEARCH_HEADER:
case SEARCH_HEADER_ADDRESS:
if (merged) {
bool parent_and)
return removals;
case SEARCH_SUB:
case SEARCH_OR:
case SEARCH_INTHREAD:
return FALSE;
if (!parent_and) {
return TRUE;
bool removals;
if (removals)
} while (removals);