]> git.r.bdr.sh - rbdr/mobius/commitdiff
Fix and refactor file transfers to handle resource forks
authorJeff Halter <redacted>
Tue, 31 May 2022 04:37:19 +0000 (21:37 -0700)
committerJeff Halter <redacted>
Tue, 31 May 2022 04:37:19 +0000 (21:37 -0700)
hotline/file_store.go
hotline/flattened_file_object.go
hotline/flattened_file_object_test.go
hotline/server.go
hotline/transaction_handlers.go
hotline/transfer.go

index e9d083f6b0a8af29354d224c6f8557fc427c22f2..ed0ffa492448cc05b5fec976005af9aee1936870 100644 (file)
@@ -13,6 +13,7 @@ type FileStore interface {
        Open(name string) (*os.File, error)
        Symlink(oldname, newname string) error
        Remove(name string) error
        Open(name string) (*os.File, error)
        Symlink(oldname, newname string) error
        Remove(name string) error
+       Create(name string) (*os.File, error)
        // TODO: implement these
        // Rename(oldpath string, newpath string) error
        // RemoveAll(path string) error
        // TODO: implement these
        // Rename(oldpath string, newpath string) error
        // RemoveAll(path string) error
@@ -40,6 +41,10 @@ func (fs *OSFileStore) Remove(name string) error {
        return os.Remove(name)
 }
 
        return os.Remove(name)
 }
 
+func (fs *OSFileStore) Create(name string) (*os.File, error) {
+       return os.Create(name)
+}
+
 type MockFileStore struct {
        mock.Mock
 }
 type MockFileStore struct {
        mock.Mock
 }
@@ -72,3 +77,8 @@ func (mfs *MockFileStore) Remove(name string) error {
        args := mfs.Called(name)
        return args.Error(0)
 }
        args := mfs.Called(name)
        return args.Error(0)
 }
+
+func (mfs *MockFileStore) Create(name string) (*os.File, error) {
+       args := mfs.Called(name)
+       return args.Get(0).(*os.File), args.Error(1)
+}
index fae8ab8051f6588288f9b81962d95937cb4dcece..ba6b0633160c9ec8728d81fcff5142e8a1c75b56 100644 (file)
@@ -18,7 +18,7 @@ type FlatFileHeader struct {
        Format    [4]byte  // Always "FILP"
        Version   [2]byte  // Always 1
        RSVD      [16]byte // Always empty zeros
        Format    [4]byte  // Always "FILP"
        Version   [2]byte  // Always 1
        RSVD      [16]byte // Always empty zeros
-       ForkCount [2]byte  // Always 2
+       ForkCount [2]byte  // Number of forks
 }
 
 // NewFlatFileHeader returns a FlatFileHeader struct
 }
 
 // NewFlatFileHeader returns a FlatFileHeader struct
