From 61947e67ae4900f965cc63fe4c8a7b35d12de79c Mon Sep 17 00:00:00 2001 From: Nicola Murino Date: Wed, 4 May 2022 19:32:02 +0200 Subject: [PATCH] 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 --- ftpd/cryptfs_test.go | 6 +-- ftpd/ftpd_test.go | 119 ++++++++++++++++++++++++++++++++++++++---- ftpd/handler.go | 72 ++++++++++++++++++++++--- ftpd/internal_test.go | 25 +++++++++ go.mod | 16 +++--- go.sum | 28 +++++----- 6 files changed, 225 insertions(+), 41 deletions(-) diff --git a/ftpd/cryptfs_test.go b/ftpd/cryptfs_test.go index a1a8a238..105d6d3b 100644 --- a/ftpd/cryptfs_test.go +++ b/ftpd/cryptfs_test.go @@ -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) diff --git a/ftpd/ftpd_test.go b/ftpd/ftpd_test.go index 75c237ce..7d58b876 100644 --- a/ftpd/ftpd_test.go +++ b/ftpd/ftpd_test.go @@ -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() diff --git a/ftpd/handler.go b/ftpd/handler.go index 65083328..a99e12db 100644 --- a/ftpd/handler.go +++ b/ftpd/handler.go @@ -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 { - return files, err + 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) + } } - 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 + return files, err } // 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)) + } +} diff --git a/ftpd/internal_test.go b/ftpd/internal_test.go index b428ac18..2af3313f 100644 --- a/ftpd/internal_test.go +++ b/ftpd/internal_test.go @@ -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) +} diff --git a/go.mod b/go.mod index 2d3e8955..a34ccd3d 100644 --- a/go.mod +++ b/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 ) diff --git a/go.sum b/go.sum index 1d82bf0e..19b4428a 100644 --- a/go.sum +++ b/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=