]> git.ipfire.org Git - thirdparty/dovecot/core.git/commitdiff
Implemented support for ESORT capability.
authorTimo Sirainen <tss@iki.fi>
Wed, 4 Feb 2009 19:58:35 +0000 (14:58 -0500)
committerTimo Sirainen <tss@iki.fi>
Wed, 4 Feb 2009 19:58:35 +0000 (14:58 -0500)
--HG--
branch : HEAD

TODO
configure.in
src/imap/Makefile.am
src/imap/cmd-search.c
src/imap/cmd-sort.c
src/imap/imap-search.c [new file with mode: 0644]
src/imap/imap-search.h [new file with mode: 0644]
src/imap/imap-sort.c [deleted file]
src/imap/imap-sort.h [deleted file]

diff --git a/TODO b/TODO
index f8bcc266a505b38284b7cb93019f2cbdce39d382..b529802fb2037a19b74946c5a2cfdf787cbb50ba 100644 (file)
--- a/TODO
+++ b/TODO
@@ -1,3 +1,20 @@
+dovecot: Jan 17 17:04:38 Panic: dict: file driver-pgsql.c: line 202 (driver_pgsql_init_v): assertion failed: (connect_string != NULL)
+  ^ kun puuttui connect setting
+
+dovecot: Jan 17 17:05:58 Error: dict: io_loop_handle_remove: epoll_ctl(2, 9): No such file or directory
+dovecot: Jan 17 17:05:58 Error: dict: pgsql: Connect failed to mails: FATAL:  password authentication failed for user "timo"
+
+dovecot: Jan 17 17:13:07 Error: dict: dict sql lookup failed: FATAL:  terminating connection due to administrator command
+dovecot: Jan 17 17:13:07 Error: dict: io_loop_handle_remove: epoll_ctl(2, 9): Bad file descriptor
+
+Jan 16 02:30:12 vh3 dovecot: auth(default): sql(user@ex2.com): User query failed: FATAL:  terminating connection due to administrator command
+Jan 16 02:30:12 vh3 deliver(user@ex2.com): Auth lookup returned failure
+Jan 16 02:30:12 vh3 dovecot: auth(default): kevent(EV_DELETE, 10) failed: Bad file descriptor
+
+ - namespace shared: prefix = shared/%%d/%%u/: LIST shared/% doesn't work
+ - index_removal_timeout leak when copy&pasting:
+     10 myrights "INBOX"
+     11 getacl "INBOX"
  - proxying: support fallbacking to local (or other?) server if the first
    one is down
 user_attrs {
@@ -13,6 +30,8 @@ imap(tss)(pid=12890): Error: dovecot-acl-list creation failed:
 safe_mkstemp(/usr/local/var/run/dovecot/user-not-found/test/temp.hurina.12890.87eb6b37b351b733) failed: No such file or directory
 
  - i_panic("Message count decreased") happens - why?
+     - at least one backtrace shows client_destroy -> client_command_cancel ->
+       imap_sync_deinit
  - fts-solr: handle DELETE, RENAME
  - fsck -> log_file_tail_offset 2273345664 -> 996 ->
    mail-transaction-log.c: line 341 (mail_transaction_log_set_mailbox_sync_pos):
@@ -32,11 +51,8 @@ safe_mkstemp(/usr/local/var/run/dovecot/user-not-found/test/temp.hurina.12890.87
       dovecot-acl files? at least not that often..
  - virtual mailboxes: backend mailbox where to save mails if they're tried to
    be saved to the virtual mailbox.
- - acl example in wiki with acl_dict = file:/var/mail/%d/dovecot.shared
+ - acl example in wiki with acl_shared_dict = file:/var/mail/%d/dovecot.shared
   - with list=children
- - change auth_worker_max_request_count to non-zero by default? at least with
-   PAM it should be non-zero.. perhaps remove the entire setting and make it
-   a pam args?
  - add anonymous environment for anon logins
  - fs quota: getquotaroot inbox vs. other-box should return different quotas
    if two quotas are defined
index e1ea98a140a447c534c660927584fbc55a64689b..c4fda978e9aef7d9657902f1585c028c644478b1 100644 (file)
@@ -2340,7 +2340,7 @@ dnl ** capabilities
 dnl **
 
 capability_banner="IMAP4rev1 LITERAL+ SASL-IR LOGIN-REFERRALS ID ENABLE"
-capability="$capability_banner SORT THREAD=REFERENCES MULTIAPPEND UNSELECT IDLE CHILDREN NAMESPACE UIDPLUS LIST-EXTENDED I18NLEVEL=1 CONDSTORE QRESYNC ESEARCH SEARCHRES WITHIN CONTEXT=SEARCH"
+capability="$capability_banner SORT THREAD=REFERENCES MULTIAPPEND UNSELECT IDLE CHILDREN NAMESPACE UIDPLUS LIST-EXTENDED I18NLEVEL=1 CONDSTORE QRESYNC ESEARCH ESORT SEARCHRES WITHIN CONTEXT=SEARCH"
 AC_DEFINE_UNQUOTED(CAPABILITY_STRING, "$capability", IMAP capabilities)
 AC_DEFINE_UNQUOTED(CAPABILITY_BANNER_STRING, "$capability_banner", IMAP capabilities advertised in banner) 
 
index f1d45b8cdacd05ccbb4867b0d19cc3ee6036bb1c..386301c0ec64b1313fab43abb7ea404733918488 100644 (file)
@@ -71,8 +71,8 @@ imap_SOURCES = \
        imap-expunge.c \
        imap-fetch.c \
        imap-fetch-body.c \
+       imap-search.c \
        imap-search-args.c \
-       imap-sort.c \
        imap-status.c \
        imap-sync.c \
        mail-storage-callbacks.c \
@@ -86,8 +86,8 @@ headers = \
        common.h \
        imap-expunge.h \
        imap-fetch.h \
+       imap-search.h \
        imap-search-args.h \
-       imap-sort.h \
        imap-status.h \
        imap-sync.h
 
index b4699f9242b4cca0b23ad80eed0fdd725b63c711..4c1228a86438a1c19751dfc97dca0996234e41c2 100644 (file)
 /* Copyright (c) 2002-2009 Dovecot authors, see the included COPYING file */
 
 #include "common.h"
-#include "ostream.h"
-#include "str.h"
-#include "seq-range-array.h"
-#include "imap-resp-code.h"
-#include "imap-quote.h"
-#include "imap-seqset.h"
-#include "imap-util.h"
-#include "mail-search-build.h"
-#include "commands.h"
 #include "imap-search-args.h"
-
-enum search_return_options {
-       SEARCH_RETURN_ESEARCH           = 0x0001,
-       SEARCH_RETURN_MIN               = 0x0002,
-       SEARCH_RETURN_MAX               = 0x0004,
-       SEARCH_RETURN_ALL               = 0x0008,
-       SEARCH_RETURN_COUNT             = 0x0010,
-       SEARCH_RETURN_MODSEQ            = 0x0020,
-       SEARCH_RETURN_SAVE              = 0x0040,
-       SEARCH_RETURN_UPDATE            = 0x0080,
-       SEARCH_RETURN_PARTIAL           = 0x0100
-/* Options that don't return any seq/uid results */
-#define SEARCH_RETURN_NORESULTS \
-       (SEARCH_RETURN_ESEARCH | SEARCH_RETURN_MODSEQ | SEARCH_RETURN_SAVE | \
-        SEARCH_RETURN_UPDATE)
-};
-
-struct imap_search_context {
-       struct client_command_context *cmd;
-       struct mailbox *box;
-       struct mailbox_transaction_context *trans;
-        struct mail_search_context *search_ctx;
-       struct mail *mail;
-
-       struct mail_search_args *sargs;
-       enum search_return_options return_options;
-       uint32_t partial1, partial2;
-
-       struct timeout *to;
-       ARRAY_TYPE(seq_range) result;
-       unsigned int result_count;
-
-       uint64_t highest_seen_modseq;
-       struct timeval start_time;
-
-       unsigned int have_seqsets:1;
-       unsigned int have_modseqs:1;
-};
-
-static int
-imap_partial_range_parse(struct imap_search_context *ctx, const char *str)
-{
-       ctx->partial1 = 0;
-       ctx->partial2 = 0;
-       for (; *str >= '0' && *str <= '9'; str++)
-               ctx->partial1 = ctx->partial1 * 10 + *str-'0';
-       if (*str != ':' || ctx->partial1 == 0)
-               return -1;
-       for (str++; *str >= '0' && *str <= '9'; str++)
-               ctx->partial2 = ctx->partial2 * 10 + *str-'0';
-       if (*str != '\0' || ctx->partial2 == 0)
-               return -1;
-
-       if (ctx->partial1 > ctx->partial2) {
-               uint32_t temp = ctx->partial2;
-               ctx->partial2 = ctx->partial1;
-               ctx->partial1 = temp;
-       }
-
-       return 0;
-}
-
-static bool
-search_parse_return_options(struct imap_search_context *ctx,
-                           const struct imap_arg *args)
-{
-       struct client_command_context *cmd = ctx->cmd;
-       const char *name, *str;
-       unsigned int idx;
-
-       while (args->type != IMAP_ARG_EOL) {
-               if (args->type != IMAP_ARG_ATOM) {
-                       client_send_command_error(cmd,
-                               "SEARCH return options contain non-atoms.");
-                       return FALSE;
-               }
-               name = t_str_ucase(IMAP_ARG_STR_NONULL(args));
-               args++;
-               if (strcmp(name, "MIN") == 0)
-                       ctx->return_options |= SEARCH_RETURN_MIN;
-               else if (strcmp(name, "MAX") == 0)
-                       ctx->return_options |= SEARCH_RETURN_MAX;
-               else if (strcmp(name, "ALL") == 0)
-                       ctx->return_options |= SEARCH_RETURN_ALL;
-               else if (strcmp(name, "COUNT") == 0)
-                       ctx->return_options |= SEARCH_RETURN_COUNT;
-               else if (strcmp(name, "SAVE") == 0)
-                       ctx->return_options |= SEARCH_RETURN_SAVE;
-               else if (strcmp(name, "UPDATE") == 0)
-                       ctx->return_options |= SEARCH_RETURN_UPDATE;
-               else if (strcmp(name, "PARTIAL") == 0) {
-                       if (ctx->partial1 != 0) {
-                               client_send_command_error(cmd,
-                                       "PARTIAL can be used only once.");
-                               return FALSE;
-                       }
-                       ctx->return_options |= SEARCH_RETURN_PARTIAL;
-                       if (args->type != IMAP_ARG_ATOM) {
-                               client_send_command_error(cmd,
-                                       "PARTIAL range missing.");
-                               return FALSE;
-                       }
-                       str = IMAP_ARG_STR_NONULL(args);
-                       if (imap_partial_range_parse(ctx, str) < 0) {
-                               client_send_command_error(cmd,
-                                       "PARTIAL range broken.");
-                               return FALSE;
-                       }
-                       args++;
-               } else {
-                       client_send_command_error(cmd,
-                               "Unknown SEARCH return option");
-                       return FALSE;
-               }
-       }
-
-       if ((ctx->return_options & SEARCH_RETURN_UPDATE) != 0 &&
-           client_search_update_lookup(cmd->client, cmd->tag, &idx) != NULL) {
-               client_send_command_error(cmd, "Duplicate search update tag");
-               return FALSE;
-       }
-       if ((ctx->return_options & SEARCH_RETURN_PARTIAL) != 0 &&
-           (ctx->return_options & SEARCH_RETURN_ALL) != 0) {
-               client_send_command_error(cmd, "PARTIAL conflicts with ALL");
-               return FALSE;
-       }
-
-       if (ctx->return_options == 0)
-               ctx->return_options = SEARCH_RETURN_ALL;
-       ctx->return_options |= SEARCH_RETURN_ESEARCH;
-       return TRUE;
-}
-
-static void imap_search_args_check(struct imap_search_context *ctx,
-                                  const struct mail_search_arg *sargs)
-{
-       for (; sargs != NULL; sargs = sargs->next) {
-               switch (sargs->type) {
-               case SEARCH_SEQSET:
-                       ctx->have_seqsets = TRUE;
-                       break;
-               case SEARCH_MODSEQ:
-                       ctx->have_modseqs = TRUE;
-                       break;
-               case SEARCH_OR:
-               case SEARCH_SUB:
-                       imap_search_args_check(ctx, sargs->value.subargs);
-                       break;
-               default:
-                       break;
-               }
-       }
-}
-
-static void imap_search_result_save(struct imap_search_context *ctx)
-{
-       struct client *client = ctx->cmd->client;
-       struct mail_search_result *result;
-       struct imap_search_update *update;
-
-       if (!array_is_created(&client->search_updates))
-               i_array_init(&client->search_updates, 32);
-       else if (array_count(&client->search_updates) >=
-                CLIENT_MAX_SEARCH_UPDATES) {
-               /* too many updates */
-               string_t *str = t_str_new(256);
-               str_append(str, "* NO [NOUPDATE ");
-               imap_quote_append_string(str, ctx->cmd->tag, FALSE);
-               str_append_c(str, ']');
-               client_send_line(client, str_c(str));
-               ctx->return_options &= ~SEARCH_RETURN_UPDATE;
-               return;
-       }
-       result = mailbox_search_result_save(ctx->search_ctx,
-                                       MAILBOX_SEARCH_RESULT_FLAG_UPDATE |
-                                       MAILBOX_SEARCH_RESULT_FLAG_QUEUE_SYNC);
-
-       update = array_append_space(&client->search_updates);
-       update->tag = i_strdup(ctx->cmd->tag);
-       update->result = result;
-       update->return_uids = ctx->cmd->uid;
-}
-
-static void
-imap_search_init(struct imap_search_context *ctx,
-                struct mail_search_args *sargs)
-{
-       imap_search_args_check(ctx, sargs->args);
-
-       if (ctx->have_modseqs) {
-               ctx->return_options |= SEARCH_RETURN_MODSEQ;
-               client_enable(ctx->cmd->client, MAILBOX_FEATURE_CONDSTORE);
-       }
-
-       ctx->box = ctx->cmd->client->mailbox;
-       ctx->trans = mailbox_transaction_begin(ctx->box, 0);
-       ctx->sargs = sargs;
-       ctx->search_ctx = mailbox_search_init(ctx->trans, sargs, NULL);
-       ctx->mail = mail_alloc(ctx->trans, 0, NULL);
-       (void)gettimeofday(&ctx->start_time, NULL);
-       i_array_init(&ctx->result, 128);
-       if ((ctx->return_options & SEARCH_RETURN_UPDATE) != 0)
-               imap_search_result_save(ctx);
-}
-
-static void imap_search_send_result_standard(struct imap_search_context *ctx)
-{
-       const struct seq_range *range;
-       unsigned int i, count;
-       string_t *str;
-       uint32_t seq;
-
-       str = t_str_new(1024);
-       range = array_get(&ctx->result, &count);
-       str_append(str, "* SEARCH");
-       for (i = 0; i < count; i++) {
-               for (seq = range[i].seq1; seq <= range[i].seq2; seq++)
-                       str_printfa(str, " %u", seq);
-               if (str_len(str) >= 1024-32) {
-                       o_stream_send(ctx->cmd->client->output,
-                                     str_data(str), str_len(str));
-                       str_truncate(str, 0);
-               }
-       }
-
-       if (ctx->highest_seen_modseq != 0) {
-               str_printfa(str, " (MODSEQ %llu)",
-                           (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));
-}
-
-static void
-imap_search_send_partial(struct imap_search_context *ctx, string_t *str)
-{
-       struct seq_range_iter iter;
-       uint32_t n1, n2;
-
-       str_printfa(str, " PARTIAL (%u:%u ", ctx->partial1, ctx->partial2);
-       seq_range_array_iter_init(&iter, &ctx->result);
-       if (!seq_range_array_iter_nth(&iter, ctx->partial1 - 1, &n1)) {
-               /* no results (in range) */
-               str_append(str, "NIL)");
-               return;
-       }
-       if (!seq_range_array_iter_nth(&iter, ctx->partial2 - 1, &n2))
-               n2 = (uint32_t)-1;
-
-       /* FIXME: we should save the search result for later use */
-       if (n1 > 1)
-               seq_range_array_remove_range(&ctx->result, 1, n1 - 1);
-       if (n2 != (uint32_t)-1) {
-               seq_range_array_remove_range(&ctx->result,
-                                            n2 + 1, (uint32_t)-1);
-       }
-       imap_write_seq_range(str, &ctx->result);
-       str_append_c(str, ')');
-}
-
-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;
-
-       if ((ctx->return_options & SEARCH_RETURN_ESEARCH) == 0) {
-               imap_search_send_result_standard(ctx);
-               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);
-       str_append_c(str, ')');
-
-       if (ctx->cmd->uid)
-               str_append(str, " UID");
-
-       range = array_get(&ctx->result, &count);
-       if (count > 0) {
-               if ((ctx->return_options & SEARCH_RETURN_MIN) != 0)
-                       str_printfa(str, " MIN %u", range[0].seq1);
-               if ((ctx->return_options & SEARCH_RETURN_MAX) != 0)
-                       str_printfa(str, " MAX %u", range[count-1].seq2);
-               if ((ctx->return_options & SEARCH_RETURN_ALL) != 0) {
-                       str_append(str, " ALL ");
-                       imap_write_seq_range(str, &ctx->result);
-               }
-       }
-
-       if ((ctx->return_options & SEARCH_RETURN_PARTIAL) != 0)
-               imap_search_send_partial(ctx, str);
-
-       if ((ctx->return_options & SEARCH_RETURN_COUNT) != 0)
-               str_printfa(str, " COUNT %u", ctx->result_count);
-       if (ctx->highest_seen_modseq != 0) {
-               str_printfa(str, " MODSEQ %llu",
-                           (unsigned long long)ctx->highest_seen_modseq);
-       }
-       str_append(str, "\r\n");
-       o_stream_send(client->output, str_data(str), str_len(str));
-}
-
-static int imap_search_deinit(struct imap_search_context *ctx)
-{
-       int ret = 0;
-
-       mail_free(&ctx->mail);
-       if (mailbox_search_deinit(&ctx->search_ctx) < 0)
-               ret = -1;
-
-       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);
-
-       if (ctx->to != NULL)
-               timeout_remove(&ctx->to);
-       array_free(&ctx->result);
-       mail_search_args_deinit(ctx->sargs);
-       mail_search_args_unref(&ctx->sargs);
-
-       ctx->cmd->context = NULL;
-       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;
-       enum search_return_options opts = ctx->return_options;
-       enum mailbox_sync_flags sync_flags;
-       struct timeval end_time;
-       const struct seq_range *range;
-       unsigned int count;
-       uint32_t id, id_min, id_max;
-       const char *ok_reply;
-       bool tryagain, minmax, lost_data;
-
-       if (cmd->cancel) {
-               (void)imap_search_deinit(ctx);
-               return TRUE;
-       }
-
-       range = array_get(&ctx->result, &count);
-       if (count == 0) {
-               id_min = (uint32_t)-1;
-               id_max = 0;
-       } else {
-               id_min = range[0].seq1;
-               id_max = range[count-1].seq2;
-       }
-
-       minmax = (opts & (SEARCH_RETURN_MIN | SEARCH_RETURN_MAX)) != 0 &&
-               (opts & ~(SEARCH_RETURN_NORESULTS |
-                         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 (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) {
-                               /* return option updates are delayed until
-                                  we know the actual min/max values */
-                               seq_range_array_add(&ctx->result, 0, id);
-                       }
-                       continue;
-               }
-
-               search_update_mail(ctx);
-               if ((opts & ~(SEARCH_RETURN_NORESULTS |
-                             SEARCH_RETURN_COUNT)) == 0) {
-                       /* we only want to count (and get modseqs) */
-                       continue;
-               }
-               seq_range_array_add(&ctx->result, 0, id);
-       }
-       if (tryagain)
-               return FALSE;
-
-       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) {
-                               if (!mail_set_uid(ctx->mail, id_min))
-                                       i_unreached();
-                       } else {
-                               mail_set_seq(ctx->mail, id_min);
-                       }
-                       search_update_mail(ctx);
-               }
-               if ((opts & SEARCH_RETURN_MAX) != 0) {
-                       i_assert(id_max != 0);
-                       if (cmd->uid) {
-                               if (!mail_set_uid(ctx->mail, id_max))
-                                       i_unreached();
-                       } else {
-                               mail_set_seq(ctx->mail, id_max);
-                       }
-                       search_update_mail(ctx);
-               }
-       }
-
-       lost_data = mailbox_search_seen_lost_data(ctx->search_ctx);
-       if (imap_search_deinit(ctx) < 0) {
-               client_send_storage_error(cmd,
-                       mailbox_get_storage(cmd->client->mailbox));
-               return TRUE;
-       }
-
-       if (gettimeofday(&end_time, NULL) < 0)
-               memset(&end_time, 0, sizeof(end_time));
-       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_usec += 1000000;
-       }
-
-       sync_flags = MAILBOX_SYNC_FLAG_FAST;
-       if (!cmd->uid || ctx->have_seqsets)
-               sync_flags |= MAILBOX_SYNC_FLAG_NO_EXPUNGES;
-       ok_reply = t_strdup_printf("OK %sSearch completed (%d.%03d secs).",
-               lost_data ? "["IMAP_RESP_CODE_EXPUNGEISSUED"] " : "",
-               (int)end_time.tv_sec, (int)(end_time.tv_usec/1000));
-       return cmd_sync(cmd, sync_flags, 0, ok_reply);
-}
-
-static void cmd_search_more_callback(struct client_command_context *cmd)
-{
-       struct client *client = cmd->client;
-       bool finished;
-
-       o_stream_cork(client->output);
-       finished = cmd_search_more(cmd);
-       o_stream_uncork(client->output);
-
-       if (!finished)
-               (void)client_handle_unfinished_cmd(cmd);
-       else
-               client_command_free(&cmd);
-       (void)cmd_sync_delayed(client);
-       client_continue_pending_input(&client);
-}
+#include "imap-search.h"
 
 bool cmd_search(struct client_command_context *cmd)
 {
-       struct client *client = cmd->client;
        struct imap_search_context *ctx;
        struct mail_search_args *sargs;
        const struct imap_arg *args;
@@ -511,7 +21,7 @@ bool cmd_search(struct client_command_context *cmd)
                                          "Missing SEARCH arguments.");
                return TRUE;
        }
