Home | History | Annotate | Download | only in test
      1 """Test script for ftplib module."""
      2 
      3 # Modified by Giampaolo Rodola' to test FTP class, IPv6 and TLS
      4 # environment
      5 
      6 import ftplib
      7 import asyncore
      8 import asynchat
      9 import socket
     10 import StringIO
     11 import errno
     12 import os
     13 try:
     14     import ssl
     15 except ImportError:
     16     ssl = None
     17 
     18 from unittest import TestCase, SkipTest, skipUnless
     19 from test import test_support
     20 from test.test_support import HOST, HOSTv6
     21 threading = test_support.import_module('threading')
     22 
     23 TIMEOUT = 3
     24 # the dummy data returned by server over the data channel when
     25 # RETR, LIST and NLST commands are issued
     26 RETR_DATA = 'abcde12345\r\n' * 1000
     27 LIST_DATA = 'foo\r\nbar\r\n'
     28 NLST_DATA = 'foo\r\nbar\r\n'
     29 
     30 
     31 class DummyDTPHandler(asynchat.async_chat):
     32     dtp_conn_closed = False
     33 
     34     def __init__(self, conn, baseclass):
     35         asynchat.async_chat.__init__(self, conn)
     36         self.baseclass = baseclass
     37         self.baseclass.last_received_data = ''
     38 
     39     def handle_read(self):
     40         self.baseclass.last_received_data += self.recv(1024)
     41 
     42     def handle_close(self):
     43         # XXX: this method can be called many times in a row for a single
     44         # connection, including in clear-text (non-TLS) mode.
     45         # (behaviour witnessed with test_data_connection)
     46         if not self.dtp_conn_closed:
     47             self.baseclass.push('226 transfer complete')
     48             self.close()
     49             self.dtp_conn_closed = True
     50 
     51     def handle_error(self):
     52         raise
     53 
     54 
     55 class DummyFTPHandler(asynchat.async_chat):
     56 
     57     dtp_handler = DummyDTPHandler
     58 
     59     def __init__(self, conn):
     60         asynchat.async_chat.__init__(self, conn)
     61         self.set_terminator("\r\n")
     62         self.in_buffer = []
     63         self.dtp = None
     64         self.last_received_cmd = None
     65         self.last_received_data = ''
     66         self.next_response = ''
     67         self.rest = None
     68         self.next_retr_data = RETR_DATA
     69         self.push('220 welcome')
     70 
     71     def collect_incoming_data(self, data):
     72         self.in_buffer.append(data)
     73 
     74     def found_terminator(self):
     75         line = ''.join(self.in_buffer)
     76         self.in_buffer = []
     77         if self.next_response:
     78             self.push(self.next_response)
     79             self.next_response = ''
     80         cmd = line.split(' ')[0].lower()
     81         self.last_received_cmd = cmd
     82         space = line.find(' ')
     83         if space != -1:
     84             arg = line[space + 1:]
     85         else:
     86             arg = ""
     87         if hasattr(self, 'cmd_' + cmd):
     88             method = getattr(self, 'cmd_' + cmd)
     89             method(arg)
     90         else:
     91             self.push('550 command "%s" not understood.' %cmd)
     92 
     93     def handle_error(self):
     94         raise
     95 
     96     def push(self, data):
     97         asynchat.async_chat.push(self, data + '\r\n')
     98 
     99     def cmd_port(self, arg):
    100         addr = map(int, arg.split(','))
    101         ip = '%d.%d.%d.%d' %tuple(addr[:4])
    102         port = (addr[4] * 256) + addr[5]
    103         s = socket.create_connection((ip, port), timeout=10)
    104         self.dtp = self.dtp_handler(s, baseclass=self)
    105         self.push('200 active data connection established')
    106 
    107     def cmd_pasv(self, arg):
    108         sock = socket.socket()
    109         sock.bind((self.socket.getsockname()[0], 0))
    110         sock.listen(5)
    111         sock.settimeout(10)
    112         ip, port = sock.getsockname()[:2]
    113         ip = ip.replace('.', ',')
    114         p1, p2 = divmod(port, 256)
    115         self.push('227 entering passive mode (%s,%d,%d)' %(ip, p1, p2))
    116         conn, addr = sock.accept()
    117         self.dtp = self.dtp_handler(conn, baseclass=self)
    118 
    119     def cmd_eprt(self, arg):
    120         af, ip, port = arg.split(arg[0])[1:-1]
    121         port = int(port)
    122         s = socket.create_connection((ip, port), timeout=10)
    123         self.dtp = self.dtp_handler(s, baseclass=self)
    124         self.push('200 active data connection established')
    125 
    126     def cmd_epsv(self, arg):
    127         sock = socket.socket(socket.AF_INET6)
    128         sock.bind((self.socket.getsockname()[0], 0))
    129         sock.listen(5)
    130         sock.settimeout(10)
    131         port = sock.getsockname()[1]
    132         self.push('229 entering extended passive mode (|||%d|)' %port)
    133         conn, addr = sock.accept()
    134         self.dtp = self.dtp_handler(conn, baseclass=self)
    135 
    136     def cmd_echo(self, arg):
    137         # sends back the received string (used by the test suite)
    138         self.push(arg)
    139 
    140     def cmd_user(self, arg):
    141         self.push('331 username ok')
    142 
    143     def cmd_pass(self, arg):
    144         self.push('230 password ok')
    145 
    146     def cmd_acct(self, arg):
    147         self.push('230 acct ok')
    148 
    149     def cmd_rnfr(self, arg):
    150         self.push('350 rnfr ok')
    151 
    152     def cmd_rnto(self, arg):
    153         self.push('250 rnto ok')
    154 
    155     def cmd_dele(self, arg):
    156         self.push('250 dele ok')
    157 
    158     def cmd_cwd(self, arg):
    159         self.push('250 cwd ok')
    160 
    161     def cmd_size(self, arg):
    162         self.push('250 1000')
    163 
    164     def cmd_mkd(self, arg):
    165         self.push('257 "%s"' %arg)
    166 
    167     def cmd_rmd(self, arg):
    168         self.push('250 rmd ok')
    169 
    170     def cmd_pwd(self, arg):
    171         self.push('257 "pwd ok"')
    172 
    173     def cmd_type(self, arg):
    174         self.push('200 type ok')
    175 
    176     def cmd_quit(self, arg):
    177         self.push('221 quit ok')
    178         self.close()
    179 
    180     def cmd_stor(self, arg):
    181         self.push('125 stor ok')
    182 
    183     def cmd_rest(self, arg):
    184         self.rest = arg
    185         self.push('350 rest ok')
    186 
    187     def cmd_retr(self, arg):
    188         self.push('125 retr ok')
    189         if self.rest is not None:
    190             offset = int(self.rest)
    191         else:
    192             offset = 0
    193         self.dtp.push(self.next_retr_data[offset:])
    194         self.dtp.close_when_done()
    195         self.rest = None
    196 
    197     def cmd_list(self, arg):
    198         self.push('125 list ok')
    199         self.dtp.push(LIST_DATA)
    200         self.dtp.close_when_done()
    201 
    202     def cmd_nlst(self, arg):
    203         self.push('125 nlst ok')
    204         self.dtp.push(NLST_DATA)
    205         self.dtp.close_when_done()
    206 
    207     def cmd_setlongretr(self, arg):
    208         # For testing. Next RETR will return long line.
    209         self.next_retr_data = 'x' * int(arg)
    210         self.push('125 setlongretr ok')
    211 
    212 
    213 class DummyFTPServer(asyncore.dispatcher, threading.Thread):
    214 
    215     handler = DummyFTPHandler
    216 
    217     def __init__(self, address, af=socket.AF_INET):
    218         threading.Thread.__init__(self)
    219         asyncore.dispatcher.__init__(self)
    220         self.create_socket(af, socket.SOCK_STREAM)
    221         self.bind(address)
    222         self.listen(5)
    223         self.active = False
    224         self.active_lock = threading.Lock()
    225         self.host, self.port = self.socket.getsockname()[:2]
    226         self.handler_instance = None
    227 
    228     def start(self):
    229         assert not self.active
    230         self.__flag = threading.Event()
    231         threading.Thread.start(self)
    232         self.__flag.wait()
    233 
    234     def run(self):
    235         self.active = True
    236         self.__flag.set()
    237         while self.active and asyncore.socket_map:
    238             self.active_lock.acquire()
    239             asyncore.loop(timeout=0.1, count=1)
    240             self.active_lock.release()
    241         asyncore.close_all(ignore_all=True)
    242 
    243     def stop(self):
    244         assert self.active
    245         self.active = False
    246         self.join()
    247 
    248     def handle_accept(self):
    249         conn, addr = self.accept()
    250         self.handler_instance = self.handler(conn)
    251 
    252     def handle_connect(self):
    253         self.close()
    254     handle_read = handle_connect
    255 
    256     def writable(self):
    257         return 0
    258 
    259     def handle_error(self):
    260         raise
    261 
    262 
    263 if ssl is not None:
    264 
    265     CERTFILE = os.path.join(os.path.dirname(__file__), "keycert3.pem")
    266     CAFILE = os.path.join(os.path.dirname(__file__), "pycacert.pem")
    267 
    268     class SSLConnection(object, asyncore.dispatcher):
    269         """An asyncore.dispatcher subclass supporting TLS/SSL."""
    270 
    271         _ssl_accepting = False
    272         _ssl_closing = False
    273 
    274         def secure_connection(self):
    275             socket = ssl.wrap_socket(self.socket, suppress_ragged_eofs=False,
    276                                      certfile=CERTFILE, server_side=True,
    277                                      do_handshake_on_connect=False,
    278                                      ssl_version=ssl.PROTOCOL_SSLv23)
    279             self.del_channel()
    280             self.set_socket(socket)
    281             self._ssl_accepting = True
    282 
    283         def _do_ssl_handshake(self):
    284             try:
    285                 self.socket.do_handshake()
    286             except ssl.SSLError as err:
    287                 if err.args[0] in (ssl.SSL_ERROR_WANT_READ,
    288                                    ssl.SSL_ERROR_WANT_WRITE):
    289                     return
    290                 elif err.args[0] == ssl.SSL_ERROR_EOF:
    291                     return self.handle_close()
    292                 raise
    293             except socket.error as err:
    294                 if err.args[0] == errno.ECONNABORTED:
    295                     return self.handle_close()
    296             else:
    297                 self._ssl_accepting = False
    298 
    299         def _do_ssl_shutdown(self):
    300             self._ssl_closing = True
    301             try:
    302                 self.socket = self.socket.unwrap()
    303             except ssl.SSLError as err:
    304                 if err.args[0] in (ssl.SSL_ERROR_WANT_READ,
    305                                    ssl.SSL_ERROR_WANT_WRITE):
    306                     return
    307             except socket.error as err:
    308                 # Any "socket error" corresponds to a SSL_ERROR_SYSCALL return
    309                 # from OpenSSL's SSL_shutdown(), corresponding to a
    310                 # closed socket condition. See also:
    311                 # http://www.mail-archive.com/openssl-users@openssl.org/msg60710.html
    312                 pass
    313             self._ssl_closing = False
    314             if getattr(self, '_ccc', False) is False:
    315                 super(SSLConnection, self).close()
    316             else:
    317                 pass
    318 
    319         def handle_read_event(self):
    320             if self._ssl_accepting:
    321                 self._do_ssl_handshake()
    322             elif self._ssl_closing:
    323                 self._do_ssl_shutdown()
    324             else:
    325                 super(SSLConnection, self).handle_read_event()
    326 
    327         def handle_write_event(self):
    328             if self._ssl_accepting:
    329                 self._do_ssl_handshake()
    330             elif self._ssl_closing:
    331                 self._do_ssl_shutdown()
    332             else:
    333                 super(SSLConnection, self).handle_write_event()
    334 
    335         def send(self, data):
    336             try:
    337                 return super(SSLConnection, self).send(data)
    338             except ssl.SSLError as err:
    339                 if err.args[0] in (ssl.SSL_ERROR_EOF, ssl.SSL_ERROR_ZERO_RETURN,
    340                                    ssl.SSL_ERROR_WANT_READ,
    341                                    ssl.SSL_ERROR_WANT_WRITE):
    342                     return 0
    343                 raise
    344 
    345         def recv(self, buffer_size):
    346             try:
    347                 return super(SSLConnection, self).recv(buffer_size)
    348             except ssl.SSLError as err:
    349                 if err.args[0] in (ssl.SSL_ERROR_WANT_READ,
    350                                    ssl.SSL_ERROR_WANT_WRITE):
    351                     return b''
    352                 if err.args[0] in (ssl.SSL_ERROR_EOF, ssl.SSL_ERROR_ZERO_RETURN):
    353                     self.handle_close()
    354                     return b''
    355                 raise
    356 
    357         def handle_error(self):
    358             raise
    359 
    360         def close(self):
    361             if (isinstance(self.socket, ssl.SSLSocket) and
    362                 self.socket._sslobj is not None):
    363                 self._do_ssl_shutdown()
    364             else:
    365                 super(SSLConnection, self).close()
    366 
    367 
    368     class DummyTLS_DTPHandler(SSLConnection, DummyDTPHandler):
    369         """A DummyDTPHandler subclass supporting TLS/SSL."""
    370 
    371         def __init__(self, conn, baseclass):
    372             DummyDTPHandler.__init__(self, conn, baseclass)
    373             if self.baseclass.secure_data_channel:
    374                 self.secure_connection()
    375 
    376 
    377     class DummyTLS_FTPHandler(SSLConnection, DummyFTPHandler):
    378         """A DummyFTPHandler subclass supporting TLS/SSL."""
    379 
    380         dtp_handler = DummyTLS_DTPHandler
    381 
    382         def __init__(self, conn):
    383             DummyFTPHandler.__init__(self, conn)
    384             self.secure_data_channel = False
    385 
    386         def cmd_auth(self, line):
    387             """Set up secure control channel."""
    388             self.push('234 AUTH TLS successful')
    389             self.secure_connection()
    390 
    391         def cmd_pbsz(self, line):
    392             """Negotiate size of buffer for secure data transfer.
    393             For TLS/SSL the only valid value for the parameter is '0'.
    394             Any other value is accepted but ignored.
    395             """
    396             self.push('200 PBSZ=0 successful.')
    397 
    398         def cmd_prot(self, line):
    399             """Setup un/secure data channel."""
    400             arg = line.upper()
    401             if arg == 'C':
    402                 self.push('200 Protection set to Clear')
    403                 self.secure_data_channel = False
    404             elif arg == 'P':
    405                 self.push('200 Protection set to Private')
    406                 self.secure_data_channel = True
    407             else:
    408                 self.push("502 Unrecognized PROT type (use C or P).")
    409 
    410 
    411     class DummyTLS_FTPServer(DummyFTPServer):
    412         handler = DummyTLS_FTPHandler
    413 
    414 
    415 class TestFTPClass(TestCase):
    416 
    417     def setUp(self):
    418         self.server = DummyFTPServer((HOST, 0))
    419         self.server.start()
    420         self.client = ftplib.FTP(timeout=10)
    421         self.client.connect(self.server.host, self.server.port)
    422 
    423     def tearDown(self):
    424         self.client.close()
    425         self.server.stop()
    426 
    427     def test_getwelcome(self):
    428         self.assertEqual(self.client.getwelcome(), '220 welcome')
    429 
    430     def test_sanitize(self):
    431         self.assertEqual(self.client.sanitize('foo'), repr('foo'))
    432         self.assertEqual(self.client.sanitize('pass 12345'), repr('pass *****'))
    433         self.assertEqual(self.client.sanitize('PASS 12345'), repr('PASS *****'))
    434 
    435     def test_exceptions(self):
    436         self.assertRaises(ftplib.error_temp, self.client.sendcmd, 'echo 400')
    437         self.assertRaises(ftplib.error_temp, self.client.sendcmd, 'echo 499')
    438         self.assertRaises(ftplib.error_perm, self.client.sendcmd, 'echo 500')
    439         self.assertRaises(ftplib.error_perm, self.client.sendcmd, 'echo 599')
    440         self.assertRaises(ftplib.error_proto, self.client.sendcmd, 'echo 999')
    441 
    442     def test_all_errors(self):
    443         exceptions = (ftplib.error_reply, ftplib.error_temp, ftplib.error_perm,
    444                       ftplib.error_proto, ftplib.Error, IOError, EOFError)
    445         for x in exceptions:
    446             try:
    447                 raise x('exception not included in all_errors set')
    448             except ftplib.all_errors:
    449                 pass
    450 
    451     def test_set_pasv(self):
    452         # passive mode is supposed to be enabled by default
    453         self.assertTrue(self.client.passiveserver)
    454         self.client.set_pasv(True)
    455         self.assertTrue(self.client.passiveserver)
    456         self.client.set_pasv(False)
    457         self.assertFalse(self.client.passiveserver)
    458 
    459     def test_voidcmd(self):
    460         self.client.voidcmd('echo 200')
    461         self.client.voidcmd('echo 299')
    462         self.assertRaises(ftplib.error_reply, self.client.voidcmd, 'echo 199')
    463         self.assertRaises(ftplib.error_reply, self.client.voidcmd, 'echo 300')
    464 
    465     def test_login(self):
    466         self.client.login()
    467 
    468     def test_acct(self):
    469         self.client.acct('passwd')
    470 
    471     def test_rename(self):
    472         self.client.rename('a', 'b')
    473         self.server.handler_instance.next_response = '200'
    474         self.assertRaises(ftplib.error_reply, self.client.rename, 'a', 'b')
    475 
    476     def test_delete(self):
    477         self.client.delete('foo')
    478         self.server.handler_instance.next_response = '199'
    479         self.assertRaises(ftplib.error_reply, self.client.delete, 'foo')
    480 
    481     def test_size(self):
    482         self.client.size('foo')
    483 
    484     def test_mkd(self):
    485         dir = self.client.mkd('/foo')
    486         self.assertEqual(dir, '/foo')
    487 
    488     def test_rmd(self):
    489         self.client.rmd('foo')
    490 
    491     def test_cwd(self):
    492         dir = self.client.cwd('/foo')
    493         self.assertEqual(dir, '250 cwd ok')
    494 
    495     def test_pwd(self):
    496         dir = self.client.pwd()
    497         self.assertEqual(dir, 'pwd ok')
    498 
    499     def test_quit(self):
    500         self.assertEqual(self.client.quit(), '221 quit ok')
    501         # Ensure the connection gets closed; sock attribute should be None
    502         self.assertEqual(self.client.sock, None)
    503 
    504     def test_retrbinary(self):
    505         received = []
    506         self.client.retrbinary('retr', received.append)
    507         self.assertEqual(''.join(received), RETR_DATA)
    508 
    509     def test_retrbinary_rest(self):
    510         for rest in (0, 10, 20):
    511             received = []
    512             self.client.retrbinary('retr', received.append, rest=rest)
    513             self.assertEqual(''.join(received), RETR_DATA[rest:],
    514                              msg='rest test case %d %d %d' % (rest,
    515                                                               len(''.join(received)),
    516                                                               len(RETR_DATA[rest:])))
    517 
    518     def test_retrlines(self):
    519         received = []
    520         self.client.retrlines('retr', received.append)
    521         self.assertEqual(''.join(received), RETR_DATA.replace('\r\n', ''))
    522 
    523     def test_storbinary(self):
    524         f = StringIO.StringIO(RETR_DATA)
    525         self.client.storbinary('stor', f)
    526         self.assertEqual(self.server.handler_instance.last_received_data, RETR_DATA)
    527         # test new callback arg
    528         flag = []
    529         f.seek(0)
    530         self.client.storbinary('stor', f, callback=lambda x: flag.append(None))
    531         self.assertTrue(flag)
    532 
    533     def test_storbinary_rest(self):
    534         f = StringIO.StringIO(RETR_DATA)
    535         for r in (30, '30'):
    536             f.seek(0)
    537             self.client.storbinary('stor', f, rest=r)
    538             self.assertEqual(self.server.handler_instance.rest, str(r))
    539 
    540     def test_storlines(self):
    541         f = StringIO.StringIO(RETR_DATA.replace('\r\n', '\n'))
    542         self.client.storlines('stor', f)
    543         self.assertEqual(self.server.handler_instance.last_received_data, RETR_DATA)
    544         # test new callback arg
    545         flag = []
    546         f.seek(0)
    547         self.client.storlines('stor foo', f, callback=lambda x: flag.append(None))
    548         self.assertTrue(flag)
    549 
    550     def test_nlst(self):
    551         self.client.nlst()
    552         self.assertEqual(self.client.nlst(), NLST_DATA.split('\r\n')[:-1])
    553 
    554     def test_dir(self):
    555         l = []
    556         self.client.dir(lambda x: l.append(x))
    557         self.assertEqual(''.join(l), LIST_DATA.replace('\r\n', ''))
    558 
    559     def test_makeport(self):
    560         self.client.makeport()
    561         # IPv4 is in use, just make sure send_eprt has not been used
    562         self.assertEqual(self.server.handler_instance.last_received_cmd, 'port')
    563 
    564     def test_makepasv(self):
    565         host, port = self.client.makepasv()
    566         conn = socket.create_connection((host, port), 10)
    567         conn.close()
    568         # IPv4 is in use, just make sure send_epsv has not been used
    569         self.assertEqual(self.server.handler_instance.last_received_cmd, 'pasv')
    570 
    571     def test_line_too_long(self):
    572         self.assertRaises(ftplib.Error, self.client.sendcmd,
    573                           'x' * self.client.maxline * 2)
    574 
    575     def test_retrlines_too_long(self):
    576         self.client.sendcmd('SETLONGRETR %d' % (self.client.maxline * 2))
    577         received = []
    578         self.assertRaises(ftplib.Error,
    579                           self.client.retrlines, 'retr', received.append)
    580 
    581     def test_storlines_too_long(self):
    582         f = StringIO.StringIO('x' * self.client.maxline * 2)
    583         self.assertRaises(ftplib.Error, self.client.storlines, 'stor', f)
    584 
    585 
    586 @skipUnless(socket.has_ipv6, "IPv6 not enabled")
    587 class TestIPv6Environment(TestCase):
    588 
    589     @classmethod
    590     def setUpClass(cls):
    591         try:
    592             DummyFTPServer((HOST, 0), af=socket.AF_INET6)
    593         except socket.error:
    594             raise SkipTest("IPv6 not enabled")
    595 
    596     def setUp(self):
    597         self.server = DummyFTPServer((HOSTv6, 0), af=socket.AF_INET6)
    598         self.server.start()
    599         self.client = ftplib.FTP()
    600         self.client.connect(self.server.host, self.server.port)
    601 
    602     def tearDown(self):
    603         self.client.close()
    604         self.server.stop()
    605 
    606     def test_af(self):
    607         self.assertEqual(self.client.af, socket.AF_INET6)
    608 
    609     def test_makeport(self):
    610         self.client.makeport()
    611         self.assertEqual(self.server.handler_instance.last_received_cmd, 'eprt')
    612 
    613     def test_makepasv(self):
    614         host, port = self.client.makepasv()
    615         conn = socket.create_connection((host, port), 10)
    616         conn.close()
    617         self.assertEqual(self.server.handler_instance.last_received_cmd, 'epsv')
    618 
    619     def test_transfer(self):
    620         def retr():
    621             received = []
    622             self.client.retrbinary('retr', received.append)
    623             self.assertEqual(''.join(received), RETR_DATA)
    624         self.client.set_pasv(True)
    625         retr()
    626         self.client.set_pasv(False)
    627         retr()
    628 
    629 
    630 @skipUnless(ssl, "SSL not available")
    631 class TestTLS_FTPClassMixin(TestFTPClass):
    632     """Repeat TestFTPClass tests starting the TLS layer for both control
    633     and data connections first.
    634     """
    635 
    636     def setUp(self):
    637         self.server = DummyTLS_FTPServer((HOST, 0))
    638         self.server.start()
    639         self.client = ftplib.FTP_TLS(timeout=10)
    640         self.client.connect(self.server.host, self.server.port)
    641         # enable TLS
    642         self.client.auth()
    643         self.client.prot_p()
    644 
    645 
    646 @skipUnless(ssl, "SSL not available")
    647 class TestTLS_FTPClass(TestCase):
    648     """Specific TLS_FTP class tests."""
    649 
    650     def setUp(self):
    651         self.server = DummyTLS_FTPServer((HOST, 0))
    652         self.server.start()
    653         self.client = ftplib.FTP_TLS(timeout=TIMEOUT)
    654         self.client.connect(self.server.host, self.server.port)
    655 
    656     def tearDown(self):
    657         self.client.close()
    658         self.server.stop()
    659 
    660     def test_control_connection(self):
    661         self.assertNotIsInstance(self.client.sock, ssl.SSLSocket)
    662         self.client.auth()
    663         self.assertIsInstance(self.client.sock, ssl.SSLSocket)
    664 
    665     def test_data_connection(self):
    666         # clear text
    667         sock = self.client.transfercmd('list')
    668         self.assertNotIsInstance(sock, ssl.SSLSocket)
    669         sock.close()
    670         self.assertEqual(self.client.voidresp(), "226 transfer complete")
    671 
    672         # secured, after PROT P
    673         self.client.prot_p()
    674         sock = self.client.transfercmd('list')
    675         self.assertIsInstance(sock, ssl.SSLSocket)
    676         sock.close()
    677         self.assertEqual(self.client.voidresp(), "226 transfer complete")
    678 
    679         # PROT C is issued, the connection must be in cleartext again
    680         self.client.prot_c()
    681         sock = self.client.transfercmd('list')
    682         self.assertNotIsInstance(sock, ssl.SSLSocket)
    683         sock.close()
    684         self.assertEqual(self.client.voidresp(), "226 transfer complete")
    685 
    686     def test_login(self):
    687         # login() is supposed to implicitly secure the control connection
    688         self.assertNotIsInstance(self.client.sock, ssl.SSLSocket)
    689         self.client.login()
    690         self.assertIsInstance(self.client.sock, ssl.SSLSocket)
    691         # make sure that AUTH TLS doesn't get issued again
    692         self.client.login()
    693 
    694     def test_auth_issued_twice(self):
    695         self.client.auth()
    696         self.assertRaises(ValueError, self.client.auth)
    697 
    698     def test_auth_ssl(self):
    699         try:
    700             self.client.ssl_version = ssl.PROTOCOL_SSLv23
    701             self.client.auth()
    702             self.assertRaises(ValueError, self.client.auth)
    703         finally:
    704             self.client.ssl_version = ssl.PROTOCOL_TLSv1
    705 
    706     def test_context(self):
    707         self.client.quit()
    708         ctx = ssl.SSLContext(ssl.PROTOCOL_TLSv1)
    709         self.assertRaises(ValueError, ftplib.FTP_TLS, keyfile=CERTFILE,
    710                           context=ctx)
    711         self.assertRaises(ValueError, ftplib.FTP_TLS, certfile=CERTFILE,
    712                           context=ctx)
    713         self.assertRaises(ValueError, ftplib.FTP_TLS, certfile=CERTFILE,
    714                           keyfile=CERTFILE, context=ctx)
    715 
    716         self.client = ftplib.FTP_TLS(context=ctx, timeout=TIMEOUT)
    717         self.client.connect(self.server.host, self.server.port)
    718         self.assertNotIsInstance(self.client.sock, ssl.SSLSocket)
    719         self.client.auth()
    720         self.assertIs(self.client.sock.context, ctx)
    721         self.assertIsInstance(self.client.sock, ssl.SSLSocket)
    722 
    723         self.client.prot_p()
    724         sock = self.client.transfercmd('list')
    725         try:
    726             self.assertIs(sock.context, ctx)
    727             self.assertIsInstance(sock, ssl.SSLSocket)
    728         finally:
    729             sock.close()
    730 
    731     def test_check_hostname(self):
    732         self.client.quit()
    733         ctx = ssl.SSLContext(ssl.PROTOCOL_TLSv1)
    734         ctx.verify_mode = ssl.CERT_REQUIRED
    735         ctx.check_hostname = True
    736         ctx.load_verify_locations(CAFILE)
    737         self.client = ftplib.FTP_TLS(context=ctx, timeout=TIMEOUT)
    738 
    739         # 127.0.0.1 doesn't match SAN
    740         self.client.connect(self.server.host, self.server.port)
    741         with self.assertRaises(ssl.CertificateError):
    742             self.client.auth()
    743         # exception quits connection
    744 
    745         self.client.connect(self.server.host, self.server.port)
    746         self.client.prot_p()
    747         with self.assertRaises(ssl.CertificateError):
    748             self.client.transfercmd("list").close()
    749         self.client.quit()
    750 
    751         self.client.connect("localhost", self.server.port)
    752         self.client.auth()
    753         self.client.quit()
    754 
    755         self.client.connect("localhost", self.server.port)
    756         self.client.prot_p()
    757         self.client.transfercmd("list").close()
    758 
    759 
    760 class TestTimeouts(TestCase):
    761 
    762     def setUp(self):
    763         self.evt = threading.Event()
    764         self.sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
    765         self.sock.settimeout(10)
    766         self.port = test_support.bind_port(self.sock)
    767         threading.Thread(target=self.server, args=(self.evt,self.sock)).start()
    768         # Wait for the server to be ready.
    769         self.evt.wait()
    770         self.evt.clear()
    771         ftplib.FTP.port = self.port
    772 
    773     def tearDown(self):
    774         self.evt.wait()
    775 
    776     def server(self, evt, serv):
    777         # This method sets the evt 3 times:
    778         #  1) when the connection is ready to be accepted.
    779         #  2) when it is safe for the caller to close the connection
    780         #  3) when we have closed the socket
    781         serv.listen(5)
    782         # (1) Signal the caller that we are ready to accept the connection.
    783         evt.set()
    784         try:
    785             conn, addr = serv.accept()
    786         except socket.timeout:
    787             pass
    788         else:
    789             conn.send("1 Hola mundo\n")
    790             # (2) Signal the caller that it is safe to close the socket.
    791             evt.set()
    792             conn.close()
    793         finally:
    794             serv.close()
    795             # (3) Signal the caller that we are done.
    796             evt.set()
    797 
    798     def testTimeoutDefault(self):
    799         # default -- use global socket timeout
    800         self.assertIsNone(socket.getdefaulttimeout())
    801         socket.setdefaulttimeout(30)
    802         try:
    803             ftp = ftplib.FTP(HOST)
    804         finally:
    805             socket.setdefaulttimeout(None)
    806         self.assertEqual(ftp.sock.gettimeout(), 30)
    807         self.evt.wait()
    808         ftp.close()
    809 
    810     def testTimeoutNone(self):
    811         # no timeout -- do not use global socket timeout
    812         self.assertIsNone(socket.getdefaulttimeout())
    813         socket.setdefaulttimeout(30)
    814         try:
    815             ftp = ftplib.FTP(HOST, timeout=None)
    816         finally:
    817             socket.setdefaulttimeout(None)
    818         self.assertIsNone(ftp.sock.gettimeout())
    819         self.evt.wait()
    820         ftp.close()
    821 
    822     def testTimeoutValue(self):
    823         # a value
    824         ftp = ftplib.FTP(HOST, timeout=30)
    825         self.assertEqual(ftp.sock.gettimeout(), 30)
    826         self.evt.wait()
    827         ftp.close()
    828 
    829     def testTimeoutConnect(self):
    830         ftp = ftplib.FTP()
    831         ftp.connect(HOST, timeout=30)
    832         self.assertEqual(ftp.sock.gettimeout(), 30)
    833         self.evt.wait()
    834         ftp.close()
    835 
    836     def testTimeoutDifferentOrder(self):
    837         ftp = ftplib.FTP(timeout=30)
    838         ftp.connect(HOST)
    839         self.assertEqual(ftp.sock.gettimeout(), 30)
    840         self.evt.wait()
    841         ftp.close()
    842 
    843     def testTimeoutDirectAccess(self):
    844         ftp = ftplib.FTP()
    845         ftp.timeout = 30
    846         ftp.connect(HOST)
    847         self.assertEqual(ftp.sock.gettimeout(), 30)
    848         self.evt.wait()
    849         ftp.close()
    850 
    851 
    852 def test_main():
    853     tests = [TestFTPClass, TestTimeouts,
    854              TestIPv6Environment,
    855              TestTLS_FTPClassMixin, TestTLS_FTPClass]
    856 
    857     thread_info = test_support.threading_setup()
    858     try:
    859         test_support.run_unittest(*tests)
    860     finally:
    861         test_support.threading_cleanup(*thread_info)
    862 
    863 
    864 if __name__ == '__main__':
    865     test_main()
    866