Mercurial > dovecot > core-2.2
view src/lib-storage/index/imapc/imapc-search.c @ 22143:98e35a5592b1
imapc: SEARCH - Don't add parenthesis around OR
There's no need to write "(OR a b)" when "OR a b" works just the same.
author | Timo Sirainen <timo.sirainen@dovecot.fi> |
---|---|
date | Wed, 31 May 2017 00:00:32 +0300 |
parents | 45dde0201d01 |
children | ff8873d5675a |
line wrap: on
line source
/* Copyright (c) 2015-2017 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_CONTEXT(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: if ((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; } /* fall thrugh */ case SEARCH_ALL: case SEARCH_UIDSET: case SEARCH_FLAGS: case SEARCH_KEYWORDS: case SEARCH_ON: 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_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 = (struct imapc_mailbox *)ctx->transaction->box; struct imapc_search_context *ictx = IMAPC_CONTEXT(ctx); 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 { mail_storage_set_critical(mbox->box.storage, "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 = (struct 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_CONTEXT(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_CONTEXT(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"); }