-       client->input_lock = NULL;
+       cmd->client->input_lock = NULL;
 
        if (!client_verify_open_mailbox(cmd))
                return TRUE;
@@ -519,30 +29,9 @@ bool cmd_search(struct client_command_context *cmd)
        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)))
-                       return TRUE;
-               args++;
-
-               if ((ctx->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;
-       }
-
-       if ((ctx->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);
+       if ((ret = cmd_search_parse_return_if_found(ctx, &args)) <= 0) {
+               /* error / waiting for unambiguity */
+               return ret < 0;
        }
 
        if (args->type == IMAP_ARG_ATOM &&
@@ -566,15 +55,5 @@ bool cmd_search(struct client_command_context *cmd)
        if (ret <= 0)
                return ret < 0;
 
-       imap_search_init(ctx, sargs);
-       cmd->func = cmd_search_more;
-       cmd->context = ctx;
-
-       if (cmd_search_more(cmd))
-               return TRUE;
-
-       /* we could have moved onto syncing by now */
-       if (cmd->func == cmd_search_more)
-               ctx->to = timeout_add(0, cmd_search_more_callback, cmd);
-       return FALSE;
+       return imap_search_start(ctx, sargs, NULL);
 }
index ffbe2f1cc27492805663329a2b9159a553c0cafd..57c47049569edc7d32fe64562af3514a96bbf95b 100644 (file)
@@ -4,7 +4,7 @@
 #include "buffer.h"
 #include "commands.h"
 #include "imap-search-args.h"
-#include "imap-sort.h"
+#include "imap-search.h"
 
 struct sort_name {
        enum mail_sort_type type;
@@ -89,9 +89,9 @@ get_sort_program(struct client_command_context *cmd,
 
 bool cmd_sort(struct client_command_context *cmd)
 {
-       struct client *client = cmd->client;
+       struct imap_search_context *ctx;
        struct mail_search_args *sargs;
-       enum mail_sort_type sorting[MAX_SORT_PROGRAM_SIZE];
+       enum mail_sort_type sort_program[MAX_SORT_PROGRAM_SIZE];
        const struct imap_arg *args;
        int args_count;
        const char *charset;
@@ -100,7 +100,7 @@ bool cmd_sort(struct client_command_context *cmd)
        args_count = imap_parser_read_args(cmd->parser, 0, 0, &args);
        if (args_count == -2)
                return FALSE;
-       client->input_lock = NULL;
+       cmd->client->input_lock = NULL;
 
        if (args_count < 3) {
                client_send_command_error(cmd, args_count < 0 ? NULL :
@@ -117,7 +117,7 @@ bool cmd_sort(struct client_command_context *cmd)
                return TRUE;
        }
 
-       if (get_sort_program(cmd, IMAP_ARG_LIST_ARGS(args), sorting) < 0)
+       if (get_sort_program(cmd, IMAP_ARG_LIST_ARGS(args), sort_program) < 0)
                return TRUE;
        args++;
 
@@ -130,19 +130,17 @@ bool cmd_sort(struct client_command_context *cmd)
        charset = IMAP_ARG_STR(args);
        args++;
 
+       ctx = p_new(cmd->pool, struct imap_search_context, 1);
+       ctx->cmd = cmd;
+
+       if ((ret = cmd_search_parse_return_if_found(ctx, &args)) <= 0) {
+               /* error / waiting for unambiguity */
+               return ret < 0;
+       }
+
        ret = imap_search_args_build(cmd, args, charset, &sargs);
        if (ret <= 0)
                return ret < 0;
 
-       ret = imap_sort(cmd, sargs, sorting);
-       mail_search_args_unref(&sargs);
-       if (ret < 0) {
-               client_send_storage_error(cmd,
-                                         mailbox_get_storage(client->mailbox));
-               return TRUE;
-       }
-
-       return cmd_sync(cmd, MAILBOX_SYNC_FLAG_FAST |
-                       (cmd->uid ? 0 : MAILBOX_SYNC_FLAG_NO_EXPUNGES),
-                       0, "OK Sort completed.");
+       return imap_search_start(ctx, sargs, sort_program);
 }
diff --git a/src/imap/imap-search.c b/src/imap/imap-search.c
new file mode 100644 (file)
index 0000000..19c864d
--- /dev/null
@@ -0,0 +1,588 @@
+/* Copyright (c) 2002-2009 Dovecot authors, see the included COPYING file */
+
+#include "common.h"
+#include "ostream.h"
+#include "str.h"
+#include "seq-range-array.h"
+#include "imap-resp-code.h"
+#include "imap-quote.h"
+#include "imap-seqset.h"
+#include "imap-util.h"
+#include "mail-search-build.h"
+#include "commands.h"
+#include "imap-search-args.h"
+#include "imap-search.h"
+
+static int imap_search_deinit(struct imap_search_context *ctx);
+
+static int
+imap_partial_range_parse(struct imap_search_context *ctx, const char *str)
+{
+       ctx->partial1 = 0;
+       ctx->partial2 = 0;
+       for (; *str >= '0' && *str <= '9'; str++)
+               ctx->partial1 = ctx->partial1 * 10 + *str-'0';
+       if (*str != ':' || ctx->partial1 == 0)
+               return -1;
+       for (str++; *str >= '0' && *str <= '9'; str++)
+               ctx->partial2 = ctx->partial2 * 10 + *str-'0';
+       if (*str != '\0' || ctx->partial2 == 0)
+               return -1;
+
+       if (ctx->partial1 > ctx->partial2) {
+               uint32_t temp = ctx->partial2;
+               ctx->partial2 = ctx->partial1;
+               ctx->partial1 = temp;
+       }
+
+       return 0;
+}
+
+static bool
+search_parse_return_options(struct imap_search_context *ctx,
+                           const struct imap_arg *args)
+{
+       struct client_command_context *cmd = ctx->cmd;
+       const char *name, *str;
+       unsigned int idx;
+
+       while (args->type != IMAP_ARG_EOL) {
+               if (args->type != IMAP_ARG_ATOM) {
+                       client_send_command_error(cmd,
+                               "SEARCH return options contain non-atoms.");
+                       return FALSE;
+               }
+               name = t_str_ucase(IMAP_ARG_STR_NONULL(args));
+               args++;
+               if (strcmp(name, "MIN") == 0)
+                       ctx->return_options |= SEARCH_RETURN_MIN;
+               else if (strcmp(name, "MAX") == 0)
+                       ctx->return_options |= SEARCH_RETURN_MAX;
+               else if (strcmp(name, "ALL") == 0)
+                       ctx->return_options |= SEARCH_RETURN_ALL;
+               else if (strcmp(name, "COUNT") == 0)
+                       ctx->return_options |= SEARCH_RETURN_COUNT;
+               else if (strcmp(name, "SAVE") == 0)
+                       ctx->return_options |= SEARCH_RETURN_SAVE;
+               else if (strcmp(name, "UPDATE") == 0)
+                       ctx->return_options |= SEARCH_RETURN_UPDATE;
+               else if (strcmp(name, "PARTIAL") == 0) {
+                       if (ctx->partial1 != 0) {
+                               client_send_command_error(cmd,
+                                       "PARTIAL can be used only once.");
+                               return FALSE;
+                       }
+                       ctx->return_options |= SEARCH_RETURN_PARTIAL;
+                       if (args->type != IMAP_ARG_ATOM) {
+                               client_send_command_error(cmd,
+                                       "PARTIAL range missing.");
+                               return FALSE;
+                       }
+                       str = IMAP_ARG_STR_NONULL(args);
+                       if (imap_partial_range_parse(ctx, str) < 0) {
+                               client_send_command_error(cmd,
+                                       "PARTIAL range broken.");
+                               return FALSE;
+                       }
+                       args++;
+               } else {
+                       client_send_command_error(cmd,
+                               "Unknown SEARCH return option");
+                       return FALSE;
+               }
+       }
+
+       if ((ctx->return_options & SEARCH_RETURN_UPDATE) != 0 &&
+           client_search_update_lookup(cmd->client, cmd->tag, &idx) != NULL) {
+               client_send_command_error(cmd, "Duplicate search update tag");
+               return FALSE;
+       }
+       if ((ctx->return_options & SEARCH_RETURN_PARTIAL) != 0 &&
+           (ctx->return_options & SEARCH_RETURN_ALL) != 0) {
+               client_send_command_error(cmd, "PARTIAL conflicts with ALL");
+               return FALSE;
+       }
+
+       if (ctx->return_options == 0)
+               ctx->return_options = SEARCH_RETURN_ALL;
+       ctx->return_options |= SEARCH_RETURN_ESEARCH;
+       return TRUE;
+}
+
+static void imap_search_args_check(struct imap_search_context *ctx,
+                                  const struct mail_search_arg *sargs)
+{
+       for (; sargs != NULL; sargs = sargs->next) {
+               switch (sargs->type) {
+               case SEARCH_SEQSET:
+                       ctx->have_seqsets = TRUE;
+                       break;
+               case SEARCH_MODSEQ:
+                       ctx->have_modseqs = TRUE;
+                       break;
+               case SEARCH_OR:
+               case SEARCH_SUB:
+                       imap_search_args_check(ctx, sargs->value.subargs);
+                       break;
+               default:
+                       break;
+               }
+       }
+}
+
+static void imap_search_result_save(struct imap_search_context *ctx)
+{
+       struct client *client = ctx->cmd->client;
+       struct mail_search_result *result;
+       struct imap_search_update *update;
+
+       if (!array_is_created(&client->search_updates))
+               i_array_init(&client->search_updates, 32);
+       else if (array_count(&client->search_updates) >=
+                CLIENT_MAX_SEARCH_UPDATES) {
+               /* too many updates */
+               string_t *str = t_str_new(256);
+               str_append(str, "* NO [NOUPDATE ");
+               imap_quote_append_string(str, ctx->cmd->tag, FALSE);
+               str_append_c(str, ']');
+               client_send_line(client, str_c(str));
+               ctx->return_options &= ~SEARCH_RETURN_UPDATE;
+               return;
+       }
+       result = mailbox_search_result_save(ctx->search_ctx,
+                                       MAILBOX_SEARCH_RESULT_FLAG_UPDATE |
+                                       MAILBOX_SEARCH_RESULT_FLAG_QUEUE_SYNC);
+
+       update = array_append_space(&client->search_updates);
+       update->tag = i_strdup(ctx->cmd->tag);
+       update->result = result;
+       update->return_uids = ctx->cmd->uid;
+}
+
+static void imap_search_send_result_standard(struct imap_search_context *ctx)
+{
+       const struct seq_range *range;
+       unsigned int i, count;
+       string_t *str;
+       uint32_t seq;
+
+       str = t_str_new(1024);
+       range = array_get(&ctx->result, &count);
+       str_append(str, ctx->sorting ? "* SORT" : "* SEARCH");
+       for (i = 0; i < count; i++) {
+               for (seq = range[i].seq1; seq <= range[i].seq2; seq++)
+                       str_printfa(str, " %u", seq);
+               if (str_len(str) >= 1024-32) {
+                       o_stream_send(ctx->cmd->client->output,
+                                     str_data(str), str_len(str));
+                       str_truncate(str, 0);
+               }
+       }
+
+       if (ctx->highest_seen_modseq != 0) {
+               str_printfa(str, " (MODSEQ %llu)",
+                           (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));
+}
+
+static void
+imap_search_send_partial(struct imap_search_context *ctx, string_t *str)
+{
+       struct seq_range *range;
+       uint32_t n, diff;
+       unsigned int i, count, delete_count;
+
+       str_printfa(str, " PARTIAL (%u:%u ", ctx->partial1, ctx->partial2);
+       ctx->partial1--;
+       ctx->partial2--;
+
+       /* we need to be able to handle non-sorted seq ranges, so do this
+          ourself instead of using seq_range_array_*() functions. */
+       range = array_get_modifiable(&ctx->result, &count);
+       delete_count = 0;
+       for (i = n = 0; i < count; i++) {
+               diff = range[i].seq2 - range[i].seq1;
+               if (n + diff >= ctx->partial1) {
+                       range[i].seq1 += ctx->partial1 - n;
+                       delete_count = i;
+                       break;
+               }
+               n += diff + 1;
+       }
+       for (n = ctx->partial1; i < count; i++) {
+               diff = range[i].seq2 - range[i].seq1;
+               if (n + diff >= ctx->partial2) {
+                       range[i].seq2 = range[i].seq1 + (ctx->partial2 - n);
+                       array_delete(&ctx->result, i + 1, count-(i+1));
+                       break;
+               }
+               n += diff + 1;
+       }
+       array_delete(&ctx->result, 0, delete_count);
+
+       if (array_count(&ctx->result) == 0) {
+               /* no results (in range) */
+               str_append(str, "NIL");
+       } else {
+               imap_write_seq_range(str, &ctx->result);
+       }
+       str_append_c(str, ')');
+}
+
+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;
+
+       if ((ctx->return_options & SEARCH_RETURN_ESEARCH) == 0) {
+               imap_search_send_result_standard(ctx);
+               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);
+       str_append_c(str, ')');
+
+       if (ctx->cmd->uid)
+               str_append(str, " UID");
+
+       range = array_get(&ctx->result, &count);
+       if (count > 0) {
+               if ((ctx->return_options & SEARCH_RETURN_MIN) != 0)
+                       str_printfa(str, " MIN %u", range[0].seq1);
+               if ((ctx->return_options & SEARCH_RETURN_MAX) != 0)
+                       str_printfa(str, " MAX %u", range[count-1].seq2);
+               if ((ctx->return_options & SEARCH_RETURN_ALL) != 0) {
+                       str_append(str, " ALL ");
+                       imap_write_seq_range(str, &ctx->result);
+               }
+       }
+
+       if ((ctx->return_options & SEARCH_RETURN_PARTIAL) != 0)
+               imap_search_send_partial(ctx, str);
+
+       if ((ctx->return_options & SEARCH_RETURN_COUNT) != 0)
+               str_printfa(str, " COUNT %u", ctx->result_count);
+       if (ctx->highest_seen_modseq != 0) {
+               str_printfa(str, " MODSEQ %llu",
+                           (unsigned long long)ctx->highest_seen_modseq);
+       }
+       str_append(str, "\r\n");
+       o_stream_send(client->output, str_data(str), str_len(str));
+}
+
+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 void search_add_result_id(struct imap_search_context *ctx, uint32_t id)
+{
+       struct seq_range *range;
+       unsigned int count;
+
+       /* only append the data. this is especially important when we're
+          returning a sort result. */
+       range = array_get_modifiable(&ctx->result, &count);
+       if (count > 0 && id == range[count-1].seq2 + 1) {
+               range[count-1].seq2++;
+       } else {
+               range = array_append_space(&ctx->result);
+               range->seq1 = range->seq2 = id;
+       }
+}
+
+static bool cmd_search_more(struct client_command_context *cmd)
+{
+       struct imap_search_context *ctx = cmd->context;
+       enum search_return_options opts = ctx->return_options;
+       enum mailbox_sync_flags sync_flags;
+       struct timeval end_time;
+       const struct seq_range *range;
+       unsigned int count;
+       uint32_t id, id_min, id_max;
+       const char *ok_reply;
+       bool tryagain, minmax, lost_data;
+
+       if (cmd->cancel) {
+               (void)imap_search_deinit(ctx);
+               return TRUE;
+       }
+
+       range = array_get(&ctx->result, &count);
+       if (count == 0) {
+               id_min = 0;
+               id_max = 0;
+       } else {
+               id_min = range[0].seq1;
+               id_max = range[count-1].seq2;
+       }
+
+       minmax = (opts & (SEARCH_RETURN_MIN | SEARCH_RETURN_MAX)) != 0 &&
+               (opts & ~(SEARCH_RETURN_NORESULTS |
+                         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 (minmax) {
+                       /* we only care about min/max */
+                       if (id_min == 0 && (opts & SEARCH_RETURN_MIN) != 0)
+                               id_min = id;
+                       if ((opts & SEARCH_RETURN_MAX) != 0)
+                               id_max = id;
+                       if (id == id_min || id == id_max) {
+                               /* return option updates are delayed until
+                                  we know the actual min/max values */
+                               search_add_result_id(ctx, id);
+                       }
+                       continue;
+               }
+
+               search_update_mail(ctx);
+               if ((opts & ~(SEARCH_RETURN_NORESULTS |
+                             SEARCH_RETURN_COUNT)) == 0) {
+                       /* we only want to count (and get modseqs) */
+                       continue;
+               }
+               search_add_result_id(ctx, id);
+       }
+       if (tryagain)
+               return FALSE;
+
+       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 != 0);
+                       if (cmd->uid) {
+                               if (!mail_set_uid(ctx->mail, id_min))
+                                       i_unreached();
+                       } else {
+                               mail_set_seq(ctx->mail, id_min);
+                       }
+                       search_update_mail(ctx);
+               }
+               if ((opts & SEARCH_RETURN_MAX) != 0) {
+                       i_assert(id_max != 0);
+                       if (cmd->uid) {
+                               if (!mail_set_uid(ctx->mail, id_max))
+                                       i_unreached();
+                       } else {
+                               mail_set_seq(ctx->mail, id_max);
+                       }
+                       search_update_mail(ctx);
+               }
+       }
+
+       lost_data = mailbox_search_seen_lost_data(ctx->search_ctx);
+       if (imap_search_deinit(ctx) < 0) {
+               client_send_storage_error(cmd,
+                       mailbox_get_storage(cmd->client->mailbox));
+               return TRUE;
+       }
+
+       if (gettimeofday(&end_time, NULL) < 0)
+               memset(&end_time, 0, sizeof(end_time));
+       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_usec += 1000000;
+       }
+
+       sync_flags = MAILBOX_SYNC_FLAG_FAST;
+       if (!cmd->uid || ctx->have_seqsets)
+               sync_flags |= MAILBOX_SYNC_FLAG_NO_EXPUNGES;
+       ok_reply = t_strdup_printf("OK %s%s completed (%d.%03d secs).",
+               lost_data ? "["IMAP_RESP_CODE_EXPUNGEISSUED"] " : "",
+               !ctx->sorting ? "Search"  : "Sort",
+               (int)end_time.tv_sec, (int)(end_time.tv_usec/1000));
+       return cmd_sync(cmd, sync_flags, 0, ok_reply);
+}
+
+static void cmd_search_more_callback(struct client_command_context *cmd)
+{
+       struct client *client = cmd->client;
+       bool finished;
+
+       o_stream_cork(client->output);
+       finished = cmd_search_more(cmd);
+       o_stream_uncork(client->output);
+
+       if (!finished)
+               (void)client_handle_unfinished_cmd(cmd);
+       else
+               client_command_free(&cmd);
+       (void)cmd_sync_delayed(client);
+       client_continue_pending_input(&client);
+}
+
+int cmd_search_parse_return_if_found(struct imap_search_context *ctx,
+                                    const struct imap_arg **_args)
+{
+       const struct imap_arg *args = *_args;
+       struct client_command_context *cmd = ctx->cmd;
+
+       if (!(args->type == IMAP_ARG_ATOM && args[1].type == IMAP_ARG_LIST &&
+             strcasecmp(IMAP_ARG_STR_NONULL(args), "RETURN") == 0)) {
+               ctx->return_options = SEARCH_RETURN_ALL;
+               return 1;
+       }
+
+       args++;
+       if (!search_parse_return_options(ctx, IMAP_ARG_LIST_ARGS(args)))
+               return -1;
+       args++;
+
+       if ((ctx->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 0;
+
+               /* make sure the search result gets cleared if SEARCH fails */
+               if (array_is_created(&cmd->client->search_saved_uidset))
+                       array_clear(&cmd->client->search_saved_uidset);
+               else
+                       i_array_init(&cmd->client->search_saved_uidset, 128);
+       }
+
+       *_args = args;
+       return 1;
+}
+
+static void wanted_fields_get(struct mailbox *box,
+                             const enum mail_sort_type *sort_program,
+                             enum mail_fetch_field *wanted_fields_r,
+                             struct mailbox_header_lookup_ctx **headers_ctx_r)
+{
+       const char *headers[2];
+
+       *wanted_fields_r = 0;
+       *headers_ctx_r = NULL;
+
+       if (sort_program == NULL)
+               return;
+
+       headers[0] = headers[1] = NULL;
+       switch (sort_program[0] & MAIL_SORT_MASK) {
+       case MAIL_SORT_ARRIVAL:
+               *wanted_fields_r = MAIL_FETCH_RECEIVED_DATE;
+               break;
+       case MAIL_SORT_CC:
+               headers[0] = "Cc";
+               break;
+       case MAIL_SORT_DATE:
+               *wanted_fields_r = MAIL_FETCH_DATE;
+               break;
+       case MAIL_SORT_FROM:
+               headers[0] = "From";
+               break;
+       case MAIL_SORT_SIZE:
+               *wanted_fields_r = MAIL_FETCH_VIRTUAL_SIZE;
+               break;
+       case MAIL_SORT_SUBJECT:
+               headers[0] = "Subject";
+               break;
+       case MAIL_SORT_TO:
+               headers[0] = "To";
+               break;
+       }
+
+       if (headers[0] != NULL)
+               *headers_ctx_r = mailbox_header_lookup_init(box, headers);
+}
+
+bool imap_search_start(struct imap_search_context *ctx,
+                      struct mail_search_args *sargs,
+                      const enum mail_sort_type *sort_program)
+{
+       struct client_command_context *cmd = ctx->cmd;
+       enum mail_fetch_field wanted_fields;
+       struct mailbox_header_lookup_ctx *wanted_headers;
+
+       imap_search_args_check(ctx, sargs->args);
+
+       if (ctx->have_modseqs) {
+               ctx->return_options |= SEARCH_RETURN_MODSEQ;
+               client_enable(cmd->client, MAILBOX_FEATURE_CONDSTORE);
+       }
+
+       wanted_fields_get(ctx->box, sort_program,
+                         &wanted_fields, &wanted_headers);
+
+       ctx->box = cmd->client->mailbox;
+       ctx->trans = mailbox_transaction_begin(ctx->box, 0);
+       ctx->sargs = sargs;
+       ctx->search_ctx = mailbox_search_init(ctx->trans, sargs, sort_program);
+       ctx->mail = mail_alloc(ctx->trans, wanted_fields, wanted_headers);
+       ctx->sorting = sort_program != NULL;
+       (void)gettimeofday(&ctx->start_time, NULL);
+       i_array_init(&ctx->result, 128);
+       if ((ctx->return_options & SEARCH_RETURN_UPDATE) != 0)
+               imap_search_result_save(ctx);
+
+       cmd->func = cmd_search_more;
+       cmd->context = ctx;
+
+       if (cmd_search_more(cmd))
+               return TRUE;
+
+       /* we may have moved onto syncing by now */
+       if (cmd->func == cmd_search_more)
+               ctx->to = timeout_add(0, cmd_search_more_callback, cmd);
+       return FALSE;
+}
+
+static int imap_search_deinit(struct imap_search_context *ctx)
+{
+       int ret = 0;
+
+       mail_free(&ctx->mail);
+       if (mailbox_search_deinit(&ctx->search_ctx) < 0)
+               ret = -1;
+
+       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);
+
+       if (ctx->to != NULL)
+               timeout_remove(&ctx->to);
+       array_free(&ctx->result);
+       mail_search_args_deinit(ctx->sargs);
+       mail_search_args_unref(&ctx->sargs);
+
+       ctx->cmd->context = NULL;
+       return ret;
+}
diff --git a/src/imap/imap-search.h b/src/imap/imap-search.h
new file mode 100644 (file)
index 0000000..ea94c78
--- /dev/null
@@ -0,0 +1,50 @@
+#ifndef IMAP_SEARCH_H
+#define IMAP_SEARCH_H
+
+enum search_return_options {
+       SEARCH_RETURN_ESEARCH           = 0x0001,
+       SEARCH_RETURN_MIN               = 0x0002,
+       SEARCH_RETURN_MAX               = 0x0004,
+       SEARCH_RETURN_ALL               = 0x0008,
+       SEARCH_RETURN_COUNT             = 0x0010,
+       SEARCH_RETURN_MODSEQ            = 0x0020,
+       SEARCH_RETURN_SAVE              = 0x0040,
+       SEARCH_RETURN_UPDATE            = 0x0080,
+       SEARCH_RETURN_PARTIAL           = 0x0100
+/* Options that don't return any seq/uid results */
+#define SEARCH_RETURN_NORESULTS \
+       (SEARCH_RETURN_ESEARCH | SEARCH_RETURN_MODSEQ | SEARCH_RETURN_SAVE | \
+        SEARCH_RETURN_UPDATE)
+};
+
+struct imap_search_context {
+       struct client_command_context *cmd;
+       struct mailbox *box;
+       struct mailbox_transaction_context *trans;
+        struct mail_search_context *search_ctx;
+       struct mail *mail;
+
+       struct mail_search_args *sargs;
+       enum search_return_options return_options;
+       uint32_t partial1, partial2;
+
+       struct timeout *to;
+       ARRAY_TYPE(seq_range) result;
+       unsigned int result_count;
+
+       uint64_t highest_seen_modseq;
+       struct timeval start_time;
+
+       unsigned int have_seqsets:1;
+       unsigned int have_modseqs:1;
+       unsigned int sorting:1;
+};
+
+int cmd_search_parse_return_if_found(struct imap_search_context *ctx,
+                                    const struct imap_arg **args);
+
+bool imap_search_start(struct imap_search_context *ctx,
+                      struct mail_search_args *sargs,
+                      const enum mail_sort_type *sort_program);
+
+#endif
diff --git a/src/imap/imap-sort.c b/src/imap/imap-sort.c
deleted file mode 100644 (file)
index 085e5c0..0000000
+++ /dev/null
@@ -1,113 +0,0 @@
-/* Copyright (c) 2002-2009 Dovecot authors, see the included COPYING file */
-
-/* Implementation of draft-ietf-imapext-sort-10 sorting algorithm.
-   Pretty messy code actually, adding any sort types requires care.
-   This is pretty fast however and takes only as much memory as needed to be
-   reasonably fast. */
-
-#include "common.h"
-#include "array.h"
-#include "hash.h"
-#include "ostream.h"
-#include "str.h"
-#include "imap-base-subject.h"
-#include "mail-storage.h"
-#include "message-address.h"
-#include "imap-sort.h"
-
-#include <stdlib.h>
-
-#define MAX_WANTED_HEADERS 10
-#define STRBUF_SIZE 1024
-
-#define IS_SORT_STRING(type) \
-       ((type) == MAIL_SORT_CC || (type) == MAIL_SORT_FROM || \
-        (type) == MAIL_SORT_SUBJECT || (type) == MAIL_SORT_TO)
-
-#define IS_SORT_TIME(type) \
-       ((type) == MAIL_SORT_ARRIVAL || (type) == MAIL_SORT_DATE)
-
-struct sort_context {
-       enum mail_sort_type sort_program[MAX_SORT_PROGRAM_SIZE];
-
-       struct mailbox *box;
-       struct ostream *output;
-       string_t *str;
-
-       bool written;
-};
-
-int imap_sort(struct client_command_context *cmd, struct mail_search_args *args,
-             const enum mail_sort_type *sort_program)
-{
-       struct client *client = cmd->client;
-       const char *wanted_headers[2];
-       enum mail_fetch_field wanted_fields;
-       struct mail_search_context *search_ctx;
-       struct mailbox_transaction_context *t;
-       struct mailbox_header_lookup_ctx *headers_ctx;
-       struct mail *mail;
-       string_t *str;
-       bool written = FALSE;
-       int ret;
-
-       wanted_fields = 0;
-       wanted_headers[0] = wanted_headers[1] = NULL;
-       switch (*sort_program & MAIL_SORT_MASK) {
-       case MAIL_SORT_ARRIVAL:
-               wanted_fields = MAIL_FETCH_RECEIVED_DATE;
-               break;
-       case MAIL_SORT_CC:
-               wanted_headers[0] = "Cc";
-               break;
-       case MAIL_SORT_DATE:
-               wanted_fields = MAIL_FETCH_DATE;
-               break;
-       case MAIL_SORT_FROM:
-               wanted_headers[0] = "From";
-               break;
-       case MAIL_SORT_SIZE:
-               wanted_fields = MAIL_FETCH_VIRTUAL_SIZE;
-               break;
-       case MAIL_SORT_SUBJECT:
-               wanted_headers[0] = "Subject";
-               break;
-       case MAIL_SORT_TO:
-               wanted_headers[0] = "To";
-               break;
-       }
-
-       headers_ctx = wanted_headers[0] == NULL ? NULL :
-               mailbox_header_lookup_init(client->mailbox, wanted_headers);
-
-       t = mailbox_transaction_begin(client->mailbox, 0);
-       search_ctx = mailbox_search_init(t, args, sort_program);
-
-       str = t_str_new(STRBUF_SIZE);
-       str_append(str, "* SORT");
-
-       mail = mail_alloc(t, wanted_fields, headers_ctx);
-       while (mailbox_search_next(search_ctx, mail) > 0) {
-               if (str_len(str) >= STRBUF_SIZE-MAX_INT_STRLEN) {
-                       o_stream_send(client->output, str_data(str),
-                                     str_len(str));
-                       str_truncate(str, 0);
-                       written = TRUE;
-               }
-               str_printfa(str, " %u", cmd->uid ? mail->uid : mail->seq);
-       }
-       ret = mailbox_search_deinit(&search_ctx);
-       mail_free(&mail);
-
-       if (mailbox_transaction_commit(&t) < 0)
-               ret = -1;
-
-       if (written || ret == 0) {
-               str_append(str, "\r\n");
-               o_stream_send(client->output, str_data(str), str_len(str));
-       }
-
-       if (headers_ctx != NULL)
-               mailbox_header_lookup_unref(&headers_ctx);
-       return ret;
-}
diff --git a/src/imap/imap-sort.h b/src/imap/imap-sort.h
deleted file mode 100644 (file)
index 3c8e51b..0000000
+++ /dev/null
@@ -1,7 +0,0 @@
-#ifndef IMAP_SORT_H
-#define IMAP_SORT_H
-
-int imap_sort(struct client_command_context *cmd, struct mail_search_args *args,
-             const enum mail_sort_type *sort_program);
-
-#endif