Ticket #3989: 3989-ESMTP_SSL-draft1.patch

File 3989-ESMTP_SSL-draft1.patch, 5.5 KB (added by argonemyth, 8 years ago)

first attempt to fix the bug!

  • twisted/mail/test/test_smtp.py

     
    1111from twisted.trial import unittest, util
    1212from twisted.protocols import basic, loopback
    1313from twisted.mail import smtp
    14 from twisted.internet import defer, protocol, reactor, interfaces
     14from twisted.internet import defer, protocol, reactor, interfaces, ssl
    1515from twisted.internet import address, error, task
    1616from twisted.test.proto_helpers import StringTransport
    1717
     
    15181518        client.sentMail(199, "Test response", 1, addresses, client.log)
    15191519
    15201520        return onDone
     1521
     1522
     1523
     1524class SimpleMessage(object):
     1525    """
     1526    L{SimpleMessage} is an L{IMessage} which discards the buffer in its
     1527    C{eomReceived} method. This is used in L{SSLTestCase}.
     1528    """
     1529    implements(smtp.IMessage)
     1530
     1531    def __init__(self, user):
     1532        self.buffer = []
     1533
     1534    def lineReceived(self, line):
     1535        self.buffer.append(line)
     1536
     1537    def eomReceived(self):
     1538        self.buffer = None
     1539        return defer.succeed(None)
     1540
     1541    def connectionLost(self):
     1542        self.buffer = None
     1543
     1544
     1545
     1546class SimpleRealm:
     1547    """
     1548    This realm is used in L{SSLTestCase}.
     1549    """
     1550    def requestAvatar(self, avatarId, mind, *interfaces):
     1551        return smtp.IMessageDelivery, SimpleDelivery(SimpleMessage), lambda: None
     1552
     1553
     1554
     1555class SimpleESMTP(smtp.ESMTP):
     1556    """
     1557    A simple ESMTP server which adds an authentication method
     1558    for our testing purpose. It's used in L{SSLTestCase}
     1559    """
     1560    realm = SimpleRealm()
     1561
     1562    def __init__(self, chal = None, contextFactory = None):
     1563        smtp.ESMTP.__init__(self)
     1564        self.challengers = {'LOGIN': imap4.LOGINCredentials}
     1565        p = cred.portal.Portal(self.realm)
     1566        p.registerChecker(DummyChecker())
     1567        self.portal = p
     1568
     1569
     1570
     1571class SSLTestCase(unittest.TestCase):
     1572    """
     1573    We need to simulate a reactor.connectSSL to test if the ESMTP client
     1574    works well with connectSSL while requireTransportSecurity is True.
     1575    """
     1576    message = "some message text"
     1577
     1578    def test_connectSSL(self):
     1579        # Create a SSL testing server
     1580        serverCTX = ServerTLSContext()
     1581        serverFactory = protocol.ServerFactory()
     1582        serverFactory.protocol = SimpleESMTP
     1583        serverPort = reactor.listenSSL(0, serverFactory, serverCTX,
     1584                                       interface='127.0.0.1')
     1585        serverHost = serverPort.getHost()
     1586        self.addCleanup(serverPort.stopListening)
     1587
     1588        # Set up a client to deliver a message to the above created server.
     1589        clientCTX = ssl.ClientContextFactory()
     1590        sentDeferred = defer.Deferred()
     1591        # Although specifying requireTransportSecurity is not required (
     1592        # it's the default value), I think it's better to be explicit here.
     1593        clientFactory = smtp.ESMTPSenderFactory(
     1594            "testuser", 'testpassword', "test@example.org",
     1595            "alice@example.org", StringIO(self.message),
     1596            sentDeferred, requireTransportSecurity = True)
     1597        clientConnector = reactor.connectSSL(
     1598            serverHost.host, serverHost.port, clientFactory, clientCTX)
     1599        self.addCleanup(clientConnector.disconnect)
     1600
     1601
     1602        def onDone(result):
     1603            expected = (1, [('alice@example.org', 250, 'Recipient address accepted')])
     1604            self.assertEqual(result, expected)
     1605        sentDeferred.addCallback(onDone)
     1606        return sentDeferred
  • twisted/mail/smtp.py

     
    1919from twisted.internet import defer
    2020from twisted.internet import error
    2121from twisted.internet import reactor
    22 from twisted.internet.interfaces import ITLSTransport
     22from twisted.internet.interfaces import ITLSTransport, ISSLTransport
    2323from twisted.python import log
    2424from twisted.python import util
    2525
     
    12331233    # Refuse to proceed if TLS is not available
    12341234    requireTransportSecurity = False
    12351235
    1236     # Indicate whether or not our transport can be considered secure.
    1237     tlsMode = False
    1238 
    12391236    # ClientContextFactory to use for STARTTLS
    12401237    context = None
    12411238
     
    12441241        self.authenticators = []
    12451242        self.secret = secret
    12461243        self.context = contextFactory
    1247         self.tlsMode = False
    12481244
    12491245
    12501246    def esmtpEHLORequired(self, code=-1, resp=None):
     
    13161312            else:
    13171313                items[e[0]] = None
    13181314
    1319         if self.tlsMode:
    1320             self.authenticate(code, resp, items)
    1321         else:
    1322             self.tryTLS(code, resp, items)
     1315        self.tryTLS(code, resp, items)
    13231316
    13241317    def tryTLS(self, code, resp, items):
     1318        sslTransport = ISSLTransport.providedBy(self.transport)
     1319
    13251320        if self.context and 'STARTTLS' in items:
    13261321            self._expected = [220]
    13271322            self._okresponse = self.esmtpState_starttls
    13281323            self._failresponse = self.esmtpTLSFailed
    13291324            self.sendLine('STARTTLS')
    1330         elif self.requireTransportSecurity:
    1331             self.tlsMode = False
     1325        elif self.requireTransportSecurity and not sslTransport:
    13321326            self.esmtpTLSRequired()
    13331327        else:
    1334             self.tlsMode = False
    13351328            self.authenticate(code, resp, items)
    13361329
    13371330    def esmtpState_starttls(self, code, resp):
    13381331        try:
    13391332            self.transport.startTLS(self.context)
    1340             self.tlsMode = True
    13411333        except:
    13421334            log.err()
    13431335            self.esmtpTLSFailed(451)