blob: 182d5a7c74326e2abf62219af065902d39bf7aa9 [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):
318 try:
319 if isinstance(self.socket, ssl.SSLSocket):
320 if self.socket._sslobj is not None:
Antoine Pitrou2c6799a2010-03-24 22:12:15 +0000321 self.socket.unwrap()
Antoine Pitrouccd5e022009-11-15 17:22:09 +0000322 finally:
Antoine Pitrou2c6799a2010-03-24 22:12:15 +0000323 super(SSLConnection, self).close()
Antoine Pitrouccd5e022009-11-15 17:22:09 +0000324
325
326 class DummyTLS_DTPHandler(SSLConnection, DummyDTPHandler):
327 """A DummyDTPHandler subclass supporting TLS/SSL."""
328
329 def __init__(self, conn, baseclass):
330 DummyDTPHandler.__init__(self, conn, baseclass)
331 if self.baseclass.secure_data_channel:
332 self.secure_connection()
333
334
335 class DummyTLS_FTPHandler(SSLConnection, DummyFTPHandler):
336 """A DummyFTPHandler subclass supporting TLS/SSL."""
337
338 dtp_handler = DummyTLS_DTPHandler
339
340 def __init__(self, conn):
341 DummyFTPHandler.__init__(self, conn)
342 self.secure_data_channel = False
343
344 def cmd_auth(self, line):
345 """Set up secure control channel."""
346 self.push('234 AUTH TLS successful')
347 self.secure_connection()
348
349 def cmd_pbsz(self, line):
350 """Negotiate size of buffer for secure data transfer.
351 For TLS/SSL the only valid value for the parameter is '0'.
352 Any other value is accepted but ignored.
353 """
354 self.push('200 PBSZ=0 successful.')
355
356 def cmd_prot(self, line):
357 """Setup un/secure data channel."""
358 arg = line.upper()
359 if arg == 'C':
360 self.push('200 Protection set to Clear')
361 self.secure_data_channel = False
362 elif arg == 'P':
363 self.push('200 Protection set to Private')
364 self.secure_data_channel = True
365 else:
366 self.push("502 Unrecognized PROT type (use C or P).")
367
368
369 class DummyTLS_FTPServer(DummyFTPServer):
370 handler = DummyTLS_FTPHandler
371
372
Benjamin Peterson3c0c4832008-09-27 02:49:54 +0000373class TestFTPClass(TestCase):
374
375 def setUp(self):
376 self.server = DummyFTPServer((HOST, 0))
377 self.server.start()
378 self.client = ftplib.FTP(timeout=2)
379 self.client.connect(self.server.host, self.server.port)
380
381 def tearDown(self):
382 self.client.close()
383 self.server.stop()
384
385 def test_getwelcome(self):
386 self.assertEqual(self.client.getwelcome(), '220 welcome')
387
388 def test_sanitize(self):
389 self.assertEqual(self.client.sanitize('foo'), repr('foo'))
390 self.assertEqual(self.client.sanitize('pass 12345'), repr('pass *****'))
391 self.assertEqual(self.client.sanitize('PASS 12345'), repr('PASS *****'))
392
393 def test_exceptions(self):
394 self.assertRaises(ftplib.error_temp, self.client.sendcmd, 'echo 400')
395 self.assertRaises(ftplib.error_temp, self.client.sendcmd, 'echo 499')
396 self.assertRaises(ftplib.error_perm, self.client.sendcmd, 'echo 500')
397 self.assertRaises(ftplib.error_perm, self.client.sendcmd, 'echo 599')
398 self.assertRaises(ftplib.error_proto, self.client.sendcmd, 'echo 999')
399
400 def test_all_errors(self):
401 exceptions = (ftplib.error_reply, ftplib.error_temp, ftplib.error_perm,
402 ftplib.error_proto, ftplib.Error, IOError, EOFError)
403 for x in exceptions:
404 try:
405 raise x('exception not included in all_errors set')
406 except ftplib.all_errors:
407 pass
408
409 def test_set_pasv(self):
410 # passive mode is supposed to be enabled by default
411 self.assertTrue(self.client.passiveserver)
412 self.client.set_pasv(True)
413 self.assertTrue(self.client.passiveserver)
414 self.client.set_pasv(False)
415 self.assertFalse(self.client.passiveserver)
416
417 def test_voidcmd(self):
418 self.client.voidcmd('echo 200')
419 self.client.voidcmd('echo 299')
420 self.assertRaises(ftplib.error_reply, self.client.voidcmd, 'echo 199')
421 self.assertRaises(ftplib.error_reply, self.client.voidcmd, 'echo 300')
422
423 def test_login(self):
424 self.client.login()
425
426 def test_acct(self):
427 self.client.acct('passwd')
428
429 def test_rename(self):
430 self.client.rename('a', 'b')
431 self.server.handler.next_response = '200'
432 self.assertRaises(ftplib.error_reply, self.client.rename, 'a', 'b')
433
434 def test_delete(self):
435 self.client.delete('foo')
436 self.server.handler.next_response = '199'
437 self.assertRaises(ftplib.error_reply, self.client.delete, 'foo')
438
439 def test_size(self):
440 self.client.size('foo')
441
442 def test_mkd(self):
443 dir = self.client.mkd('/foo')
444 self.assertEqual(dir, '/foo')
445
446 def test_rmd(self):
447 self.client.rmd('foo')
448
449 def test_pwd(self):
450 dir = self.client.pwd()
451 self.assertEqual(dir, 'pwd ok')
452
453 def test_quit(self):
454 self.assertEqual(self.client.quit(), '221 quit ok')
455 # Ensure the connection gets closed; sock attribute should be None
456 self.assertEqual(self.client.sock, None)
457
458 def test_retrbinary(self):
459 received = []
460 self.client.retrbinary('retr', received.append)
461 self.assertEqual(''.join(received), RETR_DATA)
462
Antoine Pitrouacbe3bd2009-11-27 13:18:34 +0000463 def test_retrbinary_rest(self):
464 for rest in (0, 10, 20):
465 received = []
466 self.client.retrbinary('retr', received.append, rest=rest)
467 self.assertEqual(''.join(received), RETR_DATA[rest:],
468 msg='rest test case %d %d %d' % (rest,
469 len(''.join(received)),
470 len(RETR_DATA[rest:])))
471
Benjamin Peterson3c0c4832008-09-27 02:49:54 +0000472 def test_retrlines(self):
473 received = []
474 self.client.retrlines('retr', received.append)
475 self.assertEqual(''.join(received), RETR_DATA.replace('\r\n', ''))
476
477 def test_storbinary(self):
478 f = StringIO.StringIO(RETR_DATA)
479 self.client.storbinary('stor', f)
480 self.assertEqual(self.server.handler.last_received_data, RETR_DATA)
481 # test new callback arg
482 flag = []
483 f.seek(0)
484 self.client.storbinary('stor', f, callback=lambda x: flag.append(None))
485 self.assertTrue(flag)
486
Antoine Pitrouacbe3bd2009-11-27 13:18:34 +0000487 def test_storbinary_rest(self):
488 f = StringIO.StringIO(RETR_DATA)
489 for r in (30, '30'):
490 f.seek(0)
491 self.client.storbinary('stor', f, rest=r)
492 self.assertEqual(self.server.handler.rest, str(r))
493
Benjamin Peterson3c0c4832008-09-27 02:49:54 +0000494 def test_storlines(self):
495 f = StringIO.StringIO(RETR_DATA.replace('\r\n', '\n'))
496 self.client.storlines('stor', f)
497 self.assertEqual(self.server.handler.last_received_data, RETR_DATA)
498 # test new callback arg
499 flag = []
500 f.seek(0)
501 self.client.storlines('stor foo', f, callback=lambda x: flag.append(None))
502 self.assertTrue(flag)
503
504 def test_nlst(self):
505 self.client.nlst()
506 self.assertEqual(self.client.nlst(), NLST_DATA.split('\r\n')[:-1])
507
508 def test_dir(self):
509 l = []
510 self.client.dir(lambda x: l.append(x))
511 self.assertEqual(''.join(l), LIST_DATA.replace('\r\n', ''))
512
513 def test_makeport(self):
514 self.client.makeport()
515 # IPv4 is in use, just make sure send_eprt has not been used
516 self.assertEqual(self.server.handler.last_received_cmd, 'port')
517
518 def test_makepasv(self):
519 host, port = self.client.makepasv()
520 conn = socket.create_connection((host, port), 2)
Facundo Batista93c33682007-03-30 13:00:35 +0000521 conn.close()
Benjamin Peterson3c0c4832008-09-27 02:49:54 +0000522 # IPv4 is in use, just make sure send_epsv has not been used
523 self.assertEqual(self.server.handler.last_received_cmd, 'pasv')
Facundo Batista3f100992007-03-26 20:56:09 +0000524
Benjamin Peterson3c0c4832008-09-27 02:49:54 +0000525
526class TestIPv6Environment(TestCase):
527
528 def setUp(self):
529 self.server = DummyFTPServer((HOST, 0), af=socket.AF_INET6)
530 self.server.start()
531 self.client = ftplib.FTP()
532 self.client.connect(self.server.host, self.server.port)
533
534 def tearDown(self):
535 self.client.close()
536 self.server.stop()
537
538 def test_af(self):
539 self.assertEqual(self.client.af, socket.AF_INET6)
540
541 def test_makeport(self):
542 self.client.makeport()
543 self.assertEqual(self.server.handler.last_received_cmd, 'eprt')
544
545 def test_makepasv(self):
546 host, port = self.client.makepasv()
547 conn = socket.create_connection((host, port), 2)
548 conn.close()
549 self.assertEqual(self.server.handler.last_received_cmd, 'epsv')
550
551 def test_transfer(self):
552 def retr():
553 received = []
554 self.client.retrbinary('retr', received.append)
555 self.assertEqual(''.join(received), RETR_DATA)
556 self.client.set_pasv(True)
557 retr()
558 self.client.set_pasv(False)
559 retr()
560
561
Antoine Pitrouccd5e022009-11-15 17:22:09 +0000562class TestTLS_FTPClassMixin(TestFTPClass):
563 """Repeat TestFTPClass tests starting the TLS layer for both control
564 and data connections first.
565 """
566
567 def setUp(self):
568 self.server = DummyTLS_FTPServer((HOST, 0))
569 self.server.start()
570 self.client = ftplib.FTP_TLS(timeout=2)
571 self.client.connect(self.server.host, self.server.port)
572 # enable TLS
573 self.client.auth()
574 self.client.prot_p()
575
576
577class TestTLS_FTPClass(TestCase):
578 """Specific TLS_FTP class tests."""
579
580 def setUp(self):
581 self.server = DummyTLS_FTPServer((HOST, 0))
582 self.server.start()
583 self.client = ftplib.FTP_TLS(timeout=2)
584 self.client.connect(self.server.host, self.server.port)
585
586 def tearDown(self):
587 self.client.close()
588 self.server.stop()
589
590 def test_control_connection(self):
Ezio Melottib0f5adc2010-01-24 16:58:36 +0000591 self.assertNotIsInstance(self.client.sock, ssl.SSLSocket)
Antoine Pitrouccd5e022009-11-15 17:22:09 +0000592 self.client.auth()
Ezio Melottib0f5adc2010-01-24 16:58:36 +0000593 self.assertIsInstance(self.client.sock, ssl.SSLSocket)
Antoine Pitrouccd5e022009-11-15 17:22:09 +0000594
595 def test_data_connection(self):
596 # clear text
597 sock = self.client.transfercmd('list')
Ezio Melottib0f5adc2010-01-24 16:58:36 +0000598 self.assertNotIsInstance(sock, ssl.SSLSocket)
Antoine Pitrouccd5e022009-11-15 17:22:09 +0000599 sock.close()
600 self.client.voidresp()
601
602 # secured, after PROT P
603 self.client.prot_p()
604 sock = self.client.transfercmd('list')
Ezio Melottib0f5adc2010-01-24 16:58:36 +0000605 self.assertIsInstance(sock, ssl.SSLSocket)
Antoine Pitrouccd5e022009-11-15 17:22:09 +0000606 sock.close()
607 self.client.voidresp()
608
609 # PROT C is issued, the connection must be in cleartext again
610 self.client.prot_c()
611 sock = self.client.transfercmd('list')
Ezio Melottib0f5adc2010-01-24 16:58:36 +0000612 self.assertNotIsInstance(sock, ssl.SSLSocket)
Antoine Pitrouccd5e022009-11-15 17:22:09 +0000613 sock.close()
614 self.client.voidresp()
615
616 def test_login(self):
617 # login() is supposed to implicitly secure the control connection
Ezio Melottib0f5adc2010-01-24 16:58:36 +0000618 self.assertNotIsInstance(self.client.sock, ssl.SSLSocket)
Antoine Pitrouccd5e022009-11-15 17:22:09 +0000619 self.client.login()
Ezio Melottib0f5adc2010-01-24 16:58:36 +0000620 self.assertIsInstance(self.client.sock, ssl.SSLSocket)
Antoine Pitrouccd5e022009-11-15 17:22:09 +0000621 # make sure that AUTH TLS doesn't get issued again
622 self.client.login()
623
624 def test_auth_issued_twice(self):
625 self.client.auth()
626 self.assertRaises(ValueError, self.client.auth)
627
628 def test_auth_ssl(self):
629 try:
630 self.client.ssl_version = ssl.PROTOCOL_SSLv3
631 self.client.auth()
632 self.assertRaises(ValueError, self.client.auth)
633 finally:
634 self.client.ssl_version = ssl.PROTOCOL_TLSv1
635
636
Benjamin Peterson3c0c4832008-09-27 02:49:54 +0000637class TestTimeouts(TestCase):
Neal Norwitz0d4c06e2007-04-25 06:30:05 +0000638
Facundo Batista3f100992007-03-26 20:56:09 +0000639 def setUp(self):
Facundo Batista3f100992007-03-26 20:56:09 +0000640 self.evt = threading.Event()
Trent Nelsone41b0062008-04-08 23:47:30 +0000641 self.sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
642 self.sock.settimeout(3)
643 self.port = test_support.bind_port(self.sock)
Benjamin Peterson3c0c4832008-09-27 02:49:54 +0000644 threading.Thread(target=self.server, args=(self.evt,self.sock)).start()
Neal Norwitzb0917c12008-02-26 04:50:37 +0000645 # Wait for the server to be ready.
646 self.evt.wait()
647 self.evt.clear()
Trent Nelsone41b0062008-04-08 23:47:30 +0000648 ftplib.FTP.port = self.port
Facundo Batista3f100992007-03-26 20:56:09 +0000649
650 def tearDown(self):
651 self.evt.wait()
652
Benjamin Peterson3c0c4832008-09-27 02:49:54 +0000653 def server(self, evt, serv):
654 # This method sets the evt 3 times:
655 # 1) when the connection is ready to be accepted.
656 # 2) when it is safe for the caller to close the connection
657 # 3) when we have closed the socket
658 serv.listen(5)
659 # (1) Signal the caller that we are ready to accept the connection.
660 evt.set()
661 try:
662 conn, addr = serv.accept()
663 except socket.timeout:
664 pass
665 else:
666 conn.send("1 Hola mundo\n")
667 # (2) Signal the caller that it is safe to close the socket.
668 evt.set()
669 conn.close()
670 finally:
671 serv.close()
672 # (3) Signal the caller that we are done.
673 evt.set()
Neal Norwitz0d4c06e2007-04-25 06:30:05 +0000674
Facundo Batista3f100992007-03-26 20:56:09 +0000675 def testTimeoutDefault(self):
Facundo Batista4f1b1ed2008-05-29 16:39:26 +0000676 # default -- use global socket timeout
Benjamin Peterson5c8da862009-06-30 22:57:08 +0000677 self.assertTrue(socket.getdefaulttimeout() is None)
Facundo Batista4f1b1ed2008-05-29 16:39:26 +0000678 socket.setdefaulttimeout(30)
679 try:
680 ftp = ftplib.FTP("localhost")
681 finally:
682 socket.setdefaulttimeout(None)
683 self.assertEqual(ftp.sock.gettimeout(), 30)
684 self.evt.wait()
685 ftp.close()
686
687 def testTimeoutNone(self):
688 # no timeout -- do not use global socket timeout
Benjamin Peterson5c8da862009-06-30 22:57:08 +0000689 self.assertTrue(socket.getdefaulttimeout() is None)
Facundo Batista4f1b1ed2008-05-29 16:39:26 +0000690 socket.setdefaulttimeout(30)
691 try:
692 ftp = ftplib.FTP("localhost", timeout=None)
693 finally:
694 socket.setdefaulttimeout(None)
Facundo Batista3f100992007-03-26 20:56:09 +0000695 self.assertTrue(ftp.sock.gettimeout() is None)
Neal Norwitzb0917c12008-02-26 04:50:37 +0000696 self.evt.wait()
Facundo Batista4f1b1ed2008-05-29 16:39:26 +0000697 ftp.close()
Neal Norwitz0d4c06e2007-04-25 06:30:05 +0000698
Facundo Batista3f100992007-03-26 20:56:09 +0000699 def testTimeoutValue(self):
700 # a value
Trent Nelsone41b0062008-04-08 23:47:30 +0000701 ftp = ftplib.FTP(HOST, timeout=30)
Facundo Batista3f100992007-03-26 20:56:09 +0000702 self.assertEqual(ftp.sock.gettimeout(), 30)
Neal Norwitzb0917c12008-02-26 04:50:37 +0000703 self.evt.wait()
Facundo Batista4f1b1ed2008-05-29 16:39:26 +0000704 ftp.close()
Facundo Batista3f100992007-03-26 20:56:09 +0000705
Facundo Batista93c33682007-03-30 13:00:35 +0000706 def testTimeoutConnect(self):
707 ftp = ftplib.FTP()
Trent Nelsone41b0062008-04-08 23:47:30 +0000708 ftp.connect(HOST, timeout=30)
Facundo Batista93c33682007-03-30 13:00:35 +0000709 self.assertEqual(ftp.sock.gettimeout(), 30)
Neal Norwitzb0917c12008-02-26 04:50:37 +0000710 self.evt.wait()
Facundo Batista4f1b1ed2008-05-29 16:39:26 +0000711 ftp.close()
Facundo Batista93c33682007-03-30 13:00:35 +0000712
713 def testTimeoutDifferentOrder(self):
714 ftp = ftplib.FTP(timeout=30)
Trent Nelsone41b0062008-04-08 23:47:30 +0000715 ftp.connect(HOST)
Facundo Batista93c33682007-03-30 13:00:35 +0000716 self.assertEqual(ftp.sock.gettimeout(), 30)
Neal Norwitzb0917c12008-02-26 04:50:37 +0000717 self.evt.wait()
Facundo Batista4f1b1ed2008-05-29 16:39:26 +0000718 ftp.close()
Facundo Batista93c33682007-03-30 13:00:35 +0000719
720 def testTimeoutDirectAccess(self):
721 ftp = ftplib.FTP()
722 ftp.timeout = 30
Trent Nelsone41b0062008-04-08 23:47:30 +0000723 ftp.connect(HOST)
Facundo Batista93c33682007-03-30 13:00:35 +0000724 self.assertEqual(ftp.sock.gettimeout(), 30)
Neal Norwitzb0917c12008-02-26 04:50:37 +0000725 self.evt.wait()
Facundo Batista3f100992007-03-26 20:56:09 +0000726 ftp.close()
727
728
Benjamin Peterson3c0c4832008-09-27 02:49:54 +0000729def test_main():
730 tests = [TestFTPClass, TestTimeouts]
731 if socket.has_ipv6:
732 try:
733 DummyFTPServer((HOST, 0), af=socket.AF_INET6)
734 except socket.error:
735 pass
736 else:
737 tests.append(TestIPv6Environment)
Antoine Pitrouccd5e022009-11-15 17:22:09 +0000738
739 if ssl is not None:
740 tests.extend([TestTLS_FTPClassMixin, TestTLS_FTPClass])
741
Benjamin Peterson3c0c4832008-09-27 02:49:54 +0000742 thread_info = test_support.threading_setup()
743 try:
744 test_support.run_unittest(*tests)
745 finally:
746 test_support.threading_cleanup(*thread_info)
747
Facundo Batista3f100992007-03-26 20:56:09 +0000748
749if __name__ == '__main__':
750 test_main()