package sync import ( "context" "github.com/openziti/zrok/drives/davClient" "github.com/openziti/zrok/environment/env_core" "github.com/openziti/zrok/sdk/golang/sdk" "github.com/pkg/errors" "io" "net" "net/http" "net/url" "os" "path/filepath" "strings" "time" ) type ZrokTargetConfig struct { URL *url.URL Root env_core.Root } type ZrokTarget struct { cfg *ZrokTargetConfig dc *davClient.Client } type zrokDialContext struct { root env_core.Root } func (zdc *zrokDialContext) Dial(_ context.Context, _, addr string) (net.Conn, error) { share := strings.Split(addr, ":")[0] return sdk.NewDialer(share, zdc.root) } func NewZrokTarget(cfg *ZrokTargetConfig) (*ZrokTarget, error) { transport := http.DefaultTransport.(*http.Transport).Clone() transport.DialContext = (&zrokDialContext{cfg.Root}).Dial transport.TLSClientConfig.InsecureSkipVerify = true httpUrl := strings.Replace(cfg.URL.String(), "zrok:", "http:", 1) dc, err := davClient.NewClient(&http.Client{Transport: transport}, httpUrl) if err != nil { return nil, err } return &ZrokTarget{cfg: cfg, dc: dc}, nil } func (t *ZrokTarget) Inventory() ([]*Object, error) { 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 } fis, err := t.dc.Readdir(context.Background(), t.cfg.URL.Path, true) if err != nil { return nil, err } var objects []*Object for _, fi := range fis { if fi.Path != "/" { objects = append(objects, &Object{ Path: fi.Path, IsDir: fi.IsDir, Size: fi.Size, Modified: fi.ModTime, ETag: fi.ETag, }) } } return objects, nil } func (t *ZrokTarget) 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 { if fi.Path != "/" && fi.Path != t.cfg.URL.Path+"/" { objects = append(objects, &Object{ Path: filepath.Base(fi.Path), IsDir: fi.IsDir, Size: fi.Size, Modified: fi.ModTime, }) } } return objects, nil } func (t *ZrokTarget) Mkdir(path string) error { 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) } return t.dc.Mkdir(context.Background(), filepath.Join(t.cfg.URL.Path, path)) } func (t *ZrokTarget) ReadStream(path string) (io.ReadCloser, error) { return t.dc.Open(context.Background(), filepath.Join(t.cfg.URL.Path, path)) } func (t *ZrokTarget) WriteStream(path string, rs io.Reader, _ os.FileMode) error { ws, err := t.dc.Create(context.Background(), filepath.Join(t.cfg.URL.Path, path)) if err != nil { return err } defer func() { _ = ws.Close() }() _, err = io.Copy(ws, rs) if err != nil { return err } return nil } func (t *ZrokTarget) 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 } func (t *ZrokTarget) Move(src, dest string) error { return t.dc.MoveAll(context.Background(), filepath.Join(t.cfg.URL.Path, src), dest, true) } func (t *ZrokTarget) Rm(path string) error { return t.dc.RemoveAll(context.Background(), filepath.Join(t.cfg.URL.Path, path)) } func (t *ZrokTarget) SetModificationTime(path string, mtime time.Time) error { return t.dc.Touch(context.Background(), filepath.Join(t.cfg.URL.Path, path), mtime) }