2023-11-27 21:44:38 +01:00
|
|
|
package sync
|
|
|
|
|
|
|
|
import (
|
2024-01-09 18:57:42 +01:00
|
|
|
"context"
|
2024-01-10 17:44:06 +01:00
|
|
|
"github.com/openziti/zrok/drives/davClient"
|
2024-01-11 17:36:05 +01:00
|
|
|
"github.com/pkg/errors"
|
2023-11-27 23:31:13 +01:00
|
|
|
"io"
|
2024-01-09 18:57:42 +01:00
|
|
|
"net/http"
|
2023-12-14 20:25:24 +01:00
|
|
|
"net/url"
|
2023-11-27 23:31:13 +01:00
|
|
|
"os"
|
2024-01-10 20:00:32 +01:00
|
|
|
"path/filepath"
|
2023-11-28 03:11:19 +01:00
|
|
|
"time"
|
2023-11-27 21:44:38 +01:00
|
|
|
)
|
|
|
|
|
|
|
|
type WebDAVTargetConfig struct {
|
2023-12-14 20:25:24 +01:00
|
|
|
URL *url.URL
|
2023-11-27 21:44:38 +01:00
|
|
|
Username string
|
|
|
|
Password string
|
|
|
|
}
|
|
|
|
|
|
|
|
type WebDAVTarget struct {
|
2024-01-09 20:26:07 +01:00
|
|
|
cfg *WebDAVTargetConfig
|
2024-01-10 17:44:06 +01:00
|
|
|
dc *davClient.Client
|
2023-11-27 21:44:38 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
func NewWebDAVTarget(cfg *WebDAVTargetConfig) (*WebDAVTarget, error) {
|
2024-01-11 19:40:45 +01:00
|
|
|
var httpClient davClient.HTTPClient
|
|
|
|
httpClient = http.DefaultClient
|
|
|
|
if cfg.Username != "" || cfg.Password != "" {
|
|
|
|
httpClient = davClient.HTTPClientWithBasicAuth(httpClient, cfg.Username, cfg.Password)
|
|
|
|
}
|
|
|
|
dc, err := davClient.NewClient(httpClient, cfg.URL.String())
|
2023-12-14 20:25:24 +01:00
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
2024-01-09 18:57:42 +01:00
|
|
|
return &WebDAVTarget{cfg: cfg, dc: dc}, nil
|
2023-11-27 21:44:38 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
func (t *WebDAVTarget) Inventory() ([]*Object, error) {
|
2024-01-10 20:00:32 +01:00
|
|
|
rootFi, err := t.dc.Stat(context.Background(), t.cfg.URL.Path)
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
|
|
|
|
if !rootFi.IsDir {
|
|
|
|
base := filepath.Base(t.cfg.URL.Path)
|
|
|
|
t.cfg.URL.Path = filepath.Dir(t.cfg.URL.Path)
|
|
|
|
return []*Object{{
|
|
|
|
Path: "/" + base,
|
|
|
|
IsDir: false,
|
|
|
|
Size: rootFi.Size,
|
|
|
|
Modified: rootFi.ModTime,
|
|
|
|
}}, nil
|
|
|
|
}
|
|
|
|
|
2024-01-09 18:57:42 +01:00
|
|
|
fis, err := t.dc.Readdir(context.Background(), "", true)
|
2023-11-27 21:44:38 +01:00
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
2024-01-09 18:57:42 +01:00
|
|
|
var objects []*Object
|
|
|
|
for _, fi := range fis {
|
2024-01-09 19:58:38 +01:00
|
|
|
if fi.Path != "/" {
|
2024-01-09 18:57:42 +01:00
|
|
|
objects = append(objects, &Object{
|
|
|
|
Path: fi.Path,
|
2024-01-09 19:58:38 +01:00
|
|
|
IsDir: fi.IsDir,
|
2024-01-09 18:57:42 +01:00
|
|
|
Size: fi.Size,
|
|
|
|
Modified: fi.ModTime,
|
2024-01-10 20:35:27 +01:00
|
|
|
})
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return objects, nil
|
|
|
|
}
|
|
|
|
|
|
|
|
func (t *WebDAVTarget) Dir(path string) ([]*Object, error) {
|
|
|
|
fis, err := t.dc.Readdir(context.Background(), t.cfg.URL.Path, false)
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
var objects []*Object
|
|
|
|
for _, fi := range fis {
|
2024-01-11 17:44:17 +01:00
|
|
|
if fi.Path != "/" && fi.Path != t.cfg.URL.Path+"/" {
|
2024-01-10 20:35:27 +01:00
|
|
|
objects = append(objects, &Object{
|
|
|
|
Path: filepath.Base(fi.Path),
|
|
|
|
IsDir: fi.IsDir,
|
|
|
|
Size: fi.Size,
|
|
|
|
Modified: fi.ModTime,
|
2024-01-09 18:57:42 +01:00
|
|
|
})
|
2023-11-27 21:44:38 +01:00
|
|
|
}
|
|
|
|
}
|
2024-01-09 18:57:42 +01:00
|
|
|
return objects, nil
|
2023-11-27 21:44:38 +01:00
|
|
|
}
|
2023-11-27 23:31:13 +01:00
|
|
|
|
2024-01-09 19:58:38 +01:00
|
|
|
func (t *WebDAVTarget) Mkdir(path string) error {
|
2024-01-11 17:36:05 +01:00
|
|
|
fi, err := t.dc.Stat(context.Background(), filepath.Join(t.cfg.URL.Path, path))
|
|
|
|
if err == nil {
|
|
|
|
if fi.IsDir {
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
return errors.Errorf("'%v' already exists; not directory", path)
|
|
|
|
}
|
2024-01-10 20:00:32 +01:00
|
|
|
return t.dc.Mkdir(context.Background(), filepath.Join(t.cfg.URL.Path, path))
|
2024-01-09 19:58:38 +01:00
|
|
|
}
|
|
|
|
|
2023-11-27 23:31:13 +01:00
|
|
|
func (t *WebDAVTarget) ReadStream(path string) (io.ReadCloser, error) {
|
2024-01-10 20:00:32 +01:00
|
|
|
return t.dc.Open(context.Background(), filepath.Join(t.cfg.URL.Path, path))
|
2023-11-27 23:31:13 +01:00
|
|
|
}
|
2023-11-28 03:11:19 +01:00
|
|
|
|
2024-01-09 18:57:42 +01:00
|
|
|
func (t *WebDAVTarget) WriteStream(path string, rs io.Reader, _ os.FileMode) error {
|
2024-01-10 20:00:32 +01:00
|
|
|
ws, err := t.dc.Create(context.Background(), filepath.Join(t.cfg.URL.Path, path))
|
2024-01-09 18:57:42 +01:00
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
2024-01-09 19:58:38 +01:00
|
|
|
defer func() { _ = ws.Close() }()
|
2024-01-09 18:57:42 +01:00
|
|
|
_, err = io.Copy(ws, rs)
|
|
|
|
if err != nil {
|
2023-12-01 19:53:36 +01:00
|
|
|
return err
|
|
|
|
}
|
2023-11-28 03:11:19 +01:00
|
|
|
return nil
|
|
|
|
}
|
2024-01-09 18:57:42 +01:00
|
|
|
|
2024-01-16 19:54:01 +01:00
|
|
|
func (t *WebDAVTarget) WriteStreamWithModTime(path string, rs io.Reader, _ os.FileMode, modTime time.Time) error {
|
|
|
|
ws, err := t.dc.CreateWithModTime(context.Background(), filepath.Join(t.cfg.URL.Path, path), modTime)
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
defer func() { _ = ws.Close() }()
|
|
|
|
_, err = io.Copy(ws, rs)
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
2024-01-11 19:19:20 +01:00
|
|
|
func (t *WebDAVTarget) Move(src, dest string) error {
|
|
|
|
return t.dc.MoveAll(context.Background(), filepath.Join(t.cfg.URL.Path, src), dest, true)
|
|
|
|
}
|
|
|
|
|
2024-01-11 18:11:21 +01:00
|
|
|
func (t *WebDAVTarget) Rm(path string) error {
|
|
|
|
return t.dc.RemoveAll(context.Background(), filepath.Join(t.cfg.URL.Path, path))
|
|
|
|
}
|
|
|
|
|
2024-01-09 18:57:42 +01:00
|
|
|
func (t *WebDAVTarget) SetModificationTime(path string, mtime time.Time) error {
|
2024-01-10 20:00:32 +01:00
|
|
|
return t.dc.Touch(context.Background(), filepath.Join(t.cfg.URL.Path, path), mtime)
|
2024-01-09 18:57:42 +01:00
|
|
|
}
|