]> git.ipfire.org Git - thirdparty/dovecot/core.git/commitdiff
Moved code around so that cmd-thread.c can do the THREAD reply writing.
authorTimo Sirainen <tss@iki.fi>
Mon, 9 Jun 2008 01:16:03 +0000 (04:16 +0300)
committerTimo Sirainen <tss@iki.fi>
Mon, 9 Jun 2008 01:16:03 +0000 (04:16 +0300)
--HG--
branch : HEAD

src/imap/cmd-thread.c
src/imap/imap-thread-finish.c
src/imap/imap-thread-private.h
src/imap/imap-thread.c
src/imap/imap-thread.h
src/imap/main.c

index 3a1f00695a9da3281f99abe968b5eb2af50ffecf..dba853e77d7c43b3e0e117d2af7d24a6733d6798 100644 (file)
 /* Copyright (c) 2002-2008 Dovecot authors, see the included COPYING file */
 
 #include "common.h"
-#include "buffer.h"
+#include "str.h"
+#include "ostream.h"
 #include "commands.h"
 #include "imap-search.h"
 #include "imap-thread.h"
 
+static int imap_thread_write(struct mail_thread_iterate_context *iter,
+                            string_t *str, bool root)
+{
+       const struct mail_thread_child_node *node;
+       struct mail_thread_iterate_context *child_iter;
+       unsigned int count;
+       int ret;
+
+       count = mail_thread_iterate_count(iter);
+       if (count == 0)
+               return 0;
+
+       if (count == 1 && !root) {
+               /* only one child - special case to avoid extra paranthesis */
+               node = mail_thread_iterate_next(iter, &child_iter);
+               str_printfa(str, "%u", node->uid);
+               if (child_iter != NULL) {
+                       str_append_c(str, ' ');
+                       T_BEGIN {
+                               ret = imap_thread_write(child_iter, str, FALSE);
+                       } T_END;
+                       if (mail_thread_iterate_deinit(&child_iter) < 0)
+                               return -1;
+               }
+               return ret;
+       }
+
+       while ((node = mail_thread_iterate_next(iter, &child_iter)) != NULL) {
+               if (child_iter == NULL) {
+                       /* no children */
+                       str_printfa(str, "(%u)", node->uid);
+               } else {
+                       /* node with children */
+                       str_append_c(str, '(');
+                       if (node->uid != 0)
+                               str_printfa(str, "%u ", node->uid);
+                       T_BEGIN {
+                               ret = imap_thread_write(child_iter, str, FALSE);
+                       } T_END;
+                       if (mail_thread_iterate_deinit(&child_iter) < 0 ||
+                           ret < 0)
+                               return -1;
+                       str_append_c(str, ')');
+               }
+       }
+       return 0;
+}
+
+static int
+imap_thread_write_reply(struct imap_thread_context *ctx, string_t *str,
+                       enum mail_thread_type thread_type, bool write_seqs)
+{
+       struct mail_thread_iterate_context *iter;
+       int ret;
+
+       iter = imap_thread_iterate_init(ctx, thread_type, write_seqs);
+       str_append(str, "* THREAD ");
+       T_BEGIN {
+               ret = imap_thread_write(iter, str, TRUE);
+       } T_END;
+       if (mail_thread_iterate_deinit(&iter) < 0)
+               ret = -1;
+
+       str_append(str, "\r\n");
+       return ret;
+}
+
+static int imap_thread(struct client_command_context *cmd,
+                      struct mail_search_args *search_args,
+                      enum mail_thread_type thread_type)
+{
+       struct imap_thread_context *ctx;
+       string_t *str;
+       bool reset = FALSE;
+       int ret;
+
+       i_assert(thread_type == MAIL_THREAD_REFERENCES ||
+                thread_type == MAIL_THREAD_REFERENCES2);
+
+       str = str_new(default_pool, 1024);
+       for (;;) {
+               ret = imap_thread_init(cmd->client->mailbox, reset,
+                                      search_args, &ctx);
+               if (ret == 0) {
+                       ret = imap_thread_write_reply(ctx, str, thread_type,
+                                                     !cmd->uid);
+                       imap_thread_deinit(&ctx);
+               }
+
+               if (ret == 0 || reset)
+                       break;
+               /* try again with in-memory hash */
+               reset = TRUE;
+               str_truncate(str, 0);
+       }
+
+       if (ret == 0) {
+               (void)o_stream_send(cmd->client->output,
+                                   str_data(str), str_len(str));
+       }
+       str_free(&str);
+       return ret;
+}
+
 bool cmd_thread(struct client_command_context *cmd)
 {
        struct client *client = cmd->client;
-       enum mail_thread_type threading;
+       enum mail_thread_type thread_type;
        struct mail_search_args *sargs;
        const struct imap_arg *args;
        int ret, args_count;
@@ -37,9 +142,9 @@ bool cmd_thread(struct client_command_context *cmd)
 
        str = IMAP_ARG_STR(args);
        if (strcasecmp(str, "REFERENCES") == 0)
-               threading = MAIL_THREAD_REFERENCES;
+               thread_type = MAIL_THREAD_REFERENCES;
        else if (strcasecmp(str, "X-REFERENCES2") == 0)
-               threading = MAIL_THREAD_REFERENCES2;
+               thread_type = MAIL_THREAD_REFERENCES2;
        else if (strcasecmp(str, "ORDEREDSUBJECT") == 0) {
                client_send_command_error(cmd,
                        "ORDEREDSUBJECT threading is currently not supported.");
@@ -63,8 +168,7 @@ bool cmd_thread(struct client_command_context *cmd)
        if (ret <= 0)
                return ret < 0;
 
-       ret = imap_thread(client->mailbox, cmd->uid, client->output,
-                         sargs, threading);
+       ret = imap_thread(cmd, sargs, thread_type);
        mail_search_args_unref(&sargs);
        if (ret < 0) {
                client_send_storage_error(cmd,
index 4f9ba7aa82d82ab2e6db35638bbcd8641cdca11a..e83664bd005cf5aed25dad603b460d757ce91ed2 100644 (file)
@@ -2,6 +2,7 @@
 
 #include "common.h"
 #include "array.h"
+#include "mail-storage-private.h"
 #include "imap-base-subject.h"
 #include "imap-thread-private.h"
 
@@ -42,6 +43,7 @@ struct thread_finish_context {
        unsigned int next_new_root_idx;
 
        unsigned int use_sent_date:1;
+       unsigned int return_seqs:1;
 };
 
 struct mail_thread_iterate_context {
@@ -582,6 +584,36 @@ static int mail_thread_finish(struct thread_finish_context *ctx,
        return 0;
 }
 
+static void
+nodes_change_uids_to_seqs(struct mail_thread_iterate_context *iter, bool root)
+{
+       struct mail_thread_child_node *children;
+       struct mailbox *box;
+       unsigned int i, count;
+       uint32_t uid, seq;
+
+       box = mailbox_transaction_get_mailbox(iter->ctx->tmp_mail->transaction);
+       children = array_get_modifiable(&iter->children, &count);
+       for (i = 0; i < count; i++) {
+               uid = children[i].uid;
+               if (uid == 0) {
+                       /* dummy root */
+                       if (root)
+                               continue;
+               } else {
+                       mailbox_get_seq_range(box, uid, uid, &seq, &seq);
+               }
+               if (uid == 0) {
+                       mail_hash_transaction_set_corrupted(
+                               iter->ctx->hash_trans,
+                               t_strdup_printf("Found expunged UID %u", uid));
+                       iter->failed = TRUE;
+                       seq = 0;
+               }
+               children[i].uid = seq;
+       }
+}
+
 static void
 mail_thread_iterate_fill_root(struct mail_thread_iterate_context *iter)
 {
@@ -614,6 +646,9 @@ mail_thread_iterate_children(struct mail_thread_iterate_context *parent_iter,
                                 &child_iter->children) < 0) {
                child_iter->failed = TRUE;
                array_clear(&child_iter->children);
+       } else {
+               if (child_iter->ctx->return_seqs)
+                       nodes_change_uids_to_seqs(child_iter, FALSE);
        }
        return child_iter;
 }
@@ -621,7 +656,7 @@ mail_thread_iterate_children(struct mail_thread_iterate_context *parent_iter,
 struct mail_thread_iterate_context *
 mail_thread_iterate_init(struct mail *tmp_mail,
                         struct mail_hash_transaction *hash_trans,
-                        enum mail_thread_type thread_type)
+                        enum mail_thread_type thread_type, bool return_seqs)
 {
        struct mail_thread_iterate_context *iter;
        struct thread_finish_context *ctx;
@@ -631,10 +666,14 @@ mail_thread_iterate_init(struct mail *tmp_mail,
        ctx->refcount = 1;
        ctx->tmp_mail = tmp_mail;
        ctx->hash_trans = hash_trans;
+       ctx->return_seqs = return_seqs;
        if (mail_thread_finish(ctx, thread_type) < 0)
                iter->failed = TRUE;
-       else
+       else {
                mail_thread_iterate_fill_root(iter);
+               if (return_seqs)
+                       nodes_change_uids_to_seqs(iter, TRUE);
+       }
        return iter;
 }
 
@@ -670,6 +709,14 @@ int mail_thread_iterate_deinit(struct mail_thread_iterate_context **_iter)
        int ret = iter->failed ? -1 : 0;
 
        *_iter = NULL;
+
+       if (ret < 0) {
+               struct mail *mail = iter->ctx->tmp_mail;
+               struct mailbox *box;
+
+               box = mailbox_transaction_get_mailbox(mail->transaction);
+               mail_storage_set_internal_error(box->storage);
+       }
        if (--iter->ctx->refcount == 0) {
                array_free(&iter->ctx->roots);
                array_free(&iter->ctx->shadow_nodes);
index 5b5d3a663346cf0b26ec1d14c6681f22f24172d0..70369137cff49c96dcbead7b2bc02e7118795415 100644 (file)
@@ -32,13 +32,6 @@ struct mail_thread_node {
        uint32_t exists:1;
 };
 
-struct mail_thread_child_node {
-       uint32_t idx;
-       uint32_t uid;
-       time_t sort_date;
-};
-ARRAY_DEFINE_TYPE(mail_thread_child_node, struct mail_thread_child_node);
-
 struct thread_context {
        struct mail *tmp_mail;
 
@@ -69,12 +62,6 @@ int mail_thread_remove(struct thread_context *ctx, uint32_t seq);
 struct mail_thread_iterate_context *
 mail_thread_iterate_init(struct mail *tmp_mail,
                         struct mail_hash_transaction *hash_trans,
-                        enum mail_thread_type thread_type);
-const struct mail_thread_child_node *
-mail_thread_iterate_next(struct mail_thread_iterate_context *iter,
-                        struct mail_thread_iterate_context **child_iter_r);
-unsigned int
-mail_thread_iterate_count(struct mail_thread_iterate_context *iter);
-int mail_thread_iterate_deinit(struct mail_thread_iterate_context **iter);
+                        enum mail_thread_type thread_type, bool return_seqs);
 
 #endif
index 13c44f39cf6b1ebbf3f08b487c4e8547f119e752..ad8aa6ac32d9ad1e37e3f8ac2dafa6aadb288bbe 100644 (file)
@@ -4,8 +4,6 @@
 
 #include "common.h"
 #include "array.h"
-#include "ostream.h"
-#include "str.h"
 #include "message-id.h"
 #include "mail-search.h"
 #include "imap-thread-private.h"
 #define APPROX_MSG_EXTRA_COUNT 10
 #define APPROX_MSGID_SIZE 45
 
-#define STR_FLUSH_LENGTH 512
-
 struct imap_thread_context {
-       struct mailbox *box;
-       struct ostream *output;
        struct thread_context thread_ctx;
+
+       struct mailbox *box;
        struct mailbox_transaction_context *t;
-       enum mail_thread_type thread_type;
 
        struct mail_search_context *search;
        struct mail_search_arg tmp_search_arg;
-       string_t *str;
-
-       unsigned int id_is_uid:1;
-       unsigned int flushed:1;
 };
 
 struct imap_thread_mailbox {
@@ -317,8 +308,8 @@ again:
 }
 
 static void
-imap_thread_context_init(struct imap_thread_context *ctx,
-                        struct mail_search_args *search_args, bool reset)
+imap_thread_update_init(struct imap_thread_context *ctx,
+                       struct mail_search_args *search_args, bool reset)
 {
        struct imap_thread_mailbox *tbox = IMAP_THREAD_CONTEXT(ctx->box);
        struct mail_hash *hash = NULL;
@@ -365,119 +356,26 @@ imap_thread_context_init(struct imap_thread_context *ctx,
                     I_MAX(hdr->record_count, status.messages));
 }
 
-static int imap_thread_finish(struct imap_thread_mailbox *tbox,
-                             struct imap_thread_context *ctx)
-{
-       int ret;
-
-       mail_hash_transaction_end(&ctx->thread_ctx.hash_trans);
-
-       ret = mailbox_search_deinit(&ctx->search);
-       mail_free(&ctx->thread_ctx.tmp_mail);
-       if (mailbox_transaction_commit(&ctx->t) < 0)
-               ret = -1;
-
-       mail_hash_unlock(ctx->thread_ctx.hash);
-       if (ctx->thread_ctx.hash != tbox->hash)
-               mail_hash_free(&ctx->thread_ctx.hash);
-
-       array_free(&ctx->thread_ctx.msgid_cache);
-       pool_unref(&ctx->thread_ctx.msgid_pool);
-       return ret;
-}
-
-static int str_add_id(struct imap_thread_context *ctx, uint32_t uid)
-{
-       i_assert(uid != 0);
-
-       if (!ctx->id_is_uid) {
-               mailbox_get_seq_range(ctx->box, uid, uid, &uid, &uid);
-               if (uid == 0) {
-                       mail_hash_transaction_set_corrupted(
-                               ctx->thread_ctx.hash_trans,
-                               t_strdup_printf("Found expunged UID %u", uid));
-                       return -1;
-               }
-       }
-       str_printfa(ctx->str, "%u", uid);
-
-       if (str_len(ctx->str) >= STR_FLUSH_LENGTH) {
-               (void)o_stream_send(ctx->output, str_data(ctx->str),
-                                   str_len(ctx->str));
-               str_truncate(ctx->str, 0);
-               ctx->flushed = TRUE;
-       }
-       return 0;
-}
-
-static int imap_thread_send(struct imap_thread_context *ctx,
-                           struct mail_thread_iterate_context *iter, bool root)
-{
-       const struct mail_thread_child_node *node;
-       struct mail_thread_iterate_context *child_iter;
-       string_t *str = ctx->str;
-       unsigned int count;
-       int ret;
-
-       count = mail_thread_iterate_count(iter);
-       if (count == 0)
-               return 0;
-
-       if (count == 1 && !root) {
-               /* only one child - special case to avoid extra paranthesis */
-               node = mail_thread_iterate_next(iter, &child_iter);
-               ret = str_add_id(ctx, node->uid);
-               if (child_iter != NULL) {
-                       if (ret == 0) T_BEGIN {
-                               str_append_c(str, ' ');
-                               ret = imap_thread_send(ctx, child_iter, FALSE);
-                       } T_END;
-                       mail_thread_iterate_deinit(&child_iter);
-               }
-               return ret;
-       }
-
-       while ((node = mail_thread_iterate_next(iter, &child_iter)) != NULL) {
-               if (child_iter == NULL) {
-                       /* no children */
-                       str_append_c(str, '(');
-                       if (str_add_id(ctx, node->uid) < 0)
-                               return -1;
-                       str_append_c(str, ')');
-               } else {
-                       /* node with children */
-                       str_append_c(str, '(');
-                       if (node->uid != 0) {
-                               ret = str_add_id(ctx, node->uid);
-                               str_append_c(str, ' ');
-                       }
-                       if (ret == 0) T_BEGIN {
-                               ret = imap_thread_send(ctx, child_iter, FALSE);
-                       } T_END;
-                       mail_thread_iterate_deinit(&child_iter);
-                       if (ret < 0)
-                               return -1;
-                       str_append_c(str, ')');
-               }
-       }
-       return 0;
-}
 
-static int imap_thread_run(struct imap_thread_context *ctx)
+static int
+imap_thread_update(struct imap_thread_context *ctx,
+                  struct mail_search_args *search_args, bool reset)
 {
        static const char *wanted_headers[] = {
                HDR_MESSAGE_ID, HDR_IN_REPLY_TO, HDR_REFERENCES, HDR_SUBJECT,
                NULL
        };
-       struct mailbox *box = mailbox_transaction_get_mailbox(ctx->t);
+       struct mailbox *box;
        struct mailbox_header_lookup_ctx *headers_ctx;
        struct mail_hash_header *hdr;
        struct mail *mail;
-       struct mail_thread_iterate_context *iter;
        bool changed = FALSE;
        uint32_t prev_uid;
        int ret = 0;
 
+       imap_thread_update_init(ctx, search_args, reset);
+       box = mailbox_transaction_get_mailbox(ctx->t);
+
        hdr = mail_hash_get_header(ctx->thread_ctx.hash_trans);
        prev_uid = hdr->last_uid;
 
@@ -504,66 +402,73 @@ static int imap_thread_run(struct imap_thread_context *ctx)
                   building */
                (void)mail_hash_transaction_write(ctx->thread_ctx.hash_trans);
        }
-
-       iter = mail_thread_iterate_init(ctx->thread_ctx.tmp_mail,
-                                       ctx->thread_ctx.hash_trans,
-                                       ctx->thread_type);
-       ctx->str = str_new(default_pool, STR_FLUSH_LENGTH + 32);
-       str_append(ctx->str, "* THREAD ");
-       T_BEGIN {
-               ret = imap_thread_send(ctx, iter, TRUE);
-       } T_END;
-       if (mail_thread_iterate_deinit(&iter) < 0)
-               ret = -1;
-
-       if (ret == 0) {
-               str_append(ctx->str, "\r\n");
-               (void)o_stream_send(ctx->output, str_data(ctx->str),
-                                   str_len(ctx->str));
-       } else if (ctx->flushed) {
-               o_stream_close(ctx->output);
-       }
-       str_free(&ctx->str);
-       return ret;
+       return 0;
 }
 
-int imap_thread(struct mailbox *box, bool id_is_uid, struct ostream *output,
-               struct mail_search_args *args, enum mail_thread_type type)
+int imap_thread_init(struct mailbox *box, bool reset,
+                    struct mail_search_args *args,
+                    struct imap_thread_context **ctx_r)
 {
        struct imap_thread_mailbox *tbox = IMAP_THREAD_CONTEXT(box);
        struct imap_thread_context *ctx;
-       int ret, try;
+       int ret;
 
        i_assert(tbox->ctx == NULL);
-       i_assert(type == MAIL_THREAD_REFERENCES ||
-                type == MAIL_THREAD_REFERENCES2);
 
-       ctx = t_new(struct imap_thread_context, 1);
+       ctx = i_new(struct imap_thread_context, 1);
        tbox->ctx = &ctx->thread_ctx;
+       ctx->box = box;
 
-       for (try = 0; try < 2; try++) {
-               ctx->box = box;
-               ctx->output = output;
-               ctx->id_is_uid = id_is_uid;
-               ctx->thread_type = type;
-               imap_thread_context_init(ctx, args, try == 1);
-               ret = imap_thread_run(ctx);
-               if (imap_thread_finish(tbox, ctx) < 0)
-                       ret = -1;
-
-               if (ret < 0 && ctx->thread_ctx.hash == tbox->hash) {
-                       /* try again with in-memory hash */
-                       memset(ctx, 0, sizeof(*ctx));
-               } else {
-                       break;
+       while ((ret = imap_thread_update(ctx, args, reset)) < 0) {
+               if (ctx->thread_ctx.hash == tbox->hash) {
+                       /* failed with in-memory hash */
+                       imap_thread_deinit(&ctx);
+                       return -1;
                }
+
+               /* try again with in-memory hash */
+               imap_thread_deinit(&ctx);
+               reset = TRUE;
+               memset(ctx, 0, sizeof(*ctx));
+               ctx->box = box;
        }
-       if (ret < 0)
-               mail_storage_set_internal_error(box->storage);
 
-       i_assert(!tbox->ctx->syncing);
-       tbox->ctx = NULL;
-       return ret;
+       *ctx_r = ctx;
+       return 0;
+}
+
+struct mail_thread_iterate_context *
+imap_thread_iterate_init(struct imap_thread_context *ctx,
+                        enum mail_thread_type thread_type, bool write_seqs)
+{
+       return mail_thread_iterate_init(ctx->thread_ctx.tmp_mail,
+                                       ctx->thread_ctx.hash_trans,
+                                       thread_type, write_seqs);
+}
+
+void imap_thread_deinit(struct imap_thread_context **_ctx)
+{
+       struct imap_thread_context *ctx = *_ctx;
+       struct imap_thread_mailbox *tbox = IMAP_THREAD_CONTEXT(ctx->box);
+       int ret;
+
+       *_ctx = NULL;
+
+       mail_hash_transaction_end(&ctx->thread_ctx.hash_trans);
+
+       ret = mailbox_search_deinit(&ctx->search);
+       mail_free(&ctx->thread_ctx.tmp_mail);
+       (void)mailbox_transaction_commit(&ctx->t);
+
+       mail_hash_unlock(ctx->thread_ctx.hash);
+       if (ctx->thread_ctx.hash != tbox->hash)
+               mail_hash_free(&ctx->thread_ctx.hash);
+
+       array_free(&ctx->thread_ctx.msgid_cache);
+       pool_unref(&ctx->thread_ctx.msgid_pool);
+       i_assert(!tbox->ctx->syncing);
+       tbox->ctx = NULL;
 }
 
 static bool imap_thread_index_is_up_to_date(struct thread_context *ctx,
@@ -704,13 +609,13 @@ static void imap_thread_mailbox_index_opened(struct mailbox *box)
        MODULE_CONTEXT_SET(box, imap_thread_storage_module, tbox);
 }
 
-void imap_thread_init(void)
+void imap_threads_init(void)
 {
        next_hook_mailbox_index_opened = hook_mailbox_index_opened;
        hook_mailbox_index_opened = imap_thread_mailbox_index_opened;
 }
 
-void imap_thread_deinit(void)
+void imap_threads_deinit(void)
 {
        i_assert(hook_mailbox_index_opened == imap_thread_mailbox_index_opened);
        hook_mailbox_index_opened = next_hook_mailbox_index_opened;
index 0a1f4530adcab12fbc4a5d0e8325de59d39877c5..f582e97fa05425934315589b84080e16fc2ea5bc 100644 (file)
@@ -1,6 +1,8 @@
 #ifndef IMAP_THREAD_H
 #define IMAP_THREAD_H
 
+struct imap_thread_context;
+
 enum mail_thread_type {
        MAIL_THREAD_NONE,
        MAIL_THREAD_ORDEREDSUBJECT,
@@ -8,10 +10,29 @@ enum mail_thread_type {
        MAIL_THREAD_REFERENCES2
 };
 
-int imap_thread(struct mailbox *box, bool id_is_uid, struct ostream *output,
-               struct mail_search_args *args, enum mail_thread_type type);
+struct mail_thread_child_node {
+       uint32_t idx;
+       uint32_t uid;
+       time_t sort_date;
+};
+ARRAY_DEFINE_TYPE(mail_thread_child_node, struct mail_thread_child_node);
+
+int imap_thread_init(struct mailbox *box, bool reset,
+                    struct mail_search_args *args,
+                    struct imap_thread_context **ctx_r);
+void imap_thread_deinit(struct imap_thread_context **ctx);
+
+struct mail_thread_iterate_context *
+imap_thread_iterate_init(struct imap_thread_context *ctx,
+                        enum mail_thread_type thread_type, bool write_seqs);
+const struct mail_thread_child_node *
+mail_thread_iterate_next(struct mail_thread_iterate_context *iter,
+                        struct mail_thread_iterate_context **child_iter_r);
+unsigned int
+mail_thread_iterate_count(struct mail_thread_iterate_context *iter);
+int mail_thread_iterate_deinit(struct mail_thread_iterate_context **iter);
 
-void imap_thread_init(void);
-void imap_thread_deinit(void);
+void imap_threads_init(void);
+void imap_threads_deinit(void);
 
 #endif
index da2af89b611897b32ecfc598a1c4a028627510a8..413c77e297926538f7a9ddf6d563099b79fb9a36 100644 (file)
@@ -204,7 +204,7 @@ static void main_init(void)
        mailbox_list_register_all();
        clients_init();
        commands_init();
-       imap_thread_init();
+       imap_threads_init();
 
        module_dir_init(modules);
 
@@ -257,7 +257,7 @@ static void main_deinit(void)
        clients_deinit();
 
        module_dir_unload(&modules);
-       imap_thread_deinit();
+       imap_threads_deinit();
        commands_deinit();
         mail_storage_deinit();
        dict_driver_unregister(&dict_driver_client);