Skip to content

Commit 936d9c6

Browse files
committed
Update test_smtplib.py from 3.13.11
1 parent 949f10b commit 936d9c6

File tree

1 file changed

+66
-18
lines changed

1 file changed

+66
-18
lines changed

Lib/test/test_smtplib.py

Lines changed: 66 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@
1717
import threading
1818

1919
import unittest
20+
import unittest.mock as mock
2021
from test import support, mock_socket
2122
from test.support import hashlib_helper
2223
from test.support import socket_helper
@@ -350,7 +351,7 @@ def testVRFY(self):
350351
timeout=support.LOOPBACK_TIMEOUT)
351352
self.addCleanup(smtp.close)
352353
expected = (252, b'Cannot VRFY user, but will accept message ' + \
353-
b'and attempt delivery')
354+
b'and attempt delivery')
354355
self.assertEqual(smtp.vrfy('[email protected]'), expected)
355356
self.assertEqual(smtp.verify('[email protected]'), expected)
356357
smtp.quit()
@@ -371,7 +372,7 @@ def testHELP(self):
371372
timeout=support.LOOPBACK_TIMEOUT)
372373
self.addCleanup(smtp.close)
373374
self.assertEqual(smtp.help(), b'Supported commands: EHLO HELO MAIL ' + \
374-
b'RCPT DATA RSET NOOP QUIT VRFY')
375+
b'RCPT DATA RSET NOOP QUIT VRFY')
375376
smtp.quit()
376377

377378
def testSend(self):
@@ -527,7 +528,7 @@ def testSendMessageWithAddresses(self):
527528
smtp.quit()
528529
# make sure the Bcc header is still in the message.
529530
self.assertEqual(m['Bcc'], 'John Root <root@localhost>, "Dinsdale" '
530-
531+
531532

532533
self.client_evt.set()
533534
self.serv_evt.wait()
@@ -766,7 +767,7 @@ def tearDown(self):
766767

767768
def testFailingHELO(self):
768769
self.assertRaises(smtplib.SMTPConnectError, smtplib.SMTP,
769-
HOST, self.port, 'localhost', 3)
770+
HOST, self.port, 'localhost', 3)
770771

771772

772773
class TooLongLineTests(unittest.TestCase):
@@ -804,14 +805,14 @@ def testLineTooLong(self):
804805
sim_users = {'[email protected]':'John A',
805806
'[email protected]':'Sally B',
806807
'[email protected]':'Ruth C',
807-
}
808+
}
808809

809810
sim_auth = ('[email protected]', 'somepassword')
810811
sim_cram_md5_challenge = ('PENCeUxFREJoU0NnbmhNWitOMjNGNn'
811812
'dAZWx3b29kLmlubm9zb2Z0LmNvbT4=')
812813
sim_lists = {'list-1':['[email protected]','[email protected]'],
813814
'list-2':['[email protected]',],
814-
}
815+
}
815816

816817
# Simulated SMTP channel & server
817818
class ResponseException(Exception): pass
@@ -830,6 +831,7 @@ class SimSMTPChannel(smtpd.SMTPChannel):
830831
def __init__(self, extra_features, *args, **kw):
831832
self._extrafeatures = ''.join(
832833
[ "250-{0}\r\n".format(x) for x in extra_features ])
834+
self.all_received_lines = []
833835
super(SimSMTPChannel, self).__init__(*args, **kw)
834836

835837
# AUTH related stuff. It would be nice if support for this were in smtpd.
@@ -844,6 +846,7 @@ def found_terminator(self):
844846
self.smtp_state = self.COMMAND
845847
self.push('%s %s' % (e.smtp_code, e.smtp_error))
846848
return
849+
self.all_received_lines.append(self.received_lines)
847850
super().found_terminator()
848851

849852

@@ -924,11 +927,14 @@ def _auth_cram_md5(self, arg=None):
924927
except ValueError as e:
925928
self.push('535 Splitting response {!r} into user and password '
926929
'failed: {}'.format(logpass, e))
927-
return False
928-
valid_hashed_pass = hmac.HMAC(
929-
sim_auth[1].encode('ascii'),
930-
self._decode_base64(sim_cram_md5_challenge).encode('ascii'),
931-
'md5').hexdigest()
930+
return
931+
pwd = sim_auth[1].encode('ascii')
932+
msg = self._decode_base64(sim_cram_md5_challenge).encode('ascii')
933+
try:
934+
valid_hashed_pass = hmac.HMAC(pwd, msg, 'md5').hexdigest()
935+
except ValueError:
936+
self.push('504 CRAM-MD5 is not supported')
937+
return
932938
self._authenticated(user, hashed_pass == valid_hashed_pass)
933939
# end AUTH related stuff.
934940

@@ -1170,8 +1176,7 @@ def auth_buggy(challenge=None):
11701176
finally:
11711177
smtp.close()
11721178

