dovecot-2.1: imapc: Allow accessing a mail that is being saved w...

dovecot at dovecot.org dovecot at dovecot.org
Thu Sep 22 14:24:16 EEST 2011


details:   http://hg.dovecot.org/dovecot-2.1/rev/b6633cb57814
changeset: 13542:b6633cb57814
user:      Timo Sirainen <tss at iki.fi>
date:      Thu Sep 22 14:24:03 2011 +0300
description:
imapc: Allow accessing a mail that is being saved without crashing.
This fixes crashes with LDA, LMTP, mail_log plugin, etc.

diffstat:

 src/lib-storage/index/imapc/imapc-mail-fetch.c |  76 +++++++++++++++----------
 src/lib-storage/index/imapc/imapc-mail.c       |   2 +-
 src/lib-storage/index/imapc/imapc-mail.h       |   1 +
 src/lib-storage/index/imapc/imapc-save.c       |  60 ++++++++++++++++++--
 4 files changed, 101 insertions(+), 38 deletions(-)

diffs (280 lines):

diff -r 645a56f9d8ee -r b6633cb57814 src/lib-storage/index/imapc/imapc-mail-fetch.c
--- 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,
diff -r 645a56f9d8ee -r b6633cb57814 src/lib-storage/index/imapc/imapc-mail.c
--- 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);
 }
 
diff -r 645a56f9d8ee -r b6633cb57814 src/lib-storage/index/imapc/imapc-mail.h
--- 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,
diff -r 645a56f9d8ee -r b6633cb57814 src/lib-storage/index/imapc/imapc-save.c
--- 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,


More information about the dovecot-cvs mailing list