diff options
author | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-28 09:51:24 +0000 |
---|---|---|
committer | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-28 09:51:24 +0000 |
commit | f7548d6d28c313cf80e6f3ef89aed16a19815df1 (patch) | |
tree | a3f6f2a3f247293bee59ecd28e8cd8ceb6ca064a /src/lib-storage/index/imapc/imapc-search.c | |
parent | Initial commit. (diff) | |
download | dovecot-f7548d6d28c313cf80e6f3ef89aed16a19815df1.tar.xz dovecot-f7548d6d28c313cf80e6f3ef89aed16a19815df1.zip |
Adding upstream version 1:2.3.19.1+dfsg1.upstream/1%2.3.19.1+dfsg1upstream
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'src/lib-storage/index/imapc/imapc-search.c')
-rw-r--r-- | src/lib-storage/index/imapc/imapc-search.c | 330 |
1 files changed, 330 insertions, 0 deletions
diff --git a/src/lib-storage/index/imapc/imapc-search.c b/src/lib-storage/index/imapc/imapc-search.c new file mode 100644 index 0000000..e395919 --- /dev/null +++ b/src/lib-storage/index/imapc/imapc-search.c @@ -0,0 +1,330 @@ +/* Copyright (c) 2015-2018 Dovecot authors, see the included COPYING file */ + +#include "lib.h" +#include "str.h" +#include "imap-arg.h" +#include "imap-seqset.h" +#include "imap-util.h" +#include "mail-search.h" +#include "imapc-msgmap.h" +#include "imapc-storage.h" +#include "imapc-search.h" + +#define IMAPC_SEARCHCTX(obj) \ + MODULE_CONTEXT(obj, imapc_storage_module) + +struct imapc_search_context { + union mail_search_module_context module_ctx; + + ARRAY_TYPE(seq_range) rseqs; + struct seq_range_iter iter; + unsigned int n; + bool finished; + bool success; +}; + +static MODULE_CONTEXT_DEFINE_INIT(imapc_storage_module, + &mail_storage_module_register); + +static bool +imapc_build_search_query_args(struct imapc_mailbox *mbox, + const struct mail_search_arg *args, + bool parent_or, string_t *str); + +static bool imapc_search_is_fast_local(const struct mail_search_arg *args) +{ + const struct mail_search_arg *arg; + + for (arg = args; arg != NULL; arg = arg->next) { + switch (arg->type) { + case SEARCH_OR: + case SEARCH_SUB: + if (!imapc_search_is_fast_local(arg->value.subargs)) + return FALSE; + break; + case SEARCH_ALL: + case SEARCH_SEQSET: + case SEARCH_UIDSET: + case SEARCH_FLAGS: + case SEARCH_KEYWORDS: + case SEARCH_MODSEQ: + case SEARCH_MAILBOX: + case SEARCH_MAILBOX_GUID: + case SEARCH_MAILBOX_GLOB: + case SEARCH_REAL_UID: + break; + default: + return FALSE; + } + } + return TRUE; +} + +static bool +imapc_build_search_query_arg(struct imapc_mailbox *mbox, + const struct mail_search_arg *arg, + string_t *str) +{ + struct mail_search_arg arg2 = *arg; + const char *error; + + if (arg->match_not) + str_append(str, "NOT "); + arg2.match_not = FALSE; + arg = &arg2; + + switch (arg->type) { + case SEARCH_OR: + imapc_build_search_query_args(mbox, arg->value.subargs, TRUE, str); + return TRUE; + case SEARCH_SUB: + str_append_c(str, '('); + imapc_build_search_query_args(mbox, arg->value.subargs, FALSE, str); + str_append_c(str, ')'); + return TRUE; + case SEARCH_SEQSET: + /* translate to UIDs */ + T_BEGIN { + ARRAY_TYPE(seq_range) uids; + + t_array_init(&uids, 64); + mailbox_get_uid_range(&mbox->box, &arg->value.seqset, + &uids); + str_append(str, "UID "); + imap_write_seq_range(str, &uids); + } T_END; + return TRUE; + case SEARCH_BEFORE: + case SEARCH_SINCE: + case SEARCH_ON: + if (arg->type != SEARCH_ON && + (mbox->capabilities & IMAPC_CAPABILITY_WITHIN) == 0) { + /* a bit kludgy way to check this.. */ + size_t pos = str_len(str); + if (!mail_search_arg_to_imap(str, arg, &error)) + return FALSE; + if (strncasecmp(str_c(str) + pos, "OLDER", 5) == 0 || + strncasecmp(str_c(str) + pos, "YOUNGER", 7) == 0) + return FALSE; + return TRUE; + } + if (arg->value.date_type == MAIL_SEARCH_DATE_TYPE_SAVED && + (mbox->capabilities & IMAPC_CAPABILITY_SAVEDATE) == 0) { + /* Fall back to internal date if save date is not + supported. */ + arg2.value.date_type = MAIL_SEARCH_DATE_TYPE_RECEIVED; + } + /* fall through */ + case SEARCH_ALL: + case SEARCH_UIDSET: + case SEARCH_FLAGS: + case SEARCH_KEYWORDS: + case SEARCH_SMALLER: + case SEARCH_LARGER: + case SEARCH_HEADER: + case SEARCH_HEADER_ADDRESS: + case SEARCH_HEADER_COMPRESS_LWSP: + case SEARCH_BODY: + case SEARCH_TEXT: + return mail_search_arg_to_imap(str, arg, &error); + /* extensions */ + case SEARCH_MODSEQ: + if ((mbox->capabilities & IMAPC_CAPABILITY_CONDSTORE) == 0) + return FALSE; + return mail_search_arg_to_imap(str, arg, &error); + case SEARCH_SAVEDATESUPPORTED: + if ((mbox->capabilities & IMAPC_CAPABILITY_SAVEDATE) == 0) + return FALSE; + return mail_search_arg_to_imap(str, arg, &error); + case SEARCH_INTHREAD: + case SEARCH_GUID: + case SEARCH_MAILBOX: + case SEARCH_MAILBOX_GUID: + case SEARCH_MAILBOX_GLOB: + case SEARCH_REAL_UID: + case SEARCH_MIMEPART: + /* not supported for now */ + break; + } + return FALSE; +} + +static bool +imapc_build_search_query_args(struct imapc_mailbox *mbox, + const struct mail_search_arg *args, + bool parent_or, string_t *str) +{ + const struct mail_search_arg *arg; + + for (arg = args; arg != NULL; arg = arg->next) { + if (parent_or && arg->next != NULL) + str_append(str, "OR "); + if (!imapc_build_search_query_arg(mbox, arg, str)) + return FALSE; + str_append_c(str, ' '); + } + str_truncate(str, str_len(str)-1); + return TRUE; +} + +static bool imapc_build_search_query(struct imapc_mailbox *mbox, + const struct mail_search_args *args, + const char **query_r) +{ + string_t *str = t_str_new(128); + + if (!IMAPC_BOX_HAS_FEATURE(mbox, IMAPC_FEATURE_SEARCH)) { + /* SEARCH command passthrough not enabled */ + return FALSE; + } + if (imapc_search_is_fast_local(args->args)) + return FALSE; + + if ((mbox->capabilities & IMAPC_CAPABILITY_ESEARCH) != 0) + str_append(str, "SEARCH RETURN (ALL) "); + else + str_append(str, "UID SEARCH "); + if (!imapc_build_search_query_args(mbox, args->args, FALSE, str)) + return FALSE; + *query_r = str_c(str); + return TRUE; +} + +static void imapc_search_callback(const struct imapc_command_reply *reply, + void *context) +{ + struct mail_search_context *ctx = context; + struct imapc_mailbox *mbox = IMAPC_MAILBOX(ctx->transaction->box); + struct imapc_search_context *ictx = IMAPC_SEARCHCTX(ctx); + i_assert(ictx != NULL); + + ictx->finished = TRUE; + if (reply->state == IMAPC_COMMAND_STATE_OK) { + seq_range_array_iter_init(&ictx->iter, &ictx->rseqs); + ictx->success = TRUE; + } else if (reply->state == IMAPC_COMMAND_STATE_DISCONNECTED) { + mail_storage_set_internal_error(mbox->box.storage); + } else { + mailbox_set_critical(&mbox->box, + "imapc: Command failed: %s", reply->text_full); + } + imapc_client_stop(mbox->storage->client->client); +} + +struct mail_search_context * +imapc_search_init(struct mailbox_transaction_context *t, + struct mail_search_args *args, + const enum mail_sort_type *sort_program, + enum mail_fetch_field wanted_fields, + struct mailbox_header_lookup_ctx *wanted_headers) +{ + struct imapc_mailbox *mbox = IMAPC_MAILBOX(t->box); + struct mail_search_context *ctx; + struct imapc_search_context *ictx; + struct imapc_command *cmd; + const char *search_query; + + ctx = index_storage_search_init(t, args, sort_program, + wanted_fields, wanted_headers); + + if (!imapc_build_search_query(mbox, args, &search_query)) { + /* can't optimize this with SEARCH */ + return ctx; + } + + ictx = i_new(struct imapc_search_context, 1); + i_array_init(&ictx->rseqs, 64); + MODULE_CONTEXT_SET(ctx, imapc_storage_module, ictx); + + cmd = imapc_client_mailbox_cmd(mbox->client_box, + imapc_search_callback, ctx); + imapc_command_set_flags(cmd, IMAPC_COMMAND_FLAG_RETRIABLE); + imapc_command_send(cmd, search_query); + + i_assert(mbox->search_ctx == NULL); + mbox->search_ctx = ictx; + while (!ictx->finished) + imapc_client_run(mbox->storage->client->client); + mbox->search_ctx = NULL; + return ctx; +} + +static void imapc_search_set_matches(struct mail_search_arg *args) +{ + for (; args != NULL; args = args->next) { + if (args->type == SEARCH_OR || + args->type == SEARCH_SUB) + imapc_search_set_matches(args->value.subargs); + args->match_always = TRUE; + args->result = 1; + } +} + +bool imapc_search_next_update_seq(struct mail_search_context *ctx) +{ + struct imapc_search_context *ictx = IMAPC_SEARCHCTX(ctx); + + if (ictx == NULL || !ictx->success) + return index_storage_search_next_update_seq(ctx); + + if (!seq_range_array_iter_nth(&ictx->iter, ictx->n++, &ctx->seq)) + return FALSE; + ctx->progress_cur = ctx->seq; + + imapc_search_set_matches(ctx->args->args); + return TRUE; +} + +int imapc_search_deinit(struct mail_search_context *ctx) +{ + struct imapc_search_context *ictx = IMAPC_SEARCHCTX(ctx); + + if (ictx != NULL) { + array_free(&ictx->rseqs); + i_free(ictx); + } + return index_storage_search_deinit(ctx); +} + +void imapc_search_reply_search(const struct imap_arg *args, + struct imapc_mailbox *mbox) +{ + struct imapc_msgmap *msgmap = + imapc_client_mailbox_get_msgmap(mbox->client_box); + const char *atom; + uint32_t uid, rseq; + + if (mbox->search_ctx == NULL) { + i_error("Unexpected SEARCH reply"); + return; + } + + /* we're doing UID SEARCH, so need to convert UIDs to sequences */ + for (unsigned int i = 0; args[i].type != IMAP_ARG_EOL; i++) { + if (!imap_arg_get_atom(&args[i], &atom) || + str_to_uint32(atom, &uid) < 0 || uid == 0) { + i_error("Invalid SEARCH reply"); + break; + } + if (imapc_msgmap_uid_to_rseq(msgmap, uid, &rseq)) + seq_range_array_add(&mbox->search_ctx->rseqs, rseq); + } +} + +void imapc_search_reply_esearch(const struct imap_arg *args, + struct imapc_mailbox *mbox) +{ + const char *atom; + + if (mbox->search_ctx == NULL) { + i_error("Unexpected ESEARCH reply"); + return; + } + + /* It should contain ALL <seqset> or nonexistent if nothing matched */ + if (args[0].type != IMAP_ARG_EOL && + (!imap_arg_atom_equals(&args[0], "ALL") || + !imap_arg_get_atom(&args[1], &atom) || + imap_seq_set_nostar_parse(atom, &mbox->search_ctx->rseqs) < 0)) + i_error("Invalid ESEARCH reply"); +} |