From 849de88e68f5386d2c3fb0576a613682aebb47bc Mon Sep 17 00:00:00 2001 From: Noah Hsu Date: Sat, 3 Sep 2022 22:07:08 +0800 Subject: [PATCH] feat: add ftp driver --- drivers/all.go | 1 + drivers/ftp/driver.go | 134 ++++++++++++++++++++++++++++++++++++++++++ drivers/ftp/meta.go | 32 ++++++++++ drivers/ftp/types.go | 1 + drivers/ftp/util.go | 23 ++++++++ drivers/s3/driver.go | 2 +- go.mod | 3 + go.sum | 9 +++ 8 files changed, 204 insertions(+), 1 deletion(-) create mode 100644 drivers/ftp/driver.go create mode 100644 drivers/ftp/meta.go create mode 100644 drivers/ftp/types.go create mode 100644 drivers/ftp/util.go diff --git a/drivers/all.go b/drivers/all.go index 4a50b64155d..64d666a8732 100644 --- a/drivers/all.go +++ b/drivers/all.go @@ -4,6 +4,7 @@ import ( _ "github.com/alist-org/alist/v3/drivers/123" _ "github.com/alist-org/alist/v3/drivers/aliyundrive" _ "github.com/alist-org/alist/v3/drivers/baidu_netdisk" + _ "github.com/alist-org/alist/v3/drivers/ftp" _ "github.com/alist-org/alist/v3/drivers/google_drive" _ "github.com/alist-org/alist/v3/drivers/local" _ "github.com/alist-org/alist/v3/drivers/onedrive" diff --git a/drivers/ftp/driver.go b/drivers/ftp/driver.go new file mode 100644 index 00000000000..fed9238c679 --- /dev/null +++ b/drivers/ftp/driver.go @@ -0,0 +1,134 @@ +package ftp + +import ( + "context" + stdpath "path" + + "github.com/alist-org/alist/v3/internal/driver" + "github.com/alist-org/alist/v3/internal/errs" + "github.com/alist-org/alist/v3/internal/model" + "github.com/alist-org/alist/v3/pkg/utils" + "github.com/jlaffaye/ftp" +) + +type FTP struct { + model.Storage + Addition + conn *ftp.ServerConn +} + +func (d *FTP) Config() driver.Config { + return config +} + +func (d *FTP) GetAddition() driver.Additional { + return d.Addition +} + +func (d *FTP) Init(ctx context.Context, storage model.Storage) error { + d.Storage = storage + err := utils.Json.UnmarshalFromString(d.Storage.Addition, &d.Addition) + if err != nil { + return err + } + return d.login() +} + +func (d *FTP) Drop(ctx context.Context) error { + if d.conn != nil { + _ = d.conn.Logout() + } + return nil +} + +func (d *FTP) List(ctx context.Context, dir model.Obj, args model.ListArgs) ([]model.Obj, error) { + if err := d.login(); err != nil { + return nil, err + } + entries, err := d.conn.List(dir.GetPath()) + if err != nil { + return nil, err + } + res := make([]model.Obj, 0) + for i, _ := range entries { + entry := entries[i] + if entry.Name == "." || entry.Name == ".." { + continue + } + f := model.Object{ + Name: entry.Name, + Size: int64(entry.Size), + Modified: entry.Time, + IsFolder: entry.Type == ftp.EntryTypeFolder, + } + res = append(res, &f) + } + return res, nil +} + +//func (d *FTP) Get(ctx context.Context, path string) (model.Obj, error) { +// // this is optional +// return nil, errs.NotImplement +//} + +func (d *FTP) Link(ctx context.Context, file model.Obj, args model.LinkArgs) (*model.Link, error) { + if err := d.login(); err != nil { + return nil, err + } + resp, err := d.conn.Retr(file.GetPath()) + if err != nil { + return nil, err + } + return &model.Link{ + Data: resp, + }, nil +} + +func (d *FTP) MakeDir(ctx context.Context, parentDir model.Obj, dirName string) error { + if err := d.login(); err != nil { + return err + } + return d.conn.MakeDir(stdpath.Join(parentDir.GetPath(), dirName)) +} + +func (d *FTP) Move(ctx context.Context, srcObj, dstDir model.Obj) error { + if err := d.login(); err != nil { + return err + } + return d.conn.Rename(srcObj.GetPath(), stdpath.Join(dstDir.GetPath(), srcObj.GetName())) +} + +func (d *FTP) Rename(ctx context.Context, srcObj model.Obj, newName string) error { + if err := d.login(); err != nil { + return err + } + return d.conn.Rename(srcObj.GetPath(), stdpath.Join(stdpath.Dir(srcObj.GetPath()), newName)) +} + +func (d *FTP) Copy(ctx context.Context, srcObj, dstDir model.Obj) error { + return errs.NotSupport +} + +func (d *FTP) Remove(ctx context.Context, obj model.Obj) error { + if err := d.login(); err != nil { + return err + } + if obj.IsDir() { + return d.conn.RemoveDirRecur(obj.GetPath()) + } else { + return d.conn.Delete(obj.GetPath()) + } +} + +func (d *FTP) Put(ctx context.Context, dstDir model.Obj, stream model.FileStreamer, up driver.UpdateProgress) error { + if err := d.login(); err != nil { + return err + } + return d.conn.Stor(stdpath.Join(dstDir.GetPath(), stream.GetName()), stream) +} + +func (d *FTP) Other(ctx context.Context, args model.OtherArgs) (interface{}, error) { + return nil, errs.NotSupport +} + +var _ driver.Driver = (*FTP)(nil) diff --git a/drivers/ftp/meta.go b/drivers/ftp/meta.go new file mode 100644 index 00000000000..f235ec5bf0c --- /dev/null +++ b/drivers/ftp/meta.go @@ -0,0 +1,32 @@ +package ftp + +import ( + "github.com/alist-org/alist/v3/internal/driver" + "github.com/alist-org/alist/v3/internal/op" +) + +type Addition struct { + Address string `json:"address" required:"true"` + Username string `json:"username" required:"true"` + Password string `json:"password" required:"true"` + driver.RootFolderPath +} + +var config = driver.Config{ + Name: "FTP", + LocalSort: false, + OnlyLocal: false, + OnlyProxy: false, + NoCache: false, + NoUpload: false, + NeedMs: false, + DefaultRoot: "root, / or other", +} + +func New() driver.Driver { + return &FTP{} +} + +func init() { + op.RegisterDriver(config, New) +} diff --git a/drivers/ftp/types.go b/drivers/ftp/types.go new file mode 100644 index 00000000000..4c9820334a7 --- /dev/null +++ b/drivers/ftp/types.go @@ -0,0 +1 @@ +package ftp diff --git a/drivers/ftp/util.go b/drivers/ftp/util.go new file mode 100644 index 00000000000..4169e9fd53c --- /dev/null +++ b/drivers/ftp/util.go @@ -0,0 +1,23 @@ +package ftp + +import "github.com/jlaffaye/ftp" + +// do others that not defined in Driver interface + +func (d *FTP) login() error { + if d.conn != nil { + _, err := d.conn.CurrentDir() + if err == nil { + return nil + } + } + conn, err := ftp.Dial(d.Address) + if err != nil { + return err + } + err = conn.Login(d.Username, d.Password) + if err != nil { + return err + } + return nil +} diff --git a/drivers/s3/driver.go b/drivers/s3/driver.go index 8307628e60a..ce3e0b865ad 100644 --- a/drivers/s3/driver.go +++ b/drivers/s3/driver.go @@ -127,7 +127,7 @@ func (d *S3) Rename(ctx context.Context, srcObj model.Obj, newName string) error } func (d *S3) Copy(ctx context.Context, srcObj, dstDir model.Obj) error { - return d.copy(srcObj.GetPath(), stdpath.Join(dstDir.GetPath(), stdpath.Base(srcObj.GetPath())), srcObj.IsDir()) + return d.copy(srcObj.GetPath(), stdpath.Join(dstDir.GetPath(), srcObj.GetName()), srcObj.IsDir()) } func (d *S3) Remove(ctx context.Context, obj model.Obj) error { diff --git a/go.mod b/go.mod index a5be458daa8..922f7d24424 100644 --- a/go.mod +++ b/go.mod @@ -34,6 +34,8 @@ require ( github.com/go-resty/resty/v2 v2.7.0 // indirect github.com/go-sql-driver/mysql v1.6.0 // indirect github.com/goccy/go-json v0.9.7 // indirect + github.com/hashicorp/errwrap v1.1.0 // indirect + github.com/hashicorp/go-multierror v1.1.1 // indirect github.com/inconshreveable/mousetrap v1.0.0 // indirect github.com/jackc/chunkreader/v2 v2.0.1 // indirect github.com/jackc/pgconn v1.12.1 // indirect @@ -45,6 +47,7 @@ require ( github.com/jackc/pgx/v4 v4.16.1 // indirect github.com/jinzhu/inflection v1.0.0 // indirect github.com/jinzhu/now v1.1.5 // indirect + github.com/jlaffaye/ftp v0.0.0-20220829015825-b85cf1edccd4 // indirect github.com/jmespath/go-jmespath v0.4.0 // indirect github.com/leodido/go-urn v1.2.1 // indirect github.com/mattn/go-isatty v0.0.14 // indirect diff --git a/go.sum b/go.sum index 6f82a10e6b4..2f8970eebfb 100644 --- a/go.sum +++ b/go.sum @@ -62,6 +62,11 @@ github.com/google/uuid v1.3.0 h1:t6JiXgmwXMjEs8VusXIJk2BXHsn+wx8BZdTaoZ5fu7I= github.com/google/uuid v1.3.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/gorilla/websocket v1.5.0 h1:PPwGk2jz7EePpoHN/+ClbZu8SPxiqlu12wZP/3sWmnc= github.com/gorilla/websocket v1.5.0/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE= +github.com/hashicorp/errwrap v1.0.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4= +github.com/hashicorp/errwrap v1.1.0 h1:OxrOeh75EUXMY8TBjag2fzXGZ40LB6IKw45YeGUDY2I= +github.com/hashicorp/errwrap v1.1.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4= +github.com/hashicorp/go-multierror v1.1.1 h1:H5DkEtf6CXdFp0N0Em5UCwQpXMWke8IA0+lD48awMYo= +github.com/hashicorp/go-multierror v1.1.1/go.mod h1:iw975J/qwKPdAO1clOe2L8331t/9/fmwbPZ6JB6eMoM= github.com/inconshreveable/mousetrap v1.0.0 h1:Z8tu5sraLXCXIcARxBp/8cbvlwVa7Z1NHg9XEKhtSvM= github.com/inconshreveable/mousetrap v1.0.0/go.mod h1:PxqpIevigyE2G7u3NXJIT2ANytuPF1OarO4DADm73n8= github.com/jackc/chunkreader v1.0.0/go.mod h1:RT6O25fNZIuasFJRyZ4R/Y2BbhasbmZXF9QQ7T3kePo= @@ -116,6 +121,8 @@ github.com/jinzhu/inflection v1.0.0/go.mod h1:h+uFLlag+Qp1Va5pdKtLDYj+kHp5pxUVkr github.com/jinzhu/now v1.1.4/go.mod h1:d3SSVoowX0Lcu0IBviAWJpolVfI5UJVZZ7cO71lE/z8= github.com/jinzhu/now v1.1.5 h1:/o9tlHleP7gOFmsnYNz3RGnqzefHA47wQpKrrdTIwXQ= github.com/jinzhu/now v1.1.5/go.mod h1:d3SSVoowX0Lcu0IBviAWJpolVfI5UJVZZ7cO71lE/z8= +github.com/jlaffaye/ftp v0.0.0-20220829015825-b85cf1edccd4 h1:8bWaY08VCoFn17gezYWKLhCwAJr2Er8tUOZCvDVshos= +github.com/jlaffaye/ftp v0.0.0-20220829015825-b85cf1edccd4/go.mod h1:hhq4G4crv+nW2qXtNYcuzLeOudG92Ps37HEKeg2e3lE= github.com/jmespath/go-jmespath v0.4.0 h1:BEgLn5cpjn8UN1mAw4NjwDrS35OdebyEtFe+9YPoQUg= github.com/jmespath/go-jmespath v0.4.0/go.mod h1:T8mJZnbsbmF+m6zOOFylbeCJqk5+pHWvzYPziyZiYoo= github.com/jmespath/go-jmespath/internal/testify v1.5.1/go.mod h1:L3OGu8Wl2/fWfCI6z80xFu9LTZmf1ZRjMHUOPmWr69U= @@ -202,6 +209,7 @@ github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/ github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/stretchr/testify v1.7.1 h1:5TQK59W5E3v0r2duFAb7P95B6hEeOyEnHRa8MjYSMTY= github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= +github.com/stretchr/testify v1.8.0 h1:pSgiaMZlXftHpm5L7V1+rVB+AZJydKsMxsQBIJw4PKk= github.com/ugorji/go v1.1.7/go.mod h1:kZn38zHttfInRq0xu/PH0az30d+z6vm202qpg1oXVMw= github.com/ugorji/go v1.2.7/go.mod h1:nF9osbDWLy6bDVv/Rtoh6QgnvNDpmCalQV5urGCCS6M= github.com/ugorji/go/codec v1.1.7/go.mod h1:Ax+UKWsSmolVDwsd+7N3ZtXu+yMGCf907BLYF3GoBXY= @@ -318,6 +326,7 @@ gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ= gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b h1:h8qDotaEPuJATrMmW04NCwg7v22aHH28wwpauUhK9Oo= gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= +gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= gorm.io/driver/mysql v1.3.4 h1:/KoBMgsUHC3bExsekDcmNYaBnfH2WNeFuXqqrqMc98Q= gorm.io/driver/mysql v1.3.4/go.mod h1:s4Tq0KmD0yhPGHbZEwg1VPlH0vT/GBHJZorPzhcxBUE= gorm.io/driver/postgres v1.3.7 h1:FKF6sIMDHDEvvMF/XJvbnCl0nu6KSKUaPXevJ4r+VYQ=