@@ -72,17 +72,17 @@ class error_proto(Error): pass # response does not begin with [1-5]
7272
7373# The class itself
7474class FTP :
75-
7675'''An FTP client class.
7776
7877 To create a connection, call the class using these arguments:
79- host, user, passwd, acct, timeout
78+ host, user, passwd, acct, timeout, source_address, encoding
8079
8180 The first four arguments are all strings, and have default value ''.
82- timeout must be numeric and defaults to None if not passed,
83- meaning that no timeout will be set on any ftp socket(s)
81+ The parameter ´ timeout´ must be numeric and defaults to None if not
82+ passed, meaning that no timeout will be set on any ftp socket(s).
8483 If a timeout is passed, then this is now the default timeout for all ftp
8584 socket operations for this instance.
85+ The last parameter is the encoding of filenames, which defaults to utf-8.
8686
8787 Then use self.connect() with optional host and port argument.
8888
@@ -102,15 +102,19 @@ class FTP:
102102sock = None
103103file = None
104104welcome = None
105- passiveserver = 1
106- encoding = "latin-1"
105+ passiveserver = True
106+ # Disables https://bugs.python.org/issue43285 security if set to True.
107+ trust_server_pasv_ipv4_address = False
107108
108- # Initialization method (called by class instantiation).
109- # Initialize host to localhost, port to standard ftp port
110- # Optional arguments are host (for connect()),
111- # and user, passwd, acct (for login())
112109def __init__ (self ,host = '' ,user = '' ,passwd = '' ,acct = '' ,
113- timeout = _GLOBAL_DEFAULT_TIMEOUT ,source_address = None ):
110+ timeout = _GLOBAL_DEFAULT_TIMEOUT ,source_address = None ,* ,
111+ encoding = 'utf-8' ):
112+ """Initialization method (called by class instantiation).
113+ Initialize host to localhost, port to standard ftp port.
114+ Optional arguments are host (for connect()),
115+ and user, passwd, acct (for login()).
116+ """
117+ self .encoding = encoding
114118self .source_address = source_address
115119self .timeout = timeout
116120if host :
@@ -146,6 +150,8 @@ def connect(self, host='', port=0, timeout=-999, source_address=None):
146150self .port = port
147151if timeout != - 999 :
148152self .timeout = timeout
153+ if self .timeout is not None and not self .timeout :
154+ raise ValueError ('Non-blocking socket (timeout=0) is not supported' )
149155if source_address is not None :
150156self .source_address = source_address
151157sys .audit ("ftplib.connect" ,self ,self .host ,self .port )
@@ -316,8 +322,13 @@ def makeport(self):
316322return sock
317323
318324def makepasv (self ):
325+ """Internal: Does the PASV or EPSV handshake -> (address, port)"""
319326if self .af == socket .AF_INET :
320- host ,port = parse227 (self .sendcmd ('PASV' ))
327+ untrusted_host ,port = parse227 (self .sendcmd ('PASV' ))
328+ if self .trust_server_pasv_ipv4_address :
329+ host = untrusted_host
330+ else :
331+ host = self .sock .getpeername ()[0 ]
321332else :
322333host ,port = parse229 (self .sendcmd ('EPSV' ),self .sock .getpeername ())
323334return host ,port
@@ -704,9 +715,10 @@ class FTP_TLS(FTP):
704715 '''
705716ssl_version = ssl .PROTOCOL_TLS_CLIENT
706717
707- def __init__ (self ,host = '' ,user = '' ,passwd = '' ,acct = '' ,keyfile = None ,
708- certfile = None ,context = None ,
709- timeout = _GLOBAL_DEFAULT_TIMEOUT ,source_address = None ):
718+ def __init__ (self ,host = '' ,user = '' ,passwd = '' ,acct = '' ,
719+ keyfile = None ,certfile = None ,context = None ,
720+ timeout = _GLOBAL_DEFAULT_TIMEOUT ,source_address = None ,* ,
721+ encoding = 'utf-8' ):
710722if context is not None and keyfile is not None :
711723raise ValueError ("context and keyfile arguments are mutually "
712724"exclusive" )
@@ -725,12 +737,13 @@ def __init__(self, host='', user='', passwd='', acct='', keyfile=None,
725737keyfile = keyfile )
726738self .context = context
727739self ._prot_p = False
728- FTP .__init__ (self ,host ,user ,passwd ,acct ,timeout ,source_address )
740+ super ().__init__ (host ,user ,passwd ,acct ,
741+ timeout ,source_address ,encoding = encoding )
729742
730743def login (self ,user = '' ,passwd = '' ,acct = '' ,secure = True ):
731744if secure and not isinstance (self .sock ,ssl .SSLSocket ):
732745self .auth ()
733- return FTP .login (self , user ,passwd ,acct )
746+ return super () .login (user ,passwd ,acct )
734747
735748def auth (self ):
736749'''Set up secure control connection by using TLS/SSL.'''
@@ -740,8 +753,7 @@ def auth(self):
740753resp = self .voidcmd ('AUTH TLS' )
741754else :
742755resp = self .voidcmd ('AUTH SSL' )
743- self .sock = self .context .wrap_socket (self .sock ,
744- server_hostname = self .host )
756+ self .sock = self .context .wrap_socket (self .sock ,server_hostname = self .host )
745757self .file = self .sock .makefile (mode = 'r' ,encoding = self .encoding )
746758return resp
747759
@@ -778,7 +790,7 @@ def prot_c(self):
778790# --- Overridden FTP methods
779791
780792def ntransfercmd (self ,cmd ,rest = None ):
781- conn ,size = FTP .ntransfercmd (self , cmd ,rest )
793+ conn ,size = super () .ntransfercmd (cmd ,rest )
782794if self ._prot_p :
783795conn = self .context .wrap_socket (conn ,
784796server_hostname = self .host )
@@ -823,7 +835,6 @@ def parse227(resp):
823835'''Parse the '227' response for a PASV request.
824836 Raises error_proto if it does not contain '(h1,h2,h3,h4,p1,p2)'
825837 Return ('host.addr.as.numbers', port#) tuple.'''
826-
827838if resp [:3 ]!= '227' :
828839raise error_reply (resp )
829840global _227_re
@@ -843,7 +854,6 @@ def parse229(resp, peer):
843854'''Parse the '229' response for an EPSV request.
844855 Raises error_proto if it does not contain '(|||port|)'
845856 Return ('host.addr.as.numbers', port#) tuple.'''
846-
847857if resp [:3 ]!= '229' :
848858raise error_reply (resp )
849859left = resp .find ('(' )
@@ -865,7 +875,6 @@ def parse257(resp):
865875'''Parse the '257' response for a MKD or PWD request.
866876 This is a response to a MKD or PWD request: a directory name.
867877 Returns the directoryname in the 257 reply.'''
868-
869878if resp [:3 ]!= '257' :
870879raise error_reply (resp )
871880if resp [3 :5 ]!= ' "' :