1173-
# TODO: RUSTPYTHON
1174-
@unittest.expectedFailure
1179+
@unittest.expectedFailure # TODO: RUSTPYTHON
11751180
@hashlib_helper.requires_hashdigest('md5', openssl=True)
11761181
def testAUTH_CRAM_MD5(self):
11771182
self.serv.add_feature("AUTH CRAM-MD5")
@@ -1181,8 +1186,40 @@ def testAUTH_CRAM_MD5(self):
11811186
self.assertEqual(resp, (235, b'Authentication Succeeded'))
11821187
smtp.close()
11831188

1184-
# TODO: RUSTPYTHON
1185-
@unittest.expectedFailure
1189+
@mock.patch("hmac.HMAC")
1190+
@mock.patch("smtplib._have_cram_md5_support", False)
1191+
def testAUTH_CRAM_MD5_blocked(self, hmac_constructor):
1192+
# CRAM-MD5 is the only "known" method by the server,
1193+
# but it is not supported by the client. In particular,
1194+
# no challenge will ever be sent.
1195+
self.serv.add_feature("AUTH CRAM-MD5")
1196+
smtp = smtplib.SMTP(HOST, self.port, local_hostname='localhost',
1197+
timeout=support.LOOPBACK_TIMEOUT)
1198+
self.addCleanup(smtp.close)
1199+
msg = re.escape("No suitable authentication method found.")
1200+
with self.assertRaisesRegex(smtplib.SMTPException, msg):
1201+
smtp.login(sim_auth[0], sim_auth[1])
1202+
hmac_constructor.assert_not_called() # call has been bypassed
1203+
1204+
@mock.patch("smtplib._have_cram_md5_support", False)
1205+
def testAUTH_CRAM_MD5_blocked_and_fallback(self):
1206+
# Test that PLAIN is tried after CRAM-MD5 failed
1207+
self.serv.add_feature("AUTH CRAM-MD5 PLAIN")
1208+
smtp = smtplib.SMTP(HOST, self.port, local_hostname='localhost',
1209+
timeout=support.LOOPBACK_TIMEOUT)
1210+
self.addCleanup(smtp.close)
1211+
with (
1212+
mock.patch.object(smtp, "auth_cram_md5") as smtp_auth_cram_md5,
1213+
mock.patch.object(
1214+
smtp, "auth_plain", wraps=smtp.auth_plain
1215+
) as smtp_auth_plain
1216+
):
1217+
resp = smtp.login(sim_auth[0], sim_auth[1])
1218+
smtp_auth_plain.assert_called_once()
1219+
smtp_auth_cram_md5.assert_not_called() # no call to HMAC constructor
1220+
self.assertEqual(resp, (235, b'Authentication Succeeded'))
1221+
1222+
@unittest.expectedFailure # TODO: RUSTPYTHON
11861223
@hashlib_helper.requires_hashdigest('md5', openssl=True)
11871224
def testAUTH_multiple(self):
11881225
# Test that multiple authentication methods are tried.
@@ -1193,8 +1230,7 @@ def testAUTH_multiple(self):
11931230
self.assertEqual(resp, (235, b'Authentication Succeeded'))
11941231
smtp.close()
11951232

1196-
# TODO: RUSTPYTHON
1197-
@unittest.expectedFailure
1233+
@unittest.expectedFailure # TODO: RUSTPYTHON
11981234
def test_auth_function(self):
11991235
supported = {'PLAIN', 'LOGIN'}
12001236
try:
@@ -1354,6 +1390,18 @@ def test_name_field_not_included_in_envelop_addresses(self):
13541390
self.assertEqual(self.serv._addresses['from'], '[email protected]')
13551391
self.assertEqual(self.serv._addresses['tos'], ['[email protected]'])
13561392

1393+
def test_lowercase_mail_from_rcpt_to(self):
1394+
m = 'A test message'
1395+
smtp = smtplib.SMTP(
1396+
HOST, self.port, local_hostname='localhost',
1397+
timeout=support.LOOPBACK_TIMEOUT)
1398+
self.addCleanup(smtp.close)
1399+
1400+
smtp.sendmail('John', 'Sally', m)
1401+
1402+
self.assertIn(['mail from:<John> size=14'], self.serv._SMTPchannel.all_received_lines)
1403+
self.assertIn(['rcpt to:<Sally>'], self.serv._SMTPchannel.all_received_lines)
1404+
13571405

13581406
class SimSMTPUTF8Server(SimSMTPServer):
13591407

@@ -1372,7 +1420,7 @@ def handle_accepted(self, conn, addr):
13721420
)
13731421

13741422
def process_message(self, peer, mailfrom, rcpttos, data, mail_options=None,
1375-
rcpt_options=None):
1423+
rcpt_options=None):
13761424
self.last_peer = peer
13771425
self.last_mailfrom = mailfrom
13781426
self.last_rcpttos = rcpttos

0 commit comments

Comments
 (0)