@@ -33,10 +33,10 @@ func NewFlatFileHeader() FlatFileHeader {
 
 // FlatFileInformationForkHeader is the second section of a "Flattened File Object"
 type FlatFileInformationForkHeader struct {
 
 // FlatFileInformationForkHeader is the second section of a "Flattened File Object"
 type FlatFileInformationForkHeader struct {
-       ForkType        []byte // Always "INFO"
-       CompressionType []byte // Always 0; Compression was never implemented in the Hotline protocol
-       RSVD            []byte // Always zeros
-       DataSize        []byte // Size of the flat file information fork
+       ForkType        [4]byte // Always "INFO"
+       CompressionType [4]byte // Always 0; Compression was never implemented in the Hotline protocol
+       RSVD            [4]byte // Always zeros
+       DataSize        [4]byte // Size of the flat file information fork
 }
 
 type FlatFileInformationFork struct {
 }
 
 type FlatFileInformationFork struct {
@@ -74,7 +74,7 @@ func NewFlatFileInformationFork(fileName string, modifyTime []byte) FlatFileInfo
 
 // DataSize calculates the size of the flat file information fork, which is
 // 72 bytes for the fixed length fields plus the length of the Name + Comment
 
 // DataSize calculates the size of the flat file information fork, which is
 // 72 bytes for the fixed length fields plus the length of the Name + Comment
-func (ffif FlatFileInformationFork) DataSize() []byte {
+func (ffif *FlatFileInformationFork) DataSize() []byte {
        size := make([]byte, 4)
 
        // TODO: Can I do math directly on two byte slices?
        size := make([]byte, 4)
 
        // TODO: Can I do math directly on two byte slices?
@@ -85,9 +85,9 @@ func (ffif FlatFileInformationFork) DataSize() []byte {
        return size
 }
 
        return size
 }
 
-func (ffo flattenedFileObject) TransferSize() []byte {
+func (ffo *flattenedFileObject) TransferSize() []byte {
        payloadSize := len(ffo.BinaryMarshal())
        payloadSize := len(ffo.BinaryMarshal())
-       dataSize := binary.BigEndian.Uint32(ffo.FlatFileDataForkHeader.DataSize)
+       dataSize := binary.BigEndian.Uint32(ffo.FlatFileDataForkHeader.DataSize[:])
 
        transferSize := make([]byte, 4)
        binary.BigEndian.PutUint32(transferSize, dataSize+uint32(payloadSize))
 
        transferSize := make([]byte, 4)
        binary.BigEndian.PutUint32(transferSize, dataSize+uint32(payloadSize))
@@ -95,7 +95,7 @@ func (ffo flattenedFileObject) TransferSize() []byte {
        return transferSize
 }
 
        return transferSize
 }
 
-func (ffif FlatFileInformationFork) ReadNameSize() []byte {
+func (ffif *FlatFileInformationFork) ReadNameSize() []byte {
        size := make([]byte, 2)
        binary.BigEndian.PutUint16(size, uint16(len(ffif.Name)))
 
        size := make([]byte, 2)
        binary.BigEndian.PutUint16(size, uint16(len(ffif.Name)))
 
@@ -103,65 +103,45 @@ func (ffif FlatFileInformationFork) ReadNameSize() []byte {
 }
 
 type FlatFileDataForkHeader struct {
 }
 
 type FlatFileDataForkHeader struct {
-       ForkType        []byte
-       CompressionType []byte
-       RSVD            []byte
-       DataSize        []byte
+       ForkType        [4]byte
+       CompressionType [4]byte
+       RSVD            [4]byte
+       DataSize        [4]byte
 }
 
 }
 
-// ReadFlattenedFileObject parses a byte slice into a flattenedFileObject
-func ReadFlattenedFileObject(bytes []byte) flattenedFileObject {
-       nameSize := bytes[110:112]
+func (ffif *FlatFileInformationFork) UnmarshalBinary(b []byte) error {
+
+       nameSize := b[70:72]
        bs := binary.BigEndian.Uint16(nameSize)
 
        bs := binary.BigEndian.Uint16(nameSize)
 
-       nameEnd := 112 + bs
+       nameEnd := 72 + bs
 
 
-       commentSize := bytes[nameEnd : nameEnd+2]
+       commentSize := b[nameEnd : nameEnd+2]
        commentLen := binary.BigEndian.Uint16(commentSize)
 
        commentStartPos := int(nameEnd) + 2
        commentEndPos := int(nameEnd) + 2 + int(commentLen)
 
        commentLen := binary.BigEndian.Uint16(commentSize)
 
        commentStartPos := int(nameEnd) + 2
        commentEndPos := int(nameEnd) + 2 + int(commentLen)
 
-       comment := bytes[commentStartPos:commentEndPos]
-
-       // dataSizeField := bytes[nameEnd+14+commentLen : nameEnd+18+commentLen]
-       // dataSize := binary.BigEndian.Uint32(dataSizeField)
-
-       ffo := flattenedFileObject{
-               FlatFileHeader: NewFlatFileHeader(),
-               FlatFileInformationForkHeader: FlatFileInformationForkHeader{
-                       ForkType:        bytes[24:28],
-                       CompressionType: bytes[28:32],
-                       RSVD:            bytes[32:36],
-                       DataSize:        bytes[36:40],
-               },
-               FlatFileInformationFork: FlatFileInformationFork{
-                       Platform:         bytes[40:44],
-                       TypeSignature:    bytes[44:48],
-                       CreatorSignature: bytes[48:52],
-                       Flags:            bytes[52:56],
-                       PlatformFlags:    bytes[56:60],
-                       RSVD:             bytes[60:92],
-                       CreateDate:       bytes[92:100],
-                       ModifyDate:       bytes[100:108],
-                       NameScript:       bytes[108:110],
-                       NameSize:         bytes[110:112],
-                       Name:             bytes[112:nameEnd],
-                       CommentSize:      bytes[nameEnd : nameEnd+2],
-                       Comment:          comment,
-               },
-               FlatFileDataForkHeader: FlatFileDataForkHeader{
-                       ForkType:        bytes[commentEndPos : commentEndPos+4],
-                       CompressionType: bytes[commentEndPos+4 : commentEndPos+8],
-                       RSVD:            bytes[commentEndPos+8 : commentEndPos+12],
-                       DataSize:        bytes[commentEndPos+12 : commentEndPos+16],
-               },
-       }
-
-       return ffo
+       comment := b[commentStartPos:commentEndPos]
+
+       ffif.Platform = b[0:4]
+       ffif.TypeSignature = b[4:8]
+       ffif.CreatorSignature = b[8:12]
+       ffif.Flags = b[12:16]
+       ffif.PlatformFlags = b[16:20]
+       ffif.RSVD = b[20:52]
+       ffif.CreateDate = b[52:60]
+       ffif.ModifyDate = b[60:68]
+       ffif.NameScript = b[68:70]
+       ffif.NameSize = b[70:72]
+       ffif.Name = b[72:nameEnd]
+       ffif.CommentSize = b[nameEnd : nameEnd+2]
+       ffif.Comment = comment
+
+       return nil
 }
 
 }
 
-func (f flattenedFileObject) BinaryMarshal() []byte {
+func (f *flattenedFileObject) BinaryMarshal() []byte {
        var out []byte
        out = append(out, f.FlatFileHeader.Format[:]...)
        out = append(out, f.FlatFileHeader.Version[:]...)
        var out []byte
        out = append(out, f.FlatFileHeader.Format[:]...)
        out = append(out, f.FlatFileHeader.Version[:]...)
@@ -187,10 +167,10 @@ func (f flattenedFileObject) BinaryMarshal() []byte {
        out = append(out, f.FlatFileInformationFork.CommentSize...)
        out = append(out, f.FlatFileInformationFork.Comment...)
 
        out = append(out, f.FlatFileInformationFork.CommentSize...)
        out = append(out, f.FlatFileInformationFork.Comment...)
 
-       out = append(out, f.FlatFileDataForkHeader.ForkType...)
-       out = append(out, f.FlatFileDataForkHeader.CompressionType...)
-       out = append(out, f.FlatFileDataForkHeader.RSVD...)
-       out = append(out, f.FlatFileDataForkHeader.DataSize...)
+       out = append(out, f.FlatFileDataForkHeader.ForkType[:]...)
+       out = append(out, f.FlatFileDataForkHeader.CompressionType[:]...)
+       out = append(out, f.FlatFileDataForkHeader.RSVD[:]...)
+       out = append(out, f.FlatFileDataForkHeader.DataSize[:]...)
 
        return out
 }
 
        return out
 }
@@ -220,10 +200,10 @@ func NewFlattenedFileObject(fileRoot string, filePath, fileName []byte) (*flatte
                FlatFileHeader:          NewFlatFileHeader(),
                FlatFileInformationFork: NewFlatFileInformationFork(string(fileName), mTime),
                FlatFileDataForkHeader: FlatFileDataForkHeader{
                FlatFileHeader:          NewFlatFileHeader(),
                FlatFileInformationFork: NewFlatFileInformationFork(string(fileName), mTime),
                FlatFileDataForkHeader: FlatFileDataForkHeader{
-                       ForkType:        []byte("DATA"),
-                       CompressionType: []byte{0, 0, 0, 0},
-                       RSVD:            []byte{0, 0, 0, 0},
-                       DataSize:        dataSize,
+                       ForkType:        [4]byte{0x44, 0x41, 0x54, 0x41}, // "DATA"
+                       CompressionType: [4]byte{},
+                       RSVD:            [4]byte{},
+                       DataSize:        [4]byte{dataSize[0], dataSize[1], dataSize[2], dataSize[3]},
                },
        }, nil
 }
                },
        }, nil
 }
index 12b6d32c260fe45696ff16b4e1dd728970f7c9d4..300eb343c3f0d7ee31a0b3206d999961458a9a64 100644 (file)
@@ -1,26 +1,12 @@
 package hotline
 
 import (
 package hotline
 
 import (
-       "bytes"
-       "encoding/hex"
        "fmt"
        "github.com/stretchr/testify/assert"
        "os"
        "testing"
 )
 
        "fmt"
        "github.com/stretchr/testify/assert"
        "os"
        "testing"
 )
 
-func TestReadFlattenedFileObject(t *testing.T) {
-       testData, _ := hex.DecodeString("46494c500001000000000000000000000000000000000002494e464f000000000000000000000052414d414354455854747478740000000000000100000000000000000000000000000000000000000000000000000000000000000007700000ba74247307700000ba74247300000008746573742e74787400004441544100000000000000000000000474657374")
-
-       ffo := ReadFlattenedFileObject(testData)
-
-       format := ffo.FlatFileHeader.Format[:]
-       want := []byte("FILP")
-       if !bytes.Equal(format, want) {
-               t.Errorf("ReadFlattenedFileObject() = %q, want %q", format, want)
-       }
-}
-
 func TestNewFlattenedFileObject(t *testing.T) {
        type args struct {
                fileRoot string
 func TestNewFlattenedFileObject(t *testing.T) {
        type args struct {
                fileRoot string
@@ -45,10 +31,10 @@ func TestNewFlattenedFileObject(t *testing.T) {
                                FlatFileInformationForkHeader: FlatFileInformationForkHeader{},
                                FlatFileInformationFork:       NewFlatFileInformationFork("testfile.txt", make([]byte, 8)),
                                FlatFileDataForkHeader: FlatFileDataForkHeader{
                                FlatFileInformationForkHeader: FlatFileInformationForkHeader{},
                                FlatFileInformationFork:       NewFlatFileInformationFork("testfile.txt", make([]byte, 8)),
                                FlatFileDataForkHeader: FlatFileDataForkHeader{
-                                       ForkType:        []byte("DATA"),
-                                       CompressionType: []byte{0, 0, 0, 0},
-                                       RSVD:            []byte{0, 0, 0, 0},
-                                       DataSize:        []byte{0x00, 0x00, 0x00, 0x17},
+                                       ForkType:        [4]byte{0x4d, 0x41, 0x43, 0x52}, // DATA
+                                       CompressionType: [4]byte{0, 0, 0, 0},
+                                       RSVD:            [4]byte{0, 0, 0, 0},
+                                       DataSize:        [4]byte{0x00, 0x00, 0x00, 0x17},
                                },
                                FileData: nil,
                        },
                                },
                                FileData: nil,
                        },
@@ -79,67 +65,3 @@ func TestNewFlattenedFileObject(t *testing.T) {
                })
        }
 }
                })
        }
 }
