ftpd: add basic wildcard support
this is the minimal implementation to allow mget and similar commands with wildcards. We only support wildcard for the last path level, for example: - mget *.xml is supported - mget dir/*.xml is supported - mget */*.xml is not supported Removed . and .. from FTP directory listing Signed-off-by: Nicola Murino <nicola.murino@gmail.com>
This commit is contained in:
parent
9a37e3d159
commit
61947e67ae
6 changed files with 225 additions and 41 deletions
|
@ -57,9 +57,9 @@ func TestBasicFTPHandlingCryptFs(t *testing.T) {
|
|||
}
|
||||
list, err := client.List(".")
|
||||
if assert.NoError(t, err) {
|
||||
assert.Len(t, list, 2)
|
||||
assert.Equal(t, ".", list[0].Name)
|
||||
assert.Equal(t, testFileSize, int64(list[1].Size))
|
||||
if assert.Len(t, list, 1) {
|
||||
assert.Equal(t, testFileSize, int64(list[0].Size))
|
||||
}
|
||||
}
|
||||
user, _, err = httpdtest.GetUserByUsername(user.Username, http.StatusOK)
|
||||
assert.NoError(t, err)
|
||||
|
|
|
@ -550,15 +550,11 @@ func TestBasicFTPHandling(t *testing.T) {
|
|||
}
|
||||
res, err := client.List(path.Join("/", testDir))
|
||||
assert.NoError(t, err)
|
||||
if assert.Len(t, res, 2) {
|
||||
assert.Equal(t, ".", res[0].Name)
|
||||
assert.Equal(t, "..", res[1].Name)
|
||||
}
|
||||
assert.Len(t, res, 0)
|
||||
res, err = client.List(path.Join("/"))
|
||||
assert.NoError(t, err)
|
||||
if assert.Len(t, res, 2) {
|
||||
assert.Equal(t, ".", res[0].Name)
|
||||
assert.Equal(t, testDir, res[1].Name)
|
||||
if assert.Len(t, res, 1) {
|
||||
assert.Equal(t, testDir, res[0].Name)
|
||||
}
|
||||
err = ftpUploadFile(testFilePath, testFileName, testFileSize, client, 0)
|
||||
assert.NoError(t, err)
|
||||
|
@ -597,6 +593,105 @@ func TestBasicFTPHandling(t *testing.T) {
|
|||
50*time.Millisecond)
|
||||
}
|
||||
|
||||
func TestListDirWithWildcards(t *testing.T) {
|
||||
localUser, _, err := httpdtest.AddUser(getTestUser(), http.StatusCreated)
|
||||
assert.NoError(t, err)
|
||||
sftpUser, _, err := httpdtest.AddUser(getTestSFTPUser(), http.StatusCreated)
|
||||
assert.NoError(t, err)
|
||||
for _, user := range []dataprovider.User{localUser, sftpUser} {
|
||||
client, err := getFTPClient(user, true, nil)
|
||||
if assert.NoError(t, err) {
|
||||
dir1 := "test.dir"
|
||||
dir2 := "test.dir1"
|
||||
err = client.MakeDir(dir1)
|
||||
assert.NoError(t, err)
|
||||
err = client.MakeDir(dir2)
|
||||
assert.NoError(t, err)
|
||||
testFilePath := filepath.Join(homeBasePath, testFileName)
|
||||
testFileSize := int64(65535)
|
||||
err = createTestFile(testFilePath, testFileSize)
|
||||
assert.NoError(t, err)
|
||||
fileName := "fil*e.dat"
|
||||
err = ftpUploadFile(testFilePath, fileName, testFileSize, client, 0)
|
||||
assert.NoError(t, err)
|
||||
localDownloadPath := filepath.Join(homeBasePath, testDLFileName)
|
||||
err = ftpDownloadFile(fileName, localDownloadPath, testFileSize, client, 0)
|
||||
assert.NoError(t, err)
|
||||
entries, err := client.NameList(fileName)
|
||||
if assert.NoError(t, err) {
|
||||
assert.Len(t, entries, 1)
|
||||
assert.Contains(t, entries, fileName)
|
||||
}
|
||||
entries, err = client.NameList(".")
|
||||
assert.NoError(t, err)
|
||||
assert.Len(t, entries, 3)
|
||||
entries, err = client.NameList("/test.*")
|
||||
if assert.NoError(t, err) {
|
||||
assert.Len(t, entries, 2)
|
||||
assert.Contains(t, entries, dir1)
|
||||
assert.Contains(t, entries, dir2)
|
||||
}
|
||||
entries, err = client.NameList("/*.dir?")
|
||||
if assert.NoError(t, err) {
|
||||
assert.Len(t, entries, 1)
|
||||
assert.Contains(t, entries, dir2)
|
||||
}
|
||||
entries, err = client.NameList("/test.???")
|
||||
if assert.NoError(t, err) {
|
||||
assert.Len(t, entries, 1)
|
||||
assert.Contains(t, entries, dir1)
|
||||
}
|
||||
_, err = client.NameList("/missingdir/test.*")
|
||||
assert.Error(t, err)
|
||||
_, err = client.NameList("test[-]")
|
||||
if assert.Error(t, err) {
|
||||
assert.Contains(t, err.Error(), path.ErrBadPattern.Error())
|
||||
}
|
||||
subDir := path.Join(dir1, "sub.d")
|
||||
err = client.MakeDir(subDir)
|
||||
assert.NoError(t, err)
|
||||
err = client.ChangeDir(path.Dir(subDir))
|
||||
assert.NoError(t, err)
|
||||
entries, err = client.NameList("sub.?")
|
||||
if assert.NoError(t, err) {
|
||||
assert.Len(t, entries, 1)
|
||||
assert.Contains(t, entries, path.Base(subDir))
|
||||
}
|
||||
entries, err = client.NameList("../*.dir?")
|
||||
if assert.NoError(t, err) {
|
||||
assert.Len(t, entries, 1)
|
||||
assert.Contains(t, entries, path.Join("../", dir2))
|
||||
}
|
||||
err = client.ChangeDir("/")
|
||||
assert.NoError(t, err)
|
||||
entries, err = client.NameList(path.Join(dir1, "sub.*"))
|
||||
if assert.NoError(t, err) {
|
||||
assert.Len(t, entries, 1)
|
||||
assert.Contains(t, entries, path.Join(dir1, "sub.d"))
|
||||
}
|
||||
err = client.RemoveDir(subDir)
|
||||
assert.NoError(t, err)
|
||||
err = client.RemoveDir(dir1)
|
||||
assert.NoError(t, err)
|
||||
err = client.RemoveDir(dir2)
|
||||
assert.NoError(t, err)
|
||||
err = os.Remove(testFilePath)
|
||||
assert.NoError(t, err)
|
||||
err = os.Remove(localDownloadPath)
|
||||
assert.NoError(t, err)
|
||||
err = client.Quit()
|
||||
assert.NoError(t, err)
|
||||
}
|
||||
}
|
||||
|
||||
_, err = httpdtest.RemoveUser(sftpUser, http.StatusOK)
|
||||
assert.NoError(t, err)
|
||||
_, err = httpdtest.RemoveUser(localUser, http.StatusOK)
|
||||
assert.NoError(t, err)
|
||||
err = os.RemoveAll(localUser.GetHomeDir())
|
||||
assert.NoError(t, err)
|
||||
}
|
||||
|
||||
func TestStartDirectory(t *testing.T) {
|
||||
startDir := "/start/dir"
|
||||
u := getTestUser()
|
||||
|
@ -625,12 +720,14 @@ func TestStartDirectory(t *testing.T) {
|
|||
assert.NoError(t, err)
|
||||
entries, err := client.List(".")
|
||||
assert.NoError(t, err)
|
||||
assert.Len(t, entries, 3)
|
||||
|
||||
if assert.Len(t, entries, 1) {
|
||||
assert.Equal(t, testFileName, entries[0].Name)
|
||||
}
|
||||
entries, err = client.List("/")
|
||||
assert.NoError(t, err)
|
||||
assert.Len(t, entries, 2)
|
||||
|
||||
if assert.Len(t, entries, 1) {
|
||||
assert.Equal(t, "start", entries[0].Name)
|
||||
}
|
||||
err = client.ChangeDirToParent()
|
||||
assert.NoError(t, err)
|
||||
currentDir, err = client.CurrentDir()
|
||||
|
|
|
@ -6,6 +6,7 @@ import (
|
|||
"io"
|
||||
"os"
|
||||
"path"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
ftpserver "github.com/fclairamb/ftpserverlib"
|
||||
|
@ -14,7 +15,6 @@ import (
|
|||
"github.com/drakkan/sftpgo/v2/common"
|
||||
"github.com/drakkan/sftpgo/v2/dataprovider"
|
||||
"github.com/drakkan/sftpgo/v2/logger"
|
||||
"github.com/drakkan/sftpgo/v2/util"
|
||||
"github.com/drakkan/sftpgo/v2/vfs"
|
||||
)
|
||||
|
||||
|
@ -147,6 +147,9 @@ func (c *Connection) Stat(name string) (os.FileInfo, error) {
|
|||
|
||||
fi, err := c.DoStat(name, 0, true)
|
||||
if err != nil {
|
||||
if c.isListDirWithWildcards(path.Base(name), err) {
|
||||
return vfs.NewFileInfo(name, true, 0, time.Now(), false), nil
|
||||
}
|
||||
return nil, err
|
||||
}
|
||||
return fi, nil
|
||||
|
@ -276,13 +279,15 @@ func (c *Connection) ReadDir(name string) ([]os.FileInfo, error) {
|
|||
|
||||
files, err := c.ListDir(name)
|
||||
if err != nil {
|
||||
baseName := path.Base(name)
|
||||
if c.isListDirWithWildcards(baseName, err) {
|
||||
// we only support wildcards for the last path level, for example:
|
||||
// - *.xml is supported
|
||||
// - dir*/*.xml is not supported
|
||||
return c.getListDirWithWildcards(path.Dir(name), baseName)
|
||||
}
|
||||
}
|
||||
return files, err
|
||||
}
|
||||
if name != "/" {
|
||||
files = util.PrependFileInfo(files, vfs.NewFileInfo("..", true, 0, time.Now(), false))
|
||||
}
|
||||
files = util.PrependFileInfo(files, vfs.NewFileInfo(".", true, 0, time.Now(), false))
|
||||
return files, nil
|
||||
}
|
||||
|
||||
// GetHandle implements ClientDriverExtentionFileTransfer
|
||||
|
@ -482,3 +487,56 @@ func (c *Connection) handleFTPUploadToExistingFile(fs vfs.Fs, flags int, resolve
|
|||
|
||||
return t, nil
|
||||
}
|
||||
|
||||
func (c *Connection) getListDirWithWildcards(dirName, pattern string) ([]os.FileInfo, error) {
|
||||
files, err := c.ListDir(dirName)
|
||||
if err != nil {
|
||||
return files, err
|
||||
}
|
||||
validIdx := 0
|
||||
relativeBase := getPathRelativeTo(c.clientContext.Path(), dirName)
|
||||
for _, fi := range files {
|
||||
match, err := path.Match(pattern, fi.Name())
|
||||
if err != nil {
|
||||
return files, err
|
||||
}
|
||||
if match {
|
||||
files[validIdx] = vfs.NewFileInfo(path.Join(relativeBase, fi.Name()), fi.IsDir(), fi.Size(),
|
||||
fi.ModTime(), true)
|
||||
validIdx++
|
||||
}
|
||||
}
|
||||
|
||||
return files[:validIdx], nil
|
||||
}
|
||||
|
||||
func (c *Connection) isListDirWithWildcards(name string, err error) bool {
|
||||
if errors.Is(err, c.GetNotExistError()) {
|
||||
lastCommand := c.clientContext.GetLastCommand()
|
||||
if lastCommand == "LIST" || lastCommand == "NLST" {
|
||||
return strings.ContainsAny(name, "*?[]")
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func getPathRelativeTo(base, target string) string {
|
||||
var sb strings.Builder
|
||||
for {
|
||||
if base == target {
|
||||
return sb.String()
|
||||
}
|
||||
if !strings.HasSuffix(base, "/") {
|
||||
base += "/"
|
||||
}
|
||||
if strings.HasPrefix(target, base) {
|
||||
sb.WriteString(strings.TrimPrefix(target, base))
|
||||
return sb.String()
|
||||
}
|
||||
if base == "/" || base == "./" {
|
||||
return target
|
||||
}
|
||||
sb.WriteString("../")
|
||||
base = path.Dir(path.Clean(base))
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1002,3 +1002,28 @@ func TestPassiveIPResolver(t *testing.T) {
|
|||
assert.NoError(t, err)
|
||||
assert.Equal(t, b.ForcePassiveIP, passiveIP)
|
||||
}
|
||||
|
||||
func TestRelativePath(t *testing.T) {
|
||||
rel := getPathRelativeTo("/testpath", "/testpath")
|
||||
assert.Empty(t, rel)
|
||||
rel = getPathRelativeTo("/", "/")
|
||||
assert.Empty(t, rel)
|
||||
rel = getPathRelativeTo("/", "/dir/sub")
|
||||
assert.Equal(t, "dir/sub", rel)
|
||||
rel = getPathRelativeTo("./", "/dir/sub")
|
||||
assert.Equal(t, "/dir/sub", rel)
|
||||
rel = getPathRelativeTo("/sub", "/dir/sub")
|
||||
assert.Equal(t, "../dir/sub", rel)
|
||||
rel = getPathRelativeTo("/dir", "/dir/sub")
|
||||
assert.Equal(t, "sub", rel)
|
||||
rel = getPathRelativeTo("/dir/sub", "/dir")
|
||||
assert.Equal(t, "../", rel)
|
||||
rel = getPathRelativeTo("dir", "/dir1")
|
||||
assert.Equal(t, "/dir1", rel)
|
||||
rel = getPathRelativeTo("", "/dir2")
|
||||
assert.Equal(t, "dir2", rel)
|
||||
rel = getPathRelativeTo(".", "/dir2")
|
||||
assert.Equal(t, "/dir2", rel)
|
||||
rel = getPathRelativeTo("/dir3", "dir3")
|
||||
assert.Equal(t, "dir3", rel)
|
||||
}
|
||||
|
|
16
go.mod
16
go.mod
|
@ -12,9 +12,9 @@ require (
|
|||
github.com/aws/aws-sdk-go-v2/config v1.15.4
|
||||
github.com/aws/aws-sdk-go-v2/credentials v1.12.0
|
||||
github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.12.4
|
||||
github.com/aws/aws-sdk-go-v2/feature/s3/manager v1.11.7
|
||||
github.com/aws/aws-sdk-go-v2/feature/s3/manager v1.11.8
|
||||
github.com/aws/aws-sdk-go-v2/service/marketplacemetering v1.13.4
|
||||
github.com/aws/aws-sdk-go-v2/service/s3 v1.26.7
|
||||
github.com/aws/aws-sdk-go-v2/service/s3 v1.26.8
|
||||
github.com/aws/aws-sdk-go-v2/service/secretsmanager v1.15.6
|
||||
github.com/aws/aws-sdk-go-v2/service/sts v1.16.4
|
||||
github.com/cockroachdb/cockroach-go/v2 v2.2.8
|
||||
|
@ -65,16 +65,16 @@ require (
|
|||
go.uber.org/automaxprocs v1.5.1
|
||||
gocloud.dev v0.25.0
|
||||
golang.org/x/crypto v0.0.0-20220411220226-7b82a4e95df4
|
||||
golang.org/x/net v0.0.0-20220421235706-1d1ef9303861
|
||||
golang.org/x/net v0.0.0-20220425223048-2871e0cb64e4
|
||||
golang.org/x/oauth2 v0.0.0-20220411215720-9780585627b5
|
||||
golang.org/x/sys v0.0.0-20220429233432-b5fbb4746d32
|
||||
golang.org/x/sys v0.0.0-20220503163025-988cb79eb6c6
|
||||
golang.org/x/time v0.0.0-20220411224347-583f2d630306
|
||||
google.golang.org/api v0.77.0
|
||||
google.golang.org/api v0.78.0
|
||||
gopkg.in/natefinch/lumberjack.v2 v2.0.0
|
||||
)
|
||||
|
||||
require (
|
||||
cloud.google.com/go v0.101.0 // indirect
|
||||
cloud.google.com/go v0.101.1 // indirect
|
||||
cloud.google.com/go/compute v1.6.1 // indirect
|
||||
cloud.google.com/go/iam v0.3.0 // indirect
|
||||
github.com/Azure/azure-sdk-for-go/sdk/internal v0.9.2 // indirect
|
||||
|
@ -152,7 +152,7 @@ require (
|
|||
golang.org/x/tools v0.1.10 // indirect
|
||||
golang.org/x/xerrors v0.0.0-20220411194840-2f41105eb62f // indirect
|
||||
google.golang.org/appengine v1.6.7 // indirect
|
||||
google.golang.org/genproto v0.0.0-20220429170224-98d788798c3e // indirect
|
||||
google.golang.org/genproto v0.0.0-20220504150022-98cd25cafc72 // indirect
|
||||
google.golang.org/grpc v1.46.0 // indirect
|
||||
google.golang.org/protobuf v1.28.0 // indirect
|
||||
gopkg.in/ini.v1 v1.66.4 // indirect
|
||||
|
@ -164,5 +164,5 @@ require (
|
|||
replace (
|
||||
github.com/jlaffaye/ftp => github.com/drakkan/ftp v0.0.0-20201114075148-9b9adce499a9
|
||||
golang.org/x/crypto => github.com/drakkan/crypto v0.0.0-20220412172350-e76a61f8e7d2
|
||||
golang.org/x/net => github.com/drakkan/net v0.0.0-20220425150817-e26421ba5d2e
|
||||
golang.org/x/net => github.com/drakkan/net v0.0.0-20220504073018-5d1702f4106f
|
||||
)
|
||||
|
|
28
go.sum
28
go.sum
|
@ -31,8 +31,8 @@ cloud.google.com/go v0.97.0/go.mod h1:GF7l59pYBVlXQIBLx3a761cZ41F9bBH3JUlihCt2Ud
|
|||
cloud.google.com/go v0.99.0/go.mod h1:w0Xx2nLzqWJPuozYQX+hFfCSI8WioryfRDzkoI/Y2ZA=
|
||||
cloud.google.com/go v0.100.1/go.mod h1:fs4QogzfH5n2pBXBP9vRiU+eCny7lD2vmFZy79Iuw1U=
|
||||
cloud.google.com/go v0.100.2/go.mod h1:4Xra9TjzAeYHrl5+oeLlzbM2k3mjVhZh4UqTZ//w99A=
|
||||
cloud.google.com/go v0.101.0 h1:g+LL+JvpvdyGtcaD2xw2mSByE/6F9s471eJSoaysM84=
|
||||
cloud.google.com/go v0.101.0/go.mod h1:hEiddgDb77jDQ+I80tURYNJEnuwPzFU8awCFFRLKjW0=
|
||||
cloud.google.com/go v0.101.1 h1:3+/0TAm9JD/PyhkrDWQWi2L197h3euCsM+H+J4iYTR8=
|
||||
cloud.google.com/go v0.101.1/go.mod h1:55HwjsGW4CHD3JrNuMdZtSDsgTs0CuCB/bBTugD+7AA=
|
||||
cloud.google.com/go/bigquery v1.0.1/go.mod h1:i/xbL2UlR5RvWAURpBYZTtm/cXjCha9lbfbpx4poX+o=
|
||||
cloud.google.com/go/bigquery v1.3.0/go.mod h1:PjpwJnslEMmckchkHFfq+HTD2DmtT67aNFKH1/VBDHE=
|
||||
cloud.google.com/go/bigquery v1.4.0/go.mod h1:S8dzgnTigyfTmLBfrtrhyYhwRxG72rYxvftPBK2Dvzc=
|
||||
|
@ -150,8 +150,8 @@ github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.12.3/go.mod h1:uk1vhHHERfSVCUnq
|
|||
github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.12.4 h1:FP8gquGeGHHdfY6G5llaMQDF+HAf20VKc8opRwmjf04=
|
||||
github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.12.4/go.mod h1:u/s5/Z+ohUQOPXl00m2yJVyioWDECsbpXTQlaqSlufc=
|
||||
github.com/aws/aws-sdk-go-v2/feature/s3/manager v1.11.3/go.mod h1:0dHuD2HZZSiwfJSy1FO5bX1hQ1TxVV1QXXjpn3XUE44=
|
||||
github.com/aws/aws-sdk-go-v2/feature/s3/manager v1.11.7 h1:h1y9Jn+1VTEjB4TG5prxtQjW9DM5o8y7Cu9ZdNmkXWA=
|
||||
github.com/aws/aws-sdk-go-v2/feature/s3/manager v1.11.7/go.mod h1:NZ2wPktB/I111CyzF3ezVf8jrAg/PqKeYkdR11oBWeU=
|
||||
github.com/aws/aws-sdk-go-v2/feature/s3/manager v1.11.8 h1:DrnslKj0yz31roe7+tmMblGhbr6OV7VQUJo5ylBU9YQ=
|
||||
github.com/aws/aws-sdk-go-v2/feature/s3/manager v1.11.8/go.mod h1:PCN2NuDpHRYLcO2Gl9yVToeahcRBkf+87inUVGwtM+Q=
|
||||
github.com/aws/aws-sdk-go-v2/internal/configsources v1.1.9/go.mod h1:AnVH5pvai0pAF4lXRq0bmhbes1u9R8wTE+g+183bZNM=
|
||||
github.com/aws/aws-sdk-go-v2/internal/configsources v1.1.10 h1:uFWgo6mGJI1n17nbcvSc6fxVuR3xLNqvXt12JCnEcT8=
|
||||
github.com/aws/aws-sdk-go-v2/internal/configsources v1.1.10/go.mod h1:F+EZtuIwjlv35kRJPyBGcsA4f7bnSoz15zOQ2lJq1Z4=
|
||||
|
@ -178,8 +178,8 @@ github.com/aws/aws-sdk-go-v2/service/kms v1.16.3/go.mod h1:QuiHPBqlOFCi4LqdSskYY
|
|||
github.com/aws/aws-sdk-go-v2/service/marketplacemetering v1.13.4 h1:qmHavnjRtgdH54nyG4iEk6ZCde9m2S++32INurhaNTk=
|
||||
github.com/aws/aws-sdk-go-v2/service/marketplacemetering v1.13.4/go.mod h1:CloMDruFIVZJ8qv2OsY5ENIqzg5c0eeTciVVW3KHdvE=
|
||||
github.com/aws/aws-sdk-go-v2/service/s3 v1.26.3/go.mod h1:g1qvDuRsJY+XghsV6zg00Z4KJ7DtFFCx8fJD2a491Ak=
|
||||
github.com/aws/aws-sdk-go-v2/service/s3 v1.26.7 h1:ZEPH6aBywdyn5LGr7hSNEwuPaKpKZodX0R9AjPj5A7c=
|
||||
github.com/aws/aws-sdk-go-v2/service/s3 v1.26.7/go.mod h1:iMYipLPXlWpBJ0KFX7QJHZ84rBydHBY8as2aQICTPWk=
|
||||
github.com/aws/aws-sdk-go-v2/service/s3 v1.26.8 h1:XInp7WokGrNBYC+5rm9npWC0vrF2xtfNfM/qozGI6U0=
|
||||
github.com/aws/aws-sdk-go-v2/service/s3 v1.26.8/go.mod h1:iMYipLPXlWpBJ0KFX7QJHZ84rBydHBY8as2aQICTPWk=
|
||||
github.com/aws/aws-sdk-go-v2/service/secretsmanager v1.15.4/go.mod h1:PJc8s+lxyU8rrre0/4a0pn2wgwiDvOEzoOjcJUBr67o=
|
||||
github.com/aws/aws-sdk-go-v2/service/secretsmanager v1.15.6 h1:m+mxqLIrGq7GJo5qw4rHn8BbUqHrvxvwFx54N1Pglvw=
|
||||
github.com/aws/aws-sdk-go-v2/service/secretsmanager v1.15.6/go.mod h1:Z+i6uqZgCOBXhNoEGoRm/ZaLsaJA9rGUAmkVKM/3+g4=
|
||||
|
@ -255,8 +255,8 @@ github.com/drakkan/crypto v0.0.0-20220412172350-e76a61f8e7d2 h1:5XmEywX1u5gPgOC+
|
|||
github.com/drakkan/crypto v0.0.0-20220412172350-e76a61f8e7d2/go.mod h1:SiM6ypd8Xu1xldObYtbDztuUU7xUzMnUULfphXFZmro=
|
||||
github.com/drakkan/ftp v0.0.0-20201114075148-9b9adce499a9 h1:LPH1dEblAOO/LoG7yHPMtBLXhQmjaga91/DDjWk9jWA=
|
||||
github.com/drakkan/ftp v0.0.0-20201114075148-9b9adce499a9/go.mod h1:2lmrmq866uF2tnje75wQHzmPXhmSWUt7Gyx2vgK1RCU=
|
||||
github.com/drakkan/net v0.0.0-20220425150817-e26421ba5d2e h1:QCodCQfdiYnXHR+1nnzi69+tM6FEThtzthP8ZhpHkrk=
|
||||
github.com/drakkan/net v0.0.0-20220425150817-e26421ba5d2e/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk=
|
||||
github.com/drakkan/net v0.0.0-20220504073018-5d1702f4106f h1:BUIQ/l47ZPdruH1S17AdCCq6YNR/DyLpvysRXikD/Xg=
|
||||
github.com/drakkan/net v0.0.0-20220504073018-5d1702f4106f/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk=
|
||||
github.com/eikenb/pipeat v0.0.0-20210730190139-06b3e6902001 h1:/ZshrfQzayqRSBDodmp3rhNCHJCff+utvgBuWRbiqu4=
|
||||
github.com/eikenb/pipeat v0.0.0-20210730190139-06b3e6902001/go.mod h1:kltMsfRMTHSFdMbK66XdS8mfMW77+FZA1fGY1xYMF84=
|
||||
github.com/envoyproxy/go-control-plane v0.9.0/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4=
|
||||
|
@ -965,8 +965,9 @@ golang.org/x/sys v0.0.0-20220227234510-4e6760a101f9/go.mod h1:oPkhp1MJrh7nUepCBc
|
|||
golang.org/x/sys v0.0.0-20220328115105-d36c6a25d886/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20220330033206-e17cdc41300f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20220412211240-33da011f77ad/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20220429233432-b5fbb4746d32 h1:Js08h5hqB5xyWR789+QqueR6sDE8mk+YvpETZ+F6X9Y=
|
||||
golang.org/x/sys v0.0.0-20220429233432-b5fbb4746d32/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20220502124256-b6088ccd6cba/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20220503163025-988cb79eb6c6 h1:nonptSpoQ4vQjyraW20DXPAglgQfVnM9ZC6MmNLMR60=
|
||||
golang.org/x/sys v0.0.0-20220503163025-988cb79eb6c6/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
|
||||
golang.org/x/term v0.0.0-20210927222741-03fcf44c2211 h1:JGgROgKl9N8DuW20oFS5gxc+lE67/N3FcwmBPMe7ArY=
|
||||
golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
|
||||
|
@ -1102,8 +1103,9 @@ google.golang.org/api v0.70.0/go.mod h1:Bs4ZM2HGifEvXwd50TtW70ovgJffJYw2oRCOFU/S
|
|||
google.golang.org/api v0.71.0/go.mod h1:4PyU6e6JogV1f9eA4voyrTY2batOLdgZ5qZ5HOCc4j8=
|
||||
google.golang.org/api v0.74.0/go.mod h1:ZpfMZOVRMywNyvJFeqL9HRWBgAuRfSjJFpe9QtRRyDs=
|
||||
google.golang.org/api v0.75.0/go.mod h1:pU9QmyHLnzlpar1Mjt4IbapUCy8J+6HD6GeELN69ljA=
|
||||
google.golang.org/api v0.77.0 h1:msijLTxwkJ7Jub5tv9KBVCKtHOQwnvnvkX7ErFFCVxY=
|
||||
google.golang.org/api v0.77.0/go.mod h1:pU9QmyHLnzlpar1Mjt4IbapUCy8J+6HD6GeELN69ljA=
|
||||
google.golang.org/api v0.78.0 h1:5ewPyCwP43C3i8B6C2Kb+eVAevbnke2xR8VbcSWjS4I=
|
||||
google.golang.org/api v0.78.0/go.mod h1:1Sg78yoMLOhlQTeF+ARBoytAcH1NNyyl390YMy6rKmw=
|
||||
google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM=
|
||||
google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=
|
||||
google.golang.org/appengine v1.5.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=
|
||||
|
@ -1203,8 +1205,10 @@ google.golang.org/genproto v0.0.0-20220407144326-9054f6ed7bac/go.mod h1:8w6bsBMX
|
|||
google.golang.org/genproto v0.0.0-20220413183235-5e96e2839df9/go.mod h1:8w6bsBMX6yCPbAVTeqQHvzxW0EIFigd5lZyahWgyfDo=
|
||||
google.golang.org/genproto v0.0.0-20220414192740-2d67ff6cf2b4/go.mod h1:8w6bsBMX6yCPbAVTeqQHvzxW0EIFigd5lZyahWgyfDo=
|
||||
google.golang.org/genproto v0.0.0-20220421151946-72621c1f0bd3/go.mod h1:8w6bsBMX6yCPbAVTeqQHvzxW0EIFigd5lZyahWgyfDo=
|
||||
google.golang.org/genproto v0.0.0-20220429170224-98d788798c3e h1:gMjH4zLGs9m+dGzR7qHCHaXMOwsJHJKKkHtyXhtOrJk=
|
||||
google.golang.org/genproto v0.0.0-20220429170224-98d788798c3e/go.mod h1:8w6bsBMX6yCPbAVTeqQHvzxW0EIFigd5lZyahWgyfDo=
|
||||
google.golang.org/genproto v0.0.0-20220502173005-c8bf987b8c21/go.mod h1:RAyBrSAP7Fh3Nc84ghnVLDPuV51xc9agzmm4Ph6i0Q4=
|
||||
google.golang.org/genproto v0.0.0-20220504150022-98cd25cafc72 h1:iif0mpUetMBqcQPUoq+JnCcmzvfpp8wRx515va8wP1c=
|
||||
google.golang.org/genproto v0.0.0-20220504150022-98cd25cafc72/go.mod h1:RAyBrSAP7Fh3Nc84ghnVLDPuV51xc9agzmm4Ph6i0Q4=
|
||||
google.golang.org/grpc v1.8.0/go.mod h1:yo6s7OP7yaDglbqo1J04qKzAhqBH6lvTonzMVmEdcZw=
|
||||
google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c=
|
||||
google.golang.org/grpc v1.20.1/go.mod h1:10oTOabMzJvdu6/UiuZezV6QK5dSlG84ov/aaiqXj38=
|
||||
|
|
Loading…
Reference in a new issue