From: Timo Sirainen Date: Mon, 9 Jun 2008 01:16:03 +0000 (+0300) Subject: Moved code around so that cmd-thread.c can do the THREAD reply writing. X-Git-Tag: 1.2.alpha1~367 X-Git-Url: http://git.ipfire.org/gitweb.cgi?a=commitdiff_plain;h=24e3b6aee28a8fb126e1f21348cc80acb2338518;p=thirdparty%2Fdovecot%2Fcore.git Moved code around so that cmd-thread.c can do the THREAD reply writing. --HG-- branch : HEAD --- diff --git a/src/imap/cmd-thread.c b/src/imap/cmd-thread.c index 3a1f00695a..dba853e77d 100644 --- a/src/imap/cmd-thread.c +++ b/src/imap/cmd-thread.c @@ -1,15 +1,120 @@ /* 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, diff --git a/src/imap/imap-thread-finish.c b/src/imap/imap-thread-finish.c index 4f9ba7aa82..e83664bd00 100644 --- a/src/imap/imap-thread-finish.c +++ b/src/imap/imap-thread-finish.c @@ -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); diff --git a/src/imap/imap-thread-private.h b/src/imap/imap-thread-private.h index 5b5d3a6633..70369137cf 100644 --- a/src/imap/imap-thread-private.h +++ b/src/imap/imap-thread-private.h @@ -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 diff --git a/src/imap/imap-thread.c b/src/imap/imap-thread.c index 13c44f39cf..ad8aa6ac32 100644 --- a/src/imap/imap-thread.c +++ b/src/imap/imap-thread.c @@ -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" @@ -24,21 +22,14 @@ #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; diff --git a/src/imap/imap-thread.h b/src/imap/imap-thread.h index 0a1f4530ad..f582e97fa0 100644 --- a/src/imap/imap-thread.h +++ b/src/imap/imap-thread.h @@ -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 diff --git a/src/imap/main.c b/src/imap/main.c index da2af89b61..413c77e297 100644 --- a/src/imap/main.c +++ b/src/imap/main.c @@ -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);