mailparsing: emit MIME-Version with its RFC 2045 canonical spelling

Headers were built and rebuilt as "Mime-Version" rather than
"MIME-Version". Both are valid per RFC 2045, but some spam filters,
such as rspamd, score the mixed-case form as a deliverability signal.
Fix the header name in the accessor macro and in ParsedHeader's
grammar table, the places that spell it, and update the snapshot
tests across mailparsing, message, kumo-log-types, mod-mimepart, and
kumod that captured the old spelling, along with the reference docs
that showed the old casing in their examples. #564
This commit is contained in:
Wez Furlong
2026-09-28 13:21:16 +01:00
parent b3fb13b5fa
commit d2f31b4fdf
16 changed files with 32 additions and 27 deletions
+5 -5
View File
@@ -860,7 +860,7 @@ Content-Type: multipart/report;\r
\tboundary="report-boundary";\r
\treport-type="delivery-status"\r
Subject: Returned mail\r
Mime-Version: 1.0\r
MIME-Version: 1.0\r
Message-ID: <UUID@mta1.example.com>\r
To: sender@sender.example.com\r
From: Mail Delivery Subsystem <mailer-daemon@mta1.example.com>\r
@@ -991,7 +991,7 @@ Content-Type: multipart/report;\r
\tboundary="report-boundary";\r
\treport-type="delivery-status"\r
Subject: Returned mail\r
Mime-Version: 1.0\r
MIME-Version: 1.0\r
Message-ID: <UUID@mta1.example.com>\r
To: sender@sender.example.com\r
From: Mail Delivery Subsystem <mailer-daemon@mta1.example.com>\r
@@ -1124,7 +1124,7 @@ Content-Type: multipart/report;\r
\tboundary="report-boundary";\r
\treport-type="delivery-status"\r
Subject: Returned mail\r
Mime-Version: 1.0\r
MIME-Version: 1.0\r
Message-ID: <UUID@mta1.example.com>\r
To: sender@sender.example.com\r
From: Mail Delivery Subsystem <mailer-daemon@mta1.example.com>\r
@@ -1262,7 +1262,7 @@ Content-Type: multipart/report;\r
\tboundary="report-boundary";\r
\treport-type="delivery-status"\r
Subject: Returned mail\r
Mime-Version: 1.0\r
MIME-Version: 1.0\r
Message-ID: <UUID@mta1.example.com>\r
To: sender@sender.example.com\r
From: Mail Delivery Subsystem <mailer-daemon@mta1.example.com>\r
@@ -1387,7 +1387,7 @@ Content-Type: multipart/report;\r
\tboundary="report-boundary";\r
\treport-type="delivery-status"\r
Subject: Returned mail\r
Mime-Version: 1.0\r
MIME-Version: 1.0\r
Message-ID: <UUID@mta1.example.com>\r
To: sender@sender.example.com\r
From: Mail Delivery Subsystem <mailer-daemon@mta1.example.com>\r
+2 -2
View File
@@ -1482,7 +1482,7 @@ Content-Transfer-Encoding: quoted-printable\r
From: Me <me@example.com>\r
Subject: =?UTF-8?q?A_test_=F0=9F=9B=B3=EF=B8=8F?=\r
To: "James Smythe" <user@example.com>\r
Mime-Version: 1.0\r
MIME-Version: 1.0\r
\r
This is a test message to James Smythe, with some =F0=9F=91=BB=F0=9F=8D=89=\r
=F0=9F=92=A9 emoji!\r
@@ -1532,7 +1532,7 @@ Content-Transfer-Encoding: quoted-printable\r
From: Me <me@example.com>\r
Subject: =?UTF-8?q?=D8=AA=D8=B3=D8=AA_=DB=8C=DA=A9_=D8=AF=D9=88_=D8=B3=D9=87?=\r
To: "James Smythe" <user@example.com>\r
Mime-Version: 1.0\r
MIME-Version: 1.0\r
\r
This is a test message to James Smythe, with some =F0=9F=91=BB=F0=9F=8D=89=\r
=F0=9F=92=A9 emoji!\r
+3 -3
View File
@@ -207,7 +207,7 @@ mod test {
Content-Type: multipart/alternative;\r
\tboundary="ma-boundary"\r
Subject: =?UTF-8?q?Hello_there!_=F0=9F=8D=89?=\r
Mime-Version: 1.0\r
MIME-Version: 1.0\r
Date: Tue, 1 Jul 2003 10:52:37 +0200\r
\r
--ma-boundary\r
@@ -257,7 +257,7 @@ Hello World in AMP!
Content-Type: multipart/alternative;\r
\tboundary="ma-boundary"\r
Subject: =?UTF-8?q?Hello_there!_=F0=9F=8D=89?=\r
Mime-Version: 1.0\r
MIME-Version: 1.0\r
Date: Tue, 1 Jul 2003 10:52:37 +0200\r
\r
--ma-boundary\r
@@ -317,7 +317,7 @@ Content-Transfer-Encoding: quoted-printable\r
Content-Type: multipart/mixed;\r
\tboundary="mm-boundary"\r
Subject: =?UTF-8?q?Hello_there!_=F0=9F=8D=89?=\r
Mime-Version: 1.0\r
MIME-Version: 1.0\r
Date: Tue, 1 Jul 2003 10:52:37 +0200\r
\r
--mm-boundary\r
+1 -1
View File
@@ -210,7 +210,7 @@ impl<'a> HeaderMap<'a> {
MimeParameters,
as_content_transfer_encoding
);
accessor!(mime_version, "Mime-Version", BString, as_unstructured);
accessor!(mime_version, "MIME-Version", BString, as_unstructured);
accessor!(
content_disposition,
"Content-Disposition",
+3 -3
View File
@@ -1402,7 +1402,7 @@ Content-Type: text/plain;\r
Content-Transfer-Encoding: quoted-printable\r
Subject: hello there\r
From: Someone <someone@example.com>\r
Mime-Version: 1.0\r
MIME-Version: 1.0\r
\r
hello=0A\r
@@ -2020,7 +2020,7 @@ X-Header: value\r
Subject: Hello\r
X-Header: another value\r
From :Someone@somewhere\r
Mime-Version: 1.0\r
MIME-Version: 1.0\r
\r
Body
"#
@@ -2047,7 +2047,7 @@ X-Header: value\r
Subject: Hello\r
X-Header: another value\r
From: <Someone@somewhere>\r
Mime-Version: 1.0\r
MIME-Version: 1.0\r
\r
Body\r
+1 -1
View File
@@ -131,7 +131,7 @@ const KNOWN_HEADERS: &[(&str, Grammar)] = &[
("Authentication-Results", Grammar::AuthenticationResults),
("Subject", Grammar::Unstructured),
("Comments", Grammar::Unstructured),
("Mime-Version", Grammar::Unstructured),
("MIME-Version", Grammar::Unstructured),
];
fn grammar_for_name(name: &[u8]) -> Grammar {
+2 -2
View File
@@ -2758,7 +2758,7 @@ X-Header: value\r
Subject: Hello\r
X-Header: another value\r
From :Someone@somewhere\r
Mime-Version: 1.0\r
MIME-Version: 1.0\r
\r
Body
"#
@@ -2782,7 +2782,7 @@ X-Header: value\r
Subject: Hello\r
X-Header: another value\r
From: <Someone@somewhere>\r
Mime-Version: 1.0\r
MIME-Version: 1.0\r
\r
Body\r
+1 -1
View File
@@ -427,7 +427,7 @@ const NAME_GETTER: &[(&str, fn(&Lua, &Header) -> mlua::Result<mlua::Value>)] = &
("Bcc", get_address_list),
("Message-ID", get_message_id),
("Subject", get_unstructured),
("Mime-Version", get_unstructured),
("MIME-Version", get_unstructured),
("Content-Transfer-Encoding", get_content_transfer_encoding),
("Content-Type", get_content_type),
("Content-Disposition", get_content_disposition),
+1 -1
View File
@@ -9,5 +9,5 @@ builder:set_stable_content(true)
local root = builder:build()
utils.assert_eq(
tostring(root),
'Content-Type: multipart/alternative;\r\n\tboundary="ma-boundary"\r\nMime-Version: 1.0\r\nDate: Tue, 1 Jul 2003 10:52:37 +0200\r\n\r\n--ma-boundary\r\nContent-Type: text/plain;\r\n\tcharset="us-ascii"\r\n\r\nHello there!\r\n--ma-boundary\r\nContent-Type: text/html;\r\n\tcharset="us-ascii"\r\n\r\n<b>Hello</b>\r\n--ma-boundary--\r\n'
'Content-Type: multipart/alternative;\r\n\tboundary="ma-boundary"\r\nMIME-Version: 1.0\r\nDate: Tue, 1 Jul 2003 10:52:37 +0200\r\n\r\n--ma-boundary\r\nContent-Type: text/plain;\r\n\tcharset="us-ascii"\r\n\r\nHello there!\r\n--ma-boundary\r\nContent-Type: text/html;\r\n\tcharset="us-ascii"\r\n\r\n<b>Hello</b>\r\n--ma-boundary--\r\n'
)
+5
View File
@@ -42,3 +42,8 @@
* Rebuilding a message header now recognizes `Authentication-Results` as a
structured header, re-encoding it in canonical form rather than leaving it
untouched as free text.
* Constructed messages, including those built by the HTTP injection API, now
emit the `MIME-Version` header with its uppercase spelling rather than
`Mime-Version`. Both are valid per RFC 2045, but some spam filters such as
rspamd score the mixed-case form. #564
+1 -1
View File
@@ -40,7 +40,7 @@ which is the version shown at the top of this page.
|Content-Type|mime_params||
|From|mailbox_list||
|Message-ID|message_id||
|Mime-Version|unstructured||
|MIME-Version|unstructured||
|References|message_id_list||
|Reply-To|address_list||
|Resent-Bcc|address_list||
+2 -2
View File
@@ -6,6 +6,6 @@ local mime_version = headers:mime_version()
{{since('2025.10.06-5ec871ab')}}
Parses the `Mime-Version` header, and if present, returns it as a string.
Returns `nil` if `Mime-Version` is not present.
Parses the `MIME-Version` header, and if present, returns it as a string.
Returns `nil` if `MIME-Version` is not present.
+1 -1
View File
@@ -6,4 +6,4 @@ headers:set_mime_version(VALUE)
{{since('2025.10.06-5ec871ab')}}
Assign `VALUE` to the `Mime-Version` header.
Assign `VALUE` to the `MIME-Version` header.
+1 -1
View File
@@ -51,7 +51,7 @@ Will produce a message looking something like this; the `boundary` strings will
```
Content-Type: multipart/mixed;
boundary="mm-boundary"
Mime-Version: 1.0
MIME-Version: 1.0
Date: Tue, 1 Jul 2003 10:52:37 +0200
--mm-boundary
@@ -107,7 +107,7 @@ Content-Type: multipart/report;
boundary="report-boundary";
report-type="delivery-status"
Subject: Returned mail
Mime-Version: 1.0
MIME-Version: 1.0
Message-ID: <UUID@mta1.example.com>
To: sender@sender.example.com
From: Mail Delivery Subsystem <mailer-daemon@mta1.example.com>
@@ -156,7 +156,7 @@ Content-Type: multipart/report;
boundary="report-boundary";
report-type="delivery-status"
Subject: Returned mail
Mime-Version: 1.0
MIME-Version: 1.0
Message-ID: <UUID@mta1.example.com>
To: sender@sender.example.com
From: Mail Delivery Subsystem <mailer-daemon@mta1.example.com>
@@ -53,7 +53,7 @@ The set of supported conformance issues is:
|NEEDS_TRANSFER_ENCODING|The parsed content includes 8-bit content and thus needs to have transfer encoding applied in order to safely transit the 7-bit SMTP network|
|MISSING_DATE_HEADER|The `"Date:"` header is not present|
|MISSING_MESSAGE_ID_HEADER|The `"Message-ID:"` header is not present|
|MISSING_MIME_VERSION|The `"Mime-Version:"` header is either not present or is set to some other value than `"1.0"`|
|MISSING_MIME_VERSION|The `"MIME-Version:"` header is either not present or is set to some other value than `"1.0"`|
The way this method works is that it will attempt to parse the data associated
with the message into a MIME tree. The parsing stage will accumulate the set of