dovecot-2.2: pop3-migration: Truncate header if there's line con...

dovecot at dovecot.org dovecot at dovecot.org
Thu Jul 16 15:08:53 UTC 2015


details:   http://hg.dovecot.org/dovecot-2.2/rev/ab441df52e86
changeset: 18909:ab441df52e86
user:      Timo Sirainen <tss at iki.fi>
date:      Thu Jul 16 18:08:40 2015 +0300
description:
pop3-migration: Truncate header if there's line containing only CR(s).
This fixes matching IMAP <-> POP3 messages when the servers behave
differently.

diffstat:

 src/plugins/pop3-migration/Makefile.am                  |  22 ++++++
 src/plugins/pop3-migration/pop3-migration-plugin.c      |  58 ++++++++++++----
 src/plugins/pop3-migration/pop3-migration-plugin.h      |   7 ++
 src/plugins/pop3-migration/test-pop3-migration-plugin.c |  50 ++++++++++++++
 4 files changed, 123 insertions(+), 14 deletions(-)

diffs (226 lines):

diff -r b51dfee18fd2 -r ab441df52e86 src/plugins/pop3-migration/Makefile.am
--- a/src/plugins/pop3-migration/Makefile.am	Tue Jul 14 15:08:24 2015 +0200
+++ b/src/plugins/pop3-migration/Makefile.am	Thu Jul 16 18:08:40 2015 +0300
@@ -1,5 +1,6 @@
 AM_CPPFLAGS = \
 	-I$(top_srcdir)/src/lib \
+	-I$(top_srcdir)/src/lib-test \
 	-I$(top_srcdir)/src/lib-mail \
 	-I$(top_srcdir)/src/lib-index \
 	-I$(top_srcdir)/src/lib-storage
@@ -15,3 +16,24 @@
 
 noinst_HEADERS = \
 	pop3-migration-plugin.h
+
+noinst_PROGRAMS = $(test_programs)
+
+test_programs = \
+	test-pop3-migration-plugin
+
+test_libs = \
+	../../lib-storage/libstorage.la \
+	../../lib-test/libtest.la \
+	../../lib/liblib.la
+test_deps = $(module_LTLIBRARIES) $(test_libs)
+
+test_pop3_migration_plugin_SOURCES = test-pop3-migration-plugin.c
+test_pop3_migration_plugin_LDADD = pop3-migration-plugin.lo $(test_libs)
+test_pop3_migration_plugin_DEPENDENCIES = $(test_deps)
+
+check: check-am check-test
+check-test: all-am
+	for bin in $(test_programs); do \
+	  if ! $(RUN_TEST) ./$$bin; then exit 1; fi; \
+	done
diff -r b51dfee18fd2 -r ab441df52e86 src/plugins/pop3-migration/pop3-migration-plugin.c
--- a/src/plugins/pop3-migration/pop3-migration-plugin.c	Tue Jul 14 15:08:24 2015 +0200
+++ b/src/plugins/pop3-migration/pop3-migration-plugin.c	Thu Jul 16 18:08:40 2015 +0300
@@ -112,33 +112,56 @@
 	return memcmp(map1->hdr_sha1, map2->hdr_sha1, sizeof(map1->hdr_sha1));
 }
 
+struct pop3_hdr_context {
+	bool have_eoh;
+	bool stop;
+};
+
 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)
+			    bool *matched, struct pop3_hdr_context *ctx)
 {
-	if (hdr != NULL && hdr->eoh)
-		*have_eoh = TRUE;
+	if (hdr == NULL)
+		return;
+	if (hdr->eoh) {
+		ctx->have_eoh = TRUE;
+		if (ctx->stop) {
+			/* matched is handled differently for eoh by
+			 istream-header-filter. a design bug I guess.. */
+			*matched = FALSE;
+		}
+	} else {
+		if (strspn(hdr->name, "\r") == hdr->name_len) {
+			/* CR+CR+LF - some servers stop the header processing
+			 here while others don't. To make sure they can be
+			 matched correctly we want to stop here entirely. */
+			ctx->stop = TRUE;
+		}
+		if (ctx->stop)
+			*matched = 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)
+int pop3_migration_get_hdr_sha1(uint32_t mail_seq, 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;
+	struct pop3_hdr_context hdr_ctx;
 
-	*have_eoh_r = FALSE;
-
+	memset(&hdr_ctx, 0, sizeof(hdr_ctx));
 	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),
-				pop3_header_filter_callback, have_eoh_r);
+				pop3_header_filter_callback, &hdr_ctx);
 	i_stream_unref(&input2);
 
 	sha1_init(&sha1_ctx);
