forgejo/services/repository/files/pathutils.go
David Rotermund 957a76df3b feat: Drag and drop nested directories (#6687)
Adds the ability for the drag and drop file upload to handle subdirectories. You drag and drop and it preserves your sub-folder substructure. Nothing more, nothing less.

<!--start release-notes-assistant-->

## Release notes
<!--URL:https://codeberg.org/forgejo/forgejo-->
- User Interface features
  - [PR](https://codeberg.org/forgejo/forgejo/pulls/6687): <!--number 6687 --><!--line 0 --><!--description RHJhZyBhbmQgZHJvcCBuZXN0ZWQgZGlyZWN0b3JpZXM=-->Drag and drop nested directories<!--description-->
<!--end release-notes-assistant-->

Co-authored-by: David Rotermund <davrot@neuro.uni-bremen.de>
Co-authored-by: Otto Richter <git@otto.splvs.net>
Co-authored-by: Beowulf <beowulf@beocode.eu>
Reviewed-on: https://codeberg.org/forgejo/forgejo/pulls/6687
Reviewed-by: jerger <jerger@noreply.codeberg.org>
Co-authored-by: David Rotermund <davrot@noreply.codeberg.org>
Co-committed-by: David Rotermund <davrot@noreply.codeberg.org>
2025-10-03 00:16:40 +02:00

61 lines
2 KiB
Go

// Copyright 2025 The Forgejo Authors. All rights reserved.
// SPDX-License-Identifier: MIT
package files
import (
"fmt"
"path"
"strings"
)
// This is keep in for the case that in the future, parts of the filenames need sanitizing
// var fileNameComponentSanitizeRegexp = regexp.MustCompile(`(?i)[<>:\"/\\|?*\x{0000}-\x{001F}]|^(con|prn|aux|nul|com\d|lpt\d)$`)
// SanitizePath cleans and validates a file path
func SanitizePath(inputPath string) (string, error) {
// Normalize path separators
s := strings.ReplaceAll(inputPath, "\\", "/")
// We don't want a / or \\ as the beginning of a path
if strings.HasPrefix(inputPath, "/") {
return "", fmt.Errorf("path starts with / : %s", inputPath)
}
// Make path temporarily absolute to ensure proper cleaning of all components
temp := "/" + s
// Clean the path (this will properly handle ../.. components when absolute)
temp = path.Clean(temp)
// Remove the leading / to make it relative again
s = strings.TrimPrefix(temp, "/")
// If the cleaned path is empty or becomes root, it's invalid
if s == "" {
return "", fmt.Errorf("path resolves to root or becomes empty after cleaning")
}
// Split the path components
pathComponents := strings.Split(s, "/")
// Sanitize each path component
var sanitizedComponents []string
for _, component := range pathComponents {
// Alternaitve option if parts of the filenames need sanitizing (Trim whitespace and apply regex sanitization)
// sanitizedComponent := strings.TrimSpace(fileNameComponentSanitizeRegexp.ReplaceAllString(component, "_"))
// Trim whitespace
sanitizedComponent := strings.TrimSpace(component)
// Skip empty components after sanitization
if sanitizedComponent != "" {
sanitizedComponents = append(sanitizedComponents, sanitizedComponent)
}
}
// Check if we have any components left after sanitization
if len(sanitizedComponents) == 0 {
return "", fmt.Errorf("path became empty after sanitization")
}
// Reconstruct the path
reconstructedPath := path.Join(sanitizedComponents...)
return reconstructedPath, nil
}