]> git.ipfire.org Git - thirdparty/dovecot/core.git/commitdiff
imapc: Allow accessing a mail that is being saved without crashing.
authorTimo Sirainen <tss@iki.fi>
Thu, 22 Sep 2011 11:24:03 +0000 (14:24 +0300)
committerTimo Sirainen <tss@iki.fi>
Thu, 22 Sep 2011 11:24:03 +0000 (14:24 +0300)
This fixes crashes with LDA, LMTP, mail_log plugin, etc.

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

index a33c8085a1ade29295624a5452e5f7a59163c6be..100ea836b598bae2345b229c994e0ea5c1a2cf2a 100644 (file)
@@ -67,6 +67,12 @@ imapc_mail_send_fetch(struct mail *_mail, enum mail_fetch_field fields)
        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 @@ static void imapc_stream_filter(struct istream **input)
        *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 @@ imapc_fetch_stream(struct imapc_mail *mail,
                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 @@ imapc_fetch_stream(struct imapc_mail *mail,
                                                               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,
index 24d9d580ccc29b178fe0d1724c63b3b7f5104e48..119534da0483c0705abdd20041fb9916ecdf6ca0 100644 (file)
@@ -218,7 +218,7 @@ static void imapc_mail_set_seq(struct mail *_mail, uint32_t seq, bool saving)
        }
        /* 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);
 }
 
index bc464d35de4ffe93124c66f335254abe4c5cf522..6fae5fb7766539b7dd350dad5df8a99c7489b96a 100644 (file)
@@ -23,6 +23,7 @@ imapc_mail_alloc(struct mailbox_transaction_context *t,
                 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,
index 59bd569f81b04f2594145c78896e61045ea679cb..f5d6490c4d7d588de49e04436f341123ff85274a 100644 (file)
@@ -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 @@ struct imapc_save_context {
 
        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 @@ int imapc_save_continue(struct mail_save_context *_ctx)
 }
 
 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 @@ static void imapc_save_appenduid(struct imapc_save_context *ctx,
        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 @@ int imapc_transaction_save_commit_pre(struct mail_save_context *_ctx)
        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 @@ void imapc_transaction_save_rollback(struct mail_save_context *_ctx)
 }
 
 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 @@ static void imapc_save_copyuid(struct imapc_save_context *ctx,
        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,