diff -r edee31e24bda Doc/library/imaplib.rst --- a/Doc/library/imaplib.rst Mon May 02 16:15:43 2011 +0200 +++ b/Doc/library/imaplib.rst Mon May 02 16:58:54 2011 -0400 @@ -64,14 +64,21 @@ There's also a subclass for secure connections: -.. class:: IMAP4_SSL(host='', port=IMAP4_SSL_PORT, keyfile=None, certfile=None) +.. class:: IMAP4_SSL(host='', port=IMAP4_SSL_PORT, keyfile=None, certfile=None, ssl_context=None) This is a subclass derived from :class:`IMAP4` that connects over an SSL encrypted socket (to use this class you need a socket module that was compiled with SSL support). If *host* is not specified, ``''`` (the local host) is used. If *port* is omitted, the standard IMAP4-over-SSL port (993) is used. *keyfile* and *certfile* are also optional - they can contain a PEM formatted private key - and certificate chain file for the SSL connection. + and certificate chain file for the SSL connection. *ssl_context* parameter is a + :class:`ssl.SSLContext` object which allows bundling SSL configuration + options, certificates and private keys into a single (potentially long-lived) + structure. Note that the *keyfile*/*certfile* parameters are mutually exclusive with *ssl_context*, + a :class:`ValueError` is thrown if *keyfile*/*certfile* is provided along with *ssl_context*. + + .. versionchanged:: 3.3 + *ssl_context* parameter added. The second subclass allows for connections created by a child process: diff -r edee31e24bda Doc/library/poplib.rst --- a/Doc/library/poplib.rst Mon May 02 16:15:43 2011 +0200 +++ b/Doc/library/poplib.rst Mon May 02 16:58:54 2011 -0400 @@ -45,7 +45,8 @@ *timeout* works as in the :class:`POP3` constructor. *context* parameter is a :class:`ssl.SSLContext` object which allows bundling SSL configuration options, certificates and private keys into a single (potentially long-lived) - structure. + structure. Note that the *keyfile*/*certfile* parameters are mutually exclusive with *ssl_context*, + a :class:`ValueError` is thrown if *keyfile*/*certfile* is provided along with *ssl_context*. .. versionchanged:: 3.2 *context* parameter added. diff -r edee31e24bda Lib/imaplib.py --- a/Lib/imaplib.py Mon May 02 16:15:43 2011 +0200 +++ b/Lib/imaplib.py Mon May 02 16:58:54 2011 -0400 @@ -1177,25 +1177,39 @@ """IMAP4 client class over SSL connection - Instantiate with: IMAP4_SSL([host[, port[, keyfile[, certfile]]]]) + Instantiate with: IMAP4_SSL([host[, port[, keyfile[, certfile[, ssl_context]]]]]) host - host's name (default: localhost); - port - port number (default: standard IMAP4 SSL port). + port - port number (default: standard IMAP4 SSL port); keyfile - PEM formatted file that contains your private key (default: None); certfile - PEM formatted certificate chain file (default: None); - + ssl_context - a SSLContext object that contains your certificate chain and private key (default: None) + Note: if ssl_context is provided, then parameters keyfile or certfile should not be set otherwise ValueError is thrown + for more documentation see the docstring of the parent class IMAP4. """ - def __init__(self, host = '', port = IMAP4_SSL_PORT, keyfile = None, certfile = None): + def __init__(self, host='', port=IMAP4_SSL_PORT, keyfile=None, certfile=None, ssl_context=None): + if ssl_context is not None and keyfile is not None: + raise ValueError("ssl_context and keyfile arguments are mutually " + "exclusive") + if ssl_context is not None and certfile is not None: + raise ValueError("ssl_context and certfile arguments are mutually " + "exclusive") + self.keyfile = keyfile self.certfile = certfile + self.ssl_context = ssl_context IMAP4.__init__(self, host, port) + def _create_socket(self): sock = IMAP4._create_socket(self) - return ssl.wrap_socket(sock, self.keyfile, self.certfile) + if self.ssl_context: + return self.ssl_context.wrap_socket(sock) + else: + return ssl.wrap_socket(sock, self.keyfile, self.certfile) def open(self, host='', port=IMAP4_SSL_PORT): """Setup connection to remote server on "host:port". diff -r edee31e24bda Lib/poplib.py --- a/Lib/poplib.py Mon May 02 16:15:43 2011 +0200 +++ b/Lib/poplib.py Mon May 02 16:58:54 2011 -0400 @@ -328,9 +328,11 @@ hostname - the hostname of the pop3 over ssl server port - port number - keyfile - PEM formatted file that countains your private key + keyfile - PEM formatted file that contains your private key certfile - PEM formatted certificate chain file - + context - a SSLContext object that contains your certificate chain and private key + Note: if context is provided, then parameters keyfile or certfile should not be set otherwise ValueError is thrown + See the methods of the parent class POP3 for more documentation. """ diff -r edee31e24bda Lib/test/test_imaplib.py --- a/Lib/test/test_imaplib.py Mon May 02 16:15:43 2011 +0200 +++ b/Lib/test/test_imaplib.py Mon May 02 16:58:54 2011 -0400 @@ -258,11 +258,55 @@ port = 993 imap_class = IMAP4_SSL + def setUp(self): + pass + + def tearDown(self): + pass + + def create_ssl_context(self): + ssl_context = ssl.SSLContext(ssl.PROTOCOL_SSLv23) + ssl_context.load_cert_chain(CERTFILE) + return ssl_context + + def check_logincapa(self, server): + for cap in server.capabilities: + self.assertIsInstance(cap, str) + self.assertFalse('LOGINDISABLED' in server.capabilities) + self.assertTrue('AUTH=PLAIN' in server.capabilities) + rs = server.login(self.username, self.password) + self.assertEqual(rs[0], 'OK') + def test_logincapa(self): - for cap in self.server.capabilities: - self.assertIsInstance(cap, str) - self.assertFalse('LOGINDISABLED' in self.server.capabilities) - self.assertTrue('AUTH=PLAIN' in self.server.capabilities) + with transient_internet(self.host): + _server = self.imap_class(self.host, self.port) + self.check_logincapa(_server) + + def test_logincapa_with_client_certfile(self): + with transient_internet(self.host): + _server = self.imap_class(self.host, self.port, certfile=CERTFILE) + self.check_logincapa(_server) + + def test_logincapa_with_client_ssl_context(self): + with transient_internet(self.host): + _server = self.imap_class(self.host, self.port, ssl_context=self.create_ssl_context()) + self.check_logincapa(_server) + + def test_logout(self): + with transient_internet(self.host): + _server = self.imap_class(self.host, self.port) + rs = _server.logout() + _server = None + self.assertEqual(rs[0], 'BYE') + + def test_ssl_context_certfile_exclusive(self): + with transient_internet(self.host): + self.assertRaises(ValueError, self.imap_class, self.host, self.port, certfile=CERTFILE, ssl_context=self.create_ssl_context()) + + def test_ssl_context_keyfile_exclusive(self): + with transient_internet(self.host): + self.assertRaises(ValueError, self.imap_class, self.host, self.port, keyfile=CERTFILE, ssl_context=self.create_ssl_context()) + def test_main():