Use goftp.io/server/v2 instead of goftp.io/server
This commit is contained in:
4
go.mod
4
go.mod
@@ -3,9 +3,7 @@ module goftp.io/ftpd
|
||||
go 1.12
|
||||
|
||||
require (
|
||||
gitea.com/goftp/leveldb-auth v0.0.0-20190711092309-e8e3d5ad5ac8
|
||||
gitea.com/goftp/leveldb-perm v0.0.0-20190711092750-00b79e6da99c
|
||||
gitea.com/goftp/qiniu-driver v0.0.0-20191027083326-6e505f23c4f0
|
||||
gitea.com/lunny/tango v0.6.1
|
||||
gitea.com/tango/binding v0.0.0-20200204091933-f90d5bac28d2
|
||||
gitea.com/tango/flash v0.0.0-20190606021323-2b17fd0aed7c
|
||||
@@ -17,5 +15,5 @@ require (
|
||||
github.com/shurcooL/vfsgen v0.0.0-20181202132449-6a9ea43bcacd
|
||||
github.com/syndtr/goleveldb v1.0.0
|
||||
github.com/unknwon/goconfig v0.0.0-20190425194916-3dba17dd7b9e
|
||||
goftp.io/server v0.3.5-0.20200428022247-5cd49dc54bdb
|
||||
goftp.io/server/v2 v2.0.0
|
||||
)
|
||||
|
||||
32
go.sum
32
go.sum
@@ -1,11 +1,5 @@
|
||||
gitea.com/goftp/file-driver v0.0.0-20190712091345-f79c2ed973f8 h1:uCOouUDYTyK+Zz86dnkvDBUKnbqfqA/Fk/hxp5sXWv8=
|
||||
gitea.com/goftp/file-driver v0.0.0-20190712091345-f79c2ed973f8/go.mod h1:ghdogu0Da3rwYCSJ20JPgTiMcDpzeRbzvuFIOOW3G7w=
|
||||
gitea.com/goftp/leveldb-auth v0.0.0-20190711092309-e8e3d5ad5ac8 h1:dvw8cqH+NbkiR0ikE3NSZpRKyBL9fOPuG9h4lKKDJ30=
|
||||
gitea.com/goftp/leveldb-auth v0.0.0-20190711092309-e8e3d5ad5ac8/go.mod h1:TdnYhl3DGy5KS7bkOmfQTmDpHi2jhNWbEWW6yvWe8V8=
|
||||
gitea.com/goftp/leveldb-perm v0.0.0-20190711092750-00b79e6da99c h1:PZpmF+sly/Uasc6e+JsbuJb6lFOkv1HnXOKekPnNW14=
|
||||
gitea.com/goftp/leveldb-perm v0.0.0-20190711092750-00b79e6da99c/go.mod h1:MRIEZqzha2uKixiEk+D1qxh6utiACnnYRC9yRAbD/Vw=
|
||||
gitea.com/goftp/qiniu-driver v0.0.0-20191027083326-6e505f23c4f0 h1:5RrQe6xVdRnQtWy8+c/bSCrKi0mCLd3WkTOF7hdfU3k=
|
||||
gitea.com/goftp/qiniu-driver v0.0.0-20191027083326-6e505f23c4f0/go.mod h1:ANpqO7R4FuMV14mOGVE/FlMr7wDDKkfKBfMKnBbeBWU=
|
||||
gitea.com/lunny/log v0.0.0-20190322053110-01b5df579c4e h1:r1en/D7xJmcY24VkHkjkcJFa+7ZWubVWPBrvsHkmHxk=
|
||||
gitea.com/lunny/log v0.0.0-20190322053110-01b5df579c4e/go.mod h1:uJEsN4LQpeGYRCjuPXPZBClU7N5pWzGuyF4uqLpE/e0=
|
||||
gitea.com/lunny/tango v0.6.0 h1:Z9wX9fq5e2y9Da+cmlFpE3aAo1+GfTuhYIPPYdCD6kM=
|
||||
@@ -23,8 +17,6 @@ gitea.com/tango/session v0.0.0-20190606020146-89f560e05167 h1:NqgUqEIKZD7hm++1Y5
|
||||
gitea.com/tango/session v0.0.0-20190606020146-89f560e05167/go.mod h1:00EhoJmAqWFaTCwFu5/6WAu7iNCIAj95UbDLG7UDdKU=
|
||||
gitea.com/tango/xsrf v0.0.0-20190606015726-fb1b2fb84238 h1:ti3ZmMFweqSwRCKbCVzza7SmNg3csKf58W2QY3nG/Ms=
|
||||
gitea.com/tango/xsrf v0.0.0-20190606015726-fb1b2fb84238/go.mod h1:TTynPmrkQOiNzSrbDsDEL273wUIvPC7d+sUweqVPOkI=
|
||||
github.com/Unknwon/com v0.0.0-20190321035513-0fed4efef755 h1:1B7wb36fHLSwZfHg6ngZhhtIEHQjiC5H4p7qQGBEffg=
|
||||
github.com/Unknwon/com v0.0.0-20190321035513-0fed4efef755/go.mod h1:voKvFVpXBJxdIPeqjoJuLK+UVcRlo/JLjeToGxPYu68=
|
||||
github.com/davecgh/go-spew v1.1.0 h1:ZDRjVQ15GmhC3fiQ8ni8+OwkZQO4DARzQgrnXU1Liz8=
|
||||
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
|
||||
@@ -34,10 +26,6 @@ github.com/fsnotify/fsnotify v1.4.7 h1:IXs+QLmnXW2CcXuY+8Mzv/fWEsPGWxqefPtCP5CnV
|
||||
github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo=
|
||||
github.com/go-xweb/uuid v0.0.0-20140604020037-d7dce341f851 h1:D46USD6oGNWzoJ/h5CWaFq3ELLoLoJzllJ03Xh78VYg=
|
||||
github.com/go-xweb/uuid v0.0.0-20140604020037-d7dce341f851/go.mod h1:OmDEC58ZYO1Esk+Uy32SB6LWof9lyROl7q76dBFOCWw=
|
||||
github.com/goftp/file-driver v0.0.0-20180502053751-5d604a0fc0c9 h1:cC0Hbb+18DJ4i6ybqDybvj4wdIDS4vnD0QEci98PgM8=
|
||||
github.com/goftp/file-driver v0.0.0-20180502053751-5d604a0fc0c9/go.mod h1:GpOj6zuVBG3Inr9qjEnuVTgBlk2lZ1S9DcoFiXWyKss=
|
||||
github.com/goftp/server v0.0.0-20190304020633-eabccc535b5a h1:XTJuuzIub3zu2FgPqdFM9XFYYisXWu2hN/rFwayAIcY=
|
||||
github.com/goftp/server v0.0.0-20190304020633-eabccc535b5a/go.mod h1:k/SS6VWkxY7dHPhoMQ8IdRu8L4lQtmGbhyXGg+vCnXE=
|
||||
github.com/golang/protobuf v1.2.0 h1:P3YflyNX/ehuJFLhxviNdFxQPkGK5cDcApsge1SqnvM=
|
||||
github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
|
||||
github.com/golang/snappy v0.0.0-20180518054509-2e65f85255db h1:woRePGFeVFfLKN/pOkfl+p/TAqKOfFu+7KPlMVpok/w=
|
||||
@@ -55,7 +43,6 @@ github.com/jtolds/gls v4.20.0+incompatible/go.mod h1:QJZ7F/aHp+rZTRtaJ1ow/lLfFfV
|
||||
github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ=
|
||||
github.com/lunny/log v0.0.0-20160921050905-7887c61bf0de h1:nyxwRdWHAVxpFcDThedEgQ07DbcRc5xgNObtbTp76fk=
|
||||
github.com/lunny/log v0.0.0-20160921050905-7887c61bf0de/go.mod h1:3q8WtuPQsoRbatJuy3nvq/hRSvuBJrHHr+ybPPiNvHQ=
|
||||
github.com/lunny/tango v0.5.6/go.mod h1:qW1SakbmM67DdOHN6mipeYWhB1Uu6lYsgU3u6fQmu5o=
|
||||
github.com/mattn/go-sqlite3 v1.10.0 h1:jbhqpg7tQe4SupckyijYiy0mJJ/pRyHvXf7JdWK860o=
|
||||
github.com/mattn/go-sqlite3 v1.10.0/go.mod h1:FPy6KqzDD04eiIsT53CuJW3U88zkxoIYsOqkbpncsNc=
|
||||
github.com/minio/minio-go/v6 v6.0.46 h1:waExJtO53xrnsNX//7cSc1h3478wqTryDx4RVD7o26I=
|
||||
@@ -73,12 +60,6 @@ github.com/oxtoacart/bpool v0.0.0-20190530202638-03653db5a59c h1:rp5dCmg/yLR3mgF
|
||||
github.com/oxtoacart/bpool v0.0.0-20190530202638-03653db5a59c/go.mod h1:X07ZCGwUbLaax7L0S3Tw4hpejzu63ZrrQiUe6W0hcy0=
|
||||
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
|
||||
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
||||
github.com/qiniu/api.v6 v6.0.9+incompatible h1:mG/jDC2GD9u2DqP1yIbX+USd3S60bQYSRh6Su6EbnsU=
|
||||
github.com/qiniu/api.v6 v6.0.9+incompatible/go.mod h1:iJeMuW0i5a4O1SFx2LYtxY+9hkTfkYQJL8xTaGYGDA4=
|
||||
github.com/qiniu/bytes v0.0.0-20140728010635-4887e7b2bde3 h1:PXNXOJs716xnMtH6kMkPlQfSG+x8m2Q31uTN+dQF10c=
|
||||
github.com/qiniu/bytes v0.0.0-20140728010635-4887e7b2bde3/go.mod h1:5KFTwj5mNES3FmpAF+DEDuVolB/OVAUj3oNqPLriYbo=
|
||||
github.com/qiniu/rpc v0.0.0-20140728010754-30c22466d920 h1:G6C/49DiPwATK+4oBi6OCf14WzCwNMTC1s5Udov4dwQ=
|
||||
github.com/qiniu/rpc v0.0.0-20140728010754-30c22466d920/go.mod h1:vUC++Z6RsGp85+Oyiu1l5+mpao6xy/Vi1J/G1fKiwDk=
|
||||
github.com/shurcooL/httpfs v0.0.0-20190707220628-8d4bc4ba7749 h1:bUGsEnyNbVPw06Bs80sCeARAlK8lhwqGyi6UT8ymuGk=
|
||||
github.com/shurcooL/httpfs v0.0.0-20190707220628-8d4bc4ba7749/go.mod h1:ZY1cvUeJuFPAdZ/B6v7RHavJWZn2YPVFQ1OSXhCGOkg=
|
||||
github.com/shurcooL/vfsgen v0.0.0-20181202132449-6a9ea43bcacd h1:ug7PpSOB5RBPK1Kg6qskGBoP3Vnj/aNYFTznWvlkGo0=
|
||||
@@ -97,21 +78,12 @@ github.com/stretchr/testify v1.3.0 h1:TivCn/peBQ7UY8ooIcPgZFpTNSz0Q2U6UrFlUfqbe0
|
||||
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
|
||||
github.com/syndtr/goleveldb v1.0.0 h1:fBdIW9lB4Iz0n9khmH8w27SJ3QEJ7+IgjPEwGSZiFdE=
|
||||
github.com/syndtr/goleveldb v1.0.0/go.mod h1:ZVVdQEZoIme9iO1Ch2Jdy24qqXrMMOU6lpPAyBWyWuQ=
|
||||
github.com/tango-contrib/binding v0.0.0-20170526073042-f86db18c4a9e/go.mod h1:MNdSKQVAIj+pPJfwPoqBSnG775qIUlH+T60yFz7mcTE=
|
||||
github.com/tango-contrib/flash v0.0.0-20170619055053-40456640d164/go.mod h1:fjy8sKkKvITeoogusMWTPg9pRd83vivbUUnMyE6nJfQ=
|
||||
github.com/tango-contrib/renders v0.0.0-20170526074344-86dba79a0240/go.mod h1:4kuIhm8N9wR3cGUFszRJQAvBRUaA7mw001P9BHKh0/s=
|
||||
github.com/tango-contrib/session v0.0.0-20170526074221-3115f8ddf72d/go.mod h1:nAEED8H84/GpN5Ewf/dDJkOM7S0XWyxYAdwlkMTYe2w=
|
||||
github.com/tango-contrib/xsrf v0.0.0-20170526074244-3dbe17fdad36/go.mod h1:i/hS2Yy51czffHIDR+2DMtjCRKKTP8WuYa3XHsh98rQ=
|
||||
github.com/unknwon/com v1.0.1 h1:3d1LTxD+Lnf3soQiD4Cp/0BRB+Rsa/+RTvz8GMMzIXs=
|
||||
github.com/unknwon/com v1.0.1/go.mod h1:tOOxU81rwgoCLoOVVPHb6T/wt8HZygqH5id+GNnlCXM=
|
||||
github.com/unknwon/goconfig v0.0.0-20190425194916-3dba17dd7b9e h1:WIu9z0oMGXDY19FEawfE3gNMXbJ3n2KDRvTHBnkxjnM=
|
||||
github.com/unknwon/goconfig v0.0.0-20190425194916-3dba17dd7b9e/go.mod h1:qu2ZQ/wcC/if2u32263HTVC39PeOQRSmidQk3DuDFQ8=
|
||||
goftp.io/ftpd v0.0.0-20180821024231-89d1eae9c018/go.mod h1:411wH3gYllZ079MJ6lG5UI++ERPF+VklHvGlMux980A=
|
||||
goftp.io/server v0.0.0-20190712054601-1149070ae46b/go.mod h1:xreggPYu7ZuNe9PfbxiQca7bYGwU44IvlCCg3KzWJtQ=
|
||||
goftp.io/server v0.0.0-20190812034929-9b3874d17690 h1:vHUbHALX1kwsxK6gFQeY19+10zk/pSABN/0vumWLCxQ=
|
||||
goftp.io/server v0.0.0-20190812034929-9b3874d17690/go.mod h1:99FISrRpwKfaL4Ey/dX8N48WToveng/s2OXR5sJ3cnc=
|
||||
goftp.io/server v0.3.5-0.20200428022247-5cd49dc54bdb h1:KyExRJCQa5lv7e1djnrZHOahYVdEKo3VMhTlNVrfrJw=
|
||||
goftp.io/server v0.3.5-0.20200428022247-5cd49dc54bdb/go.mod h1:hFZeR656ErRt3ojMKt7H10vQ5nuWV1e0YeUTeorlR6k=
|
||||
goftp.io/server/v2 v2.0.0 h1:FF8JKXXKDxAeO1uXEZz7G+IZwCDhl19dpVIlDtp3QAg=
|
||||
goftp.io/server/v2 v2.0.0/go.mod h1:7+H/EIq7tXdfo1Muu5p+l3oQ6rYkDZ8lY7IM5d5kVdQ=
|
||||
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
|
||||
golang.org/x/crypto v0.0.0-20190513172903-22d7a77e9e5f/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
|
||||
golang.org/x/crypto v0.0.0-20190701094942-4def268fd1a4 h1:HuIa8hRrWRSrqYzx1qI49NNxhdi2PrY7gxVSq1JjLDc=
|
||||
|
||||
45
main.go
45
main.go
@@ -7,13 +7,14 @@ import (
|
||||
|
||||
"goftp.io/ftpd/web"
|
||||
|
||||
ldbauth "gitea.com/goftp/leveldb-auth"
|
||||
ldbperm "gitea.com/goftp/leveldb-perm"
|
||||
qiniudriver "gitea.com/goftp/qiniu-driver"
|
||||
"github.com/lunny/log"
|
||||
_ "github.com/shurcooL/vfsgen"
|
||||
"github.com/syndtr/goleveldb/leveldb"
|
||||
"goftp.io/server"
|
||||
ldbauth "goftp.io/ftpd/modules/ldbauth"
|
||||
"goftp.io/server/v2"
|
||||
"goftp.io/server/v2/driver/file"
|
||||
minio_driver "goftp.io/server/v2/driver/minio"
|
||||
)
|
||||
|
||||
var (
|
||||
@@ -47,7 +48,7 @@ func main() {
|
||||
perm = server.NewSimplePerm("root", "root")
|
||||
}
|
||||
|
||||
var factory server.DriverFactory
|
||||
var driver server.Driver
|
||||
switch driverType {
|
||||
case "file":
|
||||
_, err = os.Lstat(rootPath)
|
||||
@@ -57,26 +58,30 @@ func main() {
|
||||
fmt.Println(err)
|
||||
return
|
||||
}
|
||||
factory = &server.FileDriverFactory{
|
||||
RootPath: rootPath,
|
||||
Perm: perm,
|
||||
driver, err = file.NewDriver(rootPath)
|
||||
if err != nil {
|
||||
fmt.Println(err)
|
||||
return
|
||||
}
|
||||
case "qiniu":
|
||||
factory = qiniudriver.NewQiniuDriverFactory(
|
||||
/*factory = qiniudriver.NewQiniuDriverFactory(
|
||||
qiniu.AccessKey,
|
||||
qiniu.SecretKey,
|
||||
qiniu.Bucket,
|
||||
)
|
||||
)*/
|
||||
case "minio":
|
||||
factory = server.NewMinioDriverFactory(
|
||||
driver, err = minio_driver.NewDriver(
|
||||
minio.Endpoint,
|
||||
minio.AccessKey,
|
||||
minio.SecretKey,
|
||||
"",
|
||||
minio.Bucket,
|
||||
minio.UseSSL,
|
||||
perm,
|
||||
)
|
||||
if err != nil {
|
||||
fmt.Println(err)
|
||||
return
|
||||
}
|
||||
default:
|
||||
fmt.Println("no driver type input")
|
||||
return
|
||||
@@ -86,17 +91,18 @@ func main() {
|
||||
if webCfg.Enabled {
|
||||
web.DB = auth
|
||||
web.Perm = perm
|
||||
web.Factory = factory
|
||||
web.Driver = driver
|
||||
|
||||
go web.Web(webCfg.Listen, "static", "templates", admin, pass,
|
||||
webCfg.TLS, webCfg.CertFile, webCfg.KeyFile)
|
||||
}
|
||||
|
||||
opt := &server.ServerOpts{
|
||||
Name: serv.Name,
|
||||
Factory: factory,
|
||||
Port: serv.Port,
|
||||
Auth: auth,
|
||||
opt := &server.Options{
|
||||
Name: serv.Name,
|
||||
Driver: driver,
|
||||
Port: serv.Port,
|
||||
Auth: auth,
|
||||
Perm: perm,
|
||||
}
|
||||
|
||||
opt.TLS = serv.TLS
|
||||
@@ -105,7 +111,10 @@ func main() {
|
||||
opt.ExplicitFTPS = opt.TLS
|
||||
|
||||
// start ftp server
|
||||
ftpServer := server.NewServer(opt)
|
||||
ftpServer, err := server.NewServer(opt)
|
||||
if err != nil {
|
||||
log.Fatal("Error creating server:", err)
|
||||
}
|
||||
log.Info("FTP Server", version)
|
||||
err = ftpServer.ListenAndServe()
|
||||
if err != nil {
|
||||
|
||||
@@ -7,13 +7,14 @@ import (
|
||||
"goftp.io/ftpd/web"
|
||||
"github.com/syndtr/goleveldb/leveldb"
|
||||
"github.com/syndtr/goleveldb/leveldb/util"
|
||||
"goftp.io/server/v2"
|
||||
)
|
||||
|
||||
type LDBAuth struct {
|
||||
DB *leveldb.DB
|
||||
}
|
||||
|
||||
func (db *LDBAuth) CheckPasswd(user, pass string) (bool, error) {
|
||||
func (db *LDBAuth) CheckPasswd(ctx *server.Context, user, pass string) (bool, error) {
|
||||
p, err := db.GetUser(user)
|
||||
if err != nil {
|
||||
if err == leveldb.ErrNotFound {
|
||||
@@ -135,4 +136,4 @@ func (db *LDBAuth) GroupUser(group string, users *[]string) error {
|
||||
*users = append(*users, key[len(prefix):])
|
||||
}
|
||||
return nil
|
||||
}
|
||||
}
|
||||
20
vendor/gitea.com/goftp/leveldb-auth/go.mod
generated
vendored
20
vendor/gitea.com/goftp/leveldb-auth/go.mod
generated
vendored
@@ -1,20 +0,0 @@
|
||||
module gitea.com/goftp/leveldb-auth
|
||||
|
||||
go 1.12
|
||||
|
||||
require (
|
||||
github.com/Unknwon/com v0.0.0-20190321035513-0fed4efef755 // indirect
|
||||
github.com/go-xweb/uuid v0.0.0-20140604020037-d7dce341f851 // indirect
|
||||
github.com/goftp/file-driver v0.0.0-20180502053751-5d604a0fc0c9 // indirect
|
||||
github.com/goftp/server v0.0.0-20190304020633-eabccc535b5a // indirect
|
||||
github.com/jlaffaye/ftp v0.0.0-20190624084859-c1312a7102bf // indirect
|
||||
github.com/lunny/tango v0.5.6 // indirect
|
||||
github.com/oxtoacart/bpool v0.0.0-20190530202638-03653db5a59c // indirect
|
||||
github.com/syndtr/goleveldb v1.0.0
|
||||
github.com/tango-contrib/binding v0.0.0-20170526073042-f86db18c4a9e // indirect
|
||||
github.com/tango-contrib/flash v0.0.0-20170619055053-40456640d164 // indirect
|
||||
github.com/tango-contrib/renders v0.0.0-20170526074344-86dba79a0240 // indirect
|
||||
github.com/tango-contrib/session v0.0.0-20170526074221-3115f8ddf72d // indirect
|
||||
github.com/tango-contrib/xsrf v0.0.0-20170526074244-3dbe17fdad36 // indirect
|
||||
goftp.io/ftpd v0.0.0-20180821024231-89d1eae9c018
|
||||
)
|
||||
76
vendor/gitea.com/goftp/leveldb-auth/go.sum
generated
vendored
76
vendor/gitea.com/goftp/leveldb-auth/go.sum
generated
vendored
@@ -1,76 +0,0 @@
|
||||
gitea.com/lunny/log v0.0.0-20190322053110-01b5df579c4e h1:r1en/D7xJmcY24VkHkjkcJFa+7ZWubVWPBrvsHkmHxk=
|
||||
gitea.com/lunny/log v0.0.0-20190322053110-01b5df579c4e/go.mod h1:uJEsN4LQpeGYRCjuPXPZBClU7N5pWzGuyF4uqLpE/e0=
|
||||
github.com/Unknwon/com v0.0.0-20190321035513-0fed4efef755 h1:1B7wb36fHLSwZfHg6ngZhhtIEHQjiC5H4p7qQGBEffg=
|
||||
github.com/Unknwon/com v0.0.0-20190321035513-0fed4efef755/go.mod h1:voKvFVpXBJxdIPeqjoJuLK+UVcRlo/JLjeToGxPYu68=
|
||||
github.com/davecgh/go-spew v1.1.0 h1:ZDRjVQ15GmhC3fiQ8ni8+OwkZQO4DARzQgrnXU1Liz8=
|
||||
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
github.com/fsnotify/fsnotify v1.4.7 h1:IXs+QLmnXW2CcXuY+8Mzv/fWEsPGWxqefPtCP5CnV9I=
|
||||
github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo=
|
||||
github.com/go-xweb/uuid v0.0.0-20140604020037-d7dce341f851 h1:D46USD6oGNWzoJ/h5CWaFq3ELLoLoJzllJ03Xh78VYg=
|
||||
github.com/go-xweb/uuid v0.0.0-20140604020037-d7dce341f851/go.mod h1:OmDEC58ZYO1Esk+Uy32SB6LWof9lyROl7q76dBFOCWw=
|
||||
github.com/goftp/file-driver v0.0.0-20180502053751-5d604a0fc0c9 h1:cC0Hbb+18DJ4i6ybqDybvj4wdIDS4vnD0QEci98PgM8=
|
||||
github.com/goftp/file-driver v0.0.0-20180502053751-5d604a0fc0c9/go.mod h1:GpOj6zuVBG3Inr9qjEnuVTgBlk2lZ1S9DcoFiXWyKss=
|
||||
github.com/goftp/server v0.0.0-20190304020633-eabccc535b5a h1:XTJuuzIub3zu2FgPqdFM9XFYYisXWu2hN/rFwayAIcY=
|
||||
github.com/goftp/server v0.0.0-20190304020633-eabccc535b5a/go.mod h1:k/SS6VWkxY7dHPhoMQ8IdRu8L4lQtmGbhyXGg+vCnXE=
|
||||
github.com/golang/protobuf v1.2.0 h1:P3YflyNX/ehuJFLhxviNdFxQPkGK5cDcApsge1SqnvM=
|
||||
github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
|
||||
github.com/golang/snappy v0.0.0-20180518054509-2e65f85255db h1:woRePGFeVFfLKN/pOkfl+p/TAqKOfFu+7KPlMVpok/w=
|
||||
github.com/golang/snappy v0.0.0-20180518054509-2e65f85255db/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q=
|
||||
github.com/gopherjs/gopherjs v0.0.0-20181103185306-d547d1d9531e h1:JKmoR8x90Iww1ks85zJ1lfDGgIiMDuIptTOhJq+zKyg=
|
||||
github.com/gopherjs/gopherjs v0.0.0-20181103185306-d547d1d9531e/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY=
|
||||
github.com/hpcloud/tail v1.0.0 h1:nfCOvKYfkgYP8hkirhJocXT2+zOD8yUNjXaWfTlyFKI=
|
||||
github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU=
|
||||
github.com/jlaffaye/ftp v0.0.0-20190624084859-c1312a7102bf h1:2IYBd5TD/maMqTU2YUzp2tJL4cNaOYQ9EBullN9t9pk=
|
||||
github.com/jlaffaye/ftp v0.0.0-20190624084859-c1312a7102bf/go.mod h1:lli8NYPQOFy3O++YmYbqVgOcQ1JPCwdOy+5zSjKJ9qY=
|
||||
github.com/jtolds/gls v4.2.1+incompatible h1:fSuqC+Gmlu6l/ZYAoZzx2pyucC8Xza35fpRVWLVmUEE=
|
||||
github.com/jtolds/gls v4.2.1+incompatible/go.mod h1:QJZ7F/aHp+rZTRtaJ1ow/lLfFfVYBRgL+9YlvaHOwJU=
|
||||
github.com/lunny/tango v0.5.6 h1:QeUe+2ksZ3LScC+SKhDbS1wbS/ctuyRnZ3fAsL10J4M=
|
||||
github.com/lunny/tango v0.5.6/go.mod h1:qW1SakbmM67DdOHN6mipeYWhB1Uu6lYsgU3u6fQmu5o=
|
||||
github.com/mattn/go-sqlite3 v1.10.0 h1:jbhqpg7tQe4SupckyijYiy0mJJ/pRyHvXf7JdWK860o=
|
||||
github.com/mattn/go-sqlite3 v1.10.0/go.mod h1:FPy6KqzDD04eiIsT53CuJW3U88zkxoIYsOqkbpncsNc=
|
||||
github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=
|
||||
github.com/onsi/ginkgo v1.7.0 h1:WSHQ+IS43OoUrWtD1/bbclrwK8TTH5hzp+umCiuxHgs=
|
||||
github.com/onsi/ginkgo v1.7.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=
|
||||
github.com/onsi/gomega v1.4.3 h1:RE1xgDvH7imwFD45h+u2SgIfERHlS2yNG4DObb5BSKU=
|
||||
github.com/onsi/gomega v1.4.3/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1CpauHY=
|
||||
github.com/oxtoacart/bpool v0.0.0-20190530202638-03653db5a59c h1:rp5dCmg/yLR3mgFuSOe4oEnDDmGLROTvMragMUXpTQw=
|
||||
github.com/oxtoacart/bpool v0.0.0-20190530202638-03653db5a59c/go.mod h1:X07ZCGwUbLaax7L0S3Tw4hpejzu63ZrrQiUe6W0hcy0=
|
||||
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
|
||||
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
||||
github.com/smartystreets/assertions v0.0.0-20190116191733-b6c0e53d7304 h1:Jpy1PXuP99tXNrhbq2BaPz9B+jNAvH1JPQQpG/9GCXY=
|
||||
github.com/smartystreets/assertions v0.0.0-20190116191733-b6c0e53d7304/go.mod h1:OnSkiWE9lh6wB0YB77sQom3nweQdgAjqCqsofrRNTgc=
|
||||
github.com/smartystreets/goconvey v0.0.0-20181108003508-044398e4856c h1:Ho+uVpkel/udgjbwB5Lktg9BtvJSh2DT0Hi6LPSyI2w=
|
||||
github.com/smartystreets/goconvey v0.0.0-20181108003508-044398e4856c/go.mod h1:XDJAKZRPZ1CvBcN2aX5YOUTYGHki24fSF0Iv48Ibg0s=
|
||||
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
||||
github.com/stretchr/testify v1.3.0 h1:TivCn/peBQ7UY8ooIcPgZFpTNSz0Q2U6UrFlUfqbe0Q=
|
||||
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
|
||||
github.com/syndtr/goleveldb v1.0.0 h1:fBdIW9lB4Iz0n9khmH8w27SJ3QEJ7+IgjPEwGSZiFdE=
|
||||
github.com/syndtr/goleveldb v1.0.0/go.mod h1:ZVVdQEZoIme9iO1Ch2Jdy24qqXrMMOU6lpPAyBWyWuQ=
|
||||
github.com/tango-contrib/binding v0.0.0-20170526073042-f86db18c4a9e h1:BkZTZKwMdODKtAwCrZCEqPH06G4yJXICuuwOH/WWgio=
|
||||
github.com/tango-contrib/binding v0.0.0-20170526073042-f86db18c4a9e/go.mod h1:MNdSKQVAIj+pPJfwPoqBSnG775qIUlH+T60yFz7mcTE=
|
||||
github.com/tango-contrib/flash v0.0.0-20170619055053-40456640d164 h1:mHwwqszZFhjR58HvykmZ27BAg2vwLmT5aEq4zrrV5mo=
|
||||
github.com/tango-contrib/flash v0.0.0-20170619055053-40456640d164/go.mod h1:fjy8sKkKvITeoogusMWTPg9pRd83vivbUUnMyE6nJfQ=
|
||||
github.com/tango-contrib/renders v0.0.0-20170526074344-86dba79a0240 h1:AKdgx/7/20N3enfUt0N9G7od0/UyDr6PjGRdlsvLAjE=
|
||||
github.com/tango-contrib/renders v0.0.0-20170526074344-86dba79a0240/go.mod h1:4kuIhm8N9wR3cGUFszRJQAvBRUaA7mw001P9BHKh0/s=
|
||||
github.com/tango-contrib/session v0.0.0-20170526074221-3115f8ddf72d h1:rxx/ty+Tk6oyGWV2x+CgARAWdv28H0+tBMF83MEX34c=
|
||||
github.com/tango-contrib/session v0.0.0-20170526074221-3115f8ddf72d/go.mod h1:nAEED8H84/GpN5Ewf/dDJkOM7S0XWyxYAdwlkMTYe2w=
|
||||
github.com/tango-contrib/xsrf v0.0.0-20170526074244-3dbe17fdad36 h1:ymjpu0DZHjza+7G64EZt1WR+x1qPHTi23Ph5343m0z0=
|
||||
github.com/tango-contrib/xsrf v0.0.0-20170526074244-3dbe17fdad36/go.mod h1:i/hS2Yy51czffHIDR+2DMtjCRKKTP8WuYa3XHsh98rQ=
|
||||
goftp.io/ftpd v0.0.0-20180821024231-89d1eae9c018 h1:8/II4yyuHnxQBG+d3IUTflQjrjM5eTDerVgS2VLs7h4=
|
||||
goftp.io/ftpd v0.0.0-20180821024231-89d1eae9c018/go.mod h1:411wH3gYllZ079MJ6lG5UI++ERPF+VklHvGlMux980A=
|
||||
golang.org/x/net v0.0.0-20180906233101-161cd47e91fd h1:nTDtHvHSdCn1m6ITfMRqtOd/9+7a3s8RBNOZ3eYZzJA=
|
||||
golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||
golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f h1:wMNYb4v58l5UBM7MYRLPG6ZhfOqbKu7X5eyFl8ZhKvA=
|
||||
golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e h1:o3PsSEY8E4eXWkXrIP9YJALUkVZqzHJT5DOasTyn8Vs=
|
||||
golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/text v0.3.0 h1:g61tztE5qeGQ89tm6NTjjM9VPIm088od1l6aSorWRWg=
|
||||
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM=
|
||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||
gopkg.in/fsnotify.v1 v1.4.7 h1:xOHLXZwVvI9hhs+cLKq5+I5onOuwQLhQwiu63xxlHs4=
|
||||
gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys=
|
||||
gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7 h1:uRGJdciOHaEIrze2W8Q3AKkepLTh2hOroT7a+7czfdQ=
|
||||
gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw=
|
||||
gopkg.in/yaml.v2 v2.2.1 h1:mUhvW9EsL+naU5Q3cakzfE91YhliOondGd6ZrsDBHQE=
|
||||
gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||
13
vendor/gitea.com/goftp/qiniu-driver/.drone.yml
generated
vendored
13
vendor/gitea.com/goftp/qiniu-driver/.drone.yml
generated
vendored
@@ -1,13 +0,0 @@
|
||||
kind: pipeline
|
||||
name: default
|
||||
|
||||
steps:
|
||||
- name: test
|
||||
image: golang:1.12
|
||||
environment:
|
||||
GO111MODULE: on
|
||||
GOPROXY: https://goproxy.cn
|
||||
commands:
|
||||
- go build -v
|
||||
- go vet ./...
|
||||
- go test -v -race -coverprofile=coverage.txt -covermode=atomic
|
||||
1
vendor/gitea.com/goftp/qiniu-driver/.gitignore
generated
vendored
1
vendor/gitea.com/goftp/qiniu-driver/.gitignore
generated
vendored
@@ -1 +0,0 @@
|
||||
.history
|
||||
3
vendor/gitea.com/goftp/qiniu-driver/README.md
generated
vendored
3
vendor/gitea.com/goftp/qiniu-driver/README.md
generated
vendored
@@ -1,3 +0,0 @@
|
||||
# qiniu-driver
|
||||
|
||||
Servcie qiniu bucket as a ftp driver
|
||||
244
vendor/gitea.com/goftp/qiniu-driver/driver.go
generated
vendored
244
vendor/gitea.com/goftp/qiniu-driver/driver.go
generated
vendored
@@ -1,244 +0,0 @@
|
||||
package qiniudriver
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"io"
|
||||
"net/http"
|
||||
"strings"
|
||||
|
||||
"github.com/qiniu/api.v6/auth/digest"
|
||||
"github.com/qiniu/api.v6/conf"
|
||||
qio "github.com/qiniu/api.v6/io"
|
||||
"github.com/qiniu/api.v6/rs"
|
||||
"github.com/qiniu/api.v6/rsf"
|
||||
"goftp.io/server"
|
||||
)
|
||||
|
||||
type QiniuDriver struct {
|
||||
curDir string
|
||||
client rs.Client
|
||||
client2 rsf.Client
|
||||
bucket string
|
||||
}
|
||||
|
||||
func (driver *QiniuDriver) Init(conn *server.Conn) {
|
||||
//driver.conn = conn
|
||||
}
|
||||
|
||||
func (driver *QiniuDriver) ChangeDir(path string) error {
|
||||
f, err := driver.Stat(path)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if !f.IsDir() {
|
||||
return errors.New("not a dir")
|
||||
}
|
||||
driver.curDir = path
|
||||
return nil
|
||||
}
|
||||
|
||||
func (driver *QiniuDriver) Stat(key string) (server.FileInfo, error) {
|
||||
if strings.HasSuffix(key, "/") {
|
||||
return &FileInfo{key, true, rs.Entry{}}, nil
|
||||
}
|
||||
entry, err := driver.client.Stat(nil, driver.bucket, strings.TrimLeft(key, "/"))
|
||||
if err != nil {
|
||||
entries, _, _ := driver.client2.ListPrefix(nil, driver.bucket, strings.TrimLeft(key, "/")+"/", "", 1)
|
||||
if len(entries) > 0 {
|
||||
return &FileInfo{key, true, rs.Entry{}}, nil
|
||||
}
|
||||
return nil, errors.New("dir not exists")
|
||||
}
|
||||
|
||||
return &FileInfo{key, false, entry}, nil
|
||||
}
|
||||
|
||||
func (driver *QiniuDriver) ListDir(prefix string, callback func(server.FileInfo) error) error {
|
||||
d := strings.TrimLeft(prefix, "/")
|
||||
if d != "" {
|
||||
d = d + "/"
|
||||
}
|
||||
entries, _, err := driver.client2.ListPrefix(nil, driver.bucket, d, "", 1000)
|
||||
if err == io.EOF {
|
||||
err = nil
|
||||
}
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
dirCache := make(map[string]bool)
|
||||
|
||||
for _, entry := range entries {
|
||||
if prefix != "/" && prefix != "" && !strings.HasPrefix(entry.Key, d) {
|
||||
continue
|
||||
}
|
||||
key := strings.TrimLeft(strings.TrimLeft(entry.Key, d), "/")
|
||||
if key == "" {
|
||||
continue
|
||||
}
|
||||
var f server.FileInfo
|
||||
if strings.Contains(key, "/") {
|
||||
key := strings.Trim(strings.Split(key, "/")[0], "/")
|
||||
if _, ok := dirCache[key]; ok {
|
||||
continue
|
||||
}
|
||||
dirCache[key] = true
|
||||
f = &FileInfo{name: key, isDir: true}
|
||||
} else {
|
||||
f = &FileInfo{
|
||||
name: key,
|
||||
Entry: rs.Entry{
|
||||
Hash: entry.Hash,
|
||||
Fsize: entry.Fsize,
|
||||
PutTime: entry.PutTime,
|
||||
MimeType: entry.MimeType,
|
||||
Customer: "",
|
||||
},
|
||||
}
|
||||
}
|
||||
err = callback(f)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (driver *QiniuDriver) DeleteDir(key string) error {
|
||||
d := strings.TrimLeft(key, "/")
|
||||
|
||||
entries, _, err := driver.client2.ListPrefix(nil, driver.bucket, d, "", 1000)
|
||||
if err == io.EOF {
|
||||
err = nil
|
||||
}
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if len(entries) == 0 {
|
||||
return nil
|
||||
}
|
||||
|
||||
delentries := make([]rs.EntryPath, 0)
|
||||
for _, entry := range entries {
|
||||
delentries = append(delentries, rs.EntryPath{
|
||||
Bucket: driver.bucket,
|
||||
Key: entry.Key,
|
||||
})
|
||||
}
|
||||
|
||||
fmt.Println("delete entries:", delentries)
|
||||
|
||||
_, err = driver.client.BatchDelete(nil, delentries)
|
||||
return err
|
||||
}
|
||||
|
||||
func (driver *QiniuDriver) DeleteFile(key string) error {
|
||||
fmt.Println("delete file", key)
|
||||
return driver.client.Delete(nil, driver.bucket, strings.TrimLeft(key, "/"))
|
||||
}
|
||||
|
||||
func (driver *QiniuDriver) Rename(keySrc, keyDest string) error {
|
||||
fmt.Println("rename from", keySrc, keyDest)
|
||||
var from = strings.TrimLeft(keySrc, "/")
|
||||
var to = strings.TrimLeft(keyDest, "/")
|
||||
info, err := driver.client.Stat(nil, driver.bucket, from)
|
||||
if err != nil && strings.Contains(err.Error(), "no such file or directory") {
|
||||
from = strings.TrimLeft(keySrc, "/") + "/"
|
||||
to = strings.TrimLeft(keyDest, "/") + "/"
|
||||
info, err = driver.client.Stat(nil, driver.bucket, from)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
entries, _, err := driver.client2.ListPrefix(nil, driver.bucket, from, "", 1000)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
for _, entry := range entries {
|
||||
newKey := strings.Replace(entry.Key, from, to, 1)
|
||||
err = driver.client.Move(nil, driver.bucket, entry.Key, driver.bucket, newKey)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
if err != nil {
|
||||
fmt.Println(err)
|
||||
return err
|
||||
}
|
||||
fmt.Println(info, from, to)
|
||||
return driver.client.Move(nil, driver.bucket, from, driver.bucket, to)
|
||||
}
|
||||
|
||||
func (driver *QiniuDriver) MakeDir(path string) error {
|
||||
dir := strings.TrimLeft(path, "/") + "/"
|
||||
fmt.Println("mkdir", dir)
|
||||
var s string
|
||||
reader := strings.NewReader(s)
|
||||
_, err := driver.PutFile(dir, reader, false)
|
||||
return err
|
||||
}
|
||||
|
||||
func (driver *QiniuDriver) GetFile(key string, offset int64) (int64, io.ReadCloser, error) {
|
||||
stat, err := driver.Stat(key)
|
||||
if err != nil {
|
||||
return 0, nil, err
|
||||
}
|
||||
|
||||
key = strings.TrimLeft(key, "/")
|
||||
|
||||
domain := fmt.Sprintf("%s.qiniudn.com", driver.bucket)
|
||||
baseUrl := rs.MakeBaseUrl(domain, key)
|
||||
policy := rs.GetPolicy{}
|
||||
downUrl := policy.MakeRequest(baseUrl, nil)
|
||||
|
||||
resp, err := http.Get(downUrl)
|
||||
if err != nil {
|
||||
return 0, nil, err
|
||||
}
|
||||
|
||||
return stat.Size(), NewSkipReadCloser(resp.Body, offset), nil
|
||||
}
|
||||
|
||||
func (driver *QiniuDriver) PutFile(key string, data io.Reader, appendData bool) (int64, error) {
|
||||
var err error
|
||||
var ret qio.PutRet
|
||||
var extra = &qio.PutExtra{}
|
||||
|
||||
putPolicy := rs.PutPolicy{
|
||||
Scope: driver.bucket,
|
||||
}
|
||||
uptoken := putPolicy.Token(nil)
|
||||
|
||||
rd := CountReader(data)
|
||||
err = qio.Put(nil, &ret, uptoken, strings.TrimLeft(key, "/"), rd, extra)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
|
||||
return int64(rd.Size()), nil
|
||||
}
|
||||
|
||||
type QiniuDriverFactory struct {
|
||||
bucket string
|
||||
}
|
||||
|
||||
func NewQiniuDriverFactory(accessKey, secretKey, bucket string) server.DriverFactory {
|
||||
conf.ACCESS_KEY = accessKey
|
||||
conf.SECRET_KEY = secretKey
|
||||
return &QiniuDriverFactory{bucket}
|
||||
}
|
||||
|
||||
func (factory *QiniuDriverFactory) NewDriver() (server.Driver, error) {
|
||||
mac := &digest.Mac{
|
||||
AccessKey: conf.ACCESS_KEY,
|
||||
SecretKey: []byte(conf.SECRET_KEY),
|
||||
}
|
||||
client := rs.New(mac)
|
||||
client2 := rsf.New(mac)
|
||||
return &QiniuDriver{"/", client, client2, factory.bucket}, nil
|
||||
}
|
||||
49
vendor/gitea.com/goftp/qiniu-driver/fileinfo.go
generated
vendored
49
vendor/gitea.com/goftp/qiniu-driver/fileinfo.go
generated
vendored
@@ -1,49 +0,0 @@
|
||||
package qiniudriver
|
||||
|
||||
import (
|
||||
"os"
|
||||
"time"
|
||||
|
||||
"github.com/qiniu/api.v6/rs"
|
||||
)
|
||||
|
||||
type FileInfo struct {
|
||||
name string
|
||||
isDir bool
|
||||
rs.Entry
|
||||
}
|
||||
|
||||
func (f *FileInfo) Name() string {
|
||||
return f.name
|
||||
}
|
||||
|
||||
func (f *FileInfo) Size() int64 {
|
||||
return f.Entry.Fsize
|
||||
}
|
||||
|
||||
func (f *FileInfo) Mode() os.FileMode {
|
||||
if f.isDir {
|
||||
return os.ModeDir | os.ModePerm
|
||||
}
|
||||
return os.ModePerm
|
||||
}
|
||||
|
||||
func (f *FileInfo) ModTime() time.Time {
|
||||
return time.Unix(0, f.Entry.PutTime*100)
|
||||
}
|
||||
|
||||
func (f *FileInfo) IsDir() bool {
|
||||
return f.isDir
|
||||
}
|
||||
|
||||
func (f *FileInfo) Sys() interface{} {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (f *FileInfo) Owner() string {
|
||||
return "qiniu"
|
||||
}
|
||||
|
||||
func (f *FileInfo) Group() string {
|
||||
return "qiniu"
|
||||
}
|
||||
10
vendor/gitea.com/goftp/qiniu-driver/go.mod
generated
vendored
10
vendor/gitea.com/goftp/qiniu-driver/go.mod
generated
vendored
@@ -1,10 +0,0 @@
|
||||
module gitea.com/goftp/qiniu-driver
|
||||
|
||||
go 1.12
|
||||
|
||||
require (
|
||||
github.com/qiniu/api.v6 v6.0.9+incompatible
|
||||
github.com/qiniu/bytes v0.0.0-20140728010635-4887e7b2bde3 // indirect
|
||||
github.com/qiniu/rpc v0.0.0-20140728010754-30c22466d920 // indirect
|
||||
goftp.io/server v0.0.0-20190812034929-9b3874d17690
|
||||
)
|
||||
22
vendor/gitea.com/goftp/qiniu-driver/go.sum
generated
vendored
22
vendor/gitea.com/goftp/qiniu-driver/go.sum
generated
vendored
@@ -1,22 +0,0 @@
|
||||
gitea.com/goftp/file-driver v0.0.0-20190712091345-f79c2ed973f8 h1:uCOouUDYTyK+Zz86dnkvDBUKnbqfqA/Fk/hxp5sXWv8=
|
||||
gitea.com/goftp/file-driver v0.0.0-20190712091345-f79c2ed973f8/go.mod h1:ghdogu0Da3rwYCSJ20JPgTiMcDpzeRbzvuFIOOW3G7w=
|
||||
github.com/davecgh/go-spew v1.1.0 h1:ZDRjVQ15GmhC3fiQ8ni8+OwkZQO4DARzQgrnXU1Liz8=
|
||||
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
github.com/goftp/file-driver v0.0.0-20180502053751-5d604a0fc0c9/go.mod h1:GpOj6zuVBG3Inr9qjEnuVTgBlk2lZ1S9DcoFiXWyKss=
|
||||
github.com/goftp/server v0.0.0-20190304020633-eabccc535b5a/go.mod h1:k/SS6VWkxY7dHPhoMQ8IdRu8L4lQtmGbhyXGg+vCnXE=
|
||||
github.com/jlaffaye/ftp v0.0.0-20190624084859-c1312a7102bf h1:2IYBd5TD/maMqTU2YUzp2tJL4cNaOYQ9EBullN9t9pk=
|
||||
github.com/jlaffaye/ftp v0.0.0-20190624084859-c1312a7102bf/go.mod h1:lli8NYPQOFy3O++YmYbqVgOcQ1JPCwdOy+5zSjKJ9qY=
|
||||
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
|
||||
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
||||
github.com/qiniu/api.v6 v6.0.9+incompatible h1:mG/jDC2GD9u2DqP1yIbX+USd3S60bQYSRh6Su6EbnsU=
|
||||
github.com/qiniu/api.v6 v6.0.9+incompatible/go.mod h1:iJeMuW0i5a4O1SFx2LYtxY+9hkTfkYQJL8xTaGYGDA4=
|
||||
github.com/qiniu/bytes v0.0.0-20140728010635-4887e7b2bde3 h1:PXNXOJs716xnMtH6kMkPlQfSG+x8m2Q31uTN+dQF10c=
|
||||
github.com/qiniu/bytes v0.0.0-20140728010635-4887e7b2bde3/go.mod h1:5KFTwj5mNES3FmpAF+DEDuVolB/OVAUj3oNqPLriYbo=
|
||||
github.com/qiniu/rpc v0.0.0-20140728010754-30c22466d920 h1:G6C/49DiPwATK+4oBi6OCf14WzCwNMTC1s5Udov4dwQ=
|
||||
github.com/qiniu/rpc v0.0.0-20140728010754-30c22466d920/go.mod h1:vUC++Z6RsGp85+Oyiu1l5+mpao6xy/Vi1J/G1fKiwDk=
|
||||
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
||||
github.com/stretchr/testify v1.3.0 h1:TivCn/peBQ7UY8ooIcPgZFpTNSz0Q2U6UrFlUfqbe0Q=
|
||||
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
|
||||
goftp.io/server v0.0.0-20190712054601-1149070ae46b/go.mod h1:xreggPYu7ZuNe9PfbxiQca7bYGwU44IvlCCg3KzWJtQ=
|
||||
goftp.io/server v0.0.0-20190812034929-9b3874d17690 h1:vHUbHALX1kwsxK6gFQeY19+10zk/pSABN/0vumWLCxQ=
|
||||
goftp.io/server v0.0.0-20190812034929-9b3874d17690/go.mod h1:99FISrRpwKfaL4Ey/dX8N48WToveng/s2OXR5sJ3cnc=
|
||||
23
vendor/gitea.com/goftp/qiniu-driver/io.go
generated
vendored
23
vendor/gitea.com/goftp/qiniu-driver/io.go
generated
vendored
@@ -1,23 +0,0 @@
|
||||
package qiniudriver
|
||||
|
||||
import "io"
|
||||
|
||||
type countReader struct {
|
||||
reader io.Reader
|
||||
counts int
|
||||
}
|
||||
|
||||
func (cr *countReader) Size() int {
|
||||
return cr.counts
|
||||
}
|
||||
|
||||
func (cr *countReader) Read(p []byte) (n int, err error) {
|
||||
rs, err := cr.reader.Read(p)
|
||||
cr.counts += rs
|
||||
return rs, err
|
||||
}
|
||||
|
||||
// CountReader returns a Reader that's is just for counting the total bytes of read.
|
||||
func CountReader(reader io.Reader) *countReader {
|
||||
return &countReader{reader, 0}
|
||||
}
|
||||
32
vendor/gitea.com/goftp/qiniu-driver/skipreader.go
generated
vendored
32
vendor/gitea.com/goftp/qiniu-driver/skipreader.go
generated
vendored
@@ -1,32 +0,0 @@
|
||||
package qiniudriver
|
||||
|
||||
import (
|
||||
"io"
|
||||
"io/ioutil"
|
||||
)
|
||||
|
||||
func NewSkipReadCloser(rd io.ReadCloser, count int64) io.ReadCloser {
|
||||
return &SkipReadCloser{
|
||||
ReadCloser: rd,
|
||||
count: count,
|
||||
}
|
||||
}
|
||||
|
||||
type SkipReadCloser struct {
|
||||
io.ReadCloser
|
||||
count int64
|
||||
skipped bool
|
||||
}
|
||||
|
||||
func (s *SkipReadCloser) Read(data []byte) (int, error) {
|
||||
if !s.skipped {
|
||||
if s.count > 0 {
|
||||
_, err := io.CopyN(ioutil.Discard, s.ReadCloser, s.count)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
}
|
||||
s.skipped = true
|
||||
}
|
||||
return s.ReadCloser.Read(data)
|
||||
}
|
||||
144
vendor/github.com/qiniu/api.v6/auth/digest/digest_auth.go
generated
vendored
144
vendor/github.com/qiniu/api.v6/auth/digest/digest_auth.go
generated
vendored
@@ -1,144 +0,0 @@
|
||||
package digest
|
||||
|
||||
import (
|
||||
"crypto/hmac"
|
||||
"crypto/sha1"
|
||||
"encoding/base64"
|
||||
"io"
|
||||
"net/http"
|
||||
|
||||
. "github.com/qiniu/api.v6/conf"
|
||||
"github.com/qiniu/bytes/seekable"
|
||||
)
|
||||
|
||||
// ----------------------------------------------------------
|
||||
|
||||
type Mac struct {
|
||||
AccessKey string
|
||||
SecretKey []byte
|
||||
}
|
||||
|
||||
func (mac *Mac) Sign(data []byte) (token string) {
|
||||
|
||||
h := hmac.New(sha1.New, mac.SecretKey)
|
||||
h.Write(data)
|
||||
|
||||
sign := base64.URLEncoding.EncodeToString(h.Sum(nil))
|
||||
return mac.AccessKey + ":" + sign[:27]
|
||||
}
|
||||
|
||||
func (mac *Mac) SignWithData(b []byte) (token string) {
|
||||
|
||||
blen := base64.URLEncoding.EncodedLen(len(b))
|
||||
|
||||
key := mac.AccessKey
|
||||
nkey := len(key)
|
||||
ret := make([]byte, nkey+30+blen)
|
||||
|
||||
base64.URLEncoding.Encode(ret[nkey+30:], b)
|
||||
|
||||
h := hmac.New(sha1.New, mac.SecretKey)
|
||||
h.Write(ret[nkey+30:])
|
||||
digest := h.Sum(nil)
|
||||
|
||||
copy(ret, key)
|
||||
ret[nkey] = ':'
|
||||
base64.URLEncoding.Encode(ret[nkey+1:], digest)
|
||||
ret[nkey+29] = ':'
|
||||
|
||||
return string(ret)
|
||||
}
|
||||
|
||||
func (mac *Mac) SignRequest(req *http.Request, incbody bool) (token string, err error) {
|
||||
|
||||
h := hmac.New(sha1.New, mac.SecretKey)
|
||||
|
||||
u := req.URL
|
||||
data := u.Path
|
||||
if u.RawQuery != "" {
|
||||
data += "?" + u.RawQuery
|
||||
}
|
||||
io.WriteString(h, data+"\n")
|
||||
|
||||
if incbody {
|
||||
s2, err2 := seekable.New(req)
|
||||
if err2 != nil {
|
||||
return "", err2
|
||||
}
|
||||
h.Write(s2.Bytes())
|
||||
}
|
||||
|
||||
sign := base64.URLEncoding.EncodeToString(h.Sum(nil))
|
||||
token = mac.AccessKey + ":" + sign
|
||||
return
|
||||
}
|
||||
|
||||
func Sign(mac *Mac, data []byte) string {
|
||||
|
||||
if mac == nil {
|
||||
mac = &Mac{ACCESS_KEY, []byte(SECRET_KEY)}
|
||||
}
|
||||
return mac.Sign(data)
|
||||
}
|
||||
|
||||
func SignWithData(mac *Mac, data []byte) string {
|
||||
|
||||
if mac == nil {
|
||||
mac = &Mac{ACCESS_KEY, []byte(SECRET_KEY)}
|
||||
}
|
||||
return mac.SignWithData(data)
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------------------
|
||||
|
||||
type Transport struct {
|
||||
mac Mac
|
||||
transport http.RoundTripper
|
||||
}
|
||||
|
||||
func incBody(req *http.Request) bool {
|
||||
|
||||
if req.Body == nil {
|
||||
return false
|
||||
}
|
||||
if ct, ok := req.Header["Content-Type"]; ok {
|
||||
switch ct[0] {
|
||||
case "application/x-www-form-urlencoded":
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func (t *Transport) RoundTrip(req *http.Request) (resp *http.Response, err error) {
|
||||
|
||||
token, err := t.mac.SignRequest(req, incBody(req))
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
req.Header.Set("Authorization", "QBox "+token)
|
||||
return t.transport.RoundTrip(req)
|
||||
}
|
||||
|
||||
func NewTransport(mac *Mac, transport http.RoundTripper) *Transport {
|
||||
|
||||
if transport == nil {
|
||||
transport = http.DefaultTransport
|
||||
}
|
||||
t := &Transport{transport: transport}
|
||||
if mac == nil {
|
||||
t.mac.AccessKey = ACCESS_KEY
|
||||
t.mac.SecretKey = []byte(SECRET_KEY)
|
||||
} else {
|
||||
t.mac = *mac
|
||||
}
|
||||
return t
|
||||
}
|
||||
|
||||
func NewClient(mac *Mac, transport http.RoundTripper) *http.Client {
|
||||
|
||||
t := NewTransport(mac, transport)
|
||||
return &http.Client{Transport: t}
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------------------
|
||||
41
vendor/github.com/qiniu/api.v6/conf/conf.go
generated
vendored
41
vendor/github.com/qiniu/api.v6/conf/conf.go
generated
vendored
@@ -1,41 +0,0 @@
|
||||
package conf
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"regexp"
|
||||
"runtime"
|
||||
|
||||
"github.com/qiniu/rpc"
|
||||
)
|
||||
|
||||
var UP_HOST = "http://upload.qiniu.com"
|
||||
var RS_HOST = "http://rs.qbox.me"
|
||||
var RSF_HOST = "http://rsf.qbox.me"
|
||||
|
||||
var PUB_HOST = "http://pub.qbox.me"
|
||||
var IO_HOST = "http://iovip.qbox.me"
|
||||
|
||||
var ACCESS_KEY string
|
||||
var SECRET_KEY string
|
||||
|
||||
var version = "6.0.6"
|
||||
|
||||
var userPattern = regexp.MustCompile("^[a-zA-Z0-9_.-]*$")
|
||||
|
||||
// user should be [A-Za-z0-9]*
|
||||
func SetUser(user string) error {
|
||||
if !userPattern.MatchString(user) {
|
||||
return errors.New("invalid user format")
|
||||
}
|
||||
rpc.UserAgent = formatUserAgent(user)
|
||||
return nil
|
||||
}
|
||||
|
||||
func formatUserAgent(user string) string {
|
||||
return fmt.Sprintf("QiniuGo/%s (%s; %s; %s) %s", version, runtime.GOOS, runtime.GOARCH, user, runtime.Version())
|
||||
}
|
||||
|
||||
func init() {
|
||||
SetUser("")
|
||||
}
|
||||
249
vendor/github.com/qiniu/api.v6/io/io_api.go
generated
vendored
249
vendor/github.com/qiniu/api.v6/io/io_api.go
generated
vendored
@@ -1,249 +0,0 @@
|
||||
package io
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
"hash/crc32"
|
||||
"io"
|
||||
"io/ioutil"
|
||||
"mime/multipart"
|
||||
"net/textproto"
|
||||
"os"
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
. "github.com/qiniu/api.v6/conf"
|
||||
"github.com/qiniu/rpc"
|
||||
)
|
||||
|
||||
// ----------------------------------------------------------
|
||||
|
||||
// @gist PutExtra
|
||||
type PutExtra struct {
|
||||
Params map[string]string //可选,用户自定义参数,必须以 "x:" 开头
|
||||
//若不以x:开头,则忽略
|
||||
MimeType string //可选,当为 "" 时候,服务端自动判断
|
||||
Crc32 uint32
|
||||
CheckCrc uint32
|
||||
// CheckCrc == 0: 表示不进行 crc32 校验
|
||||
// CheckCrc == 1: 对于 Put 等同于 CheckCrc = 2;对于 PutFile 会自动计算 crc32 值
|
||||
// CheckCrc == 2: 表示进行 crc32 校验,且 crc32 值就是上面的 Crc32 变量
|
||||
}
|
||||
|
||||
// @endgist
|
||||
|
||||
type PutRet struct {
|
||||
Hash string `json:"hash"` // 如果 uptoken 没有指定 ReturnBody,那么返回值是标准的 PutRet 结构
|
||||
Key string `json:"key"`
|
||||
}
|
||||
|
||||
var tmpFilePrefix = "qiniu-go-sdk-tmpfile"
|
||||
|
||||
// ----------------------------------------------------------
|
||||
// !!! Deprecated !!!
|
||||
//
|
||||
// 1. 不推荐使用该组 API, 因为可能造成本地磁盘IO
|
||||
// 2. 如果只是纯粹上传一个 io.Reader, 请使用 Put2 或者 PutWithoutKey2
|
||||
// 3. 如果需要上传一个文件, 请使用 PutFile 或者 PutFileWithoutKey
|
||||
|
||||
func Put(l rpc.Logger, ret interface{}, uptoken, key string, data io.Reader, extra *PutExtra) error {
|
||||
return putReader(l, ret, uptoken, key, true, data, extra)
|
||||
}
|
||||
|
||||
func PutWithoutKey(l rpc.Logger, ret interface{}, uptoken string, data io.Reader, extra *PutExtra) error {
|
||||
return putReader(l, ret, uptoken, "", false, data, extra)
|
||||
}
|
||||
|
||||
func putReader(l rpc.Logger, ret interface{}, uptoken, key string, hasKey bool, data io.Reader, extra *PutExtra) error {
|
||||
|
||||
rs, ok := data.(io.ReadSeeker)
|
||||
if ok {
|
||||
// 通过 Seeker 接口获取大小
|
||||
size, err := rs.Seek(0, 2)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
_, err = rs.Seek(0, 0)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return put(l, ret, uptoken, key, hasKey, data, size, extra)
|
||||
} else {
|
||||
// 写临时文件
|
||||
tmpf, err := ioutil.TempFile(os.TempDir(), tmpFilePrefix)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
fname := tmpf.Name()
|
||||
defer os.Remove(fname)
|
||||
|
||||
_, err = io.Copy(tmpf, data)
|
||||
if err != nil {
|
||||
tmpf.Close()
|
||||
return err
|
||||
}
|
||||
tmpf.Close()
|
||||
|
||||
return putFile(l, ret, uptoken, key, hasKey, fname, extra)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// ----------------------------------------------------------
|
||||
|
||||
func put(l rpc.Logger, ret interface{}, uptoken, key string, hasKey bool, data io.Reader, size int64, extra *PutExtra) error {
|
||||
|
||||
// CheckCrc == 1: 对于 Put 和 PutWithoutKey 等同于 CheckCrc == 2
|
||||
if extra != nil {
|
||||
if extra.CheckCrc == 1 {
|
||||
extra1 := *extra
|
||||
extra = &extra1
|
||||
extra.CheckCrc = 2
|
||||
}
|
||||
}
|
||||
return putWrite(l, ret, uptoken, key, hasKey, data, size, extra)
|
||||
}
|
||||
|
||||
func Put2(l rpc.Logger, ret interface{}, uptoken, key string, data io.Reader, size int64, extra *PutExtra) error {
|
||||
return put(l, ret, uptoken, key, true, data, size, extra)
|
||||
}
|
||||
|
||||
func PutWithoutKey2(l rpc.Logger, ret interface{}, uptoken string, data io.Reader, size int64, extra *PutExtra) error {
|
||||
return put(l, ret, uptoken, "", false, data, size, extra)
|
||||
}
|
||||
|
||||
// ----------------------------------------------------------
|
||||
|
||||
func PutFile(l rpc.Logger, ret interface{}, uptoken, key, localFile string, extra *PutExtra) (err error) {
|
||||
return putFile(l, ret, uptoken, key, true, localFile, extra)
|
||||
}
|
||||
|
||||
func PutFileWithoutKey(l rpc.Logger, ret interface{}, uptoken, localFile string, extra *PutExtra) (err error) {
|
||||
return putFile(l, ret, uptoken, "", false, localFile, extra)
|
||||
}
|
||||
|
||||
func putFile(l rpc.Logger, ret interface{}, uptoken, key string, hasKey bool, localFile string, extra *PutExtra) (err error) {
|
||||
|
||||
f, err := os.Open(localFile)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
defer f.Close()
|
||||
|
||||
finfo, err := f.Stat()
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
fsize := finfo.Size()
|
||||
|
||||
if extra != nil && extra.CheckCrc == 1 {
|
||||
extra.Crc32, err = getFileCrc32(f)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
return putWrite(l, ret, uptoken, key, hasKey, f, fsize, extra)
|
||||
}
|
||||
|
||||
// ----------------------------------------------------------
|
||||
|
||||
func putWrite(l rpc.Logger, ret interface{}, uptoken, key string, hasKey bool, data io.Reader, size int64, extra *PutExtra) error {
|
||||
|
||||
var b bytes.Buffer
|
||||
writer := multipart.NewWriter(&b)
|
||||
|
||||
err := writeMultipart(writer, uptoken, key, hasKey, extra)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
lastLine := fmt.Sprintf("\r\n--%s--\r\n", writer.Boundary())
|
||||
r := bytes.NewReader([]byte(lastLine))
|
||||
|
||||
bodyLen := int64(b.Len()) + size + int64(len(lastLine))
|
||||
mr := io.MultiReader(&b, data, r)
|
||||
|
||||
contentType := writer.FormDataContentType()
|
||||
return rpc.DefaultClient.CallWith64(l, ret, UP_HOST, contentType, mr, bodyLen)
|
||||
}
|
||||
|
||||
/*
|
||||
* extra.CheckCrc:
|
||||
* 0: 不进行crc32校验
|
||||
* 1: 以writeMultipart自动生成crc32的值,进行校验
|
||||
* 2: 以extra.Crc32的值,进行校验
|
||||
* other: 和2一样, 以 extra.Crc32的值,进行校验
|
||||
*/
|
||||
func writeMultipart(writer *multipart.Writer, uptoken, key string, hasKey bool, extra *PutExtra) (err error) {
|
||||
|
||||
if extra == nil {
|
||||
extra = &PutExtra{}
|
||||
}
|
||||
|
||||
//token
|
||||
if err = writer.WriteField("token", uptoken); err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
//key
|
||||
if hasKey {
|
||||
if err = writer.WriteField("key", key); err != nil {
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
// extra.Params
|
||||
if extra.Params != nil {
|
||||
for k, v := range extra.Params {
|
||||
err = writer.WriteField(k, v)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//extra.CheckCrc
|
||||
if extra.CheckCrc != 0 {
|
||||
err = writer.WriteField("crc32", strconv.FormatInt(int64(extra.Crc32), 10))
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
//file
|
||||
head := make(textproto.MIMEHeader)
|
||||
|
||||
// default the filename is same as key , but ""
|
||||
var fileName = key
|
||||
if fileName == "" {
|
||||
fileName = "filename"
|
||||
}
|
||||
|
||||
head.Set("Content-Disposition",
|
||||
fmt.Sprintf(`form-data; name="file"; filename="%s"`, escapeQuotes(fileName)))
|
||||
if extra.MimeType != "" {
|
||||
head.Set("Content-Type", extra.MimeType)
|
||||
}
|
||||
|
||||
_, err = writer.CreatePart(head)
|
||||
return err
|
||||
}
|
||||
|
||||
// ----------------------------------------------------------
|
||||
|
||||
var quoteEscaper = strings.NewReplacer("\\", "\\\\", `"`, "\\\"")
|
||||
|
||||
func escapeQuotes(s string) string {
|
||||
return quoteEscaper.Replace(s)
|
||||
}
|
||||
|
||||
func getFileCrc32(f *os.File) (uint32, error) {
|
||||
defer f.Seek(0, 0)
|
||||
|
||||
h := crc32.NewIEEE()
|
||||
_, err := io.Copy(h, f)
|
||||
|
||||
return h.Sum32(), err
|
||||
}
|
||||
93
vendor/github.com/qiniu/api.v6/rs/batch_api.go
generated
vendored
93
vendor/github.com/qiniu/api.v6/rs/batch_api.go
generated
vendored
@@ -1,93 +0,0 @@
|
||||
package rs
|
||||
|
||||
import (
|
||||
. "github.com/qiniu/api.v6/conf"
|
||||
"github.com/qiniu/rpc"
|
||||
)
|
||||
|
||||
// ----------------------------------------------------------
|
||||
|
||||
func (rs Client) Batch(l rpc.Logger, ret interface{}, op []string) (err error) {
|
||||
return rs.Conn.CallWithForm(l, ret, RS_HOST+"/batch", map[string][]string{"op": op})
|
||||
}
|
||||
|
||||
// ----------------------------------------------------------
|
||||
|
||||
// @gist batchStatItemRet
|
||||
type BatchStatItemRet struct {
|
||||
Data Entry `json:"data"`
|
||||
Error string `json:"error"`
|
||||
Code int `json:"code"`
|
||||
}
|
||||
|
||||
// @endgist
|
||||
|
||||
// @gist entryPath
|
||||
type EntryPath struct {
|
||||
Bucket string
|
||||
Key string
|
||||
}
|
||||
|
||||
// @endgist
|
||||
|
||||
func (rs Client) BatchStat(l rpc.Logger, entries []EntryPath) (ret []BatchStatItemRet, err error) {
|
||||
|
||||
b := make([]string, len(entries))
|
||||
for i, e := range entries {
|
||||
b[i] = URIStat(e.Bucket, e.Key)
|
||||
}
|
||||
err = rs.Batch(l, &ret, b)
|
||||
return
|
||||
}
|
||||
|
||||
// ----------------------------------------------------------
|
||||
|
||||
// @gist batchItemRet
|
||||
type BatchItemRet struct {
|
||||
Error string `json:"error"`
|
||||
Code int `json:"code"`
|
||||
}
|
||||
|
||||
// @endgist
|
||||
|
||||
func (rs Client) BatchDelete(l rpc.Logger, entries []EntryPath) (ret []BatchItemRet, err error) {
|
||||
|
||||
b := make([]string, len(entries))
|
||||
for i, e := range entries {
|
||||
b[i] = URIDelete(e.Bucket, e.Key)
|
||||
}
|
||||
err = rs.Batch(l, &ret, b)
|
||||
return
|
||||
}
|
||||
|
||||
// ----------------------------------------------------------
|
||||
|
||||
// @gist entryPathPair
|
||||
type EntryPathPair struct {
|
||||
Src EntryPath
|
||||
Dest EntryPath
|
||||
}
|
||||
|
||||
// @endgist
|
||||
|
||||
func (rs Client) BatchMove(l rpc.Logger, entries []EntryPathPair) (ret []BatchItemRet, err error) {
|
||||
|
||||
b := make([]string, len(entries))
|
||||
for i, e := range entries {
|
||||
b[i] = URIMove(e.Src.Bucket, e.Src.Key, e.Dest.Bucket, e.Dest.Key)
|
||||
}
|
||||
err = rs.Batch(l, &ret, b)
|
||||
return
|
||||
}
|
||||
|
||||
func (rs Client) BatchCopy(l rpc.Logger, entries []EntryPathPair) (ret []BatchItemRet, err error) {
|
||||
|
||||
b := make([]string, len(entries))
|
||||
for i, e := range entries {
|
||||
b[i] = URICopy(e.Src.Bucket, e.Src.Key, e.Dest.Bucket, e.Dest.Key)
|
||||
}
|
||||
err = rs.Batch(l, &ret, b)
|
||||
return
|
||||
}
|
||||
|
||||
// ----------------------------------------------------------
|
||||
93
vendor/github.com/qiniu/api.v6/rs/rs_api.go
generated
vendored
93
vendor/github.com/qiniu/api.v6/rs/rs_api.go
generated
vendored
@@ -1,93 +0,0 @@
|
||||
package rs
|
||||
|
||||
import (
|
||||
"encoding/base64"
|
||||
"net/http"
|
||||
|
||||
"github.com/qiniu/api.v6/auth/digest"
|
||||
. "github.com/qiniu/api.v6/conf"
|
||||
"github.com/qiniu/rpc"
|
||||
)
|
||||
|
||||
// ----------------------------------------------------------
|
||||
|
||||
type Client struct {
|
||||
Conn rpc.Client
|
||||
}
|
||||
|
||||
func New(mac *digest.Mac) Client {
|
||||
t := digest.NewTransport(mac, nil)
|
||||
client := &http.Client{Transport: t}
|
||||
return Client{rpc.Client{client}}
|
||||
}
|
||||
|
||||
func NewEx(t http.RoundTripper) Client {
|
||||
client := &http.Client{Transport: t}
|
||||
return Client{rpc.Client{client}}
|
||||
}
|
||||
|
||||
// ----------------------------------------------------------
|
||||
|
||||
// @gist entry
|
||||
type Entry struct {
|
||||
Hash string `json:"hash"`
|
||||
Fsize int64 `json:"fsize"`
|
||||
PutTime int64 `json:"putTime"`
|
||||
MimeType string `json:"mimeType"`
|
||||
Customer string `json:"customer"`
|
||||
}
|
||||
|
||||
// @endgist
|
||||
|
||||
func (rs Client) Stat(l rpc.Logger, bucket, key string) (entry Entry, err error) {
|
||||
err = rs.Conn.Call(l, &entry, RS_HOST+URIStat(bucket, key))
|
||||
return
|
||||
}
|
||||
|
||||
func (rs Client) Delete(l rpc.Logger, bucket, key string) (err error) {
|
||||
return rs.Conn.Call(l, nil, RS_HOST+URIDelete(bucket, key))
|
||||
}
|
||||
|
||||
func (rs Client) Move(l rpc.Logger, bucketSrc, keySrc, bucketDest, keyDest string) (err error) {
|
||||
return rs.Conn.Call(l, nil, RS_HOST+URIMove(bucketSrc, keySrc, bucketDest, keyDest))
|
||||
}
|
||||
|
||||
func (rs Client) Copy(l rpc.Logger, bucketSrc, keySrc, bucketDest, keyDest string) (err error) {
|
||||
return rs.Conn.Call(l, nil, RS_HOST+URICopy(bucketSrc, keySrc, bucketDest, keyDest))
|
||||
}
|
||||
|
||||
func (rs Client) Fetch(l rpc.Logger, bucket, key, url string) (err error) {
|
||||
return rs.Conn.Call(l, nil, IO_HOST+URIFetch(bucket, key, url))
|
||||
}
|
||||
|
||||
func (rs Client) ChangeMime(l rpc.Logger, bucket, key, mime string) (err error) {
|
||||
return rs.Conn.Call(l, nil, RS_HOST+URIChangeMime(bucket, key, mime))
|
||||
}
|
||||
|
||||
func encodeURI(uri string) string {
|
||||
return base64.URLEncoding.EncodeToString([]byte(uri))
|
||||
}
|
||||
|
||||
func URIDelete(bucket, key string) string {
|
||||
return "/delete/" + encodeURI(bucket+":"+key)
|
||||
}
|
||||
|
||||
func URIStat(bucket, key string) string {
|
||||
return "/stat/" + encodeURI(bucket+":"+key)
|
||||
}
|
||||
|
||||
func URICopy(bucketSrc, keySrc, bucketDest, keyDest string) string {
|
||||
return "/copy/" + encodeURI(bucketSrc+":"+keySrc) + "/" + encodeURI(bucketDest+":"+keyDest)
|
||||
}
|
||||
|
||||
func URIMove(bucketSrc, keySrc, bucketDest, keyDest string) string {
|
||||
return "/move/" + encodeURI(bucketSrc+":"+keySrc) + "/" + encodeURI(bucketDest+":"+keyDest)
|
||||
}
|
||||
|
||||
func URIFetch(bucket, key, url string) string {
|
||||
return "/fetch/" + encodeURI(url) + "/to/" + encodeURI(bucket+":"+key)
|
||||
}
|
||||
|
||||
func URIChangeMime(bucket, key, mime string) string {
|
||||
return "/chgm/" + encodeURI(bucket+":"+key) + "/mime/" + encodeURI(mime)
|
||||
}
|
||||
74
vendor/github.com/qiniu/api.v6/rs/token.go
generated
vendored
74
vendor/github.com/qiniu/api.v6/rs/token.go
generated
vendored
@@ -1,74 +0,0 @@
|
||||
package rs
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"strconv"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/qiniu/api.v6/auth/digest"
|
||||
"github.com/qiniu/api.v6/url"
|
||||
)
|
||||
|
||||
// ----------------------------------------------------------
|
||||
|
||||
type GetPolicy struct {
|
||||
Expires uint32
|
||||
}
|
||||
|
||||
func (r GetPolicy) MakeRequest(baseUrl string, mac *digest.Mac) (privateUrl string) {
|
||||
|
||||
expires := r.Expires
|
||||
if expires == 0 {
|
||||
expires = 3600
|
||||
}
|
||||
deadline := time.Now().Unix() + int64(expires)
|
||||
|
||||
if strings.Contains(baseUrl, "?") {
|
||||
baseUrl += "&e="
|
||||
} else {
|
||||
baseUrl += "?e="
|
||||
}
|
||||
baseUrl += strconv.FormatInt(deadline, 10)
|
||||
|
||||
token := digest.Sign(mac, []byte(baseUrl))
|
||||
return baseUrl + "&token=" + token
|
||||
}
|
||||
|
||||
func MakeBaseUrl(domain, key string) (baseUrl string) {
|
||||
return "http://" + domain + "/" + url.Escape(key)
|
||||
}
|
||||
|
||||
// --------------------------------------------------------------------------------
|
||||
|
||||
type PutPolicy struct {
|
||||
Scope string `json:"scope"`
|
||||
Expires uint32 `json:"deadline"` // 截止时间(以秒为单位)
|
||||
InsertOnly uint16 `json:"exclusive,omitempty"` // 若非0, 即使Scope为 Bucket:Key 的形式也是insert only
|
||||
DetectMime uint16 `json:"detectMime,omitempty"` // 若非0, 则服务端根据内容自动确定 MimeType
|
||||
FsizeLimit int64 `json:"fsizeLimit,omitempty"`
|
||||
SaveKey string `json:"saveKey,omitempty"`
|
||||
CallbackUrl string `json:"callbackUrl,omitempty"`
|
||||
CallbackBody string `json:"callbackBody,omitempty"`
|
||||
ReturnUrl string `json:"returnUrl,omitempty"`
|
||||
ReturnBody string `json:"returnBody,omitempty"`
|
||||
PersistentOps string `json:"persistentOps,omitempty"`
|
||||
PersistentNotifyUrl string `json:"persistentNotifyUrl,omitempty"`
|
||||
PersistentPipeline string `json:"persistentPipeline,omitempty"`
|
||||
AsyncOps string `json:"asyncOps,omitempty"`
|
||||
EndUser string `json:"endUser,omitempty"`
|
||||
MimeLimit string `json:"mimeLimit,omitempty"`
|
||||
}
|
||||
|
||||
func (r *PutPolicy) Token(mac *digest.Mac) string {
|
||||
|
||||
var rr = *r
|
||||
if rr.Expires == 0 {
|
||||
rr.Expires = 3600
|
||||
}
|
||||
rr.Expires += uint32(time.Now().Unix())
|
||||
b, _ := json.Marshal(&rr)
|
||||
return digest.SignWithData(mac, b)
|
||||
}
|
||||
|
||||
// ----------------------------------------------------------
|
||||
87
vendor/github.com/qiniu/api.v6/rsf/rsf_api.go
generated
vendored
87
vendor/github.com/qiniu/api.v6/rsf/rsf_api.go
generated
vendored
@@ -1,87 +0,0 @@
|
||||
package rsf
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"io"
|
||||
"net/http"
|
||||
"net/url"
|
||||
"strconv"
|
||||
|
||||
"github.com/qiniu/api.v6/auth/digest"
|
||||
. "github.com/qiniu/api.v6/conf"
|
||||
"github.com/qiniu/rpc"
|
||||
)
|
||||
|
||||
// ----------------------------------------------------------
|
||||
|
||||
type ListItem struct {
|
||||
Key string `json:"key"`
|
||||
Hash string `json:"hash"`
|
||||
Fsize int64 `json:"fsize"`
|
||||
PutTime int64 `json:"putTime"`
|
||||
MimeType string `json:"mimeType"`
|
||||
EndUser string `json:"endUser"`
|
||||
}
|
||||
|
||||
type ListRet struct {
|
||||
Marker string `json:"marker"`
|
||||
Items []ListItem `json:"items"`
|
||||
}
|
||||
|
||||
// ----------------------------------------------------------
|
||||
|
||||
type Client struct {
|
||||
Conn rpc.Client
|
||||
}
|
||||
|
||||
func New(mac *digest.Mac) Client {
|
||||
t := digest.NewTransport(mac, nil)
|
||||
client := &http.Client{Transport: t}
|
||||
return Client{rpc.Client{client}}
|
||||
}
|
||||
|
||||
func NewEx(t http.RoundTripper) Client {
|
||||
client := &http.Client{Transport: t}
|
||||
return Client{rpc.Client{client}}
|
||||
}
|
||||
|
||||
// ----------------------------------------------------------
|
||||
// 1. 首次请求 marker = ""
|
||||
// 2. 无论 err 值如何,均应该先看 entries 是否有内容
|
||||
// 3. 如果后续没有更多数据,err 返回 EOF,markerOut 返回 ""(但不通过该特征来判断是否结束)
|
||||
func (rsf Client) ListPrefix(l rpc.Logger, bucket, prefix, marker string, limit int) (entries []ListItem, markerOut string, err error) {
|
||||
|
||||
if bucket == "" {
|
||||
err = errors.New("bucket could not be nil")
|
||||
return
|
||||
}
|
||||
|
||||
URL := makeListURL(bucket, prefix, marker, limit)
|
||||
listRet := ListRet{}
|
||||
err = rsf.Conn.Call(l, &listRet, URL)
|
||||
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
if listRet.Marker == "" {
|
||||
return listRet.Items, "", io.EOF
|
||||
}
|
||||
return listRet.Items, listRet.Marker, err
|
||||
}
|
||||
|
||||
func makeListURL(bucket, prefix, marker string, limit int) string {
|
||||
|
||||
query := make(url.Values)
|
||||
query.Add("bucket", bucket)
|
||||
if prefix != "" {
|
||||
query.Add("prefix", prefix)
|
||||
}
|
||||
if marker != "" {
|
||||
query.Add("marker", marker)
|
||||
}
|
||||
if limit > 0 {
|
||||
query.Add("limit", strconv.FormatInt(int64(limit), 10))
|
||||
}
|
||||
|
||||
return RSF_HOST + "/list?" + query.Encode()
|
||||
}
|
||||
208
vendor/github.com/qiniu/api.v6/url/urlescape.go
generated
vendored
208
vendor/github.com/qiniu/api.v6/url/urlescape.go
generated
vendored
@@ -1,208 +0,0 @@
|
||||
package url
|
||||
|
||||
import (
|
||||
"strconv"
|
||||
)
|
||||
|
||||
type Encoding int
|
||||
|
||||
const (
|
||||
EncodePath Encoding = 1 + iota
|
||||
EncodeUserPassword
|
||||
EncodeQueryComponent
|
||||
EncodeFragment
|
||||
)
|
||||
|
||||
type EscapeError string
|
||||
|
||||
func (e EscapeError) Error() string {
|
||||
return "invalid URL escape " + strconv.Quote(string(e))
|
||||
}
|
||||
|
||||
func ishex(c byte) bool {
|
||||
switch {
|
||||
case '0' <= c && c <= '9':
|
||||
return true
|
||||
case 'a' <= c && c <= 'f':
|
||||
return true
|
||||
case 'A' <= c && c <= 'F':
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func unhex(c byte) byte {
|
||||
switch {
|
||||
case '0' <= c && c <= '9':
|
||||
return c - '0'
|
||||
case 'a' <= c && c <= 'f':
|
||||
return c - 'a' + 10
|
||||
case 'A' <= c && c <= 'F':
|
||||
return c - 'A' + 10
|
||||
}
|
||||
return 0
|
||||
}
|
||||
|
||||
// Return true if the specified character should be escaped when
|
||||
// appearing in a URL string, according to RFC 3986.
|
||||
// When 'all' is true the full range of reserved characters are matched.
|
||||
func shouldEscape(c byte, mode Encoding) bool {
|
||||
|
||||
// §2.3 Unreserved characters (alphanum)
|
||||
if 'A' <= c && c <= 'Z' || 'a' <= c && c <= 'z' || '0' <= c && c <= '9' {
|
||||
return false
|
||||
}
|
||||
|
||||
switch c {
|
||||
case '-', '_', '.', '~': // §2.3 Unreserved characters (mark)
|
||||
return false
|
||||
|
||||
case '$', '&', '+', ',', '/', ':', ';', '=', '?', '@': // §2.2 Reserved characters (reserved)
|
||||
// Different sections of the URL allow a few of
|
||||
// the reserved characters to appear unescaped.
|
||||
switch mode {
|
||||
case EncodePath: // §3.3
|
||||
// The RFC allows : @ & = + $ but saves / ; , for assigning
|
||||
// meaning to individual path segments. This package
|
||||
// only manipulates the path as a whole, so we allow those
|
||||
// last two as well. That leaves only ? to escape.
|
||||
return c == '?'
|
||||
|
||||
case EncodeUserPassword: // §3.2.2
|
||||
// The RFC allows ; : & = + $ , in userinfo, so we must escape only @ and /.
|
||||
// The parsing of userinfo treats : as special so we must escape that too.
|
||||
return c == '@' || c == '/' || c == ':'
|
||||
|
||||
case EncodeQueryComponent: // §3.4
|
||||
// The RFC reserves (so we must escape) everything.
|
||||
return true
|
||||
|
||||
case EncodeFragment: // §4.1
|
||||
// The RFC text is silent but the grammar allows
|
||||
// everything, so escape nothing.
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
// Everything else must be escaped.
|
||||
return true
|
||||
}
|
||||
|
||||
// QueryUnescape does the inverse transformation of QueryEscape, converting
|
||||
// %AB into the byte 0xAB and '+' into ' ' (space). It returns an error if
|
||||
// any % is not followed by two hexadecimal digits.
|
||||
func QueryUnescape(s string) (string, error) {
|
||||
|
||||
return UnescapeEx(s, EncodeQueryComponent)
|
||||
}
|
||||
|
||||
func Unescape(s string) (string, error) {
|
||||
|
||||
return UnescapeEx(s, EncodePath)
|
||||
}
|
||||
|
||||
// UnescapeEx unescapes a string; the mode specifies
|
||||
// which section of the URL string is being unescaped.
|
||||
func UnescapeEx(s string, mode Encoding) (string, error) {
|
||||
|
||||
// Count %, check that they're well-formed.
|
||||
n := 0
|
||||
hasPlus := false
|
||||
for i := 0; i < len(s); {
|
||||
switch s[i] {
|
||||
case '%':
|
||||
n++
|
||||
if i+2 >= len(s) || !ishex(s[i+1]) || !ishex(s[i+2]) {
|
||||
s = s[i:]
|
||||
if len(s) > 3 {
|
||||
s = s[0:3]
|
||||
}
|
||||
return "", EscapeError(s)
|
||||
}
|
||||
i += 3
|
||||
case '+':
|
||||
hasPlus = mode == EncodeQueryComponent
|
||||
i++
|
||||
default:
|
||||
i++
|
||||
}
|
||||
}
|
||||
|
||||
if n == 0 && !hasPlus {
|
||||
return s, nil
|
||||
}
|
||||
|
||||
t := make([]byte, len(s)-2*n)
|
||||
j := 0
|
||||
for i := 0; i < len(s); {
|
||||
switch s[i] {
|
||||
case '%':
|
||||
t[j] = unhex(s[i+1])<<4 | unhex(s[i+2])
|
||||
j++
|
||||
i += 3
|
||||
case '+':
|
||||
if mode == EncodeQueryComponent {
|
||||
t[j] = ' '
|
||||
} else {
|
||||
t[j] = '+'
|
||||
}
|
||||
j++
|
||||
i++
|
||||
default:
|
||||
t[j] = s[i]
|
||||
j++
|
||||
i++
|
||||
}
|
||||
}
|
||||
return string(t), nil
|
||||
}
|
||||
|
||||
// QueryEscape escapes the string so it can be safely placed
|
||||
// inside a URL query.
|
||||
func QueryEscape(s string) string {
|
||||
|
||||
return EscapeEx(s, EncodeQueryComponent)
|
||||
}
|
||||
|
||||
func Escape(s string) string {
|
||||
|
||||
return EscapeEx(s, EncodePath)
|
||||
}
|
||||
|
||||
func EscapeEx(s string, mode Encoding) string {
|
||||
|
||||
spaceCount, hexCount := 0, 0
|
||||
for i := 0; i < len(s); i++ {
|
||||
c := s[i]
|
||||
if shouldEscape(c, mode) {
|
||||
if c == ' ' && mode == EncodeQueryComponent {
|
||||
spaceCount++
|
||||
} else {
|
||||
hexCount++
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if spaceCount == 0 && hexCount == 0 {
|
||||
return s
|
||||
}
|
||||
|
||||
t := make([]byte, len(s)+2*hexCount)
|
||||
j := 0
|
||||
for i := 0; i < len(s); i++ {
|
||||
switch c := s[i]; {
|
||||
case c == ' ' && mode == EncodeQueryComponent:
|
||||
t[j] = '+'
|
||||
j++
|
||||
case shouldEscape(c, mode):
|
||||
t[j] = '%'
|
||||
t[j+1] = "0123456789ABCDEF"[c>>4]
|
||||
t[j+2] = "0123456789ABCDEF"[c&15]
|
||||
j += 3
|
||||
default:
|
||||
t[j] = s[i]
|
||||
j++
|
||||
}
|
||||
}
|
||||
return string(t)
|
||||
}
|
||||
22
vendor/github.com/qiniu/bytes/.gitignore
generated
vendored
22
vendor/github.com/qiniu/bytes/.gitignore
generated
vendored
@@ -1,22 +0,0 @@
|
||||
# Compiled Object files, Static and Dynamic libs (Shared Objects)
|
||||
*.o
|
||||
*.a
|
||||
*.so
|
||||
|
||||
# Folders
|
||||
_obj
|
||||
_test
|
||||
|
||||
# Architecture specific extensions/prefixes
|
||||
*.[568vq]
|
||||
[568vq].out
|
||||
|
||||
*.cgo1.go
|
||||
*.cgo2.c
|
||||
_cgo_defun.c
|
||||
_cgo_gotypes.go
|
||||
_cgo_export.*
|
||||
|
||||
_testmain.go
|
||||
|
||||
*.exe
|
||||
8
vendor/github.com/qiniu/bytes/README.md
generated
vendored
8
vendor/github.com/qiniu/bytes/README.md
generated
vendored
@@ -1,8 +0,0 @@
|
||||
bytes
|
||||
=====
|
||||
|
||||
[](https://drone.io/github.com/qiniu/bytes/latest)
|
||||
|
||||

|
||||
|
||||
Extension module of golang bytes processing
|
||||
177
vendor/github.com/qiniu/bytes/bytes.go
generated
vendored
177
vendor/github.com/qiniu/bytes/bytes.go
generated
vendored
@@ -1,177 +0,0 @@
|
||||
package bytes
|
||||
|
||||
import (
|
||||
"io"
|
||||
"syscall"
|
||||
)
|
||||
|
||||
// ---------------------------------------------------
|
||||
|
||||
type Reader struct {
|
||||
b []byte
|
||||
off int
|
||||
}
|
||||
|
||||
func NewReader(val []byte) *Reader {
|
||||
return &Reader{val, 0}
|
||||
}
|
||||
|
||||
func (r *Reader) Len() int {
|
||||
if r.off >= len(r.b) {
|
||||
return 0
|
||||
}
|
||||
return len(r.b) - r.off
|
||||
}
|
||||
|
||||
func (r *Reader) Bytes() []byte {
|
||||
return r.b[r.off:]
|
||||
}
|
||||
|
||||
func (r *Reader) SeekToBegin() (err error) {
|
||||
r.off = 0
|
||||
return
|
||||
}
|
||||
|
||||
func (r *Reader) Seek(offset int64, whence int) (ret int64, err error) {
|
||||
switch whence {
|
||||
case 0:
|
||||
case 1:
|
||||
offset += int64(r.off)
|
||||
case 2:
|
||||
offset += int64(len(r.b))
|
||||
default:
|
||||
err = syscall.EINVAL
|
||||
return
|
||||
}
|
||||
if offset < 0 {
|
||||
err = syscall.EINVAL
|
||||
return
|
||||
}
|
||||
if offset >= int64(len(r.b)) {
|
||||
r.off = len(r.b)
|
||||
} else {
|
||||
r.off = int(offset)
|
||||
}
|
||||
ret = int64(r.off)
|
||||
return
|
||||
}
|
||||
|
||||
func (r *Reader) Read(val []byte) (n int, err error) {
|
||||
n = copy(val, r.b[r.off:])
|
||||
if n == 0 && len(val) != 0 {
|
||||
err = io.EOF
|
||||
return
|
||||
}
|
||||
r.off += n
|
||||
return
|
||||
}
|
||||
|
||||
func (r *Reader) Close() (err error) {
|
||||
return
|
||||
}
|
||||
|
||||
// ---------------------------------------------------
|
||||
|
||||
type Writer struct {
|
||||
b []byte
|
||||
n int
|
||||
}
|
||||
|
||||
func NewWriter(buff []byte) *Writer {
|
||||
return &Writer{buff, 0}
|
||||
}
|
||||
|
||||
func (p *Writer) Write(val []byte) (n int, err error) {
|
||||
n = copy(p.b[p.n:], val)
|
||||
if n == 0 && len(val) > 0 {
|
||||
err = io.EOF
|
||||
return
|
||||
}
|
||||
p.n += n
|
||||
return
|
||||
}
|
||||
|
||||
func (p *Writer) Len() int {
|
||||
return p.n
|
||||
}
|
||||
|
||||
func (p *Writer) Bytes() []byte {
|
||||
return p.b[:p.n]
|
||||
}
|
||||
|
||||
func (p *Writer) Reset() {
|
||||
p.n = 0
|
||||
}
|
||||
|
||||
// ---------------------------------------------------
|
||||
|
||||
type Buffer struct {
|
||||
b []byte
|
||||
}
|
||||
|
||||
func NewBuffer() *Buffer {
|
||||
return new(Buffer)
|
||||
}
|
||||
|
||||
func (p *Buffer) ReadAt(buf []byte, off int64) (n int, err error) {
|
||||
ioff := int(off)
|
||||
if len(p.b) <= ioff {
|
||||
return 0, io.EOF
|
||||
}
|
||||
n = copy(buf, p.b[ioff:])
|
||||
if n != len(buf) {
|
||||
err = io.EOF
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func (p *Buffer) WriteAt(buf []byte, off int64) (n int, err error) {
|
||||
ioff := int(off)
|
||||
iend := ioff + len(buf)
|
||||
if len(p.b) < iend {
|
||||
if len(p.b) == ioff {
|
||||
p.b = append(p.b, buf...)
|
||||
return len(buf), nil
|
||||
}
|
||||
zero := make([]byte, iend-len(p.b))
|
||||
p.b = append(p.b, zero...)
|
||||
}
|
||||
copy(p.b[ioff:], buf)
|
||||
return len(buf), nil
|
||||
}
|
||||
|
||||
func (p *Buffer) WriteStringAt(buf string, off int64) (n int, err error) {
|
||||
ioff := int(off)
|
||||
iend := ioff + len(buf)
|
||||
if len(p.b) < iend {
|
||||
if len(p.b) == ioff {
|
||||
p.b = append(p.b, buf...)
|
||||
return len(buf), nil
|
||||
}
|
||||
zero := make([]byte, iend-len(p.b))
|
||||
p.b = append(p.b, zero...)
|
||||
}
|
||||
copy(p.b[ioff:], buf)
|
||||
return len(buf), nil
|
||||
}
|
||||
|
||||
func (p *Buffer) Truncate(fsize int64) (err error) {
|
||||
size := int(fsize)
|
||||
if len(p.b) < size {
|
||||
zero := make([]byte, size-len(p.b))
|
||||
p.b = append(p.b, zero...)
|
||||
} else {
|
||||
p.b = p.b[:size]
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (p *Buffer) Buffer() []byte {
|
||||
return p.b
|
||||
}
|
||||
|
||||
func (p *Buffer) Len() int {
|
||||
return len(p.b)
|
||||
}
|
||||
|
||||
// ---------------------------------------------------
|
||||
62
vendor/github.com/qiniu/bytes/seekable/seekable.go
generated
vendored
62
vendor/github.com/qiniu/bytes/seekable/seekable.go
generated
vendored
@@ -1,62 +0,0 @@
|
||||
package seekable
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"io"
|
||||
"io/ioutil"
|
||||
"net/http"
|
||||
|
||||
"github.com/qiniu/bytes"
|
||||
)
|
||||
|
||||
// ---------------------------------------------------
|
||||
|
||||
type Seekabler interface {
|
||||
Bytes() []byte
|
||||
Read(val []byte) (n int, err error)
|
||||
SeekToBegin() error
|
||||
}
|
||||
|
||||
type SeekableCloser interface {
|
||||
Seekabler
|
||||
io.Closer
|
||||
}
|
||||
|
||||
// ---------------------------------------------------
|
||||
|
||||
type readCloser struct {
|
||||
Seekabler
|
||||
io.Closer
|
||||
}
|
||||
|
||||
var ErrNoBody = errors.New("no body")
|
||||
|
||||
func New(req *http.Request) (r SeekableCloser, err error) {
|
||||
if req.Body == nil {
|
||||
return nil, ErrNoBody
|
||||
}
|
||||
var ok bool
|
||||
if r, ok = req.Body.(SeekableCloser); ok {
|
||||
return
|
||||
}
|
||||
b, err2 := ReadAll(req)
|
||||
if err2 != nil {
|
||||
return nil, err2
|
||||
}
|
||||
r = bytes.NewReader(b)
|
||||
req.Body = readCloser{r, req.Body}
|
||||
return
|
||||
}
|
||||
|
||||
func ReadAll(req *http.Request) (b []byte, err error) {
|
||||
if req.ContentLength > 0 {
|
||||
b = make([]byte, int(req.ContentLength))
|
||||
_, err = io.ReadFull(req.Body, b)
|
||||
return
|
||||
} else if req.ContentLength == 0 {
|
||||
return nil, ErrNoBody
|
||||
}
|
||||
return ioutil.ReadAll(req.Body)
|
||||
}
|
||||
|
||||
// ---------------------------------------------------
|
||||
22
vendor/github.com/qiniu/rpc/.gitignore
generated
vendored
22
vendor/github.com/qiniu/rpc/.gitignore
generated
vendored
@@ -1,22 +0,0 @@
|
||||
# Compiled Object files, Static and Dynamic libs (Shared Objects)
|
||||
*.o
|
||||
*.a
|
||||
*.so
|
||||
|
||||
# Folders
|
||||
_obj
|
||||
_test
|
||||
|
||||
# Architecture specific extensions/prefixes
|
||||
*.[568vq]
|
||||
[568vq].out
|
||||
|
||||
*.cgo1.go
|
||||
*.cgo2.c
|
||||
_cgo_defun.c
|
||||
_cgo_gotypes.go
|
||||
_cgo_export.*
|
||||
|
||||
_testmain.go
|
||||
|
||||
*.exe
|
||||
23
vendor/github.com/qiniu/rpc/CHANGELOG.md
generated
vendored
23
vendor/github.com/qiniu/rpc/CHANGELOG.md
generated
vendored
@@ -1,23 +0,0 @@
|
||||
#CHANGELOG
|
||||
|
||||
## v2.0.01
|
||||
|
||||
2013-09-10 Issue [#7](https://github.com/qiniu/rpc/pull/7):
|
||||
|
||||
- support jsonrpc
|
||||
|
||||
|
||||
## v2.0.00
|
||||
|
||||
2013-03-20 Issue [#5](https://github.com/qiniu/rpc/pull/5):
|
||||
|
||||
- support X-Reqid, X-Log
|
||||
- support User-Agent
|
||||
- rpc error bugfix
|
||||
|
||||
|
||||
## v1.0.00
|
||||
|
||||
2013-03-10 Issue [#2](https://github.com/qiniu/rpc/pull/2):
|
||||
|
||||
- initial version
|
||||
8
vendor/github.com/qiniu/rpc/README.md
generated
vendored
8
vendor/github.com/qiniu/rpc/README.md
generated
vendored
@@ -1,8 +0,0 @@
|
||||
rpc
|
||||
===
|
||||
|
||||
[](https://drone.io/github.com/qiniu/rpc/latest)
|
||||
|
||||
[](http://qiniu.com/)
|
||||
|
||||
Golang rpc client based on http
|
||||
207
vendor/github.com/qiniu/rpc/rpc_client.go
generated
vendored
207
vendor/github.com/qiniu/rpc/rpc_client.go
generated
vendored
@@ -1,207 +0,0 @@
|
||||
package rpc
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/json"
|
||||
"io"
|
||||
"net/http"
|
||||
"net/url"
|
||||
"strings"
|
||||
)
|
||||
|
||||
var UserAgent = "Golang qiniu/rpc package"
|
||||
|
||||
// --------------------------------------------------------------------
|
||||
|
||||
type Client struct {
|
||||
*http.Client
|
||||
}
|
||||
|
||||
var DefaultClient = Client{http.DefaultClient}
|
||||
|
||||
// --------------------------------------------------------------------
|
||||
|
||||
type Logger interface {
|
||||
ReqId() string
|
||||
Xput(logs []string)
|
||||
}
|
||||
|
||||
// --------------------------------------------------------------------
|
||||
|
||||
func (r Client) Get(l Logger, url string) (resp *http.Response, err error) {
|
||||
|
||||
req, err := http.NewRequest("GET", url, nil)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
return r.Do(l, req)
|
||||
}
|
||||
|
||||
func (r Client) PostWith(
|
||||
l Logger, url1 string, bodyType string, body io.Reader, bodyLength int) (resp *http.Response, err error) {
|
||||
|
||||
req, err := http.NewRequest("POST", url1, body)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
req.Header.Set("Content-Type", bodyType)
|
||||
req.ContentLength = int64(bodyLength)
|
||||
return r.Do(l, req)
|
||||
}
|
||||
|
||||
func (r Client) PostWith64(
|
||||
l Logger, url1 string, bodyType string, body io.Reader, bodyLength int64) (resp *http.Response, err error) {
|
||||
|
||||
req, err := http.NewRequest("POST", url1, body)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
req.Header.Set("Content-Type", bodyType)
|
||||
req.ContentLength = bodyLength
|
||||
return r.Do(l, req)
|
||||
}
|
||||
|
||||
func (r Client) PostWithForm(
|
||||
l Logger, url1 string, data map[string][]string) (resp *http.Response, err error) {
|
||||
|
||||
msg := url.Values(data).Encode()
|
||||
return r.PostWith(l, url1, "application/x-www-form-urlencoded", strings.NewReader(msg), len(msg))
|
||||
}
|
||||
|
||||
func (r Client) PostWithJson(
|
||||
l Logger, url1 string, data interface{}) (resp *http.Response, err error) {
|
||||
|
||||
msg, err := json.Marshal(data)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
return r.PostWith(l, url1, "application/json", bytes.NewReader(msg), len(msg))
|
||||
}
|
||||
|
||||
func (r Client) Do(l Logger, req *http.Request) (resp *http.Response, err error) {
|
||||
|
||||
if l != nil {
|
||||
req.Header.Set("X-Reqid", l.ReqId())
|
||||
}
|
||||
|
||||
req.Header.Set("User-Agent", UserAgent)
|
||||
resp, err = r.Client.Do(req)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
if l != nil {
|
||||
details := resp.Header["X-Log"]
|
||||
if len(details) > 0 {
|
||||
l.Xput(details)
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// --------------------------------------------------------------------
|
||||
|
||||
type ErrorInfo struct {
|
||||
Err string `json:"error"`
|
||||
Reqid string `json:"reqid"`
|
||||
Details []string `json:"details"`
|
||||
Code int `json:"code"`
|
||||
}
|
||||
|
||||
func (r *ErrorInfo) Error() string {
|
||||
msg, _ := json.Marshal(r)
|
||||
return string(msg)
|
||||
}
|
||||
|
||||
// --------------------------------------------------------------------
|
||||
|
||||
type errorRet struct {
|
||||
Error string `json:"error"`
|
||||
}
|
||||
|
||||
func ResponseError(resp *http.Response) (err error) {
|
||||
|
||||
e := &ErrorInfo{
|
||||
Details: resp.Header["X-Log"],
|
||||
Reqid: resp.Header.Get("X-Reqid"),
|
||||
Code: resp.StatusCode,
|
||||
}
|
||||
if resp.StatusCode > 299 {
|
||||
if resp.ContentLength != 0 {
|
||||
if ct, ok := resp.Header["Content-Type"]; ok && ct[0] == "application/json" {
|
||||
var ret1 errorRet
|
||||
json.NewDecoder(resp.Body).Decode(&ret1)
|
||||
e.Err = ret1.Error
|
||||
}
|
||||
}
|
||||
}
|
||||
return e
|
||||
}
|
||||
|
||||
func callRet(l Logger, ret interface{}, resp *http.Response) (err error) {
|
||||
|
||||
defer resp.Body.Close()
|
||||
|
||||
if resp.StatusCode/100 == 2 {
|
||||
if ret != nil && resp.ContentLength != 0 {
|
||||
err = json.NewDecoder(resp.Body).Decode(ret)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
}
|
||||
if resp.StatusCode == 200 {
|
||||
return nil
|
||||
}
|
||||
}
|
||||
return ResponseError(resp)
|
||||
}
|
||||
|
||||
func (r Client) CallWithForm(l Logger, ret interface{}, url1 string, param map[string][]string) (err error) {
|
||||
|
||||
resp, err := r.PostWithForm(l, url1, param)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return callRet(l, ret, resp)
|
||||
}
|
||||
|
||||
func (r Client) CallWithJson(l Logger, ret interface{}, url1 string, param interface{}) (err error) {
|
||||
|
||||
resp, err := r.PostWithJson(l, url1, param)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return callRet(l, ret, resp)
|
||||
}
|
||||
|
||||
func (r Client) CallWith(
|
||||
l Logger, ret interface{}, url1 string, bodyType string, body io.Reader, bodyLength int) (err error) {
|
||||
|
||||
resp, err := r.PostWith(l, url1, bodyType, body, bodyLength)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return callRet(l, ret, resp)
|
||||
}
|
||||
|
||||
func (r Client) CallWith64(
|
||||
l Logger, ret interface{}, url1 string, bodyType string, body io.Reader, bodyLength int64) (err error) {
|
||||
|
||||
resp, err := r.PostWith64(l, url1, bodyType, body, bodyLength)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return callRet(l, ret, resp)
|
||||
}
|
||||
|
||||
func (r Client) Call(
|
||||
l Logger, ret interface{}, url1 string) (err error) {
|
||||
|
||||
resp, err := r.PostWith(l, url1, "application/x-www-form-urlencoded", nil, 0)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return callRet(l, ret, resp)
|
||||
}
|
||||
|
||||
// --------------------------------------------------------------------
|
||||
158
vendor/goftp.io/server/driver.go
generated
vendored
158
vendor/goftp.io/server/driver.go
generated
vendored
@@ -1,158 +0,0 @@
|
||||
// Copyright 2018 The goftp Authors. All rights reserved.
|
||||
// Use of this source code is governed by a MIT-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package server
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"io"
|
||||
"strings"
|
||||
)
|
||||
|
||||
// DriverFactory is a driver factory to create driver. For each client that connects to the server, a new FTPDriver is required.
|
||||
// Create an implementation if this interface and provide it to FTPServer.
|
||||
type DriverFactory interface {
|
||||
NewDriver() (Driver, error)
|
||||
}
|
||||
|
||||
// Driver is an interface that you will create an implementation that speaks to your
|
||||
// chosen persistence layer. graval will create a new instance of your
|
||||
// driver for each client that connects and delegate to it as required.
|
||||
type Driver interface {
|
||||
// params - a file path
|
||||
// returns - a time indicating when the requested path was last modified
|
||||
// - an error if the file doesn't exist or the user lacks
|
||||
// permissions
|
||||
Stat(string) (FileInfo, error)
|
||||
|
||||
// params - path, function on file or subdir found
|
||||
// returns - error
|
||||
// path
|
||||
ListDir(string, func(FileInfo) error) error
|
||||
|
||||
// params - path
|
||||
// returns - nil if the directory was deleted or any error encountered
|
||||
DeleteDir(string) error
|
||||
|
||||
// params - path
|
||||
// returns - nil if the file was deleted or any error encountered
|
||||
DeleteFile(string) error
|
||||
|
||||
// params - from_path, to_path
|
||||
// returns - nil if the file was renamed or any error encountered
|
||||
Rename(string, string) error
|
||||
|
||||
// params - path
|
||||
// returns - nil if the new directory was created or any error encountered
|
||||
MakeDir(string) error
|
||||
|
||||
// params - path
|
||||
// returns - a string containing the file data to send to the client
|
||||
GetFile(string, int64) (int64, io.ReadCloser, error)
|
||||
|
||||
// params - destination path, an io.Reader containing the file data
|
||||
// returns - the number of bytes writen and the first error encountered while writing, if any.
|
||||
PutFile(string, io.Reader, bool) (int64, error)
|
||||
}
|
||||
|
||||
var _ Driver = &MultipleDriver{}
|
||||
|
||||
// MultipleDriver represents a composite driver
|
||||
type MultipleDriver struct {
|
||||
drivers map[string]Driver
|
||||
}
|
||||
|
||||
// Stat implements Driver
|
||||
func (driver *MultipleDriver) Stat(path string) (FileInfo, error) {
|
||||
for prefix, driver := range driver.drivers {
|
||||
if strings.HasPrefix(path, prefix) {
|
||||
return driver.Stat(strings.TrimPrefix(path, prefix))
|
||||
}
|
||||
}
|
||||
return nil, errors.New("Not a file")
|
||||
}
|
||||
|
||||
// ListDir implements Driver
|
||||
func (driver *MultipleDriver) ListDir(path string, callback func(FileInfo) error) error {
|
||||
for prefix, driver := range driver.drivers {
|
||||
if strings.HasPrefix(path, prefix) {
|
||||
return driver.ListDir(strings.TrimPrefix(path, prefix), callback)
|
||||
}
|
||||
}
|
||||
return errors.New("Not a directory")
|
||||
}
|
||||
|
||||
// DeleteDir implements Driver
|
||||
func (driver *MultipleDriver) DeleteDir(path string) error {
|
||||
for prefix, driver := range driver.drivers {
|
||||
if strings.HasPrefix(path, prefix) {
|
||||
return driver.DeleteDir(strings.TrimPrefix(path, prefix))
|
||||
}
|
||||
}
|
||||
return errors.New("Not a directory")
|
||||
}
|
||||
|
||||
// DeleteFile implements Driver
|
||||
func (driver *MultipleDriver) DeleteFile(path string) error {
|
||||
for prefix, driver := range driver.drivers {
|
||||
if strings.HasPrefix(path, prefix) {
|
||||
return driver.DeleteFile(strings.TrimPrefix(path, prefix))
|
||||
}
|
||||
}
|
||||
|
||||
return errors.New("Not a file")
|
||||
}
|
||||
|
||||
// Rename implements Driver
|
||||
func (driver *MultipleDriver) Rename(fromPath string, toPath string) error {
|
||||
for prefix, driver := range driver.drivers {
|
||||
if strings.HasPrefix(fromPath, prefix) {
|
||||
return driver.Rename(strings.TrimPrefix(fromPath, prefix), strings.TrimPrefix(toPath, prefix))
|
||||
}
|
||||
}
|
||||
|
||||
return errors.New("Not a file")
|
||||
}
|
||||
|
||||
// MakeDir implements Driver
|
||||
func (driver *MultipleDriver) MakeDir(path string) error {
|
||||
for prefix, driver := range driver.drivers {
|
||||
if strings.HasPrefix(path, prefix) {
|
||||
return driver.MakeDir(strings.TrimPrefix(path, prefix))
|
||||
}
|
||||
}
|
||||
return errors.New("Not a directory")
|
||||
}
|
||||
|
||||
// GetFile implements Driver
|
||||
func (driver *MultipleDriver) GetFile(path string, offset int64) (int64, io.ReadCloser, error) {
|
||||
for prefix, driver := range driver.drivers {
|
||||
if strings.HasPrefix(path, prefix) {
|
||||
return driver.GetFile(strings.TrimPrefix(path, prefix), offset)
|
||||
}
|
||||
}
|
||||
|
||||
return 0, nil, errors.New("Not a file")
|
||||
}
|
||||
|
||||
// PutFile implements Driver
|
||||
func (driver *MultipleDriver) PutFile(destPath string, data io.Reader, appendData bool) (int64, error) {
|
||||
for prefix, driver := range driver.drivers {
|
||||
if strings.HasPrefix(destPath, prefix) {
|
||||
return driver.PutFile(strings.TrimPrefix(destPath, prefix), data, appendData)
|
||||
}
|
||||
}
|
||||
|
||||
return 0, errors.New("Not a file")
|
||||
}
|
||||
|
||||
// MultipleDriverFactory implements a DriverFactory
|
||||
type MultipleDriverFactory struct {
|
||||
drivers map[string]Driver
|
||||
}
|
||||
|
||||
// NewDriver implements DriverFactory
|
||||
func (factory *MultipleDriverFactory) NewDriver() (Driver, error) {
|
||||
return &MultipleDriver{factory.drivers}, nil
|
||||
}
|
||||
176
vendor/goftp.io/server/notifier.go
generated
vendored
176
vendor/goftp.io/server/notifier.go
generated
vendored
@@ -1,176 +0,0 @@
|
||||
// Copyright 2020 The goftp Authors. All rights reserved.
|
||||
// Use of this source code is governed by a MIT-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package server
|
||||
|
||||
// Notifier represents a notification operator interface
|
||||
type Notifier interface {
|
||||
BeforeLoginUser(conn *Conn, userName string)
|
||||
BeforePutFile(conn *Conn, dstPath string)
|
||||
BeforeDeleteFile(conn *Conn, dstPath string)
|
||||
BeforeChangeCurDir(conn *Conn, oldCurDir, newCurDir string)
|
||||
BeforeCreateDir(conn *Conn, dstPath string)
|
||||
BeforeDeleteDir(conn *Conn, dstPath string)
|
||||
BeforeDownloadFile(conn *Conn, dstPath string)
|
||||
AfterUserLogin(conn *Conn, userName, password string, passMatched bool, err error)
|
||||
AfterFilePut(conn *Conn, dstPath string, size int64, err error)
|
||||
AfterFileDeleted(conn *Conn, dstPath string, err error)
|
||||
AfterFileDownloaded(conn *Conn, dstPath string, size int64, err error)
|
||||
AfterCurDirChanged(conn *Conn, oldCurDir, newCurDir string, err error)
|
||||
AfterDirCreated(conn *Conn, dstPath string, err error)
|
||||
AfterDirDeleted(conn *Conn, dstPath string, err error)
|
||||
}
|
||||
|
||||
type notifierList []Notifier
|
||||
|
||||
var (
|
||||
_ Notifier = notifierList{}
|
||||
)
|
||||
|
||||
func (notifiers notifierList) BeforeLoginUser(conn *Conn, userName string) {
|
||||
for _, notifier := range notifiers {
|
||||
notifier.BeforeLoginUser(conn, userName)
|
||||
}
|
||||
}
|
||||
|
||||
func (notifiers notifierList) BeforePutFile(conn *Conn, dstPath string) {
|
||||
for _, notifier := range notifiers {
|
||||
notifier.BeforePutFile(conn, dstPath)
|
||||
}
|
||||
}
|
||||
|
||||
func (notifiers notifierList) BeforeDeleteFile(conn *Conn, dstPath string) {
|
||||
for _, notifier := range notifiers {
|
||||
notifier.BeforeDeleteFile(conn, dstPath)
|
||||
}
|
||||
}
|
||||
|
||||
func (notifiers notifierList) BeforeChangeCurDir(conn *Conn, oldCurDir, newCurDir string) {
|
||||
for _, notifier := range notifiers {
|
||||
notifier.BeforeChangeCurDir(conn, oldCurDir, newCurDir)
|
||||
}
|
||||
}
|
||||
|
||||
func (notifiers notifierList) BeforeCreateDir(conn *Conn, dstPath string) {
|
||||
for _, notifier := range notifiers {
|
||||
notifier.BeforeCreateDir(conn, dstPath)
|
||||
}
|
||||
}
|
||||
|
||||
func (notifiers notifierList) BeforeDeleteDir(conn *Conn, dstPath string) {
|
||||
for _, notifier := range notifiers {
|
||||
notifier.BeforeDeleteDir(conn, dstPath)
|
||||
}
|
||||
}
|
||||
|
||||
func (notifiers notifierList) BeforeDownloadFile(conn *Conn, dstPath string) {
|
||||
for _, notifier := range notifiers {
|
||||
notifier.BeforeDownloadFile(conn, dstPath)
|
||||
}
|
||||
}
|
||||
|
||||
func (notifiers notifierList) AfterUserLogin(conn *Conn, userName, password string, passMatched bool, err error) {
|
||||
for _, notifier := range notifiers {
|
||||
notifier.AfterUserLogin(conn, userName, password, passMatched, err)
|
||||
}
|
||||
}
|
||||
|
||||
func (notifiers notifierList) AfterFilePut(conn *Conn, dstPath string, size int64, err error) {
|
||||
for _, notifier := range notifiers {
|
||||
notifier.AfterFilePut(conn, dstPath, size, err)
|
||||
}
|
||||
}
|
||||
|
||||
func (notifiers notifierList) AfterFileDeleted(conn *Conn, dstPath string, err error) {
|
||||
for _, notifier := range notifiers {
|
||||
notifier.AfterFileDeleted(conn, dstPath, err)
|
||||
}
|
||||
}
|
||||
|
||||
func (notifiers notifierList) AfterFileDownloaded(conn *Conn, dstPath string, size int64, err error) {
|
||||
for _, notifier := range notifiers {
|
||||
notifier.AfterFileDownloaded(conn, dstPath, size, err)
|
||||
}
|
||||
}
|
||||
|
||||
func (notifiers notifierList) AfterCurDirChanged(conn *Conn, oldCurDir, newCurDir string, err error) {
|
||||
for _, notifier := range notifiers {
|
||||
notifier.AfterCurDirChanged(conn, oldCurDir, newCurDir, err)
|
||||
}
|
||||
}
|
||||
|
||||
func (notifiers notifierList) AfterDirCreated(conn *Conn, dstPath string, err error) {
|
||||
for _, notifier := range notifiers {
|
||||
notifier.AfterDirCreated(conn, dstPath, err)
|
||||
}
|
||||
}
|
||||
|
||||
func (notifiers notifierList) AfterDirDeleted(conn *Conn, dstPath string, err error) {
|
||||
for _, notifier := range notifiers {
|
||||
notifier.AfterDirDeleted(conn, dstPath, err)
|
||||
}
|
||||
}
|
||||
|
||||
// NullNotifier implements Notifier
|
||||
type NullNotifier struct{}
|
||||
|
||||
var (
|
||||
_ Notifier = &NullNotifier{}
|
||||
)
|
||||
|
||||
// BeforeLoginUser implements Notifier
|
||||
func (NullNotifier) BeforeLoginUser(conn *Conn, userName string) {
|
||||
}
|
||||
|
||||
// BeforePutFile implements Notifier
|
||||
func (NullNotifier) BeforePutFile(conn *Conn, dstPath string) {
|
||||
}
|
||||
|
||||
// BeforeDeleteFile implements Notifier
|
||||
func (NullNotifier) BeforeDeleteFile(conn *Conn, dstPath string) {
|
||||
}
|
||||
|
||||
// BeforeChangeCurDir implements Notifier
|
||||
func (NullNotifier) BeforeChangeCurDir(conn *Conn, oldCurDir, newCurDir string) {
|
||||
}
|
||||
|
||||
// BeforeCreateDir implements Notifier
|
||||
func (NullNotifier) BeforeCreateDir(conn *Conn, dstPath string) {
|
||||
}
|
||||
|
||||
// BeforeDeleteDir implements Notifier
|
||||
func (NullNotifier) BeforeDeleteDir(conn *Conn, dstPath string) {
|
||||
}
|
||||
|
||||
// BeforeDownloadFile implements Notifier
|
||||
func (NullNotifier) BeforeDownloadFile(conn *Conn, dstPath string) {
|
||||
}
|
||||
|
||||
// AfterUserLogin implements Notifier
|
||||
func (NullNotifier) AfterUserLogin(conn *Conn, userName, password string, passMatched bool, err error) {
|
||||
}
|
||||
|
||||
// AfterFilePut implements Notifier
|
||||
func (NullNotifier) AfterFilePut(conn *Conn, dstPath string, size int64, err error) {
|
||||
}
|
||||
|
||||
// AfterFileDeleted implements Notifier
|
||||
func (NullNotifier) AfterFileDeleted(conn *Conn, dstPath string, err error) {
|
||||
}
|
||||
|
||||
// AfterFileDownloaded implements Notifier
|
||||
func (NullNotifier) AfterFileDownloaded(conn *Conn, dstPath string, size int64, err error) {
|
||||
}
|
||||
|
||||
// AfterCurDirChanged implements Notifier
|
||||
func (NullNotifier) AfterCurDirChanged(conn *Conn, oldCurDir, newCurDir string, err error) {
|
||||
}
|
||||
|
||||
// AfterDirCreated implements Notifier
|
||||
func (NullNotifier) AfterDirCreated(conn *Conn, dstPath string, err error) {
|
||||
}
|
||||
|
||||
// AfterDirDeleted implements Notifier
|
||||
func (NullNotifier) AfterDirDeleted(conn *Conn, dstPath string, err error) {
|
||||
}
|
||||
8
vendor/goftp.io/server/.drone.yml → vendor/goftp.io/server/v2/.drone.yml
generated
vendored
8
vendor/goftp.io/server/.drone.yml → vendor/goftp.io/server/v2/.drone.yml
generated
vendored
@@ -13,6 +13,14 @@ steps:
|
||||
exclude:
|
||||
- pull_request
|
||||
|
||||
- name: linter
|
||||
image: golang:1.14
|
||||
commands:
|
||||
- go get github.com/golangci/golangci-lint/cmd/golangci-lint@v1.30.0
|
||||
- golangci-lint run
|
||||
environment:
|
||||
GOPROXY: https://goproxy.cn,direct
|
||||
|
||||
- name: test
|
||||
pull: always
|
||||
image: golang:1.13
|
||||
3
vendor/goftp.io/server/.gitignore → vendor/goftp.io/server/v2/.gitignore
generated
vendored
3
vendor/goftp.io/server/.gitignore → vendor/goftp.io/server/v2/.gitignore
generated
vendored
@@ -1,4 +1,5 @@
|
||||
testdata
|
||||
coverage.txt
|
||||
exampleftpd/exampleftpd
|
||||
.vscode
|
||||
.vscode
|
||||
*~
|
||||
0
vendor/goftp.io/server/LICENSE → vendor/goftp.io/server/v2/LICENSE
generated
vendored
0
vendor/goftp.io/server/LICENSE → vendor/goftp.io/server/v2/LICENSE
generated
vendored
24
vendor/goftp.io/server/README.md → vendor/goftp.io/server/v2/README.md
generated
vendored
24
vendor/goftp.io/server/README.md → vendor/goftp.io/server/v2/README.md
generated
vendored
@@ -5,33 +5,25 @@
|
||||
|
||||
A FTP server framework forked from [github.com/yob/graval](http://github.com/yob/graval) and changed a lot.
|
||||
|
||||
Full documentation for the package is available on [godoc](http://godoc.org/goftp.io/server)
|
||||
|
||||
## Version
|
||||
|
||||
v0.2.3
|
||||
Full documentation for the package is available on [godoc](http://pkg.go.dev/goftp.io/server)
|
||||
|
||||
## Installation
|
||||
|
||||
go get goftp.io/server
|
||||
go get goftp.io/server/v1
|
||||
|
||||
## Usage
|
||||
|
||||
To boot a FTP server you will need to provide a driver that speaks to
|
||||
your persistence layer - the required driver contract is in [the
|
||||
documentation](http://godoc.org/goftp.io/server).
|
||||
documentation](http://pkg.go.dev/goftp.io/server).
|
||||
|
||||
Look at the [file driver](https://gitea.com/goftp/file-driver) to see
|
||||
Look at the [file driver](https://goftp.io/server/driver/file) to see
|
||||
an example of how to build a backend.
|
||||
|
||||
There is a [sample ftp server](/exampleftpd) as a demo. You can build it with this
|
||||
There is a [sample ftp server](https://goftp.io/ftpd) as a demo. You can build it with this
|
||||
command:
|
||||
|
||||
go install goftp.io/server/exampleftpd
|
||||
|
||||
Then run it if you have add $GOPATH to your $PATH:
|
||||
|
||||
exampleftpd -root /tmp
|
||||
go install goftp.io/ftpd
|
||||
|
||||
And finally, connect to the server with any FTP client and the following
|
||||
details:
|
||||
@@ -43,6 +35,10 @@ details:
|
||||
|
||||
This uses the file driver mentioned above to serve files.
|
||||
|
||||
## Contact us
|
||||
|
||||
You can contact us via discord [https://discord.gg/ytmYqfNfqh](https://discord.gg/ytmYqfNfqh) or QQ群 972357369
|
||||
|
||||
## Contributors
|
||||
|
||||
see [https://gitea.com/goftp/server/graphs/contributors](https://gitea.com/goftp/server/graphs/contributors)
|
||||
4
vendor/goftp.io/server/auth.go → vendor/goftp.io/server/v2/auth.go
generated
vendored
4
vendor/goftp.io/server/auth.go → vendor/goftp.io/server/v2/auth.go
generated
vendored
@@ -10,7 +10,7 @@ import (
|
||||
|
||||
// Auth is an interface to auth your ftp user login.
|
||||
type Auth interface {
|
||||
CheckPasswd(string, string) (bool, error)
|
||||
CheckPasswd(*Context, string, string) (bool, error)
|
||||
}
|
||||
|
||||
var (
|
||||
@@ -24,7 +24,7 @@ type SimpleAuth struct {
|
||||
}
|
||||
|
||||
// CheckPasswd will check user's password
|
||||
func (a *SimpleAuth) CheckPasswd(name, pass string) (bool, error) {
|
||||
func (a *SimpleAuth) CheckPasswd(ctx *Context, name, pass string) (bool, error) {
|
||||
return constantTimeEquals(name, a.Name) && constantTimeEquals(pass, a.Password), nil
|
||||
}
|
||||
|
||||
819
vendor/goftp.io/server/cmd.go → vendor/goftp.io/server/v2/cmd.go
generated
vendored
819
vendor/goftp.io/server/cmd.go → vendor/goftp.io/server/v2/cmd.go
generated
vendored
File diff suppressed because it is too large
Load Diff
12
vendor/goftp.io/server/v2/context.go
generated
vendored
Normal file
12
vendor/goftp.io/server/v2/context.go
generated
vendored
Normal file
@@ -0,0 +1,12 @@
|
||||
// Copyright 2020 The goftp Authors. All rights reserved.
|
||||
// Use of this source code is governed by a MIT-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package server
|
||||
|
||||
// Context represents a context the driver may want to know
|
||||
type Context struct {
|
||||
Sess *Session
|
||||
Cmd string // request command on this request
|
||||
Param string // request param on this request
|
||||
}
|
||||
125
vendor/goftp.io/server/socket.go → vendor/goftp.io/server/v2/data_socket.go
generated
vendored
125
vendor/goftp.io/server/socket.go → vendor/goftp.io/server/v2/data_socket.go
generated
vendored
@@ -15,6 +15,8 @@ import (
|
||||
"sync"
|
||||
"syscall"
|
||||
"time"
|
||||
|
||||
"goftp.io/server/v2/ratelimit"
|
||||
)
|
||||
|
||||
// DataSocket describes a data socket is used to send non-control data between the client and
|
||||
@@ -37,75 +39,80 @@ type DataSocket interface {
|
||||
Close() error
|
||||
}
|
||||
|
||||
type ftpActiveSocket struct {
|
||||
conn *net.TCPConn
|
||||
host string
|
||||
port int
|
||||
logger Logger
|
||||
type activeSocket struct {
|
||||
conn *net.TCPConn
|
||||
reader io.Reader
|
||||
writer io.Writer
|
||||
sess *Session
|
||||
host string
|
||||
port int
|
||||
}
|
||||
|
||||
func newActiveSocket(remote string, port int, logger Logger, sessionID string) (DataSocket, error) {
|
||||
func newActiveSocket(sess *Session, remote string, port int) (DataSocket, error) {
|
||||
connectTo := net.JoinHostPort(remote, strconv.Itoa(port))
|
||||
|
||||
logger.Print(sessionID, "Opening active data connection to "+connectTo)
|
||||
sess.log("Opening active data connection to " + connectTo)
|
||||
|
||||
raddr, err := net.ResolveTCPAddr("tcp", connectTo)
|
||||
|
||||
if err != nil {
|
||||
logger.Print(sessionID, err)
|
||||
sess.log(err)
|
||||
return nil, err
|
||||
}
|
||||
|
||||
tcpConn, err := net.DialTCP("tcp", nil, raddr)
|
||||
|
||||
if err != nil {
|
||||
logger.Print(sessionID, err)
|
||||
sess.log(err)
|
||||
return nil, err
|
||||
}
|
||||
|
||||
socket := new(ftpActiveSocket)
|
||||
socket := new(activeSocket)
|
||||
socket.sess = sess
|
||||
socket.conn = tcpConn
|
||||
socket.reader = ratelimit.Reader(tcpConn, sess.server.rateLimiter)
|
||||
socket.writer = ratelimit.Writer(tcpConn, sess.server.rateLimiter)
|
||||
socket.host = remote
|
||||
socket.port = port
|
||||
socket.logger = logger
|
||||
|
||||
return socket, nil
|
||||
}
|
||||
|
||||
func (socket *ftpActiveSocket) Host() string {
|
||||
func (socket *activeSocket) Host() string {
|
||||
return socket.host
|
||||
}
|
||||
|
||||
func (socket *ftpActiveSocket) Port() int {
|
||||
func (socket *activeSocket) Port() int {
|
||||
return socket.port
|
||||
}
|
||||
|
||||
func (socket *ftpActiveSocket) Read(p []byte) (n int, err error) {
|
||||
return socket.conn.Read(p)
|
||||
func (socket *activeSocket) Read(p []byte) (n int, err error) {
|
||||
return socket.reader.Read(p)
|
||||
}
|
||||
|
||||
func (socket *ftpActiveSocket) ReadFrom(r io.Reader) (int64, error) {
|
||||
return socket.conn.ReadFrom(r)
|
||||
func (socket *activeSocket) ReadFrom(r io.Reader) (int64, error) {
|
||||
return io.Copy(socket.writer, r)
|
||||
}
|
||||
|
||||
func (socket *ftpActiveSocket) Write(p []byte) (n int, err error) {
|
||||
return socket.conn.Write(p)
|
||||
func (socket *activeSocket) Write(p []byte) (n int, err error) {
|
||||
return socket.writer.Write(p)
|
||||
}
|
||||
|
||||
func (socket *ftpActiveSocket) Close() error {
|
||||
func (socket *activeSocket) Close() error {
|
||||
return socket.conn.Close()
|
||||
}
|
||||
|
||||
type ftpPassiveSocket struct {
|
||||
conn net.Conn
|
||||
port int
|
||||
host string
|
||||
ingress chan []byte
|
||||
egress chan []byte
|
||||
logger Logger
|
||||
lock sync.Mutex // protects conn and err
|
||||
err error
|
||||
tlsConfig *tls.Config
|
||||
type passiveSocket struct {
|
||||
sess *Session
|
||||
conn net.Conn
|
||||
reader io.Reader
|
||||
writer io.Writer
|
||||
port int
|
||||
host string
|
||||
ingress chan []byte
|
||||
egress chan []byte
|
||||
lock sync.Mutex // protects conn and err
|
||||
err error
|
||||
}
|
||||
|
||||
// Detect if an error is "bind: address already in use"
|
||||
@@ -134,46 +141,46 @@ func isErrorAddressAlreadyInUse(err error) bool {
|
||||
return false
|
||||
}
|
||||
|
||||
func (conn *Conn) newPassiveSocket() (DataSocket, error) {
|
||||
socket := new(ftpPassiveSocket)
|
||||
func (sess *Session) newPassiveSocket() (DataSocket, error) {
|
||||
socket := new(passiveSocket)
|
||||
socket.ingress = make(chan []byte)
|
||||
socket.egress = make(chan []byte)
|
||||
socket.logger = conn.logger
|
||||
socket.host = conn.passiveListenIP()
|
||||
socket.tlsConfig = conn.tlsConfig
|
||||
socket.sess = sess
|
||||
socket.host = sess.passiveListenIP()
|
||||
|
||||
const retries = 10
|
||||
var err error
|
||||
for i := 1; i <= retries; i++ {
|
||||
socket.port = conn.PassivePort()
|
||||
err = socket.GoListenAndServe(conn.sessionID)
|
||||
socket.port = sess.PassivePort()
|
||||
err = socket.ListenAndServe()
|
||||
if err != nil && socket.port != 0 && isErrorAddressAlreadyInUse(err) {
|
||||
// choose a different port on error already in use
|
||||
continue
|
||||
}
|
||||
break
|
||||
}
|
||||
conn.dataConn = socket
|
||||
sess.dataConn = socket
|
||||
return socket, err
|
||||
}
|
||||
|
||||
func (socket *ftpPassiveSocket) Host() string {
|
||||
func (socket *passiveSocket) Host() string {
|
||||
return socket.host
|
||||
}
|
||||
|
||||
func (socket *ftpPassiveSocket) Port() int {
|
||||
func (socket *passiveSocket) Port() int {
|
||||
return socket.port
|
||||
}
|
||||
|
||||
func (socket *ftpPassiveSocket) Read(p []byte) (n int, err error) {
|
||||
func (socket *passiveSocket) Read(p []byte) (n int, err error) {
|
||||
socket.lock.Lock()
|
||||
defer socket.lock.Unlock()
|
||||
if socket.err != nil {
|
||||
return 0, socket.err
|
||||
}
|
||||
return socket.conn.Read(p)
|
||||
return socket.reader.Read(p)
|
||||
}
|
||||
|
||||
func (socket *ftpPassiveSocket) ReadFrom(r io.Reader) (int64, error) {
|
||||
func (socket *passiveSocket) ReadFrom(r io.Reader) (int64, error) {
|
||||
socket.lock.Lock()
|
||||
defer socket.lock.Unlock()
|
||||
if socket.err != nil {
|
||||
@@ -182,19 +189,19 @@ func (socket *ftpPassiveSocket) ReadFrom(r io.Reader) (int64, error) {
|
||||
|
||||
// For normal TCPConn, this will use sendfile syscall; if not,
|
||||
// it will just downgrade to normal read/write procedure
|
||||
return io.Copy(socket.conn, r)
|
||||
return io.Copy(socket.writer, r)
|
||||
}
|
||||
|
||||
func (socket *ftpPassiveSocket) Write(p []byte) (n int, err error) {
|
||||
func (socket *passiveSocket) Write(p []byte) (n int, err error) {
|
||||
socket.lock.Lock()
|
||||
defer socket.lock.Unlock()
|
||||
if socket.err != nil {
|
||||
return 0, socket.err
|
||||
}
|
||||
return socket.conn.Write(p)
|
||||
return socket.writer.Write(p)
|
||||
}
|
||||
|
||||
func (socket *ftpPassiveSocket) Close() error {
|
||||
func (socket *passiveSocket) Close() error {
|
||||
socket.lock.Lock()
|
||||
defer socket.lock.Unlock()
|
||||
if socket.conn != nil {
|
||||
@@ -203,18 +210,18 @@ func (socket *ftpPassiveSocket) Close() error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (socket *ftpPassiveSocket) GoListenAndServe(sessionID string) (err error) {
|
||||
func (socket *passiveSocket) ListenAndServe() (err error) {
|
||||
laddr, err := net.ResolveTCPAddr("tcp", net.JoinHostPort("", strconv.Itoa(socket.port)))
|
||||
if err != nil {
|
||||
socket.logger.Print(sessionID, err)
|
||||
return
|
||||
socket.sess.log(err)
|
||||
return err
|
||||
}
|
||||
|
||||
var tcplistener *net.TCPListener
|
||||
tcplistener, err = net.ListenTCP("tcp", laddr)
|
||||
if err != nil {
|
||||
socket.logger.Print(sessionID, err)
|
||||
return
|
||||
socket.sess.log(err)
|
||||
return err
|
||||
}
|
||||
|
||||
// The timeout, for a remote client to establish connection
|
||||
@@ -222,8 +229,8 @@ func (socket *ftpPassiveSocket) GoListenAndServe(sessionID string) (err error) {
|
||||
const acceptTimeout = 60 * time.Second
|
||||
err = tcplistener.SetDeadline(time.Now().Add(acceptTimeout))
|
||||
if err != nil {
|
||||
socket.logger.Print(sessionID, err)
|
||||
return
|
||||
socket.sess.log(err)
|
||||
return err
|
||||
}
|
||||
|
||||
var listener net.Listener = tcplistener
|
||||
@@ -231,13 +238,13 @@ func (socket *ftpPassiveSocket) GoListenAndServe(sessionID string) (err error) {
|
||||
parts := strings.Split(add.String(), ":")
|
||||
port, err := strconv.Atoi(parts[len(parts)-1])
|
||||
if err != nil {
|
||||
socket.logger.Print(sessionID, err)
|
||||
return
|
||||
socket.sess.log(err)
|
||||
return err
|
||||
}
|
||||
|
||||
socket.port = port
|
||||
if socket.tlsConfig != nil {
|
||||
listener = tls.NewListener(listener, socket.tlsConfig)
|
||||
if socket.sess.server.tlsConfig != nil {
|
||||
listener = tls.NewListener(listener, socket.sess.server.tlsConfig)
|
||||
}
|
||||
|
||||
socket.lock.Lock()
|
||||
@@ -251,6 +258,8 @@ func (socket *ftpPassiveSocket) GoListenAndServe(sessionID string) (err error) {
|
||||
}
|
||||
socket.err = nil
|
||||
socket.conn = conn
|
||||
socket.reader = ratelimit.Reader(socket.conn, socket.sess.server.rateLimiter)
|
||||
socket.writer = ratelimit.Writer(socket.conn, socket.sess.server.rateLimiter)
|
||||
_ = listener.Close()
|
||||
}()
|
||||
return nil
|
||||
0
vendor/goftp.io/server/doc.go → vendor/goftp.io/server/v2/doc.go
generated
vendored
0
vendor/goftp.io/server/doc.go → vendor/goftp.io/server/v2/doc.go
generated
vendored
162
vendor/goftp.io/server/v2/driver.go
generated
vendored
Normal file
162
vendor/goftp.io/server/v2/driver.go
generated
vendored
Normal file
@@ -0,0 +1,162 @@
|
||||
// Copyright 2018 The goftp Authors. All rights reserved.
|
||||
// Use of this source code is governed by a MIT-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package server
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"io"
|
||||
"os"
|
||||
"strings"
|
||||
)
|
||||
|
||||
// FileInfo represents an file interface
|
||||
type FileInfo interface {
|
||||
os.FileInfo
|
||||
|
||||
Owner() string
|
||||
Group() string
|
||||
}
|
||||
|
||||
// Driver is an interface that you will implement to create a driver for your
|
||||
// chosen persistence layer. The server will create a new instance of your
|
||||
// driver for each client that connects and delegate to it as required.
|
||||
//
|
||||
// Note that if the driver also implements the Auth interface then
|
||||
// this will be called instead of calling Options.Auth. This allows
|
||||
// the Auth mechanism to change the driver configuration.
|
||||
type Driver interface {
|
||||
// params - a file path
|
||||
// returns - a time indicating when the requested path was last modified
|
||||
// - an error if the file doesn't exist or the user lacks
|
||||
// permissions
|
||||
Stat(*Context, string) (os.FileInfo, error)
|
||||
|
||||
// params - path, function on file or subdir found
|
||||
// returns - error
|
||||
// path
|
||||
ListDir(*Context, string, func(os.FileInfo) error) error
|
||||
|
||||
// params - path
|
||||
// returns - nil if the directory was deleted or any error encountered
|
||||
DeleteDir(*Context, string) error
|
||||
|
||||
// params - path
|
||||
// returns - nil if the file was deleted or any error encountered
|
||||
DeleteFile(*Context, string) error
|
||||
|
||||
// params - from_path, to_path
|
||||
// returns - nil if the file was renamed or any error encountered
|
||||
Rename(*Context, string, string) error
|
||||
|
||||
// params - path
|
||||
// returns - nil if the new directory was created or any error encountered
|
||||
MakeDir(*Context, string) error
|
||||
|
||||
// params - path, filepos
|
||||
// returns - a string containing the file data to send to the client
|
||||
GetFile(*Context, string, int64) (int64, io.ReadCloser, error)
|
||||
|
||||
// params - destination path, an io.Reader containing the file data
|
||||
// returns - the number of bytes written and the first error encountered while writing, if any.
|
||||
PutFile(*Context, string, io.Reader, int64) (int64, error)
|
||||
}
|
||||
|
||||
var _ Driver = &MultiDriver{}
|
||||
|
||||
// MultiDriver represents a composite driver
|
||||
type MultiDriver struct {
|
||||
drivers map[string]Driver
|
||||
}
|
||||
|
||||
// NewMultiDriver creates a multi driver to combind multiple driver
|
||||
func NewMultiDriver(drivers map[string]Driver) Driver {
|
||||
return &MultiDriver{
|
||||
drivers: drivers,
|
||||
}
|
||||
}
|
||||
|
||||
// Stat implements Driver
|
||||
func (driver *MultiDriver) Stat(ctx *Context, path string) (os.FileInfo, error) {
|
||||
for prefix, driver := range driver.drivers {
|
||||
if strings.HasPrefix(path, prefix) {
|
||||
return driver.Stat(ctx, strings.TrimPrefix(path, prefix))
|
||||
}
|
||||
}
|
||||
return nil, errors.New("Not a file")
|
||||
}
|
||||
|
||||
// ListDir implements Driver
|
||||
func (driver *MultiDriver) ListDir(ctx *Context, path string, callback func(os.FileInfo) error) error {
|
||||
for prefix, driver := range driver.drivers {
|
||||
if strings.HasPrefix(path, prefix) {
|
||||
return driver.ListDir(ctx, strings.TrimPrefix(path, prefix), callback)
|
||||
}
|
||||
}
|
||||
return errors.New("Not a directory")
|
||||
}
|
||||
|
||||
// DeleteDir implements Driver
|
||||
func (driver *MultiDriver) DeleteDir(ctx *Context, path string) error {
|
||||
for prefix, driver := range driver.drivers {
|
||||
if strings.HasPrefix(path, prefix) {
|
||||
return driver.DeleteDir(ctx, strings.TrimPrefix(path, prefix))
|
||||
}
|
||||
}
|
||||
return errors.New("Not a directory")
|
||||
}
|
||||
|
||||
// DeleteFile implements Driver
|
||||
func (driver *MultiDriver) DeleteFile(ctx *Context, path string) error {
|
||||
for prefix, driver := range driver.drivers {
|
||||
if strings.HasPrefix(path, prefix) {
|
||||
return driver.DeleteFile(ctx, strings.TrimPrefix(path, prefix))
|
||||
}
|
||||
}
|
||||
|
||||
return errors.New("Not a file")
|
||||
}
|
||||
|
||||
// Rename implements Driver
|
||||
func (driver *MultiDriver) Rename(ctx *Context, fromPath string, toPath string) error {
|
||||
for prefix, driver := range driver.drivers {
|
||||
if strings.HasPrefix(fromPath, prefix) {
|
||||
return driver.Rename(ctx, strings.TrimPrefix(fromPath, prefix), strings.TrimPrefix(toPath, prefix))
|
||||
}
|
||||
}
|
||||
|
||||
return errors.New("Not a file")
|
||||
}
|
||||
|
||||
// MakeDir implements Driver
|
||||
func (driver *MultiDriver) MakeDir(ctx *Context, path string) error {
|
||||
for prefix, driver := range driver.drivers {
|
||||
if strings.HasPrefix(path, prefix) {
|
||||
return driver.MakeDir(ctx, strings.TrimPrefix(path, prefix))
|
||||
}
|
||||
}
|
||||
return errors.New("Not a directory")
|
||||
}
|
||||
|
||||
// GetFile implements Driver
|
||||
func (driver *MultiDriver) GetFile(ctx *Context, path string, offset int64) (int64, io.ReadCloser, error) {
|
||||
for prefix, driver := range driver.drivers {
|
||||
if strings.HasPrefix(path, prefix) {
|
||||
return driver.GetFile(ctx, strings.TrimPrefix(path, prefix), offset)
|
||||
}
|
||||
}
|
||||
|
||||
return 0, nil, errors.New("Not a file")
|
||||
}
|
||||
|
||||
// PutFile implements Driver
|
||||
func (driver *MultiDriver) PutFile(ctx *Context, destPath string, data io.Reader, offset int64) (int64, error) {
|
||||
for prefix, driver := range driver.drivers {
|
||||
if strings.HasPrefix(destPath, prefix) {
|
||||
return driver.PutFile(ctx, strings.TrimPrefix(destPath, prefix), data, offset)
|
||||
}
|
||||
}
|
||||
|
||||
return 0, errors.New("Not a file")
|
||||
}
|
||||
117
vendor/goftp.io/server/driver_file.go → vendor/goftp.io/server/v2/driver/file/file.go
generated
vendored
117
vendor/goftp.io/server/driver_file.go → vendor/goftp.io/server/v2/driver/file/file.go
generated
vendored
@@ -2,7 +2,7 @@
|
||||
// Use of this source code is governed by a MIT-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package server
|
||||
package file
|
||||
|
||||
import (
|
||||
"errors"
|
||||
@@ -11,50 +11,42 @@ import (
|
||||
"os"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
|
||||
"goftp.io/server/v2"
|
||||
)
|
||||
|
||||
// FileDriver implements Driver directly read local file system
|
||||
type FileDriver struct {
|
||||
// Driver implements Driver directly read local file system
|
||||
type Driver struct {
|
||||
RootPath string
|
||||
Perm
|
||||
}
|
||||
|
||||
func (driver *FileDriver) realPath(path string) string {
|
||||
// NewDriver implements Driver
|
||||
func NewDriver(rootPath string) (server.Driver, error) {
|
||||
var err error
|
||||
rootPath, err = filepath.Abs(rootPath)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &Driver{rootPath}, nil
|
||||
}
|
||||
|
||||
func (driver *Driver) realPath(path string) string {
|
||||
paths := strings.Split(path, "/")
|
||||
return filepath.Join(append([]string{driver.RootPath}, paths...)...)
|
||||
}
|
||||
|
||||
// Stat implements Driver
|
||||
func (driver *FileDriver) Stat(path string) (FileInfo, error) {
|
||||
func (driver *Driver) Stat(ctx *server.Context, path string) (os.FileInfo, error) {
|
||||
basepath := driver.realPath(path)
|
||||
rPath, err := filepath.Abs(basepath)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
f, err := os.Lstat(rPath)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
mode, err := driver.Perm.GetMode(path)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if f.IsDir() {
|
||||
mode |= os.ModeDir
|
||||
}
|
||||
owner, err := driver.Perm.GetOwner(path)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
group, err := driver.Perm.GetGroup(path)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &fileInfo{f, mode, owner, group}, nil
|
||||
return os.Lstat(rPath)
|
||||
}
|
||||
|
||||
// ListDir implements Driver
|
||||
func (driver *FileDriver) ListDir(path string, callback func(FileInfo) error) error {
|
||||
func (driver *Driver) ListDir(ctx *server.Context, path string, callback func(os.FileInfo) error) error {
|
||||
basepath := driver.realPath(path)
|
||||
return filepath.Walk(basepath, func(f string, info os.FileInfo, err error) error {
|
||||
if err != nil {
|
||||
@@ -62,22 +54,7 @@ func (driver *FileDriver) ListDir(path string, callback func(FileInfo) error) er
|
||||
}
|
||||
rPath, _ := filepath.Rel(basepath, f)
|
||||
if rPath == info.Name() {
|
||||
mode, err := driver.Perm.GetMode(rPath)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if info.IsDir() {
|
||||
mode |= os.ModeDir
|
||||
}
|
||||
owner, err := driver.Perm.GetOwner(rPath)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
group, err := driver.Perm.GetGroup(rPath)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
err = callback(&fileInfo{info, mode, owner, group})
|
||||
err = callback(info)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -90,7 +67,7 @@ func (driver *FileDriver) ListDir(path string, callback func(FileInfo) error) er
|
||||
}
|
||||
|
||||
// DeleteDir implements Driver
|
||||
func (driver *FileDriver) DeleteDir(path string) error {
|
||||
func (driver *Driver) DeleteDir(ctx *server.Context, path string) error {
|
||||
rPath := driver.realPath(path)
|
||||
f, err := os.Lstat(rPath)
|
||||
if err != nil {
|
||||
@@ -103,7 +80,7 @@ func (driver *FileDriver) DeleteDir(path string) error {
|
||||
}
|
||||
|
||||
// DeleteFile implements Driver
|
||||
func (driver *FileDriver) DeleteFile(path string) error {
|
||||
func (driver *Driver) DeleteFile(ctx *server.Context, path string) error {
|
||||
rPath := driver.realPath(path)
|
||||
f, err := os.Lstat(rPath)
|
||||
if err != nil {
|
||||
@@ -116,38 +93,46 @@ func (driver *FileDriver) DeleteFile(path string) error {
|
||||
}
|
||||
|
||||
// Rename implements Driver
|
||||
func (driver *FileDriver) Rename(fromPath string, toPath string) error {
|
||||
func (driver *Driver) Rename(ctx *server.Context, fromPath string, toPath string) error {
|
||||
oldPath := driver.realPath(fromPath)
|
||||
newPath := driver.realPath(toPath)
|
||||
return os.Rename(oldPath, newPath)
|
||||
}
|
||||
|
||||
// MakeDir implements Driver
|
||||
func (driver *FileDriver) MakeDir(path string) error {
|
||||
func (driver *Driver) MakeDir(ctx *server.Context, path string) error {
|
||||
rPath := driver.realPath(path)
|
||||
return os.MkdirAll(rPath, os.ModePerm)
|
||||
}
|
||||
|
||||
// GetFile implements Driver
|
||||
func (driver *FileDriver) GetFile(path string, offset int64) (int64, io.ReadCloser, error) {
|
||||
func (driver *Driver) GetFile(ctx *server.Context, path string, offset int64) (int64, io.ReadCloser, error) {
|
||||
rPath := driver.realPath(path)
|
||||
f, err := os.Open(rPath)
|
||||
if err != nil {
|
||||
return 0, nil, err
|
||||
}
|
||||
defer func() {
|
||||
if err != nil && f != nil {
|
||||
f.Close()
|
||||
}
|
||||
}()
|
||||
|
||||
info, err := f.Stat()
|
||||
if err != nil {
|
||||
return 0, nil, err
|
||||
}
|
||||
|
||||
f.Seek(offset, io.SeekStart)
|
||||
_, err = f.Seek(offset, io.SeekStart)
|
||||
if err != nil {
|
||||
return 0, nil, err
|
||||
}
|
||||
|
||||
return info.Size() - offset, f, nil
|
||||
}
|
||||
|
||||
// PutFile implements Driver
|
||||
func (driver *FileDriver) PutFile(destPath string, data io.Reader, appendData bool) (int64, error) {
|
||||
func (driver *Driver) PutFile(ctx *server.Context, destPath string, data io.Reader, offset int64) (int64, error) {
|
||||
rPath := driver.realPath(destPath)
|
||||
var isExist bool
|
||||
f, err := os.Lstat(rPath)
|
||||
@@ -164,11 +149,11 @@ func (driver *FileDriver) PutFile(destPath string, data io.Reader, appendData bo
|
||||
}
|
||||
}
|
||||
|
||||
if appendData && !isExist {
|
||||
appendData = false
|
||||
if offset > -1 && !isExist {
|
||||
offset = -1
|
||||
}
|
||||
|
||||
if !appendData {
|
||||
if offset == -1 {
|
||||
if isExist {
|
||||
err = os.Remove(rPath)
|
||||
if err != nil {
|
||||
@@ -193,7 +178,15 @@ func (driver *FileDriver) PutFile(destPath string, data io.Reader, appendData bo
|
||||
}
|
||||
defer of.Close()
|
||||
|
||||
_, err = of.Seek(0, os.SEEK_END)
|
||||
info, err := of.Stat()
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
if offset > info.Size() {
|
||||
return 0, fmt.Errorf("Offset %d is beyond file size %d", offset, info.Size())
|
||||
}
|
||||
|
||||
_, err = of.Seek(offset, os.SEEK_END)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
@@ -205,19 +198,3 @@ func (driver *FileDriver) PutFile(destPath string, data io.Reader, appendData bo
|
||||
|
||||
return bytes, nil
|
||||
}
|
||||
|
||||
// FileDriverFactory implements DriverFactory
|
||||
type FileDriverFactory struct {
|
||||
RootPath string
|
||||
Perm
|
||||
}
|
||||
|
||||
// NewDriver implements DriverFactory
|
||||
func (factory *FileDriverFactory) NewDriver() (Driver, error) {
|
||||
var err error
|
||||
factory.RootPath, err = filepath.Abs(factory.RootPath)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &FileDriver{factory.RootPath, factory.Perm}, nil
|
||||
}
|
||||
181
vendor/goftp.io/server/driver_minio.go → vendor/goftp.io/server/v2/driver/minio/minio.go
generated
vendored
181
vendor/goftp.io/server/driver_minio.go → vendor/goftp.io/server/v2/driver/minio/minio.go
generated
vendored
@@ -2,10 +2,11 @@
|
||||
// Use of this source code is governed by a MIT-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package server
|
||||
package minio
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"io"
|
||||
"log"
|
||||
"os"
|
||||
@@ -13,19 +14,41 @@ import (
|
||||
"time"
|
||||
|
||||
minio "github.com/minio/minio-go/v6"
|
||||
"goftp.io/server/v2"
|
||||
)
|
||||
|
||||
var (
|
||||
_ Driver = &MinioDriver{}
|
||||
_ server.Driver = &Driver{}
|
||||
)
|
||||
|
||||
// MinioDriver implements Driver to store files in minio
|
||||
type MinioDriver struct {
|
||||
// Driver implements Driver to store files in minio
|
||||
type Driver struct {
|
||||
client *minio.Client
|
||||
perm Perm
|
||||
bucket string
|
||||
}
|
||||
|
||||
// NewDriver implements DriverFactory
|
||||
func NewDriver(endpoint, accessKeyID, secretAccessKey, location, bucket string, useSSL bool) (server.Driver, error) {
|
||||
// Initialize minio client object.
|
||||
minioClient, err := minio.New(endpoint, accessKeyID, secretAccessKey, useSSL)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if err = minioClient.MakeBucket(bucket, location); err != nil {
|
||||
// Check to see if we already own this bucket (which happens if you run this twice)
|
||||
exists, errBucketExists := minioClient.BucketExists(bucket)
|
||||
if !exists || errBucketExists != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
|
||||
return &Driver{
|
||||
client: minioClient,
|
||||
bucket: bucket,
|
||||
}, nil
|
||||
}
|
||||
|
||||
func buildMinioPath(p string) string {
|
||||
return strings.TrimPrefix(p, "/")
|
||||
}
|
||||
@@ -38,26 +61,10 @@ func buildMinioDir(p string) string {
|
||||
return v
|
||||
}
|
||||
|
||||
type myPerm struct {
|
||||
Perm
|
||||
isDir bool
|
||||
}
|
||||
|
||||
func (m *myPerm) GetMode(user string) (os.FileMode, error) {
|
||||
mode, err := m.Perm.GetMode(user)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
if m.isDir {
|
||||
return mode | os.ModeDir, nil
|
||||
}
|
||||
return mode, nil
|
||||
}
|
||||
|
||||
type minioFileInfo struct {
|
||||
p string
|
||||
info minio.ObjectInfo
|
||||
perm Perm
|
||||
p string
|
||||
info minio.ObjectInfo
|
||||
isDir bool
|
||||
}
|
||||
|
||||
func (m *minioFileInfo) Name() string {
|
||||
@@ -69,8 +76,7 @@ func (m *minioFileInfo) Size() int64 {
|
||||
}
|
||||
|
||||
func (m *minioFileInfo) Mode() os.FileMode {
|
||||
mode, _ := m.perm.GetMode(m.p)
|
||||
return mode
|
||||
return os.ModePerm
|
||||
}
|
||||
|
||||
func (m *minioFileInfo) ModTime() time.Time {
|
||||
@@ -78,24 +84,14 @@ func (m *minioFileInfo) ModTime() time.Time {
|
||||
}
|
||||
|
||||
func (m *minioFileInfo) IsDir() bool {
|
||||
return m.Mode().IsDir()
|
||||
return m.isDir
|
||||
}
|
||||
|
||||
func (m *minioFileInfo) Sys() interface{} {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (m *minioFileInfo) Owner() string {
|
||||
owner, _ := m.perm.GetOwner(m.p)
|
||||
return owner
|
||||
}
|
||||
|
||||
func (m *minioFileInfo) Group() string {
|
||||
group, _ := m.perm.GetGroup(m.p)
|
||||
return group
|
||||
}
|
||||
|
||||
func (driver *MinioDriver) isDir(path string) (bool, error) {
|
||||
func (driver *Driver) isDir(path string) (bool, error) {
|
||||
p := buildMinioDir(path)
|
||||
|
||||
info, err := driver.client.StatObject(driver.bucket, p, minio.StatObjectOptions{})
|
||||
@@ -117,11 +113,11 @@ func (driver *MinioDriver) isDir(path string) (bool, error) {
|
||||
}
|
||||
|
||||
// Stat implements Driver
|
||||
func (driver *MinioDriver) Stat(path string) (FileInfo, error) {
|
||||
func (driver *Driver) Stat(ctx *server.Context, path string) (os.FileInfo, error) {
|
||||
if path == "/" {
|
||||
return &minioFileInfo{
|
||||
p: "/",
|
||||
perm: &myPerm{driver.perm, true},
|
||||
p: "/",
|
||||
isDir: true,
|
||||
}, nil
|
||||
}
|
||||
|
||||
@@ -132,22 +128,22 @@ func (driver *MinioDriver) Stat(path string) (FileInfo, error) {
|
||||
return nil, err
|
||||
} else if isDir {
|
||||
return &minioFileInfo{
|
||||
p: path,
|
||||
perm: &myPerm{driver.perm, true},
|
||||
p: path,
|
||||
isDir: true,
|
||||
}, nil
|
||||
}
|
||||
return nil, errors.New("Not a directory")
|
||||
}
|
||||
isDir := strings.HasSuffix(objInfo.Key, "/")
|
||||
return &minioFileInfo{
|
||||
p: p,
|
||||
info: objInfo,
|
||||
perm: &myPerm{driver.perm, isDir},
|
||||
p: p,
|
||||
info: objInfo,
|
||||
isDir: isDir,
|
||||
}, nil
|
||||
}
|
||||
|
||||
// ListDir implements Driver
|
||||
func (driver *MinioDriver) ListDir(path string, callback func(FileInfo) error) error {
|
||||
func (driver *Driver) ListDir(ctx *server.Context, path string, callback func(os.FileInfo) error) error {
|
||||
doneCh := make(chan struct{})
|
||||
defer close(doneCh)
|
||||
|
||||
@@ -168,9 +164,9 @@ func (driver *MinioDriver) ListDir(path string, callback func(FileInfo) error) e
|
||||
|
||||
isDir := strings.HasSuffix(object.Key, "/")
|
||||
info := minioFileInfo{
|
||||
p: strings.TrimPrefix(object.Key, p),
|
||||
info: object,
|
||||
perm: &myPerm{driver.perm, isDir},
|
||||
p: strings.TrimPrefix(object.Key, p),
|
||||
info: object,
|
||||
isDir: isDir,
|
||||
}
|
||||
|
||||
if err := callback(&info); err != nil {
|
||||
@@ -182,7 +178,7 @@ func (driver *MinioDriver) ListDir(path string, callback func(FileInfo) error) e
|
||||
}
|
||||
|
||||
// DeleteDir implements Driver
|
||||
func (driver *MinioDriver) DeleteDir(path string) error {
|
||||
func (driver *Driver) DeleteDir(ctx *server.Context, path string) error {
|
||||
doneCh := make(chan struct{})
|
||||
defer close(doneCh)
|
||||
|
||||
@@ -201,12 +197,12 @@ func (driver *MinioDriver) DeleteDir(path string) error {
|
||||
}
|
||||
|
||||
// DeleteFile implements Driver
|
||||
func (driver *MinioDriver) DeleteFile(path string) error {
|
||||
func (driver *Driver) DeleteFile(ctx *server.Context, path string) error {
|
||||
return driver.client.RemoveObject(driver.bucket, buildMinioPath(path))
|
||||
}
|
||||
|
||||
// Rename implements Driver
|
||||
func (driver *MinioDriver) Rename(fromPath string, toPath string) error {
|
||||
func (driver *Driver) Rename(ctx *server.Context, fromPath string, toPath string) error {
|
||||
src := minio.NewSourceInfo(driver.bucket, buildMinioPath(fromPath), nil)
|
||||
dst, err := minio.NewDestinationInfo(driver.bucket, buildMinioPath(toPath), nil, nil)
|
||||
if err != nil {
|
||||
@@ -221,20 +217,28 @@ func (driver *MinioDriver) Rename(fromPath string, toPath string) error {
|
||||
}
|
||||
|
||||
// MakeDir implements Driver
|
||||
func (driver *MinioDriver) MakeDir(path string) error {
|
||||
func (driver *Driver) MakeDir(ctx *server.Context, path string) error {
|
||||
dirPath := buildMinioDir(path)
|
||||
_, err := driver.client.PutObject(driver.bucket, dirPath, nil, 0, minio.PutObjectOptions{})
|
||||
return err
|
||||
}
|
||||
|
||||
// GetFile implements Driver
|
||||
func (driver *MinioDriver) GetFile(path string, offset int64) (int64, io.ReadCloser, error) {
|
||||
func (driver *Driver) GetFile(ctx *server.Context, path string, offset int64) (int64, io.ReadCloser, error) {
|
||||
var opts = minio.GetObjectOptions{}
|
||||
object, err := driver.client.GetObject(driver.bucket, buildMinioPath(path), opts)
|
||||
if err != nil {
|
||||
return 0, nil, err
|
||||
}
|
||||
object.Seek(offset, io.SeekStart)
|
||||
defer func() {
|
||||
if err != nil && object != nil {
|
||||
object.Close()
|
||||
}
|
||||
}()
|
||||
_, err = object.Seek(offset, io.SeekStart)
|
||||
if err != nil {
|
||||
return 0, nil, err
|
||||
}
|
||||
|
||||
info, err := object.Stat()
|
||||
if err != nil {
|
||||
@@ -245,23 +249,27 @@ func (driver *MinioDriver) GetFile(path string, offset int64) (int64, io.ReadClo
|
||||
}
|
||||
|
||||
// PutFile implements Driver
|
||||
func (driver *MinioDriver) PutFile(destPath string, data io.Reader, appendData bool) (int64, error) {
|
||||
func (driver *Driver) PutFile(ctx *server.Context, destPath string, data io.Reader, offset int64) (int64, error) {
|
||||
p := buildMinioPath(destPath)
|
||||
if !appendData {
|
||||
if offset == -1 {
|
||||
return driver.client.PutObject(driver.bucket, p, data, -1, minio.PutObjectOptions{ContentType: "application/octet-stream"})
|
||||
}
|
||||
|
||||
tempFile := p + ".tmp"
|
||||
//tempDstFile := p + ".dst"
|
||||
defer func() {
|
||||
if err := driver.DeleteFile(tempFile); err != nil {
|
||||
if err := driver.DeleteFile(ctx, tempFile); err != nil {
|
||||
log.Println(err)
|
||||
}
|
||||
/*if err := driver.DeleteFile(tempDstFile); err != nil {
|
||||
log.Println(err)
|
||||
}*/
|
||||
}()
|
||||
|
||||
info, err := driver.client.StatObject(driver.bucket, p, minio.StatObjectOptions{})
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
if offset != info.Size {
|
||||
return 0, fmt.Errorf("It's unsupported that offset %d is not equal to %d", offset, info.Size)
|
||||
}
|
||||
|
||||
size, err := driver.client.PutObject(driver.bucket, tempFile, data, -1, minio.PutObjectOptions{ContentType: "application/octet-stream"})
|
||||
if err != nil {
|
||||
return size, err
|
||||
@@ -278,50 +286,3 @@ func (driver *MinioDriver) PutFile(destPath string, data io.Reader, appendData b
|
||||
|
||||
return size, driver.client.ComposeObject(dst, srcs)
|
||||
}
|
||||
|
||||
// MinioDriverFactory implements DriverFactory
|
||||
type MinioDriverFactory struct {
|
||||
endpoint string
|
||||
accessKeyID string
|
||||
secretAccessKey string
|
||||
useSSL bool
|
||||
location string
|
||||
bucket string
|
||||
perm Perm
|
||||
}
|
||||
|
||||
// NewMinioDriverFactory creates a DriverFactory implementation
|
||||
func NewMinioDriverFactory(endpoint, accessKeyID, secretAccessKey, location, bucket string, useSSL bool, perm Perm) *MinioDriverFactory {
|
||||
return &MinioDriverFactory{
|
||||
endpoint: endpoint,
|
||||
accessKeyID: accessKeyID,
|
||||
secretAccessKey: secretAccessKey,
|
||||
useSSL: useSSL,
|
||||
location: location,
|
||||
bucket: bucket,
|
||||
perm: perm,
|
||||
}
|
||||
}
|
||||
|
||||
// NewDriver implements DriverFactory
|
||||
func (factory *MinioDriverFactory) NewDriver() (Driver, error) {
|
||||
// Initialize minio client object.
|
||||
minioClient, err := minio.New(factory.endpoint, factory.accessKeyID, factory.secretAccessKey, factory.useSSL)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if err = minioClient.MakeBucket(factory.bucket, factory.location); err != nil {
|
||||
// Check to see if we already own this bucket (which happens if you run this twice)
|
||||
exists, errBucketExists := minioClient.BucketExists(factory.bucket)
|
||||
if !exists || errBucketExists != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
|
||||
return &MinioDriver{
|
||||
client: minioClient,
|
||||
bucket: factory.bucket,
|
||||
perm: factory.perm,
|
||||
}, nil
|
||||
}
|
||||
8
vendor/goftp.io/server/file_info.go → vendor/goftp.io/server/v2/file_info.go
generated
vendored
8
vendor/goftp.io/server/file_info.go → vendor/goftp.io/server/v2/file_info.go
generated
vendored
@@ -6,14 +6,6 @@ package server
|
||||
|
||||
import "os"
|
||||
|
||||
// FileInfo represents an file interface
|
||||
type FileInfo interface {
|
||||
os.FileInfo
|
||||
|
||||
Owner() string
|
||||
Group() string
|
||||
}
|
||||
|
||||
type fileInfo struct {
|
||||
os.FileInfo
|
||||
|
||||
2
vendor/goftp.io/server/go.mod → vendor/goftp.io/server/v2/go.mod
generated
vendored
2
vendor/goftp.io/server/go.mod → vendor/goftp.io/server/v2/go.mod
generated
vendored
@@ -1,4 +1,4 @@
|
||||
module goftp.io/server
|
||||
module goftp.io/server/v2
|
||||
|
||||
go 1.12
|
||||
|
||||
0
vendor/goftp.io/server/go.sum → vendor/goftp.io/server/v2/go.sum
generated
vendored
0
vendor/goftp.io/server/go.sum → vendor/goftp.io/server/v2/go.sum
generated
vendored
@@ -28,10 +28,10 @@ func (formatter listFormatter) Short() []byte {
|
||||
func (formatter listFormatter) Detailed() []byte {
|
||||
var buf bytes.Buffer
|
||||
for _, file := range formatter {
|
||||
fmt.Fprintf(&buf, file.Mode().String())
|
||||
fmt.Fprint(&buf, file.Mode().String())
|
||||
fmt.Fprintf(&buf, " 1 %s %s ", file.Owner(), file.Group())
|
||||
fmt.Fprintf(&buf, lpad(strconv.FormatInt(file.Size(), 10), 12))
|
||||
fmt.Fprintf(&buf, file.ModTime().Format(" Jan _2 15:04 "))
|
||||
fmt.Fprint(&buf, lpad(strconv.FormatInt(file.Size(), 10), 12))
|
||||
fmt.Fprint(&buf, file.ModTime().Format(" Jan _2 15:04 "))
|
||||
fmt.Fprintf(&buf, "%s\r\n", file.Name())
|
||||
}
|
||||
return buf.Bytes()
|
||||
0
vendor/goftp.io/server/logger.go → vendor/goftp.io/server/v2/logger.go
generated
vendored
0
vendor/goftp.io/server/logger.go → vendor/goftp.io/server/v2/logger.go
generated
vendored
176
vendor/goftp.io/server/v2/notifier.go
generated
vendored
Normal file
176
vendor/goftp.io/server/v2/notifier.go
generated
vendored
Normal file
@@ -0,0 +1,176 @@
|
||||
// Copyright 2020 The goftp Authors. All rights reserved.
|
||||
// Use of this source code is governed by a MIT-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package server
|
||||
|
||||
// Notifier represents a notification operator interface
|
||||
type Notifier interface {
|
||||
BeforeLoginUser(ctx *Context, userName string)
|
||||
BeforePutFile(ctx *Context, dstPath string)
|
||||
BeforeDeleteFile(ctx *Context, dstPath string)
|
||||
BeforeChangeCurDir(ctx *Context, oldCurDir, newCurDir string)
|
||||
BeforeCreateDir(ctx *Context, dstPath string)
|
||||
BeforeDeleteDir(ctx *Context, dstPath string)
|
||||
BeforeDownloadFile(ctx *Context, dstPath string)
|
||||
AfterUserLogin(ctx *Context, userName, password string, passMatched bool, err error)
|
||||
AfterFilePut(ctx *Context, dstPath string, size int64, err error)
|
||||
AfterFileDeleted(ctx *Context, dstPath string, err error)
|
||||
AfterFileDownloaded(ctx *Context, dstPath string, size int64, err error)
|
||||
AfterCurDirChanged(ctx *Context, oldCurDir, newCurDir string, err error)
|
||||
AfterDirCreated(ctx *Context, dstPath string, err error)
|
||||
AfterDirDeleted(ctx *Context, dstPath string, err error)
|
||||
}
|
||||
|
||||
type notifierList []Notifier
|
||||
|
||||
var (
|
||||
_ Notifier = notifierList{}
|
||||
)
|
||||
|
||||
func (notifiers notifierList) BeforeLoginUser(ctx *Context, userName string) {
|
||||
for _, notifier := range notifiers {
|
||||
notifier.BeforeLoginUser(ctx, userName)
|
||||
}
|
||||
}
|
||||
|
||||
func (notifiers notifierList) BeforePutFile(ctx *Context, dstPath string) {
|
||||
for _, notifier := range notifiers {
|
||||
notifier.BeforePutFile(ctx, dstPath)
|
||||
}
|
||||
}
|
||||
|
||||
func (notifiers notifierList) BeforeDeleteFile(ctx *Context, dstPath string) {
|
||||
for _, notifier := range notifiers {
|
||||
notifier.BeforeDeleteFile(ctx, dstPath)
|
||||
}
|
||||
}
|
||||
|
||||
func (notifiers notifierList) BeforeChangeCurDir(ctx *Context, oldCurDir, newCurDir string) {
|
||||
for _, notifier := range notifiers {
|
||||
notifier.BeforeChangeCurDir(ctx, oldCurDir, newCurDir)
|
||||
}
|
||||
}
|
||||
|
||||
func (notifiers notifierList) BeforeCreateDir(ctx *Context, dstPath string) {
|
||||
for _, notifier := range notifiers {
|
||||
notifier.BeforeCreateDir(ctx, dstPath)
|
||||
}
|
||||
}
|
||||
|
||||
func (notifiers notifierList) BeforeDeleteDir(ctx *Context, dstPath string) {
|
||||
for _, notifier := range notifiers {
|
||||
notifier.BeforeDeleteDir(ctx, dstPath)
|
||||
}
|
||||
}
|
||||
|
||||
func (notifiers notifierList) BeforeDownloadFile(ctx *Context, dstPath string) {
|
||||
for _, notifier := range notifiers {
|
||||
notifier.BeforeDownloadFile(ctx, dstPath)
|
||||
}
|
||||
}
|
||||
|
||||
func (notifiers notifierList) AfterUserLogin(ctx *Context, userName, password string, passMatched bool, err error) {
|
||||
for _, notifier := range notifiers {
|
||||
notifier.AfterUserLogin(ctx, userName, password, passMatched, err)
|
||||
}
|
||||
}
|
||||
|
||||
func (notifiers notifierList) AfterFilePut(ctx *Context, dstPath string, size int64, err error) {
|
||||
for _, notifier := range notifiers {
|
||||
notifier.AfterFilePut(ctx, dstPath, size, err)
|
||||
}
|
||||
}
|
||||
|
||||
func (notifiers notifierList) AfterFileDeleted(ctx *Context, dstPath string, err error) {
|
||||
for _, notifier := range notifiers {
|
||||
notifier.AfterFileDeleted(ctx, dstPath, err)
|
||||
}
|
||||
}
|
||||
|
||||
func (notifiers notifierList) AfterFileDownloaded(ctx *Context, dstPath string, size int64, err error) {
|
||||
for _, notifier := range notifiers {
|
||||
notifier.AfterFileDownloaded(ctx, dstPath, size, err)
|
||||
}
|
||||
}
|
||||
|
||||
func (notifiers notifierList) AfterCurDirChanged(ctx *Context, oldCurDir, newCurDir string, err error) {
|
||||
for _, notifier := range notifiers {
|
||||
notifier.AfterCurDirChanged(ctx, oldCurDir, newCurDir, err)
|
||||
}
|
||||
}
|
||||
|
||||
func (notifiers notifierList) AfterDirCreated(ctx *Context, dstPath string, err error) {
|
||||
for _, notifier := range notifiers {
|
||||
notifier.AfterDirCreated(ctx, dstPath, err)
|
||||
}
|
||||
}
|
||||
|
||||
func (notifiers notifierList) AfterDirDeleted(ctx *Context, dstPath string, err error) {
|
||||
for _, notifier := range notifiers {
|
||||
notifier.AfterDirDeleted(ctx, dstPath, err)
|
||||
}
|
||||
}
|
||||
|
||||
// NullNotifier implements Notifier
|
||||
type NullNotifier struct{}
|
||||
|
||||
var (
|
||||
_ Notifier = &NullNotifier{}
|
||||
)
|
||||
|
||||
// BeforeLoginUser implements Notifier
|
||||
func (NullNotifier) BeforeLoginUser(ctx *Context, userName string) {
|
||||
}
|
||||
|
||||
// BeforePutFile implements Notifier
|
||||
func (NullNotifier) BeforePutFile(ctx *Context, dstPath string) {
|
||||
}
|
||||
|
||||
// BeforeDeleteFile implements Notifier
|
||||
func (NullNotifier) BeforeDeleteFile(ctx *Context, dstPath string) {
|
||||
}
|
||||
|
||||
// BeforeChangeCurDir implements Notifier
|
||||
func (NullNotifier) BeforeChangeCurDir(ctx *Context, oldCurDir, newCurDir string) {
|
||||
}
|
||||
|
||||
// BeforeCreateDir implements Notifier
|
||||
func (NullNotifier) BeforeCreateDir(ctx *Context, dstPath string) {
|
||||
}
|
||||
|
||||
// BeforeDeleteDir implements Notifier
|
||||
func (NullNotifier) BeforeDeleteDir(ctx *Context, dstPath string) {
|
||||
}
|
||||
|
||||
// BeforeDownloadFile implements Notifier
|
||||
func (NullNotifier) BeforeDownloadFile(ctx *Context, dstPath string) {
|
||||
}
|
||||
|
||||
// AfterUserLogin implements Notifier
|
||||
func (NullNotifier) AfterUserLogin(ctx *Context, userName, password string, passMatched bool, err error) {
|
||||
}
|
||||
|
||||
// AfterFilePut implements Notifier
|
||||
func (NullNotifier) AfterFilePut(ctx *Context, dstPath string, size int64, err error) {
|
||||
}
|
||||
|
||||
// AfterFileDeleted implements Notifier
|
||||
func (NullNotifier) AfterFileDeleted(ctx *Context, dstPath string, err error) {
|
||||
}
|
||||
|
||||
// AfterFileDownloaded implements Notifier
|
||||
func (NullNotifier) AfterFileDownloaded(ctx *Context, dstPath string, size int64, err error) {
|
||||
}
|
||||
|
||||
// AfterCurDirChanged implements Notifier
|
||||
func (NullNotifier) AfterCurDirChanged(ctx *Context, oldCurDir, newCurDir string, err error) {
|
||||
}
|
||||
|
||||
// AfterDirCreated implements Notifier
|
||||
func (NullNotifier) AfterDirCreated(ctx *Context, dstPath string, err error) {
|
||||
}
|
||||
|
||||
// AfterDirDeleted implements Notifier
|
||||
func (NullNotifier) AfterDirDeleted(ctx *Context, dstPath string, err error) {
|
||||
}
|
||||
0
vendor/goftp.io/server/perm.go → vendor/goftp.io/server/v2/perm.go
generated
vendored
0
vendor/goftp.io/server/perm.go → vendor/goftp.io/server/v2/perm.go
generated
vendored
38
vendor/goftp.io/server/v2/ratelimit/limiter.go
generated
vendored
Normal file
38
vendor/goftp.io/server/v2/ratelimit/limiter.go
generated
vendored
Normal file
@@ -0,0 +1,38 @@
|
||||
// Copyright 2020 The goftp Authors. All rights reserved.
|
||||
// Use of this source code is governed by a MIT-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package ratelimit
|
||||
|
||||
import (
|
||||
"time"
|
||||
)
|
||||
|
||||
// Limiter represents a rate limiter
|
||||
type Limiter struct {
|
||||
rate time.Duration
|
||||
count int64
|
||||
t time.Time
|
||||
}
|
||||
|
||||
// New create a limiter for transfer speed, parameter rate means bytes per second
|
||||
// 0 means don't limit
|
||||
func New(rate int64) *Limiter {
|
||||
return &Limiter{
|
||||
rate: time.Duration(rate),
|
||||
count: 0,
|
||||
t: time.Now(),
|
||||
}
|
||||
}
|
||||
|
||||
// Wait sleep when write count bytes
|
||||
func (l *Limiter) Wait(count int) {
|
||||
if l.rate == 0 {
|
||||
return
|
||||
}
|
||||
l.count += int64(count)
|
||||
t := time.Duration(l.count)*time.Second/l.rate - time.Since(l.t)
|
||||
if t > 0 {
|
||||
time.Sleep(t)
|
||||
}
|
||||
}
|
||||
27
vendor/goftp.io/server/v2/ratelimit/reader.go
generated
vendored
Normal file
27
vendor/goftp.io/server/v2/ratelimit/reader.go
generated
vendored
Normal file
@@ -0,0 +1,27 @@
|
||||
// Copyright 2020 The goftp Authors. All rights reserved.
|
||||
// Use of this source code is governed by a MIT-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package ratelimit
|
||||
|
||||
import "io"
|
||||
|
||||
type reader struct {
|
||||
r io.Reader
|
||||
l *Limiter
|
||||
}
|
||||
|
||||
// Read Read
|
||||
func (r *reader) Read(buf []byte) (int, error) {
|
||||
n, err := r.r.Read(buf)
|
||||
r.l.Wait(n)
|
||||
return n, err
|
||||
}
|
||||
|
||||
// Reader returns a reader with limiter
|
||||
func Reader(r io.Reader, l *Limiter) io.Reader {
|
||||
return &reader{
|
||||
r: r,
|
||||
l: l,
|
||||
}
|
||||
}
|
||||
26
vendor/goftp.io/server/v2/ratelimit/writer.go
generated
vendored
Normal file
26
vendor/goftp.io/server/v2/ratelimit/writer.go
generated
vendored
Normal file
@@ -0,0 +1,26 @@
|
||||
// Copyright 2020 The goftp Authors. All rights reserved.
|
||||
// Use of this source code is governed by a MIT-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package ratelimit
|
||||
|
||||
import "io"
|
||||
|
||||
type writer struct {
|
||||
w io.Writer
|
||||
l *Limiter
|
||||
}
|
||||
|
||||
// Write Write
|
||||
func (w *writer) Write(buf []byte) (int, error) {
|
||||
w.l.Wait(len(buf))
|
||||
return w.w.Write(buf)
|
||||
}
|
||||
|
||||
// Writer returns a writer with limiter
|
||||
func Writer(w io.Writer, l *Limiter) io.Writer {
|
||||
return &writer{
|
||||
w: w,
|
||||
l: l,
|
||||
}
|
||||
}
|
||||
154
vendor/goftp.io/server/server.go → vendor/goftp.io/server/v2/server.go
generated
vendored
154
vendor/goftp.io/server/server.go → vendor/goftp.io/server/v2/server.go
generated
vendored
@@ -12,21 +12,29 @@ import (
|
||||
"fmt"
|
||||
"net"
|
||||
"strconv"
|
||||
|
||||
"goftp.io/server/v2/ratelimit"
|
||||
)
|
||||
|
||||
// Version returns the library version
|
||||
func Version() string {
|
||||
return "0.3.1"
|
||||
}
|
||||
var (
|
||||
version = "2.0beta"
|
||||
)
|
||||
|
||||
// ServerOpts contains parameters for server.NewServer()
|
||||
type ServerOpts struct {
|
||||
// The factory that will be used to create a new FTPDriver instance for
|
||||
// each client connection. This is a mandatory option.
|
||||
Factory DriverFactory
|
||||
// Options contains parameters for server.NewServer()
|
||||
type Options struct {
|
||||
// This server supported commands, if blank, it will be defaultCommands
|
||||
// So that users could override the Commands
|
||||
Commands map[string]Command
|
||||
|
||||
// The driver that will be used to handle files persistent
|
||||
Driver Driver
|
||||
|
||||
// How to hanle the authenticate requests
|
||||
Auth Auth
|
||||
|
||||
// How to handle the perm controls
|
||||
Perm Perm
|
||||
|
||||
// Server Name, Default is Go Ftp Server
|
||||
Name string
|
||||
|
||||
@@ -56,10 +64,16 @@ type ServerOpts struct {
|
||||
// If ture TLS is used in RFC4217 mode
|
||||
ExplicitFTPS bool
|
||||
|
||||
// If true, client must upgrade to TLS before sending any other command
|
||||
ForceTLS bool
|
||||
|
||||
WelcomeMessage string
|
||||
|
||||
// A logger implementation, if nil the StdLogger is used
|
||||
Logger Logger
|
||||
|
||||
// Rate Limit per connection bytes per second, 0 means no limit
|
||||
RateLimit int64
|
||||
}
|
||||
|
||||
// Server is the root of your FTP application. You should instantiate one
|
||||
@@ -67,7 +81,7 @@ type ServerOpts struct {
|
||||
//
|
||||
// Always use the NewServer() method to create a new Server.
|
||||
type Server struct {
|
||||
*ServerOpts
|
||||
*Options
|
||||
listenTo string
|
||||
logger Logger
|
||||
listener net.Listener
|
||||
@@ -76,18 +90,20 @@ type Server struct {
|
||||
cancel context.CancelFunc
|
||||
feats string
|
||||
notifiers notifierList
|
||||
// rate limiter per connection
|
||||
rateLimiter *ratelimit.Limiter
|
||||
}
|
||||
|
||||
// ErrServerClosed is returned by ListenAndServe() or Serve() when a shutdown
|
||||
// was requested.
|
||||
var ErrServerClosed = errors.New("ftp: Server closed")
|
||||
|
||||
// serverOptsWithDefaults copies an ServerOpts struct into a new struct,
|
||||
// optsWithDefaults copies an Options struct into a new struct,
|
||||
// then adds any default values that are missing and returns the new data.
|
||||
func serverOptsWithDefaults(opts *ServerOpts) *ServerOpts {
|
||||
var newOpts ServerOpts
|
||||
func optsWithDefaults(opts *Options) *Options {
|
||||
var newOpts Options
|
||||
if opts == nil {
|
||||
opts = &ServerOpts{}
|
||||
opts = &Options{}
|
||||
}
|
||||
if opts.Hostname == "" {
|
||||
newOpts.Hostname = "::"
|
||||
@@ -95,11 +111,11 @@ func serverOptsWithDefaults(opts *ServerOpts) *ServerOpts {
|
||||
newOpts.Hostname = opts.Hostname
|
||||
}
|
||||
if opts.Port == 0 {
|
||||
newOpts.Port = 3000
|
||||
newOpts.Port = 2121
|
||||
} else {
|
||||
newOpts.Port = opts.Port
|
||||
}
|
||||
newOpts.Factory = opts.Factory
|
||||
newOpts.Driver = opts.Driver
|
||||
if opts.Name == "" {
|
||||
newOpts.Name = "Go FTP Server"
|
||||
} else {
|
||||
@@ -122,6 +138,11 @@ func serverOptsWithDefaults(opts *ServerOpts) *ServerOpts {
|
||||
newOpts.Logger = &StdLogger{}
|
||||
}
|
||||
|
||||
if opts.Commands == nil {
|
||||
newOpts.Commands = defaultCommands
|
||||
}
|
||||
|
||||
newOpts.Perm = opts.Perm
|
||||
newOpts.TLS = opts.TLS
|
||||
newOpts.KeyFile = opts.KeyFile
|
||||
newOpts.CertFile = opts.CertFile
|
||||
@@ -129,40 +150,53 @@ func serverOptsWithDefaults(opts *ServerOpts) *ServerOpts {
|
||||
|
||||
newOpts.PublicIP = opts.PublicIP
|
||||
newOpts.PassivePorts = opts.PassivePorts
|
||||
newOpts.RateLimit = opts.RateLimit
|
||||
|
||||
return &newOpts
|
||||
}
|
||||
|
||||
// NewServer initialises a new FTP server. Configuration options are provided
|
||||
// via an instance of ServerOpts. Calling this function in your code will
|
||||
// via an instance of Options. Calling this function in your code will
|
||||
// probably look something like this:
|
||||
//
|
||||
// factory := &MyDriverFactory{}
|
||||
// server := server.NewServer(&server.ServerOpts{ Factory: factory })
|
||||
//
|
||||
// or:
|
||||
//
|
||||
// factory := &MyDriverFactory{}
|
||||
// opts := &server.ServerOpts{
|
||||
// Factory: factory,
|
||||
// driver := &MyDriver{}
|
||||
// opts := &server.Options{
|
||||
// Driver: driver,
|
||||
// Auth: auth,
|
||||
// Port: 2000,
|
||||
// Perm: perm,
|
||||
// Hostname: "127.0.0.1",
|
||||
// }
|
||||
// server := server.NewServer(opts)
|
||||
// server, err := server.NewServer(opts)
|
||||
//
|
||||
func NewServer(opts *ServerOpts) *Server {
|
||||
opts = serverOptsWithDefaults(opts)
|
||||
func NewServer(opts *Options) (*Server, error) {
|
||||
opts = optsWithDefaults(opts)
|
||||
if opts.Perm == nil {
|
||||
return nil, errors.New("No perm implementation")
|
||||
}
|
||||
s := new(Server)
|
||||
s.ServerOpts = opts
|
||||
s.Options = opts
|
||||
s.listenTo = net.JoinHostPort(opts.Hostname, strconv.Itoa(opts.Port))
|
||||
s.logger = opts.Logger
|
||||
var curFeats = featCmds
|
||||
if opts.TLS {
|
||||
curFeats += " AUTH TLS\n PBSZ\n PROT\n"
|
||||
}
|
||||
s.feats = fmt.Sprintf(feats, curFeats)
|
||||
|
||||
return s
|
||||
var (
|
||||
feats = "Extensions supported:\n%s"
|
||||
featCmds = " UTF8\n"
|
||||
)
|
||||
|
||||
for k, v := range s.Commands {
|
||||
if v.IsExtend() {
|
||||
featCmds = featCmds + " " + k + "\n"
|
||||
}
|
||||
}
|
||||
|
||||
if opts.TLS {
|
||||
featCmds += " AUTH TLS\n PBSZ\n PROT\n"
|
||||
}
|
||||
s.feats = fmt.Sprintf(feats, featCmds)
|
||||
s.rateLimiter = ratelimit.New(opts.RateLimit)
|
||||
|
||||
return s, nil
|
||||
}
|
||||
|
||||
// RegisterNotifer registers a notifier
|
||||
@@ -174,19 +208,22 @@ func (server *Server) RegisterNotifer(notifier Notifier) {
|
||||
// an active net.TCPConn. The TCP connection should already be open before
|
||||
// it is handed to this functions. driver is an instance of FTPDriver that
|
||||
// will handle all auth and persistence details.
|
||||
func (server *Server) newConn(tcpConn net.Conn, driver Driver) *Conn {
|
||||
c := new(Conn)
|
||||
c.curDir = "/"
|
||||
c.conn = tcpConn
|
||||
c.controlReader = bufio.NewReader(tcpConn)
|
||||
c.controlWriter = bufio.NewWriter(tcpConn)
|
||||
c.driver = driver
|
||||
c.auth = server.Auth
|
||||
c.server = server
|
||||
c.sessionID = newSessionID()
|
||||
c.logger = server.logger
|
||||
c.tlsConfig = server.tlsConfig
|
||||
return c
|
||||
func (server *Server) newSession(id string, tcpConn net.Conn) *Session {
|
||||
return &Session{
|
||||
id: id,
|
||||
server: server,
|
||||
conn: tcpConn,
|
||||
controlReader: bufio.NewReader(tcpConn),
|
||||
controlWriter: bufio.NewWriter(tcpConn),
|
||||
curDir: "/",
|
||||
reqUser: "",
|
||||
user: "",
|
||||
renameFrom: "",
|
||||
lastFilePos: -1,
|
||||
closed: false,
|
||||
tls: false,
|
||||
Data: make(map[string]interface{}),
|
||||
}
|
||||
}
|
||||
|
||||
func simpleTLSConfig(certFile, keyFile string) (*tls.Config, error) {
|
||||
@@ -216,13 +253,13 @@ func (server *Server) ListenAndServe() error {
|
||||
var listener net.Listener
|
||||
var err error
|
||||
|
||||
if server.ServerOpts.TLS {
|
||||
if server.Options.TLS {
|
||||
server.tlsConfig, err = simpleTLSConfig(server.CertFile, server.KeyFile)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if server.ServerOpts.ExplicitFTPS {
|
||||
if server.Options.ExplicitFTPS {
|
||||
listener, err = net.Listen("tcp", server.listenTo)
|
||||
} else {
|
||||
listener, err = tls.Listen("tcp", server.listenTo, server.tlsConfig)
|
||||
@@ -234,8 +271,7 @@ func (server *Server) ListenAndServe() error {
|
||||
return err
|
||||
}
|
||||
|
||||
sessionID := ""
|
||||
server.logger.Printf(sessionID, "%s listening on %d", server.Name, server.Port)
|
||||
server.logger.Printf("", "%s listening on %d", server.Name, server.Port)
|
||||
|
||||
return server.Serve(listener)
|
||||
}
|
||||
@@ -246,7 +282,8 @@ func (server *Server) ListenAndServe() error {
|
||||
func (server *Server) Serve(l net.Listener) error {
|
||||
server.listener = l
|
||||
server.ctx, server.cancel = context.WithCancel(context.Background())
|
||||
sessionID := ""
|
||||
defer server.cancel()
|
||||
sessionID := newSessionID()
|
||||
for {
|
||||
tcpConn, err := server.listener.Accept()
|
||||
if err != nil {
|
||||
@@ -261,14 +298,9 @@ func (server *Server) Serve(l net.Listener) error {
|
||||
}
|
||||
return err
|
||||
}
|
||||
driver, err := server.Factory.NewDriver()
|
||||
if err != nil {
|
||||
server.logger.Printf(sessionID, "Error creating driver, aborting client connection: %v", err)
|
||||
tcpConn.Close()
|
||||
} else {
|
||||
ftpConn := server.newConn(tcpConn, driver)
|
||||
go ftpConn.Serve()
|
||||
}
|
||||
|
||||
ftpConn := server.newSession(sessionID, tcpConn)
|
||||
go ftpConn.Serve()
|
||||
}
|
||||
}
|
||||
|
||||
192
vendor/goftp.io/server/conn.go → vendor/goftp.io/server/v2/session.go
generated
vendored
192
vendor/goftp.io/server/conn.go → vendor/goftp.io/server/v2/session.go
generated
vendored
@@ -26,54 +26,57 @@ const (
|
||||
defaultWelcomeMessage = "Welcome to the Go FTP Server"
|
||||
)
|
||||
|
||||
// Conn represents a connection between ftp client and the server
|
||||
type Conn struct {
|
||||
// Session represents a session between ftp client and the server
|
||||
type Session struct {
|
||||
conn net.Conn
|
||||
controlReader *bufio.Reader
|
||||
controlWriter *bufio.Writer
|
||||
dataConn DataSocket
|
||||
driver Driver
|
||||
auth Auth
|
||||
logger Logger
|
||||
server *Server
|
||||
tlsConfig *tls.Config
|
||||
sessionID string
|
||||
id string
|
||||
curDir string
|
||||
reqUser string
|
||||
user string
|
||||
renameFrom string
|
||||
lastFilePos int64
|
||||
appendData bool
|
||||
preCommand string
|
||||
closed bool
|
||||
tls bool
|
||||
clientSoft string
|
||||
Data map[string]interface{} // shared data between different commands
|
||||
}
|
||||
|
||||
// RemoteAddr returns the remote ftp client's address
|
||||
func (conn *Conn) RemoteAddr() net.Addr {
|
||||
return conn.conn.RemoteAddr()
|
||||
func (sess *Session) RemoteAddr() net.Addr {
|
||||
return sess.conn.RemoteAddr()
|
||||
}
|
||||
|
||||
// LoginUser returns the login user name if login
|
||||
func (conn *Conn) LoginUser() string {
|
||||
return conn.user
|
||||
func (sess *Session) LoginUser() string {
|
||||
return sess.user
|
||||
}
|
||||
|
||||
// IsLogin returns if user has login
|
||||
func (conn *Conn) IsLogin() bool {
|
||||
return len(conn.user) > 0
|
||||
func (sess *Session) IsLogin() bool {
|
||||
return len(sess.user) > 0
|
||||
}
|
||||
|
||||
// PublicIP returns the public ip of the server
|
||||
func (conn *Conn) PublicIP() string {
|
||||
return conn.server.PublicIP
|
||||
func (sess *Session) PublicIP() string {
|
||||
return sess.server.PublicIP
|
||||
}
|
||||
|
||||
func (conn *Conn) passiveListenIP() string {
|
||||
// Options returns the server options
|
||||
func (sess *Session) Options() *Options {
|
||||
return sess.server.Options
|
||||
}
|
||||
|
||||
func (sess *Session) passiveListenIP() string {
|
||||
var listenIP string
|
||||
if len(conn.PublicIP()) > 0 {
|
||||
listenIP = conn.PublicIP()
|
||||
if len(sess.PublicIP()) > 0 {
|
||||
listenIP = sess.PublicIP()
|
||||
} else {
|
||||
listenIP = conn.conn.LocalAddr().(*net.TCPAddr).IP.String()
|
||||
listenIP = sess.conn.LocalAddr().(*net.TCPAddr).IP.String()
|
||||
}
|
||||
|
||||
if listenIP == "::1" {
|
||||
@@ -88,9 +91,9 @@ func (conn *Conn) passiveListenIP() string {
|
||||
}
|
||||
|
||||
// PassivePort returns the port which could be used by passive mode.
|
||||
func (conn *Conn) PassivePort() int {
|
||||
if len(conn.server.PassivePorts) > 0 {
|
||||
portRange := strings.Split(conn.server.PassivePorts, "-")
|
||||
func (sess *Session) PassivePort() int {
|
||||
if len(sess.server.PassivePorts) > 0 {
|
||||
portRange := strings.Split(sess.server.PassivePorts, "-")
|
||||
|
||||
if len(portRange) != 2 {
|
||||
log.Println("empty port")
|
||||
@@ -123,59 +126,59 @@ func newSessionID() string {
|
||||
// message when the connection closes. This loop will be running inside a
|
||||
// goroutine, so use this channel to be notified when the connection can be
|
||||
// cleaned up.
|
||||
func (conn *Conn) Serve() {
|
||||
conn.logger.Print(conn.sessionID, "Connection Established")
|
||||
func (sess *Session) Serve() {
|
||||
sess.log("Connection Established")
|
||||
// send welcome
|
||||
conn.writeMessage(220, conn.server.WelcomeMessage)
|
||||
sess.writeMessage(220, sess.server.WelcomeMessage)
|
||||
// read commands
|
||||
for {
|
||||
line, err := conn.controlReader.ReadString('\n')
|
||||
line, err := sess.controlReader.ReadString('\n')
|
||||
if err != nil {
|
||||
if err != io.EOF {
|
||||
conn.logger.Print(conn.sessionID, fmt.Sprint("read error:", err))
|
||||
sess.log(fmt.Sprint("read error:", err))
|
||||
}
|
||||
|
||||
break
|
||||
}
|
||||
conn.receiveLine(line)
|
||||
sess.receiveLine(line)
|
||||
// QUIT command closes connection, break to avoid error on reading from
|
||||
// closed socket
|
||||
if conn.closed == true {
|
||||
if sess.closed {
|
||||
break
|
||||
}
|
||||
}
|
||||
conn.Close()
|
||||
conn.logger.Print(conn.sessionID, "Connection Terminated")
|
||||
sess.Close()
|
||||
sess.log("Connection Terminated")
|
||||
}
|
||||
|
||||
// Close will manually close this connection, even if the client isn't ready.
|
||||
func (conn *Conn) Close() {
|
||||
conn.conn.Close()
|
||||
conn.closed = true
|
||||
conn.reqUser = ""
|
||||
conn.user = ""
|
||||
if conn.dataConn != nil {
|
||||
conn.dataConn.Close()
|
||||
conn.dataConn = nil
|
||||
func (sess *Session) Close() {
|
||||
sess.conn.Close()
|
||||
sess.closed = true
|
||||
sess.reqUser = ""
|
||||
sess.user = ""
|
||||
if sess.dataConn != nil {
|
||||
sess.dataConn.Close()
|
||||
sess.dataConn = nil
|
||||
}
|
||||
}
|
||||
|
||||
func (conn *Conn) upgradeToTLS() error {
|
||||
conn.logger.Print(conn.sessionID, "Upgrading connectiion to TLS")
|
||||
tlsConn := tls.Server(conn.conn, conn.tlsConfig)
|
||||
func (sess *Session) upgradeToTLS() error {
|
||||
sess.log("Upgrading connectiion to TLS")
|
||||
tlsConn := tls.Server(sess.conn, sess.server.tlsConfig)
|
||||
err := tlsConn.Handshake()
|
||||
if err == nil {
|
||||
conn.conn = tlsConn
|
||||
conn.controlReader = bufio.NewReader(tlsConn)
|
||||
conn.controlWriter = bufio.NewWriter(tlsConn)
|
||||
conn.tls = true
|
||||
sess.conn = tlsConn
|
||||
sess.controlReader = bufio.NewReader(tlsConn)
|
||||
sess.controlWriter = bufio.NewWriter(tlsConn)
|
||||
sess.tls = true
|
||||
}
|
||||
return err
|
||||
}
|
||||
|
||||
// receiveLine accepts a single line FTP command and co-ordinates an
|
||||
// appropriate response.
|
||||
func (conn *Conn) receiveLine(line string) {
|
||||
func (sess *Session) receiveLine(line string) {
|
||||
defer func() {
|
||||
if e := recover(); e != nil {
|
||||
var buf bytes.Buffer
|
||||
@@ -191,50 +194,56 @@ func (conn *Conn) receiveLine(line string) {
|
||||
fmt.Fprintf(&buf, "%v:%v", file, line)
|
||||
}
|
||||
|
||||
conn.logger.Print(conn.sessionID, buf.String())
|
||||
sess.log(buf.String())
|
||||
}
|
||||
}()
|
||||
|
||||
command, param := conn.parseLine(line)
|
||||
conn.logger.PrintCommand(conn.sessionID, command, param)
|
||||
cmdObj := commands[strings.ToUpper(command)]
|
||||
command, param := sess.parseLine(line)
|
||||
sess.server.Logger.PrintCommand(sess.id, command, param)
|
||||
|
||||
var (
|
||||
commands = sess.server.Commands
|
||||
theCmd = strings.ToUpper(command)
|
||||
cmdObj = commands[theCmd]
|
||||
)
|
||||
if cmdObj == nil {
|
||||
conn.writeMessage(500, "Command not found")
|
||||
sess.writeMessage(500, "Command not found")
|
||||
return
|
||||
}
|
||||
if cmdObj.RequireParam() && param == "" {
|
||||
conn.writeMessage(553, "action aborted, required param missing")
|
||||
} else if cmdObj.RequireAuth() && conn.user == "" {
|
||||
conn.writeMessage(530, "not logged in")
|
||||
sess.writeMessage(553, "action aborted, required param missing")
|
||||
} else if sess.server.Options.ForceTLS && !sess.tls && !(cmdObj == commands["AUTH"] && param == "TLS") {
|
||||
sess.writeMessage(534, "Request denied for policy reasons. AUTH TLS required.")
|
||||
} else if cmdObj.RequireAuth() && sess.user == "" {
|
||||
sess.writeMessage(530, "not logged in")
|
||||
} else {
|
||||
cmdObj.Execute(conn, param)
|
||||
cmdObj.Execute(sess, param)
|
||||
sess.preCommand = theCmd
|
||||
}
|
||||
}
|
||||
|
||||
func (conn *Conn) parseLine(line string) (string, string) {
|
||||
func (sess *Session) parseLine(line string) (string, string) {
|
||||
params := strings.SplitN(strings.Trim(line, "\r\n"), " ", 2)
|
||||
if len(params) == 1 {
|
||||
return params[0], ""
|
||||
}
|
||||
return params[0], strings.TrimSpace(params[1])
|
||||
return params[0], params[1]
|
||||
}
|
||||
|
||||
// writeMessage will send a standard FTP response back to the client.
|
||||
func (conn *Conn) writeMessage(code int, message string) (wrote int, err error) {
|
||||
conn.logger.PrintResponse(conn.sessionID, code, message)
|
||||
func (sess *Session) writeMessage(code int, message string) {
|
||||
sess.server.Logger.PrintResponse(sess.id, code, message)
|
||||
line := fmt.Sprintf("%d %s\r\n", code, message)
|
||||
wrote, err = conn.controlWriter.WriteString(line)
|
||||
conn.controlWriter.Flush()
|
||||
return
|
||||
_, _ = sess.controlWriter.WriteString(line)
|
||||
sess.controlWriter.Flush()
|
||||
}
|
||||
|
||||
// writeMessage will send a standard FTP response back to the client.
|
||||
func (conn *Conn) writeMessageMultiline(code int, message string) (wrote int, err error) {
|
||||
conn.logger.PrintResponse(conn.sessionID, code, message)
|
||||
func (sess *Session) writeMessageMultiline(code int, message string) {
|
||||
sess.server.Logger.PrintResponse(sess.id, code, message)
|
||||
line := fmt.Sprintf("%d-%s\r\n%d END\r\n", code, message, code)
|
||||
wrote, err = conn.controlWriter.WriteString(line)
|
||||
conn.controlWriter.Flush()
|
||||
return
|
||||
_, _ = sess.controlWriter.WriteString(line)
|
||||
sess.controlWriter.Flush()
|
||||
}
|
||||
|
||||
// buildPath takes a client supplied path or filename and generates a safe
|
||||
@@ -254,13 +263,13 @@ func (conn *Conn) writeMessageMultiline(code int, message string) (wrote int, er
|
||||
// The driver implementation is responsible for deciding how to treat this path.
|
||||
// Obviously they MUST NOT just read the path off disk. The probably want to
|
||||
// prefix the path with something to scope the users access to a sandbox.
|
||||
func (conn *Conn) buildPath(filename string) (fullPath string) {
|
||||
func (sess *Session) buildPath(filename string) (fullPath string) {
|
||||
if len(filename) > 0 && filename[0:1] == "/" {
|
||||
fullPath = filepath.Clean(filename)
|
||||
} else if len(filename) > 0 && filename != "-a" {
|
||||
fullPath = filepath.Clean(conn.curDir + "/" + filename)
|
||||
fullPath = filepath.Clean(sess.curDir + "/" + filename)
|
||||
} else {
|
||||
fullPath = filepath.Clean(conn.curDir)
|
||||
fullPath = filepath.Clean(sess.curDir)
|
||||
}
|
||||
fullPath = strings.Replace(fullPath, "//", "/", -1)
|
||||
fullPath = strings.Replace(fullPath, string(filepath.Separator), "/", -1)
|
||||
@@ -269,34 +278,41 @@ func (conn *Conn) buildPath(filename string) (fullPath string) {
|
||||
|
||||
// sendOutofbandData will send a string to the client via the currently open
|
||||
// data socket. Assumes the socket is open and ready to be used.
|
||||
func (conn *Conn) sendOutofbandData(data []byte) {
|
||||
func (sess *Session) sendOutofbandData(data []byte) {
|
||||
bytes := len(data)
|
||||
if conn.dataConn != nil {
|
||||
conn.dataConn.Write(data)
|
||||
conn.dataConn.Close()
|
||||
conn.dataConn = nil
|
||||
if sess.dataConn != nil {
|
||||
_, _ = sess.dataConn.Write(data)
|
||||
sess.dataConn.Close()
|
||||
sess.dataConn = nil
|
||||
}
|
||||
message := "Closing data connection, sent " + strconv.Itoa(bytes) + " bytes"
|
||||
conn.writeMessage(226, message)
|
||||
sess.writeMessage(226, message)
|
||||
}
|
||||
|
||||
func (conn *Conn) sendOutofBandDataWriter(data io.ReadCloser) error {
|
||||
conn.lastFilePos = 0
|
||||
bytes, err := io.Copy(conn.dataConn, data)
|
||||
func (sess *Session) sendOutofBandDataWriter(data io.ReadCloser) error {
|
||||
bytes, err := io.Copy(sess.dataConn, data)
|
||||
if err != nil {
|
||||
conn.dataConn.Close()
|
||||
conn.dataConn = nil
|
||||
sess.dataConn.Close()
|
||||
sess.dataConn = nil
|
||||
return err
|
||||
}
|
||||
message := "Closing data connection, sent " + strconv.Itoa(int(bytes)) + " bytes"
|
||||
conn.writeMessage(226, message)
|
||||
conn.dataConn.Close()
|
||||
conn.dataConn = nil
|
||||
sess.writeMessage(226, message)
|
||||
sess.dataConn.Close()
|
||||
sess.dataConn = nil
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (conn *Conn) changeCurDir(path string) error {
|
||||
conn.curDir = path
|
||||
func (sess *Session) changeCurDir(path string) error {
|
||||
sess.curDir = path
|
||||
return nil
|
||||
}
|
||||
|
||||
func (sess *Session) log(message interface{}) {
|
||||
sess.server.logger.Print(sess.id, message)
|
||||
}
|
||||
|
||||
func (sess *Session) logf(format string, v ...interface{}) {
|
||||
sess.server.logger.Printf(sess.id, format, v...)
|
||||
}
|
||||
23
vendor/modules.txt
vendored
23
vendor/modules.txt
vendored
@@ -1,9 +1,5 @@
|
||||
# gitea.com/goftp/leveldb-auth v0.0.0-20190711092309-e8e3d5ad5ac8
|
||||
gitea.com/goftp/leveldb-auth
|
||||
# gitea.com/goftp/leveldb-perm v0.0.0-20190711092750-00b79e6da99c
|
||||
gitea.com/goftp/leveldb-perm
|
||||
# gitea.com/goftp/qiniu-driver v0.0.0-20191027083326-6e505f23c4f0
|
||||
gitea.com/goftp/qiniu-driver
|
||||
# gitea.com/lunny/log v0.0.0-20190322053110-01b5df579c4e
|
||||
gitea.com/lunny/log
|
||||
# gitea.com/lunny/tango v0.6.1
|
||||
@@ -37,18 +33,6 @@ github.com/minio/sha256-simd
|
||||
github.com/mitchellh/go-homedir
|
||||
# github.com/oxtoacart/bpool v0.0.0-20190530202638-03653db5a59c
|
||||
github.com/oxtoacart/bpool
|
||||
# github.com/qiniu/api.v6 v6.0.9+incompatible
|
||||
github.com/qiniu/api.v6/auth/digest
|
||||
github.com/qiniu/api.v6/conf
|
||||
github.com/qiniu/api.v6/io
|
||||
github.com/qiniu/api.v6/rs
|
||||
github.com/qiniu/api.v6/rsf
|
||||
github.com/qiniu/api.v6/url
|
||||
# github.com/qiniu/bytes v0.0.0-20140728010635-4887e7b2bde3
|
||||
github.com/qiniu/bytes
|
||||
github.com/qiniu/bytes/seekable
|
||||
# github.com/qiniu/rpc v0.0.0-20140728010754-30c22466d920
|
||||
github.com/qiniu/rpc
|
||||
# github.com/shurcooL/httpfs v0.0.0-20190707220628-8d4bc4ba7749
|
||||
github.com/shurcooL/httpfs/vfsutil
|
||||
# github.com/shurcooL/vfsgen v0.0.0-20181202132449-6a9ea43bcacd
|
||||
@@ -70,8 +54,11 @@ github.com/syndtr/goleveldb/leveldb/util
|
||||
github.com/unknwon/com
|
||||
# github.com/unknwon/goconfig v0.0.0-20190425194916-3dba17dd7b9e
|
||||
github.com/unknwon/goconfig
|
||||
# goftp.io/server v0.3.5-0.20200428022247-5cd49dc54bdb
|
||||
goftp.io/server
|
||||
# goftp.io/server/v2 v2.0.0
|
||||
goftp.io/server/v2
|
||||
goftp.io/server/v2/driver/file
|
||||
goftp.io/server/v2/driver/minio
|
||||
goftp.io/server/v2/ratelimit
|
||||
# golang.org/x/crypto v0.0.0-20190701094942-4def268fd1a4
|
||||
golang.org/x/crypto/argon2
|
||||
golang.org/x/crypto/blake2b
|
||||
|
||||
@@ -10,6 +10,8 @@ import (
|
||||
"gitea.com/tango/renders"
|
||||
"gitea.com/tango/session"
|
||||
"gitea.com/tango/xsrf"
|
||||
|
||||
"goftp.io/server/v2"
|
||||
)
|
||||
|
||||
var _ auther = new(BaseAction)
|
||||
@@ -150,12 +152,8 @@ func (d *DownAction) Get() error {
|
||||
return err
|
||||
}
|
||||
|
||||
driver, err := Factory.NewDriver()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
_, rd, err := driver.GetFile(p, 0)
|
||||
_, rd, err := Driver.GetFile(&server.Context{
|
||||
}, p, 0)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
11
web/perm.go
11
web/perm.go
@@ -6,7 +6,7 @@ import (
|
||||
"path"
|
||||
|
||||
"gitea.com/tango/renders"
|
||||
"goftp.io/server"
|
||||
"goftp.io/server/v2"
|
||||
)
|
||||
|
||||
func hasPerm(mode os.FileMode, idx int, rOrW string) bool {
|
||||
@@ -39,12 +39,9 @@ func (c *PermAction) Get() error {
|
||||
parent = "/"
|
||||
p = "/"
|
||||
}
|
||||
driver, err := Factory.NewDriver()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
var pathinfos []server.FileInfo
|
||||
err = driver.ListDir(p, func(f server.FileInfo) error {
|
||||
var pathinfos []os.FileInfo
|
||||
err = Driver.ListDir(&server.Context{
|
||||
}, p, func(f os.FileInfo) error {
|
||||
if f.Name() != "." {
|
||||
pathinfos = append(pathinfos, f)
|
||||
}
|
||||
|
||||
@@ -13,7 +13,7 @@ import (
|
||||
"gitea.com/tango/session"
|
||||
"gitea.com/tango/xsrf"
|
||||
"github.com/syndtr/goleveldb/leveldb"
|
||||
"goftp.io/server"
|
||||
"goftp.io/server/v2"
|
||||
)
|
||||
|
||||
const (
|
||||
@@ -26,7 +26,7 @@ const (
|
||||
var (
|
||||
DB UserDB
|
||||
Perm server.Perm
|
||||
Factory server.DriverFactory
|
||||
Driver server.Driver
|
||||
adminUser string
|
||||
)
|
||||
|
||||
|
||||
Reference in New Issue
Block a user