import (
"encoding/binary"
"fmt"
- "strings"
+ "math"
+ "math/rand"
+ "path/filepath"
+ "sync"
)
// File transfer types
FileUpload = 1
FolderDownload = 2
FolderUpload = 3
+ bannerDownload = 4
)
type FileTransfer struct {
- FileName []byte
- FilePath []byte
- ReferenceNumber []byte
- Type int
- TransferSize []byte // total size of all items in the folder. Only used in FolderUpload action
- FolderItemCount []byte
- BytesSent int
- clientID uint16
+ FileName []byte
+ FilePath []byte
+ ReferenceNumber []byte
+ refNum [4]byte
+ Type int
+ TransferSize []byte
+ FolderItemCount []byte
+ fileResumeData *FileResumeData
+ options []byte
+ bytesSentCounter *WriteCounter
+ ClientConn *ClientConn
}
-func (ft *FileTransfer) String() string {
- percentComplete := 10
- out := fmt.Sprintf("%s\t %v", ft.FileName, percentComplete)
-
- return out
+// WriteCounter counts the number of bytes written to it.
+type WriteCounter struct {
+ mux sync.Mutex
+ Total int64 // Total # of bytes written
}
-// 00 28 // DataSize
-// 00 00 // IsFolder
-// 00 02 // PathItemCount
+// Write implements the io.Writer interface.
//
-// 00 00
-// 09
-// 73 75 62 66 6f 6c 64 65 72 // "subfolder"
-//
-// 00 00
-// 15
-// 73 75 62 66 6f 6c 64 65 72 2d 74 65 73 74 66 69 6c 65 2d 35 6b // "subfolder-testfile-5k"
-func readFolderUpload(buf []byte) folderUpload {
- dataLen := binary.BigEndian.Uint16(buf[0:2])
-
- fu := folderUpload{
- DataSize: [2]byte{buf[0], buf[1]}, // Size of this structure (not including data size element itself)
- IsFolder: [2]byte{buf[2], buf[3]},
- PathItemCount: [2]byte{buf[4], buf[5]},
- FileNamePath: buf[6 : dataLen+2],
+// Always completes and never returns an error.
+func (wc *WriteCounter) Write(p []byte) (int, error) {
+ wc.mux.Lock()
+ defer wc.mux.Unlock()
+ n := len(p)
+ wc.Total += int64(n)
+ return n, nil
+}
+
+func (cc *ClientConn) newFileTransfer(transferType int, fileName, filePath, size []byte) *FileTransfer {
+ var transactionRef [4]byte
+ rand.Read(transactionRef[:])
+
+ ft := &FileTransfer{
+ FileName: fileName,
+ FilePath: filePath,
+ ReferenceNumber: transactionRef[:],
+ refNum: transactionRef,
+ Type: transferType,
+ TransferSize: size,
+ ClientConn: cc,
+ bytesSentCounter: &WriteCounter{},
}
- return fu
+ cc.transfersMU.Lock()
+ defer cc.transfersMU.Unlock()
+ cc.transfers[transferType][transactionRef] = ft
+
+ cc.Server.mux.Lock()
+ defer cc.Server.mux.Unlock()
+ cc.Server.fileTransfers[transactionRef] = ft
+
+ return ft
}
+// String returns a string representation of a file transfer and its progress for display in the GetInfo window
+// Example:
+// MasterOfOrionII1.4.0. 0% 197.9M
+func (ft *FileTransfer) String() string {
+ trunc := fmt.Sprintf("%.21s", ft.FileName)
+ return fmt.Sprintf("%-21s %.3s%% %6s\n", trunc, ft.percentComplete(), ft.formattedTransferSize())
+}
+
+func (ft *FileTransfer) percentComplete() string {
+ ft.bytesSentCounter.mux.Lock()
+ defer ft.bytesSentCounter.mux.Unlock()
+ return fmt.Sprintf(
+ "%v",
+ math.RoundToEven(float64(ft.bytesSentCounter.Total)/float64(binary.BigEndian.Uint32(ft.TransferSize))*100),
+ )
+}
-func (fu *folderUpload) UnmarshalBinary(b []byte) error {
- fu.DataSize = [2]byte{b[0], b[1]}
- fu.IsFolder = [2]byte{b[2], b[3]}
- fu.PathItemCount = [2]byte{b[4], b[5]}
+func (ft *FileTransfer) formattedTransferSize() string {
+ sizeInKB := float32(binary.BigEndian.Uint32(ft.TransferSize)) / 1024
+ if sizeInKB > 1024 {
+ return fmt.Sprintf("%.1fM", sizeInKB/1024)
+ } else {
+ return fmt.Sprintf("%.0fK", sizeInKB)
+ }
+}
- return nil
+func (ft *FileTransfer) ItemCount() int {
+ return int(binary.BigEndian.Uint16(ft.FolderItemCount))
}
type folderUpload struct {
var pathSegments []string
pathData := fu.FileNamePath
+ // TODO: implement scanner interface instead?
for i := uint16(0); i < pathItemLen; i++ {
segLen := pathData[2]
pathSegments = append(pathSegments, string(pathData[3:3+segLen]))
pathData = pathData[3+segLen:]
}
- return strings.Join(pathSegments, pathSeparator)
+ return filepath.Join(pathSegments...)
}