Mercurial > dovecot > original-hg > dovecot-1.2
changeset 7639:03146f02403f HEAD
Implemented SEARCHRES extension.
author | Timo Sirainen <tss@iki.fi> |
---|---|
date | Sun, 16 Mar 2008 11:05:53 +0200 |
parents | 7f57fd10c9bd |
children | c78e9204f3f2 |
files | configure.in src/imap/client.c src/imap/client.h src/imap/cmd-copy.c src/imap/cmd-expunge.c src/imap/cmd-fetch.c src/imap/cmd-search.c src/imap/cmd-sort.c src/imap/cmd-store.c src/imap/cmd-thread.c src/imap/imap-fetch.c src/imap/imap-search.c src/imap/imap-search.h src/lib-storage/mail-search-build.c src/lib-storage/mail-search-build.h |
diffstat | 15 files changed, 335 insertions(+), 169 deletions(-) [+] |
line wrap: on
line diff
--- a/configure.in Sun Mar 16 11:04:58 2008 +0200 +++ b/configure.in Sun Mar 16 11:05:53 2008 +0200 @@ -2119,7 +2119,7 @@ dnl ** capabilities dnl ** -capability="IMAP4rev1 SASL-IR SORT THREAD=REFERENCES MULTIAPPEND UNSELECT LITERAL+ IDLE CHILDREN NAMESPACE LOGIN-REFERRALS UIDPLUS LIST-EXTENDED I18NLEVEL=1 ENABLE CONDSTORE QRESYNC ESEARCH" +capability="IMAP4rev1 SASL-IR SORT THREAD=REFERENCES MULTIAPPEND UNSELECT LITERAL+ IDLE CHILDREN NAMESPACE LOGIN-REFERRALS UIDPLUS LIST-EXTENDED I18NLEVEL=1 ENABLE CONDSTORE QRESYNC ESEARCH SEARCHRES" AC_DEFINE_UNQUOTED(CAPABILITY_STRING, "$capability", IMAP capabilities) CFLAGS="$CFLAGS $EXTRA_CFLAGS"
--- a/src/imap/client.c Sun Mar 16 11:04:58 2008 +0200 +++ b/src/imap/client.c Sun Mar 16 11:05:53 2008 +0200 @@ -155,6 +155,8 @@ i_error("close(client out) failed: %m"); } + if (array_is_created(&client->search_saved_uidset)) + array_free(&client->search_saved_uidset); pool_unref(&client->command_pool); i_free(client); @@ -780,6 +782,27 @@ } } +bool client_handle_search_save_ambiguity(struct client_command_context *cmd) +{ + struct client_command_context *old_cmd = cmd->next; + + /* search only commands that were added before this command + (commands are prepended to the queue, so they're after ourself) */ + for (; old_cmd != NULL; old_cmd = old_cmd->next) { + if (old_cmd->search_save_result) + break; + } + if (old_cmd == NULL) + return FALSE; + + /* ambiguity, wait until it's over */ + i_assert(cmd->state == CLIENT_COMMAND_STATE_WAIT_INPUT); + cmd->client->input_lock = cmd; + cmd->state = CLIENT_COMMAND_STATE_WAIT_UNAMBIGUITY; + io_remove(&cmd->client->io); + return TRUE; +} + void client_enable(struct client *client, enum mailbox_feature features) { struct mailbox_status status;
--- a/src/imap/client.h Sun Mar 16 11:04:58 2008 +0200 +++ b/src/imap/client.h Sun Mar 16 11:05:53 2008 +0200 @@ -53,6 +53,7 @@ unsigned int uid:1; /* used UID command */ unsigned int cancel:1; /* command is wanted to be cancelled */ unsigned int param_error:1; + unsigned int search_save_result:1; /* search result is being updated */ unsigned int temp_executed:1; /* temporary execution state tracking */ }; @@ -78,9 +79,13 @@ struct imap_parser *free_parser; /* command_pool is cleared when the command queue gets empty */ pool_t command_pool; + /* New commands are always prepended to the queue */ struct client_command_context *command_queue; unsigned int command_queue_size; + /* SEARCHRES extension: Last saved SEARCH result */ + ARRAY_TYPE(seq_range) search_saved_uidset; + /* client input/output is locked by this command */ struct client_command_context *input_lock; struct client_command_context *output_lock; @@ -127,6 +132,10 @@ bool client_read_string_args(struct client_command_context *cmd, unsigned int count, ...); +/* SEARCHRES extension: Call if $ is being used/updated, returns TRUE if we + have to wait for an existing SEARCH SAVE to finish. */ +bool client_handle_search_save_ambiguity(struct client_command_context *cmd); + void client_enable(struct client *client, enum mailbox_feature features); void clients_init(void);
--- a/src/imap/cmd-copy.c Sun Mar 16 11:04:58 2008 +0200 +++ b/src/imap/cmd-copy.c Sun Mar 16 11:05:53 2008 +0200 @@ -109,9 +109,9 @@ if (!client_verify_mailbox_name(cmd, mailbox, TRUE, FALSE)) return TRUE; - search_arg = imap_search_get_seqset(cmd, messageset, cmd->uid); - if (search_arg == NULL) - return TRUE; + ret = imap_search_get_seqset(cmd, messageset, cmd->uid, &search_arg); + if (ret <= 0) + return ret < 0; storage = client_find_storage(cmd, &mailbox); if (storage == NULL)
--- a/src/imap/cmd-expunge.c Sun Mar 16 11:04:58 2008 +0200 +++ b/src/imap/cmd-expunge.c Sun Mar 16 11:05:53 2008 +0200 @@ -62,6 +62,7 @@ const struct imap_arg *args; struct mail_search_arg *search_arg; const char *uidset; + int ret; if (!client_read_args(cmd, 1, 0, &args)) return FALSE; @@ -75,9 +76,10 @@ return TRUE; } - search_arg = imap_search_get_seqset(cmd, uidset, TRUE); - return search_arg == NULL ? TRUE : - cmd_expunge_finish(cmd, search_arg); + ret = imap_search_get_seqset(cmd, uidset, TRUE, &search_arg); + if (ret <= 0) + return ret < 0; + return cmd_expunge_finish(cmd, search_arg); } bool cmd_expunge(struct client_command_context *cmd)
--- a/src/imap/cmd-fetch.c Sun Mar 16 11:04:58 2008 +0200 +++ b/src/imap/cmd-fetch.c Sun Mar 16 11:05:53 2008 +0200 @@ -172,10 +172,12 @@ bool cmd_fetch(struct client_command_context *cmd) { + struct client *client = cmd->client; struct imap_fetch_context *ctx; const struct imap_arg *args; struct mail_search_arg *search_arg; const char *messageset; + int ret; if (!client_read_args(cmd, 0, 0, &args)) return FALSE; @@ -183,7 +185,7 @@ if (!client_verify_open_mailbox(cmd)) return TRUE; - /* <messageset> <field(s)> [(CHANGEDSINCE <modseq>)] */ + /* <messageset> <field(s)> [(modifiers)] */ messageset = imap_arg_string(&args[0]); if (messageset == NULL || (args[1].type != IMAP_ARG_LIST && args[1].type != IMAP_ARG_ATOM) || @@ -192,11 +194,13 @@ return TRUE; } - search_arg = imap_search_get_anyset(cmd, messageset, cmd->uid); - if (search_arg == NULL) - return TRUE; + /* UID FETCH VANISHED needs the uidset, so convert it to + sequence set later */ + ret = imap_search_get_anyset(cmd, messageset, cmd->uid, &search_arg); + if (ret <= 0) + return ret < 0; - ctx = imap_fetch_init(cmd, cmd->client->mailbox); + ctx = imap_fetch_init(cmd, client->mailbox); if (ctx == NULL) return TRUE; ctx->search_args = search_arg;
--- a/src/imap/cmd-search.c Sun Mar 16 11:04:58 2008 +0200 +++ b/src/imap/cmd-search.c Sun Mar 16 11:05:53 2008 +0200 @@ -17,8 +17,10 @@ SEARCH_RETURN_MAX = 0x04, SEARCH_RETURN_ALL = 0x08, SEARCH_RETURN_COUNT = 0x10, - SEARCH_RETURN_MODSEQ = 0x20 -#define SEARCH_RETURN_EXTRAS (SEARCH_RETURN_ESEARCH | SEARCH_RETURN_MODSEQ) + SEARCH_RETURN_MODSEQ = 0x20, + SEARCH_RETURN_SAVE = 0x40 +#define SEARCH_RETURN_EXTRAS \ + (SEARCH_RETURN_ESEARCH | SEARCH_RETURN_MODSEQ | SEARCH_RETURN_SAVE) }; struct imap_search_context { @@ -38,36 +40,42 @@ struct timeval start_time; }; -static bool search_parse_return_options(struct imap_search_context *ctx, - const struct imap_arg *args) +static bool +search_parse_return_options(struct client_command_context *cmd, + const struct imap_arg *args, + enum search_return_options *return_options_r) { const char *name; + *return_options_r = 0; + while (args->type != IMAP_ARG_EOL) { if (args->type != IMAP_ARG_ATOM) { - client_send_command_error(ctx->cmd, + client_send_command_error(cmd, "SEARCH return options contain non-atoms."); return FALSE; } name = t_str_ucase(IMAP_ARG_STR(args)); args++; if (strcmp(name, "MIN") == 0) - ctx->return_options |= SEARCH_RETURN_MIN; + *return_options_r |= SEARCH_RETURN_MIN; else if (strcmp(name, "MAX") == 0) - ctx->return_options |= SEARCH_RETURN_MAX; + *return_options_r |= SEARCH_RETURN_MAX; else if (strcmp(name, "ALL") == 0) - ctx->return_options |= SEARCH_RETURN_ALL; + *return_options_r |= SEARCH_RETURN_ALL; else if (strcmp(name, "COUNT") == 0) - ctx->return_options |= SEARCH_RETURN_COUNT; + *return_options_r |= SEARCH_RETURN_COUNT; + else if (strcmp(name, "SAVE") == 0) + *return_options_r |= SEARCH_RETURN_SAVE; else { - client_send_command_error(ctx->cmd, + client_send_command_error(cmd, "Unknown SEARCH return option"); return FALSE; } } - if (ctx->return_options == 0) - ctx->return_options = SEARCH_RETURN_ALL; - ctx->return_options |= SEARCH_RETURN_ESEARCH; + if (*return_options_r == 0) + *return_options_r = SEARCH_RETURN_ALL; + *return_options_r |= SEARCH_RETURN_ESEARCH; return TRUE; } @@ -139,6 +147,7 @@ static void imap_search_send_result(struct imap_search_context *ctx) { + struct client *client = ctx->cmd->client; const struct seq_range *range; unsigned int count; string_t *str; @@ -148,6 +157,13 @@ return; } + if (ctx->return_options == + (SEARCH_RETURN_ESEARCH | SEARCH_RETURN_SAVE)) { + /* we only wanted to save the result, don't return + ESEARCH result. */ + return; + } + str = str_new(default_pool, 1024); str_append(str, "* ESEARCH (TAG "); imap_quote_append_string(str, ctx->cmd->tag, FALSE); @@ -172,8 +188,7 @@ (unsigned long long)ctx->highest_seen_modseq); } str_append(str, "\r\n"); - o_stream_send(ctx->cmd->client->output, - str_data(str), str_len(str)); + o_stream_send(client->output, str_data(str), str_len(str)); } static int imap_search_deinit(struct imap_search_context *ctx) @@ -183,8 +198,14 @@ mail_free(&ctx->mail); if (mailbox_search_deinit(&ctx->search_ctx) < 0) ret = -1; - else if (!ctx->cmd->cancel) + + if (ret == 0 && !ctx->cmd->cancel) imap_search_send_result(ctx); + else { + /* search failed */ + if ((ctx->return_options & SEARCH_RETURN_SAVE) != 0) + array_clear(&ctx->cmd->client->search_saved_uidset); + } (void)mailbox_transaction_commit(&ctx->trans); @@ -197,6 +218,21 @@ return ret; } +static void search_update_mail(struct imap_search_context *ctx) +{ + uint64_t modseq; + + if ((ctx->return_options & SEARCH_RETURN_MODSEQ) != 0) { + modseq = mail_get_modseq(ctx->mail); + if (ctx->highest_seen_modseq < modseq) + ctx->highest_seen_modseq = modseq; + } + if ((ctx->return_options & SEARCH_RETURN_SAVE) != 0) { + seq_range_array_add(&ctx->cmd->client->search_saved_uidset, + 0, ctx->mail->uid); + } +} + static bool cmd_search_more(struct client_command_context *cmd) { struct imap_search_context *ctx = cmd->context; @@ -204,9 +240,8 @@ struct timeval end_time; const struct seq_range *range; unsigned int count; - uint64_t modseq; uint32_t id, id_min, id_max; - bool tryagain; + bool tryagain, minmax; if (cmd->cancel) { (void)imap_search_deinit(ctx); @@ -222,32 +257,29 @@ id_max = range[count-1].seq2; } + minmax = (opts & (SEARCH_RETURN_MIN | SEARCH_RETURN_MAX)) != 0 && + (opts & ~(SEARCH_RETURN_EXTRAS | + SEARCH_RETURN_MIN | SEARCH_RETURN_MAX)) == 0; while (mailbox_search_next_nonblock(ctx->search_ctx, ctx->mail, &tryagain) > 0) { id = cmd->uid ? ctx->mail->uid : ctx->mail->seq; ctx->result_count++; - if ((opts & ~(SEARCH_RETURN_EXTRAS | - SEARCH_RETURN_MIN | SEARCH_RETURN_MAX)) == 0) { + if (minmax) { /* we only care about min/max */ if (id < id_min && (opts & SEARCH_RETURN_MIN) != 0) id_min = id; if (id > id_max && (opts & SEARCH_RETURN_MAX) != 0) id_max = id; if (id == id_min || id == id_max) { - /* modseq is looked up when we know the - actual min/max values */ + /* return option updates are delayed until + we know the actual min/max values */ seq_range_array_add(&ctx->result, 0, id); } continue; } - if ((ctx->return_options & SEARCH_RETURN_MODSEQ) != 0) { - modseq = mail_get_modseq(ctx->mail); - if (ctx->highest_seen_modseq < modseq) - ctx->highest_seen_modseq = modseq; - } - + search_update_mail(ctx); if ((opts & ~(SEARCH_RETURN_EXTRAS | SEARCH_RETURN_COUNT)) == 0) { /* we only want to count (and get modseqs) */ @@ -258,10 +290,9 @@ if (tryagain) return FALSE; - if (ctx->highest_seen_modseq == 0 && - (ctx->return_options & SEARCH_RETURN_MODSEQ) != 0 && - array_count(&ctx->result) > 0) { - /* get highest modseq only from MIN/MAX messages */ + if (minmax && array_count(&ctx->result) > 0 && + (opts & (SEARCH_RETURN_MODSEQ | SEARCH_RETURN_SAVE)) != 0) { + /* handle MIN/MAX modseq/save updates */ if ((opts & SEARCH_RETURN_MIN) != 0) { i_assert(id_min != (uint32_t)-1); if (cmd->uid) { @@ -270,7 +301,7 @@ } else { mail_set_seq(ctx->mail, id_min); } - ctx->highest_seen_modseq = mail_get_modseq(ctx->mail); + search_update_mail(ctx); } if ((opts & SEARCH_RETURN_MAX) != 0) { i_assert(id_max != 0); @@ -280,9 +311,7 @@ } else { mail_set_seq(ctx->mail, id_max); } - modseq = mail_get_modseq(ctx->mail); - if (ctx->highest_seen_modseq < modseq) - ctx->highest_seen_modseq = modseq; + search_update_mail(ctx); } } @@ -297,7 +326,7 @@ end_time.tv_sec -= ctx->start_time.tv_sec; end_time.tv_usec -= ctx->start_time.tv_usec; if (end_time.tv_usec < 0) { - end_time.tv_sec++; + end_time.tv_sec--; end_time.tv_usec += 1000000; } @@ -327,11 +356,13 @@ bool cmd_search(struct client_command_context *cmd) { + struct client *client = cmd->client; struct imap_search_context *ctx; struct mail_search_arg *sargs; const struct imap_arg *args; - int args_count; - const char *error, *charset; + enum search_return_options return_options; + int ret, args_count; + const char *charset; args_count = imap_parser_read_args(cmd->parser, 0, 0, &args); if (args_count < 1) { @@ -342,23 +373,42 @@ "Missing SEARCH arguments."); return TRUE; } - cmd->client->input_lock = NULL; + client->input_lock = NULL; if (!client_verify_open_mailbox(cmd)) return TRUE; - ctx = p_new(cmd->pool, struct imap_search_context, 1); - ctx->cmd = cmd; - if (args->type == IMAP_ARG_ATOM && args[1].type == IMAP_ARG_LIST && strcasecmp(IMAP_ARG_STR_NONULL(args), "RETURN") == 0) { args++; - if (!search_parse_return_options(ctx, IMAP_ARG_LIST_ARGS(args))) + if (!search_parse_return_options(cmd, IMAP_ARG_LIST_ARGS(args), + &return_options)) return TRUE; args++; + + if ((return_options & SEARCH_RETURN_SAVE) != 0) { + /* wait if there is another SEARCH SAVE command + running. */ + cmd->search_save_result = TRUE; + if (client_handle_search_save_ambiguity(cmd)) + return FALSE; + } } else { - ctx->return_options = SEARCH_RETURN_ALL; + return_options = SEARCH_RETURN_ALL; } + + if ((return_options & SEARCH_RETURN_SAVE) != 0) { + /* make sure the search result gets cleared if SEARCH fails */ + if (array_is_created(&client->search_saved_uidset)) + array_clear(&client->search_saved_uidset); + else + i_array_init(&client->search_saved_uidset, 128); + } + + ctx = p_new(cmd->pool, struct imap_search_context, 1); + ctx->cmd = cmd; + ctx->return_options = return_options; + if (args->type == IMAP_ARG_ATOM && strcasecmp(IMAP_ARG_STR_NONULL(args), "CHARSET") == 0) { /* CHARSET specified */ @@ -376,13 +426,9 @@ charset = "UTF-8"; } - sargs = imap_search_args_build(cmd->pool, cmd->client->mailbox, - args, &error); - if (sargs == NULL) { - /* error in search arguments */ - client_send_tagline(cmd, t_strconcat("BAD ", error, NULL)); - return TRUE; - } + ret = imap_search_args_build(cmd, args, &sargs); + if (ret <= 0) + return ret < 0; imap_search_init(ctx, charset, sargs); cmd->func = cmd_search_more;
--- a/src/imap/cmd-sort.c Sun Mar 16 11:04:58 2008 +0200 +++ b/src/imap/cmd-sort.c Sun Mar 16 11:05:53 2008 +0200 @@ -89,8 +89,8 @@ enum mail_sort_type sorting[MAX_SORT_PROGRAM_SIZE]; const struct imap_arg *args; int args_count; - pool_t pool; - const char *error, *charset; + const char *charset; + int ret; args_count = imap_parser_read_args(cmd->parser, 0, 0, &args); if (args_count == -2) @@ -125,22 +125,17 @@ charset = IMAP_ARG_STR(args); args++; - pool = pool_alloconly_create("mail_search_args", 2048); + ret = imap_search_args_build(cmd, args, &sargs); + if (ret <= 0) + return ret < 0; - sargs = imap_search_args_build(pool, client->mailbox, args, &error); - if (sargs == NULL) { - /* error in search arguments */ - client_send_tagline(cmd, t_strconcat("NO ", error, NULL)); - } else if (imap_sort(cmd, charset, sargs, sorting) == 0) { - pool_unref(&pool); - return cmd_sync(cmd, MAILBOX_SYNC_FLAG_FAST | - (cmd->uid ? 0 : MAILBOX_SYNC_FLAG_NO_EXPUNGES), - 0, "OK Sort completed."); - } else { + if (imap_sort(cmd, charset, sargs, sorting) < 0) { client_send_storage_error(cmd, mailbox_get_storage(client->mailbox)); + return TRUE; } - pool_unref(&pool); - return TRUE; + return cmd_sync(cmd, MAILBOX_SYNC_FLAG_FAST | + (cmd->uid ? 0 : MAILBOX_SYNC_FLAG_NO_EXPUNGES), + 0, "OK Sort completed."); }
--- a/src/imap/cmd-store.c Sun Mar 16 11:04:58 2008 +0200 +++ b/src/imap/cmd-store.c Sun Mar 16 11:05:53 2008 +0200 @@ -11,7 +11,6 @@ struct imap_store_context { struct client_command_context *cmd; - const char *messageset; uint64_t max_modseq; enum mail_flags flags; @@ -80,8 +79,6 @@ const char *const *keywords_list = NULL; ctx->max_modseq = (uint64_t)-1; - ctx->messageset = imap_arg_string(args++); - if (args->type == IMAP_ARG_LIST) { if (!store_parse_modifiers(ctx, IMAP_ARG_LIST_ARGS(args))) return FALSE; @@ -89,8 +86,7 @@ } type = imap_arg_string(args++); - if (ctx->messageset == NULL || type == NULL || - !get_modify_type(ctx, type)) { + if (type == NULL || !get_modify_type(ctx, type)) { client_send_command_error(cmd, "Invalid arguments."); return FALSE; } @@ -128,9 +124,10 @@ struct imap_store_context ctx; ARRAY_TYPE(seq_range) modified_set = ARRAY_INIT; enum mailbox_transaction_flags flags = 0; + enum imap_sync_flags imap_sync_flags = 0; const char *tagged_reply; string_t *str; - bool failed; + int ret; if (!client_read_args(cmd, 0, 0, &args)) return FALSE; @@ -138,13 +135,18 @@ if (!client_verify_open_mailbox(cmd)) return TRUE; + if (args->type != IMAP_ARG_ATOM) { + client_send_command_error(cmd, "Invalid arguments."); + return TRUE; + } + ret = imap_search_get_seqset(cmd, IMAP_ARG_STR_NONULL(args), + cmd->uid, &search_arg); + if (ret <= 0) + return ret < 0; + memset(&ctx, 0, sizeof(ctx)); ctx.cmd = cmd; - if (!store_parse_args(&ctx, args)) - return TRUE; - - search_arg = imap_search_get_seqset(cmd, ctx.messageset, cmd->uid); - if (search_arg == NULL) + if (!store_parse_args(&ctx, ++args)) return TRUE; if (ctx.silent) @@ -188,32 +190,26 @@ if (ctx.keywords != NULL) mailbox_keywords_free(client->mailbox, &ctx.keywords); - if (mailbox_search_deinit(&search_ctx) < 0) { - failed = TRUE; + ret = mailbox_search_deinit(&search_ctx); + if (ret < 0) mailbox_transaction_rollback(&t); - } else { - failed = mailbox_transaction_commit(&t) < 0; - } - - if (!failed) { - /* With UID STORE we have to return UID for the flags as well. - Unfortunately we don't have the ability to separate those - flag changes that were caused by UID STORE and those that - came externally, so we'll just send the UID for all flag - changes that we see. */ - enum imap_sync_flags imap_sync_flags = 0; - - if (cmd->uid && - (!ctx.silent || (client->enabled_features & - MAILBOX_FEATURE_CONDSTORE) != 0)) - imap_sync_flags |= IMAP_SYNC_FLAG_SEND_UID; - - return cmd_sync(cmd, - (cmd->uid ? 0 : MAILBOX_SYNC_FLAG_NO_EXPUNGES), - imap_sync_flags, tagged_reply); - } else { + else + ret = mailbox_transaction_commit(&t); + if (ret < 0) { client_send_storage_error(cmd, mailbox_get_storage(client->mailbox)); return TRUE; } + + /* With UID STORE we have to return UID for the flags as well. + Unfortunately we don't have the ability to separate those + flag changes that were caused by UID STORE and those that + came externally, so we'll just send the UID for all flag + changes that we see. */ + if (cmd->uid && (!ctx.silent || (client->enabled_features & + MAILBOX_FEATURE_CONDSTORE) != 0)) + imap_sync_flags |= IMAP_SYNC_FLAG_SEND_UID; + + return cmd_sync(cmd, (cmd->uid ? 0 : MAILBOX_SYNC_FLAG_NO_EXPUNGES), + imap_sync_flags, tagged_reply); }
--- a/src/imap/cmd-thread.c Sun Mar 16 11:04:58 2008 +0200 +++ b/src/imap/cmd-thread.c Sun Mar 16 11:05:53 2008 +0200 @@ -12,9 +12,8 @@ enum mail_thread_type threading; struct mail_search_arg *sargs; const struct imap_arg *args; - int args_count; - pool_t pool; - const char *error, *charset, *str; + int ret, args_count; + const char *charset, *str; args_count = imap_parser_read_args(cmd->parser, 0, 0, &args); if (args_count == -2) @@ -58,22 +57,17 @@ charset = IMAP_ARG_STR(args); args++; - pool = pool_alloconly_create("mail_search_args", 2048); + ret = imap_search_args_build(cmd, args, &sargs); + if (ret <= 0) + return ret < 0; - sargs = imap_search_args_build(pool, client->mailbox, args, &error); - if (sargs == NULL) { - /* error in search arguments */ - client_send_tagline(cmd, t_strconcat("NO ", error, NULL)); - } else if (imap_thread(cmd, charset, sargs, threading) == 0) { - pool_unref(&pool); - return cmd_sync(cmd, MAILBOX_SYNC_FLAG_FAST | - (cmd->uid ? 0 : MAILBOX_SYNC_FLAG_NO_EXPUNGES), - 0, "OK Thread completed."); - } else { + if (imap_thread(cmd, charset, sargs, threading) < 0) { client_send_storage_error(cmd, mailbox_get_storage(client->mailbox)); + return TRUE; } - pool_unref(&pool); - return TRUE; + return cmd_sync(cmd, MAILBOX_SYNC_FLAG_FAST | + (cmd->uid ? 0 : MAILBOX_SYNC_FLAG_NO_EXPUNGES), + 0, "OK Thread completed."); }
--- a/src/imap/imap-fetch.c Sun Mar 16 11:04:58 2008 +0200 +++ b/src/imap/imap-fetch.c Sun Mar 16 11:05:53 2008 +0200 @@ -349,7 +349,8 @@ ctx->all_headers_ctx); /* Delayed uidset -> seqset conversion. VANISHED needs the uidset. */ - mail_search_args_init(ctx->search_args, ctx->box, TRUE); + mail_search_args_init(ctx->search_args, ctx->box, TRUE, + &ctx->cmd->client->search_saved_uidset); ctx->search_ctx = mailbox_search_init(ctx->trans, NULL, ctx->search_args, NULL); return 0;
--- a/src/imap/imap-search.c Sun Mar 16 11:04:58 2008 +0200 +++ b/src/imap/imap-search.c Sun Mar 16 11:05:53 2008 +0200 @@ -16,18 +16,48 @@ const char *error; }; -struct mail_search_arg * -imap_search_args_build(pool_t pool, struct mailbox *box, - const struct imap_arg *args, const char **error_r) +static bool search_args_have_searchres(struct mail_search_arg *sargs) +{ + for (; sargs != NULL; sargs = sargs->next) { + switch (sargs->type) { + case SEARCH_UIDSET: + if (strcmp(sargs->value.str, "$") == 0) + return TRUE; + break; + case SEARCH_SUB: + case SEARCH_OR: + if (search_args_have_searchres(sargs->value.subargs)) + return TRUE; + break; + default: + break; + } + } + return FALSE; +} + +int imap_search_args_build(struct client_command_context *cmd, + const struct imap_arg *args, + struct mail_search_arg **search_args_r) { struct mail_search_arg *sargs; + const char *error; - sargs = mail_search_build_from_imap_args(pool, args, error_r); - if (sargs == NULL) - return NULL; + sargs = mail_search_build_from_imap_args(cmd->pool, args, &error); + if (sargs == NULL) { + client_send_command_error(cmd, error); + return -1; + } - mail_search_args_init(sargs, box, TRUE); - return sargs; + if (search_args_have_searchres(sargs)) { + if (client_handle_search_save_ambiguity(cmd)) + return 0; + } + + mail_search_args_init(sargs, cmd->client->mailbox, TRUE, + &cmd->client->search_saved_uidset); + *search_args_r = sargs; + return 1; } static bool @@ -95,34 +125,63 @@ return 0; } -struct mail_search_arg * -imap_search_get_seqset(struct client_command_context *cmd, - const char *set, bool uid) +int imap_search_get_seqset(struct client_command_context *cmd, + const char *set, bool uid, + struct mail_search_arg **search_arg_r) +{ + int ret; + + ret = imap_search_get_anyset(cmd, set, uid, search_arg_r); + if (ret > 0) { + mail_search_args_init(*search_arg_r, + cmd->client->mailbox, TRUE, + &cmd->client->search_saved_uidset); + } + return ret; +} + +static int imap_search_get_searchres(struct client_command_context *cmd, + struct mail_search_arg **search_arg_r) { struct mail_search_arg *search_arg; - search_arg = imap_search_get_anyset(cmd, set, uid); - if (uid && search_arg != NULL) - mail_search_args_init(search_arg, cmd->client->mailbox, TRUE); - return search_arg; + if (client_handle_search_save_ambiguity(cmd)) + return 0; + search_arg = p_new(cmd->pool, struct mail_search_arg, 1); + if (array_is_created(&cmd->client->search_saved_uidset)) { + search_arg->type = SEARCH_UIDSET; + p_array_init(&search_arg->value.seqset, cmd->pool, + array_count(&cmd->client->search_saved_uidset)); + array_append_array(&search_arg->value.seqset, + &cmd->client->search_saved_uidset); + } else { + /* $ not set yet, match nothing */ + search_arg->type = SEARCH_ALL; + search_arg->not = TRUE; + } + *search_arg_r = search_arg; + return 1; } -struct mail_search_arg * -imap_search_get_anyset(struct client_command_context *cmd, - const char *set, bool uid) +int imap_search_get_anyset(struct client_command_context *cmd, + const char *set, bool uid, + struct mail_search_arg **search_arg_r) { - struct mail_search_arg *search_arg = NULL; const char *error = NULL; int ret; + if (strcmp(set, "$") == 0) { + /* SEARCHRES extension: replace $ with the last saved + search result */ + return imap_search_get_searchres(cmd, search_arg_r); + } if (!uid) - ret = imap_search_get_msgset_arg(cmd, set, &search_arg, &error); + ret = imap_search_get_msgset_arg(cmd, set, search_arg_r, &error); else - ret = imap_search_get_uidset_arg(cmd, set, &search_arg, &error); + ret = imap_search_get_uidset_arg(cmd, set, search_arg_r, &error); if (ret < 0) { client_send_command_error(cmd, error); - return NULL; + return -1; } - - return search_arg; + return 1; }
--- a/src/imap/imap-search.h Sun Mar 16 11:04:58 2008 +0200 +++ b/src/imap/imap-search.h Sun Mar 16 11:05:53 2008 +0200 @@ -5,16 +5,20 @@ struct mailbox; struct client_command_context; -/* Builds search arguments based on IMAP arguments. */ -struct mail_search_arg * -imap_search_args_build(pool_t pool, struct mailbox *box, - const struct imap_arg *args, const char **error_r); +/* Builds search arguments based on IMAP arguments. Returns -1 if search + arguments are invalid, 0 if we have to wait for unambiguity, + 1 if we can continue. */ +int imap_search_args_build(struct client_command_context *cmd, + const struct imap_arg *args, + struct mail_search_arg **search_args_r); -struct mail_search_arg * -imap_search_get_seqset(struct client_command_context *cmd, - const char *set, bool uid); -struct mail_search_arg * -imap_search_get_anyset(struct client_command_context *cmd, - const char *set, bool uid); +/* Returns -1 if set is invalid, 0 if we have to wait for unambiguity, + 1 if we can continue. */ +int imap_search_get_seqset(struct client_command_context *cmd, + const char *set, bool uid, + struct mail_search_arg **search_arg_r); +int imap_search_get_anyset(struct client_command_context *cmd, + const char *set, bool uid, + struct mail_search_arg **search_arg_r); #endif
--- a/src/lib-storage/mail-search-build.c Sun Mar 16 11:04:58 2008 +0200 +++ b/src/lib-storage/mail-search-build.c Sun Mar 16 11:05:53 2008 +0200 @@ -473,6 +473,10 @@ 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_messageset_parse(&sarg->value.seqset, sarg->value.str) < 0) { data->error = "Invalid UID messageset"; @@ -544,6 +548,15 @@ return FALSE; } return TRUE; + } else if (strcmp(str, "$") == 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; } @@ -576,16 +589,32 @@ } static void -mailbox_uidseq_change(struct mail_search_arg *arg, struct mailbox *box) +mailbox_uidset_change(struct mail_search_arg *arg, struct mailbox *box, + const ARRAY_TYPE(seq_range) *search_saved_uidset) { struct seq_range *uids; unsigned int i, count; uint32_t seq1, seq2; + if (strcmp(arg->value.str, "$") == 0) { + /* SEARCHRES: Replace with saved uidset */ + array_clear(&arg->value.seqset); + if (search_saved_uidset == NULL || + !array_is_created(search_saved_uidset)) + return; + + array_append_array(&arg->value.seqset, search_saved_uidset); + return; + } + arg->type = SEARCH_SEQSET; /* make a copy of the UIDs */ count = array_count(&arg->value.seqset); + if (count == 0) { + /* empty set, keep it */ + return; + } uids = t_new(struct seq_range, count); memcpy(uids, array_idx(&arg->value.seqset, 0), sizeof(*uids) * count); @@ -606,7 +635,8 @@ } void mail_search_args_init(struct mail_search_arg *args, - struct mailbox *box, bool change_uidsets) + struct mailbox *box, bool change_uidsets, + const ARRAY_TYPE(seq_range) *search_saved_uidset) { const char *keywords[2]; @@ -614,7 +644,8 @@ switch (args->type) { case SEARCH_UIDSET: if (change_uidsets) T_BEGIN { - mailbox_uidseq_change(args, box); + mailbox_uidset_change(args, box, + search_saved_uidset); } T_END; break; case SEARCH_MODSEQ: @@ -633,7 +664,8 @@ case SEARCH_SUB: case SEARCH_OR: mail_search_args_init(args->value.subargs, box, - change_uidsets); + change_uidsets, + search_saved_uidset); break; default: break;
--- a/src/lib-storage/mail-search-build.h Sun Mar 16 11:04:58 2008 +0200 +++ b/src/lib-storage/mail-search-build.h Sun Mar 16 11:05:53 2008 +0200 @@ -11,7 +11,8 @@ /* 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); + struct mailbox *box, bool change_uidsets, + const ARRAY_TYPE(seq_range) *search_saved_uidset); /* Free keywords. The args can initialized afterwards again if needed. */ void mail_search_args_deinit(struct mail_search_arg *args, struct mailbox *box);