Selaa lähdekoodia

Merge branch 'master' of github.com:jlaffaye/ftp

Julien Laffaye 5 vuotta sitten
vanhempi
commit
b9f3ade291
3 muutettua tiedostoa jossa 58 lisäystä ja 5 poistoa
  1. 21 0
      client_test.go
  2. 16 5
      conn_test.go
  3. 21 0
      ftp.go

+ 21 - 0
client_test.go

@@ -113,6 +113,27 @@ func testConn(t *testing.T, disableEPSV bool) {
 		r.Close()
 	}
 
+	data2 := bytes.NewBufferString(testData)
+	err = c.Append("tset", data2)
+	if err != nil {
+		t.Error(err)
+	}
+
+	// Read without deadline, after append
+	r, err = c.Retr("tset")
+	if err != nil {
+		t.Error(err)
+	} else {
+		buf, err := ioutil.ReadAll(r)
+		if err != nil {
+			t.Error(err)
+		}
+		if string(buf) != testData+testData {
+			t.Errorf("'%s'", buf)
+		}
+		r.Close()
+	}
+
 	fileSize, err := c.FileSize("magic-file")
 	if err != nil {
 		t.Error(err)

+ 16 - 5
conn_test.go

@@ -1,9 +1,9 @@
 package ftp
 
 import (
+	"bytes"
 	"errors"
 	"io"
-	"io/ioutil"
 	"net"
 	"net/textproto"
 	"reflect"
@@ -19,6 +19,7 @@ type ftpMock struct {
 	proto    *textproto.Conn
 	commands []string // list of received commands
 	rest     int
+	fileCont *bytes.Buffer
 	dataConn *mockDataConn
 	sync.WaitGroup
 }
@@ -135,7 +136,14 @@ func (mock *ftpMock) listen(t *testing.T) {
 				break
 			}
 			mock.proto.Writer.PrintfLine("150 please send")
-			mock.recvDataConn()
+			mock.recvDataConn(false)
+		case "APPE":
+			if mock.dataConn == nil {
+				mock.proto.Writer.PrintfLine("425 Unable to build data connection: Connection refused")
+				break
+			}
+			mock.proto.Writer.PrintfLine("150 please send")
+			mock.recvDataConn(true)
 		case "LIST":
 			if mock.dataConn == nil {
 				mock.proto.Writer.PrintfLine("425 Unable to build data connection: Connection refused")
@@ -166,7 +174,7 @@ func (mock *ftpMock) listen(t *testing.T) {
 
 			mock.dataConn.Wait()
 			mock.proto.Writer.PrintfLine("150 Opening ASCII mode data connection for file list")
-			mock.dataConn.conn.Write([]byte(testData[mock.rest:]))
+			mock.dataConn.conn.Write(mock.fileCont.Bytes()[mock.rest:])
 			mock.rest = 0
 			mock.proto.Writer.PrintfLine("226 Transfer complete")
 			mock.closeDataConn()
@@ -268,9 +276,12 @@ func (mock *ftpMock) listenDataConn() (int64, error) {
 	return p, nil
 }
 
-func (mock *ftpMock) recvDataConn() {
+func (mock *ftpMock) recvDataConn(append bool) {
 	mock.dataConn.Wait()
-	io.Copy(ioutil.Discard, mock.dataConn.conn)
+	if !append {
+		mock.fileCont = new(bytes.Buffer)
+	}
+	io.Copy(mock.fileCont, mock.dataConn.conn)
 	mock.proto.Writer.PrintfLine("226 Transfer Complete")
 	mock.closeDataConn()
 }

+ 21 - 0
ftp.go

@@ -657,6 +657,27 @@ func (c *ServerConn) StorFrom(path string, r io.Reader, offset uint64) error {
 	return err
 }
 
+// Append issues a APPE FTP command to store a file to the remote FTP server.
+// If a file already exists with the given path, then the content of the
+// io.Reader is appended. Otherwise, a new file is created with that content.
+//
+// Hint: io.Pipe() can be used if an io.Writer is required.
+func (c *ServerConn) Append(path string, r io.Reader) error {
+	conn, err := c.cmdDataConnFrom(0, "APPE %s", path)
+	if err != nil {
+		return err
+	}
+
+	_, err = io.Copy(conn, r)
+	conn.Close()
+	if err != nil {
+		return err
+	}
+
+	_, _, err = c.conn.ReadResponse(StatusClosingDataConnection)
+	return err
+}
+
 // Rename renames a file on the remote FTP server.
 func (c *ServerConn) Rename(from, to string) error {
 	_, _, err := c.cmd(StatusRequestFilePending, "RNFR %s", from)