From 4634676cec29def3c70cf84ba00e6d7586bbaab7 Mon Sep 17 00:00:00 2001 From: R David Murray Date: Mon, 18 Jul 2011 21:38:54 -0400 Subject: [PATCH] #7484: no more <> around addresses in VRFY or EXPN The RFC doesn't say that they are allowed; apparently many mailers accept them, but not postfix. Contributions to this patch were made by Felipe Cruz and Catalin Iacob. The changeset also adds additional indirect tests for quoteaddr (null address and IDNA-encoded address). --- Lib/smtplib.py | 11 +++++++++-- Lib/test/test_smtplib.py | 33 ++++++++++++++++++++++++--------- Misc/NEWS | 3 +++ 3 files changed, 36 insertions(+), 11 deletions(-) diff --git a/Lib/smtplib.py b/Lib/smtplib.py index 9080e4ae570..7b97a6a88ff 100644 --- a/Lib/smtplib.py +++ b/Lib/smtplib.py @@ -152,6 +152,13 @@ def quoteaddr(addr): else: return "<%s>" % m +def _addr_only(addrstring): + displayname, addr = email.utils.parseaddr(addrstring) + if (displayname, addr) == ('', ''): + # parseaddr couldn't parse it, so use it as is. + return addrstring + return addr + # Legacy method kept for backward compatibility. def quotedata(data): """Quote data for email. @@ -514,14 +521,14 @@ class SMTP: def verify(self, address): """SMTP 'verify' command -- checks for address validity.""" - self.putcmd("vrfy", quoteaddr(address)) + self.putcmd("vrfy", _addr_only(address)) return self.getreply() # a.k.a. vrfy = verify def expn(self, address): """SMTP 'expn' command -- expands a mailing list.""" - self.putcmd("expn", quoteaddr(address)) + self.putcmd("expn", _addr_only(address)) return self.getreply() # some useful methods diff --git a/Lib/test/test_smtplib.py b/Lib/test/test_smtplib.py index bacfbdfe514..d94c88e8bec 100644 --- a/Lib/test/test_smtplib.py +++ b/Lib/test/test_smtplib.py @@ -293,6 +293,23 @@ class DebuggingServerTests(unittest.TestCase): mexpect = '%s%s\n%s' % (MSG_BEGIN, m, MSG_END) self.assertEqual(self.output.getvalue(), mexpect) + def testSendNullSender(self): + m = 'A test message' + smtp = smtplib.SMTP(HOST, self.port, local_hostname='localhost', timeout=3) + smtp.sendmail('<>', 'Sally', m) + # XXX (see comment in testSend) + time.sleep(0.01) + smtp.quit() + + self.client_evt.set() + self.serv_evt.wait() + self.output.flush() + mexpect = '%s%s\n%s' % (MSG_BEGIN, m, MSG_END) + self.assertEqual(self.output.getvalue(), mexpect) + debugout = smtpd.DEBUGSTREAM.getvalue() + sender = re.compile("^sender: <>$", re.MULTILINE) + self.assertRegex(debugout, sender) + def testSendMessage(self): m = email.mime.text.MIMEText('A test message') smtp = smtplib.SMTP(HOST, self.port, local_hostname='localhost', timeout=3) @@ -523,7 +540,7 @@ class BadHELOServerTests(unittest.TestCase): sim_users = {'Mr.A@somewhere.com':'John A', - 'Ms.B@somewhere.com':'Sally B', + 'Ms.B@xn--fo-fka.com':'Sally B', 'Mrs.C@somewhereesle.com':'Ruth C', } @@ -539,7 +556,7 @@ sim_auth_credentials = { sim_auth_login_password = 'C29TZXBHC3N3B3JK' sim_lists = {'list-1':['Mr.A@somewhere.com','Mrs.C@somewhereesle.com'], - 'list-2':['Ms.B@somewhere.com',], + 'list-2':['Ms.B@xn--fo-fka.com',], } # Simulated SMTP channel & server @@ -560,15 +577,14 @@ class SimSMTPChannel(smtpd.SMTPChannel): self.push(resp) def smtp_VRFY(self, arg): - raw_addr = email.utils.parseaddr(arg)[1] - quoted_addr = smtplib.quoteaddr(arg) - if raw_addr in sim_users: - self.push('250 %s %s' % (sim_users[raw_addr], quoted_addr)) + # For max compatibility smtplib should be sending the raw address. + if arg in sim_users: + self.push('250 %s %s' % (sim_users[arg], smtplib.quoteaddr(arg))) else: self.push('550 No such user: %s' % arg) def smtp_EXPN(self, arg): - list_name = email.utils.parseaddr(arg)[1].lower() + list_name = arg.lower() if list_name in sim_lists: user_list = sim_lists[list_name] for n, user_email in enumerate(user_list): @@ -688,8 +704,7 @@ class SMTPSimTests(unittest.TestCase): self.assertEqual(smtp.vrfy(email), expected_known) u = 'nobody@nowhere.com' - expected_unknown = (550, ('No such user: %s' - % smtplib.quoteaddr(u)).encode('ascii')) + expected_unknown = (550, ('No such user: %s' % u).encode('ascii')) self.assertEqual(smtp.vrfy(u), expected_unknown) smtp.quit() diff --git a/Misc/NEWS b/Misc/NEWS index f444709ab50..3325966477e 100644 --- a/Misc/NEWS +++ b/Misc/NEWS @@ -34,6 +34,9 @@ Core and Builtins Library ------- +- Issue #7484: smtplib no longer puts <> around addresses in VRFY and EXPN + commands; they aren't required and in fact postfix doesn't support that form. + - Close the call queue in concurrent.futures.ProcessPoolExecutor when shutdown() is called, without waiting for the garbage collector to kick in.