diff -r c47a72627f0c Doc/library/smtplib.rst --- a/Doc/library/smtplib.rst Sun Jan 22 14:41:42 2017 +0800 +++ b/Doc/library/smtplib.rst Sun Jan 22 23:31:20 2017 -0500 @@ -499,7 +499,8 @@ :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, + If the message contains more than one set of :mailheader:`Resent-*` headers + in the same block (not separated by traces or other headers), a :exc:`ValueError` is raised, since there is no way to unambiguously detect the most recent set of :mailheader:`Resent-` headers. diff -r c47a72627f0c Lib/smtplib.py --- a/Lib/smtplib.py Sun Jan 22 14:41:42 2017 +0800 +++ b/Lib/smtplib.py Sun Jan 22 23:31:20 2017 -0500 @@ -914,28 +914,30 @@ # if there is more than one 'Resent-' block there's no way to # unambiguously determine which one is the most recent in all cases, # so rather than guess we raise a ValueError in that case. - # - # TODO implement heuristics to guess the correct Resent-* block with an - # option allowing the user to enable the heuristics. (It should be - # possible to guess correctly almost all of the time.) - self.ehlo_or_helo_if_needed() - resent = msg.get_all('Resent-Date') - if resent is None: - header_prefix = '' - elif len(resent) == 1: - header_prefix = 'Resent-' - else: - raise ValueError("message has more than one 'Resent-' header block") + #Resent or Original? (get the first resent-block) + resent_block = [] + for header in msg._headers: + if header[0].startswith('Resent'): + resent_block.append(header) + elif resent_block: + break + + if [header[0] for header in resent_block].count('Resent-Date') > 1: + raise ValueError("message has more than one 'Resent-Date' in same header block") + + header_prefix = 'Resent-' if len(resent_block) >= 1 else '' + headers_to_consider = dict(resent_block) if len(resent_block) >= 1 else msg + if from_addr is None: # Prefer the sender field per RFC 2822:3.6.2. from_addr = (msg[header_prefix + 'Sender'] - if (header_prefix + 'Sender') in msg + if (header_prefix + 'Sender') in headers_to_consider else msg[header_prefix + 'From']) if to_addrs is None: - addr_fields = [f for f in (msg[header_prefix + 'To'], - msg[header_prefix + 'Bcc'], - msg[header_prefix + 'Cc']) + addr_fields = [f for f in (headers_to_consider.get(header_prefix + 'To',None), + headers_to_consider.get(header_prefix + 'Bcc',None), + headers_to_consider.get(header_prefix + 'Cc',None)) if f is not None] to_addrs = [a[1] for a in email.utils.getaddresses(addr_fields)] # Make a local copy so we can delete the bcc headers. diff -r c47a72627f0c Lib/test/test_smtplib.py --- a/Lib/test/test_smtplib.py Sun Jan 22 14:41:42 2017 +0800 +++ b/Lib/test/test_smtplib.py Sun Jan 22 23:31:20 2017 -0500 @@ -531,7 +531,7 @@ re.MULTILINE) self.assertRegex(debugout, to_addr) - def testSendMessageMultipleResentRaises(self): + def testSendMessageMultipleResentInSameBlockRaises(self): m = email.mime.text.MIMEText('A test message') m['From'] = 'foo@bar.com' m['To'] = 'John' @@ -549,6 +549,43 @@ smtp.send_message(m) smtp.close() + def testSendMessageMultipleResentBlock(self): + first_date = 'Thu, 1 Jan 1970 17:42:00 +0000' + first_from = 'holy@grail.net' + first_to = 'Martha , Jeff' + m = email.mime.text.MIMEText('A test message') + m['From'] = 'foo@bar.com' + m['To'] = 'John' + m['CC'] = 'Sally, Fred' + m['Bcc'] = 'John Root , "Dinsdale" ' + m['Resent-Bcc'] = 'doe@losthope.net' + m['Resent-Date'] = 'Thu, 2 Jan 1970 17:42:00 +0000' + m['Resent-To'] = 'holy@grail.net' + m['Resent-From'] = 'Martha , Jeff' + # a message should have Resent headers block separated by Return-paths and traces + m._headers.insert(0,m.policy.header_store_parse('Return-Path', 'holy@grail.net')) + # this prepend to the "list" in email message class + m._headers.insert(0,m.policy.header_store_parse('Resent-Date', first_date)) + m._headers.insert(0,m.policy.header_store_parse('Resent-From', first_from)) + m._headers.insert(0,m.policy.header_store_parse('Resent-To', first_to)) + + smtp = smtplib.SMTP(HOST, self.port, local_hostname='localhost', timeout=3) + smtp.send_message(m) + self.assertEqual(first=m.get('Resent-Date'), + second=first_date, + msg=""" + The Resent-Date order is not read correctly or + something wrong happened. + """) + self.assertEqual(first=m.get('Resent-From'), + second=first_from, + msg=""" + The Resent-From order is not read correctly or + something wrong happened. + """) + smtp.close() + + class NonConnectingTests(unittest.TestCase): def testNotConnected(self):