diff --git a/Doc/library/smtplib.rst b/Doc/library/smtplib.rst index b3cc60357f554a..509eedbab72e8c 100644 --- a/Doc/library/smtplib.rst +++ b/Doc/library/smtplib.rst @@ -522,12 +522,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:: 3.2 + 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 e2dbbbcf2e6d16..5e2d3f4b02ca1f 100755 --- a/Lib/smtplib.py +++ b/Lib/smtplib.py @@ -932,6 +932,10 @@ 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: + msg['Date'] = email.utils.formatdate() 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 7816ed34886e95..31af316c88cfe1 100644 --- a/Lib/test/test_smtplib.py +++ b/Lib/test/test_smtplib.py @@ -23,7 +23,7 @@ from test.support import hashlib_helper from test.support import socket_helper from test.support import threading_helper -from unittest.mock import Mock +from unittest.mock import Mock, patch HOST = socket_helper.HOST @@ -1342,36 +1342,40 @@ def test_send_unicode_with_SMTPUTF8_via_low_level_API(self): self.assertEqual(self.serv.last_rcpt_options, []) def test_send_message_uses_smtputf8_if_addrs_non_ascii(self): - msg = EmailMessage() - msg['From'] = "Páolo " - msg['To'] = 'Dinsdale' - msg['Subject'] = 'Nudge nudge, wink, wink \u1F609' - # XXX I don't know why I need two \n's here, but this is an existing - # bug (if it is one) and not a problem with the new functionality. - 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 :(. - expected = textwrap.dedent("""\ - From: Páolo - To: Dinsdale - Subject: Nudge nudge, wink, wink \u1F609 - Content-Type: text/plain; charset="utf-8" - Content-Transfer-Encoding: 8bit - MIME-Version: 1.0 - - 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), {}) - 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.assertIn('BODY=8BITMIME', self.serv.last_mail_options) - self.assertIn('SMTPUTF8', self.serv.last_mail_options) - self.assertEqual(self.serv.last_rcpt_options, []) + expected_date = "Thu, 19 Mar 2020 00:59:43 -0000" + with patch("email.utils.formatdate") as date_mock: + date_mock.return_value = expected_date + msg = EmailMessage() + msg['From'] = "Páolo " + msg['To'] = 'Dinsdale' + msg['Subject'] = 'Nudge nudge, wink, wink \u1F609' + # XXX I don't know why I need two \n's here, but this is an existing + # bug (if it is one) and not a problem with the new functionality. + 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 :(. + expected = textwrap.dedent("""\ + From: Páolo + To: Dinsdale + Subject: Nudge nudge, wink, wink \u1F609 + 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? + """.format(expected_date)) + smtp = smtplib.SMTP( + HOST, self.port, local_hostname='localhost', + timeout=support.LOOPBACK_TIMEOUT) + self.addCleanup(smtp.close) + self.assertEqual(smtp.send_message(msg), {}) + 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.assertIn('BODY=8BITMIME', self.serv.last_mail_options) + self.assertIn('SMTPUTF8', self.serv.last_mail_options) + self.assertEqual(self.serv.last_rcpt_options, []) EXPECTED_RESPONSE = encode_base64(b'\0psu\0doesnotexist', eol='') diff --git a/Misc/NEWS.d/next/Library/2018-01-13-15-52-00.bpo-28879.aW2gj0.rst b/Misc/NEWS.d/next/Library/2018-01-13-15-52-00.bpo-28879.aW2gj0.rst new file mode 100644 index 00000000000000..3cd194ba82f5f4 --- /dev/null +++ b/Misc/NEWS.d/next/Library/2018-01-13-15-52-00.bpo-28879.aW2gj0.rst @@ -0,0 +1,2 @@ +Fix ``smtplib.send_message`` to add a ``Date`` header if it is missing as per +RFC5322. \ No newline at end of file pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy