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
130
131
132
133
134
135
136
137
|
package hotline
import (
"bufio"
"bytes"
"encoding/binary"
"errors"
"fmt"
"io"
"path"
"strings"
)
// FilePathItem represents the file or directory portion of a delimited file path (e.g. foo and bar in "/foo/bar")
// Example bytes:
// 00 00
// 09
// 73 75 62 66 6f 6c 64 65 72 "subfolder"
type FilePathItem struct {
Len byte
Name []byte
}
const fileItemMinLen = 3
// fileItemScanner implements bufio.SplitFunc for parsing incoming byte slices into complete tokens
func fileItemScanner(data []byte, _ bool) (advance int, token []byte, err error) {
if len(data) < fileItemMinLen {
return 0, nil, nil
}
advance = fileItemMinLen + int(data[2])
return advance, data[0:advance], nil
}
// Write implements the io.Writer interface for FilePathItem
func (fpi *FilePathItem) Write(b []byte) (n int, err error) {
if len(b) < 3 {
return n, errors.New("buflen too small")
}
fpi.Len = b[2]
fpi.Name = b[fileItemMinLen : fpi.Len+fileItemMinLen]
return int(fpi.Len) + fileItemMinLen, nil
}
type FilePath struct {
ItemCount [2]byte
Items []FilePathItem
}
// Write implements io.Writer interface for FilePath
func (fp *FilePath) Write(b []byte) (n int, err error) {
reader := bytes.NewReader(b)
err = binary.Read(reader, binary.BigEndian, &fp.ItemCount)
if err != nil && !errors.Is(err, io.EOF) {
return n, err
}
if errors.Is(err, io.EOF) {
return n, nil
}
scanner := bufio.NewScanner(reader)
scanner.Split(fileItemScanner)
for i := 0; i < int(binary.BigEndian.Uint16(fp.ItemCount[:])); i++ {
var fpi FilePathItem
scanner.Scan()
// Make a new []byte slice and copy the scanner bytes to it. This is critical to avoid a data race as the
// scanner re-uses the buffer for subsequent scans.
buf := make([]byte, len(scanner.Bytes()))
copy(buf, scanner.Bytes())
if _, err := fpi.Write(buf); err != nil {
return n, err
}
fp.Items = append(fp.Items, fpi)
}
return n, nil
}
// IsDropbox checks if a FilePath matches the special drop box folder type
func (fp *FilePath) IsDropbox() bool {
if fp.Len() == 0 {
return false
}
return strings.Contains(strings.ToLower(string(fp.Items[fp.Len()-1].Name)), "drop box")
}
func (fp *FilePath) IsUploadDir() bool {
if fp.Len() == 0 {
return false
}
return strings.Contains(strings.ToLower(string(fp.Items[fp.Len()-1].Name)), "upload")
}
func (fp *FilePath) IsUserDir() bool {
if fp.Len() == 0 {
return false
}
return strings.HasPrefix(string(fp.Items[0].Name), "~")
}
func (fp *FilePath) Len() uint16 {
return binary.BigEndian.Uint16(fp.ItemCount[:])
}
func ReadPath(fileRoot string, filePath, fileName []byte) (fullPath string, err error) {
var fp FilePath
if filePath != nil {
if _, err = fp.Write(filePath); err != nil {
return "", err
}
}
var subPath string
for _, pathItem := range fp.Items {
subPath = path.Join("/", subPath, string(pathItem.Name))
}
fullPath = path.Join(
fileRoot,
subPath,
path.Join("/", string(fileName)),
)
fullPath, err = txtDecoder.String(fullPath)
if err != nil {
return "", fmt.Errorf("invalid filepath encoding: %w", err)
}
return fullPath, nil
}
|