blob: 3a2ec5e84b103ce34ee1bb6a231c07780b390f97 [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 asyncore
8import asynchat
9import socket
10import StringIO
Antoine Pitrouccd5e022009-11-15 17:22:09 +000011import errno
12import os
13try:
14 import ssl
15except ImportError:
16 ssl = None
Facundo Batista3f100992007-03-26 20:56:09 +000017
18from unittest import TestCase
19from test import test_support
Benjamin Peterson3c0c4832008-09-27 02:49:54 +000020from test.test_support import HOST
Victor Stinner6a102812010-04-27 23:55:59 +000021threading = test_support.import_module('threading')
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):
Antoine Pitroua5c4b552010-04-22 23:33:02 +000032 dtp_conn_closed = False
Benjamin Peterson3c0c4832008-09-27 02:49:54 +000033
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):
Antoine Pitroua5c4b552010-04-22 23:33:02 +000043 # 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
Benjamin Peterson3c0c4832008-09-27 02:49:54 +000050
51
52class DummyFTPHandler(asynchat.async_chat):
53
Antoine Pitrouccd5e022009-11-15 17:22:09 +000054 dtp_handler = DummyDTPHandler
55
Benjamin Peterson3c0c4832008-09-27 02:49:54 +000056 def __init__(self, conn):
57 asynchat.async_chat.__init__(self, conn)
58 self.set_terminator("\r\n")
59 self.in_buffer = []
60 self.dtp = None
61 self.last_received_cmd = None
62 self.last_received_data = ''
63 self.next_response = ''
Antoine Pitrouacbe3bd2009-11-27 13:18:34 +000064 self.rest = None
Benjamin Peterson3c0c4832008-09-27 02:49:54 +000065 self.push('220 welcome')
66
67 def collect_incoming_data(self, data):
68 self.in_buffer.append(data)
69
70 def found_terminator(self):
71 line = ''.join(self.in_buffer)
72 self.in_buffer = []
73 if self.next_response:
74 self.push(self.next_response)
75 self.next_response = ''
76 cmd = line.split(' ')[0].lower()
77 self.last_received_cmd = cmd
78 space = line.find(' ')
79 if space != -1:
80 arg = line[space + 1:]
81 else:
82 arg = ""
83 if hasattr(self, 'cmd_' + cmd):
84 method = getattr(self, 'cmd_' + cmd)
85 method(arg)
86 else:
87 self.push('550 command "%s" not understood.' %cmd)
88
89 def handle_error(self):
90 raise
91
92 def push(self, data):
93 asynchat.async_chat.push(self, data + '\r\n')
94
95 def cmd_port(self, arg):
96 addr = map(int, arg.split(','))
97 ip = '%d.%d.%d.%d' %tuple(addr[:4])
98 port = (addr[4] * 256) + addr[5]
99 s = socket.create_connection((ip, port), timeout=2)
Antoine Pitrouccd5e022009-11-15 17:22:09 +0000100 self.dtp = self.dtp_handler(s, baseclass=self)
Benjamin Peterson3c0c4832008-09-27 02:49:54 +0000101 self.push('200 active data connection established')
102
103 def cmd_pasv(self, arg):
104 sock = socket.socket()
105 sock.bind((self.socket.getsockname()[0], 0))
106 sock.listen(5)
107 sock.settimeout(2)
108 ip, port = sock.getsockname()[:2]
Ezio Melottidde5b942010-02-03 05:37:26 +0000109 ip = ip.replace('.', ',')
110 p1, p2 = divmod(port, 256)
Benjamin Peterson3c0c4832008-09-27 02:49:54 +0000111 self.push('227 entering passive mode (%s,%d,%d)' %(ip, p1, p2))
112 conn, addr = sock.accept()
Antoine Pitrouccd5e022009-11-15 17:22:09 +0000113 self.dtp = self.dtp_handler(conn, baseclass=self)
Benjamin Peterson3c0c4832008-09-27 02:49:54 +0000114
115 def cmd_eprt(self, arg):
116 af, ip, port = arg.split(arg[0])[1:-1]
117 port = int(port)
118 s = socket.create_connection((ip, port), timeout=2)
Antoine Pitrouccd5e022009-11-15 17:22:09 +0000119 self.dtp = self.dtp_handler(s, baseclass=self)
Benjamin Peterson3c0c4832008-09-27 02:49:54 +0000120 self.push('200 active data connection established')
121
122 def cmd_epsv(self, arg):
123 sock = socket.socket(socket.AF_INET6)
124 sock.bind((self.socket.getsockname()[0], 0))
125 sock.listen(5)
126 sock.settimeout(2)
127 port = sock.getsockname()[1]
128 self.push('229 entering extended passive mode (|||%d|)' %port)
129 conn, addr = sock.accept()
Antoine Pitrouccd5e022009-11-15 17:22:09 +0000130 self.dtp = self.dtp_handler(conn, baseclass=self)
Benjamin Peterson3c0c4832008-09-27 02:49:54 +0000131
132 def cmd_echo(self, arg):
133 # sends back the received string (used by the test suite)
134 self.push(arg)
135
136 def cmd_user(self, arg):
137 self.push('331 username ok')
138
139 def cmd_pass(self, arg):
140 self.push('230 password ok')
141
142 def cmd_acct(self, arg):
143 self.push('230 acct ok')
144
145 def cmd_rnfr(self, arg):
146 self.push('350 rnfr ok')
147
148 def cmd_rnto(self, arg):
149 self.push('250 rnto ok')
150
151 def cmd_dele(self, arg):
152 self.push('250 dele ok')
153
154 def cmd_cwd(self, arg):
155 self.push('250 cwd ok')
156
157 def cmd_size(self, arg):
158 self.push('250 1000')
159
160 def cmd_mkd(self, arg):
161 self.push('257 "%s"' %arg)
162
163 def cmd_rmd(self, arg):
164 self.push('250 rmd ok')
165
166 def cmd_pwd(self, arg):
167 self.push('257 "pwd ok"')
168
169 def cmd_type(self, arg):
170 self.push('200 type ok')
171
172 def cmd_quit(self, arg):
173 self.push('221 quit ok')
174 self.close()
175
176 def cmd_stor(self, arg):
177 self.push('125 stor ok')
178
Antoine Pitrouacbe3bd2009-11-27 13:18:34 +0000179 def cmd_rest(self, arg):
180 self.rest = arg
181 self.push('350 rest ok')
182
Benjamin Peterson3c0c4832008-09-27 02:49:54 +0000183 def cmd_retr(self, arg):
184 self.push('125 retr ok')
Antoine Pitrouacbe3bd2009-11-27 13:18:34 +0000185 if self.rest is not None:
186 offset = int(self.rest)
187 else:
188 offset = 0
189 self.dtp.push(RETR_DATA[offset:])
Benjamin Peterson3c0c4832008-09-27 02:49:54 +0000190 self.dtp.close_when_done()
Antoine Pitrouacbe3bd2009-11-27 13:18:34 +0000191 self.rest = None
Benjamin Peterson3c0c4832008-09-27 02:49:54 +0000192
193 def cmd_list(self, arg):
194 self.push('125 list ok')
195 self.dtp.push(LIST_DATA)
196 self.dtp.close_when_done()
197
198 def cmd_nlst(self, arg):
199 self.push('125 nlst ok')
200 self.dtp.push(NLST_DATA)
201 self.dtp.close_when_done()
202
203
204class DummyFTPServer(asyncore.dispatcher, threading.Thread):
205
206 handler = DummyFTPHandler
207
208 def __init__(self, address, af=socket.AF_INET):
209 threading.Thread.__init__(self)
210 asyncore.dispatcher.__init__(self)
211 self.create_socket(af, socket.SOCK_STREAM)
212 self.bind(address)
213 self.listen(5)
214 self.active = False
215 self.active_lock = threading.Lock()
216 self.host, self.port = self.socket.getsockname()[:2]
217
218 def start(self):
219 assert not self.active
220 self.__flag = threading.Event()
221 threading.Thread.start(self)
222 self.__flag.wait()
223
224 def run(self):
225 self.active = True
226 self.__flag.set()
227 while self.active and asyncore.socket_map:
228 self.active_lock.acquire()
229 asyncore.loop(timeout=0.1, count=1)
230 self.active_lock.release()
231 asyncore.close_all(ignore_all=True)
232
233 def stop(self):
234 assert self.active
235 self.active = False
236 self.join()
237
238 def handle_accept(self):
239 conn, addr = self.accept()
240 self.handler = self.handler(conn)
Benjamin Petersone14267b2008-09-28 20:57:21 +0000241 self.close()
242
243 def handle_connect(self):
244 self.close()
245 handle_read = handle_connect
Benjamin Peterson3c0c4832008-09-27 02:49:54 +0000246
247 def writable(self):
248 return 0
249
250 def handle_error(self):
251 raise
252
253
Antoine Pitrouccd5e022009-11-15 17:22:09 +0000254if ssl is not None:
255
256 CERTFILE = os.path.join(os.path.dirname(__file__), "keycert.pem")
257
258 class SSLConnection(object, asyncore.dispatcher):
259 """An asyncore.dispatcher subclass supporting TLS/SSL."""
260
261 _ssl_accepting = False
Antoine Pitroua5c4b552010-04-22 23:33:02 +0000262 _ssl_closing = False
Antoine Pitrouccd5e022009-11-15 17:22:09 +0000263
264 def secure_connection(self):
265 self.socket = ssl.wrap_socket(self.socket, suppress_ragged_eofs=False,
266 certfile=CERTFILE, server_side=True,
267 do_handshake_on_connect=False,
268 ssl_version=ssl.PROTOCOL_SSLv23)
269 self._ssl_accepting = True
270
271 def _do_ssl_handshake(self):
272 try:
273 self.socket.do_handshake()
274 except ssl.SSLError, err:
275 if err.args[0] in (ssl.SSL_ERROR_WANT_READ,
276 ssl.SSL_ERROR_WANT_WRITE):
277 return
278 elif err.args[0] == ssl.SSL_ERROR_EOF:
279 return self.handle_close()
280 raise
281 except socket.error, err:
282 if err.args[0] == errno.ECONNABORTED:
283 return self.handle_close()
284 else:
285 self._ssl_accepting = False
286
Antoine Pitroua5c4b552010-04-22 23:33:02 +0000287 def _do_ssl_shutdown(self):
288 self._ssl_closing = True
289 try:
290 self.socket = self.socket.unwrap()
291 except ssl.SSLError, err:
292 if err.args[0] in (ssl.SSL_ERROR_WANT_READ,
293 ssl.SSL_ERROR_WANT_WRITE):
294 return
295 except socket.error, err:
296 # Any "socket error" corresponds to a SSL_ERROR_SYSCALL return
297 # from OpenSSL's SSL_shutdown(), corresponding to a
298 # closed socket condition. See also:
299 # http://www.mail-archive.com/openssl-users@openssl.org/msg60710.html
300 pass
301 self._ssl_closing = False
302 super(SSLConnection, self).close()
303
Antoine Pitrouccd5e022009-11-15 17:22:09 +0000304 def handle_read_event(self):
305 if self._ssl_accepting:
306 self._do_ssl_handshake()
Antoine Pitroua5c4b552010-04-22 23:33:02 +0000307 elif self._ssl_closing:
308 self._do_ssl_shutdown()
Antoine Pitrouccd5e022009-11-15 17:22:09 +0000309 else:
310 super(SSLConnection, self).handle_read_event()
311
312 def handle_write_event(self):
313 if self._ssl_accepting:
314 self._do_ssl_handshake()
Antoine Pitroua5c4b552010-04-22 23:33:02 +0000315 elif self._ssl_closing:
316 self._do_ssl_shutdown()
Antoine Pitrouccd5e022009-11-15 17:22:09 +0000317 else:
318 super(SSLConnection, self).handle_write_event()
319
320 def send(self, data):
321 try:
322 return super(SSLConnection, self).send(data)
323 except ssl.SSLError, err:
Antoine Pitrou52093b82010-03-22 14:41:48 +0000324 if err.args[0] in (ssl.SSL_ERROR_EOF, ssl.SSL_ERROR_ZERO_RETURN,
325 ssl.SSL_ERROR_WANT_READ,
326 ssl.SSL_ERROR_WANT_WRITE):
Antoine Pitrouccd5e022009-11-15 17:22:09 +0000327 return 0
328 raise
329
330 def recv(self, buffer_size):
331 try:
332 return super(SSLConnection, self).recv(buffer_size)
333 except ssl.SSLError, err:
Antoine Pitrou52093b82010-03-22 14:41:48 +0000334 if err.args[0] in (ssl.SSL_ERROR_WANT_READ,
335 ssl.SSL_ERROR_WANT_WRITE):
336 return ''
Antoine Pitrouccd5e022009-11-15 17:22:09 +0000337 if err.args[0] in (ssl.SSL_ERROR_EOF, ssl.SSL_ERROR_ZERO_RETURN):
338 self.handle_close()
339 return ''
340 raise
341
342 def handle_error(self):
343 raise
344
345 def close(self):
Antoine Pitroua5c4b552010-04-22 23:33:02 +0000346 if (isinstance(self.socket, ssl.SSLSocket) and
347 self.socket._sslobj is not None):
348 self._do_ssl_shutdown()
Antoine Pitrouccd5e022009-11-15 17:22:09 +0000349
350
351 class DummyTLS_DTPHandler(SSLConnection, DummyDTPHandler):
352 """A DummyDTPHandler subclass supporting TLS/SSL."""
353
354 def __init__(self, conn, baseclass):
355 DummyDTPHandler.__init__(self, conn, baseclass)
356 if self.baseclass.secure_data_channel:
357 self.secure_connection()
358
359
360 class DummyTLS_FTPHandler(SSLConnection, DummyFTPHandler):
361 """A DummyFTPHandler subclass supporting TLS/SSL."""
362
363 dtp_handler = DummyTLS_DTPHandler
364
365 def __init__(self, conn):
366 DummyFTPHandler.__init__(self, conn)
367 self.secure_data_channel = False
368
369 def cmd_auth(self, line):
370 """Set up secure control channel."""
371 self.push('234 AUTH TLS successful')
372 self.secure_connection()
373
374 def cmd_pbsz(self, line):
375 """Negotiate size of buffer for secure data transfer.
376 For TLS/SSL the only valid value for the parameter is '0'.
377 Any other value is accepted but ignored.
378 """
379 self.push('200 PBSZ=0 successful.')
380
381 def cmd_prot(self, line):
382 """Setup un/secure data channel."""
383 arg = line.upper()
384 if arg == 'C':
385 self.push('200 Protection set to Clear')
386 self.secure_data_channel = False
387 elif arg == 'P':
388 self.push('200 Protection set to Private')
389 self.secure_data_channel = True
390 else:
391 self.push("502 Unrecognized PROT type (use C or P).")
392
393
394 class DummyTLS_FTPServer(DummyFTPServer):
395 handler = DummyTLS_FTPHandler
396
397
Benjamin Peterson3c0c4832008-09-27 02:49:54 +0000398class TestFTPClass(TestCase):
399
400 def setUp(self):
401 self.server = DummyFTPServer((HOST, 0))
402 self.server.start()
403 self.client = ftplib.FTP(timeout=2)
404 self.client.connect(self.server.host, self.server.port)
405
406 def tearDown(self):
407 self.client.close()
408 self.server.stop()
409
410 def test_getwelcome(self):
411 self.assertEqual(self.client.getwelcome(), '220 welcome')
412
413 def test_sanitize(self):
414 self.assertEqual(self.client.sanitize('foo'), repr('foo'))
415 self.assertEqual(self.client.sanitize('pass 12345'), repr('pass *****'))
416 self.assertEqual(self.client.sanitize('PASS 12345'), repr('PASS *****'))
417
418 def test_exceptions(self):
419 self.assertRaises(ftplib.error_temp, self.client.sendcmd, 'echo 400')
420 self.assertRaises(ftplib.error_temp, self.client.sendcmd, 'echo 499')
421 self.assertRaises(ftplib.error_perm, self.client.sendcmd, 'echo 500')
422 self.assertRaises(ftplib.error_perm, self.client.sendcmd, 'echo 599')
423 self.assertRaises(ftplib.error_proto, self.client.sendcmd, 'echo 999')
424
425 def test_all_errors(self):
426 exceptions = (ftplib.error_reply, ftplib.error_temp, ftplib.error_perm,
427 ftplib.error_proto, ftplib.Error, IOError, EOFError)
428 for x in exceptions:
429 try:
430 raise x('exception not included in all_errors set')
431 except ftplib.all_errors:
432 pass
433
434 def test_set_pasv(self):
435 # passive mode is supposed to be enabled by default
436 self.assertTrue(self.client.passiveserver)
437 self.client.set_pasv(True)
438 self.assertTrue(self.client.passiveserver)
439 self.client.set_pasv(False)
440 self.assertFalse(self.client.passiveserver)
441
442 def test_voidcmd(self):
443 self.client.voidcmd('echo 200')
444 self.client.voidcmd('echo 299')
445 self.assertRaises(ftplib.error_reply, self.client.voidcmd, 'echo 199')
446 self.assertRaises(ftplib.error_reply, self.client.voidcmd, 'echo 300')
447
448 def test_login(self):
449 self.client.login()
450
451 def test_acct(self):
452 self.client.acct('passwd')
453
454 def test_rename(self):
455 self.client.rename('a', 'b')
456 self.server.handler.next_response = '200'
457 self.assertRaises(ftplib.error_reply, self.client.rename, 'a', 'b')
458
459 def test_delete(self):
460 self.client.delete('foo')
461 self.server.handler.next_response = '199'
462 self.assertRaises(ftplib.error_reply, self.client.delete, 'foo')
463
464 def test_size(self):
465 self.client.size('foo')
466
467 def test_mkd(self):
468 dir = self.client.mkd('/foo')
469 self.assertEqual(dir, '/foo')
470
471 def test_rmd(self):
472 self.client.rmd('foo')
473
474 def test_pwd(self):
475 dir = self.client.pwd()
476 self.assertEqual(dir, 'pwd ok')
477
478 def test_quit(self):
479 self.assertEqual(self.client.quit(), '221 quit ok')
480 # Ensure the connection gets closed; sock attribute should be None
481 self.assertEqual(self.client.sock, None)
482
483 def test_retrbinary(self):
484 received = []
485 self.client.retrbinary('retr', received.append)
486 self.assertEqual(''.join(received), RETR_DATA)
487
Antoine Pitrouacbe3bd2009-11-27 13:18:34 +0000488 def test_retrbinary_rest(self):
489 for rest in (0, 10, 20):
490 received = []
491 self.client.retrbinary('retr', received.append, rest=rest)
492 self.assertEqual(''.join(received), RETR_DATA[rest:],
493 msg='rest test case %d %d %d' % (rest,
494 len(''.join(received)),
495 len(RETR_DATA[rest:])))
496
Benjamin Peterson3c0c4832008-09-27 02:49:54 +0000497 def test_retrlines(self):
498 received = []
499 self.client.retrlines('retr', received.append)
500 self.assertEqual(''.join(received), RETR_DATA.replace('\r\n', ''))
501
502 def test_storbinary(self):
503 f = StringIO.StringIO(RETR_DATA)
504 self.client.storbinary('stor', f)
505 self.assertEqual(self.server.handler.last_received_data, RETR_DATA)
506 # test new callback arg
507 flag = []
508 f.seek(0)
509 self.client.storbinary('stor', f, callback=lambda x: flag.append(None))
510 self.assertTrue(flag)
511
Antoine Pitrouacbe3bd2009-11-27 13:18:34 +0000512 def test_storbinary_rest(self):
513 f = StringIO.StringIO(RETR_DATA)
514 for r in (30, '30'):
515 f.seek(0)
516 self.client.storbinary('stor', f, rest=r)
517 self.assertEqual(self.server.handler.rest, str(r))
518
Benjamin Peterson3c0c4832008-09-27 02:49:54 +0000519 def test_storlines(self):
520 f = StringIO.StringIO(RETR_DATA.replace('\r\n', '\n'))
521 self.client.storlines('stor', f)
522 self.assertEqual(self.server.handler.last_received_data, RETR_DATA)
523 # test new callback arg
524 flag = []
525 f.seek(0)
526 self.client.storlines('stor foo', f, callback=lambda x: flag.append(None))
527 self.assertTrue(flag)
528
529 def test_nlst(self):
530 self.client.nlst()
531 self.assertEqual(self.client.nlst(), NLST_DATA.split('\r\n')[:-1])
532
533 def test_dir(self):
534 l = []
535 self.client.dir(lambda x: l.append(x))
536 self.assertEqual(''.join(l), LIST_DATA.replace('\r\n', ''))
537
538 def test_makeport(self):
539 self.client.makeport()
540 # IPv4 is in use, just make sure send_eprt has not been used
541 self.assertEqual(self.server.handler.last_received_cmd, 'port')
542
543 def test_makepasv(self):
544 host, port = self.client.makepasv()
545 conn = socket.create_connection((host, port), 2)
Facundo Batista93c33682007-03-30 13:00:35 +0000546 conn.close()
Benjamin Peterson3c0c4832008-09-27 02:49:54 +0000547 # IPv4 is in use, just make sure send_epsv has not been used
548 self.assertEqual(self.server.handler.last_received_cmd, 'pasv')
Facundo Batista3f100992007-03-26 20:56:09 +0000549
Benjamin Peterson3c0c4832008-09-27 02:49:54 +0000550
551class TestIPv6Environment(TestCase):
552
553 def setUp(self):
554 self.server = DummyFTPServer((HOST, 0), af=socket.AF_INET6)
555 self.server.start()
556 self.client = ftplib.FTP()
557 self.client.connect(self.server.host, self.server.port)
558
559 def tearDown(self):
560 self.client.close()
561 self.server.stop()
562
563 def test_af(self):
564 self.assertEqual(self.client.af, socket.AF_INET6)
565
566 def test_makeport(self):
567 self.client.makeport()
568 self.assertEqual(self.server.handler.last_received_cmd, 'eprt')
569
570 def test_makepasv(self):
571 host, port = self.client.makepasv()
572 conn = socket.create_connection((host, port), 2)
573 conn.close()
574 self.assertEqual(self.server.handler.last_received_cmd, 'epsv')
575
576 def test_transfer(self):
577 def retr():
578 received = []
579 self.client.retrbinary('retr', received.append)
580 self.assertEqual(''.join(received), RETR_DATA)
581 self.client.set_pasv(True)
582 retr()
583 self.client.set_pasv(False)
584 retr()
585
586
Antoine Pitrouccd5e022009-11-15 17:22:09 +0000587class TestTLS_FTPClassMixin(TestFTPClass):
588 """Repeat TestFTPClass tests starting the TLS layer for both control
589 and data connections first.
590 """
591
592 def setUp(self):
593 self.server = DummyTLS_FTPServer((HOST, 0))
594 self.server.start()
595 self.client = ftplib.FTP_TLS(timeout=2)
596 self.client.connect(self.server.host, self.server.port)
597 # enable TLS
598 self.client.auth()
599 self.client.prot_p()
600
601
602class TestTLS_FTPClass(TestCase):
603 """Specific TLS_FTP class tests."""
604
605 def setUp(self):
606 self.server = DummyTLS_FTPServer((HOST, 0))
607 self.server.start()
608 self.client = ftplib.FTP_TLS(timeout=2)
609 self.client.connect(self.server.host, self.server.port)
610
611 def tearDown(self):
612 self.client.close()
613 self.server.stop()
614
615 def test_control_connection(self):
Ezio Melottib0f5adc2010-01-24 16:58:36 +0000616 self.assertNotIsInstance(self.client.sock, ssl.SSLSocket)
Antoine Pitrouccd5e022009-11-15 17:22:09 +0000617 self.client.auth()
Ezio Melottib0f5adc2010-01-24 16:58:36 +0000618 self.assertIsInstance(self.client.sock, ssl.SSLSocket)
Antoine Pitrouccd5e022009-11-15 17:22:09 +0000619
620 def test_data_connection(self):
621 # clear text
622 sock = self.client.transfercmd('list')
Ezio Melottib0f5adc2010-01-24 16:58:36 +0000623 self.assertNotIsInstance(sock, ssl.SSLSocket)
Antoine Pitrouccd5e022009-11-15 17:22:09 +0000624 sock.close()
Antoine Pitroua5c4b552010-04-22 23:33:02 +0000625 self.assertEqual(self.client.voidresp(), "226 transfer complete")
Antoine Pitrouccd5e022009-11-15 17:22:09 +0000626
627 # secured, after PROT P
628 self.client.prot_p()
629 sock = self.client.transfercmd('list')
Ezio Melottib0f5adc2010-01-24 16:58:36 +0000630 self.assertIsInstance(sock, ssl.SSLSocket)
Antoine Pitrouccd5e022009-11-15 17:22:09 +0000631 sock.close()
Antoine Pitroua5c4b552010-04-22 23:33:02 +0000632 self.assertEqual(self.client.voidresp(), "226 transfer complete")
Antoine Pitrouccd5e022009-11-15 17:22:09 +0000633
634 # PROT C is issued, the connection must be in cleartext again
635 self.client.prot_c()
636 sock = self.client.transfercmd('list')
Ezio Melottib0f5adc2010-01-24 16:58:36 +0000637 self.assertNotIsInstance(sock, ssl.SSLSocket)
Antoine Pitrouccd5e022009-11-15 17:22:09 +0000638 sock.close()
Antoine Pitroua5c4b552010-04-22 23:33:02 +0000639 self.assertEqual(self.client.voidresp(), "226 transfer complete")
Antoine Pitrouccd5e022009-11-15 17:22:09 +0000640
641 def test_login(self):
642 # login() is supposed to implicitly secure the control connection
Ezio Melottib0f5adc2010-01-24 16:58:36 +0000643 self.assertNotIsInstance(self.client.sock, ssl.SSLSocket)
Antoine Pitrouccd5e022009-11-15 17:22:09 +0000644 self.client.login()
Ezio Melottib0f5adc2010-01-24 16:58:36 +0000645 self.assertIsInstance(self.client.sock, ssl.SSLSocket)
Antoine Pitrouccd5e022009-11-15 17:22:09 +0000646 # make sure that AUTH TLS doesn't get issued again
647 self.client.login()
648
649 def test_auth_issued_twice(self):
650 self.client.auth()
651 self.assertRaises(ValueError, self.client.auth)
652
653 def test_auth_ssl(self):
654 try:
655 self.client.ssl_version = ssl.PROTOCOL_SSLv3
656 self.client.auth()
657 self.assertRaises(ValueError, self.client.auth)
658 finally:
659 self.client.ssl_version = ssl.PROTOCOL_TLSv1
660
661
Benjamin Peterson3c0c4832008-09-27 02:49:54 +0000662class TestTimeouts(TestCase):
Neal Norwitz0d4c06e2007-04-25 06:30:05 +0000663
Facundo Batista3f100992007-03-26 20:56:09 +0000664 def setUp(self):
Facundo Batista3f100992007-03-26 20:56:09 +0000665 self.evt = threading.Event()
Trent Nelsone41b0062008-04-08 23:47:30 +0000666 self.sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
667 self.sock.settimeout(3)
668 self.port = test_support.bind_port(self.sock)
Benjamin Peterson3c0c4832008-09-27 02:49:54 +0000669 threading.Thread(target=self.server, args=(self.evt,self.sock)).start()
Neal Norwitzb0917c12008-02-26 04:50:37 +0000670 # Wait for the server to be ready.
671 self.evt.wait()
672 self.evt.clear()
Trent Nelsone41b0062008-04-08 23:47:30 +0000673 ftplib.FTP.port = self.port
Facundo Batista3f100992007-03-26 20:56:09 +0000674
675 def tearDown(self):
676 self.evt.wait()
677
Benjamin Peterson3c0c4832008-09-27 02:49:54 +0000678 def server(self, evt, serv):
679 # This method sets the evt 3 times:
680 # 1) when the connection is ready to be accepted.
681 # 2) when it is safe for the caller to close the connection
682 # 3) when we have closed the socket
683 serv.listen(5)
684 # (1) Signal the caller that we are ready to accept the connection.
685 evt.set()
686 try:
687 conn, addr = serv.accept()
688 except socket.timeout:
689 pass
690 else:
691 conn.send("1 Hola mundo\n")
692 # (2) Signal the caller that it is safe to close the socket.
693 evt.set()
694 conn.close()
695 finally:
696 serv.close()
697 # (3) Signal the caller that we are done.
698 evt.set()
Neal Norwitz0d4c06e2007-04-25 06:30:05 +0000699
Facundo Batista3f100992007-03-26 20:56:09 +0000700 def testTimeoutDefault(self):
Facundo Batista4f1b1ed2008-05-29 16:39:26 +0000701 # default -- use global socket timeout
Benjamin Peterson5c8da862009-06-30 22:57:08 +0000702 self.assertTrue(socket.getdefaulttimeout() is None)
Facundo Batista4f1b1ed2008-05-29 16:39:26 +0000703 socket.setdefaulttimeout(30)
704 try:
705 ftp = ftplib.FTP("localhost")
706 finally:
707 socket.setdefaulttimeout(None)
708 self.assertEqual(ftp.sock.gettimeout(), 30)
709 self.evt.wait()
710 ftp.close()
711
712 def testTimeoutNone(self):
713 # no timeout -- do not use global socket timeout
Benjamin Peterson5c8da862009-06-30 22:57:08 +0000714 self.assertTrue(socket.getdefaulttimeout() is None)
Facundo Batista4f1b1ed2008-05-29 16:39:26 +0000715 socket.setdefaulttimeout(30)
716 try:
717 ftp = ftplib.FTP("localhost", timeout=None)
718 finally:
719 socket.setdefaulttimeout(None)
Facundo Batista3f100992007-03-26 20:56:09 +0000720 self.assertTrue(ftp.sock.gettimeout() is None)
Neal Norwitzb0917c12008-02-26 04:50:37 +0000721 self.evt.wait()
Facundo Batista4f1b1ed2008-05-29 16:39:26 +0000722 ftp.close()
Neal Norwitz0d4c06e2007-04-25 06:30:05 +0000723
Facundo Batista3f100992007-03-26 20:56:09 +0000724 def testTimeoutValue(self):
725 # a value
Trent Nelsone41b0062008-04-08 23:47:30 +0000726 ftp = ftplib.FTP(HOST, timeout=30)
Facundo Batista3f100992007-03-26 20:56:09 +0000727 self.assertEqual(ftp.sock.gettimeout(), 30)
Neal Norwitzb0917c12008-02-26 04:50:37 +0000728 self.evt.wait()
Facundo Batista4f1b1ed2008-05-29 16:39:26 +0000729 ftp.close()
Facundo Batista3f100992007-03-26 20:56:09 +0000730
Facundo Batista93c33682007-03-30 13:00:35 +0000731 def testTimeoutConnect(self):
732 ftp = ftplib.FTP()
Trent Nelsone41b0062008-04-08 23:47:30 +0000733 ftp.connect(HOST, timeout=30)
Facundo Batista93c33682007-03-30 13:00:35 +0000734 self.assertEqual(ftp.sock.gettimeout(), 30)
Neal Norwitzb0917c12008-02-26 04:50:37 +0000735 self.evt.wait()
Facundo Batista4f1b1ed2008-05-29 16:39:26 +0000736 ftp.close()
Facundo Batista93c33682007-03-30 13:00:35 +0000737
738 def testTimeoutDifferentOrder(self):
739 ftp = ftplib.FTP(timeout=30)
Trent Nelsone41b0062008-04-08 23:47:30 +0000740 ftp.connect(HOST)
Facundo Batista93c33682007-03-30 13:00:35 +0000741 self.assertEqual(ftp.sock.gettimeout(), 30)
Neal Norwitzb0917c12008-02-26 04:50:37 +0000742 self.evt.wait()
Facundo Batista4f1b1ed2008-05-29 16:39:26 +0000743 ftp.close()
Facundo Batista93c33682007-03-30 13:00:35 +0000744
745 def testTimeoutDirectAccess(self):
746 ftp = ftplib.FTP()
747 ftp.timeout = 30
Trent Nelsone41b0062008-04-08 23:47:30 +0000748 ftp.connect(HOST)
Facundo Batista93c33682007-03-30 13:00:35 +0000749 self.assertEqual(ftp.sock.gettimeout(), 30)
Neal Norwitzb0917c12008-02-26 04:50:37 +0000750 self.evt.wait()
Facundo Batista3f100992007-03-26 20:56:09 +0000751 ftp.close()
752
753
Benjamin Peterson3c0c4832008-09-27 02:49:54 +0000754def test_main():
755 tests = [TestFTPClass, TestTimeouts]
756 if socket.has_ipv6:
757 try:
758 DummyFTPServer((HOST, 0), af=socket.AF_INET6)
759 except socket.error:
760 pass
761 else:
762 tests.append(TestIPv6Environment)
Antoine Pitrouccd5e022009-11-15 17:22:09 +0000763
764 if ssl is not None:
765 tests.extend([TestTLS_FTPClassMixin, TestTLS_FTPClass])
766
Benjamin Peterson3c0c4832008-09-27 02:49:54 +0000767 thread_info = test_support.threading_setup()
768 try:
769 test_support.run_unittest(*tests)
770 finally:
771 test_support.threading_cleanup(*thread_info)
772
Facundo Batista3f100992007-03-26 20:56:09 +0000773
774if __name__ == '__main__':
775 test_main()