diff --git a/Lib/email/utils.py b/Lib/email/utils.py index 6889c5591bf030..a8216a62765e65 100644 --- a/Lib/email/utils.py +++ b/Lib/email/utils.py @@ -399,7 +399,7 @@ def encode_rfc2231(s, charset=None, language=None): return "%s'%s'%s" % (charset, language, s) -rfc2231_continuation = re.compile(r'^(?P\w+)\*((?P[0-9]+)\*?)?$', +rfc2231_continuation = re.compile(r'^(?P[\w-]+)\*((?P[0-9]+)\*?)?$', re.ASCII) def decode_params(params): diff --git a/Lib/test/test_email/test_email.py b/Lib/test/test_email/test_email.py index e40c82bba9af42..6d66238b885946 100644 --- a/Lib/test/test_email/test_email.py +++ b/Lib/test/test_email/test_email.py @@ -400,6 +400,18 @@ def test_continuation_sorting_part_order(self): filename = msg.get_filename() self.assertEqual(filename, 'foo bar.txt') + def test_continuation_with_hyphenated_name(self): + # gh-130110: parameter names containing hyphens were not recognized + # as RFC 2231 continuations and were left undecoded. + msg = email.message_from_string( + "Content-Disposition: attachment; " + "file-name*0*=\"utf-8''start\"; " + "file-name*1*=\"-middle-\"; " + "file-name*2*=\"end\"\n" + ) + value = msg.get_param('file-name', header='content-disposition') + self.assertEqual(value, ('utf-8', '', 'start-middle-end')) + def test_sorting_no_continuations(self): msg = email.message_from_string( "Content-Disposition: attachment; " diff --git a/Misc/NEWS.d/next/Library/2026-07-10-23-32-52.gh-issue-130110.R7xQz2.rst b/Misc/NEWS.d/next/Library/2026-07-10-23-32-52.gh-issue-130110.R7xQz2.rst new file mode 100644 index 00000000000000..f80f0bdf8b3893 --- /dev/null +++ b/Misc/NEWS.d/next/Library/2026-07-10-23-32-52.gh-issue-130110.R7xQz2.rst @@ -0,0 +1,3 @@ +:meth:`email.utils.decode_params` (and the parameter parsing it backs) now +correctly decodes :rfc:`2231` continuation parameter names that contain +hyphens, such as ``file-name*0*``.