changeset: 71004:d2eacbbdaf57 parent: 71002:ddb8a29a6bc5 user: Giampaolo Rodola' date: Mon Jun 27 11:17:51 2011 +0200 files: Doc/library/ftplib.rst Doc/whatsnew/3.3.rst Lib/ftplib.py Lib/test/test_ftplib.py description: Issue 12139: add CCC command support to FTP_TLS class to revert the SSL connection back to clear-text. diff -r ddb8a29a6bc5 -r d2eacbbdaf57 Doc/library/ftplib.rst --- a/Doc/library/ftplib.rst Sun Jun 26 23:49:49 2011 -0700 +++ b/Doc/library/ftplib.rst Mon Jun 27 11:17:51 2011 +0200 @@ -426,6 +426,14 @@ Set up secure control connection by using TLS or SSL, depending on what specified in :meth:`ssl_version` attribute. +.. method:: FTP_TLS.ccc() + + Revert control channel back to plaintex. This can be useful to take + advantage of firewalls that know how to handle NAT with non-secure FTP + without opening fixed ports. + + .. versionadded:: 3.3 + .. method:: FTP_TLS.prot_p() Set up secure data connection. diff -r ddb8a29a6bc5 -r d2eacbbdaf57 Doc/whatsnew/3.3.rst --- a/Doc/whatsnew/3.3.rst Sun Jun 26 23:49:49 2011 -0700 +++ b/Doc/whatsnew/3.3.rst Mon Jun 27 11:17:51 2011 +0200 @@ -192,6 +192,17 @@ * :func:`~ssl.RAND_pseudo_bytes`: generate pseudo-random bytes. +ftplib +------ + +The :class:`~ftplib.FTP_TLS` class now provides a new +:func:`~ftplib.FTP_TLS.ccc` function to revert control channel back to +plaintex. This can be useful to take advantage of firewalls that know how to +handle NAT with non-secure FTP without opening fixed ports. + +(Patch submitted by Giampaolo RodolĂ  in :issue:`12139`.) + + Optimizations ============= diff -r ddb8a29a6bc5 -r d2eacbbdaf57 Lib/ftplib.py --- a/Lib/ftplib.py Sun Jun 26 23:49:49 2011 -0700 +++ b/Lib/ftplib.py Mon Jun 27 11:17:51 2011 +0200 @@ -708,6 +708,14 @@ self.file = self.sock.makefile(mode='r', encoding=self.encoding) return resp + def ccc(self): + '''Switch back to a clear-text control connection.''' + if not isinstance(self.sock, ssl.SSLSocket): + raise ValueError("not using TLS") + resp = self.voidcmd('CCC') + self.sock = self.sock.unwrap() + return resp + def prot_p(self): '''Set up secure data connection.''' # PROT defines whether or not the data channel is to be protected. diff -r ddb8a29a6bc5 -r d2eacbbdaf57 Lib/test/test_ftplib.py --- a/Lib/test/test_ftplib.py Sun Jun 26 23:49:49 2011 -0700 +++ b/Lib/test/test_ftplib.py Mon Jun 27 11:17:51 2011 +0200 @@ -303,11 +303,11 @@ _ssl_closing = False def secure_connection(self): - self.del_channel() socket = ssl.wrap_socket(self.socket, suppress_ragged_eofs=False, certfile=CERTFILE, server_side=True, do_handshake_on_connect=False, ssl_version=ssl.PROTOCOL_SSLv23) + self.del_channel() self.set_socket(socket) self._ssl_accepting = True @@ -342,7 +342,10 @@ # http://www.mail-archive.com/openssl-users@openssl.org/msg60710.html pass self._ssl_closing = False - super(SSLConnection, self).close() + if getattr(self, '_ccc', False) == False: + super(SSLConnection, self).close() + else: + pass def handle_read_event(self): if self._ssl_accepting: @@ -410,12 +413,18 @@ def __init__(self, conn): DummyFTPHandler.__init__(self, conn) self.secure_data_channel = False + self._ccc = False def cmd_auth(self, line): """Set up secure control channel.""" self.push('234 AUTH TLS successful') self.secure_connection() + def cmd_ccc(self, line): + self.push('220 Reverting back to clear-text') + self._ccc = True + self._do_ssl_shutdown() + def cmd_pbsz(self, line): """Negotiate size of buffer for secure data transfer. For TLS/SSL the only valid value for the parameter is '0'. @@ -872,6 +881,15 @@ self.assertIs(sock.context, ctx) self.assertIsInstance(sock, ssl.SSLSocket) + def test_ccc(self): + self.assertRaises(ValueError, self.client.ccc) + self.client.login(secure=True) + self.assertIsInstance(self.client.sock, ssl.SSLSocket) + self.client.ccc() + self.assertRaises(ValueError, self.client.sock.unwrap) + self.client.sendcmd('noop') + self.client.quit() + class TestTimeouts(TestCase):