blob: e043828e30c83de81d7093fbf87cda10769643a1 [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 asyncore
8import asynchat
9import socket
10import io
Antoine Pitrouf988cd02009-11-17 20:21:14 +000011import errno
12import os
Giampaolo Rodolàbd576b72010-05-10 14:53:29 +000013import time
Antoine Pitrouf988cd02009-11-17 20:21:14 +000014try:
15 import ssl
16except ImportError:
17 ssl = None
Christian Heimese7945d72013-12-15 19:38:22 +010018 HAS_SNI = False
19else:
20 from ssl import HAS_SNI
Guido van Rossumd8faa362007-04-27 19:54:29 +000021
Serhiy Storchaka43767632013-11-03 21:31:38 +020022from unittest import TestCase, skipUnless
Benjamin Petersonee8712c2008-05-20 21:35:26 +000023from test import support
Antoine Pitrouf6fbf562013-08-22 00:39:46 +020024from test.support import HOST, HOSTv6
Victor Stinner45df8202010-04-28 22:31:17 +000025threading = support.import_module('threading')
Guido van Rossumd8faa362007-04-27 19:54:29 +000026
Giampaolo Rodola'0d4f08c2013-05-16 15:12:01 +020027TIMEOUT = 3
Benjamin Petersonbe17a112008-09-27 21:49:47 +000028# the dummy data returned by server over the data channel when
Giampaolo Rodola'd78def92011-05-06 19:49:08 +020029# RETR, LIST, NLST, MLSD commands are issued
Benjamin Petersonbe17a112008-09-27 21:49:47 +000030RETR_DATA = 'abcde12345\r\n' * 1000
31LIST_DATA = 'foo\r\nbar\r\n'
32NLST_DATA = 'foo\r\nbar\r\n'
Giampaolo Rodola'd78def92011-05-06 19:49:08 +020033MLSD_DATA = ("type=cdir;perm=el;unique==keVO1+ZF4; test\r\n"
34 "type=pdir;perm=e;unique==keVO1+d?3; ..\r\n"
35 "type=OS.unix=slink:/foobar;perm=;unique==keVO1+4G4; foobar\r\n"
36 "type=OS.unix=chr-13/29;perm=;unique==keVO1+5G4; device\r\n"
37 "type=OS.unix=blk-11/108;perm=;unique==keVO1+6G4; block\r\n"
38 "type=file;perm=awr;unique==keVO1+8G4; writable\r\n"
39 "type=dir;perm=cpmel;unique==keVO1+7G4; promiscuous\r\n"
40 "type=dir;perm=;unique==keVO1+1t2; no-exec\r\n"
41 "type=file;perm=r;unique==keVO1+EG4; two words\r\n"
42 "type=file;perm=r;unique==keVO1+IH4; leading space\r\n"
43 "type=file;perm=r;unique==keVO1+1G4; file1\r\n"
44 "type=dir;perm=cpmel;unique==keVO1+7G4; incoming\r\n"
45 "type=file;perm=r;unique==keVO1+1G4; file2\r\n"
46 "type=file;perm=r;unique==keVO1+1G4; file3\r\n"
47 "type=file;perm=r;unique==keVO1+1G4; file4\r\n")
Christian Heimes836baa52008-02-26 08:18:30 +000048
Christian Heimes836baa52008-02-26 08:18:30 +000049
Benjamin Petersonbe17a112008-09-27 21:49:47 +000050class DummyDTPHandler(asynchat.async_chat):
Antoine Pitrou2c4f98b2010-04-23 00:16:21 +000051 dtp_conn_closed = False
Benjamin Petersonbe17a112008-09-27 21:49:47 +000052
53 def __init__(self, conn, baseclass):
54 asynchat.async_chat.__init__(self, conn)
55 self.baseclass = baseclass
56 self.baseclass.last_received_data = ''
57
58 def handle_read(self):
Giampaolo Rodolàf96482e2010-08-04 10:36:18 +000059 self.baseclass.last_received_data += self.recv(1024).decode('ascii')
Benjamin Petersonbe17a112008-09-27 21:49:47 +000060
61 def handle_close(self):
Antoine Pitrou2c4f98b2010-04-23 00:16:21 +000062 # XXX: this method can be called many times in a row for a single
63 # connection, including in clear-text (non-TLS) mode.
64 # (behaviour witnessed with test_data_connection)
65 if not self.dtp_conn_closed:
66 self.baseclass.push('226 transfer complete')
67 self.close()
68 self.dtp_conn_closed = True
Benjamin Petersonbe17a112008-09-27 21:49:47 +000069
70 def push(self, what):
Giampaolo Rodola'd78def92011-05-06 19:49:08 +020071 if self.baseclass.next_data is not None:
72 what = self.baseclass.next_data
73 self.baseclass.next_data = None
74 if not what:
75 return self.close_when_done()
Giampaolo Rodolàf96482e2010-08-04 10:36:18 +000076 super(DummyDTPHandler, self).push(what.encode('ascii'))
Benjamin Petersonbe17a112008-09-27 21:49:47 +000077
Giampaolo Rodolàd930b632010-05-06 20:21:57 +000078 def handle_error(self):
79 raise
80
Benjamin Petersonbe17a112008-09-27 21:49:47 +000081
82class DummyFTPHandler(asynchat.async_chat):
83
Antoine Pitrouf988cd02009-11-17 20:21:14 +000084 dtp_handler = DummyDTPHandler
85
Benjamin Petersonbe17a112008-09-27 21:49:47 +000086 def __init__(self, conn):
87 asynchat.async_chat.__init__(self, conn)
Giampaolo Rodola'0b5c21f2011-05-07 19:03:47 +020088 # tells the socket to handle urgent data inline (ABOR command)
89 self.socket.setsockopt(socket.SOL_SOCKET, socket.SO_OOBINLINE, 1)
Benjamin Petersonbe17a112008-09-27 21:49:47 +000090 self.set_terminator(b"\r\n")
91 self.in_buffer = []
92 self.dtp = None
93 self.last_received_cmd = None
94 self.last_received_data = ''
95 self.next_response = ''
Giampaolo Rodola'd78def92011-05-06 19:49:08 +020096 self.next_data = None
Antoine Pitrou648bcd72009-11-27 13:23:26 +000097 self.rest = None
Serhiy Storchakac30b1782013-10-20 16:58:27 +030098 self.next_retr_data = RETR_DATA
Benjamin Petersonbe17a112008-09-27 21:49:47 +000099 self.push('220 welcome')
100
101 def collect_incoming_data(self, data):
102 self.in_buffer.append(data)
103
104 def found_terminator(self):
105 line = b''.join(self.in_buffer).decode('ascii')
106 self.in_buffer = []
107 if self.next_response:
108 self.push(self.next_response)
109 self.next_response = ''
110 cmd = line.split(' ')[0].lower()
111 self.last_received_cmd = cmd
112 space = line.find(' ')
113 if space != -1:
114 arg = line[space + 1:]
115 else:
116 arg = ""
117 if hasattr(self, 'cmd_' + cmd):
118 method = getattr(self, 'cmd_' + cmd)
119 method(arg)
120 else:
121 self.push('550 command "%s" not understood.' %cmd)
122
123 def handle_error(self):
124 raise
125
126 def push(self, data):
127 asynchat.async_chat.push(self, data.encode('ascii') + b'\r\n')
128
129 def cmd_port(self, arg):
130 addr = list(map(int, arg.split(',')))
131 ip = '%d.%d.%d.%d' %tuple(addr[:4])
132 port = (addr[4] * 256) + addr[5]
Giampaolo Rodola'0d4f08c2013-05-16 15:12:01 +0200133 s = socket.create_connection((ip, port), timeout=TIMEOUT)
Antoine Pitrouf988cd02009-11-17 20:21:14 +0000134 self.dtp = self.dtp_handler(s, baseclass=self)
Benjamin Petersonbe17a112008-09-27 21:49:47 +0000135 self.push('200 active data connection established')
136
137 def cmd_pasv(self, arg):
Brett Cannon918e2d42010-10-29 23:26:25 +0000138 with socket.socket() as sock:
139 sock.bind((self.socket.getsockname()[0], 0))
Charles-François Natali6e204602014-07-23 19:28:13 +0100140 sock.listen()
Giampaolo Rodola'0d4f08c2013-05-16 15:12:01 +0200141 sock.settimeout(TIMEOUT)
Brett Cannon918e2d42010-10-29 23:26:25 +0000142 ip, port = sock.getsockname()[:2]
143 ip = ip.replace('.', ','); p1 = port / 256; p2 = port % 256
144 self.push('227 entering passive mode (%s,%d,%d)' %(ip, p1, p2))
145 conn, addr = sock.accept()
146 self.dtp = self.dtp_handler(conn, baseclass=self)
Benjamin Petersonbe17a112008-09-27 21:49:47 +0000147
148 def cmd_eprt(self, arg):
149 af, ip, port = arg.split(arg[0])[1:-1]
150 port = int(port)
Giampaolo Rodola'0d4f08c2013-05-16 15:12:01 +0200151 s = socket.create_connection((ip, port), timeout=TIMEOUT)
Antoine Pitrouf988cd02009-11-17 20:21:14 +0000152 self.dtp = self.dtp_handler(s, baseclass=self)
Benjamin Petersonbe17a112008-09-27 21:49:47 +0000153 self.push('200 active data connection established')
154
155 def cmd_epsv(self, arg):
Brett Cannon918e2d42010-10-29 23:26:25 +0000156 with socket.socket(socket.AF_INET6) as sock:
157 sock.bind((self.socket.getsockname()[0], 0))
Charles-François Natali6e204602014-07-23 19:28:13 +0100158 sock.listen()
Giampaolo Rodola'0d4f08c2013-05-16 15:12:01 +0200159 sock.settimeout(TIMEOUT)
Brett Cannon918e2d42010-10-29 23:26:25 +0000160 port = sock.getsockname()[1]
161 self.push('229 entering extended passive mode (|||%d|)' %port)
162 conn, addr = sock.accept()
163 self.dtp = self.dtp_handler(conn, baseclass=self)
Benjamin Petersonbe17a112008-09-27 21:49:47 +0000164
165 def cmd_echo(self, arg):
166 # sends back the received string (used by the test suite)
167 self.push(arg)
168
Giampaolo Rodolàbd576b72010-05-10 14:53:29 +0000169 def cmd_noop(self, arg):
170 self.push('200 noop ok')
171
Benjamin Petersonbe17a112008-09-27 21:49:47 +0000172 def cmd_user(self, arg):
173 self.push('331 username ok')
174
175 def cmd_pass(self, arg):
176 self.push('230 password ok')
177
178 def cmd_acct(self, arg):
179 self.push('230 acct ok')
180
181 def cmd_rnfr(self, arg):
182 self.push('350 rnfr ok')
183
184 def cmd_rnto(self, arg):
185 self.push('250 rnto ok')
186
187 def cmd_dele(self, arg):
188 self.push('250 dele ok')
189
190 def cmd_cwd(self, arg):
191 self.push('250 cwd ok')
192
193 def cmd_size(self, arg):
194 self.push('250 1000')
195
196 def cmd_mkd(self, arg):
197 self.push('257 "%s"' %arg)
198
199 def cmd_rmd(self, arg):
200 self.push('250 rmd ok')
201
202 def cmd_pwd(self, arg):
203 self.push('257 "pwd ok"')
204
205 def cmd_type(self, arg):
Giampaolo Rodolàf96482e2010-08-04 10:36:18 +0000206 self.push('200 type ok')
Benjamin Petersonbe17a112008-09-27 21:49:47 +0000207
208 def cmd_quit(self, arg):
209 self.push('221 quit ok')
210 self.close()
211
Giampaolo Rodola'0b5c21f2011-05-07 19:03:47 +0200212 def cmd_abor(self, arg):
213 self.push('226 abor ok')
214
Benjamin Petersonbe17a112008-09-27 21:49:47 +0000215 def cmd_stor(self, arg):
216 self.push('125 stor ok')
217
Antoine Pitrou648bcd72009-11-27 13:23:26 +0000218 def cmd_rest(self, arg):
219 self.rest = arg
220 self.push('350 rest ok')
221
Benjamin Petersonbe17a112008-09-27 21:49:47 +0000222 def cmd_retr(self, arg):
223 self.push('125 retr ok')
Antoine Pitrou648bcd72009-11-27 13:23:26 +0000224 if self.rest is not None:
225 offset = int(self.rest)
226 else:
227 offset = 0
Serhiy Storchakac30b1782013-10-20 16:58:27 +0300228 self.dtp.push(self.next_retr_data[offset:])
Benjamin Petersonbe17a112008-09-27 21:49:47 +0000229 self.dtp.close_when_done()
Antoine Pitrou648bcd72009-11-27 13:23:26 +0000230 self.rest = None
Benjamin Petersonbe17a112008-09-27 21:49:47 +0000231
232 def cmd_list(self, arg):
233 self.push('125 list ok')
234 self.dtp.push(LIST_DATA)
235 self.dtp.close_when_done()
236
237 def cmd_nlst(self, arg):
238 self.push('125 nlst ok')
239 self.dtp.push(NLST_DATA)
240 self.dtp.close_when_done()
241
Giampaolo Rodola'd78def92011-05-06 19:49:08 +0200242 def cmd_opts(self, arg):
243 self.push('200 opts ok')
244
245 def cmd_mlsd(self, arg):
246 self.push('125 mlsd ok')
247 self.dtp.push(MLSD_DATA)
248 self.dtp.close_when_done()
249
Serhiy Storchakac30b1782013-10-20 16:58:27 +0300250 def cmd_setlongretr(self, arg):
251 # For testing. Next RETR will return long line.
252 self.next_retr_data = 'x' * int(arg)
253 self.push('125 setlongretr ok')
254
Benjamin Petersonbe17a112008-09-27 21:49:47 +0000255
256class DummyFTPServer(asyncore.dispatcher, threading.Thread):
257
258 handler = DummyFTPHandler
259
260 def __init__(self, address, af=socket.AF_INET):
261 threading.Thread.__init__(self)
262 asyncore.dispatcher.__init__(self)
263 self.create_socket(af, socket.SOCK_STREAM)
264 self.bind(address)
265 self.listen(5)
266 self.active = False
267 self.active_lock = threading.Lock()
268 self.host, self.port = self.socket.getsockname()[:2]
Giampaolo Rodolàbd576b72010-05-10 14:53:29 +0000269 self.handler_instance = None
Benjamin Petersonbe17a112008-09-27 21:49:47 +0000270
271 def start(self):
272 assert not self.active
273 self.__flag = threading.Event()
274 threading.Thread.start(self)
275 self.__flag.wait()
276
277 def run(self):
278 self.active = True
279 self.__flag.set()
280 while self.active and asyncore.socket_map:
281 self.active_lock.acquire()
282 asyncore.loop(timeout=0.1, count=1)
283 self.active_lock.release()
284 asyncore.close_all(ignore_all=True)
285
286 def stop(self):
287 assert self.active
288 self.active = False
289 self.join()
290
Giampaolo Rodolà977c7072010-10-04 21:08:36 +0000291 def handle_accepted(self, conn, addr):
Giampaolo Rodolàbd576b72010-05-10 14:53:29 +0000292 self.handler_instance = self.handler(conn)
Benjamin Petersond06e3b02008-09-28 21:00:42 +0000293
294 def handle_connect(self):
295 self.close()
296 handle_read = handle_connect
Benjamin Petersonbe17a112008-09-27 21:49:47 +0000297
298 def writable(self):
299 return 0
300
301 def handle_error(self):
302 raise
303
304
Antoine Pitrouf988cd02009-11-17 20:21:14 +0000305if ssl is not None:
306
Christian Heimese5b5edf2013-12-02 02:56:02 +0100307 CERTFILE = os.path.join(os.path.dirname(__file__), "keycert3.pem")
308 CAFILE = os.path.join(os.path.dirname(__file__), "pycacert.pem")
Antoine Pitrouf988cd02009-11-17 20:21:14 +0000309
310 class SSLConnection(asyncore.dispatcher):
311 """An asyncore.dispatcher subclass supporting TLS/SSL."""
312
313 _ssl_accepting = False
Antoine Pitrou2c4f98b2010-04-23 00:16:21 +0000314 _ssl_closing = False
Antoine Pitrouf988cd02009-11-17 20:21:14 +0000315
316 def secure_connection(self):
Antoine Pitrouf988cd02009-11-17 20:21:14 +0000317 socket = ssl.wrap_socket(self.socket, suppress_ragged_eofs=False,
318 certfile=CERTFILE, server_side=True,
319 do_handshake_on_connect=False,
320 ssl_version=ssl.PROTOCOL_SSLv23)
Giampaolo Rodola'096dcb12011-06-27 11:17:51 +0200321 self.del_channel()
Antoine Pitrouf988cd02009-11-17 20:21:14 +0000322 self.set_socket(socket)
323 self._ssl_accepting = True
324
325 def _do_ssl_handshake(self):
326 try:
327 self.socket.do_handshake()
328 except ssl.SSLError as err:
329 if err.args[0] in (ssl.SSL_ERROR_WANT_READ,
330 ssl.SSL_ERROR_WANT_WRITE):
331 return
332 elif err.args[0] == ssl.SSL_ERROR_EOF:
333 return self.handle_close()
334 raise
Andrew Svetlov0832af62012-12-18 23:10:48 +0200335 except OSError as err:
Antoine Pitrouf988cd02009-11-17 20:21:14 +0000336 if err.args[0] == errno.ECONNABORTED:
337 return self.handle_close()
338 else:
339 self._ssl_accepting = False
340
Antoine Pitrou2c4f98b2010-04-23 00:16:21 +0000341 def _do_ssl_shutdown(self):
342 self._ssl_closing = True
343 try:
344 self.socket = self.socket.unwrap()
345 except ssl.SSLError as err:
346 if err.args[0] in (ssl.SSL_ERROR_WANT_READ,
347 ssl.SSL_ERROR_WANT_WRITE):
348 return
Andrew Svetlov0832af62012-12-18 23:10:48 +0200349 except OSError as err:
Antoine Pitrou2c4f98b2010-04-23 00:16:21 +0000350 # Any "socket error" corresponds to a SSL_ERROR_SYSCALL return
351 # from OpenSSL's SSL_shutdown(), corresponding to a
352 # closed socket condition. See also:
353 # http://www.mail-archive.com/openssl-users@openssl.org/msg60710.html
354 pass
355 self._ssl_closing = False
Benjamin Petersonb29614e2012-10-09 11:16:03 -0400356 if getattr(self, '_ccc', False) is False:
Giampaolo Rodola'096dcb12011-06-27 11:17:51 +0200357 super(SSLConnection, self).close()
358 else:
359 pass
Antoine Pitrou2c4f98b2010-04-23 00:16:21 +0000360
Antoine Pitrouf988cd02009-11-17 20:21:14 +0000361 def handle_read_event(self):
362 if self._ssl_accepting:
363 self._do_ssl_handshake()
Antoine Pitrou2c4f98b2010-04-23 00:16:21 +0000364 elif self._ssl_closing:
365 self._do_ssl_shutdown()
Antoine Pitrouf988cd02009-11-17 20:21:14 +0000366 else:
367 super(SSLConnection, self).handle_read_event()
368
369 def handle_write_event(self):
370 if self._ssl_accepting:
371 self._do_ssl_handshake()
Antoine Pitrou2c4f98b2010-04-23 00:16:21 +0000372 elif self._ssl_closing:
373 self._do_ssl_shutdown()
Antoine Pitrouf988cd02009-11-17 20:21:14 +0000374 else:
375 super(SSLConnection, self).handle_write_event()
376
377 def send(self, data):
378 try:
379 return super(SSLConnection, self).send(data)
380 except ssl.SSLError as err:
Antoine Pitrou5733c082010-03-22 14:49:10 +0000381 if err.args[0] in (ssl.SSL_ERROR_EOF, ssl.SSL_ERROR_ZERO_RETURN,
382 ssl.SSL_ERROR_WANT_READ,
383 ssl.SSL_ERROR_WANT_WRITE):
Antoine Pitrouf988cd02009-11-17 20:21:14 +0000384 return 0
385 raise
386
387 def recv(self, buffer_size):
388 try:
389 return super(SSLConnection, self).recv(buffer_size)
390 except ssl.SSLError as err:
Antoine Pitrou5733c082010-03-22 14:49:10 +0000391 if err.args[0] in (ssl.SSL_ERROR_WANT_READ,
392 ssl.SSL_ERROR_WANT_WRITE):
Antoine Pitrou2c4f98b2010-04-23 00:16:21 +0000393 return b''
Antoine Pitrouf988cd02009-11-17 20:21:14 +0000394 if err.args[0] in (ssl.SSL_ERROR_EOF, ssl.SSL_ERROR_ZERO_RETURN):
395 self.handle_close()
396 return b''
397 raise
398
399 def handle_error(self):
400 raise
401
402 def close(self):
Antoine Pitrou2c4f98b2010-04-23 00:16:21 +0000403 if (isinstance(self.socket, ssl.SSLSocket) and
404 self.socket._sslobj is not None):
405 self._do_ssl_shutdown()
Benjamin Peterson1bd93a72010-10-31 19:58:07 +0000406 else:
407 super(SSLConnection, self).close()
Antoine Pitrouf988cd02009-11-17 20:21:14 +0000408
409
410 class DummyTLS_DTPHandler(SSLConnection, DummyDTPHandler):
411 """A DummyDTPHandler subclass supporting TLS/SSL."""
412
413 def __init__(self, conn, baseclass):
414 DummyDTPHandler.__init__(self, conn, baseclass)
415 if self.baseclass.secure_data_channel:
416 self.secure_connection()
417
418
419 class DummyTLS_FTPHandler(SSLConnection, DummyFTPHandler):
420 """A DummyFTPHandler subclass supporting TLS/SSL."""
421
422 dtp_handler = DummyTLS_DTPHandler
423
424 def __init__(self, conn):
425 DummyFTPHandler.__init__(self, conn)
426 self.secure_data_channel = False
Giampaolo Rodola'096dcb12011-06-27 11:17:51 +0200427 self._ccc = False
Antoine Pitrouf988cd02009-11-17 20:21:14 +0000428
429 def cmd_auth(self, line):
430 """Set up secure control channel."""
431 self.push('234 AUTH TLS successful')
432 self.secure_connection()
433
Giampaolo Rodola'096dcb12011-06-27 11:17:51 +0200434 def cmd_ccc(self, line):
435 self.push('220 Reverting back to clear-text')
436 self._ccc = True
437 self._do_ssl_shutdown()
438
Antoine Pitrouf988cd02009-11-17 20:21:14 +0000439 def cmd_pbsz(self, line):
440 """Negotiate size of buffer for secure data transfer.
441 For TLS/SSL the only valid value for the parameter is '0'.
442 Any other value is accepted but ignored.
443 """
444 self.push('200 PBSZ=0 successful.')
445
446 def cmd_prot(self, line):
447 """Setup un/secure data channel."""
448 arg = line.upper()
449 if arg == 'C':
450 self.push('200 Protection set to Clear')
451 self.secure_data_channel = False
452 elif arg == 'P':
453 self.push('200 Protection set to Private')
454 self.secure_data_channel = True
455 else:
456 self.push("502 Unrecognized PROT type (use C or P).")
457
458
459 class DummyTLS_FTPServer(DummyFTPServer):
460 handler = DummyTLS_FTPHandler
461
462
Benjamin Petersonbe17a112008-09-27 21:49:47 +0000463class TestFTPClass(TestCase):
464
465 def setUp(self):
466 self.server = DummyFTPServer((HOST, 0))
467 self.server.start()
Giampaolo Rodola'0d4f08c2013-05-16 15:12:01 +0200468 self.client = ftplib.FTP(timeout=TIMEOUT)
Benjamin Petersonbe17a112008-09-27 21:49:47 +0000469 self.client.connect(self.server.host, self.server.port)
470
471 def tearDown(self):
472 self.client.close()
473 self.server.stop()
474
Giampaolo Rodola'8bc85852012-01-09 17:10:10 +0100475 def check_data(self, received, expected):
476 self.assertEqual(len(received), len(expected))
477 self.assertEqual(received, expected)
478
Benjamin Petersonbe17a112008-09-27 21:49:47 +0000479 def test_getwelcome(self):
480 self.assertEqual(self.client.getwelcome(), '220 welcome')
481
482 def test_sanitize(self):
483 self.assertEqual(self.client.sanitize('foo'), repr('foo'))
484 self.assertEqual(self.client.sanitize('pass 12345'), repr('pass *****'))
485 self.assertEqual(self.client.sanitize('PASS 12345'), repr('PASS *****'))
486
487 def test_exceptions(self):
488 self.assertRaises(ftplib.error_temp, self.client.sendcmd, 'echo 400')
489 self.assertRaises(ftplib.error_temp, self.client.sendcmd, 'echo 499')
490 self.assertRaises(ftplib.error_perm, self.client.sendcmd, 'echo 500')
491 self.assertRaises(ftplib.error_perm, self.client.sendcmd, 'echo 599')
492 self.assertRaises(ftplib.error_proto, self.client.sendcmd, 'echo 999')
493
494 def test_all_errors(self):
495 exceptions = (ftplib.error_reply, ftplib.error_temp, ftplib.error_perm,
Andrew Svetlovf7a17b42012-12-25 16:47:37 +0200496 ftplib.error_proto, ftplib.Error, OSError, EOFError)
Benjamin Petersonbe17a112008-09-27 21:49:47 +0000497 for x in exceptions:
498 try:
499 raise x('exception not included in all_errors set')
500 except ftplib.all_errors:
501 pass
502
503 def test_set_pasv(self):
504 # passive mode is supposed to be enabled by default
505 self.assertTrue(self.client.passiveserver)
506 self.client.set_pasv(True)
507 self.assertTrue(self.client.passiveserver)
508 self.client.set_pasv(False)
509 self.assertFalse(self.client.passiveserver)
510
511 def test_voidcmd(self):
512 self.client.voidcmd('echo 200')
513 self.client.voidcmd('echo 299')
514 self.assertRaises(ftplib.error_reply, self.client.voidcmd, 'echo 199')
515 self.assertRaises(ftplib.error_reply, self.client.voidcmd, 'echo 300')
516
517 def test_login(self):
518 self.client.login()
519
520 def test_acct(self):
521 self.client.acct('passwd')
522
523 def test_rename(self):
524 self.client.rename('a', 'b')
Giampaolo Rodolàbd576b72010-05-10 14:53:29 +0000525 self.server.handler_instance.next_response = '200'
Benjamin Petersonbe17a112008-09-27 21:49:47 +0000526 self.assertRaises(ftplib.error_reply, self.client.rename, 'a', 'b')
527
528 def test_delete(self):
529 self.client.delete('foo')
Giampaolo Rodolàbd576b72010-05-10 14:53:29 +0000530 self.server.handler_instance.next_response = '199'
Benjamin Petersonbe17a112008-09-27 21:49:47 +0000531 self.assertRaises(ftplib.error_reply, self.client.delete, 'foo')
532
533 def test_size(self):
534 self.client.size('foo')
535
536 def test_mkd(self):
537 dir = self.client.mkd('/foo')
538 self.assertEqual(dir, '/foo')
539
540 def test_rmd(self):
541 self.client.rmd('foo')
542
Senthil Kumaran0d538602013-08-12 22:25:27 -0700543 def test_cwd(self):
544 dir = self.client.cwd('/foo')
545 self.assertEqual(dir, '250 cwd ok')
546
Benjamin Petersonbe17a112008-09-27 21:49:47 +0000547 def test_pwd(self):
548 dir = self.client.pwd()
549 self.assertEqual(dir, 'pwd ok')
550
551 def test_quit(self):
552 self.assertEqual(self.client.quit(), '221 quit ok')
553 # Ensure the connection gets closed; sock attribute should be None
554 self.assertEqual(self.client.sock, None)
555
Giampaolo Rodola'0b5c21f2011-05-07 19:03:47 +0200556 def test_abort(self):
557 self.client.abort()
558
Benjamin Petersonbe17a112008-09-27 21:49:47 +0000559 def test_retrbinary(self):
560 def callback(data):
561 received.append(data.decode('ascii'))
562 received = []
563 self.client.retrbinary('retr', callback)
Giampaolo Rodola'8bc85852012-01-09 17:10:10 +0100564 self.check_data(''.join(received), RETR_DATA)
Benjamin Petersonbe17a112008-09-27 21:49:47 +0000565
Antoine Pitrou648bcd72009-11-27 13:23:26 +0000566 def test_retrbinary_rest(self):
567 def callback(data):
568 received.append(data.decode('ascii'))
569 for rest in (0, 10, 20):
570 received = []
571 self.client.retrbinary('retr', callback, rest=rest)
Giampaolo Rodola'8bc85852012-01-09 17:10:10 +0100572 self.check_data(''.join(received), RETR_DATA[rest:])
Antoine Pitrou648bcd72009-11-27 13:23:26 +0000573
Benjamin Petersonbe17a112008-09-27 21:49:47 +0000574 def test_retrlines(self):
575 received = []
576 self.client.retrlines('retr', received.append)
Giampaolo Rodola'8bc85852012-01-09 17:10:10 +0100577 self.check_data(''.join(received), RETR_DATA.replace('\r\n', ''))
Benjamin Petersonbe17a112008-09-27 21:49:47 +0000578
579 def test_storbinary(self):
580 f = io.BytesIO(RETR_DATA.encode('ascii'))
581 self.client.storbinary('stor', f)
Giampaolo Rodola'8bc85852012-01-09 17:10:10 +0100582 self.check_data(self.server.handler_instance.last_received_data, RETR_DATA)
Benjamin Petersonbe17a112008-09-27 21:49:47 +0000583 # test new callback arg
584 flag = []
585 f.seek(0)
586 self.client.storbinary('stor', f, callback=lambda x: flag.append(None))
587 self.assertTrue(flag)
588
Antoine Pitrou648bcd72009-11-27 13:23:26 +0000589 def test_storbinary_rest(self):
590 f = io.BytesIO(RETR_DATA.replace('\r\n', '\n').encode('ascii'))
591 for r in (30, '30'):
592 f.seek(0)
593 self.client.storbinary('stor', f, rest=r)
Giampaolo Rodolàbd576b72010-05-10 14:53:29 +0000594 self.assertEqual(self.server.handler_instance.rest, str(r))
Antoine Pitrou648bcd72009-11-27 13:23:26 +0000595
Giampaolo Rodolàf96482e2010-08-04 10:36:18 +0000596 def test_storlines(self):
Benjamin Petersonbe17a112008-09-27 21:49:47 +0000597 f = io.BytesIO(RETR_DATA.replace('\r\n', '\n').encode('ascii'))
598 self.client.storlines('stor', f)
Giampaolo Rodola'8bc85852012-01-09 17:10:10 +0100599 self.check_data(self.server.handler_instance.last_received_data, RETR_DATA)
Benjamin Petersonbe17a112008-09-27 21:49:47 +0000600 # test new callback arg
601 flag = []
602 f.seek(0)
603 self.client.storlines('stor foo', f, callback=lambda x: flag.append(None))
604 self.assertTrue(flag)
605
Victor Stinnered3a3032013-04-02 22:13:27 +0200606 f = io.StringIO(RETR_DATA.replace('\r\n', '\n'))
607 # storlines() expects a binary file, not a text file
Florent Xicluna5f3fef32013-07-06 15:08:21 +0200608 with support.check_warnings(('', BytesWarning), quiet=True):
609 self.assertRaises(TypeError, self.client.storlines, 'stor foo', f)
Victor Stinnered3a3032013-04-02 22:13:27 +0200610
Benjamin Petersonbe17a112008-09-27 21:49:47 +0000611 def test_nlst(self):
612 self.client.nlst()
613 self.assertEqual(self.client.nlst(), NLST_DATA.split('\r\n')[:-1])
614
615 def test_dir(self):
616 l = []
617 self.client.dir(lambda x: l.append(x))
618 self.assertEqual(''.join(l), LIST_DATA.replace('\r\n', ''))
619
Giampaolo Rodola'd78def92011-05-06 19:49:08 +0200620 def test_mlsd(self):
621 list(self.client.mlsd())
622 list(self.client.mlsd(path='/'))
623 list(self.client.mlsd(path='/', facts=['size', 'type']))
624
625 ls = list(self.client.mlsd())
626 for name, facts in ls:
Giampaolo Rodola'a55efb32011-05-07 16:06:59 +0200627 self.assertIsInstance(name, str)
628 self.assertIsInstance(facts, dict)
Giampaolo Rodola'd78def92011-05-06 19:49:08 +0200629 self.assertTrue(name)
Giampaolo Rodola'a55efb32011-05-07 16:06:59 +0200630 self.assertIn('type', facts)
631 self.assertIn('perm', facts)
632 self.assertIn('unique', facts)
Giampaolo Rodola'd78def92011-05-06 19:49:08 +0200633
634 def set_data(data):
635 self.server.handler_instance.next_data = data
636
637 def test_entry(line, type=None, perm=None, unique=None, name=None):
638 type = 'type' if type is None else type
639 perm = 'perm' if perm is None else perm
640 unique = 'unique' if unique is None else unique
641 name = 'name' if name is None else name
642 set_data(line)
643 _name, facts = next(self.client.mlsd())
644 self.assertEqual(_name, name)
645 self.assertEqual(facts['type'], type)
646 self.assertEqual(facts['perm'], perm)
647 self.assertEqual(facts['unique'], unique)
648
649 # plain
650 test_entry('type=type;perm=perm;unique=unique; name\r\n')
651 # "=" in fact value
652 test_entry('type=ty=pe;perm=perm;unique=unique; name\r\n', type="ty=pe")
653 test_entry('type==type;perm=perm;unique=unique; name\r\n', type="=type")
654 test_entry('type=t=y=pe;perm=perm;unique=unique; name\r\n', type="t=y=pe")
655 test_entry('type=====;perm=perm;unique=unique; name\r\n', type="====")
656 # spaces in name
657 test_entry('type=type;perm=perm;unique=unique; na me\r\n', name="na me")
658 test_entry('type=type;perm=perm;unique=unique; name \r\n', name="name ")
659 test_entry('type=type;perm=perm;unique=unique; name\r\n', name=" name")
660 test_entry('type=type;perm=perm;unique=unique; n am e\r\n', name="n am e")
661 # ";" in name
662 test_entry('type=type;perm=perm;unique=unique; na;me\r\n', name="na;me")
663 test_entry('type=type;perm=perm;unique=unique; ;name\r\n', name=";name")
664 test_entry('type=type;perm=perm;unique=unique; ;name;\r\n', name=";name;")
665 test_entry('type=type;perm=perm;unique=unique; ;;;;\r\n', name=";;;;")
666 # case sensitiveness
667 set_data('Type=type;TyPe=perm;UNIQUE=unique; name\r\n')
668 _name, facts = next(self.client.mlsd())
Giampaolo Rodola'a55efb32011-05-07 16:06:59 +0200669 for x in facts:
670 self.assertTrue(x.islower())
Giampaolo Rodola'd78def92011-05-06 19:49:08 +0200671 # no data (directory empty)
672 set_data('')
673 self.assertRaises(StopIteration, next, self.client.mlsd())
674 set_data('')
675 for x in self.client.mlsd():
676 self.fail("unexpected data %s" % data)
677
Benjamin Peterson3a53fbb2008-09-27 22:04:16 +0000678 def test_makeport(self):
Brett Cannon918e2d42010-10-29 23:26:25 +0000679 with self.client.makeport():
680 # IPv4 is in use, just make sure send_eprt has not been used
681 self.assertEqual(self.server.handler_instance.last_received_cmd,
682 'port')
Benjamin Petersonbe17a112008-09-27 21:49:47 +0000683
684 def test_makepasv(self):
685 host, port = self.client.makepasv()
Giampaolo Rodola'0d4f08c2013-05-16 15:12:01 +0200686 conn = socket.create_connection((host, port), timeout=TIMEOUT)
Guido van Rossumd8faa362007-04-27 19:54:29 +0000687 conn.close()
Benjamin Petersonbe17a112008-09-27 21:49:47 +0000688 # IPv4 is in use, just make sure send_epsv has not been used
Giampaolo Rodolàbd576b72010-05-10 14:53:29 +0000689 self.assertEqual(self.server.handler_instance.last_received_cmd, 'pasv')
690
691 def test_with_statement(self):
692 self.client.quit()
693
694 def is_client_connected():
695 if self.client.sock is None:
696 return False
697 try:
698 self.client.sendcmd('noop')
Andrew Svetlov0832af62012-12-18 23:10:48 +0200699 except (OSError, EOFError):
Giampaolo Rodolàbd576b72010-05-10 14:53:29 +0000700 return False
701 return True
702
703 # base test
Giampaolo Rodola'0d4f08c2013-05-16 15:12:01 +0200704 with ftplib.FTP(timeout=TIMEOUT) as self.client:
Giampaolo Rodolàbd576b72010-05-10 14:53:29 +0000705 self.client.connect(self.server.host, self.server.port)
706 self.client.sendcmd('noop')
707 self.assertTrue(is_client_connected())
708 self.assertEqual(self.server.handler_instance.last_received_cmd, 'quit')
709 self.assertFalse(is_client_connected())
710
711 # QUIT sent inside the with block
Giampaolo Rodola'0d4f08c2013-05-16 15:12:01 +0200712 with ftplib.FTP(timeout=TIMEOUT) as self.client:
Giampaolo Rodolàbd576b72010-05-10 14:53:29 +0000713 self.client.connect(self.server.host, self.server.port)
714 self.client.sendcmd('noop')
715 self.client.quit()
716 self.assertEqual(self.server.handler_instance.last_received_cmd, 'quit')
717 self.assertFalse(is_client_connected())
718
719 # force a wrong response code to be sent on QUIT: error_perm
720 # is expected and the connection is supposed to be closed
721 try:
Giampaolo Rodola'0d4f08c2013-05-16 15:12:01 +0200722 with ftplib.FTP(timeout=TIMEOUT) as self.client:
Giampaolo Rodolàbd576b72010-05-10 14:53:29 +0000723 self.client.connect(self.server.host, self.server.port)
724 self.client.sendcmd('noop')
725 self.server.handler_instance.next_response = '550 error on quit'
726 except ftplib.error_perm as err:
727 self.assertEqual(str(err), '550 error on quit')
728 else:
729 self.fail('Exception not raised')
730 # needed to give the threaded server some time to set the attribute
731 # which otherwise would still be == 'noop'
732 time.sleep(0.1)
733 self.assertEqual(self.server.handler_instance.last_received_cmd, 'quit')
734 self.assertFalse(is_client_connected())
Guido van Rossumd8faa362007-04-27 19:54:29 +0000735
Giampaolo Rodolà396ff062011-02-28 19:19:51 +0000736 def test_source_address(self):
737 self.client.quit()
738 port = support.find_unused_port()
Antoine Pitrou6dca5272011-04-03 18:29:45 +0200739 try:
740 self.client.connect(self.server.host, self.server.port,
741 source_address=(HOST, port))
742 self.assertEqual(self.client.sock.getsockname()[1], port)
743 self.client.quit()
Andrew Svetlovf7a17b42012-12-25 16:47:37 +0200744 except OSError as e:
Antoine Pitrou6dca5272011-04-03 18:29:45 +0200745 if e.errno == errno.EADDRINUSE:
746 self.skipTest("couldn't bind to port %d" % port)
747 raise
Giampaolo Rodolà396ff062011-02-28 19:19:51 +0000748
749 def test_source_address_passive_connection(self):
750 port = support.find_unused_port()
751 self.client.source_address = (HOST, port)
Antoine Pitrou6dca5272011-04-03 18:29:45 +0200752 try:
753 with self.client.transfercmd('list') as sock:
754 self.assertEqual(sock.getsockname()[1], port)
Andrew Svetlovf7a17b42012-12-25 16:47:37 +0200755 except OSError as e:
Antoine Pitrou6dca5272011-04-03 18:29:45 +0200756 if e.errno == errno.EADDRINUSE:
757 self.skipTest("couldn't bind to port %d" % port)
758 raise
Giampaolo Rodolà396ff062011-02-28 19:19:51 +0000759
Giampaolo Rodolàbbc47822010-08-23 22:10:32 +0000760 def test_parse257(self):
761 self.assertEqual(ftplib.parse257('257 "/foo/bar"'), '/foo/bar')
762 self.assertEqual(ftplib.parse257('257 "/foo/bar" created'), '/foo/bar')
763 self.assertEqual(ftplib.parse257('257 ""'), '')
764 self.assertEqual(ftplib.parse257('257 "" created'), '')
765 self.assertRaises(ftplib.error_reply, ftplib.parse257, '250 "/foo/bar"')
766 # The 257 response is supposed to include the directory
767 # name and in case it contains embedded double-quotes
768 # they must be doubled (see RFC-959, chapter 7, appendix 2).
769 self.assertEqual(ftplib.parse257('257 "/foo/b""ar"'), '/foo/b"ar')
770 self.assertEqual(ftplib.parse257('257 "/foo/b""ar" created'), '/foo/b"ar')
771
Serhiy Storchakac30b1782013-10-20 16:58:27 +0300772 def test_line_too_long(self):
773 self.assertRaises(ftplib.Error, self.client.sendcmd,
774 'x' * self.client.maxline * 2)
775
776 def test_retrlines_too_long(self):
777 self.client.sendcmd('SETLONGRETR %d' % (self.client.maxline * 2))
778 received = []
779 self.assertRaises(ftplib.Error,
780 self.client.retrlines, 'retr', received.append)
781
782 def test_storlines_too_long(self):
783 f = io.BytesIO(b'x' * self.client.maxline * 2)
784 self.assertRaises(ftplib.Error, self.client.storlines, 'stor', f)
785
Benjamin Petersonbe17a112008-09-27 21:49:47 +0000786
Serhiy Storchaka43767632013-11-03 21:31:38 +0200787@skipUnless(support.IPV6_ENABLED, "IPv6 not enabled")
Benjamin Petersonbe17a112008-09-27 21:49:47 +0000788class TestIPv6Environment(TestCase):
789
790 def setUp(self):
Antoine Pitrouf6fbf562013-08-22 00:39:46 +0200791 self.server = DummyFTPServer((HOSTv6, 0), af=socket.AF_INET6)
Benjamin Petersonbe17a112008-09-27 21:49:47 +0000792 self.server.start()
Giampaolo Rodola'0d4f08c2013-05-16 15:12:01 +0200793 self.client = ftplib.FTP(timeout=TIMEOUT)
Benjamin Petersonbe17a112008-09-27 21:49:47 +0000794 self.client.connect(self.server.host, self.server.port)
795
796 def tearDown(self):
797 self.client.close()
798 self.server.stop()
799
800 def test_af(self):
801 self.assertEqual(self.client.af, socket.AF_INET6)
802
803 def test_makeport(self):
Brett Cannon918e2d42010-10-29 23:26:25 +0000804 with self.client.makeport():
805 self.assertEqual(self.server.handler_instance.last_received_cmd,
806 'eprt')
Benjamin Petersonbe17a112008-09-27 21:49:47 +0000807
808 def test_makepasv(self):
809 host, port = self.client.makepasv()
Giampaolo Rodola'0d4f08c2013-05-16 15:12:01 +0200810 conn = socket.create_connection((host, port), timeout=TIMEOUT)
Benjamin Petersonbe17a112008-09-27 21:49:47 +0000811 conn.close()
Giampaolo Rodolàbd576b72010-05-10 14:53:29 +0000812 self.assertEqual(self.server.handler_instance.last_received_cmd, 'epsv')
Benjamin Petersonbe17a112008-09-27 21:49:47 +0000813
814 def test_transfer(self):
815 def retr():
816 def callback(data):
817 received.append(data.decode('ascii'))
818 received = []
819 self.client.retrbinary('retr', callback)
Giampaolo Rodola'8bc85852012-01-09 17:10:10 +0100820 self.assertEqual(len(''.join(received)), len(RETR_DATA))
Benjamin Petersonbe17a112008-09-27 21:49:47 +0000821 self.assertEqual(''.join(received), RETR_DATA)
822 self.client.set_pasv(True)
823 retr()
824 self.client.set_pasv(False)
825 retr()
826
827
Serhiy Storchaka43767632013-11-03 21:31:38 +0200828@skipUnless(ssl, "SSL not available")
Antoine Pitrouf988cd02009-11-17 20:21:14 +0000829class TestTLS_FTPClassMixin(TestFTPClass):
830 """Repeat TestFTPClass tests starting the TLS layer for both control
831 and data connections first.
832 """
833
834 def setUp(self):
835 self.server = DummyTLS_FTPServer((HOST, 0))
836 self.server.start()
Giampaolo Rodola'0d4f08c2013-05-16 15:12:01 +0200837 self.client = ftplib.FTP_TLS(timeout=TIMEOUT)
Antoine Pitrouf988cd02009-11-17 20:21:14 +0000838 self.client.connect(self.server.host, self.server.port)
839 # enable TLS
840 self.client.auth()
841 self.client.prot_p()
842
843
Serhiy Storchaka43767632013-11-03 21:31:38 +0200844@skipUnless(ssl, "SSL not available")
Antoine Pitrouf988cd02009-11-17 20:21:14 +0000845class TestTLS_FTPClass(TestCase):
846 """Specific TLS_FTP class tests."""
847
848 def setUp(self):
849 self.server = DummyTLS_FTPServer((HOST, 0))
850 self.server.start()
Giampaolo Rodola'0d4f08c2013-05-16 15:12:01 +0200851 self.client = ftplib.FTP_TLS(timeout=TIMEOUT)
Antoine Pitrouf988cd02009-11-17 20:21:14 +0000852 self.client.connect(self.server.host, self.server.port)
853
854 def tearDown(self):
855 self.client.close()
856 self.server.stop()
857
858 def test_control_connection(self):
Ezio Melottie9615932010-01-24 19:26:24 +0000859 self.assertNotIsInstance(self.client.sock, ssl.SSLSocket)
Antoine Pitrouf988cd02009-11-17 20:21:14 +0000860 self.client.auth()
Ezio Melottie9615932010-01-24 19:26:24 +0000861 self.assertIsInstance(self.client.sock, ssl.SSLSocket)
Antoine Pitrouf988cd02009-11-17 20:21:14 +0000862
863 def test_data_connection(self):
864 # clear text
Brett Cannon918e2d42010-10-29 23:26:25 +0000865 with self.client.transfercmd('list') as sock:
866 self.assertNotIsInstance(sock, ssl.SSLSocket)
Antoine Pitrou2c4f98b2010-04-23 00:16:21 +0000867 self.assertEqual(self.client.voidresp(), "226 transfer complete")
Antoine Pitrouf988cd02009-11-17 20:21:14 +0000868
869 # secured, after PROT P
870 self.client.prot_p()
Brett Cannon918e2d42010-10-29 23:26:25 +0000871 with self.client.transfercmd('list') as sock:
872 self.assertIsInstance(sock, ssl.SSLSocket)
Antoine Pitrou2c4f98b2010-04-23 00:16:21 +0000873 self.assertEqual(self.client.voidresp(), "226 transfer complete")
Antoine Pitrouf988cd02009-11-17 20:21:14 +0000874
875 # PROT C is issued, the connection must be in cleartext again
876 self.client.prot_c()
Brett Cannon918e2d42010-10-29 23:26:25 +0000877 with self.client.transfercmd('list') as sock:
878 self.assertNotIsInstance(sock, ssl.SSLSocket)
Antoine Pitrou2c4f98b2010-04-23 00:16:21 +0000879 self.assertEqual(self.client.voidresp(), "226 transfer complete")
Antoine Pitrouf988cd02009-11-17 20:21:14 +0000880
881 def test_login(self):
882 # login() is supposed to implicitly secure the control connection
Ezio Melottie9615932010-01-24 19:26:24 +0000883 self.assertNotIsInstance(self.client.sock, ssl.SSLSocket)
Antoine Pitrouf988cd02009-11-17 20:21:14 +0000884 self.client.login()
Ezio Melottie9615932010-01-24 19:26:24 +0000885 self.assertIsInstance(self.client.sock, ssl.SSLSocket)
Antoine Pitrouf988cd02009-11-17 20:21:14 +0000886 # make sure that AUTH TLS doesn't get issued again
887 self.client.login()
888
889 def test_auth_issued_twice(self):
890 self.client.auth()
891 self.assertRaises(ValueError, self.client.auth)
892
893 def test_auth_ssl(self):
894 try:
895 self.client.ssl_version = ssl.PROTOCOL_SSLv3
896 self.client.auth()
897 self.assertRaises(ValueError, self.client.auth)
898 finally:
899 self.client.ssl_version = ssl.PROTOCOL_TLSv1
900
Giampaolo Rodolàa67299e2010-05-26 18:06:04 +0000901 def test_context(self):
902 self.client.quit()
903 ctx = ssl.SSLContext(ssl.PROTOCOL_TLSv1)
904 self.assertRaises(ValueError, ftplib.FTP_TLS, keyfile=CERTFILE,
905 context=ctx)
906 self.assertRaises(ValueError, ftplib.FTP_TLS, certfile=CERTFILE,
907 context=ctx)
908 self.assertRaises(ValueError, ftplib.FTP_TLS, certfile=CERTFILE,
909 keyfile=CERTFILE, context=ctx)
910
Giampaolo Rodola'0d4f08c2013-05-16 15:12:01 +0200911 self.client = ftplib.FTP_TLS(context=ctx, timeout=TIMEOUT)
Giampaolo Rodolàa67299e2010-05-26 18:06:04 +0000912 self.client.connect(self.server.host, self.server.port)
913 self.assertNotIsInstance(self.client.sock, ssl.SSLSocket)
914 self.client.auth()
915 self.assertIs(self.client.sock.context, ctx)
916 self.assertIsInstance(self.client.sock, ssl.SSLSocket)
917
918 self.client.prot_p()
Brett Cannon918e2d42010-10-29 23:26:25 +0000919 with self.client.transfercmd('list') as sock:
920 self.assertIs(sock.context, ctx)
921 self.assertIsInstance(sock, ssl.SSLSocket)
Giampaolo Rodolàa67299e2010-05-26 18:06:04 +0000922
Giampaolo Rodola'096dcb12011-06-27 11:17:51 +0200923 def test_ccc(self):
924 self.assertRaises(ValueError, self.client.ccc)
925 self.client.login(secure=True)
926 self.assertIsInstance(self.client.sock, ssl.SSLSocket)
927 self.client.ccc()
928 self.assertRaises(ValueError, self.client.sock.unwrap)
Giampaolo Rodola'096dcb12011-06-27 11:17:51 +0200929
Christian Heimesb2a794d2013-12-15 19:50:13 +0100930 @skipUnless(HAS_SNI, 'No SNI support in ssl module')
Christian Heimese5b5edf2013-12-02 02:56:02 +0100931 def test_check_hostname(self):
932 self.client.quit()
933 ctx = ssl.SSLContext(ssl.PROTOCOL_TLSv1)
934 ctx.verify_mode = ssl.CERT_REQUIRED
935 ctx.check_hostname = True
936 ctx.load_verify_locations(CAFILE)
937 self.client = ftplib.FTP_TLS(context=ctx, timeout=TIMEOUT)
938
939 # 127.0.0.1 doesn't match SAN
940 self.client.connect(self.server.host, self.server.port)
941 with self.assertRaises(ssl.CertificateError):
942 self.client.auth()
943 # exception quits connection
944
945 self.client.connect(self.server.host, self.server.port)
946 self.client.prot_p()
947 with self.assertRaises(ssl.CertificateError):
948 with self.client.transfercmd("list") as sock:
949 pass
950 self.client.quit()
951
952 self.client.connect("localhost", self.server.port)
953 self.client.auth()
954 self.client.quit()
955
956 self.client.connect("localhost", self.server.port)
957 self.client.prot_p()
958 with self.client.transfercmd("list") as sock:
959 pass
960
Antoine Pitrouf988cd02009-11-17 20:21:14 +0000961
Benjamin Petersonbe17a112008-09-27 21:49:47 +0000962class TestTimeouts(TestCase):
Guido van Rossumd8faa362007-04-27 19:54:29 +0000963
964 def setUp(self):
Guido van Rossumd8faa362007-04-27 19:54:29 +0000965 self.evt = threading.Event()
Christian Heimes5e696852008-04-09 08:37:03 +0000966 self.sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
Antoine Pitrou08d02722012-12-19 20:44:02 +0100967 self.sock.settimeout(20)
Benjamin Petersonee8712c2008-05-20 21:35:26 +0000968 self.port = support.bind_port(self.sock)
Antoine Pitrou08d02722012-12-19 20:44:02 +0100969 self.server_thread = threading.Thread(target=self.server)
970 self.server_thread.start()
Christian Heimes836baa52008-02-26 08:18:30 +0000971 # Wait for the server to be ready.
972 self.evt.wait()
973 self.evt.clear()
Antoine Pitrou08d02722012-12-19 20:44:02 +0100974 self.old_port = ftplib.FTP.port
Christian Heimes5e696852008-04-09 08:37:03 +0000975 ftplib.FTP.port = self.port
Guido van Rossumd8faa362007-04-27 19:54:29 +0000976
977 def tearDown(self):
Antoine Pitrou08d02722012-12-19 20:44:02 +0100978 ftplib.FTP.port = self.old_port
979 self.server_thread.join()
Guido van Rossumd8faa362007-04-27 19:54:29 +0000980
Antoine Pitrou08d02722012-12-19 20:44:02 +0100981 def server(self):
Benjamin Petersonbe17a112008-09-27 21:49:47 +0000982 # This method sets the evt 3 times:
983 # 1) when the connection is ready to be accepted.
984 # 2) when it is safe for the caller to close the connection
985 # 3) when we have closed the socket
Charles-François Natali6e204602014-07-23 19:28:13 +0100986 self.sock.listen()
Benjamin Petersonbe17a112008-09-27 21:49:47 +0000987 # (1) Signal the caller that we are ready to accept the connection.
Antoine Pitrou08d02722012-12-19 20:44:02 +0100988 self.evt.set()
Benjamin Petersonbe17a112008-09-27 21:49:47 +0000989 try:
Antoine Pitrou08d02722012-12-19 20:44:02 +0100990 conn, addr = self.sock.accept()
Benjamin Petersonbe17a112008-09-27 21:49:47 +0000991 except socket.timeout:
992 pass
993 else:
Antoine Pitrou08d02722012-12-19 20:44:02 +0100994 conn.sendall(b"1 Hola mundo\n")
995 conn.shutdown(socket.SHUT_WR)
Benjamin Petersonbe17a112008-09-27 21:49:47 +0000996 # (2) Signal the caller that it is safe to close the socket.
Antoine Pitrou08d02722012-12-19 20:44:02 +0100997 self.evt.set()
Benjamin Petersonbe17a112008-09-27 21:49:47 +0000998 conn.close()
999 finally:
Antoine Pitrou08d02722012-12-19 20:44:02 +01001000 self.sock.close()
Guido van Rossumd8faa362007-04-27 19:54:29 +00001001
1002 def testTimeoutDefault(self):
Georg Brandlf78e02b2008-06-10 17:40:04 +00001003 # default -- use global socket timeout
Serhiy Storchaka25d8aea2014-02-08 14:50:08 +02001004 self.assertIsNone(socket.getdefaulttimeout())
Georg Brandlf78e02b2008-06-10 17:40:04 +00001005 socket.setdefaulttimeout(30)
1006 try:
Antoine Pitrouf6fbf562013-08-22 00:39:46 +02001007 ftp = ftplib.FTP(HOST)
Georg Brandlf78e02b2008-06-10 17:40:04 +00001008 finally:
1009 socket.setdefaulttimeout(None)
1010 self.assertEqual(ftp.sock.gettimeout(), 30)
1011 self.evt.wait()
1012 ftp.close()
1013
1014 def testTimeoutNone(self):
1015 # no timeout -- do not use global socket timeout
Serhiy Storchaka25d8aea2014-02-08 14:50:08 +02001016 self.assertIsNone(socket.getdefaulttimeout())
Georg Brandlf78e02b2008-06-10 17:40:04 +00001017 socket.setdefaulttimeout(30)
1018 try:
Antoine Pitrouf6fbf562013-08-22 00:39:46 +02001019 ftp = ftplib.FTP(HOST, timeout=None)
Georg Brandlf78e02b2008-06-10 17:40:04 +00001020 finally:
1021 socket.setdefaulttimeout(None)
Serhiy Storchaka25d8aea2014-02-08 14:50:08 +02001022 self.assertIsNone(ftp.sock.gettimeout())
Christian Heimes836baa52008-02-26 08:18:30 +00001023 self.evt.wait()
Georg Brandlf78e02b2008-06-10 17:40:04 +00001024 ftp.close()
Guido van Rossumd8faa362007-04-27 19:54:29 +00001025
1026 def testTimeoutValue(self):
1027 # a value
Christian Heimes5e696852008-04-09 08:37:03 +00001028 ftp = ftplib.FTP(HOST, timeout=30)
Guido van Rossumd8faa362007-04-27 19:54:29 +00001029 self.assertEqual(ftp.sock.gettimeout(), 30)
Christian Heimes836baa52008-02-26 08:18:30 +00001030 self.evt.wait()
Georg Brandlf78e02b2008-06-10 17:40:04 +00001031 ftp.close()
Guido van Rossumd8faa362007-04-27 19:54:29 +00001032
1033 def testTimeoutConnect(self):
1034 ftp = ftplib.FTP()
Christian Heimes5e696852008-04-09 08:37:03 +00001035 ftp.connect(HOST, timeout=30)
Guido van Rossumd8faa362007-04-27 19:54:29 +00001036 self.assertEqual(ftp.sock.gettimeout(), 30)
Christian Heimes836baa52008-02-26 08:18:30 +00001037 self.evt.wait()
Georg Brandlf78e02b2008-06-10 17:40:04 +00001038 ftp.close()
Guido van Rossumd8faa362007-04-27 19:54:29 +00001039
1040 def testTimeoutDifferentOrder(self):
1041 ftp = ftplib.FTP(timeout=30)
Christian Heimes5e696852008-04-09 08:37:03 +00001042 ftp.connect(HOST)
Guido van Rossumd8faa362007-04-27 19:54:29 +00001043 self.assertEqual(ftp.sock.gettimeout(), 30)
Christian Heimes836baa52008-02-26 08:18:30 +00001044 self.evt.wait()
Georg Brandlf78e02b2008-06-10 17:40:04 +00001045 ftp.close()
Guido van Rossumd8faa362007-04-27 19:54:29 +00001046
1047 def testTimeoutDirectAccess(self):
1048 ftp = ftplib.FTP()
1049 ftp.timeout = 30
Christian Heimes5e696852008-04-09 08:37:03 +00001050 ftp.connect(HOST)
Guido van Rossumd8faa362007-04-27 19:54:29 +00001051 self.assertEqual(ftp.sock.gettimeout(), 30)
Christian Heimes836baa52008-02-26 08:18:30 +00001052 self.evt.wait()
Guido van Rossumd8faa362007-04-27 19:54:29 +00001053 ftp.close()
1054
1055
R David Murray87632f12013-02-19 18:32:28 -05001056class TestNetrcDeprecation(TestCase):
1057
1058 def test_deprecation(self):
1059 with support.temp_cwd(), support.EnvironmentVarGuard() as env:
1060 env['HOME'] = os.getcwd()
1061 open('.netrc', 'w').close()
1062 with self.assertWarns(DeprecationWarning):
1063 ftplib.Netrc()
1064
1065
1066
Benjamin Petersonbe17a112008-09-27 21:49:47 +00001067def test_main():
Serhiy Storchaka43767632013-11-03 21:31:38 +02001068 tests = [TestFTPClass, TestTimeouts, TestNetrcDeprecation,
1069 TestIPv6Environment,
1070 TestTLS_FTPClassMixin, TestTLS_FTPClass]
Antoine Pitrouf988cd02009-11-17 20:21:14 +00001071
Benjamin Petersonbe17a112008-09-27 21:49:47 +00001072 thread_info = support.threading_setup()
1073 try:
1074 support.run_unittest(*tests)
1075 finally:
1076 support.threading_cleanup(*thread_info)
1077
Guido van Rossumd8faa362007-04-27 19:54:29 +00001078
1079if __name__ == '__main__':
1080 test_main()