Mercurial > dovecot > core-2.2
changeset 18909:ab441df52e86
pop3-migration: Truncate header if there's line containing only CR(s).
This fixes matching IMAP <-> POP3 messages when the servers behave
differently.
author | Timo Sirainen <tss@iki.fi> |
---|---|
date | Thu, 16 Jul 2015 18:08:40 +0300 |
parents | b51dfee18fd2 |
children | f0148bae3d62 |
files | src/plugins/pop3-migration/Makefile.am src/plugins/pop3-migration/pop3-migration-plugin.c src/plugins/pop3-migration/pop3-migration-plugin.h src/plugins/pop3-migration/test-pop3-migration-plugin.c |
diffstat | 4 files changed, 123 insertions(+), 14 deletions(-) [+] |
line wrap: on
line diff
--- 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
--- 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); }
--- 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
--- /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); +}