blob: 4b73e0082a9a5ecbb33924d8ff5e16566a905582 [file] [log] [blame]
Benjamin Peterson3c0c4832008-09-27 02:49:54 +00001"""Test script for ftplib module."""
2
Antoine Pitrouccd5e022009-11-15 17:22:09 +00003# Modified by Giampaolo Rodola' to test FTP class, IPv6 and TLS
4# environment
Benjamin Peterson3c0c4832008-09-27 02:49:54 +00005
Facundo Batista3f100992007-03-26 20:56:09 +00006import ftplib
Benjamin Peterson3c0c4832008-09-27 02:49:54 +00007import threading
8import asyncore
9import asynchat
10import socket
11import StringIO
Antoine Pitrouccd5e022009-11-15 17:22:09 +000012import errno
13import os
14try:
15 import ssl
16except ImportError:
17 ssl = None
Facundo Batista3f100992007-03-26 20:56:09 +000018
19from unittest import TestCase
20from test import test_support
Benjamin Peterson3c0c4832008-09-27 02:49:54 +000021from test.test_support import HOST
Facundo Batista3f100992007-03-26 20:56:09 +000022
Neal Norwitzb0917c12008-02-26 04:50:37 +000023
Benjamin Peterson3c0c4832008-09-27 02:49:54 +000024# the dummy data returned by server over the data channel when
25# RETR, LIST and NLST commands are issued
26RETR_DATA = 'abcde12345\r\n' * 1000
27LIST_DATA = 'foo\r\nbar\r\n'
28NLST_DATA = 'foo\r\nbar\r\n'
29
30
31class DummyDTPHandler(asynchat.async_chat):
32
33 def __init__(self, conn, baseclass):
34 asynchat.async_chat.__init__(self, conn)
35 self.baseclass = baseclass
36 self.baseclass.last_received_data = ''
37
38 def handle_read(self):
39 self.baseclass.last_received_data += self.recv(1024)
40
41 def handle_close(self):
42 self.baseclass.push('226 transfer complete')
43 self.close()
44
45
46class DummyFTPHandler(asynchat.async_chat):
47
Antoine Pitrouccd5e022009-11-15 17:22:09 +000048 dtp_handler = DummyDTPHandler
49
Benjamin Peterson3c0c4832008-09-27 02:49:54 +000050 def __init__(self, conn):
51 asynchat.async_chat.__init__(self, conn)
52 self.set_terminator("\r\n")
53 self.in_buffer = []
54 self.dtp = None
55 self.last_received_cmd = None
56 self.last_received_data = ''
57 self.next_response = ''
Antoine Pitrouacbe3bd2009-11-27 13:18:34 +000058 self.rest = None
Benjamin Peterson3c0c4832008-09-27 02:49:54 +000059 self.push('220 welcome')
60
61 def collect_incoming_data(self, data):
62 self.in_buffer.append(data)
63
64 def found_terminator(self):
65 line = ''.join(self.in_buffer)
66 self.in_buffer = []
67 if self.next_response:
68 self.push(self.next_response)
69 self.next_response = ''
70 cmd = line.split(' ')[0].lower()
71 self.last_received_cmd = cmd
72 space = line.find(' ')
73 if space != -1:
74 arg = line[space + 1:]
75 else:
76 arg = ""
77 if hasattr(self, 'cmd_' + cmd):
78 method = getattr(self, 'cmd_' + cmd)
79 method(arg)
80 else:
81 self.push('550 command "%s" not understood.' %cmd)
82
83 def handle_error(self):
84 raise
85
86 def push(self, data):
87 asynchat.async_chat.push(self, data + '\r\n')
88
89 def cmd_port(self, arg):
90 addr = map(int, arg.split(','))
91 ip = '%d.%d.%d.%d' %tuple(addr[:4])
92 port = (addr[4] * 256) + addr[5]
93 s = socket.create_connection((ip, port), timeout=2)
Antoine Pitrouccd5e022009-11-15 17:22:09 +000094 self.dtp = self.dtp_handler(s, baseclass=self)
Benjamin Peterson3c0c4832008-09-27 02:49:54 +000095 self.push('200 active data connection established')
96
97 def cmd_pasv(self, arg):
98 sock = socket.socket()
99 sock.bind((self.socket.getsockname()[0], 0))
100 sock.listen(5)
101 sock.settimeout(2)
102 ip, port = sock.getsockname()[:2]
Ezio Melottidde5b942010-02-03 05:37:26 +0000103 ip = ip.replace('.', ',')
104 p1, p2 = divmod(port, 256)
Benjamin Peterson3c0c4832008-09-27 02:49:54 +0000105 self.push('227 entering passive mode (%s,%d,%d)' %(ip, p1, p2))
106 conn, addr = sock.accept()
Antoine Pitrouccd5e022009-11-15 17:22:09 +0000107 self.dtp = self.dtp_handler(conn, baseclass=self)
Benjamin Peterson3c0c4832008-09-27 02:49:54 +0000108
109 def cmd_eprt(self, arg):
110 af, ip, port = arg.split(arg[0])[1:-1]
111 port = int(port)
112 s = socket.create_connection((ip, port), timeout=2)
Antoine Pitrouccd5e022009-11-15 17:22:09 +0000113 self.dtp = self.dtp_handler(s, baseclass=self)
Benjamin Peterson3c0c4832008-09-27 02:49:54 +0000114 self.push('200 active data connection established')
115
116 def cmd_epsv(self, arg):
117 sock = socket.socket(socket.AF_INET6)
118 sock.bind((self.socket.getsockname()[0], 0))
119 sock.listen(5)
120 sock.settimeout(2)
121 port = sock.getsockname()[1]
122 self.push('229 entering extended passive mode (|||%d|)' %port)
123 conn, addr = sock.accept()
Antoine Pitrouccd5e022009-11-15 17:22:09 +0000124 self.dtp = self.dtp_handler(conn, baseclass=self)
Benjamin Peterson3c0c4832008-09-27 02:49:54 +0000125
126 def cmd_echo(self, arg):
127 # sends back the received string (used by the test suite)
128 self.push(arg)
129
130 def cmd_user(self, arg):
131 self.push('331 username ok')
132
133 def cmd_pass(self, arg):
134 self.push('230 password ok')
135
136 def cmd_acct(self, arg):
137 self.push('230 acct ok')
138
139 def cmd_rnfr(self, arg):
140 self.push('350 rnfr ok')
141
142 def cmd_rnto(self, arg):
143 self.push('250 rnto ok')
144
145 def cmd_dele(self, arg):
146 self.push('250 dele ok')
147
148 def cmd_cwd(self, arg):
149 self.push('250 cwd ok')
150
151 def cmd_size(self, arg):
152 self.push('250 1000')
153
154 def cmd_mkd(self, arg):
155 self.push('257 "%s"' %arg)
156
157 def cmd_rmd(self, arg):
158 self.push('250 rmd ok')
159
160 def cmd_pwd(self, arg):
161 self.push('257 "pwd ok"')
162
163 def cmd_type(self, arg):
164 self.push('200 type ok')
165
166 def cmd_quit(self, arg):
167 self.push('221 quit ok')
168 self.close()
169
170 def cmd_stor(self, arg):
171 self.push('125 stor ok')
172
Antoine Pitrouacbe3bd2009-11-27 13:18:34 +0000173 def cmd_rest(self, arg):
174 self.rest = arg
175 self.push('350 rest ok')
176
Benjamin Peterson3c0c4832008-09-27 02:49:54 +0000177 def cmd_retr(self, arg):
178 self.push('125 retr ok')
Antoine Pitrouacbe3bd2009-11-27 13:18:34 +0000179 if self.rest is not None:
180 offset = int(self.rest)
181 else:
182 offset = 0
183 self.dtp.push(RETR_DATA[offset:])
Benjamin Peterson3c0c4832008-09-27 02:49:54 +0000184 self.dtp.close_when_done()
Antoine Pitrouacbe3bd2009-11-27 13:18:34 +0000185 self.rest = None
Benjamin Peterson3c0c4832008-09-27 02:49:54 +0000186
187 def cmd_list(self, arg):
188 self.push('125 list ok')
189 self.dtp.push(LIST_DATA)
190 self.dtp.close_when_done()
191
192 def cmd_nlst(self, arg):
193 self.push('125 nlst ok')
194 self.dtp.push(NLST_DATA)
195 self.dtp.close_when_done()
196
197
198class DummyFTPServer(asyncore.dispatcher, threading.Thread):
199
200 handler = DummyFTPHandler
201
202 def __init__(self, address, af=socket.AF_INET):
203 threading.Thread.__init__(self)
204 asyncore.dispatcher.__init__(self)
205 self.create_socket(af, socket.SOCK_STREAM)
206 self.bind(address)
207 self.listen(5)
208 self.active = False
209 self.active_lock = threading.Lock()
210 self.host, self.port = self.socket.getsockname()[:2]
211
212 def start(self):
213 assert not self.active
214 self.__flag = threading.Event()
215 threading.Thread.start(self)
216 self.__flag.wait()
217
218 def run(self):
219 self.active = True
220 self.__flag.set()
221 while self.active and asyncore.socket_map:
222 self.active_lock.acquire()
223 asyncore.loop(timeout=0.1, count=1)
224 self.active_lock.release()
225 asyncore.close_all(ignore_all=True)
226
227 def stop(self):
228 assert self.active
229 self.active = False
230 self.join()
231
232 def handle_accept(self):
233 conn, addr = self.accept()
234 self.handler = self.handler(conn)
Benjamin Petersone14267b2008-09-28 20:57:21 +0000235 self.close()
236
237 def handle_connect(self):
238 self.close()
239 handle_read = handle_connect
Benjamin Peterson3c0c4832008-09-27 02:49:54 +0000240
241 def writable(self):
242 return 0
243
244 def handle_error(self):
245 raise
246
247
Antoine Pitrouccd5e022009-11-15 17:22:09 +0000248if ssl is not None:
249
250 CERTFILE = os.path.join(os.path.dirname(__file__), "keycert.pem")
251
252 class SSLConnection(object, asyncore.dispatcher):
253 """An asyncore.dispatcher subclass supporting TLS/SSL."""
254
255 _ssl_accepting = False
256
257 def secure_connection(self):
258 self.socket = ssl.wrap_socket(self.socket, suppress_ragged_eofs=False,
259 certfile=CERTFILE, server_side=True,
260 do_handshake_on_connect=False,
261 ssl_version=ssl.PROTOCOL_SSLv23)
262 self._ssl_accepting = True
263
264 def _do_ssl_handshake(self):
265 try:
266 self.socket.do_handshake()
267 except ssl.SSLError, err:
268 if err.args[0] in (ssl.SSL_ERROR_WANT_READ,
269 ssl.SSL_ERROR_WANT_WRITE):
270 return
271 elif err.args[0] == ssl.SSL_ERROR_EOF:
272 return self.handle_close()
273 raise
274 except socket.error, err:
275 if err.args[0] == errno.ECONNABORTED:
276 return self.handle_close()
277 else:
278 self._ssl_accepting = False
279
280 def handle_read_event(self):
281 if self._ssl_accepting:
282 self._do_ssl_handshake()
283 else:
284 super(SSLConnection, self).handle_read_event()
285
286 def handle_write_event(self):
287 if self._ssl_accepting:
288 self._do_ssl_handshake()
289 else:
290 super(SSLConnection, self).handle_write_event()
291
292 def send(self, data):
293 try:
294 return super(SSLConnection, self).send(data)
295 except ssl.SSLError, err:
Antoine Pitrou52093b82010-03-22 14:41:48 +0000296 if err.args[0] in (ssl.SSL_ERROR_EOF, ssl.SSL_ERROR_ZERO_RETURN,
297 ssl.SSL_ERROR_WANT_READ,
298 ssl.SSL_ERROR_WANT_WRITE):
Antoine Pitrouccd5e022009-11-15 17:22:09 +0000299 return 0
300 raise
301
302 def recv(self, buffer_size):
303 try:
304 return super(SSLConnection, self).recv(buffer_size)
305 except ssl.SSLError, err:
Antoine Pitrou52093b82010-03-22 14:41:48 +0000306 if err.args[0] in (ssl.SSL_ERROR_WANT_READ,
307 ssl.SSL_ERROR_WANT_WRITE):
308 return ''
Antoine Pitrouccd5e022009-11-15 17:22:09 +0000309 if err.args[0] in (ssl.SSL_ERROR_EOF, ssl.SSL_ERROR_ZERO_RETURN):
310 self.handle_close()
311 return ''
312 raise
313
314 def handle_error(self):
315 raise
316
317 def close(self):
Antoine Pitrou914bdbb2010-03-24 21:55:12 +0000318 ssl_want_read_or_write = False
Antoine Pitrouccd5e022009-11-15 17:22:09 +0000319 try:
320 if isinstance(self.socket, ssl.SSLSocket):
321 if self.socket._sslobj is not None:
Antoine Pitrou914bdbb2010-03-24 21:55:12 +0000322 try:
323 self.socket.unwrap()
324 except ssl.SSLError, err:
325 if err.args[0] in (ssl.SSL_ERROR_WANT_READ,
326 ssl.SSL_ERROR_WANT_WRITE):
327 ssl_want_read_or_write = True
328 else:
329 raise
Antoine Pitrouccd5e022009-11-15 17:22:09 +0000330 finally:
Antoine Pitrou914bdbb2010-03-24 21:55:12 +0000331 if not ssl_want_read_or_write:
332 super(SSLConnection, self).close()
Antoine Pitrouccd5e022009-11-15 17:22:09 +0000333
334
335 class DummyTLS_DTPHandler(SSLConnection, DummyDTPHandler):
336 """A DummyDTPHandler subclass supporting TLS/SSL."""
337
338 def __init__(self, conn, baseclass):
339 DummyDTPHandler.__init__(self, conn, baseclass)
340 if self.baseclass.secure_data_channel:
341 self.secure_connection()
342
343
344 class DummyTLS_FTPHandler(SSLConnection, DummyFTPHandler):
345 """A DummyFTPHandler subclass supporting TLS/SSL."""
346
347 dtp_handler = DummyTLS_DTPHandler
348
349 def __init__(self, conn):
350 DummyFTPHandler.__init__(self, conn)
351 self.secure_data_channel = False
352
353 def cmd_auth(self, line):
354 """Set up secure control channel."""
355 self.push('234 AUTH TLS successful')
356 self.secure_connection()
357
358 def cmd_pbsz(self, line):
359 """Negotiate size of buffer for secure data transfer.
360 For TLS/SSL the only valid value for the parameter is '0'.
361 Any other value is accepted but ignored.
362 """
363 self.push('200 PBSZ=0 successful.')
364
365 def cmd_prot(self, line):
366 """Setup un/secure data channel."""
367 arg = line.upper()
368 if arg == 'C':
369 self.push('200 Protection set to Clear')
370 self.secure_data_channel = False
371 elif arg == 'P':
372 self.push('200 Protection set to Private')
373 self.secure_data_channel = True
374 else:
375 self.push("502 Unrecognized PROT type (use C or P).")
376
377
378 class DummyTLS_FTPServer(DummyFTPServer):
379 handler = DummyTLS_FTPHandler
380
381
Benjamin Peterson3c0c4832008-09-27 02:49:54 +0000382class TestFTPClass(TestCase):
383
384 def setUp(self):
385 self.server = DummyFTPServer((HOST, 0))
386 self.server.start()
387 self.client = ftplib.FTP(timeout=2)
388 self.client.connect(self.server.host, self.server.port)
389
390 def tearDown(self):
391 self.client.close()
392 self.server.stop()
393
394 def test_getwelcome(self):
395 self.assertEqual(self.client.getwelcome(), '220 welcome')
396
397 def test_sanitize(self):
398 self.assertEqual(self.client.sanitize('foo'), repr('foo'))
399 self.assertEqual(self.client.sanitize('pass 12345'), repr('pass *****'))
400 self.assertEqual(self.client.sanitize('PASS 12345'), repr('PASS *****'))
401
402 def test_exceptions(self):
403 self.assertRaises(ftplib.error_temp, self.client.sendcmd, 'echo 400')
404 self.assertRaises(ftplib.error_temp, self.client.sendcmd, 'echo 499')
405 self.assertRaises(ftplib.error_perm, self.client.sendcmd, 'echo 500')
406 self.assertRaises(ftplib.error_perm, self.client.sendcmd, 'echo 599')
407 self.assertRaises(ftplib.error_proto, self.client.sendcmd, 'echo 999')
408
409 def test_all_errors(self):
410 exceptions = (ftplib.error_reply, ftplib.error_temp, ftplib.error_perm,
411 ftplib.error_proto, ftplib.Error, IOError, EOFError)
412 for x in exceptions:
413 try:
414 raise x('exception not included in all_errors set')
415 except ftplib.all_errors:
416 pass
417
418 def test_set_pasv(self):
419 # passive mode is supposed to be enabled by default
420 self.assertTrue(self.client.passiveserver)
421 self.client.set_pasv(True)
422 self.assertTrue(self.client.passiveserver)
423 self.client.set_pasv(False)
424 self.assertFalse(self.client.passiveserver)
425
426 def test_voidcmd(self):
427 self.client.voidcmd('echo 200')
428 self.client.voidcmd('echo 299')
429 self.assertRaises(ftplib.error_reply, self.client.voidcmd, 'echo 199')
430 self.assertRaises(ftplib.error_reply, self.client.voidcmd, 'echo 300')
431
432 def test_login(self):
433 self.client.login()
434
435 def test_acct(self):
436 self.client.acct('passwd')
437
438 def test_rename(self):
439 self.client.rename('a', 'b')
440 self.server.handler.next_response = '200'
441 self.assertRaises(ftplib.error_reply, self.client.rename, 'a', 'b')
442
443 def test_delete(self):
444 self.client.delete('foo')
445 self.server.handler.next_response = '199'
446 self.assertRaises(ftplib.error_reply, self.client.delete, 'foo')
447
448 def test_size(self):
449 self.client.size('foo')
450
451 def test_mkd(self):
452 dir = self.client.mkd('/foo')
453 self.assertEqual(dir, '/foo')
454
455 def test_rmd(self):
456 self.client.rmd('foo')
457
458 def test_pwd(self):
459 dir = self.client.pwd()
460 self.assertEqual(dir, 'pwd ok')
461
462 def test_quit(self):
463 self.assertEqual(self.client.quit(), '221 quit ok')
464 # Ensure the connection gets closed; sock attribute should be None
465 self.assertEqual(self.client.sock, None)
466
467 def test_retrbinary(self):
468 received = []
469 self.client.retrbinary('retr', received.append)
470 self.assertEqual(''.join(received), RETR_DATA)
471
Antoine Pitrouacbe3bd2009-11-27 13:18:34 +0000472 def test_retrbinary_rest(self):
473 for rest in (0, 10, 20):
474 received = []
475 self.client.retrbinary('retr', received.append, rest=rest)
476 self.assertEqual(''.join(received), RETR_DATA[rest:],
477 msg='rest test case %d %d %d' % (rest,
478 len(''.join(received)),
479 len(RETR_DATA[rest:])))
480
Benjamin Peterson3c0c4832008-09-27 02:49:54 +0000481 def test_retrlines(self):
482 received = []
483 self.client.retrlines('retr', received.append)
484 self.assertEqual(''.join(received), RETR_DATA.replace('\r\n', ''))
485
486 def test_storbinary(self):
487 f = StringIO.StringIO(RETR_DATA)
488 self.client.storbinary('stor', f)
489 self.assertEqual(self.server.handler.last_received_data, RETR_DATA)
490 # test new callback arg
491 flag = []
492 f.seek(0)
493 self.client.storbinary('stor', f, callback=lambda x: flag.append(None))
494 self.assertTrue(flag)
495
Antoine Pitrouacbe3bd2009-11-27 13:18:34 +0000496 def test_storbinary_rest(self):
497 f = StringIO.StringIO(RETR_DATA)
498 for r in (30, '30'):
499 f.seek(0)
500 self.client.storbinary('stor', f, rest=r)
501 self.assertEqual(self.server.handler.rest, str(r))
502
Benjamin Peterson3c0c4832008-09-27 02:49:54 +0000503 def test_storlines(self):
504 f = StringIO.StringIO(RETR_DATA.replace('\r\n', '\n'))
505 self.client.storlines('stor', f)
506 self.assertEqual(self.server.handler.last_received_data, RETR_DATA)
507 # test new callback arg
508 flag = []
509 f.seek(0)
510 self.client.storlines('stor foo', f, callback=lambda x: flag.append(None))
511 self.assertTrue(flag)
512
513 def test_nlst(self):
514 self.client.nlst()
515 self.assertEqual(self.client.nlst(), NLST_DATA.split('\r\n')[:-1])
516
517 def test_dir(self):
518 l = []
519 self.client.dir(lambda x: l.append(x))
520 self.assertEqual(''.join(l), LIST_DATA.replace('\r\n', ''))
521
522 def test_makeport(self):
523 self.client.makeport()
524 # IPv4 is in use, just make sure send_eprt has not been used
525 self.assertEqual(self.server.handler.last_received_cmd, 'port')
526
527 def test_makepasv(self):
528 host, port = self.client.makepasv()
529 conn = socket.create_connection((host, port), 2)
Facundo Batista93c33682007-03-30 13:00:35 +0000530 conn.close()
Benjamin Peterson3c0c4832008-09-27 02:49:54 +0000531 # IPv4 is in use, just make sure send_epsv has not been used
532 self.assertEqual(self.server.handler.last_received_cmd, 'pasv')
Facundo Batista3f100992007-03-26 20:56:09 +0000533
Benjamin Peterson3c0c4832008-09-27 02:49:54 +0000534
535class TestIPv6Environment(TestCase):
536
537 def setUp(self):
538 self.server = DummyFTPServer((HOST, 0), af=socket.AF_INET6)
539 self.server.start()
540 self.client = ftplib.FTP()
541 self.client.connect(self.server.host, self.server.port)
542
543 def tearDown(self):
544 self.client.close()
545 self.server.stop()
546
547 def test_af(self):
548 self.assertEqual(self.client.af, socket.AF_INET6)
549
550 def test_makeport(self):
551 self.client.makeport()
552 self.assertEqual(self.server.handler.last_received_cmd, 'eprt')
553
554 def test_makepasv(self):
555 host, port = self.client.makepasv()
556 conn = socket.create_connection((host, port), 2)
557 conn.close()
558 self.assertEqual(self.server.handler.last_received_cmd, 'epsv')
559
560 def test_transfer(self):
561 def retr():
562 received = []
563 self.client.retrbinary('retr', received.append)
564 self.assertEqual(''.join(received), RETR_DATA)
565 self.client.set_pasv(True)
566 retr()
567 self.client.set_pasv(False)
568 retr()
569
570
Antoine Pitrouccd5e022009-11-15 17:22:09 +0000571class TestTLS_FTPClassMixin(TestFTPClass):
572 """Repeat TestFTPClass tests starting the TLS layer for both control
573 and data connections first.
574 """
575
576 def setUp(self):
577 self.server = DummyTLS_FTPServer((HOST, 0))
578 self.server.start()
579 self.client = ftplib.FTP_TLS(timeout=2)
580 self.client.connect(self.server.host, self.server.port)
581 # enable TLS
582 self.client.auth()
583 self.client.prot_p()
584
585
586class TestTLS_FTPClass(TestCase):
587 """Specific TLS_FTP class tests."""
588
589 def setUp(self):
590 self.server = DummyTLS_FTPServer((HOST, 0))
591 self.server.start()
592 self.client = ftplib.FTP_TLS(timeout=2)
593 self.client.connect(self.server.host, self.server.port)
594
595 def tearDown(self):
596 self.client.close()
597 self.server.stop()
598
599 def test_control_connection(self):
Ezio Melottib0f5adc2010-01-24 16:58:36 +0000600 self.assertNotIsInstance(self.client.sock, ssl.SSLSocket)
Antoine Pitrouccd5e022009-11-15 17:22:09 +0000601 self.client.auth()
Ezio Melottib0f5adc2010-01-24 16:58:36 +0000602 self.assertIsInstance(self.client.sock, ssl.SSLSocket)
Antoine Pitrouccd5e022009-11-15 17:22:09 +0000603
604 def test_data_connection(self):
605 # clear text
606 sock = self.client.transfercmd('list')
Ezio Melottib0f5adc2010-01-24 16:58:36 +0000607 self.assertNotIsInstance(sock, ssl.SSLSocket)
Antoine Pitrouccd5e022009-11-15 17:22:09 +0000608 sock.close()
609 self.client.voidresp()
610
611 # secured, after PROT P
612 self.client.prot_p()
613 sock = self.client.transfercmd('list')
Ezio Melottib0f5adc2010-01-24 16:58:36 +0000614 self.assertIsInstance(sock, ssl.SSLSocket)
Antoine Pitrouccd5e022009-11-15 17:22:09 +0000615 sock.close()
616 self.client.voidresp()
617
618 # PROT C is issued, the connection must be in cleartext again
619 self.client.prot_c()
620 sock = self.client.transfercmd('list')
Ezio Melottib0f5adc2010-01-24 16:58:36 +0000621 self.assertNotIsInstance(sock, ssl.SSLSocket)
Antoine Pitrouccd5e022009-11-15 17:22:09 +0000622 sock.close()
623 self.client.voidresp()
624
625 def test_login(self):
626 # login() is supposed to implicitly secure the control connection
Ezio Melottib0f5adc2010-01-24 16:58:36 +0000627 self.assertNotIsInstance(self.client.sock, ssl.SSLSocket)
Antoine Pitrouccd5e022009-11-15 17:22:09 +0000628 self.client.login()
Ezio Melottib0f5adc2010-01-24 16:58:36 +0000629 self.assertIsInstance(self.client.sock, ssl.SSLSocket)
Antoine Pitrouccd5e022009-11-15 17:22:09 +0000630 # make sure that AUTH TLS doesn't get issued again
631 self.client.login()
632
633 def test_auth_issued_twice(self):
634 self.client.auth()
635 self.assertRaises(ValueError, self.client.auth)
636
637 def test_auth_ssl(self):
638 try:
639 self.client.ssl_version = ssl.PROTOCOL_SSLv3
640 self.client.auth()
641 self.assertRaises(ValueError, self.client.auth)
642 finally:
643 self.client.ssl_version = ssl.PROTOCOL_TLSv1
644
645
Benjamin Peterson3c0c4832008-09-27 02:49:54 +0000646class TestTimeouts(TestCase):
Neal Norwitz0d4c06e2007-04-25 06:30:05 +0000647
Facundo Batista3f100992007-03-26 20:56:09 +0000648 def setUp(self):
Facundo Batista3f100992007-03-26 20:56:09 +0000649 self.evt = threading.Event()
Trent Nelsone41b0062008-04-08 23:47:30 +0000650 self.sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
651 self.sock.settimeout(3)
652 self.port = test_support.bind_port(self.sock)
Benjamin Peterson3c0c4832008-09-27 02:49:54 +0000653 threading.Thread(target=self.server, args=(self.evt,self.sock)).start()
Neal Norwitzb0917c12008-02-26 04:50:37 +0000654 # Wait for the server to be ready.
655 self.evt.wait()
656 self.evt.clear()
Trent Nelsone41b0062008-04-08 23:47:30 +0000657 ftplib.FTP.port = self.port
Facundo Batista3f100992007-03-26 20:56:09 +0000658
659 def tearDown(self):
660 self.evt.wait()
661
Benjamin Peterson3c0c4832008-09-27 02:49:54 +0000662 def server(self, evt, serv):
663 # This method sets the evt 3 times:
664 # 1) when the connection is ready to be accepted.
665 # 2) when it is safe for the caller to close the connection
666 # 3) when we have closed the socket
667 serv.listen(5)
668 # (1) Signal the caller that we are ready to accept the connection.
669 evt.set()
670 try:
671 conn, addr = serv.accept()
672 except socket.timeout:
673 pass
674 else:
675 conn.send("1 Hola mundo\n")
676 # (2) Signal the caller that it is safe to close the socket.
677 evt.set()
678 conn.close()
679 finally:
680 serv.close()
681 # (3) Signal the caller that we are done.
682 evt.set()
Neal Norwitz0d4c06e2007-04-25 06:30:05 +0000683
Facundo Batista3f100992007-03-26 20:56:09 +0000684 def testTimeoutDefault(self):
Facundo Batista4f1b1ed2008-05-29 16:39:26 +0000685 # default -- use global socket timeout
Benjamin Peterson5c8da862009-06-30 22:57:08 +0000686 self.assertTrue(socket.getdefaulttimeout() is None)
Facundo Batista4f1b1ed2008-05-29 16:39:26 +0000687 socket.setdefaulttimeout(30)
688 try:
689 ftp = ftplib.FTP("localhost")
690 finally:
691 socket.setdefaulttimeout(None)
692 self.assertEqual(ftp.sock.gettimeout(), 30)
693 self.evt.wait()
694 ftp.close()
695
696 def testTimeoutNone(self):
697 # no timeout -- do not use global socket timeout
Benjamin Peterson5c8da862009-06-30 22:57:08 +0000698 self.assertTrue(socket.getdefaulttimeout() is None)
Facundo Batista4f1b1ed2008-05-29 16:39:26 +0000699 socket.setdefaulttimeout(30)
700 try:
701 ftp = ftplib.FTP("localhost", timeout=None)
702 finally:
703 socket.setdefaulttimeout(None)
Facundo Batista3f100992007-03-26 20:56:09 +0000704 self.assertTrue(ftp.sock.gettimeout() is None)
Neal Norwitzb0917c12008-02-26 04:50:37 +0000705 self.evt.wait()
Facundo Batista4f1b1ed2008-05-29 16:39:26 +0000706 ftp.close()
Neal Norwitz0d4c06e2007-04-25 06:30:05 +0000707
Facundo Batista3f100992007-03-26 20:56:09 +0000708 def testTimeoutValue(self):
709 # a value
Trent Nelsone41b0062008-04-08 23:47:30 +0000710 ftp = ftplib.FTP(HOST, timeout=30)
Facundo Batista3f100992007-03-26 20:56:09 +0000711 self.assertEqual(ftp.sock.gettimeout(), 30)
Neal Norwitzb0917c12008-02-26 04:50:37 +0000712 self.evt.wait()
Facundo Batista4f1b1ed2008-05-29 16:39:26 +0000713 ftp.close()
Facundo Batista3f100992007-03-26 20:56:09 +0000714
Facundo Batista93c33682007-03-30 13:00:35 +0000715 def testTimeoutConnect(self):
716 ftp = ftplib.FTP()
Trent Nelsone41b0062008-04-08 23:47:30 +0000717 ftp.connect(HOST, timeout=30)
Facundo Batista93c33682007-03-30 13:00:35 +0000718 self.assertEqual(ftp.sock.gettimeout(), 30)
Neal Norwitzb0917c12008-02-26 04:50:37 +0000719 self.evt.wait()
Facundo Batista4f1b1ed2008-05-29 16:39:26 +0000720 ftp.close()
Facundo Batista93c33682007-03-30 13:00:35 +0000721
722 def testTimeoutDifferentOrder(self):
723 ftp = ftplib.FTP(timeout=30)
Trent Nelsone41b0062008-04-08 23:47:30 +0000724 ftp.connect(HOST)
Facundo Batista93c33682007-03-30 13:00:35 +0000725 self.assertEqual(ftp.sock.gettimeout(), 30)
Neal Norwitzb0917c12008-02-26 04:50:37 +0000726 self.evt.wait()
Facundo Batista4f1b1ed2008-05-29 16:39:26 +0000727 ftp.close()
Facundo Batista93c33682007-03-30 13:00:35 +0000728
729 def testTimeoutDirectAccess(self):
730 ftp = ftplib.FTP()
731 ftp.timeout = 30
Trent Nelsone41b0062008-04-08 23:47:30 +0000732 ftp.connect(HOST)
Facundo Batista93c33682007-03-30 13:00:35 +0000733 self.assertEqual(ftp.sock.gettimeout(), 30)
Neal Norwitzb0917c12008-02-26 04:50:37 +0000734 self.evt.wait()
Facundo Batista3f100992007-03-26 20:56:09 +0000735 ftp.close()
736
737
Benjamin Peterson3c0c4832008-09-27 02:49:54 +0000738def test_main():
739 tests = [TestFTPClass, TestTimeouts]
740 if socket.has_ipv6:
741 try:
742 DummyFTPServer((HOST, 0), af=socket.AF_INET6)
743 except socket.error:
744 pass
745 else:
746 tests.append(TestIPv6Environment)
Antoine Pitrouccd5e022009-11-15 17:22:09 +0000747
748 if ssl is not None:
749 tests.extend([TestTLS_FTPClassMixin, TestTLS_FTPClass])
750
Benjamin Peterson3c0c4832008-09-27 02:49:54 +0000751 thread_info = test_support.threading_setup()
752 try:
753 test_support.run_unittest(*tests)
754 finally:
755 test_support.threading_cleanup(*thread_info)
756
Facundo Batista3f100992007-03-26 20:56:09 +0000757
758if __name__ == '__main__':
759 test_main()