Added support for RFC 959's REST command (restart), closing SF patch

#101187, which some modifications.  Specifically,

ntransfercmd(), transfercmd(), and retrbinary() all grow an optional
`rest' argument, which if not None, is used as the argument to an FTP
REST comman dbefore the socket is returned.  Differences from the SF
patch:

- always compare against None with `is' or `is not' instead of == or !=

- no parens around conditional

- RFC 959 defines the argument to REST is a string containing any
  ASCII characters in the range [33..126].  Therefore, we use the %s
  format character instead of %f or %d as suggested in the patch's
  comments.  Note that we do /not/ sanity checkthe contents of the
  rest argument (but we'll document this in the library reference
  manual).
This commit is contained in:
Barry Warsaw 2000-09-01 06:09:23 +00:00
parent e0d9a83bea
commit 100d81e8e3
1 changed files with 38 additions and 23 deletions

View File

@ -1,7 +1,6 @@
"""An FTP client class and some helper functions. """An FTP client class and some helper functions.
Based on RFC 959: File Transfer Protocol Based on RFC 959: File Transfer Protocol (FTP), by J. Postel and J. Reynolds
(FTP), by J. Postel and J. Reynolds
Example: Example:
@ -235,7 +234,9 @@ class FTP:
return self.voidresp() return self.voidresp()
def sendport(self, host, port): def sendport(self, host, port):
'''Send a PORT command with the current host and the given port number.''' '''Send a PORT command with the current host and the given
port number.
'''
hbytes = string.splitfields(host, '.') hbytes = string.splitfields(host, '.')
pbytes = [`port/256`, `port%256`] pbytes = [`port/256`, `port%256`]
bytes = hbytes + pbytes bytes = hbytes + pbytes
@ -253,25 +254,35 @@ class FTP:
resp = self.sendport(host, port) resp = self.sendport(host, port)
return sock return sock
def ntransfercmd(self, cmd): def ntransfercmd(self, cmd, rest=None):
'''Initiate a transfer over the data connection. """Initiate a transfer over the data connection.
If the transfer is active, send a port command and
the transfer command, and accept the connection. If the transfer is active, send a port command and the
If the server is passive, send a pasv command, connect transfer command, and accept the connection. If the server is
to it, and start the transfer command. passive, send a pasv command, connect to it, and start the
Either way, return the socket for the connection and transfer command. Either way, return the socket for the
the expected size of the transfer. The expected size connection and the expected size of the transfer. The
may be None if it could not be determined.''' expected size may be None if it could not be determined.
Optional `rest' argument can be a string that is sent as the
argument to a RESTART command. This is essentially a server
marker used to tell the server to skip over any data up to the
given marker.
"""
size = None size = None
if self.passiveserver: if self.passiveserver:
host, port = parse227(self.sendcmd('PASV')) host, port = parse227(self.sendcmd('PASV'))
conn = socket.socket(socket.AF_INET, socket.SOCK_STREAM) conn=socket.socket(socket.AF_INET, socket.SOCK_STREAM)
conn.connect((host, port)) conn.connect((host, port))
if rest is not None:
self.sendcmd("REST %s" % rest)
resp = self.sendcmd(cmd) resp = self.sendcmd(cmd)
if resp[0] <> '1': if resp[0] <> '1':
raise error_reply, resp raise error_reply, resp
else: else:
sock = self.makeport() sock = self.makeport()
if rest is not None:
self.sendcmd("REST %s" % rest)
resp = self.sendcmd(cmd) resp = self.sendcmd(cmd)
if resp[0] <> '1': if resp[0] <> '1':
raise error_reply, resp raise error_reply, resp
@ -281,10 +292,9 @@ class FTP:
size = parse150(resp) size = parse150(resp)
return conn, size return conn, size
def transfercmd(self, cmd): def transfercmd(self, cmd, rest=None):
'''Initiate a transfer over the data connection. Returns """Like nstransfercmd() but returns only the socket."""
the socket for the connection. See also ntransfercmd().''' return self.ntransfercmd(cmd, rest)[0]
return self.ntransfercmd(cmd)[0]
def login(self, user = '', passwd = '', acct = ''): def login(self, user = '', passwd = '', acct = ''):
'''Login, default anonymous.''' '''Login, default anonymous.'''
@ -312,13 +322,18 @@ class FTP:
raise error_reply, resp raise error_reply, resp
return resp return resp
def retrbinary(self, cmd, callback, blocksize=8192): def retrbinary(self, cmd, callback, blocksize=8192, rest=None):
'''Retrieve data in binary mode. """Retrieve data in binary mode.
The argument is a RETR command.
The callback function is called for each block. `cmd' is a RETR command. `callback' is a callback function is
This creates a new port for you''' called for each block. No more than `blocksize' number of
bytes will be read from the socket. Optional `rest' is passed
to transfercmd().
A new port is created for you. Return the response code.
"""
self.voidcmd('TYPE I') self.voidcmd('TYPE I')
conn = self.transfercmd(cmd) conn = self.transfercmd(cmd, rest)
while 1: while 1:
data = conn.recv(blocksize) data = conn.recv(blocksize)
if not data: if not data: