1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
|
package hotline
import (
"encoding/binary"
"fmt"
"math"
"math/rand"
"path/filepath"
"sync"
)
// File transfer types
const (
FileDownload = 0
FileUpload = 1
FolderDownload = 2
FolderUpload = 3
bannerDownload = 4
)
type FileTransfer struct {
FileName []byte
FilePath []byte
ReferenceNumber []byte
refNum [4]byte
Type int
TransferSize []byte
FolderItemCount []byte
fileResumeData *FileResumeData
options []byte
bytesSentCounter *WriteCounter
ClientConn *ClientConn
}
// WriteCounter counts the number of bytes written to it.
type WriteCounter struct {
mux sync.Mutex
Total int64 // Total # of bytes written
}
// Write implements the io.Writer interface.
//
// 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{},
}
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 (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)
}
}
func (ft *FileTransfer) ItemCount() int {
return int(binary.BigEndian.Uint16(ft.FolderItemCount))
}
type folderUpload struct {
DataSize [2]byte
IsFolder [2]byte
PathItemCount [2]byte
FileNamePath []byte
}
func (fu *folderUpload) FormattedPath() string {
pathItemLen := binary.BigEndian.Uint16(fu.PathItemCount[:])
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 filepath.Join(pathSegments...)
}
|