dovecot-2.2: imapc: Added imapc_features=zimbra-workarounds

dovecot at dovecot.org dovecot at dovecot.org
Wed Apr 15 18:02:44 UTC 2015


details:   http://hg.dovecot.org/dovecot-2.2/rev/1c17f06d5e52
changeset: 18404:1c17f06d5e52
user:      Timo Sirainen <tss at iki.fi>
date:      Wed Apr 15 21:01:11 2015 +0300
description:
imapc: Added imapc_features=zimbra-workarounds
Zimbra (at least v5.0.18_GA_3011.RHEL4_64) can return different headers
depending on whether the whole message body was fetched or only (partial)
headers. This probably happens only for invalid characters that are
translated into '?'. With this workaround enabled we don't use FETCH
BODY.PEEK[], but we do FETCH (BODY.PEEK[HEADER] BODY.PEEK[TEXT]) and merge
the results together. This way the results are always consistent and headers
don't suddenly change.

diffstat:

 src/lib-storage/index/imapc/imapc-mail-fetch.c |  96 +++++++++++++++++++++----
 src/lib-storage/index/imapc/imapc-mail.c       |   2 +
 src/lib-storage/index/imapc/imapc-mail.h       |   3 +-
 src/lib-storage/index/imapc/imapc-save.c       |   5 +-
 src/lib-storage/index/imapc/imapc-settings.c   |   1 +
 src/lib-storage/index/imapc/imapc-settings.h   |   3 +-
 6 files changed, 90 insertions(+), 20 deletions(-)

diffs (268 lines):

diff -r 93dab55ae36e -r 1c17f06d5e52 src/lib-storage/index/imapc/imapc-mail-fetch.c
--- a/src/lib-storage/index/imapc/imapc-mail-fetch.c	Tue Apr 14 09:58:01 2015 +0300
+++ b/src/lib-storage/index/imapc/imapc-mail-fetch.c	Wed Apr 15 21:01:11 2015 +0300
@@ -4,6 +4,7 @@
 #include "str.h"
 #include "ioloop.h"
 #include "istream.h"
+#include "istream-concat.h"
 #include "istream-header-filter.h"
 #include "message-header-parser.h"
 #include "imap-arg.h"
@@ -218,9 +219,18 @@
 		str_append_c(str, ' ');
 	}
 
-	if ((fields & MAIL_FETCH_STREAM_BODY) != 0)
-		str_append(str, "BODY.PEEK[] ");
-	else if ((fields & MAIL_FETCH_STREAM_HEADER) != 0)
+	if ((fields & MAIL_FETCH_STREAM_BODY) != 0) {
+		if (!IMAPC_BOX_HAS_FEATURE(mbox, IMAPC_FEATURE_ZIMBRA_WORKAROUNDS))
+			str_append(str, "BODY.PEEK[] ");
+		else {
+			/* BODY.PEEK[] can return different headers than
+			   BODY.PEEK[HEADER] (e.g. invalid 8bit chars replaced
+			   with '?' in HEADER) - this violates IMAP protocol
+			   and messes up dsync since it sometimes fetches the
+			   full body and sometimes only the headers. */
+			str_append(str, "BODY.PEEK[HEADER] BODY.PEEK[TEXT] ");
+		}
+	} else if ((fields & MAIL_FETCH_STREAM_HEADER) != 0)
 		str_append(str, "BODY.PEEK[HEADER] ");
 	else if (headers != NULL) {
 		mail->fetching_headers =
@@ -267,8 +277,9 @@
 	} else {
 		return;
 	}
+	mail->header_fetched = TRUE;
 	mail->body_fetched = TRUE;
-	imapc_mail_init_stream(mail, TRUE);
+	imapc_mail_init_stream(mail);
 }
 
 static enum mail_fetch_field
@@ -450,7 +461,7 @@
 	*input = filter_input;
 }
 
-void imapc_mail_init_stream(struct imapc_mail *mail, bool have_body)
+void imapc_mail_init_stream(struct imapc_mail *mail)
 {
 	struct index_mail *imail = &mail->imail;
 	struct mail *_mail = &imail->mail.mail;
@@ -469,7 +480,7 @@
 			index_mail_close_streams(imail);
 			return;
 		}
-	} else if (have_body) {
+	} else if (mail->body_fetched) {
 		ret = i_stream_get_size(imail->data.stream, TRUE, &size);
 		if (ret < 0) {
 			index_mail_close_streams(imail);
@@ -482,7 +493,7 @@
 		imail->data.virtual_size = size;
 	}
 
-	imail->data.stream_has_only_header = !have_body;
+	imail->data.stream_has_only_header = !mail->body_fetched;
 	if (index_mail_init_stream(imail, NULL, NULL, &input) < 0)
 		index_mail_close_streams(imail);
 }
