blob: 29f7f7d84bcdb75d3e4af3074a31d21f395a03b8 [file] [log] [blame]
Benjamin Petersonbe17a112008-09-27 21:49:47 +00001"""Test script for ftplib module."""
2
Antoine Pitrouf988cd02009-11-17 20:21:14 +00003# Modified by Giampaolo Rodola' to test FTP class, IPv6 and TLS
4# environment
Benjamin Petersonbe17a112008-09-27 21:49:47 +00005
Guido van Rossumd8faa362007-04-27 19:54:29 +00006import ftplib
Benjamin Petersonbe17a112008-09-27 21:49:47 +00007import threading
8import asyncore
9import asynchat
10import socket
11import io
Antoine Pitrouf988cd02009-11-17 20:21:14 +000012import errno
13import os
14try:
15 import ssl
16except ImportError:
17 ssl = None
Guido van Rossumd8faa362007-04-27 19:54:29 +000018
19from unittest import TestCase
Benjamin Petersonee8712c2008-05-20 21:35:26 +000020from test import support
Benjamin Petersonbe17a112008-09-27 21:49:47 +000021from test.support import HOST
Guido van Rossumd8faa362007-04-27 19:54:29 +000022
Benjamin Petersonbe17a112008-09-27 21:49:47 +000023# the dummy data returned by server over the data channel when
24# RETR, LIST and NLST commands are issued
25RETR_DATA = 'abcde12345\r\n' * 1000
26LIST_DATA = 'foo\r\nbar\r\n'
27NLST_DATA = 'foo\r\nbar\r\n'
Christian Heimes836baa52008-02-26 08:18:30 +000028
Christian Heimes836baa52008-02-26 08:18:30 +000029
Benjamin Petersonbe17a112008-09-27 21:49:47 +000030class DummyDTPHandler(asynchat.async_chat):
31
32 def __init__(self, conn, baseclass):
33 asynchat.async_chat.__init__(self, conn)
34 self.baseclass = baseclass
35 self.baseclass.last_received_data = ''
36
37 def handle_read(self):
38 self.baseclass.last_received_data += self.recv(1024).decode('ascii')
39
40 def handle_close(self):
41 self.baseclass.push('226 transfer complete')
42 self.close()
43
44 def push(self, what):
45 super(DummyDTPHandler, self).push(what.encode('ascii'))
46
47
48class DummyFTPHandler(asynchat.async_chat):
49
Antoine Pitrouf988cd02009-11-17 20:21:14 +000050 dtp_handler = DummyDTPHandler
51
Benjamin Petersonbe17a112008-09-27 21:49:47 +000052 def __init__(self, conn):
53 asynchat.async_chat.__init__(self, conn)
54 self.set_terminator(b"\r\n")
55 self.in_buffer = []
56 self.dtp = None
57 self.last_received_cmd = None
58 self.last_received_data = ''
59 self.next_response = ''
Antoine Pitrou648bcd72009-11-27 13:23:26 +000060 self.rest = None
Benjamin Petersonbe17a112008-09-27 21:49:47 +000061 self.push('220 welcome')
62
63 def collect_incoming_data(self, data):
64 self.in_buffer.append(data)
65
66 def found_terminator(self):
67 line = b''.join(self.in_buffer).decode('ascii')
68 self.in_buffer = []
69 if self.next_response:
70 self.push(self.next_response)
71 self.next_response = ''
72 cmd = line.split(' ')[0].lower()
73 self.last_received_cmd = cmd
74 space = line.find(' ')
75 if space != -1:
76 arg = line[space + 1:]
77 else:
78 arg = ""
79 if hasattr(self, 'cmd_' + cmd):
80 method = getattr(self, 'cmd_' + cmd)
81 method(arg)
82 else:
83 self.push('550 command "%s" not understood.' %cmd)
84
85 def handle_error(self):
86 raise
87
88 def push(self, data):
89 asynchat.async_chat.push(self, data.encode('ascii') + b'\r\n')
90
91 def cmd_port(self, arg):
92 addr = list(map(int, arg.split(',')))
93 ip = '%d.%d.%d.%d' %tuple(addr[:4])
94 port = (addr[4] * 256) + addr[5]
95 s = socket.create_connection((ip, port), timeout=2)
Antoine Pitrouf988cd02009-11-17 20:21:14 +000096 self.dtp = self.dtp_handler(s, baseclass=self)
Benjamin Petersonbe17a112008-09-27 21:49:47 +000097 self.push('200 active data connection established')
98
99 def cmd_pasv(self, arg):
100 sock = socket.socket()
101 sock.bind((self.socket.getsockname()[0], 0))
102 sock.listen(5)
103 sock.settimeout(2)
104 ip, port = sock.getsockname()[:2]
105 ip = ip.replace('.', ','); p1 = port / 256; p2 = port % 256
106 self.push('227 entering passive mode (%s,%d,%d)' %(ip, p1, p2))
107 conn, addr = sock.accept()
Antoine Pitrouf988cd02009-11-17 20:21:14 +0000108 self.dtp = self.dtp_handler(conn, baseclass=self)
Benjamin Petersonbe17a112008-09-27 21:49:47 +0000109
110 def cmd_eprt(self, arg):
111 af, ip, port = arg.split(arg[0])[1:-1]
112 port = int(port)
113 s = socket.create_connection((ip, port), timeout=2)
Antoine Pitrouf988cd02009-11-17 20:21:14 +0000114 self.dtp = self.dtp_handler(s, baseclass=self)
Benjamin Petersonbe17a112008-09-27 21:49:47 +0000115 self.push('200 active data connection established')
116
117 def cmd_epsv(self, arg):
118 sock = socket.socket(socket.AF_INET6)
119 sock.bind((self.socket.getsockname()[0], 0))
120 sock.listen(5)
121 sock.settimeout(2)
122 port = sock.getsockname()[1]
123 self.push('229 entering extended passive mode (|||%d|)' %port)
124 conn, addr = sock.accept()
Antoine Pitrouf988cd02009-11-17 20:21:14 +0000125 self.dtp = self.dtp_handler(conn, baseclass=self)
Benjamin Petersonbe17a112008-09-27 21:49:47 +0000126
127 def cmd_echo(self, arg):
128 # sends back the received string (used by the test suite)
129 self.push(arg)
130
131 def cmd_user(self, arg):
132 self.push('331 username ok')
133
134 def cmd_pass(self, arg):
135 self.push('230 password ok')
136
137 def cmd_acct(self, arg):
138 self.push('230 acct ok')
139
140 def cmd_rnfr(self, arg):
141 self.push('350 rnfr ok')
142
143 def cmd_rnto(self, arg):
144 self.push('250 rnto ok')
145
146 def cmd_dele(self, arg):
147 self.push('250 dele ok')
148
149 def cmd_cwd(self, arg):
150 self.push('250 cwd ok')
151
152 def cmd_size(self, arg):
153 self.push('250 1000')
154
155 def cmd_mkd(self, arg):
156 self.push('257 "%s"' %arg)
157
158 def cmd_rmd(self, arg):
159 self.push('250 rmd ok')
160
161 def cmd_pwd(self, arg):
162 self.push('257 "pwd ok"')
163
164 def cmd_type(self, arg):
165 self.push('200 type ok')
166
167 def cmd_quit(self, arg):
168 self.push('221 quit ok')
169 self.close()
170
171 def cmd_stor(self, arg):
172 self.push('125 stor ok')
173
Antoine Pitrou648bcd72009-11-27 13:23:26 +0000174 def cmd_rest(self, arg):
175 self.rest = arg
176 self.push('350 rest ok')
177
Benjamin Petersonbe17a112008-09-27 21:49:47 +0000178 def cmd_retr(self, arg):
179 self.push('125 retr ok')
Antoine Pitrou648bcd72009-11-27 13:23:26 +0000180 if self.rest is not None:
181 offset = int(self.rest)
182 else:
183 offset = 0
184 self.dtp.push(RETR_DATA[offset:])
Benjamin Petersonbe17a112008-09-27 21:49:47 +0000185 self.dtp.close_when_done()
Antoine Pitrou648bcd72009-11-27 13:23:26 +0000186 self.rest = None
Benjamin Petersonbe17a112008-09-27 21:49:47 +0000187
188 def cmd_list(self, arg):
189 self.push('125 list ok')
190 self.dtp.push(LIST_DATA)
191 self.dtp.close_when_done()
192
193 def cmd_nlst(self, arg):
194 self.push('125 nlst ok')
195 self.dtp.push(NLST_DATA)
196 self.dtp.close_when_done()
197
198
199class DummyFTPServer(asyncore.dispatcher, threading.Thread):
200
201 handler = DummyFTPHandler
202
203 def __init__(self, address, af=socket.AF_INET):
204 threading.Thread.__init__(self)
205 asyncore.dispatcher.__init__(self)
206 self.create_socket(af, socket.SOCK_STREAM)
207 self.bind(address)
208 self.listen(5)
209 self.active = False
210 self.active_lock = threading.Lock()
211 self.host, self.port = self.socket.getsockname()[:2]
212
213 def start(self):
214 assert not self.active
215 self.__flag = threading.Event()
216 threading.Thread.start(self)
217 self.__flag.wait()
218
219 def run(self):
220 self.active = True
221 self.__flag.set()
222 while self.active and asyncore.socket_map:
223 self.active_lock.acquire()
224 asyncore.loop(timeout=0.1, count=1)
225 self.active_lock.release()
226 asyncore.close_all(ignore_all=True)
227
228 def stop(self):
229 assert self.active
230 self.active = False
231 self.join()
232
233 def handle_accept(self):
234 conn, addr = self.accept()
235 self.handler = self.handler(conn)
Benjamin Petersond06e3b02008-09-28 21:00:42 +0000236 self.close()
237
238 def handle_connect(self):
239 self.close()
240 handle_read = handle_connect
Benjamin Petersonbe17a112008-09-27 21:49:47 +0000241
242 def writable(self):
243 return 0
244
245 def handle_error(self):
246 raise
247
248
Antoine Pitrouf988cd02009-11-17 20:21:14 +0000249if ssl is not None:
250
251 CERTFILE = os.path.join(os.path.dirname(__file__), "keycert.pem")
252
253 class SSLConnection(asyncore.dispatcher):
254 """An asyncore.dispatcher subclass supporting TLS/SSL."""
255
256 _ssl_accepting = False
257
258 def secure_connection(self):
259 self.del_channel()
260 socket = ssl.wrap_socket(self.socket, suppress_ragged_eofs=False,
261 certfile=CERTFILE, server_side=True,
262 do_handshake_on_connect=False,
263 ssl_version=ssl.PROTOCOL_SSLv23)
264 self.set_socket(socket)
265 self._ssl_accepting = True
266
267 def _do_ssl_handshake(self):
268 try:
269 self.socket.do_handshake()
270 except ssl.SSLError as err:
271 if err.args[0] in (ssl.SSL_ERROR_WANT_READ,
272 ssl.SSL_ERROR_WANT_WRITE):
273 return
274 elif err.args[0] == ssl.SSL_ERROR_EOF:
275 return self.handle_close()
276 raise
277 except socket.error as err:
278 if err.args[0] == errno.ECONNABORTED:
279 return self.handle_close()
280 else:
281 self._ssl_accepting = False
282
283 def handle_read_event(self):
284 if self._ssl_accepting:
285 self._do_ssl_handshake()
286 else:
287 super(SSLConnection, self).handle_read_event()
288
289 def handle_write_event(self):
290 if self._ssl_accepting:
291 self._do_ssl_handshake()
292 else:
293 super(SSLConnection, self).handle_write_event()
294
295 def send(self, data):
296 try:
297 return super(SSLConnection, self).send(data)
298 except ssl.SSLError as err:
Antoine Pitrou5733c082010-03-22 14:49:10 +0000299 if err.args[0] in (ssl.SSL_ERROR_EOF, ssl.SSL_ERROR_ZERO_RETURN,
300 ssl.SSL_ERROR_WANT_READ,
301 ssl.SSL_ERROR_WANT_WRITE):
Antoine Pitrouf988cd02009-11-17 20:21:14 +0000302 return 0
303 raise
304
305 def recv(self, buffer_size):
306 try:
307 return super(SSLConnection, self).recv(buffer_size)
308 except ssl.SSLError as err:
Antoine Pitrou5733c082010-03-22 14:49:10 +0000309 if err.args[0] in (ssl.SSL_ERROR_WANT_READ,
310 ssl.SSL_ERROR_WANT_WRITE):
311 return ''
Antoine Pitrouf988cd02009-11-17 20:21:14 +0000312 if err.args[0] in (ssl.SSL_ERROR_EOF, ssl.SSL_ERROR_ZERO_RETURN):
313 self.handle_close()
314 return b''
315 raise
316
317 def handle_error(self):
318 raise
319
320 def close(self):
321 try:
322 if isinstance(self.socket, ssl.SSLSocket):
323 if self.socket._sslobj is not None:
324 self.socket.unwrap()
325 finally:
326 super(SSLConnection, self).close()
327
328
329 class DummyTLS_DTPHandler(SSLConnection, DummyDTPHandler):
330 """A DummyDTPHandler subclass supporting TLS/SSL."""
331
332 def __init__(self, conn, baseclass):
333 DummyDTPHandler.__init__(self, conn, baseclass)
334 if self.baseclass.secure_data_channel:
335 self.secure_connection()
336
337
338 class DummyTLS_FTPHandler(SSLConnection, DummyFTPHandler):
339 """A DummyFTPHandler subclass supporting TLS/SSL."""
340
341 dtp_handler = DummyTLS_DTPHandler
342
343 def __init__(self, conn):
344 DummyFTPHandler.__init__(self, conn)
345 self.secure_data_channel = False
346
347 def cmd_auth(self, line):
348 """Set up secure control channel."""
349 self.push('234 AUTH TLS successful')
350 self.secure_connection()
351
352 def cmd_pbsz(self, line):
353 """Negotiate size of buffer for secure data transfer.
354 For TLS/SSL the only valid value for the parameter is '0'.
355 Any other value is accepted but ignored.
356 """
357 self.push('200 PBSZ=0 successful.')
358
359 def cmd_prot(self, line):
360 """Setup un/secure data channel."""
361 arg = line.upper()
362 if arg == 'C':
363 self.push('200 Protection set to Clear')
364 self.secure_data_channel = False
365 elif arg == 'P':
366 self.push('200 Protection set to Private')
367 self.secure_data_channel = True
368 else:
369 self.push("502 Unrecognized PROT type (use C or P).")
370
371
372 class DummyTLS_FTPServer(DummyFTPServer):
373 handler = DummyTLS_FTPHandler
374
375
Benjamin Petersonbe17a112008-09-27 21:49:47 +0000376class TestFTPClass(TestCase):
377
378 def setUp(self):
379 self.server = DummyFTPServer((HOST, 0))
380 self.server.start()
381 self.client = ftplib.FTP(timeout=2)
382 self.client.connect(self.server.host, self.server.port)
383
384 def tearDown(self):
385 self.client.close()
386 self.server.stop()
387
388 def test_getwelcome(self):
389 self.assertEqual(self.client.getwelcome(), '220 welcome')
390
391 def test_sanitize(self):
392 self.assertEqual(self.client.sanitize('foo'), repr('foo'))
393 self.assertEqual(self.client.sanitize('pass 12345'), repr('pass *****'))
394 self.assertEqual(self.client.sanitize('PASS 12345'), repr('PASS *****'))
395
396 def test_exceptions(self):
397 self.assertRaises(ftplib.error_temp, self.client.sendcmd, 'echo 400')
398 self.assertRaises(ftplib.error_temp, self.client.sendcmd, 'echo 499')
399 self.assertRaises(ftplib.error_perm, self.client.sendcmd, 'echo 500')
400 self.assertRaises(ftplib.error_perm, self.client.sendcmd, 'echo 599')
401 self.assertRaises(ftplib.error_proto, self.client.sendcmd, 'echo 999')
402
403 def test_all_errors(self):
404 exceptions = (ftplib.error_reply, ftplib.error_temp, ftplib.error_perm,
405 ftplib.error_proto, ftplib.Error, IOError, EOFError)
406 for x in exceptions:
407 try:
408 raise x('exception not included in all_errors set')
409 except ftplib.all_errors:
410 pass
411
412 def test_set_pasv(self):
413 # passive mode is supposed to be enabled by default
414 self.assertTrue(self.client.passiveserver)
415 self.client.set_pasv(True)
416 self.assertTrue(self.client.passiveserver)
417 self.client.set_pasv(False)
418 self.assertFalse(self.client.passiveserver)
419
420 def test_voidcmd(self):
421 self.client.voidcmd('echo 200')
422 self.client.voidcmd('echo 299')
423 self.assertRaises(ftplib.error_reply, self.client.voidcmd, 'echo 199')
424 self.assertRaises(ftplib.error_reply, self.client.voidcmd, 'echo 300')
425
426 def test_login(self):
427 self.client.login()
428
429 def test_acct(self):
430 self.client.acct('passwd')
431
432 def test_rename(self):
433 self.client.rename('a', 'b')
434 self.server.handler.next_response = '200'
435 self.assertRaises(ftplib.error_reply, self.client.rename, 'a', 'b')
436
437 def test_delete(self):
438 self.client.delete('foo')
439 self.server.handler.next_response = '199'
440 self.assertRaises(ftplib.error_reply, self.client.delete, 'foo')
441
442 def test_size(self):
443 self.client.size('foo')
444
445 def test_mkd(self):
446 dir = self.client.mkd('/foo')
447 self.assertEqual(dir, '/foo')
448
449 def test_rmd(self):
450 self.client.rmd('foo')
451
452 def test_pwd(self):
453 dir = self.client.pwd()
454 self.assertEqual(dir, 'pwd ok')
455
456 def test_quit(self):
457 self.assertEqual(self.client.quit(), '221 quit ok')
458 # Ensure the connection gets closed; sock attribute should be None
459 self.assertEqual(self.client.sock, None)
460
461 def test_retrbinary(self):
462 def callback(data):
463 received.append(data.decode('ascii'))
464 received = []
465 self.client.retrbinary('retr', callback)
466 self.assertEqual(''.join(received), RETR_DATA)
467
Antoine Pitrou648bcd72009-11-27 13:23:26 +0000468 def test_retrbinary_rest(self):
469 def callback(data):
470 received.append(data.decode('ascii'))
471 for rest in (0, 10, 20):
472 received = []
473 self.client.retrbinary('retr', callback, rest=rest)
474 self.assertEqual(''.join(received), RETR_DATA[rest:],
475 msg='rest test case %d %d %d' % (rest,
476 len(''.join(received)),
477 len(RETR_DATA[rest:])))
478
Benjamin Petersonbe17a112008-09-27 21:49:47 +0000479 def test_retrlines(self):
480 received = []
481 self.client.retrlines('retr', received.append)
482 self.assertEqual(''.join(received), RETR_DATA.replace('\r\n', ''))
483
484 def test_storbinary(self):
485 f = io.BytesIO(RETR_DATA.encode('ascii'))
486 self.client.storbinary('stor', f)
487 self.assertEqual(self.server.handler.last_received_data, RETR_DATA)
488 # test new callback arg
489 flag = []
490 f.seek(0)
491 self.client.storbinary('stor', f, callback=lambda x: flag.append(None))
492 self.assertTrue(flag)
493
Antoine Pitrou648bcd72009-11-27 13:23:26 +0000494 def test_storbinary_rest(self):
495 f = io.BytesIO(RETR_DATA.replace('\r\n', '\n').encode('ascii'))
496 for r in (30, '30'):
497 f.seek(0)
498 self.client.storbinary('stor', f, rest=r)
499 self.assertEqual(self.server.handler.rest, str(r))
500
Benjamin Petersonbe17a112008-09-27 21:49:47 +0000501 def test_storlines(self):
502 f = io.BytesIO(RETR_DATA.replace('\r\n', '\n').encode('ascii'))
503 self.client.storlines('stor', f)
504 self.assertEqual(self.server.handler.last_received_data, RETR_DATA)
505 # test new callback arg
506 flag = []
507 f.seek(0)
508 self.client.storlines('stor foo', f, callback=lambda x: flag.append(None))
509 self.assertTrue(flag)
510
511 def test_nlst(self):
512 self.client.nlst()
513 self.assertEqual(self.client.nlst(), NLST_DATA.split('\r\n')[:-1])
514
515 def test_dir(self):
516 l = []
517 self.client.dir(lambda x: l.append(x))
518 self.assertEqual(''.join(l), LIST_DATA.replace('\r\n', ''))
519
Benjamin Peterson3a53fbb2008-09-27 22:04:16 +0000520 def test_makeport(self):
Benjamin Petersonbe17a112008-09-27 21:49:47 +0000521 self.client.makeport()
522 # IPv4 is in use, just make sure send_eprt has not been used
523 self.assertEqual(self.server.handler.last_received_cmd, 'port')
524
525 def test_makepasv(self):
526 host, port = self.client.makepasv()
527 conn = socket.create_connection((host, port), 2)
Guido van Rossumd8faa362007-04-27 19:54:29 +0000528 conn.close()
Benjamin Petersonbe17a112008-09-27 21:49:47 +0000529 # IPv4 is in use, just make sure send_epsv has not been used
530 self.assertEqual(self.server.handler.last_received_cmd, 'pasv')
Guido van Rossumd8faa362007-04-27 19:54:29 +0000531
Benjamin Petersonbe17a112008-09-27 21:49:47 +0000532
533class TestIPv6Environment(TestCase):
534
535 def setUp(self):
536 self.server = DummyFTPServer((HOST, 0), af=socket.AF_INET6)
537 self.server.start()
538 self.client = ftplib.FTP()
539 self.client.connect(self.server.host, self.server.port)
540
541 def tearDown(self):
542 self.client.close()
543 self.server.stop()
544
545 def test_af(self):
546 self.assertEqual(self.client.af, socket.AF_INET6)
547
548 def test_makeport(self):
549 self.client.makeport()
550 self.assertEqual(self.server.handler.last_received_cmd, 'eprt')
551
552 def test_makepasv(self):
553 host, port = self.client.makepasv()
554 conn = socket.create_connection((host, port), 2)
555 conn.close()
556 self.assertEqual(self.server.handler.last_received_cmd, 'epsv')
557
558 def test_transfer(self):
559 def retr():
560 def callback(data):
561 received.append(data.decode('ascii'))
562 received = []
563 self.client.retrbinary('retr', callback)
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 Pitrouf988cd02009-11-17 20:21:14 +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 Melottie9615932010-01-24 19:26:24 +0000600 self.assertNotIsInstance(self.client.sock, ssl.SSLSocket)
Antoine Pitrouf988cd02009-11-17 20:21:14 +0000601 self.client.auth()
Ezio Melottie9615932010-01-24 19:26:24 +0000602 self.assertIsInstance(self.client.sock, ssl.SSLSocket)
Antoine Pitrouf988cd02009-11-17 20:21:14 +0000603
604 def test_data_connection(self):
605 # clear text
606 sock = self.client.transfercmd('list')
Ezio Melottie9615932010-01-24 19:26:24 +0000607 self.assertNotIsInstance(sock, ssl.SSLSocket)
Antoine Pitrouf988cd02009-11-17 20:21:14 +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 Melottie9615932010-01-24 19:26:24 +0000614 self.assertIsInstance(sock, ssl.SSLSocket)
Antoine Pitrouf988cd02009-11-17 20:21:14 +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 Melottie9615932010-01-24 19:26:24 +0000621 self.assertNotIsInstance(sock, ssl.SSLSocket)
Antoine Pitrouf988cd02009-11-17 20:21:14 +0000622 sock.close()
623 self.client.voidresp()
624
625 def test_login(self):
626 # login() is supposed to implicitly secure the control connection
Ezio Melottie9615932010-01-24 19:26:24 +0000627 self.assertNotIsInstance(self.client.sock, ssl.SSLSocket)
Antoine Pitrouf988cd02009-11-17 20:21:14 +0000628 self.client.login()
Ezio Melottie9615932010-01-24 19:26:24 +0000629 self.assertIsInstance(self.client.sock, ssl.SSLSocket)
Antoine Pitrouf988cd02009-11-17 20:21:14 +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 Petersonbe17a112008-09-27 21:49:47 +0000646class TestTimeouts(TestCase):
Guido van Rossumd8faa362007-04-27 19:54:29 +0000647
648 def setUp(self):
Guido van Rossumd8faa362007-04-27 19:54:29 +0000649 self.evt = threading.Event()
Christian Heimes5e696852008-04-09 08:37:03 +0000650 self.sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
651 self.sock.settimeout(3)
Benjamin Petersonee8712c2008-05-20 21:35:26 +0000652 self.port = support.bind_port(self.sock)
Benjamin Petersonbe17a112008-09-27 21:49:47 +0000653 threading.Thread(target=self.server, args=(self.evt,self.sock)).start()
Christian Heimes836baa52008-02-26 08:18:30 +0000654 # Wait for the server to be ready.
655 self.evt.wait()
656 self.evt.clear()
Christian Heimes5e696852008-04-09 08:37:03 +0000657 ftplib.FTP.port = self.port
Guido van Rossumd8faa362007-04-27 19:54:29 +0000658
659 def tearDown(self):
660 self.evt.wait()
661
Benjamin Petersonbe17a112008-09-27 21:49:47 +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(b"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()
Guido van Rossumd8faa362007-04-27 19:54:29 +0000683
684 def testTimeoutDefault(self):
Georg Brandlf78e02b2008-06-10 17:40:04 +0000685 # default -- use global socket timeout
Benjamin Petersonc9c0f202009-06-30 23:06:06 +0000686 self.assertTrue(socket.getdefaulttimeout() is None)
Georg Brandlf78e02b2008-06-10 17:40:04 +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 Petersonc9c0f202009-06-30 23:06:06 +0000698 self.assertTrue(socket.getdefaulttimeout() is None)
Georg Brandlf78e02b2008-06-10 17:40:04 +0000699 socket.setdefaulttimeout(30)
700 try:
701 ftp = ftplib.FTP("localhost", timeout=None)
702 finally:
703 socket.setdefaulttimeout(None)
Guido van Rossumd8faa362007-04-27 19:54:29 +0000704 self.assertTrue(ftp.sock.gettimeout() is None)
Christian Heimes836baa52008-02-26 08:18:30 +0000705 self.evt.wait()
Georg Brandlf78e02b2008-06-10 17:40:04 +0000706 ftp.close()
Guido van Rossumd8faa362007-04-27 19:54:29 +0000707
708 def testTimeoutValue(self):
709 # a value
Christian Heimes5e696852008-04-09 08:37:03 +0000710 ftp = ftplib.FTP(HOST, timeout=30)
Guido van Rossumd8faa362007-04-27 19:54:29 +0000711 self.assertEqual(ftp.sock.gettimeout(), 30)
Christian Heimes836baa52008-02-26 08:18:30 +0000712 self.evt.wait()
Georg Brandlf78e02b2008-06-10 17:40:04 +0000713 ftp.close()
Guido van Rossumd8faa362007-04-27 19:54:29 +0000714
715 def testTimeoutConnect(self):
716 ftp = ftplib.FTP()
Christian Heimes5e696852008-04-09 08:37:03 +0000717 ftp.connect(HOST, timeout=30)
Guido van Rossumd8faa362007-04-27 19:54:29 +0000718 self.assertEqual(ftp.sock.gettimeout(), 30)
Christian Heimes836baa52008-02-26 08:18:30 +0000719 self.evt.wait()
Georg Brandlf78e02b2008-06-10 17:40:04 +0000720 ftp.close()
Guido van Rossumd8faa362007-04-27 19:54:29 +0000721
722 def testTimeoutDifferentOrder(self):
723 ftp = ftplib.FTP(timeout=30)
Christian Heimes5e696852008-04-09 08:37:03 +0000724 ftp.connect(HOST)
Guido van Rossumd8faa362007-04-27 19:54:29 +0000725 self.assertEqual(ftp.sock.gettimeout(), 30)
Christian Heimes836baa52008-02-26 08:18:30 +0000726 self.evt.wait()
Georg Brandlf78e02b2008-06-10 17:40:04 +0000727 ftp.close()
Guido van Rossumd8faa362007-04-27 19:54:29 +0000728
729 def testTimeoutDirectAccess(self):
730 ftp = ftplib.FTP()
731 ftp.timeout = 30
Christian Heimes5e696852008-04-09 08:37:03 +0000732 ftp.connect(HOST)
Guido van Rossumd8faa362007-04-27 19:54:29 +0000733 self.assertEqual(ftp.sock.gettimeout(), 30)
Christian Heimes836baa52008-02-26 08:18:30 +0000734 self.evt.wait()
Guido van Rossumd8faa362007-04-27 19:54:29 +0000735 ftp.close()
736
737
Benjamin Petersonbe17a112008-09-27 21:49:47 +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 Pitrouf988cd02009-11-17 20:21:14 +0000747
748 if ssl is not None:
749 tests.extend([TestTLS_FTPClassMixin, TestTLS_FTPClass])
750
Benjamin Petersonbe17a112008-09-27 21:49:47 +0000751 thread_info = support.threading_setup()
752 try:
753 support.run_unittest(*tests)
754 finally:
755 support.threading_cleanup(*thread_info)
756
Guido van Rossumd8faa362007-04-27 19:54:29 +0000757
758if __name__ == '__main__':
759 test_main()