Repository: aio-libs/aiosmtpd
Pull request #90 (2017-05-11 01:07:31)

Add some coverage and clean up #68


Patch:
diff --git a/aiosmtpd/smtp.py b/aiosmtpd/smtp.py
index 2e1a5da..85dfa05 100644
--- a/aiosmtpd/smtp.py
+++ b/aiosmtpd/smtp.py
@@ -388,7 +388,7 @@ class SMTP(asyncio.StreamReaderProtocol):
         keylen = len(keyword)
         if arg[:keylen].upper() == keyword:
             return arg[keylen:].strip()
-        return ''
+        return None
 
     def _getaddr(self, arg):
         if not arg:
@@ -397,7 +397,12 @@ class SMTP(asyncio.StreamReaderProtocol):
             address, rest = get_angle_addr(arg)
         else:
             address, rest = get_addr_spec(arg)
-        return address.addr_spec, rest
+        try:
+            address = address.addr_spec
+        except IndexError:
+            # Workaround http://bugs.python.org/issue27931
+            address = None
+        return address, rest
 
     def _getparams(self, params):
         # Return params as dictionary. Return None if not all parameters
@@ -479,8 +484,11 @@ class SMTP(asyncio.StreamReaderProtocol):
             yield from self.push(syntaxerr)
             return
         arg = self._strip_command_keyword('FROM:', arg)
+        if arg is None:
+            yield from self.push(syntaxerr)
+            return
         address, params = self._getaddr(arg)
-        if not address:
+        if address is None:
             yield from self.push(syntaxerr)
             return
         if not self.session.extended_smtp and params:
@@ -547,7 +555,13 @@ class SMTP(asyncio.StreamReaderProtocol):
             yield from self.push(syntaxerr)
             return
         arg = self._strip_command_keyword('TO:', arg)
+        if arg is None:
+            yield from self.push(syntaxerr)
+            return
         address, params = self._getaddr(arg)
+        if address is None:
+            yield from self.push(syntaxerr)
+            return
         if not address:
             yield from self.push(syntaxerr)
             return


Test patch:
diff --git a/aiosmtpd/tests/test_smtp.py b/aiosmtpd/tests/test_smtp.py
index e02af93..d21b895 100644
--- a/aiosmtpd/tests/test_smtp.py
+++ b/aiosmtpd/tests/test_smtp.py
@@ -11,7 +11,7 @@ from aiosmtpd.testing.helpers import reset_connection
 from contextlib import ExitStack
 from smtplib import (
     SMTP, SMTPDataError, SMTPResponseException, SMTPServerDisconnected)
-from unittest.mock import Mock, patch
+from unittest.mock import Mock, PropertyMock, patch
 
 CRLF = '\r\n'
 BCRLF = b'\r\n'
@@ -471,6 +471,16 @@ class TestSMTP(unittest.TestCase):
                 response,
                 b'Syntax: MAIL FROM: <address> [SP <mail-parameters>]')
 
+    # Test the workaround http://bugs.python.org/issue27931
+    @patch('email._header_value_parser.AngleAddr.addr_spec',
+           side_effect=IndexError, new_callable=PropertyMock)
+    def test_mail_fail_parse_email(self, addr_spec):
+        with SMTP(*self.address) as client:
+            client.helo('example.com')
+            code, response = client.docmd('MAIL FROM: <""@example.com>')
+            self.assertEqual(code, 501)
+            self.assertEqual(response, b'Syntax: MAIL FROM: <address>')
+
     def test_rcpt_no_helo(self):
         with SMTP(*self.address) as client:
             code, response = client.docmd('RCPT TO: <anne@example.com>')
@@ -495,6 +505,16 @@ class TestSMTP(unittest.TestCase):
             self.assertEqual(code, 501)
             self.assertEqual(response, b'Syntax: RCPT TO: <address>')
 
+    def test_rcpt_no_to(self):
+        with SMTP(*self.address) as client:
+            code, response = client.helo('example.com')
+            self.assertEqual(code, 250)
+            code, response = client.docmd('MAIL FROM: <anne@example.com>')
+            self.assertEqual(code, 250)
+            code, response = client.docmd('RCPT <anne@example.com')
+            self.assertEqual(code, 501)
+            self.assertEqual(response, b'Syntax: RCPT TO: <address>')
+
     def test_rcpt_no_arg_esmtp(self):
         with SMTP(*self.address) as client:
             code, response = client.ehlo('example.com')
@@ -556,6 +576,22 @@ class TestSMTP(unittest.TestCase):
                 response,
                 b'RCPT TO parameters not recognized or not implemented')
 
+    # Test the workaround http://bugs.python.org/issue27931
+    @patch('email._header_value_parser.AngleAddr.addr_spec',
+           new_callable=PropertyMock)
+    def test_rcpt_fail_parse_email(self, addr_spec):
+        with SMTP(*self.address) as client:
+            code, response = client.ehlo('example.com')
+            self.assertEqual(code, 250)
+            code, response = client.docmd('MAIL FROM: <anne@example.com>')
+            self.assertEqual(code, 250)
+            addr_spec.side_effect = IndexError
+            code, response = client.docmd('RCPT TO: <""@example.com>')
+            self.assertEqual(code, 501)
+            self.assertEqual(
+                response,
+                b'Syntax: RCPT TO: <address> [SP <mail-parameters>]')
+
     def test_rset(self):
         with SMTP(*self.address) as client:
             code, response = client.rset()