@@ -490,52 +501,100 @@
 static void
 imapc_fetch_stream(struct imapc_mail *mail,
 		   const struct imapc_untagged_reply *reply,
-		   const struct imap_arg *arg, bool body)
+		   const struct imap_arg *arg,
+		   bool have_header, bool have_body)
 {
 	struct index_mail *imail = &mail->imail;
+	struct istream *hdr_stream = NULL;
 	const char *value;
 	int fd;
 
 	if (imail->data.stream != NULL) {
-		if (!body)
+		i_assert(mail->header_fetched);
+		if (mail->body_fetched || !have_body)
 			return;
-		/* maybe the existing stream has no body. replace it. */
+		if (have_header) {
+			/* replace the existing stream */
+		} else if (mail->fd == -1) {
+			/* append this body stream to the existing
+			   header stream */
+			hdr_stream = imail->data.stream;
+			i_stream_ref(hdr_stream);
+		} else {
+			/* append this body stream to the existing
+			   header stream. we'll need to recreate the stream
+			   with autoclosed fd. */
+			if (lseek(mail->fd, 0, SEEK_SET) < 0)
+				i_error("lseek(imapc) failed: %m");
+			hdr_stream = i_stream_create_fd_autoclose(&mail->fd, 0);
+		}
 		index_mail_close_streams(imail);
 		if (mail->fd != -1) {
 			if (close(mail->fd) < 0)
 				i_error("close(imapc mail) failed: %m");
 			mail->fd = -1;
 		}
+	} else {
+		if (!have_header) {
+			/* BODY.PEEK[TEXT] received - we can't currently handle
+			   this before receiving BODY.PEEK[HEADER] reply */
+			return;
+		}
 	}
 
 	if (arg->type == IMAP_ARG_LITERAL_SIZE) {
-		if (!imapc_find_lfile_arg(reply, arg, &fd))
+		if (!imapc_find_lfile_arg(reply, arg, &fd)) {
+			if (hdr_stream != NULL)
+				i_stream_unref(&hdr_stream);
 			return;
+		}
 		if ((fd = dup(fd)) == -1) {
 			i_error("dup() failed: %m");
+			if (hdr_stream != NULL)
+				i_stream_unref(&hdr_stream);
 			return;
 		}
 		mail->fd = fd;
 		imail->data.stream = i_stream_create_fd(fd, 0, FALSE);
 	} else {
 		if (!imap_arg_get_nstring(arg, &value))
-			return;
+			value = NULL;
 		if (value == NULL) {
 			mail_set_expunged(&imail->mail.mail);
+			if (hdr_stream != NULL)
+				i_stream_unref(&hdr_stream);
 			return;
 		}
 		if (mail->body == NULL) {
 			mail->body = buffer_create_dynamic(default_pool,
 							   arg->str_len + 1);
+		} else if (!have_header && hdr_stream != NULL) {
+			/* header is already in the buffer - add body now
+			   without destroying the existing header data */
+			i_stream_unref(&hdr_stream);
+		} else {
+			buffer_set_used_size(mail->body, 0);
 		}
-		buffer_set_used_size(mail->body, 0);
 		buffer_append(mail->body, value, arg->str_len);
 		imail->data.stream = i_stream_create_from_data(mail->body->data,
 							       mail->body->used);
 	}
-	mail->body_fetched = body;
+	if (have_header)
+		mail->header_fetched = TRUE;
+	mail->body_fetched = have_body;
 
-	imapc_mail_init_stream(mail, body);
+	if (hdr_stream != NULL) {
+		struct istream *inputs[3];
+
+		inputs[0] = hdr_stream;
+		inputs[1] = imail->data.stream;
+		inputs[2] = NULL;
+		imail->data.stream = i_stream_create_concat(inputs);
+		i_stream_unref(&inputs[0]);
+		i_stream_unref(&inputs[1]);
+	}
+
+	imapc_mail_init_stream(mail);
 }
 
 static void
