@@ -72,17 +72,17 @@ class error_proto(Error): pass # response does not begin with [1-5]
72
72
73
73
# The class itself
74
74
class FTP :
75
-
76
75
'''An FTP client class.
77
76
78
77
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
80
79
81
80
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).
84
83
If a timeout is passed, then this is now the default timeout for all ftp
85
84
socket operations for this instance.
85
+ The last parameter is the encoding of filenames, which defaults to utf-8.
86
86
87
87
Then use self.connect() with optional host and port argument.
88
88
@@ -102,15 +102,19 @@ class FTP:
102
102
sock = None
103
103
file = None
104
104
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
107
108
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())
112
109
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
114
118
self .source_address = source_address
115
119
self .timeout = timeout
116
120
if host :
@@ -146,6 +150,8 @@ def connect(self, host='', port=0, timeout=-999, source_address=None):
146
150
self .port = port
147
151
if timeout != - 999 :
148
152
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' )
149
155
if source_address is not None :
150
156
self .source_address = source_address
151
157
sys .audit ("ftplib.connect" , self , self .host , self .port )
@@ -316,8 +322,13 @@ def makeport(self):
316
322
return sock
317
323
318
324
def makepasv (self ):
325
+ """Internal: Does the PASV or EPSV handshake -> (address, port)"""
319
326
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 ]
321
332
else :
322
333
host , port = parse229 (self .sendcmd ('EPSV' ), self .sock .getpeername ())
323
334
return host , port
@@ -704,9 +715,10 @@ class FTP_TLS(FTP):
704
715
'''
705
716
ssl_version = ssl .PROTOCOL_TLS_CLIENT
706
717
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' ):
710
722
if context is not None and keyfile is not None :
711
723
raise ValueError ("context and keyfile arguments are mutually "
712
724
"exclusive" )
@@ -725,12 +737,13 @@ def __init__(self, host='', user='', passwd='', acct='', keyfile=None,
725
737
keyfile = keyfile )
726
738
self .context = context
727
739
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 )
729
742
730
743
def login (self , user = '' , passwd = '' , acct = '' , secure = True ):
731
744
if secure and not isinstance (self .sock , ssl .SSLSocket ):
732
745
self .auth ()
733
- return FTP .login (self , user , passwd , acct )
746
+ return super () .login (user , passwd , acct )
734
747
735
748
def auth (self ):
736
749
'''Set up secure control connection by using TLS/SSL.'''
@@ -740,8 +753,7 @@ def auth(self):
740
753
resp = self .voidcmd ('AUTH TLS' )
741
754
else :
742
755
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 )
745
757
self .file = self .sock .makefile (mode = 'r' , encoding = self .encoding )
746
758
return resp
747
759
@@ -778,7 +790,7 @@ def prot_c(self):
778
790
# --- Overridden FTP methods
779
791
780
792
def ntransfercmd (self , cmd , rest = None ):
781
- conn , size = FTP .ntransfercmd (self , cmd , rest )
793
+ conn , size = super () .ntransfercmd (cmd , rest )
782
794
if self ._prot_p :
783
795
conn = self .context .wrap_socket (conn ,
784
796
server_hostname = self .host )
@@ -823,7 +835,6 @@ def parse227(resp):
823
835
'''Parse the '227' response for a PASV request.
824
836
Raises error_proto if it does not contain '(h1,h2,h3,h4,p1,p2)'
825
837
Return ('host.addr.as.numbers', port#) tuple.'''
826
-
827
838
if resp [:3 ] != '227' :
828
839
raise error_reply (resp )
829
840
global _227_re
@@ -843,7 +854,6 @@ def parse229(resp, peer):
843
854
'''Parse the '229' response for an EPSV request.
844
855
Raises error_proto if it does not contain '(|||port|)'
845
856
Return ('host.addr.as.numbers', port#) tuple.'''
846
-
847
857
if resp [:3 ] != '229' :
848
858
raise error_reply (resp )
849
859
left = resp .find ('(' )
@@ -865,7 +875,6 @@ def parse257(resp):
865
875
'''Parse the '257' response for a MKD or PWD request.
866
876
This is a response to a MKD or PWD request: a directory name.
867
877
Returns the directoryname in the 257 reply.'''
868
-
869
878
if resp [:3 ] != '257' :
870
879
raise error_reply (resp )
871
880
if resp [3 :5 ] != ' "' :
0 commit comments