-
-func Test_flattenedFileObject_BinaryMarshal(t *testing.T) {
-
-       testData, _ := hex.DecodeString("46494c500001000000000000000000000000000000000002494e464f000000000000000000000052414d414354455854747478740000000000000100000000000000000000000000000000000000000000000000000000000000000007700000ba74247307700000ba74247300000008746573742e74787400004441544100000000000000000000000474657374")
-       testFile := ReadFlattenedFileObject(testData)
-       testFile.FlatFileInformationFork.Comment = []byte("test!")
-       testFile.FlatFileInformationFork.CommentSize = []byte{0x00, 0x05}
-
-       type fields struct {
-               FlatFileHeader                FlatFileHeader
-               FlatFileInformationForkHeader FlatFileInformationForkHeader
-               FlatFileInformationFork       FlatFileInformationFork
-               FlatFileDataForkHeader        FlatFileDataForkHeader
-               FileData                      []byte
-       }
-       tests := []struct {
-               name   string
-               fields fields
-               want   []byte
-       }{
-               {
-                       name: "with a valid file",
-                       fields: fields{
-                               FlatFileHeader:                testFile.FlatFileHeader,
-                               FlatFileInformationForkHeader: testFile.FlatFileInformationForkHeader,
-                               FlatFileInformationFork:       testFile.FlatFileInformationFork,
-                               FlatFileDataForkHeader:        testFile.FlatFileDataForkHeader,
-                               FileData:                      testFile.FileData,
-                       },
-                       want: []byte{
-                               0x46, 0x49, 0x4c, 0x50, 0x00, 0x01, 0x00, 0x00,
-                               0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-                               0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02,
-                               0x49, 0x4e, 0x46, 0x4f, 0x00, 0x00, 0x00, 0x00,
-                               0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x57,
-                               0x41, 0x4d, 0x41, 0x43, 0x54, 0x45, 0x58, 0x54,
-                               0x74, 0x74, 0x78, 0x74, 0x00, 0x00, 0x00, 0x00,
-                               0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00,
-                               0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-                               0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-                               0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-                               0x00, 0x00, 0x00, 0x00, 0x07, 0x70, 0x00, 0x00,
-                               0xba, 0x74, 0x24, 0x73, 0x07, 0x70, 0x00, 0x00,
-                               0xba, 0x74, 0x24, 0x73, 0x00, 0x00, 0x00, 0x08,
-                               0x74, 0x65, 0x73, 0x74, 0x2e, 0x74, 0x78, 0x74,
-                               0x00, 0x05, 0x74, 0x65, 0x73, 0x74, 0x21, 0x44,
-                               0x41, 0x54, 0x41, 0x00, 0x00, 0x00, 0x00, 0x00,
-                               0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04,
-                       },
-               },
-       }
-       for _, tt := range tests {
-               t.Run(tt.name, func(t *testing.T) {
-                       f := flattenedFileObject{
-                               FlatFileHeader:                tt.fields.FlatFileHeader,
-                               FlatFileInformationForkHeader: tt.fields.FlatFileInformationForkHeader,
-                               FlatFileInformationFork:       tt.fields.FlatFileInformationFork,
-                               FlatFileDataForkHeader:        tt.fields.FlatFileDataForkHeader,
-                               FileData:                      tt.fields.FileData,
-                       }
-                       assert.Equalf(t, tt.want, f.BinaryMarshal(), "BinaryMarshal()")
-               })
-       }
-}
index 7b409d5921fae8a1fcdc55fc12d33f2ebc7625ed..90b7ea019db4bc575b62253634a0e70fc19c6f90 100644 (file)
@@ -91,7 +91,7 @@ func (s *Server) ServeFileTransfers(ln net.Listener) error {
                }
 
                go func() {
                }
 
                go func() {
-                       if err := s.TransferFile(conn); err != nil {
+                       if err := s.handleFileTransfer(conn); err != nil {
                                s.Logger.Errorw("file transfer error", "reason", err)
                        }
                }()
                                s.Logger.Errorw("file transfer error", "reason", err)
                        }
                }()
