]>
Commit | Line | Data |
---|---|---|
1 | package hotline | |
2 | ||
3 | import ( | |
4 | "bufio" | |
5 | "bytes" | |
6 | "encoding/binary" | |
7 | "errors" | |
8 | "io" | |
9 | "path/filepath" | |
10 | "strings" | |
11 | ) | |
12 | ||
13 | // FilePathItem represents the file or directory portion of a delimited file path (e.g. foo and bar in "/foo/bar") | |
14 | // Example bytes: | |
15 | // 00 00 | |
16 | // 09 | |
17 | // 73 75 62 66 6f 6c 64 65 72 "subfolder" | |
18 | type FilePathItem struct { | |
19 | Len byte | |
20 | Name []byte | |
21 | } | |
22 | ||
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) { | |
37 | fpi.Len = b[2] | |
38 | fpi.Name = b[fileItemMinLen : fpi.Len+fileItemMinLen] | |
39 | ||
40 | return int(fpi.Len) + fileItemMinLen, nil | |
41 | } | |
42 | ||
43 | type FilePath struct { | |
44 | ItemCount [2]byte | |
45 | Items []FilePathItem | |
46 | } | |
47 | ||
48 | func (fp *FilePath) Write(b []byte) (n int, err error) { | |
49 | reader := bytes.NewReader(b) | |
50 | err = binary.Read(reader, binary.BigEndian, &fp.ItemCount) | |
51 | if err != nil && !errors.Is(err, io.EOF) { | |
52 | return n, err | |
53 | } | |
54 | if errors.Is(err, io.EOF) { | |
55 | return n, nil | |
56 | } | |
57 | ||
58 | scanner := bufio.NewScanner(reader) | |
59 | scanner.Split(fileItemScanner) | |
60 | ||
61 | for i := 0; i < int(binary.BigEndian.Uint16(fp.ItemCount[:])); i++ { | |
62 | var fpi FilePathItem | |
63 | scanner.Scan() | |
64 | if _, err := fpi.Write(scanner.Bytes()); err != nil { | |
65 | return n, err | |
66 | } | |
67 | fp.Items = append(fp.Items, fpi) | |
68 | } | |
69 | ||
70 | return n, nil | |
71 | } | |
72 | ||
73 | // IsDropbox checks if a FilePath matches the special drop box folder type | |
74 | func (fp *FilePath) IsDropbox() bool { | |
75 | if fp.Len() == 0 { | |
76 | return false | |
77 | } | |
78 | ||
79 | return strings.Contains(strings.ToLower(string(fp.Items[fp.Len()-1].Name)), "drop box") | |
80 | } | |
81 | ||
82 | func (fp *FilePath) IsUploadDir() bool { | |
83 | if fp.Len() == 0 { | |
84 | return false | |
85 | } | |
86 | ||
87 | return strings.Contains(strings.ToLower(string(fp.Items[fp.Len()-1].Name)), "upload") | |
88 | } | |
89 | ||
90 | func (fp *FilePath) Len() uint16 { | |
91 | return binary.BigEndian.Uint16(fp.ItemCount[:]) | |
92 | } | |
93 | ||
94 | func readPath(fileRoot string, filePath, fileName []byte) (fullPath string, err error) { | |
95 | var fp FilePath | |
96 | if filePath != nil { | |
97 | if _, err = fp.Write(filePath); err != nil { | |
98 | return "", err | |
99 | } | |
100 | } | |
101 | ||
102 | var subPath string | |
103 | for _, pathItem := range fp.Items { | |
104 | subPath = filepath.Join("/", subPath, string(pathItem.Name)) | |
105 | } | |
106 | ||
107 | fullPath = filepath.Join( | |
108 | fileRoot, | |
109 | subPath, | |
110 | filepath.Join("/", string(fileName)), | |
111 | ) | |
112 | ||
113 | return fullPath, nil | |
114 | } |