Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Appearance settings

Commit 5384a47

Browse filesBrowse files
authored
Merge pull request #5073 from dvermd/ftplib_311
Update ftplib to CPython 3.11.5
2 parents a12233e + 0a76a9b commit 5384a47
Copy full SHA for 5384a47

File tree

2 files changed

+149
-70
lines changed
Filter options

2 files changed

+149
-70
lines changed

‎Lib/ftplib.py

Copy file name to clipboardExpand all lines: Lib/ftplib.py
+32-23Lines changed: 32 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -72,17 +72,17 @@ class error_proto(Error): pass # response does not begin with [1-5]
7272

7373
# The class itself
7474
class 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

Comments
0 (0)
Morty Proxy This is a proxified and sanitized view of the page, visit original site.