dovecot-2.2: pop3-migration: Work around IMAP/POP3 server bugs w...

dovecot at dovecot.org dovecot at dovecot.org
Tue Feb 4 18:22:57 EET 2014


details:   http://hg.dovecot.org/dovecot-2.2/rev/18d0ce83bf7a
changeset: 17122:18d0ce83bf7a
user:      Timo Sirainen <tss at iki.fi>
date:      Mon Feb 03 10:47:31 2014 -0500
description:
pop3-migration: Work around IMAP/POP3 server bugs which truncate the header too early.

diffstat:

 src/plugins/pop3-migration/pop3-migration-plugin.c |  82 +++++++++++++++++----
 1 files changed, 67 insertions(+), 15 deletions(-)

diffs (121 lines):

diff -r 5432b55a2b87 -r 18d0ce83bf7a src/plugins/pop3-migration/pop3-migration-plugin.c
--- a/src/plugins/pop3-migration/pop3-migration-plugin.c	Fri Jan 31 02:12:30 2014 +0100
+++ b/src/plugins/pop3-migration/pop3-migration-plugin.c	Mon Feb 03 10:47:31 2014 -0500
@@ -6,6 +6,7 @@
 #include "istream-header-filter.h"
 #include "sha1.h"
 #include "message-size.h"
+#include "message-header-parser.h"
 #include "mail-namespace.h"
 #include "mail-search-build.h"
 #include "mail-storage-private.h"
@@ -110,26 +111,33 @@
 	return memcmp(map1->hdr_sha1, map2->hdr_sha1, sizeof(map1->hdr_sha1));
 }
 
-static int get_hdr_sha1(struct mail *mail, unsigned char sha1[SHA1_RESULTLEN])
+static void
+pop3_header_filter_callback(struct header_filter_istream *input ATTR_UNUSED,
+			    struct message_header_line *hdr,
+			    bool *matched ATTR_UNUSED, bool *have_eoh)
 {
-	struct message_size hdr_size;
-	struct istream *input, *input2;
-	const unsigned char *data;
-	size_t size;
+	if (hdr != NULL && hdr->eoh)
+		*have_eoh = TRUE;
+}
+
+static int
+get_hdr_sha1_stream(struct mail *mail, struct istream *input, uoff_t hdr_size,
+		    unsigned char sha1_r[SHA1_RESULTLEN], bool *have_eoh_r)
+{
+	struct istream *input2;
+	const unsigned char *data, *p;
+	size_t size, idx;
 	struct sha1_ctxt sha1_ctx;
 
-	if (mail_get_hdr_stream(mail, &hdr_size, &input) < 0) {
-		i_error("pop3_migration: Failed to get header for msg %u: %s",
-			mail->seq, mailbox_get_last_error(mail->box, NULL));
-		return -1;
-	}
-	input2 = i_stream_create_limit(input, hdr_size.physical_size);
+	*have_eoh_r = FALSE;
+
+	input2 = i_stream_create_limit(input, hdr_size);
 	/* hide headers that might change or be different in IMAP vs. POP3 */
 	input = i_stream_create_header_filter(input2,
 				HEADER_FILTER_EXCLUDE | HEADER_FILTER_NO_CR,
 				hdr_hash_skip_headers,
 				N_ELEMENTS(hdr_hash_skip_headers),
-				*null_header_filter_callback, (void *)NULL);
+				pop3_header_filter_callback, have_eoh_r);
 	i_stream_unref(&input2);
 
 	sha1_init(&sha1_ctx);
@@ -138,16 +146,60 @@
 		i_stream_skip(input, size);
 	}
 	if (input->stream_errno != 0) {
-		i_error("pop3_migration: Failed to read header for msg %u: %m",
-			mail->seq);
+		i_error("pop3_migration: Failed to read header for msg %u: %s",
+			mail->seq, i_stream_get_error(input));
 		i_stream_unref(&input);
 		return -1;
 	}
-	sha1_result(&sha1_ctx, sha1);
+	sha1_result(&sha1_ctx, sha1_r);
 	i_stream_unref(&input);
 	return 0;
 }
 
+static int
+get_hdr_sha1(struct mail *mail, unsigned char sha1_r[SHA1_RESULTLEN])
+{
+	struct istream *input;
+	struct message_size hdr_size;
+	bool have_eoh;
+
+	if (mail_get_hdr_stream(mail, &hdr_size, &input) < 0) {
+		i_error("pop3_migration: Failed to get header for msg %u: %s",
+			mail->seq, mailbox_get_last_error(mail->box, NULL));
+		return -1;
+	}
+	if (get_hdr_sha1_stream(mail, input, hdr_size.physical_size,
+				sha1_r, &have_eoh) < 0)
+		return -1;
+	if (have_eoh)
+		return 0;
+
+	/* The empty "end of headers" line is missing. Either this means that
+	   the headers ended unexpectedly (which is ok) or that the remote
+	   server is buggy. Some servers have problems with
+
+	   1) header line continuations that contain only whitespace and
+	   2) headers that have no ":". The header gets truncated when such
+	   line is reached.
+
+	   At least Oracle IMS IMAP FETCH BODY[HEADER] handles 1) by not
+	   returning the whitespace line and 2) by returning the line but
+	   truncating the rest. POP3 TOP instead returns the entire header.
+	   This causes the IMAP and POP3 hashes not to match.
+
+	   So we'll try to avoid this by falling back to full FETCH BODY[]
+	   (and/or RETR) and we'll parse the header ourself from it. This
+	   should work around any similar bugs in all IMAP/POP3 servers. */
+	if (mail_get_stream(mail, &hdr_size, NULL, &input) < 0) {
+		i_error("pop3_migration: Failed to get body for msg %u: %s",
+			mail->seq, mailbox_get_last_error(mail->box, NULL));
+		return -1;
+	}
+	return get_hdr_sha1_stream(mail, input, hdr_size.physical_size,
+				   sha1_r, &have_eoh);
+
+}
+
 static struct mailbox *pop3_mailbox_alloc(struct mail_storage *storage)
 {
 	struct pop3_migration_mail_storage *mstorage =


More information about the dovecot-cvs mailing list