aboutsummaryrefslogtreecommitdiff
path: root/hotline
diff options
context:
space:
mode:
authorJeff Halter <868228+jhalter@users.noreply.github.com>2025-06-26 17:31:07 -0700
committerJeff Halter <868228+jhalter@users.noreply.github.com>2025-06-26 17:31:07 -0700
commit83430dba76359f3b84a50051dd3fffcbbef90c18 (patch)
tree95c96a38804cf646b7a782570b5484d95af0c5de /hotline
parent55b8e77c409761639e95168c77dc22c13e858b6b (diff)
Refactor FormattedPath to use scanner interface and add comprehensive tests
- Replace manual byte slicing with bufio.Scanner for safer parsing - Add pathSegmentScanner implementing bufio.SplitFunc pattern - Add comprehensive table tests covering edge cases and special characters - Improve code safety with proper bounds checking - Follow established codebase patterns for binary data parsing
Diffstat (limited to 'hotline')
-rw-r--r--hotline/file_transfer.go37
-rw-r--r--hotline/file_transfer_test.go91
2 files changed, 122 insertions, 6 deletions
diff --git a/hotline/file_transfer.go b/hotline/file_transfer.go
index 0ddac0b..701259a 100644
--- a/hotline/file_transfer.go
+++ b/hotline/file_transfer.go
@@ -2,6 +2,7 @@ package hotline
import (
"bufio"
+ "bytes"
"crypto/rand"
"encoding/binary"
"errors"
@@ -188,17 +189,41 @@ type folderUpload struct {
// return n + 6, nil
//}
+// pathSegmentScanner implements bufio.SplitFunc for parsing path segments
+func pathSegmentScanner(data []byte, _ bool) (advance int, token []byte, err error) {
+ if len(data) < 3 {
+ return 0, nil, nil
+ }
+
+ segLen := int(data[2])
+ totalLen := 3 + segLen
+
+ if len(data) < totalLen {
+ return 0, nil, nil
+ }
+
+ return totalLen, data[0:totalLen], nil
+}
+
func (fu *folderUpload) FormattedPath() string {
pathItemLen := binary.BigEndian.Uint16(fu.PathItemCount[:])
+ if pathItemLen == 0 {
+ return ""
+ }
+
var pathSegments []string
- pathData := fu.FileNamePath
+ scanner := bufio.NewScanner(bytes.NewReader(fu.FileNamePath))
+ scanner.Split(pathSegmentScanner)
- // 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:]
+ for scanner.Scan() && len(pathSegments) < int(pathItemLen) {
+ segmentData := scanner.Bytes()
+ if len(segmentData) >= 3 {
+ segLen := int(segmentData[2])
+ if len(segmentData) >= 3+segLen {
+ pathSegments = append(pathSegments, string(segmentData[3:3+segLen]))
+ }
+ }
}
return path.Join(pathSegments...)
diff --git a/hotline/file_transfer_test.go b/hotline/file_transfer_test.go
index 0b549f8..ba29910 100644
--- a/hotline/file_transfer_test.go
+++ b/hotline/file_transfer_test.go
@@ -175,3 +175,94 @@ func TestFileHeader_Payload(t *testing.T) {
})
}
}
+
+func Test_folderUpload_FormattedPath(t *testing.T) {
+ tests := []struct {
+ name string
+ pathItemCount [2]byte
+ fileNamePath []byte
+ want string
+ }{
+ {
+ name: "empty path",
+ pathItemCount: [2]byte{0x00, 0x00},
+ fileNamePath: []byte{},
+ want: "",
+ },
+ {
+ name: "single path segment",
+ pathItemCount: [2]byte{0x00, 0x01},
+ fileNamePath: []byte{
+ 0x00, 0x00, // path separator
+ 0x03, // segment length
+ 0x66, 0x6f, 0x6f, // "foo"
+ },
+ want: "foo",
+ },
+ {
+ name: "multiple path segments",
+ pathItemCount: [2]byte{0x00, 0x03},
+ fileNamePath: []byte{
+ 0x00, 0x00, // path separator
+ 0x04, // segment length
+ 0x68, 0x6f, 0x6d, 0x65, // "home"
+ 0x00, 0x00, // path separator
+ 0x04, // segment length
+ 0x75, 0x73, 0x65, 0x72, // "user"
+ 0x00, 0x00, // path separator
+ 0x09, // segment length
+ 0x64, 0x6f, 0x63, 0x75, 0x6d, 0x65, 0x6e, 0x74, 0x73, // "documents"
+ },
+ want: "home/user/documents",
+ },
+ {
+ name: "path with spaces",
+ pathItemCount: [2]byte{0x00, 0x02},
+ fileNamePath: []byte{
+ 0x00, 0x00, // path separator
+ 0x07, // segment length
+ 0x4d, 0x79, 0x20, 0x46, 0x69, 0x6c, 0x65, // "My File"
+ 0x00, 0x00, // path separator
+ 0x0d, // segment length (13 bytes)
+ 0x49, 0x6d, 0x70, 0x6f, 0x72, 0x74, 0x61, 0x6e, 0x74, 0x2e, 0x74, 0x78, 0x74, // "Important.txt"
+ },
+ want: "My File/Important.txt",
+ },
+ {
+ name: "single character segments",
+ pathItemCount: [2]byte{0x00, 0x03},
+ fileNamePath: []byte{
+ 0x00, 0x00, // path separator
+ 0x01, // segment length
+ 0x61, // "a"
+ 0x00, 0x00, // path separator
+ 0x01, // segment length
+ 0x62, // "b"
+ 0x00, 0x00, // path separator
+ 0x01, // segment length
+ 0x63, // "c"
+ },
+ want: "a/b/c",
+ },
+ {
+ name: "path with special characters",
+ pathItemCount: [2]byte{0x00, 0x01},
+ fileNamePath: []byte{
+ 0x00, 0x00, // path separator
+ 0x08, // segment length
+ 0x74, 0x65, 0x73, 0x74, 0x40, 0x24, 0x25, 0x26, // "test@$%&"
+ },
+ want: "test@$%&",
+ },
+ }
+ for _, tt := range tests {
+ t.Run(tt.name, func(t *testing.T) {
+ fu := &folderUpload{
+ PathItemCount: tt.pathItemCount,
+ FileNamePath: tt.fileNamePath,
+ }
+ got := fu.FormattedPath()
+ assert.Equal(t, tt.want, got)
+ })
+ }
+}