Mercurial > dovecot > original-hg > dovecot-1.2
changeset 7619:56f55bd35aa5 HEAD
Moved IMAP messageset handling to lib-imap/ and searching to lib-storage/.
Rewrote messageset handling to use struct seq_range instead.
author | Timo Sirainen <tss@iki.fi> |
---|---|
date | Fri, 14 Mar 2008 11:59:36 +0200 |
parents | 6dbd70663adf |
children | 4b8c1c164d8f |
files | src/imap/Makefile.am src/imap/cmd-search.c src/imap/imap-messageset.c src/imap/imap-messageset.h src/imap/imap-search.c src/imap/imap-search.h src/lib-imap/Makefile.am src/lib-imap/imap-messageset.c src/lib-imap/imap-messageset.h src/lib-storage/Makefile.am src/lib-storage/index/index-search.c src/lib-storage/mail-search-build.c src/lib-storage/mail-search-build.h src/lib-storage/mail-search.h src/plugins/fts/fts-storage.c src/pop3/commands.c |
diffstat | 16 files changed, 771 insertions(+), 781 deletions(-) [+] |
line wrap: on
line diff
--- a/src/imap/Makefile.am Fri Mar 14 09:44:34 2008 +0200 +++ b/src/imap/Makefile.am Fri Mar 14 11:59:36 2008 +0200 @@ -75,7 +75,6 @@ imap-expunge.c \ imap-fetch.c \ imap-fetch-body.c \ - imap-messageset.c \ imap-search.c \ imap-sort.c \ imap-status.c \ @@ -92,7 +91,6 @@ common.h \ imap-expunge.h \ imap-fetch.h \ - imap-messageset.h \ imap-search.h \ imap-sort.h \ imap-status.h \
--- a/src/imap/cmd-search.c Fri Mar 14 09:44:34 2008 +0200 +++ b/src/imap/cmd-search.c Fri Mar 14 11:59:36 2008 +0200 @@ -4,6 +4,7 @@ #include "ostream.h" #include "str.h" #include "commands.h" +#include "mail-search-build.h" #include "imap-search.h" #define OUTBUF_SIZE 65536 @@ -62,7 +63,7 @@ if (ctx->to != NULL) timeout_remove(&ctx->to); str_free(&ctx->output_buf); - imap_search_args_free(ctx->box, ctx->sargs); + mail_search_args_deinit(ctx->sargs, ctx->box); cmd->context = NULL; return ret;
--- a/src/imap/imap-messageset.c Fri Mar 14 09:44:34 2008 +0200 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,86 +0,0 @@ -/* Copyright (c) 2002-2008 Dovecot authors, see the included COPYING file */ - -#include "lib.h" -#include "mail-search.h" -#include "imap-search.h" -#include "imap-messageset.h" - -static uint32_t get_next_number(const char **str) -{ - uint32_t num; - - num = 0; - while (**str != '\0') { - if (**str < '0' || **str > '9') - break; - - num = num*10 + (**str - '0'); - (*str)++; - } - - if (num == (uint32_t)-1) { - /* FIXME: ugly hack, we're using this number to mean the - last existing message. In reality UIDs should never get - this high, so we can quite safely just drop this one down. */ - num--; - } - - return num; -} - -struct mail_search_seqset * -imap_messageset_parse(pool_t pool, const char *messageset) -{ - struct mail_search_seqset *ret, **next; - uint32_t seq1, seq2; - - ret = NULL; - next = &ret; - - while (*messageset != '\0') { - if (*messageset == '*') { - /* last message */ - seq1 = (uint32_t)-1; - messageset++; - } else { - seq1 = get_next_number(&messageset); - if (seq1 == 0) - return NULL; - } - - if (*messageset != ':') - seq2 = seq1; - else { - /* first:last range */ - messageset++; - - if (*messageset == '*') { - seq2 = (uint32_t)-1; - messageset++; - } else { - seq2 = get_next_number(&messageset); - if (seq2 == 0) - return NULL; - } - } - - if (*messageset == ',') - messageset++; - else if (*messageset != '\0') - return NULL; - - if (seq1 > seq2) { - /* swap, as specified by RFC-3501 */ - uint32_t temp = seq1; - seq1 = seq2; - seq2 = temp; - } - - *next = p_new(pool, struct mail_search_seqset, 1); - (*next)->seq1 = seq1; - (*next)->seq2 = seq2; - next = &(*next)->next; - } - - return ret; -}
--- a/src/imap/imap-messageset.h Fri Mar 14 09:44:34 2008 +0200 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,7 +0,0 @@ -#ifndef IMAP_MESSAGESET_H -#define IMAP_MESSAGESET_H - -struct mail_search_seqset * -imap_messageset_parse(pool_t pool, const char *messageset); - -#endif
--- a/src/imap/imap-search.c Fri Mar 14 09:44:34 2008 +0200 +++ b/src/imap/imap-search.c Fri Mar 14 11:59:36 2008 +0200 @@ -3,7 +3,7 @@ #include "common.h" #include "mail-storage.h" #include "mail-search.h" -#include "imap-date.h" +#include "mail-search-build.h" #include "imap-search.h" #include "imap-parser.h" #include "imap-messageset.h" @@ -16,556 +16,42 @@ const char *error; }; -static int -imap_uidset_parse(pool_t pool, struct mailbox *box, const char *uidset, - struct mail_search_seqset **seqset_r, const char **error_r) -{ - struct mail_search_seqset *seqset, **p; - bool last; - - *seqset_r = imap_messageset_parse(pool, uidset); - if (*seqset_r == NULL) { - *error_r = "Invalid UID messageset"; - return -1; - } - - p = seqset_r; - for (seqset = *seqset_r; seqset != NULL; seqset = seqset->next) { - if (seqset->seq1 == (uint32_t)-1) { - /* last message, stays same */ - continue; - } - - last = seqset->seq2 == (uint32_t)-1; - mailbox_get_uids(box, seqset->seq1, seqset->seq2, - &seqset->seq1, &seqset->seq2); - if (seqset->seq1 == 0 && last) { - /* we need special case for too_high_uid:* case */ - seqset->seq1 = seqset->seq2 = (uint32_t)-1; - } - - if (seqset->seq1 != 0) - p = &seqset->next; - else - *p = seqset->next; - } - - *error_r = NULL; - return 0; -} - -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 ((*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) \ - 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; -} - -static bool -arg_new_keyword(struct search_build_data *data, - const struct imap_arg **args, - struct mail_search_arg **next_sarg) -{ - struct mail_search_arg *sarg; - const char *value, *keywords[2]; - struct mail_storage *storage; - enum mail_error error; - - *next_sarg = sarg = search_arg_new(data->pool, SEARCH_KEYWORDS); - if (!arg_get_next(data, args, &value)) - return FALSE; - - keywords[0] = value; - keywords[1] = NULL; - - if (mailbox_keywords_create(data->box, keywords, - &sarg->value.keywords) < 0) { - storage = mailbox_get_storage(data->box); - data->error = mail_storage_get_last_error(storage, &error); - return FALSE; - } - 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; - char *p; - - *next_sarg = sarg = search_arg_new(data->pool, type); - if (!arg_get_next(data, args, &value)) - return FALSE; - - sarg->value.size = strtoull(value, &p, 10); - if (*p != '\0') { - data->error = "Invalid search size parameter"; - return FALSE; - } - return TRUE; -} - -#define ARG_NEW_DATE(type) \ - arg_new_date(data, args, next_sarg, 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) -{ - 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; - } - 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 search_arg_build(struct search_build_data *data, - const struct imap_arg **args, - struct mail_search_arg **next_sarg) -{ - struct mail_search_seqset *seqset; - struct mail_search_arg **subargs; - const struct imap_arg *arg; - const char *str; - - if ((*args)->type == IMAP_ARG_EOL) { - data->error = "Missing argument"; - return FALSE; - } - - 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 (listargs->type == IMAP_ARG_EOL) { - data->error = "Empty list not allowed"; - return FALSE; - } - - *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; - } - - *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); - - switch (*str) { - case 'A': - if (strcmp(str, "ANSWERED") == 0) - return ARG_NEW_FLAGS(MAIL_ANSWERED); - else if (strcmp(str, "ALL") == 0) - return ARG_NEW_SINGLE(SEARCH_ALL); - break; - case 'B': - if (strcmp(str, "BODY") == 0) { - /* <string> */ - if (IMAP_ARG_TYPE_IS_STRING((*args)->type) && - *IMAP_ARG_STR(*args) == '\0') { - *args += 1; - return ARG_NEW_SINGLE(SEARCH_ALL); - } - return ARG_NEW_STR(SEARCH_BODY); - } else if (strcmp(str, "BEFORE") == 0) { - /* <date> */ - return ARG_NEW_DATE(SEARCH_BEFORE); - } else if (strcmp(str, "BCC") == 0) { - /* <string> */ - return ARG_NEW_HEADER(SEARCH_HEADER_ADDRESS, str); - } - break; - case 'C': - if (strcmp(str, "CC") == 0) { - /* <string> */ - return ARG_NEW_HEADER(SEARCH_HEADER_ADDRESS, str); - } - break; - case 'D': - if (strcmp(str, "DELETED") == 0) - return ARG_NEW_FLAGS(MAIL_DELETED); - else if (strcmp(str, "DRAFT") == 0) - return ARG_NEW_FLAGS(MAIL_DRAFT); - break; - case 'F': - if (strcmp(str, "FLAGGED") == 0) - return ARG_NEW_FLAGS(MAIL_FLAGGED); - else if (strcmp(str, "FROM") == 0) { - /* <string> */ - return ARG_NEW_HEADER(SEARCH_HEADER_ADDRESS, str); - } - break; - case 'H': - if (strcmp(str, "HEADER") == 0) { - /* <field-name> <string> */ - const char *key; - - if ((*args)->type == IMAP_ARG_EOL) { - data->error = "Missing parameter for HEADER"; - return FALSE; - } - if ((*args)->type != IMAP_ARG_ATOM && - (*args)->type != IMAP_ARG_STRING) { - data->error = "Invalid parameter for HEADER"; - return FALSE; - } - - key = t_str_ucase(IMAP_ARG_STR(*args)); - *args += 1; - return ARG_NEW_HEADER(SEARCH_HEADER, key); - } - break; - case 'K': - if (strcmp(str, "KEYWORD") == 0) { - /* <flag> */ - return arg_new_keyword(data, args, next_sarg); - } - break; - case 'L': - if (strcmp(str, "LARGER") == 0) { - /* <n> */ - return ARG_NEW_SIZE(SEARCH_LARGER); - } - break; - case 'N': - if (strcmp(str, "NOT") == 0) { - if (!search_arg_build(data, args, next_sarg)) - return FALSE; - (*next_sarg)->not = !(*next_sarg)->not; - return TRUE; - } else if (strcmp(str, "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(str, "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 ((*args)->type == IMAP_ARG_EOL) - break; - - if ((*args)->type != IMAP_ARG_ATOM || - strcasecmp(IMAP_ARG_STR_NONULL(*args), - "OR") != 0) - break; - - *args += 1; - } - - if (!search_arg_build(data, args, subargs)) - return FALSE; - return TRUE; - } if (strcmp(str, "ON") == 0) { - /* <date> */ - return ARG_NEW_DATE(SEARCH_ON); - } if (strcmp(str, "OLD") == 0) { - /* OLD == NOT RECENT */ - if (!ARG_NEW_FLAGS(MAIL_RECENT)) - return FALSE; - - (*next_sarg)->not = TRUE; - return TRUE; - } - break; - case 'R': - if (strcmp(str, "RECENT") == 0) - return ARG_NEW_FLAGS(MAIL_RECENT); - break; - case 'S': - if (strcmp(str, "SEEN") == 0) - return ARG_NEW_FLAGS(MAIL_SEEN); - else if (strcmp(str, "SUBJECT") == 0) { - /* <string> */ - return ARG_NEW_HEADER(SEARCH_HEADER_COMPRESS_LWSP, str); - } else if (strcmp(str, "SENTBEFORE") == 0) { - /* <date> */ - return ARG_NEW_DATE(SEARCH_SENTBEFORE); - } else if (strcmp(str, "SENTON") == 0) { - /* <date> */ - return ARG_NEW_DATE(SEARCH_SENTON); - } else if (strcmp(str, "SENTSINCE") == 0) { - /* <date> */ - return ARG_NEW_DATE(SEARCH_SENTSINCE); - } else if (strcmp(str, "SINCE") == 0) { - /* <date> */ - return ARG_NEW_DATE(SEARCH_SINCE); - } else if (strcmp(str, "SMALLER") == 0) { - /* <n> */ - return ARG_NEW_SIZE(SEARCH_SMALLER); - } - break; - case 'T': - if (strcmp(str, "TEXT") == 0) { - /* <string> */ - if (IMAP_ARG_TYPE_IS_STRING((*args)->type) && - *IMAP_ARG_STR(*args) == '\0') { - *args += 1; - return ARG_NEW_SINGLE(SEARCH_ALL); - } - return ARG_NEW_STR(SEARCH_TEXT); - } else if (strcmp(str, "TO") == 0) { - /* <string> */ - return ARG_NEW_HEADER(SEARCH_HEADER_ADDRESS, str); - } - break; - case 'U': - if (strcmp(str, "UID") == 0) { - /* <message set> */ - if (!ARG_NEW_STR(SEARCH_SEQSET)) - return FALSE; - - return imap_uidset_parse(data->pool, data->box, - (*next_sarg)->value.str, - &(*next_sarg)->value.seqset, - &data->error) == 0; - } else if (strcmp(str, "UNANSWERED") == 0) { - if (!ARG_NEW_FLAGS(MAIL_ANSWERED)) - return FALSE; - (*next_sarg)->not = TRUE; - return TRUE; - } else if (strcmp(str, "UNDELETED") == 0) { - if (!ARG_NEW_FLAGS(MAIL_DELETED)) - return FALSE; - (*next_sarg)->not = TRUE; - return TRUE; - } else if (strcmp(str, "UNDRAFT") == 0) { - if (!ARG_NEW_FLAGS(MAIL_DRAFT)) - return FALSE; - (*next_sarg)->not = TRUE; - return TRUE; - } else if (strcmp(str, "UNFLAGGED") == 0) { - if (!ARG_NEW_FLAGS(MAIL_FLAGGED)) - return FALSE; - (*next_sarg)->not = TRUE; - return TRUE; - } else if (strcmp(str, "UNKEYWORD") == 0) { - /* <flag> */ - if (!arg_new_keyword(data, args, next_sarg)) - return FALSE; - (*next_sarg)->not = TRUE; - return TRUE; - } else if (strcmp(str, "UNSEEN") == 0) { - if (!ARG_NEW_FLAGS(MAIL_SEEN)) - return FALSE; - (*next_sarg)->not = TRUE; - return TRUE; - } - break; - case 'X': - if (strcmp(str, "X-BODY-FAST") == 0) { - /* <string> */ - if (IMAP_ARG_TYPE_IS_STRING((*args)->type) && - *IMAP_ARG_STR(*args) == '\0') { - *args += 1; - return ARG_NEW_SINGLE(SEARCH_ALL); - } - return ARG_NEW_STR(SEARCH_BODY_FAST); - } else if (strcmp(str, "X-TEXT-FAST") == 0) { - /* <string> */ - if (IMAP_ARG_TYPE_IS_STRING((*args)->type) && - *IMAP_ARG_STR(*args) == '\0') { - *args += 1; - return ARG_NEW_SINGLE(SEARCH_ALL); - } - return ARG_NEW_STR(SEARCH_TEXT_FAST); - } - break; - default: - if (*str == '*' || (*str >= '0' && *str <= '9')) { - /* <message-set> */ - seqset = imap_messageset_parse(data->pool, str); - if (seqset == NULL) { - data->error = "Invalid messageset"; - return FALSE; - } - - if (!ARG_NEW_SINGLE(SEARCH_SEQSET)) - return FALSE; - - (*next_sarg)->value.seqset = seqset; - return TRUE; - } - break; - } - - data->error = t_strconcat("Unknown argument ", str, NULL); - return FALSE; -} - struct mail_search_arg * imap_search_args_build(pool_t pool, struct mailbox *box, const struct imap_arg *args, const char **error_r) { - struct search_build_data data; - struct mail_search_arg *first_sarg, **sargs; - - *error_r = NULL; - - data.box = box; - data.pool = pool; - data.error = NULL; + struct mail_search_arg *sargs; - /* get the first arg */ - first_sarg = NULL; sargs = &first_sarg; - while (args->type != IMAP_ARG_EOL) { - if (!search_arg_build(&data, &args, sargs)) { - imap_search_args_free(box, first_sarg); - *error_r = data.error; - return NULL; - } - sargs = &(*sargs)->next; - } + sargs = mail_search_build_from_imap_args(pool, args, error_r); + if (sargs == NULL) + return NULL; - return first_sarg; + mail_search_args_init(sargs, box, TRUE); + return sargs; } static bool -msgset_is_valid(const struct mail_search_seqset *set, uint32_t messages_count) +msgset_is_valid(ARRAY_TYPE(seq_range) *seqset, uint32_t messages_count) { + const struct seq_range *range; + unsigned int count; + /* when there are no messages, all messagesets are invalid. if there's at least one message: - * gives seq1 = seq2 = (uint32_t)-1 - n:* should work if n <= messages_count - n:m or m should work if m <= messages_count */ - if (set == NULL || messages_count == 0) + range = array_get(seqset, &count); + if (count == 0 || messages_count == 0) return FALSE; - for (; set != NULL; set = set->next) { - if ((set->seq1 > messages_count && set->seq1 != (uint32_t)-1) || - (set->seq2 > messages_count && set->seq2 != (uint32_t)-1)) + if (range[count-1].seq2 == (uint32_t)-1) { + if (range[count-1].seq1 > messages_count && + range[0].seq1 != (uint32_t)-1) + return FALSE; + } else { + if (range[count-1].seq2 > messages_count) return FALSE; } return TRUE; @@ -580,8 +66,9 @@ arg = p_new(cmd->pool, struct mail_search_arg, 1); arg->type = SEARCH_SEQSET; - arg->value.seqset = imap_messageset_parse(cmd->pool, messageset); - if (!msgset_is_valid(arg->value.seqset, cmd->client->messages_count)) { + p_array_init(&arg->value.seqset, cmd->pool, 16); + if (imap_messageset_parse(&arg->value.seqset, messageset) < 0 || + !msgset_is_valid(&arg->value.seqset, cmd->client->messages_count)) { *error_r = "Invalid messageset"; return -1; } @@ -589,16 +76,6 @@ return 0; } -void imap_search_args_free(struct mailbox *box, struct mail_search_arg *args) -{ - for (; args != NULL; args = args->next) { - if (args->type == SEARCH_KEYWORDS) - mailbox_keywords_free(box, &args->value.keywords); - else if (args->type == SEARCH_SUB || args->type == SEARCH_OR) - imap_search_args_free(box, args->value.subargs); - } -} - static int imap_search_get_uidset_arg(pool_t pool, struct mailbox *box, const char *uidset, struct mail_search_arg **arg_r, const char **error_r) @@ -606,10 +83,16 @@ struct mail_search_arg *arg; arg = p_new(pool, struct mail_search_arg, 1); - arg->type = SEARCH_SEQSET; + arg->type = SEARCH_UIDSET; + p_array_init(&arg->value.seqset, pool, 16); + if (imap_messageset_parse(&arg->value.seqset, uidset) < 0) { + *error_r = "Invalid uidset"; + return -1; + } + + mail_search_args_init(arg, box, TRUE); *arg_r = arg; - return imap_uidset_parse(pool, box, uidset, &arg->value.seqset, - error_r); + return 0; } struct mail_search_arg * @@ -617,7 +100,7 @@ const char *set, bool uid) { struct mail_search_arg *search_arg = NULL; - const char *error; + const char *error = NULL; int ret; if (!uid) {
--- a/src/imap/imap-search.h Fri Mar 14 09:44:34 2008 +0200 +++ b/src/imap/imap-search.h Fri Mar 14 11:59:36 2008 +0200 @@ -9,8 +9,6 @@ struct mail_search_arg * imap_search_args_build(pool_t pool, struct mailbox *box, const struct imap_arg *args, const char **error_r); -/* Free allocated keywords */ -void imap_search_args_free(struct mailbox *box, struct mail_search_arg *args); struct mail_search_arg * imap_search_get_arg(struct client_command_context *cmd,
--- a/src/lib-imap/Makefile.am Fri Mar 14 09:44:34 2008 +0200 +++ b/src/lib-imap/Makefile.am Fri Mar 14 11:59:36 2008 +0200 @@ -11,8 +11,9 @@ imap-date.c \ imap-envelope.c \ imap-match.c \ + imap-messageset.c \ + imap-parser.c \ imap-quote.c \ - imap-parser.c \ imap-util.c headers = \ @@ -21,8 +22,9 @@ imap-date.h \ imap-envelope.h \ imap-match.h \ + imap-messageset.h \ + imap-parser.h \ imap-quote.h \ - imap-parser.h \ imap-util.h if INSTALL_HEADERS
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/lib-imap/imap-messageset.c Fri Mar 14 11:59:36 2008 +0200 @@ -0,0 +1,75 @@ +/* Copyright (c) 2002-2008 Dovecot authors, see the included COPYING file */ + +#include "lib.h" +#include "imap-messageset.h" + +static uint32_t get_next_number(const char **str) +{ + uint32_t num; + + num = 0; + while (**str != '\0') { + if (**str < '0' || **str > '9') + break; + + num = num*10 + (**str - '0'); + (*str)++; + } + + if (num == (uint32_t)-1) { + /* FIXME: ugly hack, we're using this number to mean the + last existing message. In reality UIDs should never get + this high, so we can quite safely just drop this one down. */ + num--; + } + + return num; +} + +int imap_messageset_parse(ARRAY_TYPE(seq_range) *dest, const char *messageset) +{ + uint32_t seq1, seq2; + + while (*messageset != '\0') { + if (*messageset == '*') { + /* last message */ + seq1 = (uint32_t)-1; + messageset++; + } else { + seq1 = get_next_number(&messageset); + if (seq1 == 0) + return -1; + } + + if (*messageset != ':') + seq2 = seq1; + else { + /* first:last range */ + messageset++; + + if (*messageset == '*') { + seq2 = (uint32_t)-1; + messageset++; + } else { + seq2 = get_next_number(&messageset); + if (seq2 == 0) + return -1; + } + } + + if (*messageset == ',') + messageset++; + else if (*messageset != '\0') + return -1; + + if (seq1 > seq2) { + /* swap, as specified by RFC-3501 */ + uint32_t temp = seq1; + seq1 = seq2; + seq2 = temp; + } + + seq_range_array_add_range(dest, seq1, seq2); + } + return 0; +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/lib-imap/imap-messageset.h Fri Mar 14 11:59:36 2008 +0200 @@ -0,0 +1,8 @@ +#ifndef IMAP_MESSAGESET_H +#define IMAP_MESSAGESET_H + +#include "seq-range-array.h" + +int imap_messageset_parse(ARRAY_TYPE(seq_range) *dest, const char *messageset); + +#endif
--- a/src/lib-storage/Makefile.am Fri Mar 14 09:44:34 2008 +0200 +++ b/src/lib-storage/Makefile.am Fri Mar 14 11:59:36 2008 +0200 @@ -14,6 +14,7 @@ mail-error.c \ mail-namespace.c \ mail-search.c \ + mail-search-build.c \ mail-storage.c \ mailbox-list.c \ mailbox-tree.c @@ -23,6 +24,7 @@ mail-error.h \ mail-namespace.h \ mail-search.h \ + mail-search-build.h \ mail-storage.h \ mail-storage-private.h \ mailbox-list.h \
--- a/src/lib-storage/index/index-search.c Fri Mar 14 09:44:34 2008 +0200 +++ b/src/lib-storage/index/index-search.c Fri Mar 14 11:59:36 2008 +0200 @@ -89,22 +89,11 @@ } } -static int seqset_contains(struct mail_search_seqset *set, uint32_t seq) -{ - while (set != NULL) { - if (seq >= set->seq1 && seq <= set->seq2) - return TRUE; - set = set->next; - } - - return FALSE; -} - static void search_seqset_arg(struct mail_search_arg *arg, struct index_search_context *ctx) { if (arg->type == SEARCH_SEQSET) { - if (seqset_contains(arg->value.seqset, ctx->mail_ctx.seq)) + if (seq_range_exists(&arg->value.seqset, ctx->mail_ctx.seq)) ARG_SET_RESULT(arg, 1); else ARG_SET_RESULT(arg, 0); @@ -146,7 +135,7 @@ switch (arg->type) { case SEARCH_UIDSET: - return seqset_contains(arg->value.seqset, rec->uid); + return seq_range_exists(&arg->value.seqset, rec->uid); case SEARCH_FLAGS: flags = rec->flags; if ((arg->value.flags & MAIL_RECENT) != 0 && @@ -605,106 +594,57 @@ } static bool search_msgset_fix_limits(const struct mail_index_header *hdr, - struct mail_search_seqset *set, bool not) + ARRAY_TYPE(seq_range) *seqset, bool not) { - if (set == NULL) - return FALSE; - - for (; set != NULL; set = set->next) { - if (set->seq1 > hdr->messages_count) { - if (set->seq1 != (uint32_t)-1 && - set->seq2 != (uint32_t)-1) { - if (not) - continue; + struct seq_range *range; + unsigned int count; - /* completely outside our range */ - return FALSE; - } - /* either seq1 or seq2 is '*', so the last message is - in range. */ - set->seq1 = hdr->messages_count; - } - if (set->seq2 > hdr->messages_count) - set->seq2 = hdr->messages_count; - - if (set->seq1 == 0 || set->seq2 == 0) { - /* this shouldn't happen. treat as nonexisting. */ - return FALSE; - } - } - return TRUE; -} - -static int mail_search_seqset_cmp(const void *p1, const void *p2) -{ - struct mail_search_seqset *const *set1 = p1, *const *set2 = p2; - - return (*set1)->seq1 < (*set2)->seq2 ? -1 : - ((*set1)->seq1 > (*set2)->seq2 ? 1 : 0); -} + i_assert(hdr->messages_count > 0); -static struct mail_search_seqset * -search_msgset_sort(struct mail_search_seqset *set) -{ - struct mail_search_seqset **sets, *cur; - unsigned int i, count; - - for (cur = set, count = 0; cur != NULL; cur = cur->next) - count++; - - /* @UNSAFE */ - sets = i_new(struct mail_search_seqset *, count); - for (i = 0, cur = set; i < count; i++, cur = cur->next) - sets[i] = cur; - qsort(sets, count, sizeof(*sets), mail_search_seqset_cmp); - for (i = 0; i < count-1; i++) - sets[i]->next = sets[i+1]; - sets[i]->next = NULL; - set = sets[0]; - i_free(sets); - return set; -} - -static void search_msgset_compress(struct mail_search_seqset *set, - struct mail_search_seqset **last_r) -{ - struct mail_search_seqset *cur; - - for (cur = set; cur->next != NULL; ) { - if (cur->seq2 + 1 >= cur->next->seq1) { - if (cur->seq2 < cur->next->seq2) - cur->seq2 = cur->next->seq2; - cur->next = cur->next->next; - } else { - cur = cur->next; + range = array_get_modifiable(seqset, &count); + if (count > 0) { + i_assert(range[0].seq1 != 0); + if (range[count-1].seq2 == (uint32_t)-1) { + /* "*" used, make sure the last message is in the range + (e.g. with count+1:* we still want to include it) */ + seq_range_array_add(seqset, 0, hdr->messages_count); } + /* remove all non-existing messages */ + seq_range_array_remove_range(seqset, hdr->messages_count + 1, + (uint32_t)-1); } - *last_r = cur; + if (!not) + return array_count(seqset) > 0; + else { + /* if all messages are in the range, it can't match */ + range = array_get_modifiable(seqset, &count); + return range[0].seq1 == 1 && + range[count-1].seq2 == hdr->messages_count; + } } static void search_msgset_fix(const struct mail_index_header *hdr, - struct mail_search_seqset **set_p, + ARRAY_TYPE(seq_range) *seqset, uint32_t *seq1_r, uint32_t *seq2_r, bool not) { - struct mail_search_seqset *set = *set_p; - struct mail_search_seqset *last; + const struct seq_range *range; + unsigned int count; uint32_t min_seq, max_seq; - if (!search_msgset_fix_limits(hdr, set, not)) { + if (!search_msgset_fix_limits(hdr, seqset, not)) { *seq1_r = (uint32_t)-1; *seq2_r = 0; return; } - set = *set_p = search_msgset_sort(set); - search_msgset_compress(set, &last); + range = array_get(seqset, &count); if (!not) { - min_seq = set->seq1; - max_seq = last->seq2; + min_seq = range[0].seq1; + max_seq = range[count-1].seq2; } else { - min_seq = set->seq1 > 1 ? 1 : set->seq2 + 1; - max_seq = last->seq2 < hdr->messages_count ? - hdr->messages_count : last->seq1 - 1; + min_seq = range[0].seq1 > 1 ? 1 : range[0].seq2 + 1; + max_seq = range[count-1].seq2 < hdr->messages_count ? + hdr->messages_count : range[count-1].seq1 - 1; if (min_seq > max_seq) { *seq1_r = (uint32_t)-1; *seq2_r = 0;
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/lib-storage/mail-search-build.c Fri Mar 14 11:59:36 2008 +0200 @@ -0,0 +1,565 @@ +/* Copyright (c) 2002-2008 Dovecot authors, see the included COPYING file */ + +#include "lib.h" +#include "imap-date.h" +#include "imap-parser.h" +#include "imap-messageset.h" +#include "mail-search.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 ((*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) \ + 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; + char *p; + + *next_sarg = sarg = search_arg_new(data->pool, type); + if (!arg_get_next(data, args, &value)) + return FALSE; + + sarg->value.size = strtoull(value, &p, 10); + if (*p != '\0') { + data->error = "Invalid search size parameter"; + return FALSE; + } + return TRUE; +} + +#define ARG_NEW_DATE(type) \ + arg_new_date(data, args, next_sarg, 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) +{ + 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; + } + 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 search_arg_build(struct search_build_data *data, + const struct imap_arg **args, + struct mail_search_arg **next_sarg) +{ + struct mail_search_arg **subargs, *sarg; + const struct imap_arg *arg; + const char *str; + + if ((*args)->type == IMAP_ARG_EOL) { + data->error = "Missing argument"; + return FALSE; + } + + 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 (listargs->type == IMAP_ARG_EOL) { + data->error = "Empty list not allowed"; + return FALSE; + } + + *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; + } + + *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); + + switch (*str) { + case 'A': + if (strcmp(str, "ANSWERED") == 0) + return ARG_NEW_FLAGS(MAIL_ANSWERED); + else if (strcmp(str, "ALL") == 0) + return ARG_NEW_SINGLE(SEARCH_ALL); + break; + case 'B': + if (strcmp(str, "BODY") == 0) { + /* <string> */ + if (IMAP_ARG_TYPE_IS_STRING((*args)->type) && + *IMAP_ARG_STR(*args) == '\0') { + *args += 1; + return ARG_NEW_SINGLE(SEARCH_ALL); + } + return ARG_NEW_STR(SEARCH_BODY); + } else if (strcmp(str, "BEFORE") == 0) { + /* <date> */ + return ARG_NEW_DATE(SEARCH_BEFORE); + } else if (strcmp(str, "BCC") == 0) { + /* <string> */ + return ARG_NEW_HEADER(SEARCH_HEADER_ADDRESS, str); + } + break; + case 'C': + if (strcmp(str, "CC") == 0) { + /* <string> */ + return ARG_NEW_HEADER(SEARCH_HEADER_ADDRESS, str); + } + break; + case 'D': + if (strcmp(str, "DELETED") == 0) + return ARG_NEW_FLAGS(MAIL_DELETED); + else if (strcmp(str, "DRAFT") == 0) + return ARG_NEW_FLAGS(MAIL_DRAFT); + break; + case 'F': + if (strcmp(str, "FLAGGED") == 0) + return ARG_NEW_FLAGS(MAIL_FLAGGED); + else if (strcmp(str, "FROM") == 0) { + /* <string> */ + return ARG_NEW_HEADER(SEARCH_HEADER_ADDRESS, str); + } + break; + case 'H': + if (strcmp(str, "HEADER") == 0) { + /* <field-name> <string> */ + const char *key; + + if ((*args)->type == IMAP_ARG_EOL) { + data->error = "Missing parameter for HEADER"; + return FALSE; + } + if ((*args)->type != IMAP_ARG_ATOM && + (*args)->type != IMAP_ARG_STRING) { + data->error = "Invalid parameter for HEADER"; + return FALSE; + } + + key = t_str_ucase(IMAP_ARG_STR(*args)); + *args += 1; + return ARG_NEW_HEADER(SEARCH_HEADER, key); + } + break; + case 'K': + if (strcmp(str, "KEYWORD") == 0) { + return ARG_NEW_STR(SEARCH_KEYWORDS); + } + break; + case 'L': + if (strcmp(str, "LARGER") == 0) { + /* <n> */ + return ARG_NEW_SIZE(SEARCH_LARGER); + } + break; + case 'N': + if (strcmp(str, "NOT") == 0) { + if (!search_arg_build(data, args, next_sarg)) + return FALSE; + (*next_sarg)->not = !(*next_sarg)->not; + return TRUE; + } else if (strcmp(str, "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(str, "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 ((*args)->type == IMAP_ARG_EOL) + break; + + if ((*args)->type != IMAP_ARG_ATOM || + strcasecmp(IMAP_ARG_STR_NONULL(*args), + "OR") != 0) + break; + + *args += 1; + } + + if (!search_arg_build(data, args, subargs)) + return FALSE; + return TRUE; + } if (strcmp(str, "ON") == 0) { + /* <date> */ + return ARG_NEW_DATE(SEARCH_ON); + } if (strcmp(str, "OLD") == 0) { + /* OLD == NOT RECENT */ + if (!ARG_NEW_FLAGS(MAIL_RECENT)) + return FALSE; + + (*next_sarg)->not = TRUE; + return TRUE; + } + break; + case 'R': + if (strcmp(str, "RECENT") == 0) + return ARG_NEW_FLAGS(MAIL_RECENT); + break; + case 'S': + if (strcmp(str, "SEEN") == 0) + return ARG_NEW_FLAGS(MAIL_SEEN); + else if (strcmp(str, "SUBJECT") == 0) { + /* <string> */ + return ARG_NEW_HEADER(SEARCH_HEADER_COMPRESS_LWSP, str); + } else if (strcmp(str, "SENTBEFORE") == 0) { + /* <date> */ + return ARG_NEW_DATE(SEARCH_SENTBEFORE); + } else if (strcmp(str, "SENTON") == 0) { + /* <date> */ + return ARG_NEW_DATE(SEARCH_SENTON); + } else if (strcmp(str, "SENTSINCE") == 0) { + /* <date> */ + return ARG_NEW_DATE(SEARCH_SENTSINCE); + } else if (strcmp(str, "SINCE") == 0) { + /* <date> */ + return ARG_NEW_DATE(SEARCH_SINCE); + } else if (strcmp(str, "SMALLER") == 0) { + /* <n> */ + return ARG_NEW_SIZE(SEARCH_SMALLER); + } + break; + case 'T': + if (strcmp(str, "TEXT") == 0) { + /* <string> */ + if (IMAP_ARG_TYPE_IS_STRING((*args)->type) && + *IMAP_ARG_STR(*args) == '\0') { + *args += 1; + return ARG_NEW_SINGLE(SEARCH_ALL); + } + return ARG_NEW_STR(SEARCH_TEXT); + } else if (strcmp(str, "TO") == 0) { + /* <string> */ + return ARG_NEW_HEADER(SEARCH_HEADER_ADDRESS, str); + } + break; + case 'U': + if (strcmp(str, "UID") == 0) { + /* <message set> */ + if (!ARG_NEW_STR(SEARCH_SEQSET)) + return FALSE; + + sarg = *next_sarg; + p_array_init(&sarg->value.seqset, data->pool, 16); + if (imap_messageset_parse(&sarg->value.seqset, + sarg->value.str) < 0) { + data->error = "Invalid UID messageset"; + return FALSE; + } + return TRUE; + } else if (strcmp(str, "UNANSWERED") == 0) { + if (!ARG_NEW_FLAGS(MAIL_ANSWERED)) + return FALSE; + (*next_sarg)->not = TRUE; + return TRUE; + } else if (strcmp(str, "UNDELETED") == 0) { + if (!ARG_NEW_FLAGS(MAIL_DELETED)) + return FALSE; + (*next_sarg)->not = TRUE; + return TRUE; + } else if (strcmp(str, "UNDRAFT") == 0) { + if (!ARG_NEW_FLAGS(MAIL_DRAFT)) + return FALSE; + (*next_sarg)->not = TRUE; + return TRUE; + } else if (strcmp(str, "UNFLAGGED") == 0) { + if (!ARG_NEW_FLAGS(MAIL_FLAGGED)) + return FALSE; + (*next_sarg)->not = TRUE; + return TRUE; + } else if (strcmp(str, "UNKEYWORD") == 0) { + if (!ARG_NEW_STR(SEARCH_KEYWORDS)) + return FALSE; + (*next_sarg)->not = TRUE; + return TRUE; + } else if (strcmp(str, "UNSEEN") == 0) { + if (!ARG_NEW_FLAGS(MAIL_SEEN)) + return FALSE; + (*next_sarg)->not = TRUE; + return TRUE; + } + break; + case 'X': + if (strcmp(str, "X-BODY-FAST") == 0) { + /* <string> */ + if (IMAP_ARG_TYPE_IS_STRING((*args)->type) && + *IMAP_ARG_STR(*args) == '\0') { + *args += 1; + return ARG_NEW_SINGLE(SEARCH_ALL); + } + return ARG_NEW_STR(SEARCH_BODY_FAST); + } else if (strcmp(str, "X-TEXT-FAST") == 0) { + /* <string> */ + if (IMAP_ARG_TYPE_IS_STRING((*args)->type) && + *IMAP_ARG_STR(*args) == '\0') { + *args += 1; + return ARG_NEW_SINGLE(SEARCH_ALL); + } + return ARG_NEW_STR(SEARCH_TEXT_FAST); + } + break; + default: + if (*str == '*' || (*str >= '0' && *str <= '9')) { + /* <message-set> */ + if (!ARG_NEW_SINGLE(SEARCH_SEQSET)) + return FALSE; + + p_array_init(&(*next_sarg)->value.seqset, + data->pool, 16); + if (imap_messageset_parse(&(*next_sarg)->value.seqset, + str) < 0) { + data->error = "Invalid messageset"; + return FALSE; + } + return TRUE; + } + break; + } + + data->error = t_strconcat("Unknown argument ", str, NULL); + return FALSE; +} + +struct mail_search_arg * +mail_search_build_from_imap_args(pool_t pool, const struct imap_arg *args, + const char **error_r) +{ + struct search_build_data data; + struct mail_search_arg *first_sarg, **sargs; + + data.pool = pool; + data.error = NULL; + + first_sarg = NULL; sargs = &first_sarg; + while (args->type != IMAP_ARG_EOL) { + if (!search_arg_build(&data, &args, sargs)) { + first_sarg = NULL; + break; + } + sargs = &(*sargs)->next; + } + + *error_r = data.error; + return first_sarg; +} + +static void +mailbox_uidseq_change(struct mail_search_arg *arg, struct mailbox *box) +{ + struct seq_range *uids; + unsigned int i, count; + uint32_t seq1, seq2; + + arg->type = SEARCH_SEQSET; + + /* make a copy of the UIDs */ + count = array_count(&arg->value.seqset); + uids = t_new(struct seq_range, count); + memcpy(uids, array_idx(&arg->value.seqset, 0), sizeof(*uids) * count); + + /* put them back to the range as sequences */ + array_clear(&arg->value.seqset); + for (i = 0; i < count; i++) { + mailbox_get_uids(box, uids[i].seq1, uids[i].seq2, &seq1, &seq2); + if (seq1 != 0) { + seq_range_array_add_range(&arg->value.seqset, + seq1, seq2); + } + if (uids[i].seq2 == (uint32_t)-1) { + /* make sure the last message is in the range */ + mailbox_get_uids(box, 1, (uint32_t)-1, &seq1, &seq2); + seq_range_array_add(&arg->value.seqset, 0, seq2); + } + } +} + +void mail_search_args_init(struct mail_search_arg *args, + struct mailbox *box, bool change_uidsets) +{ + const char *keywords[2]; + + for (; args != NULL; args = args->next) { + switch (args->type) { + case SEARCH_UIDSET: + if (change_uidsets) T_BEGIN { + mailbox_uidseq_change(args, box); + } T_END; + break; + case SEARCH_KEYWORDS: + keywords[0] = args->value.str; + keywords[1] = NULL; + + i_assert(args->value.keywords == NULL); + args->value.keywords = + mailbox_keywords_create_valid(box, keywords); + break; + case SEARCH_SUB: + case SEARCH_OR: + mail_search_args_init(args->value.subargs, box, + change_uidsets); + break; + default: + break; + } + } +} + +void mail_search_args_deinit(struct mail_search_arg *args, + struct mailbox *box) +{ + for (; args != NULL; args = args->next) { + switch (args->type) { + case SEARCH_KEYWORDS: + if (args->value.keywords == NULL) + break; + mailbox_keywords_free(box, &args->value.keywords); + break; + case SEARCH_SUB: + case SEARCH_OR: + mail_search_args_deinit(args->value.subargs, box); + break; + default: + break; + } + } +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/lib-storage/mail-search-build.h Fri Mar 14 11:59:36 2008 +0200 @@ -0,0 +1,19 @@ +#ifndef MAIL_SEARCH_BUILD_H +#define MAIL_SEARCH_BUILD_H + +struct imap_arg; +struct mailbox; + +struct mail_search_arg * +mail_search_build_from_imap_args(pool_t pool, const struct imap_arg *args, + const char **error_r); + +/* Allocate keywords for search arguments. If change_uidsets is TRUE, + change uidsets to seqsets. */ +void mail_search_args_init(struct mail_search_arg *args, + struct mailbox *box, bool change_uidsets); +/* Free keywords. The args can initialized afterwards again if needed. */ +void mail_search_args_deinit(struct mail_search_arg *args, + struct mailbox *box); + +#endif
--- a/src/lib-storage/mail-search.h Fri Mar 14 09:44:34 2008 +0200 +++ b/src/lib-storage/mail-search.h Fri Mar 14 11:59:36 2008 +0200 @@ -1,6 +1,7 @@ #ifndef MAIL_SEARCH_H #define MAIL_SEARCH_H +#include "seq-range-array.h" #include "mail-types.h" enum mail_search_arg_type { @@ -40,18 +41,13 @@ SEARCH_TEXT_FAST }; -struct mail_search_seqset { - uint32_t seq1, seq2; - struct mail_search_seqset *next; -}; - struct mail_search_arg { struct mail_search_arg *next; enum mail_search_arg_type type; struct { struct mail_search_arg *subargs; - struct mail_search_seqset *seqset; + ARRAY_TYPE(seq_range) seqset; const char *str; time_t time; uoff_t size;
--- a/src/plugins/fts/fts-storage.c Fri Mar 14 09:44:34 2008 +0200 +++ b/src/plugins/fts/fts-storage.c Fri Mar 14 11:59:36 2008 +0200 @@ -25,7 +25,6 @@ struct fts_storage_build_context { struct mail_search_context *search_ctx; - struct mail_search_seqset seqset; struct mail_search_arg search_arg; struct mail *mail; struct fts_backend_build_context *build; @@ -171,16 +170,13 @@ struct fts_backend *backend = fctx->build_backend; struct fts_storage_build_context *ctx; struct fts_backend_build_context *build; - struct mail_search_seqset seqset; - uint32_t last_uid, last_uid_locked; + uint32_t last_uid, last_uid_locked, seq1, seq2; if (fts_backend_get_last_uid(backend, &last_uid) < 0) return -1; - memset(&seqset, 0, sizeof(seqset)); - mailbox_get_uids(t->box, last_uid+1, (uint32_t)-1, - &seqset.seq1, &seqset.seq2); - if (seqset.seq1 == 0) { + mailbox_get_uids(t->box, last_uid+1, (uint32_t)-1, &seq1, &seq2); + if (seq1 == 0) { /* no new messages */ return 0; } @@ -197,8 +193,8 @@ last_uid = last_uid_locked; mailbox_get_uids(t->box, last_uid+1, (uint32_t)-1, - &seqset.seq1, &seqset.seq2); - if (seqset.seq1 == 0) { + &seq1, &seq2); + if (seq1 == 0) { /* no new messages */ (void)fts_backend_build_deinit(&build); return 0; @@ -207,9 +203,9 @@ ctx = i_new(struct fts_storage_build_context, 1); ctx->build = build; - ctx->seqset = seqset; ctx->search_arg.type = SEARCH_SEQSET; - ctx->search_arg.value.seqset = &ctx->seqset; + i_array_init(&ctx->search_arg.value.seqset, 1); + seq_range_array_add_range(&ctx->search_arg.value.seqset, seq1, seq2); ctx->headers = str_new(default_pool, 512); ctx->mail = mail_alloc(t, 0, NULL); @@ -243,6 +239,7 @@ } str_free(&ctx->headers); + array_free(&ctx->search_arg.value.seqset); i_free(ctx); return ret; } @@ -250,6 +247,7 @@ static void fts_build_notify(struct fts_storage_build_context *ctx) { struct mailbox *box = ctx->mail->transaction->box; + const struct seq_range *range; float percentage; unsigned int msecs, secs; @@ -258,8 +256,9 @@ already spent some time indexing the mailbox */ ctx->search_start_time = ioloop_timeval; } else if (box->storage->callbacks->notify_ok != NULL) { - percentage = (ctx->mail->seq - ctx->seqset.seq1) * 100.0 / - (ctx->seqset.seq2 - ctx->seqset.seq1); + range = array_idx(&ctx->search_arg.value.seqset, 0); + percentage = (ctx->mail->seq - range->seq1) * 100.0 / + (range->seq2 - range->seq1); msecs = (ioloop_timeval.tv_sec - ctx->search_start_time.tv_sec) * 1000 + (ioloop_timeval.tv_usec -
--- a/src/pop3/commands.c Fri Mar 14 09:44:34 2008 +0200 +++ b/src/pop3/commands.c Fri Mar 14 11:59:36 2008 +0200 @@ -1,6 +1,7 @@ /* Copyright (c) 2002-2008 Dovecot authors, see the included COPYING file */ #include "common.h" +#include "array.h" #include "istream.h" #include "ostream.h" #include "str.h" @@ -185,7 +186,6 @@ static bool expunge_mails(struct client *client) { struct mail_search_arg search_arg; - struct mail_search_seqset seqset; struct mail_search_context *ctx; struct mail *mail; uint32_t idx; @@ -198,12 +198,11 @@ return TRUE; } - memset(&seqset, 0, sizeof(seqset)); memset(&search_arg, 0, sizeof(search_arg)); - seqset.seq1 = 1; - seqset.seq2 = client->messages_count; search_arg.type = SEARCH_SEQSET; - search_arg.value.seqset = &seqset; + t_array_init(&search_arg.value.seqset, 1); + seq_range_array_add_range(&search_arg.value.seqset, + 1, client->messages_count); ctx = mailbox_search_init(client->trans, NULL, &search_arg, NULL); mail = mail_alloc(client->trans, 0, NULL); @@ -255,7 +254,6 @@ uoff_t body_lines; struct mail_search_arg search_arg; - struct mail_search_seqset seqset; unsigned char last; bool cr_skipped, in_body; @@ -264,6 +262,7 @@ static void fetch_deinit(struct fetch_context *ctx) { (void)mailbox_search_deinit(&ctx->search_ctx); + array_free(&ctx->search_arg.value.seqset); mail_free(&ctx->mail); i_free(ctx); } @@ -371,9 +370,9 @@ ctx = i_new(struct fetch_context, 1); - ctx->seqset.seq1 = ctx->seqset.seq2 = msgnum+1; ctx->search_arg.type = SEARCH_SEQSET; - ctx->search_arg.value.seqset = &ctx->seqset; + i_array_init(&ctx->search_arg.value.seqset, 1); + seq_range_array_add(&ctx->search_arg.value.seqset, 0, msgnum+1); ctx->search_ctx = mailbox_search_init(client->trans, NULL, &ctx->search_arg, NULL); @@ -432,7 +431,6 @@ struct mail_search_context *search_ctx; struct mail *mail; struct mail_search_arg search_arg; - struct mail_search_seqset seqset; client->last_seen = 0; @@ -445,12 +443,11 @@ if (enable_last_command) { /* remove all \Seen flags (as specified by RFC 1460) */ - memset(&seqset, 0, sizeof(seqset)); memset(&search_arg, 0, sizeof(search_arg)); - seqset.seq1 = 1; - seqset.seq2 = client->messages_count; search_arg.type = SEARCH_SEQSET; - search_arg.value.seqset = &seqset; + t_array_init(&search_arg.value.seqset, 1); + seq_range_array_add_range(&search_arg.value.seqset, + 1, client->messages_count); search_ctx = mailbox_search_init(client->trans, NULL, &search_arg, NULL); @@ -503,7 +500,6 @@ unsigned int message; struct mail_search_arg search_arg; - struct mail_search_seqset seqset; }; static bool list_uids_iter(struct client *client, struct cmd_uidl_context *ctx) @@ -586,6 +582,7 @@ if (ctx->message == 0) client_send_line(client, "."); + array_free(&ctx->search_arg.value.seqset); i_free(ctx); return found; } @@ -605,15 +602,15 @@ ctx = i_new(struct cmd_uidl_context, 1); + ctx->search_arg.type = SEARCH_SEQSET; + i_array_init(&ctx->search_arg.value.seqset, 1); if (message == 0) { - ctx->seqset.seq1 = 1; - ctx->seqset.seq2 = client->messages_count; + seq_range_array_add_range(&ctx->search_arg.value.seqset, + 1, client->messages_count); } else { ctx->message = message; - ctx->seqset.seq1 = ctx->seqset.seq2 = message; + seq_range_array_add(&ctx->search_arg.value.seqset, 0, message); } - ctx->search_arg.type = SEARCH_SEQSET; - ctx->search_arg.value.seqset = &ctx->seqset; wanted_fields = 0; if ((uidl_keymask & UIDL_MD5) != 0)