aboutsummaryrefslogtreecommitdiff
path: root/hotline/file_transfer.go
diff options
context:
space:
mode:
authorJeff Halter <868228+jhalter@users.noreply.github.com>2026-06-01 10:56:43 -0700
committerJeff Halter <868228+jhalter@users.noreply.github.com>2026-06-01 10:56:43 -0700
commit30db5839a93936f8b69d0d5d005bbe03197722c1 (patch)
tree81ee122b064d5800bf924f7b1c5c2d56543a341c /hotline/file_transfer.go
parentc42c103ddb66028a085fb452102f73f8b27dcdcb (diff)
Normalize folder upload item paths and consolidate path handling
Anchor the path returned by folderUpload.FormattedPath relative to the upload root so item paths are resolved consistently, matching the behavior of ReadPath. Resolve each destination path once per item in UploadFolderHandler and use path.Join in place of manual string concatenation. Add test coverage for FormattedPath normalization.
Diffstat (limited to 'hotline/file_transfer.go')
-rw-r--r--hotline/file_transfer.go21
1 files changed, 13 insertions, 8 deletions
diff --git a/hotline/file_transfer.go b/hotline/file_transfer.go
index 86e10eb..8670439 100644
--- a/hotline/file_transfer.go
+++ b/hotline/file_transfer.go
@@ -209,7 +209,9 @@ func (fu *folderUpload) FormattedPath() string {
}
}
- return path.Join(pathSegments...)
+ // Anchor at "/" so any ".." segments are collapsed and cannot escape the upload
+ // root (mirrors ReadPath). Strip the leading separator to keep the path relative.
+ return strings.TrimPrefix(path.Join("/", path.Join(pathSegments...)), "/")
}
type FileHeader struct {
@@ -561,9 +563,12 @@ func UploadFolderHandler(rwc io.ReadWriter, fullPath string, fileTransfer *FileT
return err
}
+ // Resolve the item path once. FormattedPath is sanitized to stay within fullPath.
+ itemPath := path.Join(fullPath, fu.FormattedPath())
+
if fu.IsFolder == [2]byte{0, 1} {
- if _, err := os.Stat(path.Join(fullPath, fu.FormattedPath())); os.IsNotExist(err) {
- if err := os.Mkdir(path.Join(fullPath, fu.FormattedPath()), 0777); err != nil {
+ if _, err := os.Stat(itemPath); os.IsNotExist(err) {
+ if err := os.Mkdir(itemPath, 0777); err != nil {
return err
}
}
@@ -576,7 +581,7 @@ func UploadFolderHandler(rwc io.ReadWriter, fullPath string, fileTransfer *FileT
nextAction := DlFldrActionSendFile
// Check if we have the full file already. If so, send dlFldrAction_NextFile to client to skip.
- _, err := os.Stat(path.Join(fullPath, fu.FormattedPath()))
+ _, err := os.Stat(itemPath)
if err != nil && !errors.Is(err, fs.ErrNotExist) {
return err
}
@@ -585,7 +590,7 @@ func UploadFolderHandler(rwc io.ReadWriter, fullPath string, fileTransfer *FileT
}
// Check if we have a partial file already. If so, send dlFldrAction_ResumeFile to client to resume upload.
- incompleteFile, err := os.Stat(path.Join(fullPath, fu.FormattedPath()+IncompleteFileSuffix))
+ incompleteFile, err := os.Stat(itemPath + IncompleteFileSuffix)
if err != nil && !errors.Is(err, fs.ErrNotExist) {
return err
}
@@ -604,7 +609,7 @@ func UploadFolderHandler(rwc io.ReadWriter, fullPath string, fileTransfer *FileT
offset := make([]byte, 4)
binary.BigEndian.PutUint32(offset, uint32(incompleteFile.Size()))
- file, err := os.OpenFile(fullPath+"/"+fu.FormattedPath()+IncompleteFileSuffix, os.O_APPEND|os.O_CREATE|os.O_WRONLY, 0644)
+ file, err := os.OpenFile(itemPath+IncompleteFileSuffix, os.O_APPEND|os.O_CREATE|os.O_WRONLY, 0644)
if err != nil {
return err
}
@@ -628,7 +633,7 @@ func UploadFolderHandler(rwc io.ReadWriter, fullPath string, fileTransfer *FileT
rLogger.Error("Error receiving file", "err", err)
}
- err = os.Rename(fullPath+"/"+fu.FormattedPath()+".incomplete", fullPath+"/"+fu.FormattedPath())
+ err = os.Rename(itemPath+IncompleteFileSuffix, itemPath)
if err != nil {
return err
}
@@ -638,7 +643,7 @@ func UploadFolderHandler(rwc io.ReadWriter, fullPath string, fileTransfer *FileT
return err
}
- filePath := path.Join(fullPath, fu.FormattedPath())
+ filePath := itemPath
hlFile, err := NewFile(fileStore, filePath, 0)
if err != nil {