@@ -613,8 +613,13 @@ const dlFldrActionSendFile = 1
 const dlFldrActionResumeFile = 2
 const dlFldrActionNextFile = 3
 
 const dlFldrActionResumeFile = 2
 const dlFldrActionNextFile = 3
 
-func (s *Server) TransferFile(conn net.Conn) error {
-       defer func() { _ = conn.Close() }()
+// handleFileTransfer receives a client net.Conn from the file transfer server, performs the requested transfer type, then closes the connection
+func (s *Server) handleFileTransfer(conn io.ReadWriteCloser) error {
+       defer func() {
+               if err := conn.Close(); err != nil {
+                       s.Logger.Errorw("error closing connection", "error", err)
+               }
+       }()
 
        txBuf := make([]byte, 16)
        _, err := conn.Read(txBuf)
 
        txBuf := make([]byte, 16)
        _, err := conn.Read(txBuf)
@@ -647,7 +652,7 @@ func (s *Server) TransferFile(conn net.Conn) error {
                        return err
                }
 
                        return err
                }
 
-               s.Logger.Infow("File download started", "filePath", fullFilePath, "transactionRef", fileTransfer.ReferenceNumber, "RemoteAddr", conn.RemoteAddr().String())
+               s.Logger.Infow("File download started", "filePath", fullFilePath, "transactionRef", fileTransfer.ReferenceNumber)
 
                // Start by sending flat file object to client
                if _, err := conn.Write(ffo.BinaryMarshal()); err != nil {
 
                // Start by sending flat file object to client
                if _, err := conn.Write(ffo.BinaryMarshal()); err != nil {
@@ -675,63 +680,20 @@ func (s *Server) TransferFile(conn net.Conn) error {
                        }
                }
        case FileUpload:
                        }
                }
        case FileUpload:
