mirror of
https://github.com/truewhile/MeBox.git
synced 2026-09-30 03:36:37 +08:00
129 lines
3.4 KiB
Go
129 lines
3.4 KiB
Go
package service
|
|
|
|
import (
|
|
"context"
|
|
"errors"
|
|
"fmt"
|
|
"io"
|
|
"net/http"
|
|
"net/url"
|
|
"os"
|
|
"path"
|
|
"strings"
|
|
)
|
|
|
|
type webDAVUploader struct {
|
|
base *url.URL
|
|
username string
|
|
password string
|
|
client *http.Client
|
|
}
|
|
|
|
func newWebDAVUploader(cfg map[string]any) *webDAVUploader {
|
|
u, _ := url.Parse(strings.TrimRight(strr(cfg["url"]), "/"))
|
|
return &webDAVUploader{
|
|
base: u,
|
|
username: strr(cfg["username"]),
|
|
password: strr(cfg["password"]),
|
|
client: &http.Client{},
|
|
}
|
|
}
|
|
|
|
func (w *webDAVUploader) ensureDir(ctx context.Context, remoteDir string) error {
|
|
if w.base == nil || w.base.Scheme == "" || w.base.Host == "" {
|
|
return errors.New("webdav missing url")
|
|
}
|
|
remoteDir = normalizeRemotePath(remoteDir)
|
|
if remoteDir == "/" {
|
|
return nil
|
|
}
|
|
current := ""
|
|
for _, part := range strings.Split(strings.Trim(remoteDir, "/"), "/") {
|
|
current = normalizeRemotePath(path.Join(current, part))
|
|
req, err := http.NewRequestWithContext(ctx, "MKCOL", w.urlFor(current), nil)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
w.auth(req)
|
|
resp, err := w.client.Do(req)
|
|
if err != nil {
|
|
return decorateStorageTransportError("webdav", w.urlFor(current), err)
|
|
}
|
|
_, _ = io.Copy(io.Discard, resp.Body)
|
|
_ = resp.Body.Close()
|
|
if resp.StatusCode >= 200 && resp.StatusCode < 300 {
|
|
continue
|
|
}
|
|
if resp.StatusCode == http.StatusMethodNotAllowed || resp.StatusCode == http.StatusConflict {
|
|
continue
|
|
}
|
|
return fmt.Errorf("webdav mkdir %s: http %d", current, resp.StatusCode)
|
|
}
|
|
return nil
|
|
}
|
|
|
|
func (w *webDAVUploader) exists(ctx context.Context, remotePath string) (bool, error) {
|
|
req, err := http.NewRequestWithContext(ctx, http.MethodHead, w.urlFor(remotePath), nil)
|
|
if err != nil {
|
|
return false, err
|
|
}
|
|
w.auth(req)
|
|
resp, err := w.client.Do(req)
|
|
if err != nil {
|
|
return false, decorateStorageTransportError("webdav", w.urlFor(remotePath), err)
|
|
}
|
|
_, _ = io.Copy(io.Discard, resp.Body)
|
|
_ = resp.Body.Close()
|
|
if resp.StatusCode == http.StatusNotFound {
|
|
return false, nil
|
|
}
|
|
return resp.StatusCode >= 200 && resp.StatusCode < 300, nil
|
|
}
|
|
|
|
func (w *webDAVUploader) upload(ctx context.Context, localPath, remotePath string, size int64) error {
|
|
f, err := os.Open(localPath) // #nosec G304 -- localPath is selected from configured local media files before upload.
|
|
if err != nil {
|
|
return err
|
|
}
|
|
defer f.Close()
|
|
req, err := http.NewRequestWithContext(ctx, http.MethodPut, w.urlFor(remotePath), f)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
w.auth(req)
|
|
req.ContentLength = size
|
|
resp, err := w.client.Do(req)
|
|
if err != nil {
|
|
return decorateStorageTransportError("webdav", w.urlFor(remotePath), err)
|
|
}
|
|
_, _ = io.Copy(io.Discard, resp.Body)
|
|
_ = resp.Body.Close()
|
|
if resp.StatusCode < 200 || resp.StatusCode >= 300 {
|
|
return fmt.Errorf("webdav upload %s: http %d", remotePath, resp.StatusCode)
|
|
}
|
|
return nil
|
|
}
|
|
|
|
func (w *webDAVUploader) auth(req *http.Request) {
|
|
if w.username != "" {
|
|
req.SetBasicAuth(w.username, w.password)
|
|
}
|
|
}
|
|
|
|
func (w *webDAVUploader) urlFor(remotePath string) string {
|
|
u := *w.base
|
|
basePath := strings.TrimRight(u.EscapedPath(), "/")
|
|
segments := make([]string, 0)
|
|
if basePath != "" && basePath != "/" {
|
|
segments = append(segments, strings.Trim(basePath, "/"))
|
|
}
|
|
for _, part := range strings.Split(strings.Trim(normalizeRemotePath(remotePath), "/"), "/") {
|
|
if part != "" {
|
|
segments = append(segments, url.PathEscape(part))
|
|
}
|
|
}
|
|
u.RawPath = ""
|
|
u.Path = "/" + strings.Join(segments, "/")
|
|
return u.String()
|
|
}
|