Ticket #3989: 3989-ESMTP_SSL-draft2.patch

File 3989-ESMTP_SSL-draft2.patch, 5.0 KB (added by argonemyth, 8 years ago)
  • 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
    16 from twisted.test.proto_helpers import StringTransport
     16from twisted.test.proto_helpers import StringTransport, MemoryReactor
    1717
    1818from twisted import cred
    1919import twisted.cred.error
     
    15181518        client.sentMail(199, "Test response", 1, addresses, client.log)
    15191519
    15201520        return onDone
     1521
     1522
     1523
     1524class SSLStringTransport(StringTransport):
     1525    """
     1526    Faking a string transport that implements C{ISSLTransport}.
     1527    """
     1528    from twisted.internet.interfaces import ISSLTransport
     1529    implements(ISSLTransport) # not really, just faking it
     1530
     1531
     1532
     1533class CustomMemoryReactor(MemoryReactor):
     1534    def connectSSL(self, host, port, factory, contextFactory):
     1535        """
     1536        Returns a client for our testing purpose.
     1537        """
     1538        conn = MemoryReactor.connectSSL(self, host, port,
     1539                                        factory, contextFactory)
     1540        client = factory.buildProtocol(None)
     1541        client.makeConnection(SSLStringTransport())
     1542        return conn, client
     1543
     1544
     1545
     1546class SSLTestCase(unittest.TestCase):
     1547    """
     1548    We need to simulate a reactor.connectSSL to test if the ESMTP client
     1549    works well with connectSSL while requireTransportSecurity is True.
     1550    """
     1551    reactor = CustomMemoryReactor()
     1552
     1553    def test_connectSSL(self):
     1554        """
     1555        When requireTransportSecurity is true and the client is connected
     1556        via SSL, the server would choose to authenticate an user over the
     1557        ssl transport.
     1558        """
     1559        # Set up a test client
     1560        clientCTX = ssl.ClientContextFactory()
     1561        sentDeferred = defer.Deferred()
     1562
     1563        # Although specifying requireTransportSecurity is not required (
     1564        # it's the default value), I think it's better to be explicit here.
     1565        clientFactory = smtp.ESMTPSenderFactory(
     1566            "testuser", 'testpassword', "test@example.org",
     1567            "alice@example.org", StringIO("message"),
     1568            sentDeferred, requireTransportSecurity = True)
     1569        clientConnector, client = self.reactor.connectSSL(
     1570            'localhost', 3422, clientFactory, clientCTX)
     1571
     1572        # Feed some server messages to our client
     1573        client.dataReceived(
     1574            "220 localhost NO UCE NO UBE NO RELAY PROBES ESMTP\r\n"
     1575            "250-localhost Hello 127.0.0.1, nice to meet you\r\n"
     1576            "250 AUTH LOGIN\r\n"
     1577            "334 VXNlciBOYW1lAA==\r\n"
     1578            "235 Authentication successful.\r\n"
     1579        )
     1580
     1581        expected = (
     1582            "EHLO localhost\r\n"
     1583            "AUTH LOGIN\r\n"
     1584            "dGVzdHVzZXI=\r\n"
     1585            "MAIL FROM:<test@example.org>\r\n"
     1586        )
     1587        data = client.transport.value()
     1588        client.transport.clear()
     1589        self.assertEqual(data, expected)
     1590        self.addCleanup(clientConnector.disconnect)
  • 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
     
    12441244        self.authenticators = []
    12451245        self.secret = secret
    12461246        self.context = contextFactory
    1247         self.tlsMode = False
    12481247
    12491248
    12501249    def esmtpEHLORequired(self, code=-1, resp=None):
     
    13161315            else:
    13171316                items[e[0]] = None
    13181317
    1319         if self.tlsMode:
    1320             self.authenticate(code, resp, items)
    1321         else:
    1322             self.tryTLS(code, resp, items)
     1318        self.tryTLS(code, resp, items)
    13231319
    13241320    def tryTLS(self, code, resp, items):
     1321        sslTransport = ISSLTransport.providedBy(self.transport)
     1322
    13251323        if self.context and 'STARTTLS' in items:
    13261324            self._expected = [220]
    13271325            self._okresponse = self.esmtpState_starttls
    13281326            self._failresponse = self.esmtpTLSFailed
    13291327            self.sendLine('STARTTLS')
    1330         elif self.requireTransportSecurity:
    1331             self.tlsMode = False
     1328        elif self.requireTransportSecurity and not sslTransport:
    13321329            self.esmtpTLSRequired()
    13331330        else:
    1334             self.tlsMode = False
    13351331            self.authenticate(code, resp, items)
    13361332
    13371333    def esmtpState_starttls(self, code, resp):
    13381334        try:
    13391335            self.transport.startTLS(self.context)
    1340             self.tlsMode = True
    13411336        except:
    13421337            log.err()
    13431338            self.esmtpTLSFailed(451)