-               const buffSize = 1460
-
-               uploadBuf := make([]byte, buffSize)
-
-               _, err := conn.Read(uploadBuf)
-               if err != nil {
-                       return err
-               }
-
-               ffo := ReadFlattenedFileObject(uploadBuf)
-               payloadLen := len(ffo.BinaryMarshal())
-               fileSize := int(binary.BigEndian.Uint32(ffo.FlatFileDataForkHeader.DataSize))
-
                destinationFile := s.Config.FileRoot + ReadFilePath(fileTransfer.FilePath) + "/" + string(fileTransfer.FileName)
                destinationFile := s.Config.FileRoot + ReadFilePath(fileTransfer.FilePath) + "/" + string(fileTransfer.FileName)
-               s.Logger.Infow(
-                       "File upload started",
-                       "transactionRef", fileTransfer.ReferenceNumber,
-                       "RemoteAddr", conn.RemoteAddr().String(),
-                       "size", fileSize,
-                       "dstFile", destinationFile,
-               )
-
-               newFile, err := os.Create(destinationFile)
+               newFile, err := FS.Create(destinationFile)
                if err != nil {
                        return err
                }
                if err != nil {
                        return err
                }
-
                defer func() { _ = newFile.Close() }()
 
                defer func() { _ = newFile.Close() }()
 
-               if _, err := newFile.Write(uploadBuf[payloadLen:]); err != nil {
-                       return err
-               }
-               receivedBytes := buffSize - payloadLen
-
-               for {
-                       if (fileSize - receivedBytes) < buffSize {
-                               s.Logger.Infow(
-                                       "File upload complete",
-                                       "transactionRef", fileTransfer.ReferenceNumber,
-                                       "RemoteAddr", conn.RemoteAddr().String(),
-                                       "size", fileSize,
-                                       "dstFile", destinationFile,
-                               )
-
-                               if _, err := io.CopyN(newFile, conn, int64(fileSize-receivedBytes)); err != nil {
-                                       return fmt.Errorf("file transfer failed: %s", err)
-                               }
-                               return nil
-                       }
+               s.Logger.Infow("File upload started", "transactionRef", fileTransfer.ReferenceNumber, "dstFile", destinationFile)
 
 
-                       // Copy N bytes from conn to upload file
-                       n, err := io.CopyN(newFile, conn, buffSize)
-                       if err != nil {
-                               return err
-                       }
-                       receivedBytes += int(n)
+               if err := receiveFile(conn, newFile, nil); err != nil {
+                       s.Logger.Errorw("file upload error", "error", err)
                }
                }
+
+               s.Logger.Infow("File upload complete", "transactionRef", fileTransfer.ReferenceNumber, "dstFile", destinationFile)
        case FolderDownload:
                // Folder Download flow:
                // 1. Get filePath from the transfer
        case FolderDownload:
                // Folder Download flow:
                // 1. Get filePath from the transfer
