diff options
Diffstat (limited to 'vendor/github.com')
35 files changed, 779 insertions, 241 deletions
diff --git a/vendor/github.com/acarl005/stripansi/LICENSE b/vendor/github.com/acarl005/stripansi/LICENSE new file mode 100644 index 000000000..00abe0dbf --- /dev/null +++ b/vendor/github.com/acarl005/stripansi/LICENSE @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2018 Andrew Carlson + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/vendor/github.com/acarl005/stripansi/README.md b/vendor/github.com/acarl005/stripansi/README.md new file mode 100644 index 000000000..8bdb1f505 --- /dev/null +++ b/vendor/github.com/acarl005/stripansi/README.md @@ -0,0 +1,30 @@ +Strip ANSI +========== + +This Go package removes ANSI escape codes from strings. + +Ideally, we would prevent these from appearing in any text we want to process. +However, sometimes this can't be helped, and we need to be able to deal with that noise. +This will use a regexp to remove those unwanted escape codes. + + +## Install + +```sh +$ go get -u github.com/acarl005/stripansi +``` + +## Usage + +```go +import ( + "fmt" + "github.com/acarl005/stripansi" +) + +func main() { + msg := "\x1b[38;5;140m foo\x1b[0m bar" + cleanMsg := stripansi.Strip(msg) + fmt.Println(cleanMsg) // " foo bar" +} +``` diff --git a/vendor/github.com/acarl005/stripansi/stripansi.go b/vendor/github.com/acarl005/stripansi/stripansi.go new file mode 100644 index 000000000..235732a78 --- /dev/null +++ b/vendor/github.com/acarl005/stripansi/stripansi.go @@ -0,0 +1,13 @@ +package stripansi + +import ( + "regexp" +) + +const ansi = "[\u001B\u009B][[\\]()#;?]*(?:(?:(?:[a-zA-Z\\d]*(?:;[a-zA-Z\\d]*)*)?\u0007)|(?:(?:\\d{1,4}(?:;\\d{0,4})*)?[\\dA-PRZcf-ntqry=><~]))" + +var re = regexp.MustCompile(ansi) + +func Strip(str string) string { + return re.ReplaceAllString(str, "") +} diff --git a/vendor/github.com/containers/image/v5/copy/copy.go b/vendor/github.com/containers/image/v5/copy/copy.go index 29660b6b2..36957fc77 100644 --- a/vendor/github.com/containers/image/v5/copy/copy.go +++ b/vendor/github.com/containers/image/v5/copy/copy.go @@ -709,7 +709,7 @@ func checkImageDestinationForCurrentRuntime(ctx context.Context, sys *types.Syst wantedOS = sys.OSChoice } if wantedOS != c.OS { - return fmt.Errorf("Image operating system mismatch: image uses %q, expecting %q", c.OS, wantedOS) + logrus.Infof("Image operating system mismatch: image uses %q, expecting %q", c.OS, wantedOS) } wantedArch := runtime.GOARCH @@ -717,7 +717,7 @@ func checkImageDestinationForCurrentRuntime(ctx context.Context, sys *types.Syst wantedArch = sys.ArchitectureChoice } if wantedArch != c.Architecture { - return fmt.Errorf("Image architecture mismatch: image uses %q, expecting %q", c.Architecture, wantedArch) + logrus.Infof("Image architecture mismatch: image uses %q, expecting %q", c.Architecture, wantedArch) } } return nil diff --git a/vendor/github.com/containers/image/v5/directory/directory_dest.go b/vendor/github.com/containers/image/v5/directory/directory_dest.go index caa7a207f..d70b6c07f 100644 --- a/vendor/github.com/containers/image/v5/directory/directory_dest.go +++ b/vendor/github.com/containers/image/v5/directory/directory_dest.go @@ -6,6 +6,7 @@ import ( "io/ioutil" "os" "path/filepath" + "runtime" "github.com/containers/image/v5/types" "github.com/opencontainers/go-digest" @@ -142,8 +143,11 @@ func (d *dirImageDestination) PutBlob(ctx context.Context, stream io.Reader, inp return types.BlobInfo{}, err } succeeded := false + explicitClosed := false defer func() { - blobFile.Close() + if !explicitClosed { + blobFile.Close() + } if !succeeded { os.Remove(blobFile.Name()) } @@ -164,10 +168,21 @@ func (d *dirImageDestination) PutBlob(ctx context.Context, stream io.Reader, inp if err := blobFile.Sync(); err != nil { return types.BlobInfo{}, err } - if err := blobFile.Chmod(0644); err != nil { - return types.BlobInfo{}, err + + // On POSIX systems, blobFile was created with mode 0600, so we need to make it readable. + // On Windows, the “permissions of newly created files” argument to syscall.Open is + // ignored and the file is already readable; besides, blobFile.Chmod, i.e. syscall.Fchmod, + // always fails on Windows. + if runtime.GOOS != "windows" { + if err := blobFile.Chmod(0644); err != nil { + return types.BlobInfo{}, err + } } + blobPath := d.ref.layerPath(computedDigest) + // need to explicitly close the file, since a rename won't otherwise not work on Windows + blobFile.Close() + explicitClosed = true if err := os.Rename(blobFile.Name(), blobPath); err != nil { return types.BlobInfo{}, err } diff --git a/vendor/github.com/containers/image/v5/docker/docker_client.go b/vendor/github.com/containers/image/v5/docker/docker_client.go index 986bcb986..aa8463d18 100644 --- a/vendor/github.com/containers/image/v5/docker/docker_client.go +++ b/vendor/github.com/containers/image/v5/docker/docker_client.go @@ -6,7 +6,6 @@ import ( "encoding/json" "fmt" "io" - "io/ioutil" "net/http" "net/url" "os" @@ -17,6 +16,7 @@ import ( "time" "github.com/containers/image/v5/docker/reference" + "github.com/containers/image/v5/internal/iolimits" "github.com/containers/image/v5/pkg/docker/config" "github.com/containers/image/v5/pkg/sysregistriesv2" "github.com/containers/image/v5/pkg/tlsclientconfig" @@ -45,6 +45,10 @@ const ( extensionSignatureSchemaVersion = 2 // extensionSignature.Version extensionSignatureTypeAtomic = "atomic" // extensionSignature.Type + + backoffNumIterations = 5 + backoffInitialDelay = 2 * time.Second + backoffMaxDelay = 60 * time.Second ) var systemPerHostCertDirPaths = [2]string{"/etc/containers/certs.d", "/etc/docker/certs.d"} @@ -277,7 +281,7 @@ func CheckAuth(ctx context.Context, sys *types.SystemContext, username, password } defer resp.Body.Close() - return httpResponseToError(resp) + return httpResponseToError(resp, "") } // SearchResult holds the information of each matching image @@ -351,7 +355,7 @@ func SearchRegistry(ctx context.Context, sys *types.SystemContext, registry, ima } else { defer resp.Body.Close() if resp.StatusCode != http.StatusOK { - logrus.Debugf("error getting search results from v1 endpoint %q: %v", registry, httpResponseToError(resp)) + logrus.Debugf("error getting search results from v1 endpoint %q: %v", registry, httpResponseToError(resp, "")) } else { if err := json.NewDecoder(resp.Body).Decode(v1Res); err != nil { return nil, err @@ -368,7 +372,7 @@ func SearchRegistry(ctx context.Context, sys *types.SystemContext, registry, ima } else { defer resp.Body.Close() if resp.StatusCode != http.StatusOK { - logrus.Errorf("error getting search results from v2 endpoint %q: %v", registry, httpResponseToError(resp)) + logrus.Errorf("error getting search results from v2 endpoint %q: %v", registry, httpResponseToError(resp, "")) } else { if err := json.NewDecoder(resp.Body).Decode(v2Res); err != nil { return nil, err @@ -400,74 +404,64 @@ func (c *dockerClient) makeRequest(ctx context.Context, method, path string, hea return c.makeRequestToResolvedURL(ctx, method, url, headers, stream, -1, auth, extraScope) } +// parseRetryAfter determines the delay required by the "Retry-After" header in res and returns it, +// silently falling back to fallbackDelay if the header is missing or invalid. +func parseRetryAfter(res *http.Response, fallbackDelay time.Duration) time.Duration { + after := res.Header.Get("Retry-After") + if after == "" { + return fallbackDelay + } + logrus.Debugf("Detected 'Retry-After' header %q", after) + // First, check if we have a numerical value. + if num, err := strconv.ParseInt(after, 10, 64); err == nil { + return time.Duration(num) * time.Second + } + // Second, check if we have an HTTP date. + // If the delta between the date and now is positive, use it. + // Otherwise, fall back to using the default exponential back off. + if t, err := http.ParseTime(after); err == nil { + delta := time.Until(t) + if delta > 0 { + return delta + } + logrus.Debugf("Retry-After date in the past, ignoring it") + return fallbackDelay + } + // If the header contents are bogus, fall back to using the default exponential back off. + logrus.Debugf("Invalid Retry-After format, ignoring it") + return fallbackDelay +} + // makeRequestToResolvedURL creates and executes a http.Request with the specified parameters, adding authentication and TLS options for the Docker client. // streamLen, if not -1, specifies the length of the data expected on stream. // makeRequest should generally be preferred. -// In case of an http 429 status code in the response, it performs an exponential back off starting at 2 seconds for at most 5 iterations. -// If the `Retry-After` header is set in the response, the specified value or date is -// If the stream is non-nil, no back off will be performed. +// In case of an HTTP 429 status code in the response, it may automatically retry a few times. // TODO(runcom): too many arguments here, use a struct func (c *dockerClient) makeRequestToResolvedURL(ctx context.Context, method, url string, headers map[string][]string, stream io.Reader, streamLen int64, auth sendAuth, extraScope *authScope) (*http.Response, error) { - var ( - res *http.Response - err error - delay int64 - ) - delay = 2 - const numIterations = 5 - const maxDelay = 60 - - // math.Min() only supports float64, so have an anonymous func to avoid - // casting. - min := func(a int64, b int64) int64 { - if a < b { - return a + delay := backoffInitialDelay + attempts := 0 + for { + res, err := c.makeRequestToResolvedURLOnce(ctx, method, url, headers, stream, streamLen, auth, extraScope) + attempts++ + if res == nil || res.StatusCode != http.StatusTooManyRequests || // Only retry on StatusTooManyRequests, success or other failure is returned to caller immediately + stream != nil || // We can't retry with a body (which is not restartable in the general case) + attempts == backoffNumIterations { + return res, err } - return b - } - nextDelay := func(r *http.Response, delay int64) int64 { - after := res.Header.Get("Retry-After") - if after == "" { - return min(delay, maxDelay) - } - logrus.Debugf("detected 'Retry-After' header %q", after) - // First check if we have a numerical value. - if num, err := strconv.ParseInt(after, 10, 64); err == nil { - return min(num, maxDelay) + delay = parseRetryAfter(res, delay) + if delay > backoffMaxDelay { + delay = backoffMaxDelay } - // Secondly check if we have an http date. - // If the delta between the date and now is positive, use it. - // Otherwise, fall back to using the default exponential back off. - if t, err := http.ParseTime(after); err == nil { - delta := int64(time.Until(t).Seconds()) - if delta > 0 { - return min(delta, maxDelay) - } - logrus.Debugf("negative date: falling back to using %d seconds", delay) - return min(delay, maxDelay) + logrus.Debugf("Too many requests to %s: sleeping for %f seconds before next attempt", url, delay.Seconds()) + select { + case <-ctx.Done(): + return nil, ctx.Err() + case <-time.After(delay): + // Nothing } - // If the header contains bogus, fall back to using the default - // exponential back off. - logrus.Debugf("invalid format: falling back to using %d seconds", delay) - return min(delay, maxDelay) - } - - for i := 0; i < numIterations; i++ { - res, err = c.makeRequestToResolvedURLOnce(ctx, method, url, headers, stream, streamLen, auth, extraScope) - if stream == nil && res != nil && res.StatusCode == http.StatusTooManyRequests { - if i < numIterations-1 { - logrus.Errorf("HEADER %v", res.Header) - delay = nextDelay(res, delay) // compute next delay - does NOT exceed maxDelay - logrus.Debugf("too many request to %s: sleeping for %d seconds before next attempt", url, delay) - time.Sleep(time.Duration(delay) * time.Second) - delay = delay * 2 // exponential back off - } - continue - } - break + delay = delay * 2 // exponential back off } - return res, err } // makeRequestToResolvedURLOnce creates and executes a http.Request with the specified parameters, adding authentication and TLS options for the Docker client. @@ -597,7 +591,7 @@ func (c *dockerClient) getBearerToken(ctx context.Context, challenge challenge, default: return nil, errors.Errorf("unexpected http code: %d (%s), URL: %s", res.StatusCode, http.StatusText(res.StatusCode), authReq.URL) } - tokenBlob, err := ioutil.ReadAll(res.Body) + tokenBlob, err := iolimits.ReadAtMost(res.Body, iolimits.MaxAuthTokenBodySize) if err != nil { return nil, err } @@ -627,7 +621,7 @@ func (c *dockerClient) detectPropertiesHelper(ctx context.Context) error { defer resp.Body.Close() logrus.Debugf("Ping %s status %d", url, resp.StatusCode) if resp.StatusCode != http.StatusOK && resp.StatusCode != http.StatusUnauthorized { - return httpResponseToError(resp) + return httpResponseToError(resp, "") } c.challenges = parseAuthHeader(resp.Header) c.scheme = scheme @@ -690,7 +684,7 @@ func (c *dockerClient) getExtensionsSignatures(ctx context.Context, ref dockerRe return nil, errors.Wrapf(clientLib.HandleErrorResponse(res), "Error downloading signatures for %s in %s", manifestDigest, ref.ref.Name()) } - body, err := ioutil.ReadAll(res.Body) + body, err := iolimits.ReadAtMost(res.Body, iolimits.MaxSignatureListBodySize) if err != nil { return nil, err } diff --git a/vendor/github.com/containers/image/v5/docker/docker_image.go b/vendor/github.com/containers/image/v5/docker/docker_image.go index dad382cd0..483581dbc 100644 --- a/vendor/github.com/containers/image/v5/docker/docker_image.go +++ b/vendor/github.com/containers/image/v5/docker/docker_image.go @@ -70,7 +70,7 @@ func GetRepositoryTags(ctx context.Context, sys *types.SystemContext, ref types. return nil, err } defer res.Body.Close() - if err := httpResponseToError(res); err != nil { + if err := httpResponseToError(res, "Error fetching tags list"); err != nil { return nil, err } diff --git a/vendor/github.com/containers/image/v5/docker/docker_image_dest.go b/vendor/github.com/containers/image/v5/docker/docker_image_dest.go index 47a73d868..ab74e1607 100644 --- a/vendor/github.com/containers/image/v5/docker/docker_image_dest.go +++ b/vendor/github.com/containers/image/v5/docker/docker_image_dest.go @@ -15,6 +15,7 @@ import ( "strings" "github.com/containers/image/v5/docker/reference" + "github.com/containers/image/v5/internal/iolimits" "github.com/containers/image/v5/manifest" "github.com/containers/image/v5/pkg/blobinfocache/none" "github.com/containers/image/v5/types" @@ -58,14 +59,16 @@ func (d *dockerImageDestination) Close() error { } func (d *dockerImageDestination) SupportedManifestMIMETypes() []string { - return []string{ + mimeTypes := []string{ imgspecv1.MediaTypeImageManifest, manifest.DockerV2Schema2MediaType, imgspecv1.MediaTypeImageIndex, manifest.DockerV2ListMediaType, - manifest.DockerV2Schema1SignedMediaType, - manifest.DockerV2Schema1MediaType, } + if d.c.sys == nil || !d.c.sys.DockerDisableDestSchema1MIMETypes { + mimeTypes = append(mimeTypes, manifest.DockerV2Schema1SignedMediaType, manifest.DockerV2Schema1MediaType) + } + return mimeTypes } // SupportsSignatures returns an error (to be displayed to the user) if the destination certainly can't store signatures. @@ -620,7 +623,7 @@ sigExists: } defer res.Body.Close() if res.StatusCode != http.StatusCreated { - body, err := ioutil.ReadAll(res.Body) + body, err := iolimits.ReadAtMost(res.Body, iolimits.MaxErrorBodySize) if err == nil { logrus.Debugf("Error body %s", string(body)) } diff --git a/vendor/github.com/containers/image/v5/docker/docker_image_src.go b/vendor/github.com/containers/image/v5/docker/docker_image_src.go index 35beb30e5..967845e72 100644 --- a/vendor/github.com/containers/image/v5/docker/docker_image_src.go +++ b/vendor/github.com/containers/image/v5/docker/docker_image_src.go @@ -10,8 +10,10 @@ import ( "net/url" "os" "strconv" + "strings" "github.com/containers/image/v5/docker/reference" + "github.com/containers/image/v5/internal/iolimits" "github.com/containers/image/v5/manifest" "github.com/containers/image/v5/pkg/sysregistriesv2" "github.com/containers/image/v5/types" @@ -53,43 +55,77 @@ func newImageSource(ctx context.Context, sys *types.SystemContext, ref dockerRef // non-mirror original location last; this both transparently handles the case // of no mirrors configured, and ensures we return the error encountered when // acessing the upstream location if all endpoints fail. - manifestLoadErr := errors.New("Internal error: newImageSource returned without trying any endpoint") pullSources, err := registry.PullSourcesFromReference(ref.ref) if err != nil { return nil, err } + type attempt struct { + ref reference.Named + err error + } + attempts := []attempt{} for _, pullSource := range pullSources { - logrus.Debugf("Trying to pull %q", pullSource.Reference) - dockerRef, err := newReference(pullSource.Reference) - if err != nil { - return nil, err + logrus.Debugf("Trying to access %q", pullSource.Reference) + s, err := newImageSourceAttempt(ctx, sys, pullSource, primaryDomain) + if err == nil { + return s, nil } - - endpointSys := sys - // sys.DockerAuthConfig does not explicitly specify a registry; we must not blindly send the credentials intended for the primary endpoint to mirrors. - if endpointSys != nil && endpointSys.DockerAuthConfig != nil && reference.Domain(dockerRef.ref) != primaryDomain { - copy := *endpointSys - copy.DockerAuthConfig = nil - endpointSys = © + logrus.Debugf("Accessing %q failed: %v", pullSource.Reference, err) + attempts = append(attempts, attempt{ + ref: pullSource.Reference, + err: err, + }) + } + switch len(attempts) { + case 0: + return nil, errors.New("Internal error: newImageSource returned without trying any endpoint") + case 1: + return nil, attempts[0].err // If no mirrors are used, perfectly preserve the error type and add no noise. + default: + // Don’t just build a string, try to preserve the typed error. + primary := &attempts[len(attempts)-1] + extras := []string{} + for i := 0; i < len(attempts)-1; i++ { + // This is difficult to fit into a single-line string, when the error can contain arbitrary strings including any metacharacters we decide to use. + // The paired [] at least have some chance of being unambiguous. + extras = append(extras, fmt.Sprintf("[%s: %v]", attempts[i].ref.String(), attempts[i].err)) } + return nil, errors.Wrapf(primary.err, "(Mirrors also failed: %s): %s", strings.Join(extras, "\n"), primary.ref.String()) + } +} - client, err := newDockerClientFromRef(endpointSys, dockerRef, false, "pull") - if err != nil { - return nil, err - } - client.tlsClientConfig.InsecureSkipVerify = pullSource.Endpoint.Insecure +// newImageSourceAttempt is an internal helper for newImageSource. Everyone else must call newImageSource. +// Given a pullSource and primaryDomain, return a dockerImageSource if it is reachable. +// The caller must call .Close() on the returned ImageSource. +func newImageSourceAttempt(ctx context.Context, sys *types.SystemContext, pullSource sysregistriesv2.PullSource, primaryDomain string) (*dockerImageSource, error) { + ref, err := newReference(pullSource.Reference) + if err != nil { + return nil, err + } - testImageSource := &dockerImageSource{ - ref: dockerRef, - c: client, - } + endpointSys := sys + // sys.DockerAuthConfig does not explicitly specify a registry; we must not blindly send the credentials intended for the primary endpoint to mirrors. + if endpointSys != nil && endpointSys.DockerAuthConfig != nil && reference.Domain(ref.ref) != primaryDomain { + copy := *endpointSys + copy.DockerAuthConfig = nil + endpointSys = © + } - manifestLoadErr = testImageSource.ensureManifestIsLoaded(ctx) - if manifestLoadErr == nil { - return testImageSource, nil - } + client, err := newDockerClientFromRef(endpointSys, ref, false, "pull") + if err != nil { + return nil, err } - return nil, manifestLoadErr + client.tlsClientConfig.InsecureSkipVerify = pullSource.Endpoint.Insecure + + s := &dockerImageSource{ + ref: ref, + c: client, + } + + if err := s.ensureManifestIsLoaded(ctx); err != nil { + return nil, err + } + return s, nil } // Reference returns the reference used to set up this source, _as specified by the user_ @@ -156,7 +192,8 @@ func (s *dockerImageSource) fetchManifest(ctx context.Context, tagOrDigest strin if res.StatusCode != http.StatusOK { return nil, "", errors.Wrapf(client.HandleErrorResponse(res), "Error reading manifest %s in %s", tagOrDigest, s.ref.ref.Name()) } - manblob, err := ioutil.ReadAll(res.Body) + + manblob, err := iolimits.ReadAtMost(res.Body, iolimits.MaxManifestBodySize) if err != nil { return nil, "", err } @@ -239,7 +276,7 @@ func (s *dockerImageSource) GetBlob(ctx context.Context, info types.BlobInfo, ca if err != nil { return nil, 0, err } - if err := httpResponseToError(res); err != nil { + if err := httpResponseToError(res, "Error fetching blob"); err != nil { return nil, 0, err } cache.RecordKnownLocation(s.ref.Transport(), bicTransportScope(s.ref), info.Digest, newBICLocationReference(s.ref)) @@ -342,7 +379,7 @@ func (s *dockerImageSource) getOneSignature(ctx context.Context, url *url.URL) ( } else if res.StatusCode != http.StatusOK { return nil, false, errors.Errorf("Error reading signature from %s: status %d (%s)", url.String(), res.StatusCode, http.StatusText(res.StatusCode)) } - sig, err := ioutil.ReadAll(res.Body) + sig, err := iolimits.ReadAtMost(res.Body, iolimits.MaxSignatureBodySize) if err != nil { return nil, false, err } @@ -401,7 +438,7 @@ func deleteImage(ctx context.Context, sys *types.SystemContext, ref dockerRefere return err } defer get.Body.Close() - manifestBody, err := ioutil.ReadAll(get.Body) + manifestBody, err := iolimits.ReadAtMost(get.Body, iolimits.MaxManifestBodySize) if err != nil { return err } @@ -424,7 +461,7 @@ func deleteImage(ctx context.Context, sys *types.SystemContext, ref dockerRefere } defer delete.Body.Close() - body, err := ioutil.ReadAll(delete.Body) + body, err := iolimits.ReadAtMost(delete.Body, iolimits.MaxErrorBodySize) if err != nil { return err } diff --git a/vendor/github.com/containers/image/v5/docker/errors.go b/vendor/github.com/containers/image/v5/docker/errors.go index 860868f41..f626cc7da 100644 --- a/vendor/github.com/containers/image/v5/docker/errors.go +++ b/vendor/github.com/containers/image/v5/docker/errors.go @@ -14,7 +14,7 @@ var ( // docker V1 registry. ErrV1NotSupported = errors.New("can't talk to a V1 docker registry") // ErrTooManyRequests is returned when the status code returned is 429 - ErrTooManyRequests = errors.New("too many request to registry") + ErrTooManyRequests = errors.New("too many requests to registry") ) // ErrUnauthorizedForCredentials is returned when the status code returned is 401 @@ -26,9 +26,9 @@ func (e ErrUnauthorizedForCredentials) Error() string { return fmt.Sprintf("unable to retrieve auth token: invalid username/password: %s", e.Err.Error()) } -// httpResponseToError translates the https.Response into an error. It returns +// httpResponseToError translates the https.Response into an error, possibly prefixing it with the supplied context. It returns // nil if the response is not considered an error. -func httpResponseToError(res *http.Response) error { +func httpResponseToError(res *http.Response, context string) error { switch res.StatusCode { case http.StatusOK: return nil @@ -38,6 +38,9 @@ func httpResponseToError(res *http.Response) error { err := client.HandleErrorResponse(res) return ErrUnauthorizedForCredentials{Err: err} default: - return perrors.Errorf("invalid status code from registry %d (%s)", res.StatusCode, http.StatusText(res.StatusCode)) + if context != "" { + context = context + ": " + } + return perrors.Errorf("%sinvalid status code from registry %d (%s)", context, res.StatusCode, http.StatusText(res.StatusCode)) } } diff --git a/vendor/github.com/containers/image/v5/docker/tarfile/dest.go b/vendor/github.com/containers/image/v5/docker/tarfile/dest.go index c322156b5..c171da505 100644 --- a/vendor/github.com/containers/image/v5/docker/tarfile/dest.go +++ b/vendor/github.com/containers/image/v5/docker/tarfile/dest.go @@ -13,6 +13,7 @@ import ( "time" "github.com/containers/image/v5/docker/reference" + "github.com/containers/image/v5/internal/iolimits" "github.com/containers/image/v5/internal/tmpdir" "github.com/containers/image/v5/manifest" "github.com/containers/image/v5/types" @@ -143,7 +144,7 @@ func (d *Destination) PutBlob(ctx context.Context, stream io.Reader, inputInfo t } if isConfig { - buf, err := ioutil.ReadAll(stream) + buf, err := iolimits.ReadAtMost(stream, iolimits.MaxConfigBodySize) if err != nil { return types.BlobInfo{}, errors.Wrap(err, "Error reading Config file stream") } diff --git a/vendor/github.com/containers/image/v5/docker/tarfile/src.go b/vendor/github.com/containers/image/v5/docker/tarfile/src.go index 80dd753e4..c1f1a0f27 100644 --- a/vendor/github.com/containers/image/v5/docker/tarfile/src.go +++ b/vendor/github.com/containers/image/v5/docker/tarfile/src.go @@ -11,6 +11,7 @@ import ( "path" "sync" + "github.com/containers/image/v5/internal/iolimits" "github.com/containers/image/v5/internal/tmpdir" "github.com/containers/image/v5/manifest" "github.com/containers/image/v5/pkg/compression" @@ -203,13 +204,13 @@ func findTarComponent(inputFile io.Reader, path string) (*tar.Reader, *tar.Heade } // readTarComponent returns full contents of componentPath. -func (s *Source) readTarComponent(path string) ([]byte, error) { +func (s *Source) readTarComponent(path string, limit int) ([]byte, error) { file, err := s.openTarComponent(path) if err != nil { return nil, errors.Wrapf(err, "Error loading tar component %s", path) } defer file.Close() - bytes, err := ioutil.ReadAll(file) + bytes, err := iolimits.ReadAtMost(file, limit) if err != nil { return nil, err } @@ -240,7 +241,7 @@ func (s *Source) ensureCachedDataIsPresentPrivate() error { } // Read and parse config. - configBytes, err := s.readTarComponent(tarManifest[0].Config) + configBytes, err := s.readTarComponent(tarManifest[0].Config, iolimits.MaxConfigBodySize) if err != nil { return err } @@ -266,7 +267,7 @@ func (s *Source) ensureCachedDataIsPresentPrivate() error { // loadTarManifest loads and decodes the manifest.json. func (s *Source) loadTarManifest() ([]ManifestItem, error) { // FIXME? Do we need to deal with the legacy format? - bytes, err := s.readTarComponent(manifestFileName) + bytes, err := s.readTarComponent(manifestFileName, iolimits.MaxTarFileManifestSize) if err != nil { return nil, err } diff --git a/vendor/github.com/containers/image/v5/image/docker_schema2.go b/vendor/github.com/containers/image/v5/image/docker_schema2.go index 7891562b7..1e2114d7e 100644 --- a/vendor/github.com/containers/image/v5/image/docker_schema2.go +++ b/vendor/github.com/containers/image/v5/image/docker_schema2.go @@ -7,10 +7,10 @@ import ( "encoding/hex" "encoding/json" "fmt" - "io/ioutil" "strings" "github.com/containers/image/v5/docker/reference" + "github.com/containers/image/v5/internal/iolimits" "github.com/containers/image/v5/manifest" "github.com/containers/image/v5/pkg/blobinfocache/none" "github.com/containers/image/v5/types" @@ -102,7 +102,7 @@ func (m *manifestSchema2) ConfigBlob(ctx context.Context) ([]byte, error) { return nil, err } defer stream.Close() - blob, err := ioutil.ReadAll(stream) + blob, err := iolimits.ReadAtMost(stream, iolimits.MaxConfigBodySize) if err != nil { return nil, err } diff --git a/vendor/github.com/containers/image/v5/image/oci.go b/vendor/github.com/containers/image/v5/image/oci.go index 059d84977..b5ddb9aaa 100644 --- a/vendor/github.com/containers/image/v5/image/oci.go +++ b/vendor/github.com/containers/image/v5/image/oci.go @@ -4,9 +4,9 @@ import ( "context" "encoding/json" "fmt" - "io/ioutil" "github.com/containers/image/v5/docker/reference" + "github.com/containers/image/v5/internal/iolimits" "github.com/containers/image/v5/manifest" "github.com/containers/image/v5/pkg/blobinfocache/none" "github.com/containers/image/v5/types" @@ -67,7 +67,7 @@ func (m *manifestOCI1) ConfigBlob(ctx context.Context) ([]byte, error) { return nil, err } defer stream.Close() - blob, err := ioutil.ReadAll(stream) + blob, err := iolimits.ReadAtMost(stream, iolimits.MaxConfigBodySize) if err != nil { return nil, err } diff --git a/vendor/github.com/containers/image/v5/internal/iolimits/iolimits.go b/vendor/github.com/containers/image/v5/internal/iolimits/iolimits.go new file mode 100644 index 000000000..3fed1995c --- /dev/null +++ b/vendor/github.com/containers/image/v5/internal/iolimits/iolimits.go @@ -0,0 +1,60 @@ +package iolimits + +import ( + "io" + "io/ioutil" + + "github.com/pkg/errors" +) + +// All constants below are intended to be used as limits for `ReadAtMost`. The +// immediate use-case for limiting the size of in-memory copied data is to +// protect against OOM DOS attacks as described inCVE-2020-1702. Instead of +// copying data until running out of memory, we error out after hitting the +// specified limit. +const ( + // megaByte denotes one megabyte and is intended to be used as a limit in + // `ReadAtMost`. + megaByte = 1 << 20 + // MaxManifestBodySize is the maximum allowed size of a manifest. The limit + // of 4 MB aligns with the one of a Docker registry: + // https://github.com/docker/distribution/blob/a8371794149d1d95f1e846744b05c87f2f825e5a/registry/handlers/manifests.go#L30 + MaxManifestBodySize = 4 * megaByte + // MaxAuthTokenBodySize is the maximum allowed size of an auth token. + // The limit of 1 MB is considered to be greatly sufficient. + MaxAuthTokenBodySize = megaByte + // MaxSignatureListBodySize is the maximum allowed size of a signature list. + // The limit of 4 MB is considered to be greatly sufficient. + MaxSignatureListBodySize = 4 * megaByte + // MaxSignatureBodySize is the maximum allowed size of a signature. + // The limit of 4 MB is considered to be greatly sufficient. + MaxSignatureBodySize = 4 * megaByte + // MaxErrorBodySize is the maximum allowed size of an error-response body. + // The limit of 1 MB is considered to be greatly sufficient. + MaxErrorBodySize = megaByte + // MaxConfigBodySize is the maximum allowed size of a config blob. + // The limit of 4 MB is considered to be greatly sufficient. + MaxConfigBodySize = 4 * megaByte + // MaxOpenShiftStatusBody is the maximum allowed size of an OpenShift status body. + // The limit of 4 MB is considered to be greatly sufficient. + MaxOpenShiftStatusBody = 4 * megaByte + // MaxTarFileManifestSize is the maximum allowed size of a (docker save)-like manifest (which may contain multiple images) + // The limit of 1 MB is considered to be greatly sufficient. + MaxTarFileManifestSize = megaByte +) + +// ReadAtMost reads from reader and errors out if the specified limit (in bytes) is exceeded. +func ReadAtMost(reader io.Reader, limit int) ([]byte, error) { + limitedReader := io.LimitReader(reader, int64(limit+1)) + + res, err := ioutil.ReadAll(limitedReader) + if err != nil { + return nil, err + } + + if len(res) > limit { + return nil, errors.Errorf("exceeded maximum allowed size of %d bytes", limit) + } + + return res, nil +} diff --git a/vendor/github.com/containers/image/v5/openshift/openshift.go b/vendor/github.com/containers/image/v5/openshift/openshift.go index b9242da6e..28bfc456d 100644 --- a/vendor/github.com/containers/image/v5/openshift/openshift.go +++ b/vendor/github.com/containers/image/v5/openshift/openshift.go @@ -7,13 +7,13 @@ import ( "encoding/json" "fmt" "io" - "io/ioutil" "net/http" "net/url" "strings" "github.com/containers/image/v5/docker" "github.com/containers/image/v5/docker/reference" + "github.com/containers/image/v5/internal/iolimits" "github.com/containers/image/v5/manifest" "github.com/containers/image/v5/types" "github.com/containers/image/v5/version" @@ -102,7 +102,7 @@ func (c *openshiftClient) doRequest(ctx context.Context, method, path string, re return nil, err } defer res.Body.Close() - body, err := ioutil.ReadAll(res.Body) + body, err := iolimits.ReadAtMost(res.Body, iolimits.MaxOpenShiftStatusBody) if err != nil { return nil, err } diff --git a/vendor/github.com/containers/image/v5/signature/mechanism_gpgme.go b/vendor/github.com/containers/image/v5/signature/mechanism_gpgme.go index 4825ab27c..277fba16b 100644 --- a/vendor/github.com/containers/image/v5/signature/mechanism_gpgme.go +++ b/vendor/github.com/containers/image/v5/signature/mechanism_gpgme.go @@ -139,7 +139,7 @@ func (m *gpgmeSigningMechanism) Sign(input []byte, keyIdentity string) ([]byte, } // Verify parses unverifiedSignature and returns the content and the signer's identity -func (m gpgmeSigningMechanism) Verify(unverifiedSignature []byte) (contents []byte, keyIdentity string, err error) { +func (m *gpgmeSigningMechanism) Verify(unverifiedSignature []byte) (contents []byte, keyIdentity string, err error) { signedBuffer := bytes.Buffer{} signedData, err := gpgme.NewDataWriter(&signedBuffer) if err != nil { @@ -170,6 +170,6 @@ func (m gpgmeSigningMechanism) Verify(unverifiedSignature []byte) (contents []by // WARNING: The short key identifier (which correponds to "Key ID" for OpenPGP keys) // is NOT the same as a "key identity" used in other calls ot this interface, and // the values may have no recognizable relationship if the public key is not available. -func (m gpgmeSigningMechanism) UntrustedSignatureContents(untrustedSignature []byte) (untrustedContents []byte, shortKeyIdentifier string, err error) { +func (m *gpgmeSigningMechanism) UntrustedSignatureContents(untrustedSignature []byte) (untrustedContents []byte, shortKeyIdentifier string, err error) { return gpgUntrustedSignatureContents(untrustedSignature) } diff --git a/vendor/github.com/containers/image/v5/signature/mechanism_openpgp.go b/vendor/github.com/containers/image/v5/signature/mechanism_openpgp.go index eccd610c9..51f20f310 100644 --- a/vendor/github.com/containers/image/v5/signature/mechanism_openpgp.go +++ b/vendor/github.com/containers/image/v5/signature/mechanism_openpgp.go @@ -154,6 +154,6 @@ func (m *openpgpSigningMechanism) Verify(unverifiedSignature []byte) (contents [ // WARNING: The short key identifier (which correponds to "Key ID" for OpenPGP keys) // is NOT the same as a "key identity" used in other calls ot this interface, and // the values may have no recognizable relationship if the public key is not available. -func (m openpgpSigningMechanism) UntrustedSignatureContents(untrustedSignature []byte) (untrustedContents []byte, shortKeyIdentifier string, err error) { +func (m *openpgpSigningMechanism) UntrustedSignatureContents(untrustedSignature []byte) (untrustedContents []byte, shortKeyIdentifier string, err error) { return gpgUntrustedSignatureContents(untrustedSignature) } diff --git a/vendor/github.com/containers/image/v5/types/types.go b/vendor/github.com/containers/image/v5/types/types.go index d38a17fab..ba249ca25 100644 --- a/vendor/github.com/containers/image/v5/types/types.go +++ b/vendor/github.com/containers/image/v5/types/types.go @@ -547,6 +547,8 @@ type SystemContext struct { // Note that this field is used mainly to integrate containers/image into projectatomic/docker // in order to not break any existing docker's integration tests. DockerDisableV1Ping bool + // If true, dockerImageDestination.SupportedManifestMIMETypes will omit the Schema1 media types from the supported list + DockerDisableDestSchema1MIMETypes bool // Directory to use for OSTree temporary files OSTreeTmpDirPath string diff --git a/vendor/github.com/containers/image/v5/version/version.go b/vendor/github.com/containers/image/v5/version/version.go index a92774466..1a44baf99 100644 --- a/vendor/github.com/containers/image/v5/version/version.go +++ b/vendor/github.com/containers/image/v5/version/version.go @@ -6,7 +6,7 @@ const ( // VersionMajor is for an API incompatible changes VersionMajor = 5 // VersionMinor is for functionality in a backwards-compatible manner - VersionMinor = 1 + VersionMinor = 2 // VersionPatch is for backwards-compatible bug fixes VersionPatch = 0 diff --git a/vendor/github.com/mtrmac/gpgme/.appveyor.yml b/vendor/github.com/mtrmac/gpgme/.appveyor.yml new file mode 100644 index 000000000..2fdc09ab5 --- /dev/null +++ b/vendor/github.com/mtrmac/gpgme/.appveyor.yml @@ -0,0 +1,40 @@ +--- +version: 0.{build} +platform: x86 +branches: + only: + - master + +clone_folder: c:\gopath\src\github.com\proglottis\gpgme + +environment: + GOPATH: c:\gopath + GOROOT: C:\go-x86 + CGO_LDFLAGS: -LC:\gpg\lib + CGO_CFLAGS: -IC:\gpg\include + GPG_DIR: C:\gpg + +install: + - nuget install 7ZipCLI -ExcludeVersion + - set PATH=%appveyor_build_folder%\7ZipCLI\tools;%PATH% + - appveyor DownloadFile https://www.gnupg.org/ftp/gcrypt/binary/gnupg-w32-2.1.20_20170403.exe -FileName gnupg-w32-2.1.20_20170403.exe + - 7z x -o%GPG_DIR% gnupg-w32-2.1.20_20170403.exe + - copy "%GPG_DIR%\lib\libgpg-error.imp" "%GPG_DIR%\lib\libgpg-error.a" + - copy "%GPG_DIR%\lib\libassuan.imp" "%GPG_DIR%\lib\libassuan.a" + - copy "%GPG_DIR%\lib\libgpgme.imp" "%GPG_DIR%\lib\libgpgme.a" + - set PATH=%GOPATH%\bin;%GOROOT%\bin;%GPG_DIR%\bin;C:\MinGW\bin;%PATH% + - C:\cygwin\bin\sed -i 's/"GPG_AGENT_INFO"/"GPG_AGENT_INFO="/;s/C.unsetenv(v)/C.putenv(v)/' %APPVEYOR_BUILD_FOLDER%\gpgme.go + +test_script: + - go test -v github.com/proglottis/gpgme + + +build_script: + - go build -o example_decrypt.exe -i %APPVEYOR_BUILD_FOLDER%\examples\decrypt.go + - go build -o example_encrypt.exe -i %APPVEYOR_BUILD_FOLDER%\examples\encrypt.go + +artifacts: + - path: example_decrypt.exe + name: decrypt example binary + - path: example_encrypt.exe + name: encrypt example binary
\ No newline at end of file diff --git a/vendor/github.com/mtrmac/gpgme/.travis.yml b/vendor/github.com/mtrmac/gpgme/.travis.yml new file mode 100644 index 000000000..619e33721 --- /dev/null +++ b/vendor/github.com/mtrmac/gpgme/.travis.yml @@ -0,0 +1,32 @@ +--- +language: go +os: + - linux + - osx + - windows +dist: xenial +sudo: false + +go: + - 1.11 + - 1.12 + - 1.13 + +addons: + apt: + packages: + - libgpgme11-dev + homebrew: + packages: + - gnupg + - gnupg@1.4 + - gpgme + update: true + +matrix: + allow_failures: + - os: windows + +before_install: + - if [[ "$TRAVIS_OS_NAME" == "windows" ]]; then choco install msys2; fi + - if [[ "$TRAVIS_OS_NAME" == "windows" ]]; then choco install gpg4win; fi diff --git a/vendor/github.com/mtrmac/gpgme/data.go b/vendor/github.com/mtrmac/gpgme/data.go index eebc97263..eee32c032 100644 --- a/vendor/github.com/mtrmac/gpgme/data.go +++ b/vendor/github.com/mtrmac/gpgme/data.go @@ -50,25 +50,25 @@ func gogpgme_writefunc(handle, buffer unsafe.Pointer, size C.size_t) C.ssize_t { } //export gogpgme_seekfunc -func gogpgme_seekfunc(handle unsafe.Pointer, offset C.off_t, whence C.int) C.off_t { +func gogpgme_seekfunc(handle unsafe.Pointer, offset C.gpgme_off_t, whence C.int) C.gpgme_off_t { d := callbackLookup(uintptr(handle)).(*Data) n, err := d.s.Seek(int64(offset), int(whence)) if err != nil { C.gpgme_err_set_errno(C.EIO) return -1 } - return C.off_t(n) + return C.gpgme_off_t(n) } // The Data buffer used to communicate with GPGME type Data struct { - dh C.gpgme_data_t + dh C.gpgme_data_t // WARNING: Call runtime.KeepAlive(d) after ANY passing of d.dh to C buf []byte cbs C.struct_gpgme_data_cbs r io.Reader w io.Writer s io.Seeker - cbc uintptr + cbc uintptr // WARNING: Call runtime.KeepAlive(d) after ANY use of d.cbc in C (typically via d.dh) } func newData() *Data { @@ -154,12 +154,14 @@ func (d *Data) Close() error { callbackDelete(d.cbc) } _, err := C.gpgme_data_release(d.dh) + runtime.KeepAlive(d) d.dh = nil return err } func (d *Data) Write(p []byte) (int, error) { n, err := C.gpgme_data_write(d.dh, unsafe.Pointer(&p[0]), C.size_t(len(p))) + runtime.KeepAlive(d) if err != nil { return 0, err } @@ -171,6 +173,7 @@ func (d *Data) Write(p []byte) (int, error) { func (d *Data) Read(p []byte) (int, error) { n, err := C.gpgme_data_read(d.dh, unsafe.Pointer(&p[0]), C.size_t(len(p))) + runtime.KeepAlive(d) if err != nil { return 0, err } @@ -181,11 +184,14 @@ func (d *Data) Read(p []byte) (int, error) { } func (d *Data) Seek(offset int64, whence int) (int64, error) { - n, err := C.gpgme_data_seek(d.dh, C.off_t(offset), C.int(whence)) + n, err := C.gogpgme_data_seek(d.dh, C.gpgme_off_t(offset), C.int(whence)) + runtime.KeepAlive(d) return int64(n), err } // Name returns the associated filename if any func (d *Data) Name() string { - return C.GoString(C.gpgme_data_get_file_name(d.dh)) + res := C.GoString(C.gpgme_data_get_file_name(d.dh)) + runtime.KeepAlive(d) + return res } diff --git a/vendor/github.com/mtrmac/gpgme/go.mod b/vendor/github.com/mtrmac/gpgme/go.mod new file mode 100644 index 000000000..3dd09c9fb --- /dev/null +++ b/vendor/github.com/mtrmac/gpgme/go.mod @@ -0,0 +1,3 @@ +module github.com/mtrmac/gpgme + +go 1.11 diff --git a/vendor/github.com/mtrmac/gpgme/go_gpgme.c b/vendor/github.com/mtrmac/gpgme/go_gpgme.c index b887574e0..00da3ab30 100644 --- a/vendor/github.com/mtrmac/gpgme/go_gpgme.c +++ b/vendor/github.com/mtrmac/gpgme/go_gpgme.c @@ -8,6 +8,28 @@ void gogpgme_set_passphrase_cb(gpgme_ctx_t ctx, gpgme_passphrase_cb_t cb, uintpt gpgme_set_passphrase_cb(ctx, cb, (void *)handle); } +gpgme_off_t gogpgme_data_seek(gpgme_data_t dh, gpgme_off_t offset, int whence) { + return gpgme_data_seek(dh, offset, whence); +} + +gpgme_error_t gogpgme_op_assuan_transact_ext( + gpgme_ctx_t ctx, + char* cmd, + uintptr_t data_h, + uintptr_t inquiry_h, + uintptr_t status_h, + gpgme_error_t *operr + ){ + return gpgme_op_assuan_transact_ext( + ctx, + cmd, + (gpgme_assuan_data_cb_t) gogpgme_assuan_data_callback, (void *)data_h, + (gpgme_assuan_inquire_cb_t) gogpgme_assuan_inquiry_callback, (void *)inquiry_h, + (gpgme_assuan_status_cb_t) gogpgme_assuan_status_callback, (void *)status_h, + operr + ); +} + unsigned int key_revoked(gpgme_key_t k) { return k->revoked; } diff --git a/vendor/github.com/mtrmac/gpgme/go_gpgme.h b/vendor/github.com/mtrmac/gpgme/go_gpgme.h index a3678b127..eb3a4ba88 100644 --- a/vendor/github.com/mtrmac/gpgme/go_gpgme.h +++ b/vendor/github.com/mtrmac/gpgme/go_gpgme.h @@ -12,6 +12,13 @@ extern off_t gogpgme_seekfunc(void *handle, off_t offset, int whence); extern gpgme_error_t gogpgme_passfunc(void *hook, char *uid_hint, char *passphrase_info, int prev_was_bad, int fd); extern gpgme_error_t gogpgme_data_new_from_cbs(gpgme_data_t *dh, gpgme_data_cbs_t cbs, uintptr_t handle); extern void gogpgme_set_passphrase_cb(gpgme_ctx_t ctx, gpgme_passphrase_cb_t cb, uintptr_t handle); +extern gpgme_off_t gogpgme_data_seek(gpgme_data_t dh, gpgme_off_t offset, int whence); + +extern gpgme_error_t gogpgme_op_assuan_transact_ext(gpgme_ctx_t ctx, char *cmd, uintptr_t data_h, uintptr_t inquiry_h , uintptr_t status_h, gpgme_error_t *operr); + +extern gpgme_error_t gogpgme_assuan_data_callback(void *opaque, void* data, size_t datalen ); +extern gpgme_error_t gogpgme_assuan_inquiry_callback(void *opaque, char* name, char* args); +extern gpgme_error_t gogpgme_assuan_status_callback(void *opaque, char* status, char* args); extern unsigned int key_revoked(gpgme_key_t k); extern unsigned int key_expired(gpgme_key_t k); diff --git a/vendor/github.com/mtrmac/gpgme/gpgme.go b/vendor/github.com/mtrmac/gpgme/gpgme.go index 20aad737c..c19b9aebc 100644 --- a/vendor/github.com/mtrmac/gpgme/gpgme.go +++ b/vendor/github.com/mtrmac/gpgme/gpgme.go @@ -7,7 +7,6 @@ package gpgme // #include <gpgme.h> // #include "go_gpgme.h" import "C" - import ( "fmt" "io" @@ -48,9 +47,8 @@ const ( ProtocolAssuan Protocol = C.GPGME_PROTOCOL_ASSUAN ProtocolG13 Protocol = C.GPGME_PROTOCOL_G13 ProtocolUIServer Protocol = C.GPGME_PROTOCOL_UISERVER - // ProtocolSpawn Protocol = C.GPGME_PROTOCOL_SPAWN // Unavailable in 1.4.3 - ProtocolDefault Protocol = C.GPGME_PROTOCOL_DEFAULT - ProtocolUnknown Protocol = C.GPGME_PROTOCOL_UNKNOWN + ProtocolDefault Protocol = C.GPGME_PROTOCOL_DEFAULT + ProtocolUnknown Protocol = C.GPGME_PROTOCOL_UNKNOWN ) type PinEntryMode int @@ -70,7 +68,6 @@ const ( EncryptNoEncryptTo EncryptFlag = C.GPGME_ENCRYPT_NO_ENCRYPT_TO EncryptPrepare EncryptFlag = C.GPGME_ENCRYPT_PREPARE EncryptExceptSign EncryptFlag = C.GPGME_ENCRYPT_EXPECT_SIGN - // EncryptNoCompress EncryptFlag = C.GPGME_ENCRYPT_NO_COMPRESS // Unavailable in 1.4.3 ) type HashAlgo int @@ -84,7 +81,6 @@ const ( KeyListModeExtern KeyListMode = C.GPGME_KEYLIST_MODE_EXTERN KeyListModeSigs KeyListMode = C.GPGME_KEYLIST_MODE_SIGS KeyListModeSigNotations KeyListMode = C.GPGME_KEYLIST_MODE_SIG_NOTATIONS - // KeyListModeWithSecret KeyListMode = C.GPGME_KEYLIST_MODE_WITH_SECRET // Unavailable in 1.4.3 KeyListModeEphemeral KeyListMode = C.GPGME_KEYLIST_MODE_EPHEMERAL KeyListModeModeValidate KeyListMode = C.GPGME_KEYLIST_MODE_VALIDATE ) @@ -168,39 +164,60 @@ func EngineCheckVersion(p Protocol) error { } type EngineInfo struct { - info C.gpgme_engine_info_t + next *EngineInfo + protocol Protocol + fileName string + homeDir string + version string + requiredVersion string } -func (e *EngineInfo) Next() *EngineInfo { - if e.info.next == nil { - return nil +func copyEngineInfo(info C.gpgme_engine_info_t) *EngineInfo { + res := &EngineInfo{ + next: nil, + protocol: Protocol(info.protocol), + fileName: C.GoString(info.file_name), + homeDir: C.GoString(info.home_dir), + version: C.GoString(info.version), + requiredVersion: C.GoString(info.req_version), + } + if info.next != nil { + res.next = copyEngineInfo(info.next) } - return &EngineInfo{info: e.info.next} + return res +} + +func (e *EngineInfo) Next() *EngineInfo { + return e.next } func (e *EngineInfo) Protocol() Protocol { - return Protocol(e.info.protocol) + return e.protocol } func (e *EngineInfo) FileName() string { - return C.GoString(e.info.file_name) + return e.fileName } func (e *EngineInfo) Version() string { - return C.GoString(e.info.version) + return e.version } func (e *EngineInfo) RequiredVersion() string { - return C.GoString(e.info.req_version) + return e.requiredVersion } func (e *EngineInfo) HomeDir() string { - return C.GoString(e.info.home_dir) + return e.homeDir } func GetEngineInfo() (*EngineInfo, error) { - info := &EngineInfo{} - return info, handleError(C.gpgme_get_engine_info(&info.info)) + var cInfo C.gpgme_engine_info_t + err := handleError(C.gpgme_get_engine_info(&cInfo)) + if err != nil { + return nil, err + } + return copyEngineInfo(cInfo), nil // It is up to the caller not to invalidate cInfo concurrently until this is done. } func SetEngineInfo(proto Protocol, fileName, homeDir string) error { @@ -261,9 +278,9 @@ type Context struct { KeyError error callback Callback - cbc uintptr + cbc uintptr // WARNING: Call runtime.KeepAlive(c) after ANY use of c.cbc in C (typically via c.ctx) - ctx C.gpgme_ctx_t + ctx C.gpgme_ctx_t // WARNING: Call runtime.KeepAlive(c) after ANY passing of c.ctx to C } func New() (*Context, error) { @@ -281,49 +298,68 @@ func (c *Context) Release() { callbackDelete(c.cbc) } C.gpgme_release(c.ctx) + runtime.KeepAlive(c) c.ctx = nil } func (c *Context) SetArmor(yes bool) { C.gpgme_set_armor(c.ctx, cbool(yes)) + runtime.KeepAlive(c) } func (c *Context) Armor() bool { - return C.gpgme_get_armor(c.ctx) != 0 + res := C.gpgme_get_armor(c.ctx) != 0 + runtime.KeepAlive(c) + return res } func (c *Context) SetTextMode(yes bool) { C.gpgme_set_textmode(c.ctx, cbool(yes)) + runtime.KeepAlive(c) } func (c *Context) TextMode() bool { - return C.gpgme_get_textmode(c.ctx) != 0 + res := C.gpgme_get_textmode(c.ctx) != 0 + runtime.KeepAlive(c) + return res } func (c *Context) SetProtocol(p Protocol) error { - return handleError(C.gpgme_set_protocol(c.ctx, C.gpgme_protocol_t(p))) + err := handleError(C.gpgme_set_protocol(c.ctx, C.gpgme_protocol_t(p))) + runtime.KeepAlive(c) + return err } func (c *Context) Protocol() Protocol { - return Protocol(C.gpgme_get_protocol(c.ctx)) + res := Protocol(C.gpgme_get_protocol(c.ctx)) + runtime.KeepAlive(c) + return res } func (c *Context) SetKeyListMode(m KeyListMode) error { - return handleError(C.gpgme_set_keylist_mode(c.ctx, C.gpgme_keylist_mode_t(m))) + err := handleError(C.gpgme_set_keylist_mode(c.ctx, C.gpgme_keylist_mode_t(m))) + runtime.KeepAlive(c) + return err } func (c *Context) KeyListMode() KeyListMode { - return KeyListMode(C.gpgme_get_keylist_mode(c.ctx)) + res := KeyListMode(C.gpgme_get_keylist_mode(c.ctx)) + runtime.KeepAlive(c) + return res } // Unavailable in 1.3.2: // func (c *Context) SetPinEntryMode(m PinEntryMode) error { -// return handleError(C.gpgme_set_pinentry_mode(c.ctx, C.gpgme_pinentry_mode_t(m))) +// err := handleError(C.gpgme_set_pinentry_mode(c.ctx, C.gpgme_pinentry_mode_t(m))) +// runtime.KeepAlive(c) +// return err // } // Unavailable in 1.3.2: // func (c *Context) PinEntryMode() PinEntryMode { -// return PinEntryMode(C.gpgme_get_pinentry_mode(c.ctx)) +// res := PinEntryMode(C.gpgme_get_pinentry_mode(c.ctx)) +// runtime.KeepAlive(c) +// return res // } func (c *Context) SetCallback(callback Callback) error { @@ -340,11 +376,17 @@ func (c *Context) SetCallback(callback Callback) error { c.cbc = 0 _, err = C.gogpgme_set_passphrase_cb(c.ctx, nil, 0) } + runtime.KeepAlive(c) return err } func (c *Context) EngineInfo() *EngineInfo { - return &EngineInfo{info: C.gpgme_ctx_get_engine_info(c.ctx)} + cInfo := C.gpgme_ctx_get_engine_info(c.ctx) + runtime.KeepAlive(c) + // NOTE: c must be live as long as we are accessing cInfo. + res := copyEngineInfo(cInfo) + runtime.KeepAlive(c) // for accesses to cInfo + return res } func (c *Context) SetEngineInfo(proto Protocol, fileName, homeDir string) error { @@ -357,19 +399,23 @@ func (c *Context) SetEngineInfo(proto Protocol, fileName, homeDir string) error chome = C.CString(homeDir) defer C.free(unsafe.Pointer(chome)) } - return handleError(C.gpgme_ctx_set_engine_info(c.ctx, C.gpgme_protocol_t(proto), cfn, chome)) + err := handleError(C.gpgme_ctx_set_engine_info(c.ctx, C.gpgme_protocol_t(proto), cfn, chome)) + runtime.KeepAlive(c) + return err } func (c *Context) KeyListStart(pattern string, secretOnly bool) error { cpattern := C.CString(pattern) defer C.free(unsafe.Pointer(cpattern)) - err := C.gpgme_op_keylist_start(c.ctx, cpattern, cbool(secretOnly)) - return handleError(err) + err := handleError(C.gpgme_op_keylist_start(c.ctx, cpattern, cbool(secretOnly))) + runtime.KeepAlive(c) + return err } func (c *Context) KeyListNext() bool { c.Key = newKey() err := handleError(C.gpgme_op_keylist_next(c.ctx, &c.Key.k)) + runtime.KeepAlive(c) // implies runtime.KeepAlive(c.Key) if err != nil { if e, ok := err.(Error); ok && e.Code() == ErrorEOF { c.KeyError = nil @@ -383,7 +429,9 @@ func (c *Context) KeyListNext() bool { } func (c *Context) KeyListEnd() error { - return handleError(C.gpgme_op_keylist_end(c.ctx)) + err := handleError(C.gpgme_op_keylist_end(c.ctx)) + runtime.KeepAlive(c) + return err } func (c *Context) GetKey(fingerprint string, secret bool) (*Key, error) { @@ -391,7 +439,11 @@ func (c *Context) GetKey(fingerprint string, secret bool) (*Key, error) { cfpr := C.CString(fingerprint) defer C.free(unsafe.Pointer(cfpr)) err := handleError(C.gpgme_get_key(c.ctx, cfpr, &key.k, cbool(secret))) - if e, ok := err.(Error); key.k == nil && ok && e.Code() == ErrorEOF { + runtime.KeepAlive(c) + runtime.KeepAlive(key) + keyKIsNil := key.k == nil + runtime.KeepAlive(key) + if e, ok := err.(Error); keyKIsNil && ok && e.Code() == ErrorEOF { return nil, fmt.Errorf("key %q not found", fingerprint) } if err != nil { @@ -401,11 +453,19 @@ func (c *Context) GetKey(fingerprint string, secret bool) (*Key, error) { } func (c *Context) Decrypt(ciphertext, plaintext *Data) error { - return handleError(C.gpgme_op_decrypt(c.ctx, ciphertext.dh, plaintext.dh)) + err := handleError(C.gpgme_op_decrypt(c.ctx, ciphertext.dh, plaintext.dh)) + runtime.KeepAlive(c) + runtime.KeepAlive(ciphertext) + runtime.KeepAlive(plaintext) + return err } func (c *Context) DecryptVerify(ciphertext, plaintext *Data) error { - return handleError(C.gpgme_op_decrypt_verify(c.ctx, ciphertext.dh, plaintext.dh)) + err := handleError(C.gpgme_op_decrypt_verify(c.ctx, ciphertext.dh, plaintext.dh)) + runtime.KeepAlive(c) + runtime.KeepAlive(ciphertext) + runtime.KeepAlive(plaintext) + return err } type Signature struct { @@ -432,10 +492,20 @@ func (c *Context) Verify(sig, signedText, plain *Data) (string, []Signature, err plainPtr = plain.dh } err := handleError(C.gpgme_op_verify(c.ctx, sig.dh, signedTextPtr, plainPtr)) + runtime.KeepAlive(c) + runtime.KeepAlive(sig) + if signedText != nil { + runtime.KeepAlive(signedText) + } + if plain != nil { + runtime.KeepAlive(plain) + } if err != nil { return "", nil, err } res := C.gpgme_op_verify_result(c.ctx) + runtime.KeepAlive(c) + // NOTE: c must be live as long as we are accessing res. sigs := []Signature{} for s := res.signatures; s != nil; s = s.next { sig := Signature{ @@ -455,7 +525,9 @@ func (c *Context) Verify(sig, signedText, plain *Data) (string, []Signature, err } sigs = append(sigs, sig) } - return C.GoString(res.file_name), sigs, nil + fileName := C.GoString(res.file_name) + runtime.KeepAlive(c) // for all accesses to res above + return fileName, sigs, nil } func (c *Context) Encrypt(recipients []*Key, flags EncryptFlag, plaintext, ciphertext *Data) error { @@ -467,18 +539,116 @@ func (c *Context) Encrypt(recipients []*Key, flags EncryptFlag, plaintext, ciphe *ptr = recipients[i].k } err := C.gpgme_op_encrypt(c.ctx, (*C.gpgme_key_t)(recp), C.gpgme_encrypt_flags_t(flags), plaintext.dh, ciphertext.dh) + runtime.KeepAlive(c) + runtime.KeepAlive(recipients) + runtime.KeepAlive(plaintext) + runtime.KeepAlive(ciphertext) return handleError(err) } func (c *Context) Sign(signers []*Key, plain, sig *Data, mode SigMode) error { C.gpgme_signers_clear(c.ctx) + runtime.KeepAlive(c) for _, k := range signers { - if err := handleError(C.gpgme_signers_add(c.ctx, k.k)); err != nil { + err := handleError(C.gpgme_signers_add(c.ctx, k.k)) + runtime.KeepAlive(c) + runtime.KeepAlive(k) + if err != nil { C.gpgme_signers_clear(c.ctx) + runtime.KeepAlive(c) return err } } - return handleError(C.gpgme_op_sign(c.ctx, plain.dh, sig.dh, C.gpgme_sig_mode_t(mode))) + err := handleError(C.gpgme_op_sign(c.ctx, plain.dh, sig.dh, C.gpgme_sig_mode_t(mode))) + runtime.KeepAlive(c) + runtime.KeepAlive(plain) + runtime.KeepAlive(sig) + return err +} + +type AssuanDataCallback func(data []byte) error +type AssuanInquireCallback func(name, args string) error +type AssuanStatusCallback func(status, args string) error + +// AssuanSend sends a raw Assuan command to gpg-agent +func (c *Context) AssuanSend( + cmd string, + data AssuanDataCallback, + inquiry AssuanInquireCallback, + status AssuanStatusCallback, +) error { + var operr C.gpgme_error_t + + dataPtr := callbackAdd(&data) + inquiryPtr := callbackAdd(&inquiry) + statusPtr := callbackAdd(&status) + cmdCStr := C.CString(cmd) + defer C.free(unsafe.Pointer(cmdCStr)) + err := C.gogpgme_op_assuan_transact_ext( + c.ctx, + cmdCStr, + C.uintptr_t(dataPtr), + C.uintptr_t(inquiryPtr), + C.uintptr_t(statusPtr), + &operr, + ) + runtime.KeepAlive(c) + + if handleError(operr) != nil { + return handleError(operr) + } + return handleError(err) +} + +//export gogpgme_assuan_data_callback +func gogpgme_assuan_data_callback(handle unsafe.Pointer, data unsafe.Pointer, datalen C.size_t) C.gpgme_error_t { + c := callbackLookup(uintptr(handle)).(*AssuanDataCallback) + if *c == nil { + return 0 + } + (*c)(C.GoBytes(data, C.int(datalen))) + return 0 +} + +//export gogpgme_assuan_inquiry_callback +func gogpgme_assuan_inquiry_callback(handle unsafe.Pointer, cName *C.char, cArgs *C.char) C.gpgme_error_t { + name := C.GoString(cName) + args := C.GoString(cArgs) + c := callbackLookup(uintptr(handle)).(*AssuanInquireCallback) + if *c == nil { + return 0 + } + (*c)(name, args) + return 0 +} + +//export gogpgme_assuan_status_callback +func gogpgme_assuan_status_callback(handle unsafe.Pointer, cStatus *C.char, cArgs *C.char) C.gpgme_error_t { + status := C.GoString(cStatus) + args := C.GoString(cArgs) + c := callbackLookup(uintptr(handle)).(*AssuanStatusCallback) + if *c == nil { + return 0 + } + (*c)(status, args) + return 0 +} + +// ExportModeFlags defines how keys are exported from Export +type ExportModeFlags uint + +const ( + ExportModeExtern ExportModeFlags = C.GPGME_EXPORT_MODE_EXTERN + ExportModeMinimal ExportModeFlags = C.GPGME_EXPORT_MODE_MINIMAL +) + +func (c *Context) Export(pattern string, mode ExportModeFlags, data *Data) error { + pat := C.CString(pattern) + defer C.free(unsafe.Pointer(pat)) + err := handleError(C.gpgme_op_export(c.ctx, pat, C.gpgme_export_mode_t(mode), data.dh)) + runtime.KeepAlive(c) + runtime.KeepAlive(data) + return err } // ImportStatusFlags describes the type of ImportStatus.Status. The C API in gpgme.h simply uses "unsigned". @@ -517,10 +687,14 @@ type ImportResult struct { func (c *Context) Import(keyData *Data) (*ImportResult, error) { err := handleError(C.gpgme_op_import(c.ctx, keyData.dh)) + runtime.KeepAlive(c) + runtime.KeepAlive(keyData) if err != nil { return nil, err } res := C.gpgme_op_import_result(c.ctx) + runtime.KeepAlive(c) + // NOTE: c must be live as long as we are accessing res. imports := []ImportStatus{} for s := res.imports; s != nil; s = s.next { imports = append(imports, ImportStatus{ @@ -529,7 +703,7 @@ func (c *Context) Import(keyData *Data) (*ImportResult, error) { Status: ImportStatusFlags(s.status), }) } - return &ImportResult{ + importResult := &ImportResult{ Considered: int(res.considered), NoUserID: int(res.no_user_id), Imported: int(res.imported), @@ -544,11 +718,13 @@ func (c *Context) Import(keyData *Data) (*ImportResult, error) { SecretUnchanged: int(res.secret_unchanged), NotImported: int(res.not_imported), Imports: imports, - }, nil + } + runtime.KeepAlive(c) // for all accesses to res above + return importResult, nil } type Key struct { - k C.gpgme_key_t + k C.gpgme_key_t // WARNING: Call Runtime.KeepAlive(k) after ANY passing of k.k to C } func newKey() *Key { @@ -559,85 +735,122 @@ func newKey() *Key { func (k *Key) Release() { C.gpgme_key_release(k.k) + runtime.KeepAlive(k) k.k = nil } func (k *Key) Revoked() bool { - return C.key_revoked(k.k) != 0 + res := C.key_revoked(k.k) != 0 + runtime.KeepAlive(k) + return res } func (k *Key) Expired() bool { - return C.key_expired(k.k) != 0 + res := C.key_expired(k.k) != 0 + runtime.KeepAlive(k) + return res } func (k *Key) Disabled() bool { - return C.key_disabled(k.k) != 0 + res := C.key_disabled(k.k) != 0 + runtime.KeepAlive(k) + return res } func (k *Key) Invalid() bool { - return C.key_invalid(k.k) != 0 + res := C.key_invalid(k.k) != 0 + runtime.KeepAlive(k) + return res } func (k *Key) CanEncrypt() bool { - return C.key_can_encrypt(k.k) != 0 + res := C.key_can_encrypt(k.k) != 0 + runtime.KeepAlive(k) + return res } func (k *Key) CanSign() bool { - return C.key_can_sign(k.k) != 0 + res := C.key_can_sign(k.k) != 0 + runtime.KeepAlive(k) + return res } func (k *Key) CanCertify() bool { - return C.key_can_certify(k.k) != 0 + res := C.key_can_certify(k.k) != 0 + runtime.KeepAlive(k) + return res } func (k *Key) Secret() bool { - return C.key_secret(k.k) != 0 + res := C.key_secret(k.k) != 0 + runtime.KeepAlive(k) + return res } func (k *Key) CanAuthenticate() bool { - return C.key_can_authenticate(k.k) != 0 + res := C.key_can_authenticate(k.k) != 0 + runtime.KeepAlive(k) + return res } func (k *Key) IsQualified() bool { - return C.key_is_qualified(k.k) != 0 + res := C.key_is_qualified(k.k) != 0 + runtime.KeepAlive(k) + return res } func (k *Key) Protocol() Protocol { - return Protocol(k.k.protocol) + res := Protocol(k.k.protocol) + runtime.KeepAlive(k) + return res } func (k *Key) IssuerSerial() string { - return C.GoString(k.k.issuer_serial) + res := C.GoString(k.k.issuer_serial) + runtime.KeepAlive(k) + return res } func (k *Key) IssuerName() string { - return C.GoString(k.k.issuer_name) + res := C.GoString(k.k.issuer_name) + runtime.KeepAlive(k) + return res } func (k *Key) ChainID() string { - return C.GoString(k.k.chain_id) + res := C.GoString(k.k.chain_id) + runtime.KeepAlive(k) + return res } func (k *Key) OwnerTrust() Validity { - return Validity(k.k.owner_trust) + res := Validity(k.k.owner_trust) + runtime.KeepAlive(k) + return res } func (k *Key) SubKeys() *SubKey { - if k.k.subkeys == nil { + subKeys := k.k.subkeys + runtime.KeepAlive(k) + if subKeys == nil { return nil } - return &SubKey{k: k.k.subkeys, parent: k} + return &SubKey{k: subKeys, parent: k} // The parent: k reference ensures subKeys remains valid } func (k *Key) UserIDs() *UserID { - if k.k.uids == nil { + uids := k.k.uids + runtime.KeepAlive(k) + if uids == nil { return nil } - return &UserID{u: k.k.uids, parent: k} + return &UserID{u: uids, parent: k} // The parent: k reference ensures uids remains valid } func (k *Key) KeyListMode() KeyListMode { - return KeyListMode(k.k.keylist_mode) + res := KeyListMode(k.k.keylist_mode) + runtime.KeepAlive(k) + return res } type SubKey struct { @@ -737,12 +950,3 @@ func (u *UserID) Comment() string { func (u *UserID) Email() string { return C.GoString(u.u.email) } - -// This is somewhat of a horrible hack. We need to unset GPG_AGENT_INFO so that gpgme does not pass --use-agent to GPG. -// os.Unsetenv should be enough, but that only calls the underlying C library (which gpgme uses) if cgo is involved -// - and cgo can't be used in tests. So, provide this helper for test initialization. -func unsetenvGPGAgentInfo() { - v := C.CString("GPG_AGENT_INFO") - defer C.free(unsafe.Pointer(v)) - C.unsetenv(v) -} diff --git a/vendor/github.com/mtrmac/gpgme/unset_agent_info.go b/vendor/github.com/mtrmac/gpgme/unset_agent_info.go new file mode 100644 index 000000000..986aca59f --- /dev/null +++ b/vendor/github.com/mtrmac/gpgme/unset_agent_info.go @@ -0,0 +1,18 @@ +// +build !windows + +package gpgme + +// #include <stdlib.h> +import "C" +import ( + "unsafe" +) + +// This is somewhat of a horrible hack. We need to unset GPG_AGENT_INFO so that gpgme does not pass --use-agent to GPG. +// os.Unsetenv should be enough, but that only calls the underlying C library (which gpgme uses) if cgo is involved +// - and cgo can't be used in tests. So, provide this helper for test initialization. +func unsetenvGPGAgentInfo() { + v := C.CString("GPG_AGENT_INFO") + defer C.free(unsafe.Pointer(v)) + C.unsetenv(v) +} diff --git a/vendor/github.com/mtrmac/gpgme/unset_agent_info_windows.go b/vendor/github.com/mtrmac/gpgme/unset_agent_info_windows.go new file mode 100644 index 000000000..431ec86d3 --- /dev/null +++ b/vendor/github.com/mtrmac/gpgme/unset_agent_info_windows.go @@ -0,0 +1,14 @@ +package gpgme + +// #include <stdlib.h> +import "C" +import ( + "unsafe" +) + +// unsetenv is not available in mingw +func unsetenvGPGAgentInfo() { + v := C.CString("GPG_AGENT_INFO=") + defer C.free(unsafe.Pointer(v)) + C.putenv(v) +} diff --git a/vendor/github.com/vbauerster/mpb/v4/bar_option.go b/vendor/github.com/vbauerster/mpb/v4/bar_option.go index bb79ac6a4..7fb152562 100644 --- a/vendor/github.com/vbauerster/mpb/v4/bar_option.go +++ b/vendor/github.com/vbauerster/mpb/v4/bar_option.go @@ -10,11 +10,10 @@ import ( // BarOption is a function option which changes the default behavior of a bar. type BarOption func(*bState) -type mergeWrapper interface { - MergeUnwrap() []decor.Decorator -} - func (s *bState) addDecorators(dest *[]decor.Decorator, decorators ...decor.Decorator) { + type mergeWrapper interface { + MergeUnwrap() []decor.Decorator + } for _, decorator := range decorators { if mw, ok := decorator.(mergeWrapper); ok { *dest = append(*dest, mw.MergeUnwrap()...) diff --git a/vendor/github.com/vbauerster/mpb/v4/decor/decorator.go b/vendor/github.com/vbauerster/mpb/v4/decor/decorator.go index 5c0d16880..2271cbbe1 100644 --- a/vendor/github.com/vbauerster/mpb/v4/decor/decorator.go +++ b/vendor/github.com/vbauerster/mpb/v4/decor/decorator.go @@ -4,6 +4,8 @@ import ( "fmt" "time" "unicode/utf8" + + "github.com/acarl005/stripansi" ) const ( @@ -117,25 +119,29 @@ var ( // W represents width and C represents bit set of width related config. // A decorator should embed WC, to enable width synchronization. type WC struct { - W int - C int - dynFormat string - staticFormat string - wsync chan int + W int + C int + dynFormat string + wsync chan int } // FormatMsg formats final message according to WC.W and WC.C. // Should be called by any Decorator implementation. func (wc *WC) FormatMsg(msg string) string { + var format string + runeCount := utf8.RuneCountInString(stripansi.Strip(msg)) + ansiCount := utf8.RuneCountInString(msg) - runeCount if (wc.C & DSyncWidth) != 0 { - wc.wsync <- utf8.RuneCountInString(msg) - max := <-wc.wsync if (wc.C & DextraSpace) != 0 { - max++ + runeCount++ } - return fmt.Sprintf(fmt.Sprintf(wc.dynFormat, max), msg) + wc.wsync <- runeCount + max := <-wc.wsync + format = fmt.Sprintf(wc.dynFormat, ansiCount+max) + } else { + format = fmt.Sprintf(wc.dynFormat, ansiCount+wc.W) } - return fmt.Sprintf(wc.staticFormat, msg) + return fmt.Sprintf(format, msg) } // Init initializes width related config. @@ -145,7 +151,6 @@ func (wc *WC) Init() WC { wc.dynFormat += "-" } wc.dynFormat += "%ds" - wc.staticFormat = fmt.Sprintf(wc.dynFormat, wc.W) if (wc.C & DSyncWidth) != 0 { // it's deliberate choice to override wsync on each Init() call, // this way globals like WCSyncSpace can be reused diff --git a/vendor/github.com/vbauerster/mpb/v4/decor/merge.go b/vendor/github.com/vbauerster/mpb/v4/decor/merge.go index fdf9e107b..723869209 100644 --- a/vendor/github.com/vbauerster/mpb/v4/decor/merge.go +++ b/vendor/github.com/vbauerster/mpb/v4/decor/merge.go @@ -2,6 +2,7 @@ package decor import ( "fmt" + "strings" "unicode/utf8" ) @@ -28,10 +29,7 @@ func Merge(decorator Decorator, placeholders ...WC) Decorator { if (wc.C & DSyncWidth) == 0 { return decorator } - md.placeHolders[i] = &placeHolderDecorator{ - WC: wc.Init(), - wch: make(chan int), - } + md.placeHolders[i] = &placeHolderDecorator{wc.Init()} } return md } @@ -69,29 +67,40 @@ func (d *mergeDecorator) Base() Decorator { func (d *mergeDecorator) Decor(st *Statistics) string { msg := d.Decorator.Decor(st) msgLen := utf8.RuneCountInString(msg) - - var space int - for _, ph := range d.placeHolders { - space += <-ph.wch + if (d.wc.C & DextraSpace) != 0 { + msgLen++ } - d.wc.wsync <- msgLen - space + var total int + max := utf8.RuneCountInString(d.placeHolders[0].FormatMsg("")) + total += max + pw := (msgLen - max) / len(d.placeHolders) + rem := (msgLen - max) % len(d.placeHolders) - max := <-d.wc.wsync - if (d.wc.C & DextraSpace) != 0 { - max++ + var diff int + for i := 1; i < len(d.placeHolders); i++ { + ph := d.placeHolders[i] + width := pw - diff + if (ph.WC.C & DextraSpace) != 0 { + width-- + if width < 0 { + width = 0 + } + } + max = utf8.RuneCountInString(ph.FormatMsg(strings.Repeat(" ", width))) + total += max + diff = max - pw } - return fmt.Sprintf(fmt.Sprintf(d.wc.dynFormat, max+space), msg) + + d.wc.wsync <- pw + rem + max = <-d.wc.wsync + return fmt.Sprintf(fmt.Sprintf(d.wc.dynFormat, max+total), msg) } type placeHolderDecorator struct { WC - wch chan int } -func (d *placeHolderDecorator) Decor(st *Statistics) string { - go func() { - d.wch <- utf8.RuneCountInString(d.FormatMsg("")) - }() +func (d *placeHolderDecorator) Decor(_ *Statistics) string { return "" } diff --git a/vendor/github.com/vbauerster/mpb/v4/go.mod b/vendor/github.com/vbauerster/mpb/v4/go.mod index 0c5ce51f1..9e7287d5d 100644 --- a/vendor/github.com/vbauerster/mpb/v4/go.mod +++ b/vendor/github.com/vbauerster/mpb/v4/go.mod @@ -2,6 +2,7 @@ module github.com/vbauerster/mpb/v4 require ( github.com/VividCortex/ewma v1.1.1 + github.com/acarl005/stripansi v0.0.0-20180116102854-5a71ef0e047d golang.org/x/crypto v0.0.0-20191112222119-e1110fd1c708 golang.org/x/sys v0.0.0-20191113165036-4c7a9d0fe056 // indirect ) diff --git a/vendor/github.com/vbauerster/mpb/v4/go.sum b/vendor/github.com/vbauerster/mpb/v4/go.sum index 94a9f1a28..5a1316274 100644 --- a/vendor/github.com/vbauerster/mpb/v4/go.sum +++ b/vendor/github.com/vbauerster/mpb/v4/go.sum @@ -1,5 +1,7 @@ github.com/VividCortex/ewma v1.1.1 h1:MnEK4VOv6n0RSY4vtRe3h11qjxL3+t0B8yOL8iMXdcM= github.com/VividCortex/ewma v1.1.1/go.mod h1:2Tkkvm3sRDVXaiyucHiACn4cqf7DpdyLvmxzcbUokwA= +github.com/acarl005/stripansi v0.0.0-20180116102854-5a71ef0e047d h1:licZJFw2RwpHMqeKTCYkitsPqHNxTmd4SNR5r94FGM8= +github.com/acarl005/stripansi v0.0.0-20180116102854-5a71ef0e047d/go.mod h1:asat636LX7Bqt5lYEZ27JNDcqxfjdBQuJ/MM4CN/Lzo= golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= golang.org/x/crypto v0.0.0-20191112222119-e1110fd1c708 h1:pXVtWnwHkrWD9ru3sDxY/qFK/bfc0egRovX91EjWjf4= golang.org/x/crypto v0.0.0-20191112222119-e1110fd1c708/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= diff --git a/vendor/github.com/vbauerster/mpb/v4/proxyreader.go b/vendor/github.com/vbauerster/mpb/v4/proxyreader.go index 736142412..0e4b51f09 100644 --- a/vendor/github.com/vbauerster/mpb/v4/proxyreader.go +++ b/vendor/github.com/vbauerster/mpb/v4/proxyreader.go @@ -18,9 +18,7 @@ func (prox *proxyReader) Read(p []byte) (n int, err error) { prox.iT = time.Now() } if err == io.EOF { - go func() { - prox.bar.SetTotal(0, true) - }() + go prox.bar.SetTotal(0, true) } return } @@ -37,9 +35,7 @@ func (prox *proxyWriterTo) WriteTo(w io.Writer) (n int64, err error) { prox.iT = time.Now() } if err == io.EOF { - go func() { - prox.bar.SetTotal(0, true) - }() + go prox.bar.SetTotal(0, true) } return } |