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 \
mail-namespace.h \
mail-search.h \
mail-search-build.h \
+ mail-search-register.h \
mail-thread.h \
mail-storage.h \
mail-storage-private.h \
/* 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 <stdlib.h>
-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) {
- /* <name> <type> */
- 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) {
- /* <string> */
- 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) {
- /* <date> */
- return ARG_NEW_DATE(SEARCH_BEFORE,
- MAIL_SEARCH_DATE_TYPE_RECEIVED);
- } else if (strcmp(key, "BCC") == 0) {
- /* <string> */
- return ARG_NEW_HEADER(SEARCH_HEADER_ADDRESS, key);
- }
- break;
- case 'C':
- if (strcmp(key, "CC") == 0) {
- /* <string> */
- 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) {
- /* <string> */
- return ARG_NEW_HEADER(SEARCH_HEADER_ADDRESS, key);
- }
- break;
- case 'H':
- if (strcmp(key, "HEADER") == 0) {
- /* <field-name> <string> */
- 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) {
- /* <algorithm> <search key> */
- 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) {
- /* <n> */
- return ARG_NEW_SIZE(SEARCH_LARGER);
- }
- break;
- case 'M':
- if (strcmp(key, "MODSEQ") == 0) {
- /* [<name> <type>] <n> */
- 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) {
- /* <search-key1> <search-key2> */
- *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;
-
- /* <key> OR <key> OR ... <key> - 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) {
- /* <date> */
- 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) {
- /* <interval> - 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) {
- /* <string> */
- return ARG_NEW_HEADER(SEARCH_HEADER_COMPRESS_LWSP, key);
- } else if (strcmp(key, "SENTBEFORE") == 0) {
- /* <date> */
- return ARG_NEW_DATE(SEARCH_BEFORE,
- MAIL_SEARCH_DATE_TYPE_SENT);
- } else if (strcmp(key, "SENTON") == 0) {
- /* <date> */
- return ARG_NEW_DATE(SEARCH_ON,
- MAIL_SEARCH_DATE_TYPE_SENT);
- } else if (strcmp(key, "SENTSINCE") == 0) {
- /* <date> */
- return ARG_NEW_DATE(SEARCH_SINCE,
- MAIL_SEARCH_DATE_TYPE_SENT);
- } else if (strcmp(key, "SINCE") == 0) {
- /* <date> */
- return ARG_NEW_DATE(SEARCH_SINCE,
- MAIL_SEARCH_DATE_TYPE_RECEIVED);
- } else if (strcmp(key, "SMALLER") == 0) {
- /* <n> */
- return ARG_NEW_SIZE(SEARCH_SMALLER);
- }
- break;
- case 'T':
- if (strcmp(key, "TEXT") == 0) {
- /* <string> */
- 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) {
- /* <string> */
- return ARG_NEW_HEADER(SEARCH_HEADER_ADDRESS, key);
- }
- break;
- case 'U':
- if (strcmp(key, "UID") == 0) {
- /* <message set> */
- 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) {
- /* <interval> - WITHIN extension */
- return ARG_NEW_INTERVAL(SEARCH_SINCE);
- }
- break;
- case 'X':
- if (strcmp(key, "X-BODY-FAST") == 0) {
- /* <string> */
- 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) {
- /* <string> */
- 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) {
- /* <string> */
- return ARG_NEW_STR(SEARCH_GUID);
- } else if (strcmp(key, "X-MAILBOX") == 0) {
- /* <string> */
- return ARG_NEW_STR(SEARCH_MAILBOX);
- } else if (strcmp(key, "X-SAVEDBEFORE") == 0) {
- /* <date> */
- return ARG_NEW_DATE(SEARCH_BEFORE,
- MAIL_SEARCH_DATE_TYPE_SAVED);
- } else if (strcmp(key, "X-SAVEDON") == 0) {
- /* <date> */
- return ARG_NEW_DATE(SEARCH_ON,
- MAIL_SEARCH_DATE_TYPE_SAVED);
- } else if (strcmp(key, "X-SAVEDSINCE") == 0) {
- /* <date> */
- return ARG_NEW_DATE(SEARCH_SINCE,
- MAIL_SEARCH_DATE_TYPE_SAVED);
- }
- break;
- default:
- if (*key == '*' || (*key >= '0' && *key <= '9')) {
- /* <message-set> */
- 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,
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;
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;
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);
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
--- /dev/null
+/* 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 <stdlib.h>
+
+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')) {
+ /* <message-set> */
+ 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;
+
+ /* <search-key1> <search-key2> */
+ 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;
+
+ /* <key> OR <key> OR ... <key> - 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;
+
+ /* <message set> */
+ 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;
+
+ /* <field-name> <string> */
+ 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;
+
+ /* [<name> <type>] <modseq> */
+ 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) {
+ /* <name> <type> */
+ 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;
+
+ /* <algorithm> <search key> */
+ 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;
+}
--- /dev/null
+/* 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;
+}
+
--- /dev/null
+#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
/* 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,
#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 <stdlib.h>
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);
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();