From 20b2d47ed762ca2c009aa37e360af6f223ac71bd Mon Sep 17 00:00:00 2001 From: Timo Sirainen Date: Tue, 13 Apr 2010 18:59:23 +0300 Subject: [PATCH] lib-storage: Rewrote search query building code to be more modular. --HG-- branch : HEAD --- src/lib-storage/Makefile.am | 3 + src/lib-storage/mail-search-build.c | 666 ++------------------ src/lib-storage/mail-search-build.h | 10 + src/lib-storage/mail-search-register-imap.c | 591 +++++++++++++++++ src/lib-storage/mail-search-register.c | 71 +++ src/lib-storage/mail-search-register.h | 44 ++ src/lib-storage/mail-storage-private.h | 2 + src/lib-storage/mail-storage.c | 4 + 8 files changed, 772 insertions(+), 619 deletions(-) create mode 100644 src/lib-storage/mail-search-register-imap.c create mode 100644 src/lib-storage/mail-search-register.c create mode 100644 src/lib-storage/mail-search-register.h diff --git a/src/lib-storage/Makefile.am b/src/lib-storage/Makefile.am index 17cb412114..189f0c6c63 100644 --- a/src/lib-storage/Makefile.am +++ b/src/lib-storage/Makefile.am @@ -22,6 +22,8 @@ libstorage_la_SOURCES = \ mail-namespace.c \ mail-search.c \ mail-search-build.c \ + mail-search-register.c \ + mail-search-register-imap.c \ mail-storage.c \ mail-storage-hooks.c \ mail-storage-settings.c \ @@ -41,6 +43,7 @@ headers = \ mail-namespace.h \ mail-search.h \ mail-search-build.h \ + mail-search-register.h \ mail-thread.h \ mail-storage.h \ mail-storage-private.h \ diff --git a/src/lib-storage/mail-search-build.c b/src/lib-storage/mail-search-build.c index 086956f37a..6eac850bbc 100644 --- a/src/lib-storage/mail-search-build.c +++ b/src/lib-storage/mail-search-build.c @@ -1,644 +1,70 @@ /* Copyright (c) 2002-2010 Dovecot authors, see the included COPYING file */ #include "lib.h" -#include "ioloop.h" -#include "imap-date.h" #include "imap-arg.h" -#include "imap-seqset.h" +#include "mail-storage-private.h" +#include "mail-search-register.h" #include "mail-search-build.h" -#include "mail-storage.h" #include -struct search_build_data { - pool_t pool; - const char *error; -}; - -static struct mail_search_arg * -search_arg_new(pool_t pool, enum mail_search_arg_type type) -{ - struct mail_search_arg *arg; - - arg = p_new(pool, struct mail_search_arg, 1); - arg->type = type; - - return arg; -} - -static bool -arg_get_next(struct search_build_data *data, const struct imap_arg **args, - const char **value_r) -{ - if (IMAP_ARG_IS_EOL(*args)) { - data->error = "Missing parameter for argument"; - return FALSE; - } - if (!imap_arg_get_astring(*args, value_r)) { - data->error = "Invalid parameter for argument"; - return FALSE; - } - - *args += 1; - return TRUE; -} - -#define ARG_NEW_SINGLE(type) \ - arg_new_single(data, next_sarg, type) -static bool -arg_new_single(struct search_build_data *data, - struct mail_search_arg **next_sarg, - enum mail_search_arg_type type) -{ - *next_sarg = search_arg_new(data->pool, type); - return TRUE; -} - -#define ARG_NEW_STR(type) \ - arg_new_str(data, args, next_sarg, type) -static bool -arg_new_str(struct search_build_data *data, - const struct imap_arg **args, struct mail_search_arg **next_sarg, - enum mail_search_arg_type type) -{ - struct mail_search_arg *sarg; - const char *value; - - *next_sarg = sarg = search_arg_new(data->pool, type); - if (!arg_get_next(data, args, &value)) - return FALSE; - sarg->value.str = p_strdup(data->pool, value); - return TRUE; -} - -#define ARG_NEW_FLAGS(flags) \ - arg_new_flags(data, next_sarg, flags) -static bool -arg_new_flags(struct search_build_data *data, - struct mail_search_arg **next_sarg, enum mail_flags flags) -{ - struct mail_search_arg *sarg; - - *next_sarg = sarg = search_arg_new(data->pool, SEARCH_FLAGS); - sarg->value.flags = flags; - return TRUE; -} - -#define ARG_NEW_SIZE(type) \ - arg_new_size(data, args, next_sarg, type) -static bool -arg_new_size(struct search_build_data *data, - const struct imap_arg **args, struct mail_search_arg **next_sarg, - enum mail_search_arg_type type) -{ - struct mail_search_arg *sarg; - const char *value; - - *next_sarg = sarg = search_arg_new(data->pool, type); - if (!arg_get_next(data, args, &value)) - return FALSE; - - if (str_to_uoff(value, &sarg->value.size) < 0) { - data->error = "Invalid search size parameter"; - return FALSE; - } - return TRUE; -} - -#define ARG_NEW_DATE(type, date_type) \ - arg_new_date(data, args, next_sarg, type, date_type) -static bool -arg_new_date(struct search_build_data *data, - const struct imap_arg **args, struct mail_search_arg **next_sarg, - enum mail_search_arg_type type, - enum mail_search_date_type date_type) -{ - struct mail_search_arg *sarg; - const char *value; - - *next_sarg = sarg = search_arg_new(data->pool, type); - if (!arg_get_next(data, args, &value)) - return FALSE; - if (!imap_parse_date(value, &sarg->value.time)) { - data->error = "Invalid search date parameter"; - return FALSE; - } - sarg->value.date_type = date_type; - return TRUE; -} - -#define ARG_NEW_INTERVAL(type) \ - arg_new_interval(data, args, next_sarg, type) -static bool -arg_new_interval(struct search_build_data *data, - const struct imap_arg **args, - struct mail_search_arg **next_sarg, - enum mail_search_arg_type type) -{ - struct mail_search_arg *sarg; - const char *value; - uint32_t interval; - - *next_sarg = sarg = search_arg_new(data->pool, type); - if (!arg_get_next(data, args, &value)) - return FALSE; - - if (str_to_uint32(value, &interval) < 0 || interval == 0) { - data->error = "Invalid search interval parameter"; - return FALSE; - } - sarg->value.search_flags = MAIL_SEARCH_ARG_FLAG_USE_TZ; - sarg->value.time = ioloop_time - interval; - return TRUE; -} - -#define ARG_NEW_HEADER(type, hdr_name) \ - arg_new_header(data, args, next_sarg, type, hdr_name) -static bool -arg_new_header(struct search_build_data *data, - const struct imap_arg **args, struct mail_search_arg **next_sarg, - enum mail_search_arg_type type, const char *hdr_name) -{ - struct mail_search_arg *sarg; - const char *value; - - *next_sarg = sarg = search_arg_new(data->pool, type); - if (!arg_get_next(data, args, &value)) - return FALSE; - - sarg->hdr_field_name = p_strdup(data->pool, hdr_name); - sarg->value.str = p_strdup(data->pool, value); - return TRUE; -} - -static bool -arg_modseq_set_name(struct search_build_data *data, - struct mail_search_arg *sarg, const char *name) -{ - name = t_str_lcase(name); - if (strncmp(name, "/flags/", 7) != 0) { - data->error = "Invalid MODSEQ entry"; - return FALSE; - } - name += 7; - - if (*name == '\\') { - /* system flag */ - name++; - if (strcmp(name, "answered") == 0) - sarg->value.flags = MAIL_ANSWERED; - else if (strcmp(name, "flagged") == 0) - sarg->value.flags = MAIL_FLAGGED; - else if (strcmp(name, "deleted") == 0) - sarg->value.flags = MAIL_DELETED; - else if (strcmp(name, "seen") == 0) - sarg->value.flags = MAIL_SEEN; - else if (strcmp(name, "draft") == 0) - sarg->value.flags = MAIL_DRAFT; - else { - data->error = "Invalid MODSEQ system flag"; - return FALSE; - } - return TRUE; - } - sarg->value.str = p_strdup(data->pool, name); - return TRUE; -} - -static bool -arg_modseq_set_type(struct search_build_data *data, - struct mail_search_modseq *modseq, const char *name) -{ - if (strcasecmp(name, "all") == 0) - modseq->type = MAIL_SEARCH_MODSEQ_TYPE_ANY; - else if (strcasecmp(name, "priv") == 0) - modseq->type = MAIL_SEARCH_MODSEQ_TYPE_PRIVATE; - else if (strcasecmp(name, "shared") == 0) - modseq->type = MAIL_SEARCH_MODSEQ_TYPE_SHARED; - else { - data->error = "Invalid MODSEQ type"; - return FALSE; - } - return TRUE; -} - -#define ARG_NEW_MODSEQ() \ - arg_new_modseq(data, args, next_sarg) -static bool -arg_new_modseq(struct search_build_data *data, - const struct imap_arg **args, struct mail_search_arg **next_sarg) -{ - struct mail_search_arg *sarg; - const char *value; - - *next_sarg = sarg = search_arg_new(data->pool, SEARCH_MODSEQ); - if (!arg_get_next(data, args, &value)) - return FALSE; - - sarg->value.modseq = p_new(data->pool, struct mail_search_modseq, 1); - if ((*args)[-1].type == IMAP_ARG_STRING) { - /* */ - if (!arg_modseq_set_name(data, sarg, value)) - return FALSE; - - if (!arg_get_next(data, args, &value)) - return FALSE; - if (!arg_modseq_set_type(data, sarg->value.modseq, value)) - return FALSE; - - if (!arg_get_next(data, args, &value)) - return FALSE; - } - if (str_to_uint64(value, &sarg->value.modseq->modseq) < 0) { - data->error = "Invalid MODSEQ value"; - return FALSE; - } - return TRUE; -} - -static bool search_arg_build(struct search_build_data *data, - const struct imap_arg **args, - struct mail_search_arg **next_sarg) +struct mail_search_arg * +mail_search_build_next(struct mail_search_build_context *ctx, + const struct imap_arg **imap_args) { struct mail_search_arg **subargs, *sarg; - const struct imap_arg *arg, *listargs; - const char *key, *value; - - if (IMAP_ARG_IS_EOL(*args)) { - data->error = "Missing argument"; - return FALSE; + const struct imap_arg *listargs; + const char *key; + const struct mail_search_register_arg *reg_arg; + mail_search_register_fallback_t *fallback; + + if (IMAP_ARG_IS_EOL(*imap_args)) { + ctx->error = "Missing argument"; + return NULL; } - arg = *args; - if (arg->type == IMAP_ARG_NIL) { + if ((*imap_args)->type == IMAP_ARG_NIL) { /* NIL not allowed */ - data->error = "NIL not allowed"; - return FALSE; + ctx->error = "NIL not allowed"; + return NULL; } - if (imap_arg_get_list(arg, &listargs)) { + if (imap_arg_get_list(*imap_args, &listargs)) { if (IMAP_ARG_IS_EOL(listargs)) { - data->error = "Empty list not allowed"; - return FALSE; + ctx->error = "Empty list not allowed"; + return NULL; } - *next_sarg = search_arg_new(data->pool, SEARCH_SUB); - subargs = &(*next_sarg)->value.subargs; - while (IMAP_ARG_IS_EOL(listargs)) { - if (!search_arg_build(data, &listargs, subargs)) - return FALSE; + sarg = p_new(ctx->pool, struct mail_search_arg, 1); + sarg->type = SEARCH_SUB; + subargs = &sarg->value.subargs; + while (!IMAP_ARG_IS_EOL(listargs)) { + *subargs = mail_search_build_next(ctx, &listargs); + if (*subargs == NULL) + return NULL; subargs = &(*subargs)->next; } - *args += 1; - return TRUE; + *imap_args += 1; + return sarg; } /* string argument - get the name and jump to next */ - key = imap_arg_as_astring(arg); - *args += 1; + key = imap_arg_as_astring(*imap_args); + *imap_args += 1; key = t_str_ucase(key); - switch (*key) { - case 'A': - if (strcmp(key, "ANSWERED") == 0) - return ARG_NEW_FLAGS(MAIL_ANSWERED); - else if (strcmp(key, "ALL") == 0) - return ARG_NEW_SINGLE(SEARCH_ALL); - break; - case 'B': - if (strcmp(key, "BODY") == 0) { - /* */ - if (imap_arg_get_astring(*args, &value) && - *value == '\0') { - /* optimization: BODY "" matches everything */ - *args += 1; - return ARG_NEW_SINGLE(SEARCH_ALL); - } - return ARG_NEW_STR(SEARCH_BODY); - } else if (strcmp(key, "BEFORE") == 0) { - /* */ - return ARG_NEW_DATE(SEARCH_BEFORE, - MAIL_SEARCH_DATE_TYPE_RECEIVED); - } else if (strcmp(key, "BCC") == 0) { - /* */ - return ARG_NEW_HEADER(SEARCH_HEADER_ADDRESS, key); - } - break; - case 'C': - if (strcmp(key, "CC") == 0) { - /* */ - return ARG_NEW_HEADER(SEARCH_HEADER_ADDRESS, key); - } - break; - case 'D': - if (strcmp(key, "DELETED") == 0) - return ARG_NEW_FLAGS(MAIL_DELETED); - else if (strcmp(key, "DRAFT") == 0) - return ARG_NEW_FLAGS(MAIL_DRAFT); - break; - case 'F': - if (strcmp(key, "FLAGGED") == 0) - return ARG_NEW_FLAGS(MAIL_FLAGGED); - else if (strcmp(key, "FROM") == 0) { - /* */ - return ARG_NEW_HEADER(SEARCH_HEADER_ADDRESS, key); - } - break; - case 'H': - if (strcmp(key, "HEADER") == 0) { - /* */ - if (IMAP_ARG_IS_EOL(*args)) { - data->error = "Missing parameter for HEADER"; - return FALSE; - } - if (!imap_arg_get_astring(*args, &value)) { - data->error = "Invalid parameter for HEADER"; - return FALSE; - } - - *args += 1; - return ARG_NEW_HEADER(SEARCH_HEADER, - t_str_ucase(value)); - } - break; - case 'I': - if (strcmp(key, "INTHREAD") == 0) { - /* */ - enum mail_thread_type thread_type; - - if (!imap_arg_get_atom(*args, &value)) { - data->error = "Invalid parameter for INTHREAD"; - return FALSE; - } - - if (!mail_thread_type_parse(value, &thread_type)) { - data->error = "Unknown thread algorithm"; - return FALSE; - } - *args += 1; - - *next_sarg = search_arg_new(data->pool, - SEARCH_INTHREAD); - (*next_sarg)->value.thread_type = thread_type; - subargs = &(*next_sarg)->value.subargs; - return search_arg_build(data, args, subargs); - } - break; - case 'K': - if (strcmp(key, "KEYWORD") == 0) { - return ARG_NEW_STR(SEARCH_KEYWORDS); - } - break; - case 'L': - if (strcmp(key, "LARGER") == 0) { - /* */ - return ARG_NEW_SIZE(SEARCH_LARGER); - } - break; - case 'M': - if (strcmp(key, "MODSEQ") == 0) { - /* [ ] */ - return ARG_NEW_MODSEQ(); - } - break; - case 'N': - if (strcmp(key, "NOT") == 0) { - if (!search_arg_build(data, args, next_sarg)) - return FALSE; - (*next_sarg)->not = !(*next_sarg)->not; - return TRUE; - } else if (strcmp(key, "NEW") == 0) { - /* NEW == (RECENT UNSEEN) */ - *next_sarg = search_arg_new(data->pool, SEARCH_SUB); - - subargs = &(*next_sarg)->value.subargs; - *subargs = search_arg_new(data->pool, SEARCH_FLAGS); - (*subargs)->value.flags = MAIL_RECENT; - (*subargs)->next = search_arg_new(data->pool, - SEARCH_FLAGS); - (*subargs)->next->value.flags = MAIL_SEEN; - (*subargs)->next->not = TRUE; - return TRUE; - } - break; - case 'O': - if (strcmp(key, "OR") == 0) { - /* */ - *next_sarg = search_arg_new(data->pool, SEARCH_OR); - - subargs = &(*next_sarg)->value.subargs; - for (;;) { - if (!search_arg_build(data, args, subargs)) - return FALSE; - - subargs = &(*subargs)->next; - - /* OR OR ... - put them all - under one SEARCH_OR list. */ - if (!imap_arg_get_atom(*args, &value) || - strcasecmp(value, "OR") != 0) - break; - - *args += 1; - } - - if (!search_arg_build(data, args, subargs)) - return FALSE; - return TRUE; - } if (strcmp(key, "ON") == 0) { - /* */ - return ARG_NEW_DATE(SEARCH_ON, - MAIL_SEARCH_DATE_TYPE_RECEIVED); - } if (strcmp(key, "OLD") == 0) { - /* OLD == NOT RECENT */ - if (!ARG_NEW_FLAGS(MAIL_RECENT)) - return FALSE; - - (*next_sarg)->not = TRUE; - return TRUE; - } if (strcmp(key, "OLDER") == 0) { - /* - WITHIN extension */ - if (!ARG_NEW_INTERVAL(SEARCH_BEFORE)) - return FALSE; - - /* we need to match also equal, but standard BEFORE - compares with "<" */ - (*next_sarg)->value.time++; - return TRUE; - } - break; - case 'R': - if (strcmp(key, "RECENT") == 0) - return ARG_NEW_FLAGS(MAIL_RECENT); - break; - case 'S': - if (strcmp(key, "SEEN") == 0) - return ARG_NEW_FLAGS(MAIL_SEEN); - else if (strcmp(key, "SUBJECT") == 0) { - /* */ - return ARG_NEW_HEADER(SEARCH_HEADER_COMPRESS_LWSP, key); - } else if (strcmp(key, "SENTBEFORE") == 0) { - /* */ - return ARG_NEW_DATE(SEARCH_BEFORE, - MAIL_SEARCH_DATE_TYPE_SENT); - } else if (strcmp(key, "SENTON") == 0) { - /* */ - return ARG_NEW_DATE(SEARCH_ON, - MAIL_SEARCH_DATE_TYPE_SENT); - } else if (strcmp(key, "SENTSINCE") == 0) { - /* */ - return ARG_NEW_DATE(SEARCH_SINCE, - MAIL_SEARCH_DATE_TYPE_SENT); - } else if (strcmp(key, "SINCE") == 0) { - /* */ - return ARG_NEW_DATE(SEARCH_SINCE, - MAIL_SEARCH_DATE_TYPE_RECEIVED); - } else if (strcmp(key, "SMALLER") == 0) { - /* */ - return ARG_NEW_SIZE(SEARCH_SMALLER); - } - break; - case 'T': - if (strcmp(key, "TEXT") == 0) { - /* */ - if (imap_arg_get_astring(*args, &value) && - *value == '\0') { - /* optimization: TEXT "" matches everything */ - *args += 1; - return ARG_NEW_SINGLE(SEARCH_ALL); - } - return ARG_NEW_STR(SEARCH_TEXT); - } else if (strcmp(key, "TO") == 0) { - /* */ - return ARG_NEW_HEADER(SEARCH_HEADER_ADDRESS, key); - } - break; - case 'U': - if (strcmp(key, "UID") == 0) { - /* */ - if (!ARG_NEW_STR(SEARCH_UIDSET)) - return FALSE; - - sarg = *next_sarg; - p_array_init(&sarg->value.seqset, data->pool, 16); - if (strcmp(sarg->value.str, "$") == 0) { - /* SEARCHRES: delay initialization */ - return TRUE; - } - if (imap_seq_set_parse(sarg->value.str, - &sarg->value.seqset) < 0) { - data->error = "Invalid UID messageset"; - return FALSE; - } - return TRUE; - } else if (strcmp(key, "UNANSWERED") == 0) { - if (!ARG_NEW_FLAGS(MAIL_ANSWERED)) - return FALSE; - (*next_sarg)->not = TRUE; - return TRUE; - } else if (strcmp(key, "UNDELETED") == 0) { - if (!ARG_NEW_FLAGS(MAIL_DELETED)) - return FALSE; - (*next_sarg)->not = TRUE; - return TRUE; - } else if (strcmp(key, "UNDRAFT") == 0) { - if (!ARG_NEW_FLAGS(MAIL_DRAFT)) - return FALSE; - (*next_sarg)->not = TRUE; - return TRUE; - } else if (strcmp(key, "UNFLAGGED") == 0) { - if (!ARG_NEW_FLAGS(MAIL_FLAGGED)) - return FALSE; - (*next_sarg)->not = TRUE; - return TRUE; - } else if (strcmp(key, "UNKEYWORD") == 0) { - if (!ARG_NEW_STR(SEARCH_KEYWORDS)) - return FALSE; - (*next_sarg)->not = TRUE; - return TRUE; - } else if (strcmp(key, "UNSEEN") == 0) { - if (!ARG_NEW_FLAGS(MAIL_SEEN)) - return FALSE; - (*next_sarg)->not = TRUE; - return TRUE; - } - break; - case 'Y': - if (strcmp(key, "YOUNGER") == 0) { - /* - WITHIN extension */ - return ARG_NEW_INTERVAL(SEARCH_SINCE); - } - break; - case 'X': - if (strcmp(key, "X-BODY-FAST") == 0) { - /* */ - if (imap_arg_get_astring(*args, &value) && - *value == '\0') { - /* optimization: X-BODY-FAST "" matches - everything */ - *args += 1; - return ARG_NEW_SINGLE(SEARCH_ALL); - } - return ARG_NEW_STR(SEARCH_BODY_FAST); - } else if (strcmp(key, "X-TEXT-FAST") == 0) { - /* */ - if (imap_arg_get_astring(*args, &value) && - *value == '\0') { - /* optimization: X-TEXT-FAST "" matches - everything */ - *args += 1; - return ARG_NEW_SINGLE(SEARCH_ALL); - } - return ARG_NEW_STR(SEARCH_TEXT_FAST); - } else if (strcmp(key, "X-GUID") == 0) { - /* */ - return ARG_NEW_STR(SEARCH_GUID); - } else if (strcmp(key, "X-MAILBOX") == 0) { - /* */ - return ARG_NEW_STR(SEARCH_MAILBOX); - } else if (strcmp(key, "X-SAVEDBEFORE") == 0) { - /* */ - return ARG_NEW_DATE(SEARCH_BEFORE, - MAIL_SEARCH_DATE_TYPE_SAVED); - } else if (strcmp(key, "X-SAVEDON") == 0) { - /* */ - return ARG_NEW_DATE(SEARCH_ON, - MAIL_SEARCH_DATE_TYPE_SAVED); - } else if (strcmp(key, "X-SAVEDSINCE") == 0) { - /* */ - return ARG_NEW_DATE(SEARCH_SINCE, - MAIL_SEARCH_DATE_TYPE_SAVED); - } - break; - default: - if (*key == '*' || (*key >= '0' && *key <= '9')) { - /* */ - if (!ARG_NEW_SINGLE(SEARCH_SEQSET)) - return FALSE; - - p_array_init(&(*next_sarg)->value.seqset, - data->pool, 16); - if (imap_seq_set_parse(key, &(*next_sarg)->value.seqset) < 0) { - data->error = "Invalid messageset"; - return FALSE; - } - return TRUE; - } else if (strcmp(key, "$") == 0) { - /* SEARCHRES: delay initialization */ - if (!ARG_NEW_SINGLE(SEARCH_UIDSET)) - return FALSE; - - (*next_sarg)->value.str = p_strdup(data->pool, "$"); - p_array_init(&(*next_sarg)->value.seqset, - data->pool, 16); - return TRUE; - } - break; + reg_arg = mail_search_register_find(ctx->reg, key); + if (reg_arg != NULL) + sarg = reg_arg->build(ctx, imap_args); + else if (mail_search_register_get_fallback(ctx->reg, &fallback)) + sarg = fallback(ctx, key, imap_args); + else { + sarg = NULL; + ctx->error = p_strconcat(ctx->pool, "Unknown argument ", + key, NULL); } - - data->error = t_strconcat("Unknown argument ", key, NULL); - return FALSE; + return sarg; } int mail_search_build_from_imap_args(const struct imap_arg *imap_args, @@ -646,7 +72,7 @@ int mail_search_build_from_imap_args(const struct imap_arg *imap_args, struct mail_search_args **args_r, const char **error_r) { - struct search_build_data data; + struct mail_search_build_context ctx; struct mail_search_args *args; struct mail_search_arg **sargs; @@ -656,14 +82,16 @@ int mail_search_build_from_imap_args(const struct imap_arg *imap_args, args = mail_search_build_init(); args->charset = p_strdup(args->pool, charset); - data.pool = args->pool; - data.error = NULL; + memset(&ctx, 0, sizeof(ctx)); + ctx.pool = args->pool; + ctx.reg = mail_search_register_imap; sargs = &args->args; while (!IMAP_ARG_IS_EOL(imap_args)) { - if (!search_arg_build(&data, &imap_args, sargs)) { + *sargs = mail_search_build_next(&ctx, &imap_args); + if (*sargs == NULL) { + *error_r = t_strdup(ctx.error); pool_unref(&args->pool); - *error_r = data.error; return -1; } sargs = &(*sargs)->next; diff --git a/src/lib-storage/mail-search-build.h b/src/lib-storage/mail-search-build.h index 03cdb8adef..880bdf0307 100644 --- a/src/lib-storage/mail-search-build.h +++ b/src/lib-storage/mail-search-build.h @@ -6,6 +6,12 @@ struct imap_arg; struct mailbox; +struct mail_search_build_context { + pool_t pool; + struct mail_search_register *reg; + const char *error; +}; + /* Start building a new search query. Use mail_search_args_unref() to free it. */ struct mail_search_args *mail_search_build_init(void); @@ -22,4 +28,8 @@ void mail_search_build_add_all(struct mail_search_args *args); void mail_search_build_add_seqset(struct mail_search_args *args, uint32_t seq1, uint32_t seq2); +struct mail_search_arg * +mail_search_build_next(struct mail_search_build_context *ctx, + const struct imap_arg **imap_args); + #endif diff --git a/src/lib-storage/mail-search-register-imap.c b/src/lib-storage/mail-search-register-imap.c new file mode 100644 index 0000000000..28864154c9 --- /dev/null +++ b/src/lib-storage/mail-search-register-imap.c @@ -0,0 +1,591 @@ +/* Copyright (c) 2010 Dovecot authors, see the included COPYING file */ + +#include "lib.h" +#include "ioloop.h" +#include "imap-date.h" +#include "imap-arg.h" +#include "imap-seqset.h" +#include "imap-util.h" +#include "mail-search-register.h" +#include "mail-search-build.h" + +#include + +static struct mail_search_arg * +search_arg_new(pool_t pool, enum mail_search_arg_type type) +{ + struct mail_search_arg *arg; + + arg = p_new(pool, struct mail_search_arg, 1); + arg->type = type; + return arg; +} + +static int +arg_get_next(struct mail_search_build_context *ctx, + const struct imap_arg **imap_args, const char **value_r) +{ + if (IMAP_ARG_IS_EOL(*imap_args)) { + ctx->error = "Missing parameter for argument"; + return -1; + } + if (!imap_arg_get_astring(*imap_args, value_r)) { + ctx->error = "Invalid parameter for argument"; + return -1; + } + + *imap_args += 1; + return 0; +} + +static struct mail_search_arg * +imap_search_fallback(struct mail_search_build_context *ctx, + const char *key, + const struct imap_arg **imap_args ATTR_UNUSED) +{ + struct mail_search_arg *sarg; + + if (*key == '*' || (*key >= '0' && *key <= '9')) { + /* */ + sarg = search_arg_new(ctx->pool, SEARCH_SEQSET); + p_array_init(&sarg->value.seqset, ctx->pool, 16); + if (imap_seq_set_parse(key, &sarg->value.seqset) < 0) { + ctx->error = "Invalid messageset"; + return NULL; + } + return sarg; + } + ctx->error = p_strconcat(ctx->pool, "Unknown argument ", key, NULL); + return NULL; +} + +static struct mail_search_arg * +imap_search_not(struct mail_search_build_context *ctx, + const struct imap_arg **imap_args) +{ + struct mail_search_arg *sarg; + + sarg = mail_search_build_next(ctx, imap_args); + if (sarg != NULL) + sarg->not = !sarg->not; + return sarg; +} + +static struct mail_search_arg * +imap_search_or(struct mail_search_build_context *ctx, + const struct imap_arg **imap_args) +{ + struct mail_search_arg *sarg, **subargs; + + /* */ + sarg = search_arg_new(ctx->pool, SEARCH_OR); + + subargs = &sarg->value.subargs; + for (;;) { + *subargs = mail_search_build_next(ctx, imap_args); + if (*subargs == NULL) + return NULL; + subargs = &(*subargs)->next; + + /* OR OR ... - put them all + under one SEARCH_OR list. */ + if (!imap_arg_atom_equals(*imap_args, "OR")) + break; + + *imap_args += 1; + } + + *subargs = mail_search_build_next(ctx, imap_args); + if (*subargs == NULL) + return NULL; + return sarg; +} + +static struct mail_search_arg * +arg_new_str(struct mail_search_build_context *ctx, + const struct imap_arg **imap_args, + enum mail_search_arg_type type) +{ + struct mail_search_arg *sarg; + const char *value; + + sarg = search_arg_new(ctx->pool, type); + if (arg_get_next(ctx, imap_args, &value) < 0) + return NULL; + sarg->value.str = p_strdup(ctx->pool, value); + return sarg; +} + +#define CALLBACK_STR(_func, _type) \ +static struct mail_search_arg *\ +imap_search_##_func(struct mail_search_build_context *ctx, \ + const struct imap_arg **imap_args) \ +{ \ + return arg_new_str(ctx, imap_args, _type); \ +} +static struct mail_search_arg * +imap_search_all(struct mail_search_build_context *ctx, + const struct imap_arg **imap_args ATTR_UNUSED) +{ + return search_arg_new(ctx->pool, SEARCH_ALL); +} + +static struct mail_search_arg * +imap_search_uid(struct mail_search_build_context *ctx, + const struct imap_arg **imap_args ATTR_UNUSED) +{ + struct mail_search_arg *sarg; + + /* */ + if ((sarg = arg_new_str(ctx, imap_args, SEARCH_UIDSET)) == NULL) + return NULL; + + p_array_init(&sarg->value.seqset, ctx->pool, 16); + if (strcmp(sarg->value.str, "$") == 0) { + /* SEARCHRES: delay initialization */ + } else { + if (imap_seq_set_parse(sarg->value.str, + &sarg->value.seqset) < 0) { + ctx->error = "Invalid UID messageset"; + return NULL; + } + } + return sarg; +} + +#define CALLBACK_FLAG(_func, _flag, _not) \ +static struct mail_search_arg *\ +imap_search_##_func(struct mail_search_build_context *ctx, \ + const struct imap_arg **imap_args ATTR_UNUSED) \ +{ \ + struct mail_search_arg *sarg; \ + sarg = search_arg_new(ctx->pool, SEARCH_FLAGS); \ + sarg->value.flags = _flag; \ + sarg->not = _not; \ + return sarg; \ +} +CALLBACK_FLAG(answered, MAIL_ANSWERED, FALSE); +CALLBACK_FLAG(unanswered, MAIL_ANSWERED, TRUE); +CALLBACK_FLAG(deleted, MAIL_DELETED, FALSE); +CALLBACK_FLAG(undeleted, MAIL_DELETED, TRUE); +CALLBACK_FLAG(draft, MAIL_DRAFT, FALSE); +CALLBACK_FLAG(undraft, MAIL_DRAFT, TRUE); +CALLBACK_FLAG(flagged, MAIL_FLAGGED, FALSE); +CALLBACK_FLAG(unflagged, MAIL_FLAGGED, TRUE); +CALLBACK_FLAG(seen, MAIL_SEEN, FALSE); +CALLBACK_FLAG(unseen, MAIL_SEEN, TRUE); +CALLBACK_FLAG(recent, MAIL_RECENT, FALSE); +CALLBACK_FLAG(old, MAIL_RECENT, TRUE); + +static struct mail_search_arg * +imap_search_new(struct mail_search_build_context *ctx, + const struct imap_arg **imap_args ATTR_UNUSED) +{ + struct mail_search_arg *sarg; + + /* NEW == (RECENT UNSEEN) */ + sarg = search_arg_new(ctx->pool, SEARCH_SUB); + sarg->value.subargs = imap_search_recent(ctx, NULL); + sarg->value.subargs->next = imap_search_unseen(ctx, NULL); + return sarg; +} + +CALLBACK_STR(keyword, SEARCH_KEYWORDS); + +static struct mail_search_arg * +imap_search_unkeyword(struct mail_search_build_context *ctx, + const struct imap_arg **imap_args) +{ + struct mail_search_arg *sarg; + + sarg = imap_search_keyword(ctx, imap_args); + if (sarg != NULL) + sarg->not = TRUE; + return sarg; +} + +static struct mail_search_arg * +arg_new_date(struct mail_search_build_context *ctx, + const struct imap_arg **imap_args, + enum mail_search_arg_type type, + enum mail_search_date_type date_type) +{ + struct mail_search_arg *sarg; + const char *value; + + sarg = search_arg_new(ctx->pool, type); + if (arg_get_next(ctx, imap_args, &value) < 0) + return NULL; + if (!imap_parse_date(value, &sarg->value.time)) { + ctx->error = "Invalid search date parameter"; + return NULL; + } + sarg->value.date_type = date_type; + return sarg; +} + +#define CALLBACK_DATE(_func, _type, _date_type) \ +static struct mail_search_arg *\ +imap_search_##_func(struct mail_search_build_context *ctx, \ + const struct imap_arg **imap_args) \ +{ \ + return arg_new_date(ctx, imap_args, _type, _date_type); \ +} +CALLBACK_DATE(before, SEARCH_BEFORE, MAIL_SEARCH_DATE_TYPE_RECEIVED); +CALLBACK_DATE(on, SEARCH_ON, MAIL_SEARCH_DATE_TYPE_RECEIVED); +CALLBACK_DATE(since, SEARCH_SINCE, MAIL_SEARCH_DATE_TYPE_RECEIVED); + +CALLBACK_DATE(sentbefore, SEARCH_BEFORE, MAIL_SEARCH_DATE_TYPE_SENT); +CALLBACK_DATE(senton, SEARCH_ON, MAIL_SEARCH_DATE_TYPE_SENT); +CALLBACK_DATE(sentsince, SEARCH_SINCE, MAIL_SEARCH_DATE_TYPE_SENT); + +CALLBACK_DATE(x_savedbefore, SEARCH_BEFORE, MAIL_SEARCH_DATE_TYPE_SAVED); +CALLBACK_DATE(x_savedon, SEARCH_ON, MAIL_SEARCH_DATE_TYPE_SAVED); +CALLBACK_DATE(x_savedsince, SEARCH_SINCE, MAIL_SEARCH_DATE_TYPE_SAVED); + +static struct mail_search_arg * +arg_new_size(struct mail_search_build_context *ctx, + const struct imap_arg **imap_args, + enum mail_search_arg_type type) +{ + struct mail_search_arg *sarg; + const char *value; + + sarg = search_arg_new(ctx->pool, type); + if (arg_get_next(ctx, imap_args, &value) < 0) + return NULL; + + if (str_to_uoff(value, &sarg->value.size) < 0) { + ctx->error = "Invalid search size parameter"; + return NULL; + } + return sarg; +} + +static struct mail_search_arg * +imap_search_larger(struct mail_search_build_context *ctx, + const struct imap_arg **imap_args) +{ + return arg_new_size(ctx, imap_args, SEARCH_LARGER); +} + +static struct mail_search_arg * +imap_search_smaller(struct mail_search_build_context *ctx, + const struct imap_arg **imap_args) +{ + return arg_new_size(ctx, imap_args, SEARCH_SMALLER); +} + +static struct mail_search_arg * +arg_new_header(struct mail_search_build_context *ctx, + const struct imap_arg **imap_args, + enum mail_search_arg_type type, const char *hdr_name) +{ + struct mail_search_arg *sarg; + const char *value; + + sarg = search_arg_new(ctx->pool, type); + if (arg_get_next(ctx, imap_args, &value) < 0) + return NULL; + + sarg->hdr_field_name = p_strdup(ctx->pool, hdr_name); + sarg->value.str = p_strdup(ctx->pool, value); + return sarg; +} + +#define CALLBACK_HDR(_name, _type) \ +static struct mail_search_arg *\ +imap_search_##_name(struct mail_search_build_context *ctx, \ + const struct imap_arg **imap_args) \ +{ \ + return arg_new_header(ctx, imap_args, _type, #_name); \ +} +CALLBACK_HDR(bcc, SEARCH_HEADER_ADDRESS); +CALLBACK_HDR(cc, SEARCH_HEADER_ADDRESS); +CALLBACK_HDR(from, SEARCH_HEADER_ADDRESS); +CALLBACK_HDR(to, SEARCH_HEADER_ADDRESS); +CALLBACK_HDR(subject, SEARCH_HEADER_COMPRESS_LWSP); + +static struct mail_search_arg * +imap_search_header(struct mail_search_build_context *ctx, + const struct imap_arg **imap_args) +{ + const char *value; + + /* */ + if (IMAP_ARG_IS_EOL(*imap_args)) { + ctx->error = "Missing parameter for HEADER"; + return NULL; + } + if (!imap_arg_get_astring(*imap_args, &value)) { + ctx->error = "Invalid parameter for HEADER"; + return NULL; + } + + *imap_args += 1; + return arg_new_header(ctx, imap_args, SEARCH_HEADER, + t_str_ucase(value)); +} + +#define CALLBACK_BODY(_func, _type) \ +static struct mail_search_arg *\ +imap_search_##_func(struct mail_search_build_context *ctx, \ + const struct imap_arg **imap_args) \ +{ \ + const char *value; \ + if (imap_arg_get_astring(*imap_args, &value) && *value == '\0') { \ + /* optimization: BODY "" matches everything */ \ + *imap_args += 1; \ + return search_arg_new(ctx->pool, SEARCH_ALL); \ + } \ + return arg_new_str(ctx, imap_args, _type); \ +} +CALLBACK_BODY(body, SEARCH_BODY); +CALLBACK_BODY(text, SEARCH_TEXT); +CALLBACK_BODY(x_body_fast, SEARCH_BODY_FAST); +CALLBACK_BODY(x_text_fast, SEARCH_TEXT_FAST); + +static struct mail_search_arg * +arg_new_interval(struct mail_search_build_context *ctx, + const struct imap_arg **imap_args, + enum mail_search_arg_type type) +{ + struct mail_search_arg *sarg; + const char *value; + uint32_t interval; + + sarg = search_arg_new(ctx->pool, type); + if (arg_get_next(ctx, imap_args, &value) < 0) + return NULL; + + if (str_to_uint32(value, &interval) < 0 || interval == 0) { + ctx->error = "Invalid search interval parameter"; + return NULL; + } + sarg->value.search_flags = MAIL_SEARCH_ARG_FLAG_USE_TZ; + sarg->value.time = ioloop_time - interval; + return sarg; +} + +static struct mail_search_arg * +imap_search_older(struct mail_search_build_context *ctx, + const struct imap_arg **imap_args) +{ + struct mail_search_arg *sarg; + + sarg = arg_new_interval(ctx, imap_args, SEARCH_BEFORE); + /* we need to match also equal, but SEARCH_BEFORE compares with "<" */ + sarg->value.time++; + return sarg; +} + +static struct mail_search_arg * +imap_search_younger(struct mail_search_build_context *ctx, + const struct imap_arg **imap_args) +{ + return arg_new_interval(ctx, imap_args, SEARCH_SINCE); +} + +static int +arg_modseq_set_name(struct mail_search_build_context *ctx, + struct mail_search_arg *sarg, const char *name) +{ + name = t_str_lcase(name); + if (strncmp(name, "/flags/", 7) != 0) { + ctx->error = "Invalid MODSEQ entry"; + return -1; + } + name += 7; + + if (*name == '\\') { + /* system flag */ + sarg->value.flags = imap_parse_system_flag(name); + if (sarg->value.flags == 0 || + sarg->value.flags == MAIL_RECENT) { + ctx->error = "Invalid MODSEQ system flag"; + return -1; + } + return 0; + } + sarg->value.str = p_strdup(ctx->pool, name); + return 0; +} + +static int +arg_modseq_set_type(struct mail_search_build_context *ctx, + struct mail_search_modseq *modseq, const char *name) +{ + if (strcasecmp(name, "all") == 0) + modseq->type = MAIL_SEARCH_MODSEQ_TYPE_ANY; + else if (strcasecmp(name, "priv") == 0) + modseq->type = MAIL_SEARCH_MODSEQ_TYPE_PRIVATE; + else if (strcasecmp(name, "shared") == 0) + modseq->type = MAIL_SEARCH_MODSEQ_TYPE_SHARED; + else { + ctx->error = "Invalid MODSEQ type"; + return -1; + } + return 0; +} + +static struct mail_search_arg * +imap_search_modseq(struct mail_search_build_context *ctx, + const struct imap_arg **imap_args) +{ + struct mail_search_arg *sarg; + const char *value; + + /* [ ] */ + sarg = search_arg_new(ctx->pool, SEARCH_MODSEQ); + if (arg_get_next(ctx, imap_args, &value) < 0) + return NULL; + + sarg->value.modseq = p_new(ctx->pool, struct mail_search_modseq, 1); + if ((*imap_args)[-1].type == IMAP_ARG_STRING) { + /* */ + if (arg_modseq_set_name(ctx, sarg, value) < 0) + return NULL; + + if (arg_get_next(ctx, imap_args, &value) < 0) + return NULL; + if (arg_modseq_set_type(ctx, sarg->value.modseq, value) < 0) + return NULL; + + if (arg_get_next(ctx, imap_args, &value) < 0) + return NULL; + } + if (str_to_uint64(value, &sarg->value.modseq->modseq) < 0) { + ctx->error = "Invalid MODSEQ value"; + return NULL; + } + return sarg; +} + +static struct mail_search_arg * +imap_search_last_result(struct mail_search_build_context *ctx, + const struct imap_arg **imap_args ATTR_UNUSED) +{ + struct mail_search_arg *sarg; + + /* SEARCHRES: delay initialization */ + sarg = search_arg_new(ctx->pool, SEARCH_UIDSET); + sarg->value.str = "$"; + p_array_init(&sarg->value.seqset, ctx->pool, 16); + return sarg; +} + +static struct mail_search_arg * +imap_search_inthread(struct mail_search_build_context *ctx, + const struct imap_arg **imap_args) +{ + struct mail_search_arg *sarg; + + /* */ + enum mail_thread_type thread_type; + const char *algorithm; + + if (!imap_arg_get_atom(*imap_args, &algorithm)) { + ctx->error = "Invalid parameter for INTHREAD"; + return NULL; + } + + if (!mail_thread_type_parse(algorithm, &thread_type)) { + ctx->error = "Unknown thread algorithm"; + return NULL; + } + *imap_args += 1; + + sarg = search_arg_new(ctx->pool, SEARCH_INTHREAD); + sarg->value.thread_type = thread_type; + sarg->value.subargs = mail_search_build_next(ctx, imap_args); + if (sarg->value.subargs == NULL) + return NULL; + return sarg; +} + +CALLBACK_STR(x_guid, SEARCH_GUID); +CALLBACK_STR(x_mailbox, SEARCH_MAILBOX); + +const struct mail_search_register_arg mail_search_register_imap[] = { + /* argument set operations */ + { "NOT", imap_search_not }, + { "OR", imap_search_or }, + + /* message sets */ + { "ALL", imap_search_all }, + { "UID", imap_search_uid }, + + /* flags */ + { "ANSWERED", imap_search_answered }, + { "UNANSWERED", imap_search_unanswered }, + { "DELETED", imap_search_deleted }, + { "UNDELETED", imap_search_undeleted }, + { "DRAFT", imap_search_draft }, + { "UNDRAFT", imap_search_undraft }, + { "FLAGGED", imap_search_flagged }, + { "UNFLAGGED", imap_search_unflagged }, + { "SEEN", imap_search_seen }, + { "UNSEEN", imap_search_unseen }, + { "RECENT", imap_search_recent }, + { "OLD", imap_search_old }, + { "NEW", imap_search_new }, + + /* keywords */ + { "KEYWORD", imap_search_keyword }, + { "UNKEYWORD", imap_search_unkeyword }, + + /* dates */ + { "BEFORE", imap_search_before }, + { "ON", imap_search_on }, + { "SINCE", imap_search_since }, + { "SENTBEFORE", imap_search_sentbefore }, + { "SENTON", imap_search_senton }, + { "SENTSINCE", imap_search_sentsince }, + { "X-SAVEDBEFORE", imap_search_x_savedbefore }, + { "X-SAVEDON", imap_search_x_savedon }, + { "X-SAVEDSINCE", imap_search_x_savedsince }, + + /* sizes */ + { "LARGER", imap_search_larger }, + { "SMALLER", imap_search_smaller }, + + /* headers */ + { "BCC", imap_search_bcc }, + { "CC", imap_search_cc }, + { "FROM", imap_search_from }, + { "TO", imap_search_to }, + { "SUBJECT", imap_search_subject }, + { "HEADER", imap_search_header }, + + /* body */ + { "BODY", imap_search_body }, + { "TEXT", imap_search_text }, + { "X-BODY-FAST", imap_search_x_body_fast }, + { "X-TEXT-FAST", imap_search_x_text_fast }, + + /* WITHIN extension: */ + { "OLDER", imap_search_older }, + { "YOUNGER", imap_search_younger }, + + /* CONDSTORE extension: */ + { "MODSEQ", imap_search_modseq }, + + /* SEARCHRES extension: */ + { "$", imap_search_last_result }, + + /* Other Dovecot extensions: */ + { "INTHREAD", imap_search_inthread }, + { "X-GUID", imap_search_x_guid }, + { "X-MAILBOX", imap_search_x_mailbox } +}; + +struct mail_search_register *mail_search_register_init_imap(void) +{ + struct mail_search_register *reg; + + reg = mail_search_register_init(); + mail_search_register_add(reg, mail_search_register_imap, + N_ELEMENTS(mail_search_register_imap)); + mail_search_register_fallback(reg, imap_search_fallback); + return reg; +} diff --git a/src/lib-storage/mail-search-register.c b/src/lib-storage/mail-search-register.c new file mode 100644 index 0000000000..2e86c04765 --- /dev/null +++ b/src/lib-storage/mail-search-register.c @@ -0,0 +1,71 @@ +/* Copyright (c) 2010 Dovecot authors, see the included COPYING file */ + +#include "lib.h" +#include "array.h" +#include "mail-search.h" +#include "mail-search-register.h" + +struct mail_search_register { + ARRAY_DEFINE(args, struct mail_search_register_arg); + mail_search_register_fallback_t *fallback; +}; + +struct mail_search_register *mail_search_register_init(void) +{ + struct mail_search_register *reg; + + reg = i_new(struct mail_search_register, 1); + i_array_init(®->args, 64); + return reg; +} + +void mail_search_register_deinit(struct mail_search_register **_reg) +{ + struct mail_search_register *reg = *_reg; + + *_reg = NULL; + + array_free(®->args); + i_free(reg); +} + +static int +mail_search_register_arg_cmp(const struct mail_search_register_arg *arg1, + const struct mail_search_register_arg *arg2) +{ + return strcmp(arg1->key, arg2->key); +} + +void mail_search_register_add(struct mail_search_register *reg, + const struct mail_search_register_arg *arg, + unsigned int count) +{ + array_append(®->args, arg, count); + array_sort(®->args, mail_search_register_arg_cmp); +} + +void mail_search_register_fallback(struct mail_search_register *reg, + mail_search_register_fallback_t *fallback) +{ + reg->fallback = fallback; +} + +const struct mail_search_register_arg * +mail_search_register_find(struct mail_search_register *reg, const char *key) +{ + struct mail_search_register_arg arg; + + arg.key = key; + return array_bsearch(®->args, &arg, mail_search_register_arg_cmp); +} + +bool mail_search_register_get_fallback(struct mail_search_register *reg, + mail_search_register_fallback_t **fallback_r) +{ + if (reg->fallback == NULL) + return FALSE; + + *fallback_r = reg->fallback; + return TRUE; +} + diff --git a/src/lib-storage/mail-search-register.h b/src/lib-storage/mail-search-register.h new file mode 100644 index 0000000000..8b3b636771 --- /dev/null +++ b/src/lib-storage/mail-search-register.h @@ -0,0 +1,44 @@ +#ifndef MAIL_SEARCH_REGISTER_H +#define MAIL_SEARCH_REGISTER_H + +struct imap_arg; +struct mail_search_arg; +struct mail_search_build_context; + +struct mail_search_register_arg { + const char *key; + + /* read wanted parameters from imap_arg, returns parsed arg or NULL if + error. error message is set to ctx. */ + struct mail_search_arg * + (*build)(struct mail_search_build_context *ctx, + const struct imap_arg **imap_args); +}; + +typedef struct mail_search_arg * +mail_search_register_fallback_t(struct mail_search_build_context *ctx, + const char *key, + const struct imap_arg **imap_args); + +struct mail_search_register *mail_search_register_init(void); +void mail_search_register_deinit(struct mail_search_register **reg); + +void mail_search_register_add(struct mail_search_register *reg, + const struct mail_search_register_arg *arg, + unsigned int count); +/* Register a fallback handler. It's responsible for giving also the + "unknown key" error. */ +void mail_search_register_fallback(struct mail_search_register *reg, + mail_search_register_fallback_t *fallback); + +/* Find key's registered arg, or NULL if not found. */ +const struct mail_search_register_arg * +mail_search_register_find(struct mail_search_register *reg, const char *key); +/* Get registered fallback arg. Returns FALSE if fallback hasn't been + registered. */ +bool mail_search_register_get_fallback(struct mail_search_register *reg, + mail_search_register_fallback_t **fallback_r); + +struct mail_search_register *mail_search_register_init_imap(void); + +#endif diff --git a/src/lib-storage/mail-storage-private.h b/src/lib-storage/mail-storage-private.h index 4012851e65..23c2d73be8 100644 --- a/src/lib-storage/mail-storage-private.h +++ b/src/lib-storage/mail-storage-private.h @@ -445,6 +445,8 @@ extern struct mail_storage_module_register mail_storage_module_register; /* Storage's module_id for mail_index. */ extern struct mail_module_register mail_module_register; +extern struct mail_search_register *mail_search_register_imap; + #define MAIL_STORAGE_CONTEXT(obj) \ MODULE_CONTEXT(obj, mail_storage_mail_index_module) extern MODULE_CONTEXT_DEFINE(mail_storage_mail_index_module, diff --git a/src/lib-storage/mail-storage.c b/src/lib-storage/mail-storage.c index ad696d937f..44fccbf763 100644 --- a/src/lib-storage/mail-storage.c +++ b/src/lib-storage/mail-storage.c @@ -15,6 +15,7 @@ #include "mail-storage-settings.h" #include "mail-namespace.h" #include "mail-search.h" +#include "mail-search-register.h" #include "mailbox-search-result-private.h" #include @@ -24,6 +25,7 @@ struct mail_storage_module_register mail_storage_module_register = { 0 }; struct mail_module_register mail_module_register = { 0 }; +struct mail_search_register *mail_search_register_imap; struct mail_storage_mail_index_module mail_storage_mail_index_module = MODULE_CONTEXT_INIT(&mail_index_module_register); @@ -35,10 +37,12 @@ void mail_storage_init(void) mailbox_lists_init(); mail_storage_hooks_init(); i_array_init(&mail_storage_classes, 8); + mail_search_register_imap = mail_search_register_init_imap(); } void mail_storage_deinit(void) { + mail_search_register_deinit(&mail_search_register_imap); if (array_is_created(&mail_storage_classes)) array_free(&mail_storage_classes); mail_storage_hooks_deinit(); -- 2.47.3