]>
Commit | Line | Data |
---|---|---|
6988a057 JH |
1 | package hotline |
2 | ||
3 | import ( | |
8fc43f8e | 4 | "bufio" |
00d1ef67 | 5 | "bytes" |
6988a057 | 6 | "encoding/binary" |
00d1ef67 | 7 | "errors" |
050407a3 | 8 | "io" |
f22acf38 | 9 | "path/filepath" |
7e2e07da | 10 | "strings" |
6988a057 JH |
11 | ) |
12 | ||
6988a057 | 13 | // FilePathItem represents the file or directory portion of a delimited file path (e.g. foo and bar in "/foo/bar") |
8fc43f8e | 14 | // Example bytes: |
6988a057 JH |
15 | // 00 00 |
16 | // 09 | |
8fc43f8e | 17 | // 73 75 62 66 6f 6c 64 65 72 "subfolder" |
6988a057 JH |
18 | type FilePathItem struct { |
19 | Len byte | |
20 | Name []byte | |
21 | } | |
22 | ||
8fc43f8e JH |
23 | const fileItemMinLen = 3 |
24 | ||
25 | // fileItemScanner implements bufio.SplitFunc for parsing incoming byte slices into complete tokens | |
26 | func fileItemScanner(data []byte, _ bool) (advance int, token []byte, err error) { | |
27 | if len(data) < fileItemMinLen { | |
28 | return 0, nil, nil | |
29 | } | |
30 | ||
31 | advance = fileItemMinLen + int(data[2]) | |
32 | return advance, data[0:advance], nil | |
33 | } | |
34 | ||
35 | // Write implements the io.Writer interface for FilePathItem | |
36 | func (fpi *FilePathItem) Write(b []byte) (n int, err error) { | |
469a6387 JH |
37 | if len(b) < 3 { |
38 | return n, errors.New("buflen too small") | |
39 | } | |
8fc43f8e JH |
40 | fpi.Len = b[2] |
41 | fpi.Name = b[fileItemMinLen : fpi.Len+fileItemMinLen] | |
42 | ||
43 | return int(fpi.Len) + fileItemMinLen, nil | |
44 | } | |
45 | ||
6988a057 | 46 | type FilePath struct { |
00d1ef67 | 47 | ItemCount [2]byte |
72dd37f1 | 48 | Items []FilePathItem |
6988a057 JH |
49 | } |
50 | ||
469a6387 | 51 | // Write implements io.Writer interface for FilePath |
8fc43f8e | 52 | func (fp *FilePath) Write(b []byte) (n int, err error) { |
050407a3 | 53 | reader := bytes.NewReader(b) |
8fc43f8e | 54 | err = binary.Read(reader, binary.BigEndian, &fp.ItemCount) |
050407a3 | 55 | if err != nil && !errors.Is(err, io.EOF) { |
8fc43f8e | 56 | return n, err |
00d1ef67 | 57 | } |
050407a3 | 58 | if errors.Is(err, io.EOF) { |
8fc43f8e | 59 | return n, nil |
050407a3 | 60 | } |
6988a057 | 61 | |
8fc43f8e JH |
62 | scanner := bufio.NewScanner(reader) |
63 | scanner.Split(fileItemScanner) | |
050407a3 | 64 | |
8fc43f8e JH |
65 | for i := 0; i < int(binary.BigEndian.Uint16(fp.ItemCount[:])); i++ { |
66 | var fpi FilePathItem | |
67 | scanner.Scan() | |
469a6387 JH |
68 | |
69 | // Make a new []byte slice and copy the scanner bytes to it. This is critical to avoid a data race as the | |
70 | // scanner re-uses the buffer for subsequent scans. | |
71 | buf := make([]byte, len(scanner.Bytes())) | |
72 | copy(buf, scanner.Bytes()) | |
73 | ||
74 | if _, err := fpi.Write(buf); err != nil { | |
8fc43f8e | 75 | return n, err |
050407a3 | 76 | } |
8fc43f8e | 77 | fp.Items = append(fp.Items, fpi) |
6988a057 JH |
78 | } |
79 | ||
8fc43f8e | 80 | return n, nil |
72dd37f1 JH |
81 | } |
82 | ||
8fc43f8e | 83 | // IsDropbox checks if a FilePath matches the special drop box folder type |
7e2e07da JH |
84 | func (fp *FilePath) IsDropbox() bool { |
85 | if fp.Len() == 0 { | |
86 | return false | |
87 | } | |
88 | ||
89 | return strings.Contains(strings.ToLower(string(fp.Items[fp.Len()-1].Name)), "drop box") | |
90 | } | |
91 | ||
92 | func (fp *FilePath) IsUploadDir() bool { | |
93 | if fp.Len() == 0 { | |
94 | return false | |
95 | } | |
96 | ||
25f0d77d | 97 | return strings.Contains(strings.ToLower(string(fp.Items[fp.Len()-1].Name)), "upload") |
7e2e07da JH |
98 | } |
99 | ||
72dd37f1 | 100 | func (fp *FilePath) Len() uint16 { |
00d1ef67 | 101 | return binary.BigEndian.Uint16(fp.ItemCount[:]) |
6988a057 JH |
102 | } |
103 | ||
92a7e455 JH |
104 | func readPath(fileRoot string, filePath, fileName []byte) (fullPath string, err error) { |
105 | var fp FilePath | |
106 | if filePath != nil { | |
8fc43f8e | 107 | if _, err = fp.Write(filePath); err != nil { |
92a7e455 JH |
108 | return "", err |
109 | } | |
110 | } | |
111 | ||
2e08be58 JH |
112 | var subPath string |
113 | for _, pathItem := range fp.Items { | |
114 | subPath = filepath.Join("/", subPath, string(pathItem.Name)) | |
115 | } | |
116 | ||
f22acf38 | 117 | fullPath = filepath.Join( |
92a7e455 | 118 | fileRoot, |
2e08be58 | 119 | subPath, |
f22acf38 | 120 | filepath.Join("/", string(fileName)), |
92a7e455 JH |
121 | ) |
122 | ||
123 | return fullPath, nil | |
124 | } |