@@ -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:
102102 sock = None
103103 file = None
104104 welcome = 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())
112109 def __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
114118 self .source_address = source_address
115119 self .timeout = timeout
116120 if host :
@@ -146,6 +150,8 @@ def connect(self, host='', port=0, timeout=-999, source_address=None):
146150 self .port = port
147151 if timeout != - 999 :
148152 self .timeout = timeout
153+ if self .timeout is not None and not self .timeout :
154+ raise ValueError ('Non-blocking socket (timeout=0) is not supported' )
149155 if source_address is not None :
150156 self .source_address = source_address
151157 sys .audit ("ftplib.connect" , self , self .host , self .port )
@@ -316,8 +322,13 @@ def makeport(self):
316322 return sock
317323
318324 def makepasv (self ):
325+ """Internal: Does the PASV or EPSV handshake -> (address, port)"""
319326 if 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 ]
321332 else :
322333 host , port = parse229 (self .sendcmd ('EPSV' ), self .sock .getpeername ())
323334 return host , port
@@ -704,9 +715,10 @@ class FTP_TLS(FTP):
704715 '''
705716 ssl_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' ):
710722 if context is not None and keyfile is not None :
711723 raise ValueError ("context and keyfile arguments are mutually "
712724 "exclusive" )
@@ -725,12 +737,13 @@ def __init__(self, host='', user='', passwd='', acct='', keyfile=None,
725737 keyfile = keyfile )
726738 self .context = context
727739 self ._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
730743 def login (self , user = '' , passwd = '' , acct = '' , secure = True ):
731744 if secure and not isinstance (self .sock , ssl .SSLSocket ):
732745 self .auth ()
733- return FTP .login (self , user , passwd , acct )
746+ return super () .login (user , passwd , acct )
734747
735748 def auth (self ):
736749 '''Set up secure control connection by using TLS/SSL.'''
@@ -740,8 +753,7 @@ def auth(self):
740753 resp = self .voidcmd ('AUTH TLS' )
741754 else :
742755 resp = 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 )
745757 self .file = self .sock .makefile (mode = 'r' , encoding = self .encoding )
746758 return resp
747759
@@ -778,7 +790,7 @@ def prot_c(self):
778790 # --- Overridden FTP methods
779791
780792 def ntransfercmd (self , cmd , rest = None ):
781- conn , size = FTP .ntransfercmd (self , cmd , rest )
793+ conn , size = super () .ntransfercmd (cmd , rest )
782794 if self ._prot_p :
783795 conn = self .context .wrap_socket (conn ,
784796 server_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-
827838 if resp [:3 ] != '227' :
828839 raise error_reply (resp )
829840 global _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-
847857 if resp [:3 ] != '229' :
848858 raise error_reply (resp )
849859 left = 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-
869878 if resp [:3 ] != '257' :
870879 raise error_reply (resp )
871880 if resp [3 :5 ] != ' "' :
0 commit comments