From b31e44c678bf3bfe81bcc5f159e627551f12700f Mon Sep 17 00:00:00 2001 From: David Bremner Date: Thu, 30 Aug 2018 08:29:14 -0300 Subject: [PATCH] lib: add _notmuch_message_id_parse_strict The idea is that if a message-id parses with this function, the MUA generating it was probably sane, and in particular it's probably safe to use the result as a parent from In-Reply-to. --- lib/message-id.c | 30 +++++++++++++++++ lib/notmuch-private.h | 14 ++++++++ test/Makefile.local | 6 +++- test/T710-message-id.sh | 73 +++++++++++++++++++++++++++++++++++++++++ test/message-id-parse.c | 26 +++++++++++++++ 5 files changed, 148 insertions(+), 1 deletion(-) create mode 100755 test/T710-message-id.sh create mode 100644 test/message-id-parse.c diff --git a/lib/message-id.c b/lib/message-id.c index d7541d50..e71ce9f4 100644 --- a/lib/message-id.c +++ b/lib/message-id.c @@ -1,4 +1,5 @@ #include "notmuch-private.h" +#include "string-util.h" /* Advance 'str' past any whitespace or RFC 822 comments. A comment is * a (potentially nested) parenthesized sequence with '\' used to @@ -94,3 +95,32 @@ _notmuch_message_id_parse (void *ctx, const char *message_id, const char **next) return result; } + +char * +_notmuch_message_id_parse_strict (void *ctx, const char *message_id) +{ + const char *s, *end; + + if (message_id == NULL || *message_id == '\0') + return NULL; + + s = skip_space (message_id); + if (*s == '<') + s++; + else + return NULL; + + for (end = s; *end && *end != '>'; end++) + if (isspace (*end)) + return NULL; + + if (*end != '>') + return NULL; + else { + const char *last = skip_space (end + 1); + if (*last != '\0') + return NULL; + } + + return talloc_strndup (ctx, s, end - s); +} diff --git a/lib/notmuch-private.h b/lib/notmuch-private.h index 9eca0789..df32d39c 100644 --- a/lib/notmuch-private.h +++ b/lib/notmuch-private.h @@ -533,6 +533,20 @@ _notmuch_query_count_documents (notmuch_query_t *query, char * _notmuch_message_id_parse (void *ctx, const char *message_id, const char **next); +/* Parse a message-id, discarding leading and trailing whitespace, and + * '<' and '>' delimiters. + * + * Apply a probably-stricter-than RFC definition of what is allowed in + * a message-id. In particular, forbid whitespace. + * + * Returns a newly talloc'ed string belonging to 'ctx'. + * + * Returns NULL if there is any error parsing the message-id. + */ + +char * +_notmuch_message_id_parse_strict (void *ctx, const char *message_id); + /* message.cc */ diff --git a/test/Makefile.local b/test/Makefile.local index 1a0ab813..1cf09778 100644 --- a/test/Makefile.local +++ b/test/Makefile.local @@ -15,6 +15,9 @@ smtp_dummy_modules = $(smtp_dummy_srcs:.c=.o) $(dir)/arg-test: $(dir)/arg-test.o command-line-arguments.o util/libnotmuch_util.a $(call quiet,CC) $^ -o $@ $(LDFLAGS) +$(dir)/message-id-parse: $(dir)/message-id-parse.o lib/libnotmuch.a util/libnotmuch_util.a + $(call quiet,CC) $^ -o $@ $(LDFLAGS) $(TALLOC_LDFLAGS) + $(dir)/hex-xcode: $(dir)/hex-xcode.o command-line-arguments.o util/libnotmuch_util.a $(call quiet,CC) $^ -o $@ $(LDFLAGS) $(TALLOC_LDFLAGS) @@ -50,7 +53,8 @@ test_main_srcs=$(dir)/arg-test.c \ $(dir)/smtp-dummy.c \ $(dir)/symbol-test.cc \ $(dir)/make-db-version.cc \ - $(dir)/ghost-report.cc + $(dir)/ghost-report.cc \ + $(dir)/message-id-parse.c test_srcs=$(test_main_srcs) $(dir)/database-test.c diff --git a/test/T710-message-id.sh b/test/T710-message-id.sh new file mode 100755 index 00000000..e73d6ba9 --- /dev/null +++ b/test/T710-message-id.sh @@ -0,0 +1,73 @@ +#!/usr/bin/env bash +test_description="message id parsing" + +. $(dirname "$0")/test-lib.sh || exit 1 + +test_begin_subtest "good message ids" +${TEST_DIRECTORY}/message-id-parse <OUTPUT +<018b1a8f2d1df62e804ce88b65401304832dfbbf.1346614915.git.jani@nikula.org> +<1530507300.raoomurnbf.astroid@strange.none> +<1258787708-21121-2-git-send-email-keithp@keithp.com> +EOF +cat <EXPECTED +GOOD: 018b1a8f2d1df62e804ce88b65401304832dfbbf.1346614915.git.jani@nikula.org +GOOD: 1530507300.raoomurnbf.astroid@strange.none +GOOD: 1258787708-21121-2-git-send-email-keithp@keithp.com +EOF +test_expect_equal_file EXPECTED OUTPUT + +test_begin_subtest "leading and trailing space is OK" +${TEST_DIRECTORY}/message-id-parse <OUTPUT + <018b1a8f2d1df62e804ce88b65401304832dfbbf.1346614915.git.jani@nikula.org> +<1530507300.raoomurnbf.astroid@strange.none> + <1258787708-21121-2-git-send-email-keithp@keithp.com> +EOF +cat <EXPECTED +GOOD: 018b1a8f2d1df62e804ce88b65401304832dfbbf.1346614915.git.jani@nikula.org +GOOD: 1530507300.raoomurnbf.astroid@strange.none +GOOD: 1258787708-21121-2-git-send-email-keithp@keithp.com +EOF +test_expect_equal_file EXPECTED OUTPUT + +test_begin_subtest "<> delimeters are required" +${TEST_DIRECTORY}/message-id-parse <OUTPUT +018b1a8f2d1df62e804ce88b65401304832dfbbf.1346614915.git.jani@nikula.org> +<1530507300.raoomurnbf.astroid@strange.none +1258787708-21121-2-git-send-email-keithp@keithp.com +EOF +cat <EXPECTED +BAD: 018b1a8f2d1df62e804ce88b65401304832dfbbf.1346614915.git.jani@nikula.org> +BAD: <1530507300.raoomurnbf.astroid@strange.none +BAD: 1258787708-21121-2-git-send-email-keithp@keithp.com +EOF +test_expect_equal_file EXPECTED OUTPUT + +test_begin_subtest "embedded whitespace is forbidden" +${TEST_DIRECTORY}/message-id-parse <OUTPUT +<018b1a8f2d1df62e804ce88b65401304832dfbbf.1346614915 .git.jani@nikula.org> +<1530507300.raoomurnbf.astroid @strange.none> +<1258787708-21121- 2-git-send-email-keithp@keithp.com> +EOF +cat <EXPECTED +BAD: <018b1a8f2d1df62e804ce88b65401304832dfbbf.1346614915 .git.jani@nikula.org> +BAD: <1530507300.raoomurnbf.astroid @strange.none> +BAD: <1258787708-21121- 2-git-send-email-keithp@keithp.com> +EOF +test_expect_equal_file EXPECTED OUTPUT + + +test_begin_subtest "folded real life bad In-Reply-To values" +${TEST_DIRECTORY}/message-id-parse <OUTPUT +<22597.31869.380767.339702@chiark.greenend.org.uk> (Ian Jackson's message of "Mon, 5 Dec 2016 14:41:01 +0000") +<20170625141242.loaalhis2eodo66n@gaara.hadrons.org> <149719990964.27883.13021127452105787770.reportbug@seneca.home.org> +Your message of Tue, 09 Dec 2014 13:21:11 +0100. <1900758.CgLNVPbY9N@liber> +EOF +cat <EXPECTED +BAD: <22597.31869.380767.339702@chiark.greenend.org.uk> (Ian Jackson's message of "Mon, 5 Dec 2016 14:41:01 +0000") +BAD: <20170625141242.loaalhis2eodo66n@gaara.hadrons.org> <149719990964.27883.13021127452105787770.reportbug@seneca.home.org> +BAD: Your message of Tue, 09 Dec 2014 13:21:11 +0100. <1900758.CgLNVPbY9N@liber> +EOF +test_expect_equal_file EXPECTED OUTPUT + + +test_done diff --git a/test/message-id-parse.c b/test/message-id-parse.c new file mode 100644 index 00000000..752eb1fd --- /dev/null +++ b/test/message-id-parse.c @@ -0,0 +1,26 @@ +#include +#include +#include "notmuch-private.h" + +int +main (unused (int argc), unused (char **argv)) +{ + char *line = NULL; + size_t len = 0; + ssize_t nread; + void *local = talloc_new (NULL); + + while ((nread = getline (&line, &len, stdin)) != -1) { + int last = strlen (line) - 1; + if (line[last] == '\n') + line[last] = '\0'; + + char *mid = _notmuch_message_id_parse_strict (local, line); + if (mid) + printf ("GOOD: %s\n", mid); + else + printf ("BAD: %s\n", line); + } + + talloc_free (local); +}