diff -r d440b7cad709 src/lib-storage/mail-search-build.c --- a/src/lib-storage/mail-search-build.c Mon Jun 09 22:02:02 2008 +0300 +++ b/src/lib-storage/mail-search-build.c Mon Jun 09 22:24:19 2008 +0300 @@ -15,6 +15,105 @@ const char *error; }; +struct mail_search_arg_sub { + struct mail_search_arg arg; + struct mail_search_arg *subargs; +}; + +struct mail_search_arg_seqset { + struct mail_search_arg arg; + ARRAY_TYPE(seq_range) seqset; + /* SEARCHRES extension: $ used */ + bool use_last_saved_search; +}; + +static struct mail_search_arg * +search_arg_build(struct mail_search_args *args, + const struct imap_arg **imap_args, const char **error_r); + +static void search_args_link(struct mail_search_arg **args, + struct mail_search_arg *subarg) +{ + i_assert(subarg->next == NULL); + + subarg->next = *args; + *args = subarg; +} + +static const char * +arg_next_str(const struct imap_arg **imap_args, const char **error_r) +{ + const char *value; + + if ((*imap_args)->type == IMAP_ARG_EOL) { + *error_r = "Missing parameter for argument"; + return NULL; + } + if ((*imap_args)->type != IMAP_ARG_ATOM && + (*imap_args)->type != IMAP_ARG_STRING) { + *error_r = "Invalid parameter for argument"; + return NULL; + } + + value = IMAP_ARG_STR_NONULL(*imap_args); + *imap_args += 1; + return value; +} + +static struct mail_search_arg * +search_seqset_build(struct mail_search_args *args, + const struct imap_arg **imap_args, const char **error_r) +{ + struct mail_search_arg_seqset *arg; + const char *str; + + str = arg_next_str(imap_args, error_r); + if (str == NULL) + return NULL; + + arg = p_new(args->pool, struct mail_search_arg_seqset, 1); + p_array_init(&arg->seqset, args->pool, 16); + if (strcmp(str, "$") == 0) { + /* SEARCHRES: delay initialization */ + arg->use_last_saved_search = TRUE; + } else if (imap_seq_set_parse(str, &arg->seqset) < 0) { + *error_r = "Invalid seq-set"; + return NULL; + } + return &arg->arg; +} + +static struct mail_search_arg * +search_sub_build(struct mail_search_args *args, + const struct imap_arg **imap_args, const char **error_r) +{ + struct mail_search_arg_sub *arg; + struct mail_search_arg *subarg; + + arg = p_new(args->pool, struct mail_search_arg_sub, 1); + while ((*imap_args)->type != IMAP_ARG_EOL) { + subarg = search_arg_build(args, imap_args, error_r); + if (subarg == NULL) + return NULL; + + search_args_link(&arg->subargs, subarg); + } + return &arg->arg; +} + +static const struct mail_search_handler search_handlers[] = { + { "UID", SEARCH_ARG_TYPE_STATIC_METADATA, + search_seqset_build, NULL, NULL } +}; +static const struct mail_search_handler search_seqset_handler = { + NULL, SEARCH_ARG_TYPE_STATIC_METADATA, + search_seqset_build, NULL, NULL +}; +static const struct mail_search_handler search_sub_handler = { + NULL, SEARCH_ARG_TYPE_SUB, search_sub_build, NULL, NULL +}; + +#if 0 static struct mail_search_arg * search_arg_new(pool_t pool, enum mail_search_arg_type type) { @@ -24,25 +123,6 @@ 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 ((*args)->type == IMAP_ARG_EOL) { - data->error = "Missing parameter for argument"; - return FALSE; - } - if ((*args)->type != IMAP_ARG_ATOM && - (*args)->type != IMAP_ARG_STRING) { - data->error = "Invalid parameter for argument"; - return FALSE; - } - - *value_r = IMAP_ARG_STR(*args); - *args += 1; - return TRUE; } #define ARG_NEW_SINGLE(type) \ @@ -260,56 +340,79 @@ sarg->value.modseq->modseq = strtoull(value, NULL, 10); return TRUE; } +#endif -static bool search_arg_build(struct search_build_data *data, - const struct imap_arg **args, - struct mail_search_arg **next_sarg) +static int search_handler_cmp(const void *key, const void *data) { - struct mail_search_arg **subargs, *sarg; - const struct imap_arg *arg; + const char *name = key; + const struct mail_search_handler *handler = data; + + return strcmp(name, handler->name); +} + +static const struct mail_search_handler *search_handler_find(const char *name) +{ + return bsearch(name, search_handlers, N_ELEMENTS(search_handlers), + sizeof(search_handlers[0]), search_handler_cmp); +} + +static struct mail_search_arg * +search_arg_build(struct mail_search_args *args, + const struct imap_arg **imap_args, const char **error_r) +{ + const struct imap_arg *imap_arg = *imap_args; + const struct mail_search_handler *handler; + struct mail_search_arg *arg; const char *str; - if ((*args)->type == IMAP_ARG_EOL) { - data->error = "Missing argument"; - return FALSE; + if (imap_arg->type == IMAP_ARG_EOL) { + *error_r = "Missing argument"; + return NULL; + } + *imap_args += 1; + + if (imap_arg->type == IMAP_ARG_NIL) { + /* NIL not allowed */ + *error_r = "NIL not allowed"; + return NULL; } - arg = *args; - - if (arg->type == IMAP_ARG_NIL) { - /* NIL not allowed */ - data->error = "NIL not allowed"; - return FALSE; - } - - if (arg->type == IMAP_ARG_LIST) { - const struct imap_arg *listargs = IMAP_ARG_LIST_ARGS(arg); + if (imap_arg->type == IMAP_ARG_LIST) { + const struct imap_arg *listargs = IMAP_ARG_LIST_ARGS(imap_arg); if (listargs->type == IMAP_ARG_EOL) { - data->error = "Empty list not allowed"; - return FALSE; + *error_r = "Empty list not allowed"; + return NULL; } - *next_sarg = search_arg_new(data->pool, SEARCH_SUB); - subargs = &(*next_sarg)->value.subargs; - while (listargs->type != IMAP_ARG_EOL) { - if (!search_arg_build(data, &listargs, subargs)) - return FALSE; - subargs = &(*subargs)->next; + handler = &search_sub_handler; + arg = handler->build(args, &listargs, error_r); + } else { + i_assert(imap_arg->type == IMAP_ARG_ATOM || + imap_arg->type == IMAP_ARG_STRING); + + str = t_str_ucase(IMAP_ARG_STR_NONULL(imap_arg)); + if (*str >= '0' && *str <= '9') { + /* sequence-set most likely */ + *imap_args -= 1; + handler = &search_seqset_handler; + } else { + handler = search_handler_find(str); + if (handler == NULL) { + *error_r = t_strconcat("Unknown search key ", + str, NULL); + return NULL; + } } + arg = handler->build(args, imap_args, error_r); + } + if (arg == NULL) + return NULL; - *args += 1; - return TRUE; - } - - i_assert(arg->type == IMAP_ARG_ATOM || - arg->type == IMAP_ARG_STRING); - - /* string argument - get the name and jump to next */ - str = IMAP_ARG_STR(arg); - *args += 1; - str = t_str_ucase(str); - + arg->handler = handler; + search_args_link(&args->args, arg); + return arg; +#if 0 switch (*str) { case 'A': if (strcmp(str, "ANSWERED") == 0) @@ -320,9 +423,9 @@ case 'B': if (strcmp(str, "BODY") == 0) { /* */ - if (IMAP_ARG_TYPE_IS_STRING((*args)->type) && - *IMAP_ARG_STR(*args) == '\0') { - *args += 1; + if (IMAP_ARG_TYPE_IS_STRING((*imap_args)->type) && + *IMAP_ARG_STR(*imap_args) == '\0') { + *imap_args += 1; return ARG_NEW_SINGLE(SEARCH_ALL); } return ARG_NEW_STR(SEARCH_BODY); @@ -359,18 +462,18 @@ /* */ const char *key; - if ((*args)->type == IMAP_ARG_EOL) { - data->error = "Missing parameter for HEADER"; + if ((*imap_args)->type == IMAP_ARG_EOL) { + *error_r = "Missing parameter for HEADER"; return FALSE; } - if ((*args)->type != IMAP_ARG_ATOM && - (*args)->type != IMAP_ARG_STRING) { - data->error = "Invalid parameter for HEADER"; + if ((*imap_args)->type != IMAP_ARG_ATOM && + (*imap_args)->type != IMAP_ARG_STRING) { + *error_r = "Invalid parameter for HEADER"; return FALSE; } - key = t_str_ucase(IMAP_ARG_STR(*args)); - *args += 1; + key = t_str_ucase(IMAP_ARG_STR(*imap_args)); + *imap_args += 1; return ARG_NEW_HEADER(SEARCH_HEADER, key); } break; @@ -393,7 +496,7 @@ break; case 'N': if (strcmp(str, "NOT") == 0) { - if (!search_arg_build(data, args, next_sarg)) + if (!search_arg_build(data, imap_args, next_sarg)) return FALSE; (*next_sarg)->not = !(*next_sarg)->not; return TRUE; @@ -418,25 +521,25 @@ subargs = &(*next_sarg)->value.subargs; for (;;) { - if (!search_arg_build(data, args, subargs)) + if (!search_arg_build(data, imap_args, subargs)) return FALSE; subargs = &(*subargs)->next; /* OR OR ... - put them all under one SEARCH_OR list. */ - if ((*args)->type == IMAP_ARG_EOL) + if ((*imap_args)->type == IMAP_ARG_EOL) break; - if ((*args)->type != IMAP_ARG_ATOM || - strcasecmp(IMAP_ARG_STR_NONULL(*args), + if ((*imap_args)->type != IMAP_ARG_ATOM || + strcasecmp(IMAP_ARG_STR_NONULL(*imap_args), "OR") != 0) break; - *args += 1; + *imap_args += 1; } - if (!search_arg_build(data, args, subargs)) + if (!search_arg_build(data, imap_args, subargs)) return FALSE; return TRUE; } if (strcmp(str, "ON") == 0) { @@ -490,9 +593,9 @@ case 'T': if (strcmp(str, "TEXT") == 0) { /* */ - if (IMAP_ARG_TYPE_IS_STRING((*args)->type) && - *IMAP_ARG_STR(*args) == '\0') { - *args += 1; + if (IMAP_ARG_TYPE_IS_STRING((*imap_args)->type) && + *IMAP_ARG_STR(*imap_args) == '\0') { + *imap_args += 1; return ARG_NEW_SINGLE(SEARCH_ALL); } return ARG_NEW_STR(SEARCH_TEXT); @@ -515,7 +618,7 @@ } if (imap_seq_set_parse(sarg->value.str, &sarg->value.seqset) < 0) { - data->error = "Invalid UID messageset"; + *error_r = "Invalid UID messageset"; return FALSE; } return TRUE; @@ -560,17 +663,17 @@ case 'X': if (strcmp(str, "X-BODY-FAST") == 0) { /* */ - if (IMAP_ARG_TYPE_IS_STRING((*args)->type) && - *IMAP_ARG_STR(*args) == '\0') { - *args += 1; + if (IMAP_ARG_TYPE_IS_STRING((*imap_args)->type) && + *IMAP_ARG_STR(*imap_args) == '\0') { + *imap_args += 1; return ARG_NEW_SINGLE(SEARCH_ALL); } return ARG_NEW_STR(SEARCH_BODY_FAST); } else if (strcmp(str, "X-TEXT-FAST") == 0) { /* */ - if (IMAP_ARG_TYPE_IS_STRING((*args)->type) && - *IMAP_ARG_STR(*args) == '\0') { - *args += 1; + if (IMAP_ARG_TYPE_IS_STRING((*imap_args)->type) && + *IMAP_ARG_STR(*imap_args) == '\0') { + *imap_args += 1; return ARG_NEW_SINGLE(SEARCH_ALL); } return ARG_NEW_STR(SEARCH_TEXT_FAST); @@ -585,7 +688,7 @@ p_array_init(&(*next_sarg)->value.seqset, data->pool, 16); if (imap_seq_set_parse(str, &(*next_sarg)->value.seqset) < 0) { - data->error = "Invalid messageset"; + *error_r = "Invalid messageset"; return FALSE; } return TRUE; @@ -601,9 +704,7 @@ } break; } - - data->error = t_strconcat("Unknown argument ", str, NULL); - return FALSE; +#endif } int mail_search_build_from_imap_args(const struct imap_arg *imap_args, @@ -611,9 +712,8 @@ struct mail_search_args **args_r, const char **error_r) { - struct search_build_data data; struct mail_search_args *args; - struct mail_search_arg **sargs; + struct mail_search_arg *arg; *args_r = NULL; *error_r = NULL; @@ -621,17 +721,14 @@ args = mail_search_build_init(); args->charset = p_strdup(args->pool, charset); - data.pool = args->pool; - data.error = NULL; - - sargs = &args->args; while (imap_args->type != IMAP_ARG_EOL) { - if (!search_arg_build(&data, &imap_args, sargs)) { + arg = search_arg_build(args, &imap_args, error_r); + if (arg == NULL) { pool_unref(&args->pool); - *error_r = data.error; return -1; } - sargs = &(*sargs)->next; + + search_args_link(&args->args, arg); } *args_r = args; diff -r d440b7cad709 src/lib-storage/mail-search.h --- a/src/lib-storage/mail-search.h Mon Jun 09 22:02:02 2008 +0300 +++ b/src/lib-storage/mail-search.h Mon Jun 09 22:24:19 2008 +0300 @@ -4,44 +4,17 @@ #include "seq-range-array.h" #include "mail-types.h" +struct imap_arg; +struct mail_search_args; + enum mail_search_arg_type { - SEARCH_OR, - SEARCH_SUB, - - /* sequence sets */ - SEARCH_ALL, - SEARCH_SEQSET, - SEARCH_UIDSET, - - /* flags */ - SEARCH_FLAGS, - SEARCH_KEYWORDS, - - /* dates */ - SEARCH_BEFORE, - SEARCH_ON, /* time must point to beginning of the day */ - SEARCH_SINCE, - SEARCH_SENTBEFORE, - SEARCH_SENTON, /* time must point to beginning of the day */ - SEARCH_SENTSINCE, - - /* sizes */ - SEARCH_SMALLER, - SEARCH_LARGER, - - /* headers */ - SEARCH_HEADER, - SEARCH_HEADER_ADDRESS, - SEARCH_HEADER_COMPRESS_LWSP, - - /* body */ - SEARCH_BODY, - SEARCH_TEXT, - SEARCH_BODY_FAST, - SEARCH_TEXT_FAST, - - /* extensions */ - SEARCH_MODSEQ + SEARCH_ARG_TYPE_SUB, + SEARCH_ARG_TYPE_DYNAMIC, + SEARCH_ARG_TYPE_STATIC_METADATA, + SEARCH_ARG_TYPE_STATIC_HEADER, + SEARCH_ARG_TYPE_STATIC_HEADER_ADDRESS, + SEARCH_ARG_TYPE_STATIC_HEADER_COMPRESS_LWSP, + SEARCH_ARG_TYPE_STATIC_BODY }; enum mail_search_arg_flag { @@ -61,9 +34,29 @@ enum mail_search_modseq_type type; }; +struct mail_search_handler { + const char *name; + enum mail_search_arg_type type; + + /* If imap_args is invalid, return NULL and set error_r. Mailbox isn't + known at this point. */ + struct mail_search_arg * + (*build)(struct mail_search_args *args, + const struct imap_arg **imap_args, + const char **error_r); + /* Initialize arguments before searching. Mailbox is known now. + May be NULL. */ + void (*init)(struct mail_search_args *args, + struct mail_search_arg *arg); + /* Free any resourced used by the arg. May be NULL. */ + void (*free)(struct mail_search_args *args, + struct mail_search_arg *arg); +}; + struct mail_search_arg { struct mail_search_arg *next; + const struct mail_search_handler *handler; enum mail_search_arg_type type; struct { struct mail_search_arg *subargs;