diff --git a/Doc/library/smtplib.rst b/Doc/library/smtplib.rst index c5f8516f768a68..344514a8d434cf 100644 --- a/Doc/library/smtplib.rst +++ b/Doc/library/smtplib.rst @@ -510,12 +510,17 @@ An :class:`SMTP` instance has the following methods: specified in :rfc:`5322`\: *from_addr* is set to the :mailheader:`Sender` field if it is present, and otherwise to the :mailheader:`From` field. *to_addrs* combines the values (if any) of the :mailheader:`To`, - :mailheader:`Cc`, and :mailheader:`Bcc` fields from *msg*. If exactly one - set of :mailheader:`Resent-*` headers appear in the message, the regular - headers are ignored and the :mailheader:`Resent-*` headers are used instead. - If the message contains more than one set of :mailheader:`Resent-*` headers, - a :exc:`ValueError` is raised, since there is no way to unambiguously detect - the most recent set of :mailheader:`Resent-` headers. + :mailheader:`Cc`, and :mailheader:`Bcc` fields from *msg*. If there's no + :mailheader:`Date` header inside the message, ``send_message`` will add one to the data. + If exactly one set of :mailheader:`Resent-*` headers appear in the message, + the regular headers are ignored and the :mailheader:`Resent-*` headers are + used instead. If the message contains more than one set of + :mailheader:`Resent-*` headers, a :exc:`ValueError` is raised, since there + is no way to unambiguously detect the most recent set of + :mailheader:`Resent-` headers. + + .. versionchanged:: next + Support to add :mailheader:`Date` header to the message if one does not exist. ``send_message`` serializes *msg* using :class:`~email.generator.BytesGenerator` with ``\r\n`` as the *linesep*, and diff --git a/Lib/smtplib.py b/Lib/smtplib.py index 84d6d858e7dec1..99aad1d549bd30 100644 --- a/Lib/smtplib.py +++ b/Lib/smtplib.py @@ -935,6 +935,11 @@ def send_message(self, msg, from_addr=None, to_addrs=None, header_prefix = 'Resent-' else: raise ValueError("message has more than one 'Resent-' header block") + + # RFC 5322 section 3.6, 4th Paragraph + if msg.get('Date', None) is None: + # localtime: RFC 5322 section 3.3, 4th Paragraph + msg['Date'] = email.utils.formatdate(localtime=True) if from_addr is None: # Prefer the sender field per RFC 2822:3.6.2. from_addr = (msg[header_prefix + 'Sender'] diff --git a/Lib/test/test_smtplib.py b/Lib/test/test_smtplib.py index 4c9fc14bd43f54..d364851ef8e7d8 100644 --- a/Lib/test/test_smtplib.py +++ b/Lib/test/test_smtplib.py @@ -1467,6 +1467,7 @@ def test_send_unicode_with_SMTPUTF8_via_low_level_API(self): self.assertIn('SMTPUTF8', self.serv.last_mail_options) self.assertEqual(self.serv.last_rcpt_options, []) + @support.run_with_tz('UTC-02') def test_send_message_uses_smtputf8_if_addrs_non_ascii(self): msg = EmailMessage() msg['From'] = "Páolo " @@ -1477,6 +1478,20 @@ def test_send_message_uses_smtputf8_if_addrs_non_ascii(self): msg.set_content("oh là là, know what I mean, know what I mean?\n\n") # XXX smtpd converts received /r/n to /n, so we can't easily test that # we are successfully sending /r/n :(. + smtp = smtplib.SMTP( + HOST, self.port, local_hostname='localhost', + timeout=support.LOOPBACK_TIMEOUT) + self.addCleanup(smtp.close) + self.assertEqual(smtp.send_message(msg), {}) + + last_message = self.serv.last_message.decode() + date = email.message_from_string(last_message)['Date'] + # asserts RFC 5322 section 3.3 4th Paragraph + self.assertEqual( + email.utils.parsedate_to_datetime(date).tzname(), + "UTC+02:00" + ) + expected = textwrap.dedent("""\ From: Páolo To: Dinsdale @@ -1484,17 +1499,14 @@ def test_send_message_uses_smtputf8_if_addrs_non_ascii(self): Content-Type: text/plain; charset="utf-8" Content-Transfer-Encoding: 8bit MIME-Version: 1.0 + Date: {} oh là là, know what I mean, know what I mean? - """) - smtp = smtplib.SMTP( - HOST, self.port, local_hostname='localhost', - timeout=support.LOOPBACK_TIMEOUT) - self.addCleanup(smtp.close) - self.assertEqual(smtp.send_message(msg), {}) + """.format(date)) + self.assertEqual(self.serv.last_mailfrom, 'főo@bar.com') self.assertEqual(self.serv.last_rcpttos, ['Dinsdale']) - self.assertEqual(self.serv.last_message.decode(), expected) + self.assertEqual(last_message, expected) self.assertIn('BODY=8BITMIME', self.serv.last_mail_options) self.assertIn('SMTPUTF8', self.serv.last_mail_options) self.assertEqual(self.serv.last_rcpt_options, []) diff --git a/Misc/NEWS.d/next/Library/2025-07-20-09-51-25.bpo-28879.SLDgcy.rst b/Misc/NEWS.d/next/Library/2025-07-20-09-51-25.bpo-28879.SLDgcy.rst new file mode 100644 index 00000000000000..f3fd71b58d89e1 --- /dev/null +++ b/Misc/NEWS.d/next/Library/2025-07-20-09-51-25.bpo-28879.SLDgcy.rst @@ -0,0 +1,2 @@ +Fix ``smtplib.send_message`` to add a ``Date`` header if it is missing as +per :rfc:`5322`.