@@ -623,10 +682,13 @@
 			break;
 
 		if (strcasecmp(key, "BODY[]") == 0) {
-			imapc_fetch_stream(mail, reply, &args[i+1], TRUE);
+			imapc_fetch_stream(mail, reply, &args[i+1], TRUE, TRUE);
 			match = TRUE;
 		} else if (strcasecmp(key, "BODY[HEADER]") == 0) {
-			imapc_fetch_stream(mail, reply, &args[i+1], FALSE);
+			imapc_fetch_stream(mail, reply, &args[i+1], TRUE, FALSE);
+			match = TRUE;
+		} else if (strcasecmp(key, "BODY[TEXT]") == 0) {
+			imapc_fetch_stream(mail, reply, &args[i+1], FALSE, TRUE);
 			match = TRUE;
 		} else if (strcasecmp(key, "BODY[HEADER.FIELDS") == 0) {
 			imapc_fetch_header_stream(mail, reply, &args[i+1]);
diff -r 93dab55ae36e -r 1c17f06d5e52 src/lib-storage/index/imapc/imapc-mail.c
--- a/src/lib-storage/index/imapc/imapc-mail.c	Tue Apr 14 09:58:01 2015 +0300
+++ b/src/lib-storage/index/imapc/imapc-mail.c	Wed Apr 15 21:01:11 2015 +0300
@@ -386,6 +386,8 @@
 	}
 	if (mail->body != NULL)
 		buffer_free(&mail->body);
+	mail->header_fetched = FALSE;
+	mail->body_fetched = FALSE;
 }
 
 static int imapc_mail_get_hdr_hash(struct index_mail *imail)
diff -r 93dab55ae36e -r 1c17f06d5e52 src/lib-storage/index/imapc/imapc-mail.h
--- a/src/lib-storage/index/imapc/imapc-mail.h	Tue Apr 14 09:58:01 2015 +0300
+++ b/src/lib-storage/index/imapc/imapc-mail.h	Wed Apr 15 21:01:11 2015 +0300
@@ -17,6 +17,7 @@
 
 	int fd;
 	buffer_t *body;
+	bool header_fetched;
 	bool body_fetched;
 	bool header_list_fetched;
 };
@@ -31,7 +32,7 @@
 		     const char *const *headers);
 bool imapc_mail_prefetch(struct mail *mail);
 void imapc_mail_fetch_flush(struct imapc_mailbox *mbox);
-void imapc_mail_init_stream(struct imapc_mail *mail, bool have_body);
+void imapc_mail_init_stream(struct imapc_mail *mail);
 
 void imapc_mail_fetch_update(struct imapc_mail *mail,
 			     const struct imapc_untagged_reply *reply,
diff -r 93dab55ae36e -r 1c17f06d5e52 src/lib-storage/index/imapc/imapc-save.c
--- a/src/lib-storage/index/imapc/imapc-save.c	Tue Apr 14 09:58:01 2015 +0300
+++ b/src/lib-storage/index/imapc/imapc-save.c	Wed Apr 15 21:01:11 2015 +0300
@@ -152,8 +152,11 @@
 	imail->data.forced_no_caching = TRUE;
 
 	if (ctx->fd != -1) {
+		struct imapc_mail *imapc_mail = (struct imapc_mail *)imail;
 		imail->data.stream = i_stream_create_fd_autoclose(&ctx->fd, 0);
-		imapc_mail_init_stream((struct imapc_mail *)imail, TRUE);
+		imapc_mail->header_fetched = TRUE;
+		imapc_mail->body_fetched = TRUE;
+		imapc_mail_init_stream(imapc_mail);
 	}
 
 	ctx->save_count++;
diff -r 93dab55ae36e -r 1c17f06d5e52 src/lib-storage/index/imapc/imapc-settings.c
--- a/src/lib-storage/index/imapc/imapc-settings.c	Tue Apr 14 09:58:01 2015 +0300
+++ b/src/lib-storage/index/imapc/imapc-settings.c	Wed Apr 15 21:01:11 2015 +0300
@@ -86,6 +86,7 @@
 	{ "fetch-headers", IMAPC_FEATURE_FETCH_HEADERS },
 	{ "gmail-migration", IMAPC_FEATURE_GMAIL_MIGRATION },
 	{ "search", IMAPC_FEATURE_SEARCH },
+	{ "zimbra-workarounds", IMAPC_FEATURE_ZIMBRA_WORKAROUNDS },
 	{ NULL, 0 }
 };
 
diff -r 93dab55ae36e -r 1c17f06d5e52 src/lib-storage/index/imapc/imapc-settings.h
--- a/src/lib-storage/index/imapc/imapc-settings.h	Tue Apr 14 09:58:01 2015 +0300
+++ b/src/lib-storage/index/imapc/imapc-settings.h	Wed Apr 15 21:01:11 2015 +0300
@@ -7,7 +7,8 @@
 	IMAPC_FEATURE_GUID_FORCED		= 0x02,
 	IMAPC_FEATURE_FETCH_HEADERS		= 0x04,
 	IMAPC_FEATURE_GMAIL_MIGRATION		= 0x08,
-	IMAPC_FEATURE_SEARCH			= 0x10
+	IMAPC_FEATURE_SEARCH			= 0x10,
+	IMAPC_FEATURE_ZIMBRA_WORKAROUNDS	= 0x20
 };
 /* </settings checks> */
 


More information about the dovecot-cvs mailing list