[Python-checkins] r86431 - in python/branches/py3k: Doc/library/imaplib.rst Lib/imaplib.py Lib/test/test_imaplib.py Misc/NEWS

antoine.pitrou python-checkins at python.org
Fri Nov 12 19:49:16 CET 2010


Author: antoine.pitrou
Date: Fri Nov 12 19:49:16 2010
New Revision: 86431

Log:
Issue #4471: Add the IMAP.starttls() method to enable encryption on
standard IMAP4 connections.  Original patch by Lorenzo M. Catucci.



Modified:
   python/branches/py3k/Doc/library/imaplib.rst
   python/branches/py3k/Lib/imaplib.py
   python/branches/py3k/Lib/test/test_imaplib.py
   python/branches/py3k/Misc/NEWS

Modified: python/branches/py3k/Doc/library/imaplib.rst
==============================================================================
--- python/branches/py3k/Doc/library/imaplib.rst	(original)
+++ python/branches/py3k/Doc/library/imaplib.rst	Fri Nov 12 19:49:16 2010
@@ -56,6 +56,7 @@
    write permission, and the mailbox will need to be re-opened to re-obtain write
    permission.
 
+
 There's also a subclass for secure connections:
 
 
@@ -68,6 +69,7 @@
    and *certfile* are also optional - they can contain a PEM formatted private key
    and certificate chain file for the SSL connection.
 
+
 The second subclass allows for connections created by a child process:
 
 
@@ -406,6 +408,15 @@
    This is an ``IMAP4rev1`` extension command.
 
 
+.. method:: IMAP4.starttls(ssl_context=None)
+
+   Send a ``STARTTLS`` command.  The *ssl_context* argument is optional
+   and should be a :class:`ssl.SSLContext` object.  This will enable
+   encryption on the IMAP connection.
+
+   .. versionadded:: 3.2
+
+
 .. method:: IMAP4.status(mailbox, names)
 
    Request named status conditions for *mailbox*.

Modified: python/branches/py3k/Lib/imaplib.py
==============================================================================
--- python/branches/py3k/Lib/imaplib.py	(original)
+++ python/branches/py3k/Lib/imaplib.py	Fri Nov 12 19:49:16 2010
@@ -24,6 +24,12 @@
 
 import binascii, errno, random, re, socket, subprocess, sys, time
 
+try:
+    import ssl
+    HAVE_SSL = True
+except ImportError:
+    HAVE_SSL = False
+
 __all__ = ["IMAP4", "IMAP4_stream", "Internaldate2tuple",
            "Int2AP", "ParseFlags", "Time2Internaldate"]
 
@@ -71,6 +77,7 @@
         'SETANNOTATION':('AUTH', 'SELECTED'),
         'SETQUOTA':     ('AUTH', 'SELECTED'),
         'SORT':         ('SELECTED',),
+        'STARTTLS':     ('NONAUTH',),
         'STATUS':       ('AUTH', 'SELECTED'),
         'STORE':        ('SELECTED',),
         'SUBSCRIBE':    ('AUTH', 'SELECTED'),
@@ -156,6 +163,7 @@
         self.continuation_response = '' # Last continuation response
         self.is_readonly = False        # READ-ONLY desired state
         self.tagnum = 0
+        self._tls_established = False
 
         # Open socket to server.
 
@@ -711,6 +719,33 @@
         return self._untagged_response(typ, dat, name)
 
 
+    def starttls(self, ssl_context=None):
+        name = 'STARTTLS'
+        if not HAVE_SSL:
+            raise self.error('SSL support missing')
+        if self._tls_established:
+            raise self.abort('TLS session already established')
+        if name not in self.capabilities:
+            raise self.abort('TLS not supported by server')
+        # Generate a default SSL context if none was passed.
+        if ssl_context is None:
+            ssl_context = ssl.SSLContext(ssl.PROTOCOL_SSLv23)
+            # SSLv2 considered harmful.
+            ssl_context.options |= ssl.OP_NO_SSLv2
+        typ, dat = self._simple_command(name)
+        if typ == 'OK':
+            self.sock = ssl_context.wrap_socket(self.sock)
+            self.file = self.sock.makefile('rb')
+            self._tls_established = True
+            typ, dat = self.capability()
+            if dat == [None]:
+                raise self.error('no CAPABILITY response from server')
+            self.capabilities = tuple(dat[-1].upper().split())
+        else:
+            raise self.error("Couldn't establish TLS session")
+        return self._untagged_response(typ, dat, name)
+
+
     def status(self, mailbox, names):
         """Request named status conditions for mailbox.
 
@@ -1125,12 +1160,8 @@
                 n -= 1
 
 
+if HAVE_SSL:
 
-try:
-    import ssl
-except ImportError:
-    pass
-else:
     class IMAP4_SSL(IMAP4):
 
         """IMAP4 client class over SSL connection

Modified: python/branches/py3k/Lib/test/test_imaplib.py
==============================================================================
--- python/branches/py3k/Lib/test/test_imaplib.py	(original)
+++ python/branches/py3k/Lib/test/test_imaplib.py	Fri Nov 12 19:49:16 2010
@@ -209,8 +209,6 @@
 
     def test_logincapa(self):
         self.assertTrue('LOGINDISABLED' in self.server.capabilities)
-
-    def test_anonlogin(self):
         self.assertTrue('AUTH=ANONYMOUS' in self.server.capabilities)
         rs = self.server.login(self.username, self.password)
         self.assertEqual(rs[0], 'OK')
@@ -222,6 +220,18 @@
 
 
 @unittest.skipUnless(ssl, "SSL not available")
+class RemoteIMAP_STARTTLSTest(RemoteIMAPTest):
+
+    def setUp(self):
+        super().setUp()
+        rs = self.server.starttls()
+        self.assertEqual(rs[0], 'OK')
+
+    def test_logincapa(self):
+        self.assertFalse('LOGINDISABLED' in self.server.capabilities)
+
+
+ at unittest.skipUnless(ssl, "SSL not available")
 class RemoteIMAP_SSLTest(RemoteIMAPTest):
     port = 993
     imap_class = IMAP4_SSL
@@ -243,7 +253,7 @@
                 raise support.TestFailed("Can't read certificate files!")
         tests.extend([
             ThreadedNetworkedTests, ThreadedNetworkedTestsSSL,
-            RemoteIMAPTest, RemoteIMAP_SSLTest,
+            RemoteIMAPTest, RemoteIMAP_SSLTest, RemoteIMAP_STARTTLSTest,
         ])
 
     support.run_unittest(*tests)

Modified: python/branches/py3k/Misc/NEWS
==============================================================================
--- python/branches/py3k/Misc/NEWS	(original)
+++ python/branches/py3k/Misc/NEWS	Fri Nov 12 19:49:16 2010
@@ -63,6 +63,9 @@
 Library
 -------
 
+- Issue #4471: Add the IMAP.starttls() method to enable encryption on
+  standard IMAP4 connections.  Original patch by Lorenzo M. Catucci.
+
 - Issue #1466065: Add 'validate' option to base64.b64decode to raise
   an error if there are non-base64 alphabet characters in the input.
 


More information about the Python-checkins mailing list