ultisuite-backend/internal/nextcloud/dav_path.go
2026-06-04 00:12:11 +02:00

172 lines
4.1 KiB
Go

package nextcloud
import (
"net/url"
"strings"
)
// WebDAVPath builds an encoded WebDAV URL for a logical client path (may contain spaces).
func (c *Client) WebDAVPath(userID, path string) string {
userSeg := url.PathEscape(strings.TrimSpace(userID))
logical := strings.Trim(path, "/")
var encoded string
if logical == "" {
encoded = ""
} else {
parts := strings.Split(logical, "/")
for i, p := range parts {
parts[i] = url.PathEscape(p)
}
encoded = strings.Join(parts, "/")
}
if encoded == "" {
return "/remote.php/dav/files/" + userSeg
}
return "/remote.php/dav/files/" + userSeg + "/" + encoded
}
func decodeDAVSegment(seg string) string {
if seg == "" {
return seg
}
dec, err := url.PathUnescape(seg)
if err != nil {
return seg
}
return dec
}
// decodeDAVPath turns a slash-separated (possibly encoded) path into a logical path.
func decodeDAVPath(p string) string {
p = strings.TrimSpace(p)
p = strings.Trim(p, "/")
if p == "" {
return "/"
}
parts := strings.Split(p, "/")
for i, seg := range parts {
parts[i] = decodeDAVSegment(seg)
}
return "/" + strings.Join(parts, "/")
}
// clientPathFromDAVHref maps a WebDAV href to a logical path (/Folder/My File.docx).
func clientPathFromDAVHref(href string) string {
href = strings.TrimSpace(href)
if href == "" {
return "/"
}
if marker := "/dav/files/"; strings.Contains(href, marker) {
idx := strings.Index(href, marker)
rest := strings.TrimSuffix(href[idx+len(marker):], "/")
slash := strings.Index(rest, "/")
if slash < 0 {
return "/"
}
return decodeDAVPath("/" + rest[slash+1:])
}
if i := strings.LastIndex(href, "/trash/"); i >= 0 {
return decodeDAVPath("/" + href[i+len("/trash/"):])
}
return decodeDAVPath(href)
}
func fileNameFromDAVProp(displayName, href string) string {
if dn := strings.TrimSpace(displayName); dn != "" {
return dn
}
href = strings.TrimSuffix(strings.TrimSpace(href), "/")
if href == "" {
return ""
}
if i := strings.LastIndex(href, "/"); i >= 0 {
href = href[i+1:]
}
return decodeDAVSegment(href)
}
// NormalizeClientPath decodes segments and ensures a leading slash.
func NormalizeClientPath(path string) string {
return decodeDAVPath(path)
}
// JoinClientPath joins a directory path with a file or folder name.
func JoinClientPath(dir, name string) string {
name = strings.TrimSpace(name)
dir = NormalizeClientPath(dir)
if name == "" {
return dir
}
if dir == "/" {
return "/" + name
}
return dir + "/" + name
}
// NormalizeClientFilePath maps OCS/WebDAV paths to logical paths under the user files root.
func NormalizeClientFilePath(userID, path string) string {
path = strings.TrimSpace(path)
if path == "" {
return "/"
}
path = NormalizeClientPath(path)
uid := strings.TrimSpace(userID)
if uid == "" {
return path
}
trimmed := strings.Trim(path, "/")
if trimmed == "" {
return "/"
}
parts := strings.Split(trimmed, "/")
if len(parts) >= 2 {
head := decodeDAVSegment(parts[0])
if head == uid && parts[1] == "files" {
return NormalizeClientPath("/" + strings.Join(parts[2:], "/"))
}
if parts[0] == "files" && decodeDAVSegment(parts[1]) == uid {
return NormalizeClientPath("/" + strings.Join(parts[2:], "/"))
}
}
return path
}
// EnsureClientFilePath joins name when path is a parent directory (Nextcloud recent API).
func EnsureClientFilePath(path, name string) string {
path = NormalizeClientPath(path)
name = strings.TrimSpace(name)
if name == "" {
return path
}
if path == "/" {
return "/" + name
}
if strings.HasSuffix(path, "/"+name) {
return path
}
base := path[strings.LastIndex(path, "/")+1:]
if base == name {
return path
}
return JoinClientPath(path, name)
}
// ResolvePropfindClientPath resolves a PROPFIND child href against the listed directory.
func ResolvePropfindClientPath(listDir, href, fileName string) string {
if strings.Contains(href, "/dav/files/") {
return clientPathFromDAVHref(href)
}
base := NormalizeClientPath(listDir)
rel := strings.TrimPrefix(clientPathFromDAVHref(href), "/")
if rel == "" {
rel = strings.TrimSpace(fileName)
}
if rel == "" {
return base
}
if base == "/" {
return "/" + rel
}
return base + "/" + rel
}