diff options
| author | Jeff Halter <868228+jhalter@users.noreply.github.com> | 2025-06-26 17:31:07 -0700 |
|---|---|---|
| committer | Jeff Halter <868228+jhalter@users.noreply.github.com> | 2025-06-26 17:31:07 -0700 |
| commit | 83430dba76359f3b84a50051dd3fffcbbef90c18 (patch) | |
| tree | 95c96a38804cf646b7a782570b5484d95af0c5de /hotline | |
| parent | 55b8e77c409761639e95168c77dc22c13e858b6b (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.go | 37 | ||||
| -rw-r--r-- | hotline/file_transfer_test.go | 91 |
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) + }) + } +} |