From 8d3a48a8c710e2c9db438e24f7e6e75a7234b06c Mon Sep 17 00:00:00 2001 From: Arnaud Ysmal Date: Wed, 4 Dec 2013 23:30:50 +0100 Subject: [PATCH] The REST command must precede RETR/STOR --- ftp.go | 37 +++++++++++++++++++++++++++++++------ 1 file changed, 31 insertions(+), 6 deletions(-) diff --git a/ftp.go b/ftp.go index 93d7dfa..87007b0 100644 --- a/ftp.go +++ b/ftp.go @@ -232,13 +232,21 @@ func (c *ServerConn) cmd(expected int, format string, args ...interface{}) (int, return code, line, err } -// cmdDataConn executes a command which require a FTP data connection. -func (c *ServerConn) cmdDataConn(format string, args ...interface{}) (net.Conn, error) { +// cmdDataConnFrom executes a command which require a FTP data connection. +// Issues a REST FTP command to specify the number of bytes to skip for the transfer. +func (c *ServerConn) cmdDataConnFrom(offset uint64, format string, args ...interface{}) (net.Conn, error) { conn, err := c.openDataConn() if err != nil { return nil, err } + if offset != 0 { + _, _, err := c.cmd(StatusRequestFilePending, "REST %d", offset) + if err != nil { + return nil, err + } + } + _, err = c.conn.Cmd(format, args...) if err != nil { conn.Close() @@ -304,7 +312,7 @@ func parseListLine(line string) (*Entry, error) { // NameList issues an NLST FTP command. func (c *ServerConn) NameList(path string) (entries []string, err error) { - conn, err := c.cmdDataConn("NLST %s", path) + conn, err := c.cmdDataConnFrom(0, "NLST %s", path) if err != nil { return } @@ -324,7 +332,7 @@ func (c *ServerConn) NameList(path string) (entries []string, err error) { // List issues a LIST FTP command. func (c *ServerConn) List(path string) (entries []*Entry, err error) { - conn, err := c.cmdDataConn("LIST %s", path) + conn, err := c.cmdDataConnFrom(0, "LIST %s", path) if err != nil { return } @@ -386,7 +394,15 @@ func (c *ServerConn) CurrentDir() (string, error) { // // The returned ReadCloser must be closed to cleanup the FTP data connection. func (c *ServerConn) Retr(path string) (io.ReadCloser, error) { - conn, err := c.cmdDataConn("RETR %s", path) + return c.RetrFrom(path, 0) +} + +// Retr issues a RETR FTP command to fetch the specified file from the remote +// FTP server, the server will not send the offset first bytes of the file. +// +// The returned ReadCloser must be closed to cleanup the FTP data connection. +func (c *ServerConn) RetrFrom(path string, offset uint64) (io.ReadCloser, error) { + conn, err := c.cmdDataConnFrom(offset, "RETR %s", path) if err != nil { return nil, err } @@ -400,7 +416,16 @@ func (c *ServerConn) Retr(path string) (io.ReadCloser, error) { // // Hint: io.Pipe() can be used if an io.Writer is required. func (c *ServerConn) Stor(path string, r io.Reader) error { - conn, err := c.cmdDataConn("STOR %s", path) + return c.StorFrom(path, r, 0) +} + +// Stor issues a STOR FTP command to store a file to the remote FTP server. +// Stor creates the specified file with the content of the io.Reader, writing +// on the server will start at the given file offset. +// +// Hint: io.Pipe() can be used if an io.Writer is required. +func (c *ServerConn) StorFrom(path string, r io.Reader, offset uint64) error { + conn, err := c.cmdDataConnFrom(offset, "STOR %s", path) if err != nil { return err }