#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).
This commit is contained in:
R David Murray 2011-07-18 21:38:54 -04:00
parent ae4a78b0a8
commit 4634676cec
3 changed files with 36 additions and 11 deletions

View File

@ -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

View File

@ -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()

View File

@ -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.