Skip to content
17 changes: 11 additions & 6 deletions Doc/library/smtplib.rst
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
5 changes: 5 additions & 0 deletions Lib/smtplib.py
Original file line number Diff line number Diff line change
Expand Up @@ -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']
Expand Down
26 changes: 19 additions & 7 deletions Lib/test/test_smtplib.py
Original file line number Diff line number Diff line change
Expand Up @@ -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 <fő[email protected]>"
Expand All @@ -1477,24 +1478,35 @@ 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 <fő[email protected]>
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?
""")
smtp = smtplib.SMTP(
HOST, self.port, local_hostname='localhost',
timeout=support.LOOPBACK_TIMEOUT)
self.addCleanup(smtp.close)
self.assertEqual(smtp.send_message(msg), {})
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I am moving these assertions further up so that I can extract the date from the message in order to build the expected string

""".format(date))

self.assertEqual(self.serv.last_mailfrom, 'fő[email protected]')
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, [])
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
Fix ``smtplib.send_message`` to add a ``Date`` header if it is missing as
per :rfc:`5322`.
Loading