Temporarily commit fix to issue #8108, to check for buildbot response
diff --git a/Lib/test/test_ftplib.py b/Lib/test/test_ftplib.py
index 182d5a7..4f6d1c1 100644
--- a/Lib/test/test_ftplib.py
+++ b/Lib/test/test_ftplib.py
@@ -29,6 +29,7 @@
 
 
 class DummyDTPHandler(asynchat.async_chat):
+    dtp_conn_closed = False
 
     def __init__(self, conn, baseclass):
         asynchat.async_chat.__init__(self, conn)
@@ -39,8 +40,13 @@
         self.baseclass.last_received_data += self.recv(1024)
 
     def handle_close(self):
-        self.baseclass.push('226 transfer complete')
-        self.close()
+        # XXX: this method can be called many times in a row for a single
+        # connection, including in clear-text (non-TLS) mode.
+        # (behaviour witnessed with test_data_connection)
+        if not self.dtp_conn_closed:
+            self.baseclass.push('226 transfer complete')
+            self.close()
+            self.dtp_conn_closed = True
 
 
 class DummyFTPHandler(asynchat.async_chat):
@@ -253,6 +259,7 @@
         """An asyncore.dispatcher subclass supporting TLS/SSL."""
 
         _ssl_accepting = False
+        _ssl_closing = False
 
         def secure_connection(self):
             self.socket = ssl.wrap_socket(self.socket, suppress_ragged_eofs=False,
@@ -277,15 +284,36 @@
             else:
                 self._ssl_accepting = False
 
+        def _do_ssl_shutdown(self):
+            self._ssl_closing = True
+            try:
+                self.socket = self.socket.unwrap()
+            except ssl.SSLError, err:
+                if err.args[0] in (ssl.SSL_ERROR_WANT_READ,
+                                   ssl.SSL_ERROR_WANT_WRITE):
+                    return
+            except socket.error, err:
+                # Any "socket error" corresponds to a SSL_ERROR_SYSCALL return
+                # from OpenSSL's SSL_shutdown(), corresponding to a
+                # closed socket condition. See also:
+                # http://www.mail-archive.com/openssl-users@openssl.org/msg60710.html
+                pass
+            self._ssl_closing = False
+            super(SSLConnection, self).close()
+
         def handle_read_event(self):
             if self._ssl_accepting:
                 self._do_ssl_handshake()
+            elif self._ssl_closing:
+                self._do_ssl_shutdown()
             else:
                 super(SSLConnection, self).handle_read_event()
 
         def handle_write_event(self):
             if self._ssl_accepting:
                 self._do_ssl_handshake()
+            elif self._ssl_closing:
+                self._do_ssl_shutdown()
             else:
                 super(SSLConnection, self).handle_write_event()
 
@@ -315,12 +343,9 @@
             raise
 
         def close(self):
-            try:
-                if isinstance(self.socket, ssl.SSLSocket):
-                    if self.socket._sslobj is not None:
-                        self.socket.unwrap()
-            finally:
-                super(SSLConnection, self).close()
+            if (isinstance(self.socket, ssl.SSLSocket) and
+                self.socket._sslobj is not None):
+                self._do_ssl_shutdown()
 
 
     class DummyTLS_DTPHandler(SSLConnection, DummyDTPHandler):
@@ -597,21 +622,21 @@
         sock = self.client.transfercmd('list')
         self.assertNotIsInstance(sock, ssl.SSLSocket)
         sock.close()
-        self.client.voidresp()
+        self.assertEqual(self.client.voidresp(), "226 transfer complete")
 
         # secured, after PROT P
         self.client.prot_p()
         sock = self.client.transfercmd('list')
         self.assertIsInstance(sock, ssl.SSLSocket)
         sock.close()
-        self.client.voidresp()
+        self.assertEqual(self.client.voidresp(), "226 transfer complete")
 
         # PROT C is issued, the connection must be in cleartext again
         self.client.prot_c()
         sock = self.client.transfercmd('list')
         self.assertNotIsInstance(sock, ssl.SSLSocket)
         sock.close()
-        self.client.voidresp()
+        self.assertEqual(self.client.voidresp(), "226 transfer complete")
 
     def test_login(self):
         # login() is supposed to implicitly secure the control connection