From d2f31b4fdf89cb6e782002e4cd0cf50f5a5c2d06 Mon Sep 17 00:00:00 2001 From: Wez Furlong Date: Wed, 23 Sep 2026 08:29:16 +0100 Subject: [PATCH] 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 --- crates/kumo-log-types/src/rfc3464.rs | 10 +++++----- crates/kumod/src/http_server/inject_v1.rs | 4 ++-- crates/mailparsing/src/builder.rs | 6 +++--- crates/mailparsing/src/headermap.rs | 2 +- crates/mailparsing/src/mimepart.rs | 6 +++--- crates/mailparsing/src/parsed_header.rs | 2 +- crates/message/src/message.rs | 4 ++-- crates/mod-mimepart/src/headers.rs | 2 +- crates/mod-mimepart/test_builder.lua | 2 +- docs/changelog/main.md | 5 +++++ docs/reference/header/value.md | 2 +- docs/reference/headermap/mime_version.md | 4 ++-- docs/reference/headermap/set_mime_version.md | 2 +- docs/reference/kumo.mimepart/builder.md | 2 +- docs/reference/kumo/generate_rfc3464_message.md | 4 ++-- docs/reference/message/check_fix_conformance.md | 2 +- 16 files changed, 32 insertions(+), 27 deletions(-) diff --git a/crates/kumo-log-types/src/rfc3464.rs b/crates/kumo-log-types/src/rfc3464.rs index f19ae2753..0e02f883f 100644 --- a/crates/kumo-log-types/src/rfc3464.rs +++ b/crates/kumo-log-types/src/rfc3464.rs @@ -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: \r To: sender@sender.example.com\r From: Mail Delivery Subsystem \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: \r To: sender@sender.example.com\r From: Mail Delivery Subsystem \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: \r To: sender@sender.example.com\r From: Mail Delivery Subsystem \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: \r To: sender@sender.example.com\r From: Mail Delivery Subsystem \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: \r To: sender@sender.example.com\r From: Mail Delivery Subsystem \r diff --git a/crates/kumod/src/http_server/inject_v1.rs b/crates/kumod/src/http_server/inject_v1.rs index 9cbece312..66d6b9f75 100644 --- a/crates/kumod/src/http_server/inject_v1.rs +++ b/crates/kumod/src/http_server/inject_v1.rs @@ -1482,7 +1482,7 @@ Content-Transfer-Encoding: quoted-printable\r From: Me \r Subject: =?UTF-8?q?A_test_=F0=9F=9B=B3=EF=B8=8F?=\r To: "James Smythe" \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 \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" \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 diff --git a/crates/mailparsing/src/builder.rs b/crates/mailparsing/src/builder.rs index fae2f68b8..756eb6d9a 100644 --- a/crates/mailparsing/src/builder.rs +++ b/crates/mailparsing/src/builder.rs @@ -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 diff --git a/crates/mailparsing/src/headermap.rs b/crates/mailparsing/src/headermap.rs index 9b8495d5f..3a1b02414 100644 --- a/crates/mailparsing/src/headermap.rs +++ b/crates/mailparsing/src/headermap.rs @@ -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", diff --git a/crates/mailparsing/src/mimepart.rs b/crates/mailparsing/src/mimepart.rs index 8f29d48c8..200d1f4f4 100644 --- a/crates/mailparsing/src/mimepart.rs +++ b/crates/mailparsing/src/mimepart.rs @@ -1402,7 +1402,7 @@ Content-Type: text/plain;\r Content-Transfer-Encoding: quoted-printable\r Subject: hello there\r From: Someone \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: \r -Mime-Version: 1.0\r +MIME-Version: 1.0\r \r Body\r diff --git a/crates/mailparsing/src/parsed_header.rs b/crates/mailparsing/src/parsed_header.rs index 1d1d15041..f80bc7054 100644 --- a/crates/mailparsing/src/parsed_header.rs +++ b/crates/mailparsing/src/parsed_header.rs @@ -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 { diff --git a/crates/message/src/message.rs b/crates/message/src/message.rs index 0fb91f2cc..4bbaa81aa 100644 --- a/crates/message/src/message.rs +++ b/crates/message/src/message.rs @@ -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: \r -Mime-Version: 1.0\r +MIME-Version: 1.0\r \r Body\r diff --git a/crates/mod-mimepart/src/headers.rs b/crates/mod-mimepart/src/headers.rs index e716754c1..56ade87e8 100644 --- a/crates/mod-mimepart/src/headers.rs +++ b/crates/mod-mimepart/src/headers.rs @@ -427,7 +427,7 @@ const NAME_GETTER: &[(&str, fn(&Lua, &Header) -> mlua::Result)] = & ("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), diff --git a/crates/mod-mimepart/test_builder.lua b/crates/mod-mimepart/test_builder.lua index 2366ab7d3..5856c5a75 100644 --- a/crates/mod-mimepart/test_builder.lua +++ b/crates/mod-mimepart/test_builder.lua @@ -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\nHello\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\nHello\r\n--ma-boundary--\r\n' ) diff --git a/docs/changelog/main.md b/docs/changelog/main.md index e2c04e672..ac994f77b 100644 --- a/docs/changelog/main.md +++ b/docs/changelog/main.md @@ -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 diff --git a/docs/reference/header/value.md b/docs/reference/header/value.md index fbda87f81..7a4aa5747 100644 --- a/docs/reference/header/value.md +++ b/docs/reference/header/value.md @@ -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|| diff --git a/docs/reference/headermap/mime_version.md b/docs/reference/headermap/mime_version.md index 406985579..7bbbaf70f 100644 --- a/docs/reference/headermap/mime_version.md +++ b/docs/reference/headermap/mime_version.md @@ -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. diff --git a/docs/reference/headermap/set_mime_version.md b/docs/reference/headermap/set_mime_version.md index 7ecf8549e..0f61aff6b 100644 --- a/docs/reference/headermap/set_mime_version.md +++ b/docs/reference/headermap/set_mime_version.md @@ -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. diff --git a/docs/reference/kumo.mimepart/builder.md b/docs/reference/kumo.mimepart/builder.md index a0233f0cb..a42250e0f 100644 --- a/docs/reference/kumo.mimepart/builder.md +++ b/docs/reference/kumo.mimepart/builder.md @@ -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 diff --git a/docs/reference/kumo/generate_rfc3464_message.md b/docs/reference/kumo/generate_rfc3464_message.md index 2e16a7c89..c2117afa2 100644 --- a/docs/reference/kumo/generate_rfc3464_message.md +++ b/docs/reference/kumo/generate_rfc3464_message.md @@ -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: To: sender@sender.example.com From: Mail Delivery Subsystem @@ -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: To: sender@sender.example.com From: Mail Delivery Subsystem diff --git a/docs/reference/message/check_fix_conformance.md b/docs/reference/message/check_fix_conformance.md index d47ab1dfb..5f6b3b41f 100644 --- a/docs/reference/message/check_fix_conformance.md +++ b/docs/reference/message/check_fix_conformance.md @@ -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