@@ -159,12 +182,14 @@
 	}
 	if (input->stream_errno != 0) {
 		i_error("pop3_migration: Failed to read header for msg %u: %s",
-			mail->seq, i_stream_get_error(input));
+			mail_seq, i_stream_get_error(input));
 		i_stream_unref(&input);
 		return -1;
 	}
 	sha1_result(&sha1_ctx, sha1_r);
 	i_stream_unref(&input);
+
+	*have_eoh_r = hdr_ctx.have_eoh;
 	return 0;
 }
 
@@ -180,8 +205,9 @@
 			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)
+	if (pop3_migration_get_hdr_sha1(mail->seq, input,
+					hdr_size.physical_size,
+					sha1_r, &have_eoh) < 0)
 		return -1;
 	if (have_eoh)
 		return 0;
@@ -199,6 +225,9 @@
 	   truncating the rest. POP3 TOP instead returns the entire header.
 	   This causes the IMAP and POP3 hashes not to match.
 
+	   If there's LF+CR+CR+LF in the middle of headers, Courier IMAP's
+	   FETCH BODY[HEADER] stops after that, but Courier POP3's TOP doesn't.
+
 	   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. */
@@ -207,8 +236,9 @@
 			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);
+	return pop3_migration_get_hdr_sha1(mail->seq, input,
+					   hdr_size.physical_size,
+					   sha1_r, &have_eoh);
 
 }
 
diff -r b51dfee18fd2 -r ab441df52e86 src/plugins/pop3-migration/pop3-migration-plugin.h
--- a/src/plugins/pop3-migration/pop3-migration-plugin.h	Tue Jul 14 15:08:24 2015 +0200
+++ b/src/plugins/pop3-migration/pop3-migration-plugin.h	Thu Jul 16 18:08:40 2015 +0300
@@ -1,7 +1,14 @@
 #ifndef POP3_MIGRATION_PLUGIN_H
 #define POP3_MIGRATION_PLUGIN_H
 
+struct module;
+
 void pop3_migration_plugin_init(struct module *module);
 void pop3_migration_plugin_deinit(void);
 
+int pop3_migration_get_hdr_sha1(uint32_t mail_seq, struct istream *input,
+				uoff_t hdr_size,
+				unsigned char sha1_r[SHA1_RESULTLEN],
+				bool *have_eoh_r);
+
 #endif
diff -r b51dfee18fd2 -r ab441df52e86 src/plugins/pop3-migration/test-pop3-migration-plugin.c
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/plugins/pop3-migration/test-pop3-migration-plugin.c	Thu Jul 16 18:08:40 2015 +0300
@@ -0,0 +1,50 @@
+/* Copyright (c) 2015 Dovecot authors, see the included COPYING file */
+
+#include "lib.h"
+#include "sha1.h"
+#include "hex-binary.h"
+#include "istream.h"
+#include "test-common.h"
+#include "pop3-migration-plugin.h"
+
+static void test_pop3_migration_get_hdr_sha1(void)
+{
+	struct {
+		const char *input;
+		const char *sha1;
+		bool have_eoh;
+	} tests[] = {
+		{ "", "da39a3ee5e6b4b0d3255bfef95601890afd80709", FALSE },
+		{ "\n", "adc83b19e793491b1c6ea0fd8b46cd9f32e592fc", TRUE },
+		{ "a: b\r\n", "3edb5ce145cf1d1e2413e02b8bed70f1ae3ed105", FALSE },
+		{ "a: b\r\n\r\n", "d14841695e1d9e2de6625d9222abd149ec821b0d", TRUE },
+		{ "a: b\r\n\r\r\n", "3edb5ce145cf1d1e2413e02b8bed70f1ae3ed105", FALSE },
+		{ "a: b\r\n\r\r\nc: d\r\n\r\n", "3edb5ce145cf1d1e2413e02b8bed70f1ae3ed105", TRUE }
+	};
+	struct istream *input;
+	unsigned char digest[SHA1_RESULTLEN];
+	unsigned int i;
+	bool have_eoh;
+
+	test_begin("pop3 migration get hdr sha1");
+
+	for (i = 0; i < N_ELEMENTS(tests); i++) {
+		input = i_stream_create_from_data(tests[i].input,
+						  strlen(tests[i].input));
+		test_assert_idx(pop3_migration_get_hdr_sha1(1, input, strlen(tests[i].input),
+							    digest, &have_eoh) == 0, i);
+		test_assert_idx(strcasecmp(binary_to_hex(digest, sizeof(digest)), tests[i].sha1) == 0, i);
+		test_assert_idx(tests[i].have_eoh == have_eoh, i);
+	}
+
+	test_end();
+}
+
+int main(void)
+{
+	static void (*test_functions[])(void) = {
+		test_pop3_migration_get_hdr_sha1,
+		NULL
+	};
+	return test_run(test_functions);
+}


More information about the dovecot-cvs mailing list