@@ -767,22 +729,28 @@ func (s *Server) TransferFile(conn net.Conn) error {
 
                basePathLen := len(fullFilePath)
 
 
                basePathLen := len(fullFilePath)
 
-               readBuffer := make([]byte, 1024)
+               s.Logger.Infow("Start folder download", "path", fullFilePath, "ReferenceNumber", fileTransfer.ReferenceNumber)
 
 
-               s.Logger.Infow("Start folder download", "path", fullFilePath, "ReferenceNumber", fileTransfer.ReferenceNumber, "RemoteAddr", conn.RemoteAddr())
+               nextAction := make([]byte, 2)
+               if _, err := conn.Read(nextAction); err != nil {
+                       return err
+               }
 
                i := 0
 
                i := 0
-               _ = filepath.Walk(fullFilePath+"/", func(path string, info os.FileInfo, _ error) error {
+               err = filepath.Walk(fullFilePath+"/", func(path string, info os.FileInfo, err error) error {
+                       if err != nil {
+                               return err
+                       }
                        i += 1
                        i += 1
-                       subPath := path[basePathLen:]
+                       subPath := path[basePathLen+1:]
                        s.Logger.Infow("Sending fileheader", "i", i, "path", path, "fullFilePath", fullFilePath, "subPath", subPath, "IsDir", info.IsDir())
 
                        s.Logger.Infow("Sending fileheader", "i", i, "path", path, "fullFilePath", fullFilePath, "subPath", subPath, "IsDir", info.IsDir())
 
-                       fileHeader := NewFileHeader(subPath, info.IsDir())
-
                        if i == 1 {
                                return nil
                        }
 
                        if i == 1 {
                                return nil
                        }
 
+                       fileHeader := NewFileHeader(subPath, info.IsDir())
+
                        // Send the file header to client
                        if _, err := conn.Write(fileHeader.Payload()); err != nil {
                                s.Logger.Errorf("error sending file header: %v", err)
                        // Send the file header to client
                        if _, err := conn.Write(fileHeader.Payload()); err != nil {
                                s.Logger.Errorf("error sending file header: %v", err)
@@ -790,12 +758,14 @@ func (s *Server) TransferFile(conn net.Conn) error {
                        }
 
                        // Read the client's Next Action request
                        }
 
                        // Read the client's Next Action request
-                       // TODO: Remove hardcoded behavior and switch behaviors based on the next action send
-                       if _, err := conn.Read(readBuffer); err != nil {
+                       if _, err := conn.Read(nextAction); err != nil {
                                return err
                        }
                                return err
                        }
+                       if nextAction[1] == 3 {
+                               return nil
+                       }
 
 
-                       s.Logger.Infow("Client folder download action", "action", fmt.Sprintf("%X", readBuffer[0:2]))
+                       s.Logger.Infow("Client folder download action", "action", fmt.Sprintf("%X", nextAction[0:2]))
 
                        if info.IsDir() {
                                return nil
 
                        if info.IsDir() {
                                return nil
@@ -814,7 +784,6 @@ func (s *Server) TransferFile(conn net.Conn) error {
                        s.Logger.Infow("File download started",
                                "fileName", info.Name(),
                                "transactionRef", fileTransfer.ReferenceNumber,
                        s.Logger.Infow("File download started",
                                "fileName", info.Name(),
                                "transactionRef", fileTransfer.ReferenceNumber,
-                               "RemoteAddr", conn.RemoteAddr().String(),
                                "TransferSize", fmt.Sprintf("%x", ffo.TransferSize()),
                        )
 
                                "TransferSize", fmt.Sprintf("%x", ffo.TransferSize()),
                        )
 
@@ -824,7 +793,7 @@ func (s *Server) TransferFile(conn net.Conn) error {
                                return err
                        }
 
                                return err
                        }
 
-                       // Send file bytes to client
+                       // Send ffo bytes to client
                        if _, err := conn.Write(ffo.BinaryMarshal()); err != nil {
                                s.Logger.Error(err)
                                return err
                        if _, err := conn.Write(ffo.BinaryMarshal()); err != nil {
                                s.Logger.Error(err)
                                return err
@@ -835,28 +804,22 @@ func (s *Server) TransferFile(conn net.Conn) error {
                                return err
                        }
 
                                return err
                        }
 
-                       sendBuffer := make([]byte, 1048576)
-                       totalBytesSent := len(ffo.BinaryMarshal())
+                       // Copy N bytes from file to connection
+                       _, err = io.CopyN(conn, file, int64(binary.BigEndian.Uint32(ffo.FlatFileDataForkHeader.DataSize[:])))
+                       if err != nil {
+                               return err
+                       }
+                       file.Close()
 
 
-                       for {
-                               bytesRead, err := file.Read(sendBuffer)
-                               if err == io.EOF {
-                                       // Read the client's Next Action request
-                                       // TODO: Remove hardcoded behavior and switch behaviors based on the next action send
-                                       if _, err := conn.Read(readBuffer); err != nil {
-                                               s.Logger.Errorf("error reading next action: %v", err)
-                                               return err
-                                       }
-                                       break
-                               }
+                       // TODO: optionally send resource fork header and resource fork data
 
 
-                               sentBytes, readErr := conn.Write(sendBuffer[:bytesRead])
-                               totalBytesSent += sentBytes
-                               if readErr != nil {
-                                       return err
-                               }
+                       // Read the client's Next Action request
+                       if _, err := conn.Read(nextAction); err != nil {
+                               return err
                        }
                        }
-                       return nil
+                       // TODO: switch behavior based on possible next action
+
+                       return err
                })
 
        case FolderUpload:
                })
 
        case FolderUpload:
@@ -867,7 +830,6 @@ func (s *Server) TransferFile(conn net.Conn) error {
                s.Logger.Infow(
                        "Folder upload started",
                        "transactionRef", fileTransfer.ReferenceNumber,
                s.Logger.Infow(
                        "Folder upload started",
                        "transactionRef", fileTransfer.ReferenceNumber,
-                       "RemoteAddr", conn.RemoteAddr().String(),
                        "dstPath", dstPath,
                        "TransferSize", fmt.Sprintf("%x", fileTransfer.TransferSize),
                        "FolderItemCount", fileTransfer.FolderItemCount,
                        "dstPath", dstPath,
                        "TransferSize", fmt.Sprintf("%x", fileTransfer.TransferSize),
                        "FolderItemCount", fileTransfer.FolderItemCount,
@@ -881,8 +843,6 @@ func (s *Server) TransferFile(conn net.Conn) error {
                        }
                }
 
                        }
                }
 
-               readBuffer := make([]byte, 1024)
-
                // Begin the folder upload flow by sending the "next file action" to client
                if _, err := conn.Write([]byte{0, dlFldrActionNextFile}); err != nil {
                        return err
                // Begin the folder upload flow by sending the "next file action" to client
                if _, err := conn.Write([]byte{0, dlFldrActionNextFile}); err != nil {
                        return err
@@ -891,8 +851,10 @@ func (s *Server) TransferFile(conn net.Conn) error {
                fileSize := make([]byte, 4)
                itemCount := binary.BigEndian.Uint16(fileTransfer.FolderItemCount)
 
                fileSize := make([]byte, 4)
                itemCount := binary.BigEndian.Uint16(fileTransfer.FolderItemCount)
 
+               readBuffer := make([]byte, 1024)
                for i := uint16(0); i < itemCount; i++ {
                for i := uint16(0); i < itemCount; i++ {
-                       if _, err := conn.Read(readBuffer); err != nil {
+                       _, err := conn.Read(readBuffer)
+                       if err != nil {
                                return err
                        }
                        fu := readFolderUpload(readBuffer)
                                return err
                        }
                        fu := readFolderUpload(readBuffer)
@@ -900,7 +862,6 @@ func (s *Server) TransferFile(conn net.Conn) error {
                        s.Logger.Infow(
                                "Folder upload continued",
                                "transactionRef", fmt.Sprintf("%x", fileTransfer.ReferenceNumber),
                        s.Logger.Infow(
                                "Folder upload continued",
                                "transactionRef", fmt.Sprintf("%x", fileTransfer.ReferenceNumber),
-                               "RemoteAddr", conn.RemoteAddr().String(),
                                "FormattedPath", fu.FormattedPath(),
                                "IsFolder", fmt.Sprintf("%x", fu.IsFolder),
                                "PathItemCount", binary.BigEndian.Uint16(fu.PathItemCount[:]),
                                "FormattedPath", fu.FormattedPath(),
                                "IsFolder", fmt.Sprintf("%x", fu.IsFolder),
                                "PathItemCount", binary.BigEndian.Uint16(fu.PathItemCount[:]),
@@ -928,14 +889,21 @@ func (s *Server) TransferFile(conn net.Conn) error {
                                }
 
                                if _, err := conn.Read(fileSize); err != nil {
                                }
 
                                if _, err := conn.Read(fileSize); err != nil {
-                                       fmt.Println("Error reading:", err.Error()) // TODO: handle
+                                       return err
                                }
 
                                }
 
-                               s.Logger.Infow("Starting file transfer", "fileNum", i+1, "totalFiles", itemCount, "fileSize", fileSize)
+                               filePath := dstPath + "/" + fu.FormattedPath()
+                               s.Logger.Infow("Starting file transfer", "path", filePath, "fileNum", i+1, "totalFiles", itemCount, "fileSize", binary.BigEndian.Uint32(fileSize))
 
 
-                               if err := transferFile(conn, dstPath+"/"+fu.FormattedPath()); err != nil {
+                               newFile, err := FS.Create(filePath)
+                               if err != nil {
+                                       return err
+                               }
+
+                               if err := receiveFile(conn, newFile, ioutil.Discard); err != nil {
                                        s.Logger.Error(err)
                                }
                                        s.Logger.Error(err)
                                }
+                               _ = newFile.Close()
 
                                // Tell client to send next file
                                if _, err := conn.Write([]byte{0, dlFldrActionNextFile}); err != nil {
 
                                // Tell client to send next file
                                if _, err := conn.Write([]byte{0, dlFldrActionNextFile}); err != nil {
@@ -943,11 +911,6 @@ func (s *Server) TransferFile(conn net.Conn) error {
                                        return err
                                }
 
                                        return err
                                }
 
-                               // Client sends "MACR" after the file.  Read and discard.
-                               // TODO: This doesn't seem to be documented.  What is this?  Maybe resource fork?
-                               if _, err := conn.Read(readBuffer); err != nil {
-                                       return err
-                               }
                        }
                }
                s.Logger.Infof("Folder upload complete")
                        }
                }
                s.Logger.Infof("Folder upload complete")
@@ -956,43 +919,6 @@ func (s *Server) TransferFile(conn net.Conn) error {
        return nil
 }
 
        return nil
 }
 
-func transferFile(conn net.Conn, dst string) error {
-       const buffSize = 1024
-       buf := make([]byte, buffSize)
-
-       // Read first chunk of bytes from conn; this will be the Flat File Object and initial chunk of file bytes
-       if _, err := conn.Read(buf); err != nil {
-               return err
-       }
-       ffo := ReadFlattenedFileObject(buf)
-       payloadLen := len(ffo.BinaryMarshal())
-       fileSize := int(binary.BigEndian.Uint32(ffo.FlatFileDataForkHeader.DataSize))
-
-       newFile, err := os.Create(dst)
-       if err != nil {
-               return err
-       }
-       defer func() { _ = newFile.Close() }()
-       if _, err := newFile.Write(buf[payloadLen:]); err != nil {
-               return err
-       }
-       receivedBytes := buffSize - payloadLen
-
-       for {
-               if (fileSize - receivedBytes) < buffSize {
-                       _, err := io.CopyN(newFile, conn, int64(fileSize-receivedBytes))
-                       return err
-               }
-
-               // Copy N bytes from conn to upload file
-               n, err := io.CopyN(newFile, conn, buffSize)
-               if err != nil {
-                       return err
-               }
-               receivedBytes += int(n)
-       }
-}
-
 // sortedClients is a utility function that takes a map of *ClientConn and returns a sorted slice of the values.
 // The purpose of this is to ensure that the ordering of client connections is deterministic so that test assertions work.
 func sortedClients(unsortedClients map[uint16]*ClientConn) (clients []*ClientConn) {
 // sortedClients is a utility function that takes a map of *ClientConn and returns a sorted slice of the values.
 // The purpose of this is to ensure that the ordering of client connections is deterministic so that test assertions work.
 func sortedClients(unsortedClients map[uint16]*ClientConn) (clients []*ClientConn) {
index c17510a53d214da3c054d322d72704f8cff9ce82..992dcec8525b2fa64035cb0970a66c12c04d19e8 100644 (file)
@@ -401,7 +401,7 @@ func HandleGetFileInfo(cc *ClientConn, t *Transaction) (res []Transaction, err e
                NewField(fieldFileType, ffo.FlatFileInformationFork.TypeSignature),
                NewField(fieldFileCreateDate, ffo.FlatFileInformationFork.CreateDate),
                NewField(fieldFileModifyDate, ffo.FlatFileInformationFork.ModifyDate),
                NewField(fieldFileType, ffo.FlatFileInformationFork.TypeSignature),
                NewField(fieldFileCreateDate, ffo.FlatFileInformationFork.CreateDate),
                NewField(fieldFileModifyDate, ffo.FlatFileInformationFork.ModifyDate),
-               NewField(fieldFileSize, ffo.FlatFileDataForkHeader.DataSize),
+               NewField(fieldFileSize, ffo.FlatFileDataForkHeader.DataSize[:]),
        ))
        return res, err
 }
        ))
        return res, err
 }
@@ -1213,7 +1213,7 @@ func HandleDownloadFile(cc *ClientConn, t *Transaction) (res []Transaction, err
                NewField(fieldRefNum, transactionRef),
                NewField(fieldWaitingCount, []byte{0x00, 0x00}), // TODO: Implement waiting count
                NewField(fieldTransferSize, ffo.TransferSize()),
                NewField(fieldRefNum, transactionRef),
                NewField(fieldWaitingCount, []byte{0x00, 0x00}), // TODO: Implement waiting count
                NewField(fieldTransferSize, ffo.TransferSize()),
-               NewField(fieldFileSize, ffo.FlatFileDataForkHeader.DataSize),
+               NewField(fieldFileSize, ffo.FlatFileDataForkHeader.DataSize[:]),
        ))
 
        return res, err
        ))
 
        return res, err
index fa431cee176de216af5582336c5d3e6f0ecea198..703dae0582b02c9457fa0ef35c802dd53cf77b58 100644 (file)
@@ -4,6 +4,7 @@ import (
        "bytes"
        "encoding/binary"
        "errors"
        "bytes"
        "encoding/binary"
        "errors"
+       "io"
 )
 
 type transfer struct {
 )
 
 type transfer struct {
@@ -26,3 +27,78 @@ func (tf *transfer) Write(b []byte) (int, error) {
 
        return len(b), nil
 }
 
        return len(b), nil
 }
+
+func receiveFile(conn io.Reader, targetFile io.Writer, resForkFile io.Writer) error {
+       ffhBuf := make([]byte, 24)
+       if _, err := conn.Read(ffhBuf); err != nil {
+               return err
+       }
+
+       var ffh FlatFileHeader
+       err := binary.Read(bytes.NewReader(ffhBuf), binary.BigEndian, &ffh)
+       if err != nil {
+               return err
+       }
+
+       ffifhBuf := make([]byte, 16)
+       if _, err := conn.Read(ffifhBuf); err != nil {
+               return err
+       }
+
+       var ffifh FlatFileInformationForkHeader
+       err = binary.Read(bytes.NewReader(ffifhBuf), binary.BigEndian, &ffifh)
+       if err != nil {
+               return err
+       }
+
+       var ffif FlatFileInformationFork
+
+       dataLen := binary.BigEndian.Uint32(ffifh.DataSize[:])
+       ffifBuf := make([]byte, dataLen)
+       if _, err := conn.Read(ffifBuf); err != nil {
+               return err
+       }
+       if err := ffif.UnmarshalBinary(ffifBuf); err != nil {
+               return err
+       }
+
+       var ffdfh FlatFileDataForkHeader
+       ffdfhBuf := make([]byte, 16)
+       if _, err := conn.Read(ffdfhBuf); err != nil {
+               return err
+       }
+       err = binary.Read(bytes.NewReader(ffdfhBuf), binary.BigEndian, &ffdfh)
+       if err != nil {
+               return err
+       }
+
+       // this will be zero if the file only has a resource fork
+       fileSize := int(binary.BigEndian.Uint32(ffdfh.DataSize[:]))
+
+       // Copy N bytes from conn to upload file
+       _, err = io.CopyN(targetFile, conn, int64(fileSize))
+       if err != nil {
+               return err
+       }
+
+       if ffh.ForkCount == [2]byte{0, 3} {
+               var resForkHeader FlatFileDataForkHeader
+               resForkBuf := make([]byte, 16)
+
+               if _, err := conn.Read(resForkBuf); err != nil {
+                       return err
+               }
+               err = binary.Read(bytes.NewReader(resForkBuf), binary.BigEndian, &resForkHeader)
+               if err != nil {
+                       return err
+               }
+
+               fileSize = int(binary.BigEndian.Uint32(resForkHeader.DataSize[:]))
+
+               _, err = io.CopyN(resForkFile, conn, int64(fileSize))
+               if err != nil {
+                       return err
+               }
+       }
+       return nil
+}