Skip to content

bpo-30681: Change error handling to return None in case of invalid date #2229

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
wants to merge 2 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions Lib/email/headerregistry.py
Original file line number Diff line number Diff line change
Expand Up @@ -300,7 +300,13 @@ def parse(cls, value, kwds):
kwds['parse_tree'] = parser.TokenList()
return
if isinstance(value, str):
kwds['decoded'] = value
value = utils.parsedate_to_datetime(value)
if value is None:
kwds['defects'].append(errors.InvalidHeaderDefect('Invalid value in date'))
kwds['datetime'] = None
kwds['parse_tree'] = parser.TokenList()
return
kwds['datetime'] = value
kwds['decoded'] = utils.format_datetime(kwds['datetime'])
kwds['parse_tree'] = cls.value_parser(kwds['decoded'])
Expand Down
18 changes: 13 additions & 5 deletions Lib/email/utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -207,11 +207,19 @@ def make_msgid(idstring=None, domain=None):


def parsedate_to_datetime(data):
*dtuple, tz = _parsedate_tz(data)
if tz is None:
return datetime.datetime(*dtuple[:6])
return datetime.datetime(*dtuple[:6],
tzinfo=datetime.timezone(datetime.timedelta(seconds=tz)))
try:
*dtuple, tz = _parsedate_tz(data)
except TypeError:
# _parsedate_tz(data) returned None due to failure to parse
return None
try:
if tz is None:
return datetime.datetime(*dtuple[:6])
return datetime.datetime(*dtuple[:6],
tzinfo=datetime.timezone(datetime.timedelta(seconds=tz)))
except ValueError:
# Date parsed ok, but one or more component values are invalid
return None


def parseaddr(addr):
Expand Down
6 changes: 4 additions & 2 deletions Lib/test/test_email/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -37,9 +37,11 @@ def __init__(self, *args, **kw):
# Backward compatibility to minimize test_email test changes.
ndiffAssertEqual = unittest.TestCase.assertEqual

def _msgobj(self, filename):
def _msgobj(self, filename, policy=None):
if policy is None:
policy = self.policy
with openfile(filename) as fp:
return email.message_from_file(fp, policy=self.policy)
return email.message_from_file(fp, policy=policy)

def _str_msg(self, string, message=None, policy=None):
if policy is None:
Expand Down
6 changes: 6 additions & 0 deletions Lib/test/test_email/data/msg_47.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
Date: Tue, 06 Jun 2017 27:39:33 +0600
From: "[email protected]" <[email protected]>
To: <[email protected]>
Subject: VIARGA|CIALIS|LEVITRA

### PHARRMACY ON1INE 24/7 ###
31 changes: 27 additions & 4 deletions Lib/test/test_email/test_email.py
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,7 @@
from email import iterators
from email import base64mime
from email import quoprimime
from email._policybase import compat32

from test.support import unlink, start_threads
from test.test_email import openfile, TestEmailBase
Expand Down Expand Up @@ -759,6 +760,10 @@ def test_unicode_body_defaults_to_utf8_encoding(self):
w4kgdGVzdGFiYwo=
"""))

def test_date_header_from_message_with_invalid_date(self):
msg = self._msgobj('msg_47.txt', policy=email.policy.default)
self.assertEqual(msg['date'], 'Tue, 06 Jun 2017 27:39:33 +0600')


# Test the email.encoders module
class TestEncoders(unittest.TestCase):
Expand Down Expand Up @@ -2710,10 +2715,12 @@ class TestIdempotent(TestEmailBase):

linesep = '\n'

def _msgobj(self, filename):
def _msgobj(self, filename, policy=None):
if policy is None:
policy = self.policy
with openfile(filename) as fp:
data = fp.read()
msg = email.message_from_string(data)
msg = email.message_from_string(data, policy=policy)
return msg, data

def _idempotent(self, msg, text, unixfrom=False):
Expand Down Expand Up @@ -2815,6 +2822,10 @@ def test_message_signed_idempotent(self):
msg, text = self._msgobj('msg_45.txt')
self._idempotent(msg, text)

def test_invalid_date(self):
msg, text = self._msgobj('msg_47.txt', policy=email.policy.default)
self._idempotent(msg, text)

def test_content_type(self):
eq = self.assertEqual
# Get a message object and reset the seek pointer for other tests
Expand Down Expand Up @@ -3012,6 +3023,16 @@ def test_parsedate_acceptable_to_time_functions(self):
eq(time.localtime(t)[:6], timetup[:6])
eq(int(time.strftime('%Y', timetup[:9])), 2003)

def test_parsedate_to_datetime_returns_None_for_invalid_strings(self):
self.assertIsNone(utils.parsedate_to_datetime(''))
self.assertIsNone(utils.parsedate_to_datetime('0'))
self.assertIsNone(utils.parsedate_to_datetime('A Complete Waste of Time'))

def test_parsedate_to_datetime_returns_None_for_invalid_dates(self):
self.assertIsNone(utils.parsedate_to_datetime('Tue, 06 Jun 2017 27:39:33 +0600'))
self.assertIsNone(utils.parsedate_to_datetime('Tue, 06 Jun 2017 07:39:33 +2600'))
self.assertIsNone(utils.parsedate_to_datetime('Tue, 06 Jun 2017 27:39:33'))

def test_mktime_tz(self):
self.assertEqual(utils.mktime_tz((1970, 1, 1, 0, 0, 0,
-1, -1, -1, 0)), 0)
Expand Down Expand Up @@ -4154,11 +4175,13 @@ class BaseTestBytesGeneratorIdempotent:

maxDiff = None

def _msgobj(self, filename):
def _msgobj(self, filename, policy=None):
if policy is None:
policy = self.policy
with openfile(filename, 'rb') as fp:
data = fp.read()
data = self.normalize_linesep_regex.sub(self.blinesep, data)
msg = email.message_from_bytes(data)
msg = email.message_from_bytes(data, policy=policy)
return msg, data

def _idempotent(self, msg, data, unixfrom=False):
Expand Down