From f4c611b736ac290ec4f99faaa48b59fdd7c63809 Mon Sep 17 00:00:00 2001 From: Jan Engelhardt Date: Wed, 9 Dec 2020 16:58:57 +0100 Subject: [PATCH] Avoid force-encoding display names that fit within qcontent When the display name contains an At sign, or anything of the sort, libvmime would forcibly encode this to =?...?=, even if the line is fine ASCII which only needs quoting. rspamd takes excessive quoting as a sign of spam and penalizes such mails by raising the score (rule/match: TO_EXCESS_QP et al.) --- src/vmime/mailbox.cpp | 34 ++-------------------------------- tests/parser/bodyTest.cpp | 1 - tests/parser/mailboxTest.cpp | 14 ++++++++++++++ 3 files changed, 16 insertions(+), 33 deletions(-) diff --git a/src/vmime/mailbox.cpp b/src/vmime/mailbox.cpp index db5cc74e..30a082ea 100644 --- a/src/vmime/mailbox.cpp +++ b/src/vmime/mailbox.cpp @@ -355,44 +355,14 @@ void mailbox::generateImpl( // - if it contains characters in a charset different from "US-ASCII", // - and/or if it contains one or more of these special chars: // CR LF TAB " ; , < > ( ) @ / ? . = : - + // these special chars only require quoting, not full encoding // Check whether there are words that are not "US-ASCII" // and/or contain the special chars. bool forceEncode = false; for (size_t w = 0 ; !forceEncode && w != m_name.getWordCount() ; ++w) { - if (m_name.getWordAt(w)->getCharset() == charset(charsets::US_ASCII)) { - - const string& buffer = m_name.getWordAt(w)->getBuffer(); - - for (string::const_iterator c = buffer.begin() ; - !forceEncode && c != buffer.end() ; ++c) { - - switch (*c) { - - case '\r': - case '\n': - case '\t': - case ';': - case ',': - case '<': case '>': - case '(': case ')': - case '@': - case '/': - case '?': - case '.': - case '=': - case ':': - case '"': - - forceEncode = true; - break; - } - } - - } else { - + if (m_name.getWordAt(w)->getCharset() != charset(charsets::US_ASCII)) { forceEncode = true; } } diff --git a/tests/parser/bodyTest.cpp b/tests/parser/bodyTest.cpp index fa396af5..31054f30 100644 --- a/tests/parser/bodyTest.cpp +++ b/tests/parser/bodyTest.cpp @@ -31,7 +31,6 @@ VMIME_TEST_SUITE_BEGIN(bodyTest) VMIME_TEST(testGenerate_NonText) VMIME_TEST_LIST_END - void testGenerate_Text() { // RFC-2015: [Quoted-Printable encoding] A line break in a text body, diff --git a/tests/parser/mailboxTest.cpp b/tests/parser/mailboxTest.cpp index 23d1b4ac..997a6a38 100644 --- a/tests/parser/mailboxTest.cpp +++ b/tests/parser/mailboxTest.cpp @@ -31,6 +31,7 @@ VMIME_TEST_SUITE_BEGIN(mailboxTest) VMIME_TEST(testEmptyEmailAddress) VMIME_TEST(testSeparatorInComment) VMIME_TEST(testMalformations) + VMIME_TEST(testExcessiveQuoting) VMIME_TEST_LIST_END @@ -170,4 +171,17 @@ VMIME_TEST_SUITE_BEGIN(mailboxTest) VASSERT_EQ("email", "bar@y.com", mbox.getEmail()); } + void testExcessiveQuoting() { + using namespace vmime; + + // Check that ASCII display names are not encoded more than necessary + emailAddress e("a@b.com"); + auto a = make_shared(text(word("Foo B@r", charsets::US_ASCII)), e); + VASSERT_EQ("generate", "\"Foo B@r\" ", a->generate()); + VASSERT_NEQ("generate", "=?utf-8?Q?Foo_B=40r?= ", a->generate()); + + a = make_shared(text(word("Foo B@r", charsets::UTF_8)), e); + VASSERT_EQ("generate", "=?utf-8?Q?Foo_B=40r?= ", a->generate()); + } + VMIME_TEST_SUITE_END