From 24808f680279f1b3b96d16a2afd617de3e09ee76 Mon Sep 17 00:00:00 2001 From: ShukeBta <272197458+ShukeBta@users.noreply.github.com> Date: Sat, 27 Jun 2026 13:16:33 +0800 Subject: [PATCH] split clouddrive2 dav helpers --- internal/service/cloud/clouddrive2_dav.go | 126 ------------------ .../service/cloud/clouddrive2_dav_error.go | 55 ++++++++ .../service/cloud/clouddrive2_dav_path.go | 85 ++++++++++++ 3 files changed, 140 insertions(+), 126 deletions(-) create mode 100644 internal/service/cloud/clouddrive2_dav_error.go create mode 100644 internal/service/cloud/clouddrive2_dav_path.go diff --git a/internal/service/cloud/clouddrive2_dav.go b/internal/service/cloud/clouddrive2_dav.go index 9f5f225..042946c 100644 --- a/internal/service/cloud/clouddrive2_dav.go +++ b/internal/service/cloud/clouddrive2_dav.go @@ -9,7 +9,6 @@ import ( "net/http" "net/url" "path" - "strconv" "strings" ) @@ -171,128 +170,3 @@ func (p *cloudDrive2Provider) auth(req *http.Request) { req.SetBasicAuth(p.username, p.password) } } - -func (p *cloudDrive2Provider) decorateDAVStatusError(resp *http.Response, target string) error { - body, _ := io.ReadAll(io.LimitReader(resp.Body, 4096)) - detail := compactDAVErrorBody(string(body)) - if detail == "" { - if resp.StatusCode == http.StatusMethodNotAllowed { - return fmt.Errorf("%s: list %s returned http %d;请确认填写的是 WebDAV 地址(通常以 /dav 结尾),并且桥接网盘已在 OpenList/CloudDrive2 内完成登录或 Cookie 保存", p.name, target, resp.StatusCode) - } - if resp.StatusCode == http.StatusUnauthorized || resp.StatusCode == http.StatusForbidden { - return fmt.Errorf("%s: list %s returned http %d;请填写 OpenList/CloudDrive2 的 Token 或用户名密码,或确认 WebDAV 凭据可用", p.name, target, resp.StatusCode) - } - return fmt.Errorf("%s: list %s returned http %d", p.name, target, resp.StatusCode) - } - if resp.StatusCode == http.StatusMethodNotAllowed { - return fmt.Errorf("%s: list %s returned http %d:%s;请确认填写的是 WebDAV 地址(通常以 /dav 结尾),并且桥接网盘已在 OpenList/CloudDrive2 内完成登录或 Cookie 保存", p.name, target, resp.StatusCode, detail) - } - if resp.StatusCode == http.StatusUnauthorized || resp.StatusCode == http.StatusForbidden { - return fmt.Errorf("%s: list %s returned http %d:%s;请检查 WebDAV 用户名/密码、Authorization Token,或先在 OpenList/CloudDrive2 中保存对应网盘 Cookie", p.name, target, resp.StatusCode, detail) - } - return fmt.Errorf("%s: list %s returned http %d:%s", p.name, target, resp.StatusCode, detail) -} - -func compactDAVErrorBody(raw string) string { - raw = strings.TrimSpace(strings.ReplaceAll(raw, "\x00", "")) - if raw == "" { - return "" - } - raw = strings.Join(strings.Fields(raw), " ") - if len([]rune(raw)) > 180 { - return string([]rune(raw)[:180]) + "…" - } - return raw -} - -func decorateDAVTransportError(name, target string, err error) error { - if err == nil { - return nil - } - message := err.Error() - if strings.Contains(message, "server gave HTTP response to HTTPS client") { - return fmt.Errorf("%s: %w;当前地址使用 https://,但服务端返回 HTTP。请改用 http:// 地址,例如 OpenList 默认 WebDAV 通常是 http://host:5244/dav/;如果必须使用 https,请在 OpenList 前配置反向代理和证书", name, err) - } - if strings.Contains(message, "first record does not look like a TLS handshake") { - return fmt.Errorf("%s: %w;疑似把 HTTP 服务配置成了 https://,请检查 %s 的协议头", name, err, target) - } - return err -} - -func (p *cloudDrive2Provider) urlFor(remotePath string) string { - u := *p.base - u.RawPath = "" - basePath := strings.TrimRight(u.Path, "/") - remote := strings.Trim(normalizeCloudDAVPath(remotePath), "/") - switch { - case basePath == "" || basePath == "/": - if remote == "" { - u.Path = "/" - } else { - u.Path = "/" + remote - } - case remote == "": - u.Path = basePath - default: - u.Path = basePath + "/" + remote - } - return u.String() -} - -func (p *cloudDrive2Provider) entryIDFromHref(href, basePath string) (string, error) { - if href == "" { - return "", nil - } - parsed, err := url.Parse(href) - if err != nil { - return "", err - } - hrefPath := parsed.EscapedPath() - if hrefPath == "" { - hrefPath = href - } - if basePath != "" && basePath != "/" { - hrefPath = strings.TrimPrefix(hrefPath, basePath) - } - if decoded, err := url.PathUnescape(hrefPath); err == nil { - hrefPath = decoded - } - return normalizeCloudDAVPath(hrefPath), nil -} - -const cloudDAVPropfindBody = ` - - - - - - -` - -type cloudDAVMultiStatus struct { - Responses []cloudDAVResponse `xml:"response"` -} - -type cloudDAVResponse struct { - Href string `xml:"href"` - PropStat cloudDAVPropStat `xml:"propstat"` -} - -type cloudDAVPropStat struct { - Prop cloudDAVProp `xml:"prop"` -} - -type cloudDAVProp struct { - DisplayName string `xml:"displayname"` - ContentLength string `xml:"getcontentlength"` - ResourceType cloudDAVResourceType `xml:"resourcetype"` -} - -type cloudDAVResourceType struct { - Collection *struct{} `xml:"collection"` -} - -func parseDAVSize(raw string) int64 { - n, _ := strconv.ParseInt(strings.TrimSpace(raw), 10, 64) - return n -} diff --git a/internal/service/cloud/clouddrive2_dav_error.go b/internal/service/cloud/clouddrive2_dav_error.go new file mode 100644 index 0000000..6ae5372 --- /dev/null +++ b/internal/service/cloud/clouddrive2_dav_error.go @@ -0,0 +1,55 @@ +package cloud + +import ( + "fmt" + "io" + "net/http" + "strings" +) + +func (p *cloudDrive2Provider) decorateDAVStatusError(resp *http.Response, target string) error { + body, _ := io.ReadAll(io.LimitReader(resp.Body, 4096)) + detail := compactDAVErrorBody(string(body)) + if detail == "" { + if resp.StatusCode == http.StatusMethodNotAllowed { + return fmt.Errorf("%s: list %s returned http %d;请确认填写的是 WebDAV 地址(通常以 /dav 结尾),并且桥接网盘已在 OpenList/CloudDrive2 内完成登录或 Cookie 保存", p.name, target, resp.StatusCode) + } + if resp.StatusCode == http.StatusUnauthorized || resp.StatusCode == http.StatusForbidden { + return fmt.Errorf("%s: list %s returned http %d;请填写 OpenList/CloudDrive2 的 Token 或用户名密码,或确认 WebDAV 凭据可用", p.name, target, resp.StatusCode) + } + return fmt.Errorf("%s: list %s returned http %d", p.name, target, resp.StatusCode) + } + if resp.StatusCode == http.StatusMethodNotAllowed { + return fmt.Errorf("%s: list %s returned http %d:%s;请确认填写的是 WebDAV 地址(通常以 /dav 结尾),并且桥接网盘已在 OpenList/CloudDrive2 内完成登录或 Cookie 保存", p.name, target, resp.StatusCode, detail) + } + if resp.StatusCode == http.StatusUnauthorized || resp.StatusCode == http.StatusForbidden { + return fmt.Errorf("%s: list %s returned http %d:%s;请检查 WebDAV 用户名/密码、Authorization Token,或先在 OpenList/CloudDrive2 中保存对应网盘 Cookie", p.name, target, resp.StatusCode, detail) + } + return fmt.Errorf("%s: list %s returned http %d:%s", p.name, target, resp.StatusCode, detail) +} + +func compactDAVErrorBody(raw string) string { + raw = strings.TrimSpace(strings.ReplaceAll(raw, "\x00", "")) + if raw == "" { + return "" + } + raw = strings.Join(strings.Fields(raw), " ") + if len([]rune(raw)) > 180 { + return string([]rune(raw)[:180]) + "…" + } + return raw +} + +func decorateDAVTransportError(name, target string, err error) error { + if err == nil { + return nil + } + message := err.Error() + if strings.Contains(message, "server gave HTTP response to HTTPS client") { + return fmt.Errorf("%s: %w;当前地址使用 https://,但服务端返回 HTTP。请改用 http:// 地址,例如 OpenList 默认 WebDAV 通常是 http://host:5244/dav/;如果必须使用 https,请在 OpenList 前配置反向代理和证书", name, err) + } + if strings.Contains(message, "first record does not look like a TLS handshake") { + return fmt.Errorf("%s: %w;疑似把 HTTP 服务配置成了 https://,请检查 %s 的协议头", name, err, target) + } + return err +} diff --git a/internal/service/cloud/clouddrive2_dav_path.go b/internal/service/cloud/clouddrive2_dav_path.go new file mode 100644 index 0000000..14837c4 --- /dev/null +++ b/internal/service/cloud/clouddrive2_dav_path.go @@ -0,0 +1,85 @@ +package cloud + +import ( + "net/url" + "strconv" + "strings" +) + +func (p *cloudDrive2Provider) urlFor(remotePath string) string { + u := *p.base + u.RawPath = "" + basePath := strings.TrimRight(u.Path, "/") + remote := strings.Trim(normalizeCloudDAVPath(remotePath), "/") + switch { + case basePath == "" || basePath == "/": + if remote == "" { + u.Path = "/" + } else { + u.Path = "/" + remote + } + case remote == "": + u.Path = basePath + default: + u.Path = basePath + "/" + remote + } + return u.String() +} + +func (p *cloudDrive2Provider) entryIDFromHref(href, basePath string) (string, error) { + if href == "" { + return "", nil + } + parsed, err := url.Parse(href) + if err != nil { + return "", err + } + hrefPath := parsed.EscapedPath() + if hrefPath == "" { + hrefPath = href + } + if basePath != "" && basePath != "/" { + hrefPath = strings.TrimPrefix(hrefPath, basePath) + } + if decoded, err := url.PathUnescape(hrefPath); err == nil { + hrefPath = decoded + } + return normalizeCloudDAVPath(hrefPath), nil +} + +const cloudDAVPropfindBody = ` + + + + + + +` + +type cloudDAVMultiStatus struct { + Responses []cloudDAVResponse `xml:"response"` +} + +type cloudDAVResponse struct { + Href string `xml:"href"` + PropStat cloudDAVPropStat `xml:"propstat"` +} + +type cloudDAVPropStat struct { + Prop cloudDAVProp `xml:"prop"` +} + +type cloudDAVProp struct { + DisplayName string `xml:"displayname"` + ContentLength string `xml:"getcontentlength"` + ResourceType cloudDAVResourceType `xml:"resourcetype"` +} + +type cloudDAVResourceType struct { + Collection *struct{} `xml:"collection"` +} + +func parseDAVSize(raw string) int64 { + n, _ := strconv.ParseInt(strings.TrimSpace(raw), 10, 64) + return n +}