]> git.ipfire.org Git - thirdparty/dovecot/core.git/commitdiff
lib-storage: Rewrote search query building code to be more modular.
authorTimo Sirainen <tss@iki.fi>
Tue, 13 Apr 2010 15:59:23 +0000 (18:59 +0300)
committerTimo Sirainen <tss@iki.fi>
Tue, 13 Apr 2010 15:59:23 +0000 (18:59 +0300)
--HG--
branch : HEAD

src/lib-storage/Makefile.am
src/lib-storage/mail-search-build.c
src/lib-storage/mail-search-build.h
src/lib-storage/mail-search-register-imap.c [new file with mode: 0644]
src/lib-storage/mail-search-register.c [new file with mode: 0644]
src/lib-storage/mail-search-register.h [new file with mode: 0644]
src/lib-storage/mail-storage-private.h
src/lib-storage/mail-storage.c

index 17cb412114c79b35b856c3fe07a55f1fc7168c60..189f0c6c6336f26545168c7683a841a75d8d82af 100644 (file)
@@ -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 \
index 086956f37aa045dab647f74ea7a4ed6739bf9259..6eac850bbcb190895e8eab58ce9644744f8d3027 100644 (file)
 /* 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,
@@ -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;
index 03cdb8adef2f26ceb7e40742ea4014a4e8dea27c..880bdf0307ba7e6f33ee5d5b6aa8296d09e798f7 100644 (file)
@@ -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 (file)
index 0000000..2886415
--- /dev/null
@@ -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 <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;
+}
diff --git a/src/lib-storage/mail-search-register.c b/src/lib-storage/mail-search-register.c
new file mode 100644 (file)
index 0000000..2e86c04
--- /dev/null
@@ -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(&reg->args, 64);
+       return reg;
+}
+
+void mail_search_register_deinit(struct mail_search_register **_reg)
+{
+       struct mail_search_register *reg = *_reg;
+
+       *_reg = NULL;
+
+       array_free(&reg->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(&reg->args, arg, count);
+       array_sort(&reg->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(&reg->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 (file)
index 0000000..8b3b636
--- /dev/null
@@ -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
index 4012851e65ec748557b1e6e32328cbf0f419b33c..23c2d73be885f55a15129f904a9d011df913ce28 100644 (file)
@@ -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,
index ad696d937f61c642f266544ef123b17a2bcd48ef..44fccbf763d9961eea22a0b4c2c077dee05ca7a3 100644 (file)
@@ -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 <stdlib.h>
@@ -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();