Mercurial > dovecot > core-2.2
changeset 13542:b6633cb57814
imapc: Allow accessing a mail that is being saved without crashing.
This fixes crashes with LDA, LMTP, mail_log plugin, etc.
author | Timo Sirainen <tss@iki.fi> |
---|---|
date | Thu, 22 Sep 2011 14:24:03 +0300 |
parents | 645a56f9d8ee |
children | 43df6495edb8 |
files | src/lib-storage/index/imapc/imapc-mail-fetch.c src/lib-storage/index/imapc/imapc-mail.c src/lib-storage/index/imapc/imapc-mail.h src/lib-storage/index/imapc/imapc-save.c |
diffstat | 4 files changed, 101 insertions(+), 38 deletions(-) [+] |
line wrap: on
line diff
--- a/src/lib-storage/index/imapc/imapc-mail-fetch.c Thu Sep 22 14:21:56 2011 +0300 +++ b/src/lib-storage/index/imapc/imapc-mail-fetch.c Thu Sep 22 14:24:03 2011 +0300 @@ -67,6 +67,12 @@ if (fields == 0) return 0; + if (_mail->saving) { + mail_storage_set_critical(_mail->box->storage, + "Can't fetch data from uncommitted message"); + return -1; + } + /* if we already know that the mail is expunged, don't try to FETCH it */ view = mbox->delayed_sync_view != NULL ? @@ -200,17 +206,50 @@ *input = filter_input; } +void imapc_mail_init_stream(struct imapc_mail *mail, bool have_body) +{ + struct index_mail *imail = &mail->imail; + struct mail *_mail = &imail->mail.mail; + struct istream *input; + uoff_t size; + int ret; + + i_stream_set_name(imail->data.stream, + t_strdup_printf("imapc mail uid=%u", _mail->uid)); + index_mail_set_read_buffer_size(_mail, imail->data.stream); + + imapc_stream_filter(&imail->data.stream); + if (imail->mail.v.istream_opened != NULL) { + if (imail->mail.v.istream_opened(_mail, + &imail->data.stream) < 0) { + i_stream_unref(&imail->data.stream); + return; + } + } else if (have_body) { + ret = i_stream_get_size(imail->data.stream, TRUE, &size); + if (ret < 0) { + i_stream_unref(&imail->data.stream); + return; + } + i_assert(ret != 0); + imail->data.physical_size = size; + /* we'll assume that the remote server is working properly and + sending CRLF linefeeds */ + imail->data.virtual_size = size; + } + + if (index_mail_init_stream(imail, NULL, NULL, &input) < 0) + i_stream_unref(&imail->data.stream); +} + static void imapc_fetch_stream(struct imapc_mail *mail, const struct imapc_untagged_reply *reply, const struct imap_arg *arg, bool body) { struct index_mail *imail = &mail->imail; - struct mail *_mail = &imail->mail.mail; - struct istream *input; - uoff_t size; const char *value; - int fd, ret; + int fd; if (imail->data.stream != NULL) { if (!body) @@ -231,7 +270,7 @@ if (!imap_arg_get_nstring(arg, &value)) return; if (value == NULL) { - mail_set_expunged(_mail); + mail_set_expunged(&imail->mail.mail); return; } if (mail->body == NULL) { @@ -244,32 +283,7 @@ mail->body->used); } - i_stream_set_name(imail->data.stream, - t_strdup_printf("imapc mail uid=%u", _mail->uid)); - index_mail_set_read_buffer_size(_mail, imail->data.stream); - - imapc_stream_filter(&imail->data.stream); - if (imail->mail.v.istream_opened != NULL) { - if (imail->mail.v.istream_opened(_mail, - &imail->data.stream) < 0) { - i_stream_unref(&imail->data.stream); - return; - } - } else if (body) { - ret = i_stream_get_size(imail->data.stream, TRUE, &size); - if (ret < 0) { - i_stream_unref(&imail->data.stream); - return; - } - i_assert(ret != 0); - imail->data.physical_size = size; - /* we'll assume that the remote server is working properly and - sending CRLF linefeeds */ - imail->data.virtual_size = size; - } - - if (index_mail_init_stream(imail, NULL, NULL, &input) < 0) - i_stream_unref(&imail->data.stream); + imapc_mail_init_stream(mail, body); } void imapc_mail_fetch_update(struct imapc_mail *mail,
--- a/src/lib-storage/index/imapc/imapc-mail.c Thu Sep 22 14:21:56 2011 +0300 +++ b/src/lib-storage/index/imapc/imapc-mail.c Thu Sep 22 14:24:03 2011 +0300 @@ -218,7 +218,7 @@ } /* searching code handles prefetching internally, elsewhere we want to do it immediately */ - if (!mail->search_mail) + if (!mail->search_mail && !_mail->saving) (void)imapc_mail_prefetch(_mail); }
--- a/src/lib-storage/index/imapc/imapc-mail.h Thu Sep 22 14:21:56 2011 +0300 +++ b/src/lib-storage/index/imapc/imapc-mail.h Thu Sep 22 14:24:03 2011 +0300 @@ -23,6 +23,7 @@ struct mailbox_header_lookup_ctx *wanted_headers); int imapc_mail_fetch(struct mail *mail, enum mail_fetch_field fields); bool imapc_mail_prefetch(struct mail *mail); +void imapc_mail_init_stream(struct imapc_mail *mail, bool have_body); void imapc_mail_fetch_update(struct imapc_mail *mail, const struct imapc_untagged_reply *reply,
--- a/src/lib-storage/index/imapc/imapc-save.c Thu Sep 22 14:21:56 2011 +0300 +++ b/src/lib-storage/index/imapc/imapc-save.c Thu Sep 22 14:24:03 2011 +0300 @@ -12,6 +12,7 @@ #include "imapc-client.h" #include "imapc-storage.h" #include "imapc-sync.h" +#include "imapc-mail.h" struct imapc_save_context { struct mail_save_context ctx; @@ -25,6 +26,7 @@ uint32_t dest_uid_validity; ARRAY_TYPE(seq_range) dest_saved_uids; + unsigned int save_count; unsigned int failed:1; unsigned int finished:1; @@ -103,11 +105,14 @@ } static void imapc_save_appenduid(struct imapc_save_context *ctx, - const struct imapc_command_reply *reply) + const struct imapc_command_reply *reply, + uint32_t *uid_r) { const char *const *args; uint32_t uid_validity, dest_uid; + *uid_r = 0; + /* <uidvalidity> <dest uid-set> */ args = t_strsplit(reply->resp_text_value, " "); if (str_array_length(args) != 2) @@ -120,18 +125,48 @@ else if (ctx->dest_uid_validity != uid_validity) return; - if (str_to_uint32(args[1], &dest_uid) == 0) + if (str_to_uint32(args[1], &dest_uid) == 0) { seq_range_array_add(&ctx->dest_saved_uids, 0, dest_uid); + *uid_r = dest_uid; + } +} + +static void +imapc_save_add_to_index(struct imapc_save_context *ctx, uint32_t uid) +{ + struct mail *_mail = ctx->ctx.dest_mail; + struct index_mail *imail = (struct index_mail *)_mail; + uint32_t seq; + + if (_mail == NULL) + return; + + /* we'll temporarily append messages and at commit time expunge + them all, since we can't guarantee that no one else has saved + messages to remote server during our transaction */ + mail_index_append(ctx->trans, uid, &seq); + mail_set_seq_saving(_mail, seq); + imail->data.forced_no_caching = TRUE; + + if (ctx->fd != -1) { + imail->data.stream = i_stream_create_fd(ctx->fd, 0, TRUE); + imapc_mail_init_stream((struct imapc_mail *)imail, TRUE); + ctx->fd = -1; + } + + ctx->save_count++; } static void imapc_save_callback(const struct imapc_command_reply *reply, void *context) { struct imapc_save_cmd_context *ctx = context; + uint32_t uid = 0; if (reply->state == IMAPC_COMMAND_STATE_OK) { if (strcasecmp(reply->resp_text_key, "APPENDUID") == 0) - imapc_save_appenduid(ctx->ctx, reply); + imapc_save_appenduid(ctx->ctx, reply, &uid); + imapc_save_add_to_index(ctx->ctx, uid); ctx->ret = 0; } else if (reply->state == IMAPC_COMMAND_STATE_NO) { imapc_copy_error_from_reply(ctx->ctx->mbox->storage, @@ -245,9 +280,15 @@ struct imapc_save_context *ctx = (struct imapc_save_context *)_ctx; struct mail_transaction_commit_changes *changes = _ctx->transaction->changes; + uint32_t i, last_seq; i_assert(ctx->finished); + /* expunge all added messages from index before commit */ + last_seq = mail_index_view_get_messages_count(ctx->trans->view); + for (i = 0; i < ctx->save_count; i++) + mail_index_expunge(ctx->trans, last_seq - i); + if (array_is_created(&ctx->dest_saved_uids)) { changes->uid_validity = ctx->dest_uid_validity; array_append_array(&changes->saved_uids, &ctx->dest_saved_uids); @@ -277,11 +318,14 @@ } static void imapc_save_copyuid(struct imapc_save_context *ctx, - const struct imapc_command_reply *reply) + const struct imapc_command_reply *reply, + uint32_t *uid_r) { const char *const *args; uint32_t uid_validity, dest_uid; + *uid_r = 0; + /* <uidvalidity> <source uid-set> <dest uid-set> */ args = t_strsplit(reply->resp_text_value, " "); if (str_array_length(args) != 3) @@ -294,18 +338,22 @@ else if (ctx->dest_uid_validity != uid_validity) return; - if (str_to_uint32(args[2], &dest_uid) == 0) + if (str_to_uint32(args[2], &dest_uid) == 0) { seq_range_array_add(&ctx->dest_saved_uids, 0, dest_uid); + *uid_r = dest_uid; + } } static void imapc_copy_callback(const struct imapc_command_reply *reply, void *context) { struct imapc_save_cmd_context *ctx = context; + uint32_t uid = 0; if (reply->state == IMAPC_COMMAND_STATE_OK) { if (strcasecmp(reply->resp_text_key, "COPYUID") == 0) - imapc_save_copyuid(ctx->ctx, reply); + imapc_save_copyuid(ctx->ctx, reply, &uid); + imapc_save_add_to_index(ctx->ctx, uid); ctx->ret = 0; } else if (reply->state == IMAPC_COMMAND_STATE_NO) { imapc_copy_error_from_reply(ctx->ctx->mbox->storage,