From: Marco Bettini Date: Thu, 20 Oct 2022 14:07:23 +0000 (+0000) Subject: lib-storage: enum mail_search_arg_type - Add SEARCH_NIL X-Git-Tag: 2.3.20~17 X-Git-Url: http://git.ipfire.org/?a=commitdiff_plain;h=07ec8339eeb3a7a7e9b3231922fbe03d5cac83db;p=thirdparty%2Fdovecot%2Fcore.git lib-storage: enum mail_search_arg_type - Add SEARCH_NIL --- diff --git a/src/lib-storage/index/imapc/imapc-search.c b/src/lib-storage/index/imapc/imapc-search.c index e395919f34..98fa8b5662 100644 --- a/src/lib-storage/index/imapc/imapc-search.c +++ b/src/lib-storage/index/imapc/imapc-search.c @@ -145,6 +145,8 @@ imapc_build_search_query_arg(struct imapc_mailbox *mbox, case SEARCH_MIMEPART: /* not supported for now */ break; + case SEARCH_NIL: + i_unreached(); } return FALSE; } diff --git a/src/lib-storage/index/index-search.c b/src/lib-storage/index/index-search.c index dec52f3074..05eaa399a8 100644 --- a/src/lib-storage/index/index-search.c +++ b/src/lib-storage/index/index-search.c @@ -1456,6 +1456,7 @@ static bool search_arg_is_static(struct mail_search_arg *arg) case SEARCH_MAILBOX_GLOB: case SEARCH_REAL_UID: case SEARCH_MIMEPART: + case SEARCH_NIL: return TRUE; } return FALSE; diff --git a/src/lib-storage/mail-search-args-cmdline.c b/src/lib-storage/mail-search-args-cmdline.c index 5b1c47fbae..23da429b7c 100644 --- a/src/lib-storage/mail-search-args-cmdline.c +++ b/src/lib-storage/mail-search-args-cmdline.c @@ -86,6 +86,8 @@ mail_search_arg_to_cmdline(string_t *dest, const struct mail_search_arg *arg) case SEARCH_REAL_UID: case SEARCH_MIMEPART: break; + case SEARCH_NIL: + i_unreached(); } new_arg = *arg; new_arg.match_not = FALSE; diff --git a/src/lib-storage/mail-search-args-imap.c b/src/lib-storage/mail-search-args-imap.c index e89e11940a..faf0a43c61 100644 --- a/src/lib-storage/mail-search-args-imap.c +++ b/src/lib-storage/mail-search-args-imap.c @@ -307,6 +307,9 @@ bool mail_search_arg_to_imap(string_t *dest, const struct mail_search_arg *arg, arg->value.mime_part, error_r)) return FALSE; break; + case SEARCH_NIL: + str_append(dest, "NIL "); + break; } return TRUE; } diff --git a/src/lib-storage/mail-search-args-simplify.c b/src/lib-storage/mail-search-args-simplify.c index 2a1cace6d6..e6490b542b 100644 --- a/src/lib-storage/mail-search-args-simplify.c +++ b/src/lib-storage/mail-search-args-simplify.c @@ -535,6 +535,118 @@ mail_search_args_simplify_extract_common(struct mail_search_args *all_args, return TRUE; } +static bool mail_search_args_nils_removable(enum mail_search_arg_type type) { + switch(type) { + case SEARCH_FLAGS: + case SEARCH_KEYWORDS: + case SEARCH_BEFORE: + case SEARCH_ON: + case SEARCH_SINCE: + case SEARCH_SMALLER: + case SEARCH_LARGER: + case SEARCH_GUID: + case SEARCH_MAILBOX: + case SEARCH_MAILBOX_GUID: + case SEARCH_MAILBOX_GLOB: + case SEARCH_MODSEQ: + case SEARCH_REAL_UID: + case SEARCH_SEQSET: + case SEARCH_UIDSET: + case SEARCH_MIMEPART: + case SEARCH_SAVEDATESUPPORTED: + /* these want NILs to become NOT ALL */ + return FALSE; + + case SEARCH_ALL: + case SEARCH_NIL: + case SEARCH_HEADER: + case SEARCH_HEADER_ADDRESS: + case SEARCH_HEADER_COMPRESS_LWSP: + case SEARCH_BODY: + case SEARCH_TEXT: + /* these allow to remove NILs */ + return TRUE; + + case SEARCH_INTHREAD: + case SEARCH_SUB: + case SEARCH_OR: + /* these are handled in the caller as they need + insight on the tree under that expression */ + i_unreached(); + + default: + i_unreached(); + } +} + +static bool +mail_search_args_handle_nils(struct mail_search_arg **argsp, bool *remove_nils_r) { + /* allow_remove + deny_remove + NILs count = args count */ + int allow_remove = 0; + int deny_remove = 0; + bool changed = FALSE; + + for (struct mail_search_arg *arg = *argsp; arg != NULL; arg = arg->next) { + + switch(arg->type) { + case SEARCH_INTHREAD: + case SEARCH_SUB: + case SEARCH_OR: { + bool term_remove_nils; + if (mail_search_args_handle_nils( + &arg->value.subargs, &term_remove_nils)) + changed = TRUE; + + if (arg->value.subargs == NULL) + arg->type = SEARCH_NIL; + else if (term_remove_nils) + allow_remove++; + else + deny_remove++; + break; + } + + case SEARCH_NIL: + break; + + default: + if (mail_search_args_nils_removable(arg->type)) + allow_remove++; + else + deny_remove++; + } + } + + /* The NILs can be removed if either: + (a) no other terms deny the removal + (b) or at least one other term allows the removal + otherwise, they are replaced with NOT ALL + + [DENY, NIL] -> [DENY, NOT ALL] -- NIL replaced with NOT ALL + [DENY, ALLOW, NIL] -> [DENY ALLOW] -- NIL removed + [ALLOW, NIL] -> [ALLOW] -- NIL removed + [NIL] -> [] -- NIL removed */ + bool remove_nils = deny_remove == 0 || allow_remove > 0; + if (remove_nils_r != NULL) *remove_nils_r = remove_nils; + + while (*argsp != NULL) { + bool is_nil = (*argsp)->type == SEARCH_NIL; + if (is_nil && remove_nils) { + changed = TRUE; + *argsp = (*argsp)->next; + } else if (is_nil) { + changed = TRUE; + (*argsp)->type = SEARCH_ALL; + (*argsp)->match_not = TRUE; + argsp = &(*argsp)->next; + } else { + argsp = &(*argsp)->next; + } + } + + return changed; +} + static bool mail_search_args_simplify_sub(struct mail_search_args *all_args, pool_t pool, struct mail_search_arg **argsp, bool parent_and) @@ -781,11 +893,11 @@ mail_search_args_unnest_inthreads(struct mail_search_args *args, void mail_search_args_simplify(struct mail_search_args *args) { - bool removals; - args->simplified = TRUE; - removals = mail_search_args_simplify_sub(args, args->pool, &args->args, TRUE); + bool removals = mail_search_args_handle_nils(&args->args, NULL); + if (mail_search_args_simplify_sub(args, args->pool, &args->args, TRUE)) + removals = TRUE; if (mail_search_args_unnest_inthreads(args, &args->args, FALSE, TRUE)) { /* we may have added some extra SUBs that could be dropped */ diff --git a/src/lib-storage/mail-search-register-imap.c b/src/lib-storage/mail-search-register-imap.c index c888b9e195..7df05000cb 100644 --- a/src/lib-storage/mail-search-register-imap.c +++ b/src/lib-storage/mail-search-register-imap.c @@ -82,6 +82,11 @@ imap_search_all(struct mail_search_build_context *ctx) { return mail_search_build_new(ctx, SEARCH_ALL); } +static struct mail_search_arg * +imap_search_nil(struct mail_search_build_context *ctx) +{ + return mail_search_build_new(ctx, SEARCH_NIL); +} static struct mail_search_arg * imap_search_uid(struct mail_search_build_context *ctx) @@ -539,6 +544,7 @@ static const struct mail_search_register_arg imap_register_args[] = { /* argument set operations */ { "NOT", imap_search_not }, { "OR", imap_search_or }, + { "NIL", imap_search_nil }, /* message sets */ { "ALL", imap_search_all }, diff --git a/src/lib-storage/mail-search.c b/src/lib-storage/mail-search.c index fc426533ec..e0f595d2da 100644 --- a/src/lib-storage/mail-search.c +++ b/src/lib-storage/mail-search.c @@ -374,6 +374,8 @@ mail_search_arg_dup_one(pool_t pool, const struct mail_search_arg *arg) new_arg->value.mime_part = mail_search_mime_part_dup(pool, arg->value.mime_part); break; + case SEARCH_NIL: + i_unreached(); } return new_arg; } @@ -651,6 +653,8 @@ bool mail_search_arg_one_equals(const struct mail_search_arg *arg1, return FALSE; switch (arg1->type) { + case SEARCH_NIL: + return TRUE; case SEARCH_OR: case SEARCH_SUB: return mail_search_arg_equals(arg1->value.subargs, diff --git a/src/lib-storage/mail-search.h b/src/lib-storage/mail-search.h index 8224e9ef2e..46bcbcbf25 100644 --- a/src/lib-storage/mail-search.h +++ b/src/lib-storage/mail-search.h @@ -47,7 +47,11 @@ enum mail_search_arg_type { SEARCH_MAILBOX_GUID, SEARCH_MAILBOX_GLOB, SEARCH_REAL_UID, - SEARCH_MIMEPART + SEARCH_MIMEPART, + + /* This term is allowed only in SEARCH_OR and SEARCH_SUB sublists. + When it is encountered during the simplification, it must be removed */ + SEARCH_NIL, }; enum mail_search_date_type { diff --git a/src/lib-storage/test-mail-search-args-simplify.c b/src/lib-storage/test-mail-search-args-simplify.c index dcde23665b..a4803920e2 100644 --- a/src/lib-storage/test-mail-search-args-simplify.c +++ b/src/lib-storage/test-mail-search-args-simplify.c @@ -229,6 +229,87 @@ static const struct { { "( OR BODY z BODY y ) ( OR BODY z BODY w ) BODY x BODY y BODY w", "BODY x BODY y BODY w" }, { "subject y", "SUBJECT y"}, + + /* NIL cases */ + { "NIL", ""}, + { "NOT NIL", ""}, + { "( ( NIL ) )", ""}, + { "( ( NOT NIL ) )", ""}, + + { "NIL NIL", "" }, + { "( ( NIL NIL ) )", "" }, + { "OR NIL NIL", "" }, + + { "OR ( NIL NIL ) OR NIL NIL", "" }, + { "( NIL NIL ) OR NIL NIL", "" }, + + { "NIL LARGER 0", "NOT ALL"}, + { "NOT NIL LARGER 0", "NOT ALL"}, + { "NIL NOT LARGER 0", "NOT ALL"}, + { "NOT NIL NOT LARGER 0", "NOT ALL"}, + + { "( NIL NIL ) TEXT uno", "TEXT uno" }, + { "OR NIL NIL TEXT uno", "TEXT uno" }, + + { "TEXT foo NIL", "TEXT foo"}, + { "NIL TEXT foo", "TEXT foo"}, + + { "NOT ( TEXT foo NIL )", "NOT TEXT foo"}, + { "NOT ( NIL TEXT foo )", "NOT TEXT foo"}, + + { "TEXT foo NOT NIL", "TEXT foo"}, + { "NOT NIL TEXT foo", "TEXT foo"}, + + { "NOT TEXT foo NIL", "NOT TEXT foo"}, + { "NIL NOT TEXT foo", "NOT TEXT foo"}, + + { "OR TEXT foo NIL", "TEXT foo"}, + { "OR NIL TEXT foo", "TEXT foo"}, + + { "NOT OR TEXT foo NIL", "NOT TEXT foo"}, + { "NOT OR NIL TEXT foo", "NOT TEXT foo"}, + + { "OR TEXT foo NOT NIL", "TEXT foo"}, + { "OR NOT NIL TEXT foo", "TEXT foo"}, + + { "OR NOT TEXT foo NIL", "NOT TEXT foo"}, + { "OR NIL NOT TEXT foo", "NOT TEXT foo"}, + + // avec & and & uno & due + // -> {en}(avec & NIL & uno & due) | {fr}(NIL & and & uno & (du | due) + { "OR ( TEXT avec NIL TEXT uno TEXT due ) ( NIL TEXT and TEXT uno OR TEXT du TEXT due )", + "OR (TEXT avec TEXT due) (TEXT and OR TEXT du TEXT due) TEXT uno" }, + + // avec | and | uno | due + // -> {en}(avec | NIL | uno | due) | {fr}(NIL | and | uno | (du | due) + { "OR ( OR TEXT avec OR NIL OR TEXT uno TEXT due ) ( OR NIL OR TEXT and OR TEXT uno OR TEXT du TEXT due )", + "OR TEXT avec OR TEXT uno OR TEXT due OR TEXT and TEXT du" }, + + // (avec | and) & (uno | due) + // -> {en}((avec | NIL) & (uno | due)) | {fr}((NIL | and) & (uno | (du | due))) + { "OR ( OR TEXT avec NIL OR TEXT uno TEXT due ) ( OR NIL TEXT and OR TEXT uno ( OR TEXT du TEXT due ) )", + "OR (TEXT avec OR TEXT uno TEXT due) (TEXT and OR TEXT uno OR TEXT du TEXT due)" }, + + // (avec | uno) & (and | due) + // -> {en}((avec | uno) & (NIL | due)) | {fr}((NIL | uno) & (and | (du | due))) + { "OR ( OR TEXT avec TEXT uno OR NIL TEXT due ) ( OR NIL TEXT uno OR TEXT and OR TEXT du TEXT due )", + "OR (OR TEXT avec TEXT uno TEXT due) (TEXT uno OR TEXT and OR TEXT du TEXT due)"}, + + // (uno | due) & ( tre | NIL) + { "OR TEXT uno TEXT due OR TEXT tre NIL", + "OR TEXT uno TEXT due TEXT tre" }, + + // (uno & due) | ( tre & NIL) + { "OR ( TEXT uno TEXT due ) ( TEXT tre NIL )", + "OR (TEXT uno TEXT due) TEXT tre" }, + + // (uno | due) & ( uno | NIL) + { "OR TEXT uno TEXT due OR TEXT uno NIL", + "TEXT uno" }, + + // (uno & due) | ( uno & NIL) + { "OR ( TEXT uno TEXT due ) ( TEXT uno NIL )", + "TEXT uno" }, }; static struct mail_search_args *