├── .drone.yml ├── .gitignore ├── DOCS.md ├── Dockerfile ├── Dockerfile.armhf ├── LICENSE ├── Makefile ├── README.md ├── cache ├── archive.go ├── cache.go ├── cache_test.go └── sftp │ └── sftp.go ├── logo.svg ├── main.go ├── plugin.go ├── plugin_test.go ├── tests ├── .ssh │ ├── id_rsa │ └── id_rsa.pub ├── a.txt ├── b.txt └── entrypoint.sh └── vendor ├── github.com ├── davecgh │ └── go-spew │ │ ├── LICENSE │ │ └── spew │ │ ├── bypass.go │ │ ├── bypasssafe.go │ │ ├── common.go │ │ ├── config.go │ │ ├── doc.go │ │ ├── dump.go │ │ ├── format.go │ │ └── spew.go ├── joho │ └── godotenv │ │ ├── LICENCE │ │ ├── README.md │ │ ├── autoload │ │ └── autoload.go │ │ └── godotenv.go ├── kr │ └── fs │ │ ├── LICENSE │ │ ├── Readme │ │ ├── filesystem.go │ │ └── walk.go ├── pkg │ ├── errors │ │ ├── LICENSE │ │ ├── README.md │ │ ├── appveyor.yml │ │ ├── errors.go │ │ └── stack.go │ └── sftp │ │ ├── CONTRIBUTORS │ │ ├── LICENSE │ │ ├── README.md │ │ ├── attrs.go │ │ ├── attrs_stubs.go │ │ ├── attrs_unix.go │ │ ├── client.go │ │ ├── conn.go │ │ ├── debug.go │ │ ├── match.go │ │ ├── packet-manager.go │ │ ├── packet-manager_go1.8.go │ │ ├── packet-manager_legacy.go │ │ ├── packet-typing.go │ │ ├── packet.go │ │ ├── release.go │ │ ├── request-example.go │ │ ├── request-interfaces.go │ │ ├── request-readme.md │ │ ├── request-server.go │ │ ├── request-unix.go │ │ ├── request.go │ │ ├── request_windows.go │ │ ├── server.go │ │ ├── server_statvfs_darwin.go │ │ ├── server_statvfs_impl.go │ │ ├── server_statvfs_linux.go │ │ ├── server_statvfs_stubs.go │ │ ├── server_stubs.go │ │ ├── server_unix.go │ │ └── sftp.go ├── pmezard │ └── go-difflib │ │ ├── LICENSE │ │ └── difflib │ │ └── difflib.go ├── stretchr │ └── testify │ │ ├── LICENSE │ │ └── assert │ │ ├── assertion_forward.go │ │ ├── assertion_forward.go.tmpl │ │ ├── assertions.go │ │ ├── doc.go │ │ ├── errors.go │ │ ├── forward_assertions.go │ │ └── http_assertions.go └── urfave │ └── cli │ ├── CHANGELOG.md │ ├── LICENSE │ ├── README.md │ ├── app.go │ ├── appveyor.yml │ ├── category.go │ ├── cli.go │ ├── command.go │ ├── context.go │ ├── errors.go │ ├── flag-types.json │ ├── flag.go │ ├── flag_generated.go │ ├── funcs.go │ ├── generate-flag-types │ ├── help.go │ └── runtests ├── golang.org └── x │ └── crypto │ ├── LICENSE │ ├── PATENTS │ ├── curve25519 │ ├── const_amd64.h │ ├── const_amd64.s │ ├── cswap_amd64.s │ ├── curve25519.go │ ├── doc.go │ ├── freeze_amd64.s │ ├── ladderstep_amd64.s │ ├── mont25519_amd64.go │ ├── mul_amd64.s │ └── square_amd64.s │ ├── ed25519 │ ├── ed25519.go │ └── internal │ │ └── edwards25519 │ │ ├── const.go │ │ └── edwards25519.go │ └── ssh │ ├── buffer.go │ ├── certs.go │ ├── channel.go │ ├── cipher.go │ ├── client.go │ ├── client_auth.go │ ├── common.go │ ├── connection.go │ ├── doc.go │ ├── handshake.go │ ├── kex.go │ ├── keys.go │ ├── mac.go │ ├── messages.go │ ├── mux.go │ ├── server.go │ ├── session.go │ ├── streamlocal.go │ ├── tcpip.go │ └── transport.go └── vendor.json /.drone.yml: -------------------------------------------------------------------------------- 1 | workspace: 2 | base: /go/src/ 3 | path: github.com/appleboy/drone-sftp-cache 4 | 5 | clone: 6 | git: 7 | image: plugins/git 8 | depth: 50 9 | tags: true 10 | 11 | pipeline: 12 | test: 13 | image: appleboy/golang-testing 14 | pull: true 15 | commands: 16 | - make vet 17 | - make lint 18 | - coverage all 19 | - make coverage 20 | - make build 21 | # build binary for docker image 22 | - make static_build 23 | when: 24 | event: [ push, tag, pull_request ] 25 | 26 | codecov: 27 | image: robertstettner/drone-codecov 28 | group: build 29 | secrets: [ codecov_token ] 30 | files: 31 | - .cover/coverage.txt 32 | when: 33 | event: [ push, pull_request ] 34 | 35 | # deploy image from tag event 36 | docker: 37 | image: plugins/docker 38 | repo: ${DRONE_REPO} 39 | tags: [ '${DRONE_TAG}' ] 40 | secrets: [ docker_username, docker_password ] 41 | when: 42 | event: [ tag ] 43 | branch: [ refs/tags/* ] 44 | 45 | # deploy image from master branch 46 | docker: 47 | image: plugins/docker 48 | repo: ${DRONE_REPO} 49 | tags: [ 'latest' ] 50 | secrets: [ docker_username, docker_password ] 51 | when: 52 | event: [ push ] 53 | branch: [ master ] 54 | 55 | discord: 56 | image: appleboy/drone-discord 57 | pull: true 58 | secrets: [ discord_webhook_id, discord_webhook_token ] 59 | when: 60 | status: [ changed, failure ] 61 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Compiled Object files, Static and Dynamic libs (Shared Objects) 2 | *.o 3 | *.a 4 | *.so 5 | 6 | # Folders 7 | _obj 8 | _test 9 | 10 | # Architecture specific extensions/prefixes 11 | *.[568vq] 12 | [568vq].out 13 | 14 | *.cgo1.go 15 | *.cgo2.c 16 | _cgo_defun.c 17 | _cgo_gotypes.go 18 | _cgo_export.* 19 | 20 | _testmain.go 21 | 22 | *.exe 23 | *.test 24 | *.prof 25 | .env 26 | 27 | coverage.txt 28 | drone-sftp-cache 29 | .cover 30 | -------------------------------------------------------------------------------- /DOCS.md: -------------------------------------------------------------------------------- 1 | --- 2 | date: 2017-04-11T00:00:00+00:00 3 | title: SFTP Cache 4 | author: appleboy 5 | tags: [ publish, ssh, sftp, cache ] 6 | repo: appleboy/drone-sftp-cache 7 | logo: logo.svg 8 | image: appleboy/drone-sftp-cache 9 | --- 10 | 11 | Use this plugin for caching build artifacts to speed up your build times. This plugin can create and restore caches of any folders. 12 | 13 | ```yaml 14 | pipeline: 15 | restore_cache: 16 | image: appleboy/drone-sftp-cache 17 | server: ${SFTP_CACHE_SERVER} 18 | port: ${SFTP_CACHE_PORT} 19 | username: ${SFTP_CACHE_USERNAME} 20 | password: ${SFTP_CACHE_PASSWORD} 21 | path: /var/cache/drone 22 | restore: true 23 | mount: 24 | - node_modules 25 | 26 | build: 27 | image: node:latest 28 | commands: 29 | - npm install 30 | 31 | rebuild_cache: 32 | image: appleboy/drone-sftp-cache 33 | server: ${SFTP_CACHE_SERVER} 34 | port: ${SFTP_CACHE_PORT} 35 | username: ${SFTP_CACHE_USERNAME} 36 | password: ${SFTP_CACHE_PASSWORD} 37 | path: /var/cache/drone 38 | rebuild: true 39 | mount: 40 | - node_modules 41 | ``` 42 | 43 | Example configuration for login with user private key: 44 | 45 | ```diff 46 | pipeline: 47 | rebuild_cache: 48 | image: appleboy/drone-sftp-cache 49 | server: ${SFTP_CACHE_SERVER} 50 | port: ${SFTP_CACHE_PORT} 51 | username: ${SFTP_CACHE_USERNAME} 52 | - password: ${SFTP_CACHE_PASSWORD} 53 | + key: ${SFTP_CACHE_PRIVATE_KEY} 54 | path: /var/cache/drone 55 | rebuild: true 56 | mount: 57 | - node_modules 58 | ``` 59 | 60 | Example configuration for ignoring creates a hash file name based on branch name: 61 | 62 | ```diff 63 | pipeline: 64 | rebuild_cache: 65 | image: appleboy/drone-sftp-cache 66 | server: ${SFTP_CACHE_SERVER} 67 | port: ${SFTP_CACHE_PORT} 68 | username: ${SFTP_CACHE_USERNAME} 69 | password: ${SFTP_CACHE_PASSWORD} 70 | + ignore_branch: true 71 | path: /var/cache/drone 72 | rebuild: true 73 | mount: 74 | - node_modules 75 | ``` 76 | 77 | Example configuration for success build: 78 | 79 | ```diff 80 | pipeline: 81 | rebuild_cache: 82 | image: appleboy/drone-sftp-cache 83 | path: /var/cache/drone 84 | rebuild: true 85 | mount: 86 | - node_modules 87 | + when: 88 | + status: success 89 | ``` 90 | 91 | Example configuration for tag event: 92 | 93 | ```diff 94 | pipeline: 95 | rebuild_cache: 96 | image: appleboy/drone-sftp-cache 97 | path: /var/cache/drone 98 | rebuild: true 99 | mount: 100 | - node_modules 101 | + when: 102 | + status: success 103 | + event: tag 104 | ``` 105 | 106 | # Parameter Reference 107 | 108 | server 109 | : target hostname or IP 110 | 111 | port 112 | : ssh port of target host 113 | 114 | username 115 | : account for target host user 116 | 117 | password 118 | : password for target host user 119 | 120 | key 121 | : plain text of user private key 122 | 123 | rebuild 124 | : boolean flag to trigger a rebuild 125 | 126 | restore 127 | : boolean flag to trigger a restore 128 | 129 | ignore_branch 130 | : boolean flag to ignore commit branch name on hash value 131 | -------------------------------------------------------------------------------- /Dockerfile: -------------------------------------------------------------------------------- 1 | FROM alpine:3.4 2 | 3 | LABEL maintainer="Bo-Yi Wu " 4 | 5 | RUN apk update && \ 6 | apk add \ 7 | ca-certificates && \ 8 | rm -rf /var/cache/apk/* 9 | 10 | ADD drone-sftp-cache /bin/ 11 | ENTRYPOINT ["/bin/drone-sftp-cache"] 12 | -------------------------------------------------------------------------------- /Dockerfile.armhf: -------------------------------------------------------------------------------- 1 | FROM armhfbuild/alpine:3.4 2 | 3 | RUN apk update && \ 4 | apk add \ 5 | ca-certificates && \ 6 | rm -rf /var/cache/apk/* 7 | 8 | ADD drone-sftp-cache /bin/ 9 | ENTRYPOINT ["/bin/drone-sftp-cache"] 10 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | .PHONY: drone-sftp-cache 2 | 3 | EXECUTABLE := drone-sftp-cache 4 | GOFMT ?= gofmt -s 5 | 6 | # for dockerhub 7 | DEPLOY_ACCOUNT := appleboy 8 | DEPLOY_IMAGE := $(EXECUTABLE) 9 | 10 | GOFILES := find . -name "*.go" -type f -not -path "./vendor/*" 11 | PACKAGES ?= $(shell go list ./... | grep -v /vendor/) 12 | SOURCES ?= $(shell find . -name "*.go" -type f) 13 | TAGS ?= 14 | LDFLAGS ?= -X 'main.Version=$(VERSION)' 15 | 16 | ifneq ($(shell uname), Darwin) 17 | EXTLDFLAGS = -extldflags "-static" $(null) 18 | else 19 | EXTLDFLAGS = 20 | endif 21 | 22 | ifneq ($(DRONE_TAG),) 23 | VERSION ?= $(DRONE_TAG) 24 | else 25 | VERSION ?= $(shell git describe --tags --always || git rev-parse --short HEAD) 26 | endif 27 | 28 | all: build 29 | 30 | vet: 31 | go vet $(PACKAGES) 32 | 33 | fmt: 34 | $(GOFILES) | xargs $(GOFMT) -w 35 | 36 | lint: 37 | @which golint > /dev/null; if [ $$? -ne 0 ]; then \ 38 | go get -u github.com/golang/lint/golint; \ 39 | fi 40 | for PKG in $(PACKAGES); do golint -set_exit_status $$PKG || exit 1; done; 41 | 42 | unconvert: 43 | @which unconvert > /dev/null; if [ $$? -ne 0 ]; then \ 44 | go get -u github.com/mdempsky/unconvert; \ 45 | fi 46 | for PKG in $(PACKAGES); do unconvert -v $$PKG || exit 1; done; 47 | 48 | .PHONY: fmt-check 49 | fmt-check: 50 | # get all go files and run go fmt on them 51 | @files=$$($(GOFILES) | xargs $(GOFMT) -l); if [ -n "$$files" ]; then \ 52 | echo "Please run 'make fmt' and commit the result:"; \ 53 | echo "$${files}"; \ 54 | exit 1; \ 55 | fi; 56 | 57 | test: fmt-check 58 | for PKG in $(PACKAGES); do go test -v -cover -coverprofile $$GOPATH/src/$$PKG/coverage.txt $$PKG || exit 1; done; 59 | 60 | install: $(SOURCES) 61 | go install -v -tags '$(TAGS)' -ldflags '$(EXTLDFLAGS)-s -w $(LDFLAGS)' 62 | 63 | build: $(EXECUTABLE) 64 | 65 | $(EXECUTABLE): $(SOURCES) 66 | go build -v -tags '$(TAGS)' -ldflags '$(EXTLDFLAGS)-s -w $(LDFLAGS)' -o $@ 67 | 68 | # for docker. 69 | static_build: 70 | CGO_ENABLED=0 GOOS=linux GOARCH=amd64 go build -a -tags '$(TAGS)' -ldflags '$(EXTLDFLAGS)-s -w $(LDFLAGS)' -o $(DEPLOY_IMAGE) 71 | 72 | docker_image: 73 | docker build -t $(DEPLOY_ACCOUNT)/$(DEPLOY_IMAGE) . 74 | 75 | docker: static_build docker_image 76 | 77 | coverage: 78 | sed -i '/main.go/d' .cover/coverage.txt 79 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # drone-sftp-cache 2 | 3 | [![GoDoc](https://godoc.org/github.com/appleboy/drone-sftp-cache?status.svg)](https://godoc.org/github.com/appleboy/drone-sftp-cache) [![Build Status](http://drone.wu-boy.com/api/badges/appleboy/drone-sftp-cache/status.svg)](http://drone.wu-boy.com/appleboy/drone-sftp-cache) [![codecov](https://codecov.io/gh/appleboy/drone-sftp-cache/branch/master/graph/badge.svg)](https://codecov.io/gh/appleboy/drone-sftp-cache) [![Go Report Card](https://goreportcard.com/badge/github.com/appleboy/drone-sftp-cache)](https://goreportcard.com/report/github.com/appleboy/drone-sftp-cache) [![Docker Pulls](https://img.shields.io/docker/pulls/appleboy/drone-sftp-cache.svg)](https://hub.docker.com/r/appleboy/drone-sftp-cache/) [![](https://images.microbadger.com/badges/image/appleboy/drone-sftp-cache.svg)](https://microbadger.com/images/appleboy/drone-sftp-cache "Get your own image badge on microbadger.com") 4 | 5 | Drone plugin for caching artifacts to a central server using rsync. For the 6 | usage information and a listing of the available options please take a look at 7 | [the docs](DOCS.md). 8 | 9 | ## Build 10 | 11 | Build the binary with the following commands: 12 | 13 | ``` 14 | go build 15 | go test 16 | ``` 17 | 18 | ## Docker 19 | 20 | Build the docker image with the following commands: 21 | 22 | ``` 23 | CGO_ENABLED=0 GOOS=linux GOARCH=amd64 go build -a -tags netgo 24 | docker build --rm=true -t appleboy/drone-sftp-cache . 25 | ``` 26 | 27 | Please note incorrectly building the image for the correct x64 linux and with 28 | GCO disabled will result in an error when running the Docker image: 29 | 30 | ``` 31 | docker: Error response from daemon: Container command 32 | '/bin/drone-sftp-cache' not found or does not exist.. 33 | ``` 34 | 35 | ## Usage 36 | 37 | Execute from the working directory: 38 | 39 | ``` 40 | docker run --rm \ 41 | -e DRONE_REPO=octocat/hello-world \ 42 | -e DRONE_REPO_BRANCH=master \ 43 | -e DRONE_COMMIT_BRANCH=master \ 44 | -e PLUGIN_MOUNT=node_modules \ 45 | -e PLUGIN_RESTORE=false \ 46 | -e PLUGIN_REBUILD=true \ 47 | -e PLUGIN_IGNORE_BRANCH=false \ 48 | -e SFTP_CACHE_SERVER=1.2.3.4 \ 49 | -e SFTP_CACHE_PORT=22 \ 50 | -e SFTP_CACHE_PATH=/root/cache \ 51 | -e SFTP_CACHE_USERNAME=root \ 52 | -e SFTP_CACHE_PRIVATE_KEY="$(cat ~/.ssh/id_rsa)" \ 53 | appleboy/drone-sftp-cache 54 | ``` 55 | -------------------------------------------------------------------------------- /cache/archive.go: -------------------------------------------------------------------------------- 1 | package cache 2 | 3 | // special thanks to this medium article: 4 | // https://medium.com/@skdomino/taring-untaring-files-in-go-6b07cf56bc07 5 | 6 | import ( 7 | "archive/tar" 8 | "io" 9 | "os" 10 | "path/filepath" 11 | "strings" 12 | ) 13 | 14 | // helper function to tar source directory to io.Writer w. 15 | func archive(src string, w io.Writer) error { 16 | 17 | // ensure the src actually exists before trying to tar it 18 | if _, err := os.Stat(src); err != nil { 19 | return err 20 | } 21 | 22 | tw := tar.NewWriter(w) 23 | defer tw.Close() 24 | 25 | // walk path 26 | return filepath.Walk(src, func(file string, fi os.FileInfo, err error) error { 27 | 28 | // return on any error 29 | if err != nil { 30 | return err 31 | } 32 | 33 | // create a new dir/file header 34 | header, err := tar.FileInfoHeader(fi, fi.Name()) 35 | if err != nil { 36 | return err 37 | } 38 | 39 | // update the name to correctly reflect the desired destination when untaring 40 | header.Name = strings.TrimPrefix(strings.Replace(file, src, "", -1), string(filepath.Separator)) 41 | 42 | // write the header 43 | if err := tw.WriteHeader(header); err != nil { 44 | return err 45 | } 46 | 47 | // return on directories since there will be no content to tar 48 | if fi.Mode().IsDir() { 49 | return nil 50 | } 51 | 52 | // open files for taring 53 | f, err := os.Open(file) 54 | defer f.Close() 55 | if err != nil { 56 | return err 57 | } 58 | 59 | // copy file data into tar writer 60 | if _, err := io.Copy(tw, f); err != nil { 61 | return err 62 | } 63 | 64 | return nil 65 | }) 66 | } 67 | 68 | // helper function to untar io.Reader r to the destincation directory. 69 | func extract(dst string, r io.Reader) error { 70 | tr := tar.NewReader(r) 71 | 72 | for { 73 | header, err := tr.Next() 74 | 75 | switch { 76 | 77 | // if no more files are found return 78 | case err == io.EOF: 79 | return nil 80 | 81 | // return any other error 82 | case err != nil: 83 | return err 84 | 85 | // if the header is nil, just skip it (not sure how this happens) 86 | case header == nil: 87 | continue 88 | } 89 | 90 | // the target location where the dir/file should be created 91 | target := filepath.Join(dst, header.Name) 92 | 93 | // the following switch could also be done using fi.Mode(), not sure if there 94 | // a benefit of using one vs. the other. 95 | // fi := header.FileInfo() 96 | 97 | // check the file type 98 | switch header.Typeflag { 99 | 100 | // if its a dir and it doesn't exist create it 101 | case tar.TypeDir: 102 | if _, err := os.Stat(target); err != nil { 103 | if err := os.MkdirAll(target, 0755); err != nil { 104 | return err 105 | } 106 | } 107 | 108 | // if it's a file create it 109 | case tar.TypeReg: 110 | f, err := os.OpenFile(target, os.O_CREATE|os.O_RDWR, os.FileMode(header.Mode)) 111 | if err != nil { 112 | return err 113 | } 114 | defer f.Close() 115 | 116 | // copy over contents 117 | if _, err := io.Copy(f, tr); err != nil { 118 | return err 119 | } 120 | } 121 | } 122 | } 123 | -------------------------------------------------------------------------------- /cache/cache.go: -------------------------------------------------------------------------------- 1 | package cache 2 | 3 | import ( 4 | "io" 5 | "io/ioutil" 6 | "os" 7 | "os/exec" 8 | "path/filepath" 9 | "time" 10 | ) 11 | 12 | // Cache implements operations for caching files. 13 | type Cache interface { 14 | List(string) ([]os.FileInfo, error) 15 | Get(string) (io.ReadCloser, error) 16 | Put(string, time.Duration, io.Reader) error 17 | Remove(string) error 18 | } 19 | 20 | // Rebuild is a helper function that pushes the archived file to the cache. 21 | func Rebuild(c Cache, src, dst string) error { 22 | r, w := io.Pipe() 23 | defer func() { 24 | w.Close() 25 | r.Close() 26 | }() 27 | 28 | c1 := make(chan error) 29 | c2 := make(chan error) 30 | 31 | go func() { 32 | c1 <- archive(src, w) 33 | w.Close() 34 | }() 35 | go func() { 36 | c2 <- c.Put(dst, 0, r) 37 | r.Close() 38 | }() 39 | 40 | err1 := <-c1 41 | err2 := <-c2 42 | if err1 != nil { 43 | return err1 44 | } 45 | return err2 46 | } 47 | 48 | // Restore is a helper function that fetches the archived file from the cache 49 | // and restores to the host machine's file system. 50 | func Restore(c Cache, src, dst string) error { 51 | rc, err := c.Get(src) 52 | if err != nil { 53 | return err 54 | } 55 | defer rc.Close() 56 | 57 | return extract(dst, rc) 58 | } 59 | 60 | // 61 | // NOTE below are alternate implementations of the above functions that use 62 | // the tar command for building archives. 63 | // 64 | 65 | // RebuildCmd is a helper function that pushes the archived file to the cache. 66 | func RebuildCmd(c Cache, src, dst string) (err error) { 67 | 68 | src = filepath.Clean(src) 69 | src, err = filepath.Abs(src) 70 | if err != nil { 71 | return err 72 | } 73 | 74 | // create a temporary file for the archive 75 | dir, err := ioutil.TempDir("", "") 76 | if err != nil { 77 | return err 78 | } 79 | tar := filepath.Join(dir, "archive.tar") 80 | 81 | // run archive command 82 | cmd := exec.Command("tar", "-cf", tar, src) 83 | cmd.Stdout = os.Stdout 84 | cmd.Stderr = os.Stderr 85 | if err := cmd.Run(); err != nil { 86 | return err 87 | } 88 | 89 | // upload file to server 90 | f, err := os.Open(tar) 91 | if err != nil { 92 | return err 93 | } 94 | defer f.Close() 95 | return c.Put(dst, 0, f) 96 | } 97 | 98 | // RestoreCmd is a helper function that fetches the archived file from the cache 99 | // and restores to the host machine's file system. 100 | func RestoreCmd(c Cache, src, dst string) error { 101 | rc, err := c.Get(src) 102 | if err != nil { 103 | return err 104 | } 105 | defer rc.Close() 106 | 107 | // create temp file for archive 108 | temp, err := ioutil.TempFile("", "") 109 | if err != nil { 110 | return err 111 | } 112 | defer func() { 113 | temp.Close() 114 | os.Remove(temp.Name()) 115 | }() 116 | 117 | // download archive to temp file 118 | if _, err := io.Copy(temp, rc); err != nil { 119 | return err 120 | } 121 | 122 | // cleanup after ourself 123 | temp.Close() 124 | 125 | // run extraction command 126 | cmd := exec.Command("tar", "-xf", temp.Name(), "-C", "/") 127 | cmd.Stdout = os.Stdout 128 | cmd.Stderr = os.Stderr 129 | return cmd.Run() 130 | } 131 | -------------------------------------------------------------------------------- /cache/cache_test.go: -------------------------------------------------------------------------------- 1 | package cache 2 | -------------------------------------------------------------------------------- /cache/sftp/sftp.go: -------------------------------------------------------------------------------- 1 | package sftp 2 | 3 | import ( 4 | "io" 5 | "os" 6 | "path/filepath" 7 | "strings" 8 | "time" 9 | 10 | "github.com/appleboy/drone-sftp-cache/cache" 11 | "github.com/pkg/sftp" 12 | "golang.org/x/crypto/ssh" 13 | ) 14 | 15 | // cacher is an SFTP implementation of the Cache. 16 | type cacher struct { 17 | sftp *sftp.Client 18 | ssh *ssh.Client 19 | } 20 | 21 | // List returns a list of all files at the defined path. 22 | func (c *cacher) List(root string) ([]os.FileInfo, error) { 23 | var files []os.FileInfo 24 | 25 | f := c.sftp.Walk(root) 26 | for f.Step() { 27 | if f.Err() != nil { 28 | continue 29 | } 30 | files = append(files, f.Stat()) 31 | } 32 | return files, nil 33 | } 34 | 35 | // Get returns an io.Reader for reading the contents of the file. 36 | func (c *cacher) Get(p string) (io.ReadCloser, error) { 37 | _, err := c.sftp.Stat(p) 38 | if err != nil { 39 | return nil, err 40 | } 41 | return c.sftp.Open(p) 42 | } 43 | 44 | // Put uploads the contents of the io.Reader to the SFTP server. 45 | func (c *cacher) Put(p string, t time.Duration, src io.Reader) error { 46 | if e := c.CreateDirectories(p); e != nil { 47 | return e 48 | } 49 | 50 | dst, err := c.sftp.Create(p) 51 | if err != nil { 52 | return err 53 | } 54 | defer dst.Close() 55 | 56 | _, err = io.Copy(dst, src) 57 | return err 58 | } 59 | 60 | // Remove removes the file from the remote SFTP server. 61 | func (c *cacher) Remove(p string) error { 62 | _, err := c.sftp.Stat(p) 63 | if err != nil { 64 | return err 65 | } 66 | return c.sftp.Remove(p) 67 | } 68 | 69 | // Close closes the SFTP connection. 70 | func (c *cacher) Close() error { 71 | if c.ssh != nil { 72 | c.ssh.Close() 73 | } 74 | if c.ssh != nil { 75 | c.sftp.Close() 76 | } 77 | return nil 78 | } 79 | 80 | // New returns a new SFTP remote Cache implementated. 81 | func New(server, username, password, key, port string) (cache.Cache, error) { 82 | // auths holds the detected ssh auth methods 83 | auths := []ssh.AuthMethod{} 84 | 85 | // figure out what auths are requested, what is supported 86 | if password != "" { 87 | auths = append(auths, ssh.Password(password)) 88 | } 89 | 90 | // private key authentication takes precedence 91 | if key != "" { 92 | signer, err := ssh.ParsePrivateKey([]byte(key)) 93 | if err != nil { 94 | return nil, err 95 | } 96 | auths = append(auths, ssh.PublicKeys(signer)) 97 | } 98 | 99 | config := &ssh.ClientConfig{ 100 | Timeout: time.Minute * 5, 101 | User: username, 102 | Auth: auths, 103 | HostKeyCallback: ssh.InsecureIgnoreHostKey(), 104 | } 105 | 106 | // create the ssh connection and client 107 | client, err := ssh.Dial("tcp", server+":"+port, config) 108 | if err != nil { 109 | return nil, err 110 | } 111 | 112 | // open the sftp session using the ssh connection 113 | sftp, err := sftp.NewClient(client) 114 | if err != nil { 115 | client.Close() 116 | return nil, err 117 | } 118 | 119 | return &cacher{sftp, client}, nil 120 | } 121 | 122 | // CreateDirectories creates repo directories on sftp server. 123 | // It works like mkdir -p /foo/bar 124 | func (c *cacher) CreateDirectories(p string) error { 125 | pathElements := strings.Split(filepath.Dir(p), "/") 126 | path := "/" 127 | 128 | for _, el := range pathElements { 129 | path = filepath.Join(path, el) 130 | if _, serr := c.sftp.Stat(path); serr != nil { 131 | err := c.sftp.Mkdir(path) 132 | if err != nil { 133 | return err 134 | } 135 | } 136 | } 137 | 138 | return nil 139 | } 140 | -------------------------------------------------------------------------------- /logo.svg: -------------------------------------------------------------------------------- 1 | 2 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | -------------------------------------------------------------------------------- /main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "log" 5 | "os" 6 | 7 | "github.com/joho/godotenv" 8 | _ "github.com/joho/godotenv/autoload" 9 | "github.com/urfave/cli" 10 | ) 11 | 12 | // Version set at compile-time 13 | var Version string 14 | 15 | func main() { 16 | app := cli.NewApp() 17 | app.Name = "sftp cache plugin" 18 | app.Usage = "sftp cache plugin" 19 | app.Action = run 20 | app.Version = Version 21 | app.Flags = []cli.Flag{ 22 | cli.StringFlag{ 23 | Name: "repo.name", 24 | Usage: "repository full name", 25 | EnvVar: "DRONE_REPO", 26 | }, 27 | cli.StringFlag{ 28 | Name: "repo.branch", 29 | Usage: "repository default branch", 30 | EnvVar: "DRONE_REPO_BRANCH", 31 | }, 32 | cli.StringFlag{ 33 | Name: "commit.branch", 34 | Value: "master", 35 | Usage: "repository branch", 36 | EnvVar: "DRONE_COMMIT_BRANCH", 37 | }, 38 | cli.StringSliceFlag{ 39 | Name: "mount", 40 | Usage: "cache directories", 41 | EnvVar: "PLUGIN_MOUNT", 42 | }, 43 | cli.BoolFlag{ 44 | Name: "rebuild", 45 | Usage: "rebuild the cache directories", 46 | EnvVar: "PLUGIN_REBUILD", 47 | }, 48 | cli.BoolFlag{ 49 | Name: "restore", 50 | Usage: "restore the cache directories", 51 | EnvVar: "PLUGIN_RESTORE", 52 | }, 53 | cli.BoolFlag{ 54 | Name: "ignore_branch", 55 | Usage: "ignore branch name on hash value", 56 | EnvVar: "PLUGIN_IGNORE_BRANCH", 57 | }, 58 | cli.StringFlag{ 59 | Name: "server", 60 | Usage: "sftp server", 61 | EnvVar: "SFTP_CACHE_SERVER,PLUGIN_SERVER", 62 | }, 63 | cli.StringFlag{ 64 | Name: "port", 65 | Usage: "sftp server port", 66 | EnvVar: "SFTP_CACHE_PORT,PLUGIN_PORT", 67 | Value: "22", 68 | }, 69 | cli.StringFlag{ 70 | Name: "path", 71 | Usage: "sftp server path", 72 | EnvVar: "SFTP_CACHE_PATH,PLUGIN_PATH", 73 | Value: "/var/lib/cache/drone", 74 | }, 75 | cli.StringFlag{ 76 | Name: "username", 77 | Usage: "sftp username", 78 | EnvVar: "SFTP_CACHE_USERNAME,PLUGIN_USERNAME", 79 | Value: "root", 80 | }, 81 | cli.StringFlag{ 82 | Name: "password", 83 | Usage: "sftp password", 84 | EnvVar: "SFTP_CACHE_PASSWORD,PLUGIN_PASSWORD", 85 | }, 86 | cli.StringFlag{ 87 | Name: "key", 88 | Usage: "sftp private key", 89 | EnvVar: "SFTP_CACHE_PRIVATE_KEY,PLUGIN_KEY", 90 | }, 91 | cli.StringFlag{ 92 | Name: "env-file", 93 | Usage: "source env file", 94 | }, 95 | cli.StringFlag{ 96 | Name: "commit.message", 97 | Usage: "commit message", 98 | EnvVar: "DRONE_COMMIT_MESSAGE", 99 | }, 100 | } 101 | 102 | if err := app.Run(os.Args); err != nil { 103 | log.Fatal(err) 104 | } 105 | } 106 | 107 | func run(c *cli.Context) error { 108 | if c.String("env-file") != "" { 109 | _ = godotenv.Load(c.String("env-file")) 110 | } 111 | 112 | plugin := Plugin{ 113 | IgnoreBranch: c.Bool("ignore_branch"), 114 | Rebuild: c.Bool("rebuild"), 115 | Restore: c.Bool("restore"), 116 | Server: c.String("server"), 117 | Port: c.String("port"), 118 | Username: c.String("username"), 119 | Password: c.String("password"), 120 | Key: c.String("key"), 121 | Mount: c.StringSlice("mount"), 122 | Path: c.String("path"), 123 | Repo: c.String("repo.name"), 124 | Default: c.String("repo.branch"), 125 | Branch: c.String("commit.branch"), 126 | Message: c.String("commit.message"), 127 | } 128 | 129 | return plugin.Exec() 130 | } 131 | -------------------------------------------------------------------------------- /plugin.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "crypto/md5" 5 | "errors" 6 | "fmt" 7 | "io" 8 | "log" 9 | "path/filepath" 10 | "regexp" 11 | "time" 12 | 13 | "github.com/appleboy/drone-sftp-cache/cache" 14 | "github.com/appleboy/drone-sftp-cache/cache/sftp" 15 | ) 16 | 17 | var skipRe = regexp.MustCompile(`\[(?i:cache *skip|skip *cache)\]`) 18 | 19 | // Plugin for caching directories to an SFTP server. 20 | type Plugin struct { 21 | IgnoreBranch bool 22 | Rebuild bool 23 | Restore bool 24 | Server string 25 | Port string 26 | Username string 27 | Password string 28 | Key string 29 | Mount []string 30 | Path string 31 | Repo string 32 | Branch string 33 | Default string 34 | Message string 35 | } 36 | 37 | func (p *Plugin) check() error { 38 | if len(p.Server) == 0 || len(p.Username) == 0 { 39 | return errors.New("missing sftp server and username config") 40 | } 41 | 42 | if len(p.Password) == 0 && len(p.Key) == 0 { 43 | return errors.New("missing sftp password or private key") 44 | } 45 | 46 | return nil 47 | } 48 | 49 | // Exec executes the plugin. 50 | func (p *Plugin) Exec() error { 51 | if err := p.check(); err != nil { 52 | return err 53 | } 54 | 55 | sftp, err := sftp.New( 56 | p.Server, 57 | p.Username, 58 | p.Password, 59 | p.Key, 60 | p.Port, 61 | ) 62 | 63 | if err != nil { 64 | return err 65 | } 66 | 67 | defer sftp.(io.Closer).Close() 68 | 69 | if p.Rebuild { 70 | now := time.Now() 71 | err = p.ProcessRebuild(sftp) 72 | log.Printf("cache built in %v\n", time.Since(now)) 73 | } 74 | 75 | if p.Restore { 76 | // skip the restore if any case-insensitive combination of the words "skip" and "cache" 77 | skipMatch := skipRe.FindString(p.Message) 78 | if len(skipMatch) > 0 { 79 | log.Printf("skip restore cache. %s found in '%s'\n", skipMatch, p.Message) 80 | return nil 81 | } 82 | 83 | now := time.Now() 84 | err = p.ProcessRestore(sftp) 85 | log.Printf("cache restored in %v\n", time.Since(now)) 86 | } 87 | 88 | if err != nil { 89 | log.Println(err) 90 | } 91 | 92 | return nil 93 | } 94 | 95 | // ProcessRebuild rebuild the remote cache from the local environment. 96 | func (p Plugin) ProcessRebuild(c cache.Cache) error { 97 | for _, mount := range p.Mount { 98 | var hash string 99 | if p.IgnoreBranch { 100 | hash = hasher(mount) 101 | } else { 102 | hash = hasher(mount, p.Branch) 103 | } 104 | path := filepath.Join(p.Path, p.Repo, hash) 105 | 106 | log.Printf("archiving directory <%s> to remote cache <%s>\n", mount, path) 107 | 108 | err := cache.RebuildCmd(c, mount, path) 109 | if err != nil { 110 | return err 111 | } 112 | } 113 | return nil 114 | } 115 | 116 | // ProcessRestore restore the local environment from the remote cache. 117 | func (p Plugin) ProcessRestore(c cache.Cache) error { 118 | for _, mount := range p.Mount { 119 | var hash string 120 | if p.IgnoreBranch { 121 | hash = hasher(mount) 122 | } else { 123 | hash = hasher(mount, p.Branch) 124 | } 125 | path := filepath.Join(p.Path, p.Repo, hash) 126 | 127 | log.Printf("restoring directory <%s> from remote cache <%s>\n", mount, path) 128 | 129 | err := cache.RestoreCmd(c, path, mount) 130 | if err != nil { 131 | return err 132 | } 133 | } 134 | return nil 135 | } 136 | 137 | // helper function to hash a file name based on path and branch. 138 | func hasher(args ...string) string { 139 | // calculate the hash using the branch 140 | h := md5.New() 141 | for _, part := range args { 142 | io.WriteString(h, part) 143 | } 144 | return fmt.Sprintf("%x", h.Sum(nil)) 145 | } 146 | -------------------------------------------------------------------------------- /plugin_test.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "testing" 5 | 6 | "github.com/stretchr/testify/assert" 7 | ) 8 | 9 | func TestPlugin_check(t *testing.T) { 10 | tests := []struct { 11 | name string 12 | fields Plugin 13 | wantErr bool 14 | }{ 15 | { 16 | "Test missing server or username", 17 | Plugin{ 18 | Server: "localhost", 19 | }, 20 | true, 21 | }, 22 | { 23 | "Test missing password or key", 24 | Plugin{ 25 | Server: "localhost", 26 | Username: "ubuntu", 27 | }, 28 | true, 29 | }, 30 | } 31 | for _, tt := range tests { 32 | p := &Plugin{ 33 | Server: tt.fields.Server, 34 | Username: tt.fields.Username, 35 | Password: tt.fields.Password, 36 | Key: tt.fields.Key, 37 | } 38 | if err := p.check(); (err != nil) != tt.wantErr { 39 | t.Errorf("%q. Plugin.check() error = %v, wantErr %v", tt.name, err, tt.wantErr) 40 | } 41 | } 42 | } 43 | 44 | func TestConfigCheck(t *testing.T) { 45 | plugin := Plugin{ 46 | Server: "localhost", 47 | Username: "drone-scp", 48 | } 49 | 50 | err := plugin.Exec() 51 | assert.NotNil(t, err) 52 | } 53 | 54 | func TestIncorrectPassword(t *testing.T) { 55 | plugin := Plugin{ 56 | Server: "localhost", 57 | Username: "drone-scp", 58 | Port: "22", 59 | Password: "123456", 60 | } 61 | 62 | err := plugin.Exec() 63 | assert.NotNil(t, err) 64 | } 65 | -------------------------------------------------------------------------------- /tests/.ssh/id_rsa: -------------------------------------------------------------------------------- 1 | -----BEGIN RSA PRIVATE KEY----- 2 | MIIEpAIBAAKCAQEA4e2D/qPN08pzTac+a8ZmlP1ziJOXk45CynMPtva0rtK/RB26 3 | VbfAF0hIJji7ltvnYnqCU9oFfvEM33cTn7T96+od8ib/Vz25YU8ZbstqtIskPuwC 4 | bv3K0mAHgsviJyRD7yM+QKTbBQEgbGuW6gtbMKhiYfiIB4Dyj7AdS/fk3v26wDgz 5 | 7SHI5OBqu9bv1KhxQYdFEnU3PAtAqeccgzNpbH3eYLyGzuUxEIJlhpZ/uU2G9ppj 6 | /cSrONVPiI8Ahi4RrlZjmP5l57/sq1ClGulyLpFcMw68kP5FikyqHpHJHRBNgU57 7 | 1y0Ph33SjBbs0haCIAcmreWEhGe+/OXnJe6VUQIDAQABAoIBAH97emORIm9DaVSD 8 | 7mD6DqA7c5m5Tmpgd6eszU08YC/Vkz9oVuBPUwDQNIX8tT0m0KVs42VVPIyoj874 9 | bgZMJoucC1G8V5Bur9AMxhkShx9g9A7dNXJTmsKilRpk2TOk7wBdLp9jZoKoZBdJ 10 | jlp6FfaazQjjKD6zsCsMATwAoRCBpBNsmT6QDN0n0bIgY0tE6YGQaDdka0dAv68G 11 | R0VZrcJ9voT6+f+rgJLoojn2DAu6iXaM99Gv8FK91YCymbQlXXgrk6CyS0IHexN7 12 | V7a3k767KnRbrkqd3o6JyNun/CrUjQwHs1IQH34tvkWScbseRaFehcAm6mLT93RP 13 | muauvMECgYEA9AXGtfDMse0FhvDPZx4mx8x+vcfsLvDHcDLkf/lbyPpu97C27b/z 14 | ia07bu5TAXesUZrWZtKA5KeRE5doQSdTOv1N28BEr8ZwzDJwfn0DPUYUOxsN2iIy 15 | MheO5A45Ko7bjKJVkZ61Mb1UxtqCTF9mqu9R3PBdJGthWOd+HUvF460CgYEA7QRf 16 | Z8+vpGA+eSuu29e0xgRKnRzed5zXYpcI4aERc3JzBgO4Z0er9G8l66OWVGdMfpe6 17 | CBajC5ToIiT8zqoYxXwqJgN+glir4gJe3mm8J703QfArZiQrdk0NTi5bY7+vLLG/ 18 | knTrtpdsKih6r3kjhuPPaAsIwmMxIydFvATKjLUCgYEAh/y4EihRSk5WKC8GxeZt 19 | oiZ58vT4z+fqnMIfyJmD5up48JuQNcokw/LADj/ODiFM7GUnWkGxBrvDA3H67WQm 20 | 49bJjs8E+BfUQFdTjYnJRlpJZ+7Zt1gbNQMf5ENw5CCchTDqEq6pN0DVf8PBnSIF 21 | KvkXW9KvdV5J76uCAn15mDkCgYA1y8dHzbjlCz9Cy2pt1aDfTPwOew33gi7U3skS 22 | RTerx29aDyAcuQTLfyrROBkX4TZYiWGdEl5Bc7PYhCKpWawzrsH2TNa7CRtCOh2E 23 | R+V/84+GNNf04ALJYCXD9/ugQVKmR1XfDRCvKeFQFE38Y/dvV2etCswbKt5tRy2p 24 | xkCe/QKBgQCkLqafD4S20YHf6WTp3jp/4H/qEy2X2a8gdVVBi1uKkGDXr0n+AoVU 25 | ib4KbP5ovZlrjL++akMQ7V2fHzuQIFWnCkDA5c2ZAqzlM+ZN+HRG7gWur7Bt4XH1 26 | 7XC9wlRna4b3Ln8ew3q1ZcBjXwD4ppbTlmwAfQIaZTGJUgQbdsO9YA== 27 | -----END RSA PRIVATE KEY----- 28 | -------------------------------------------------------------------------------- /tests/.ssh/id_rsa.pub: -------------------------------------------------------------------------------- 1 | ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQDh7YP+o83TynNNpz5rxmaU/XOIk5eTjkLKcw+29rSu0r9EHbpVt8AXSEgmOLuW2+dieoJT2gV+8QzfdxOftP3r6h3yJv9XPblhTxluy2q0iyQ+7AJu/crSYAeCy+InJEPvIz5ApNsFASBsa5bqC1swqGJh+IgHgPKPsB1L9+Te/brAODPtIcjk4Gq71u/UqHFBh0USdTc8C0Cp5xyDM2lsfd5gvIbO5TEQgmWGln+5TYb2mmP9xKs41U+IjwCGLhGuVmOY/mXnv+yrUKUa6XIukVwzDryQ/kWKTKoekckdEE2BTnvXLQ+HfdKMFuzSFoIgByat5YSEZ7785ecl7pVR drone-scp@localhost 2 | -------------------------------------------------------------------------------- /tests/a.txt: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/appleboy/drone-sftp-cache/7e2f0fd71d91ed79c9f009a3c54e05f036057df1/tests/a.txt -------------------------------------------------------------------------------- /tests/b.txt: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/appleboy/drone-sftp-cache/7e2f0fd71d91ed79c9f009a3c54e05f036057df1/tests/b.txt -------------------------------------------------------------------------------- /tests/entrypoint.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | if [ ! -f "/etc/ssh/ssh_host_rsa_key" ]; then 4 | # generate fresh rsa key 5 | ssh-keygen -f /etc/ssh/ssh_host_rsa_key -N '' -t rsa 6 | fi 7 | 8 | if [ ! -f "/etc/ssh/ssh_host_dsa_key" ]; then 9 | # generate fresh dsa key 10 | ssh-keygen -f /etc/ssh/ssh_host_dsa_key -N '' -t dsa 11 | fi 12 | 13 | exec "$@" 14 | -------------------------------------------------------------------------------- /vendor/github.com/davecgh/go-spew/LICENSE: -------------------------------------------------------------------------------- 1 | ISC License 2 | 3 | Copyright (c) 2012-2016 Dave Collins 4 | 5 | Permission to use, copy, modify, and distribute this software for any 6 | purpose with or without fee is hereby granted, provided that the above 7 | copyright notice and this permission notice appear in all copies. 8 | 9 | THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 10 | WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 11 | MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 12 | ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 13 | WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 14 | ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 15 | OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 16 | -------------------------------------------------------------------------------- /vendor/github.com/davecgh/go-spew/spew/bypass.go: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2015-2016 Dave Collins 2 | // 3 | // Permission to use, copy, modify, and distribute this software for any 4 | // purpose with or without fee is hereby granted, provided that the above 5 | // copyright notice and this permission notice appear in all copies. 6 | // 7 | // THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 8 | // WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 9 | // MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 10 | // ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 11 | // WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 12 | // ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 13 | // OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 14 | 15 | // NOTE: Due to the following build constraints, this file will only be compiled 16 | // when the code is not running on Google App Engine, compiled by GopherJS, and 17 | // "-tags safe" is not added to the go build command line. The "disableunsafe" 18 | // tag is deprecated and thus should not be used. 19 | // +build !js,!appengine,!safe,!disableunsafe 20 | 21 | package spew 22 | 23 | import ( 24 | "reflect" 25 | "unsafe" 26 | ) 27 | 28 | const ( 29 | // UnsafeDisabled is a build-time constant which specifies whether or 30 | // not access to the unsafe package is available. 31 | UnsafeDisabled = false 32 | 33 | // ptrSize is the size of a pointer on the current arch. 34 | ptrSize = unsafe.Sizeof((*byte)(nil)) 35 | ) 36 | 37 | var ( 38 | // offsetPtr, offsetScalar, and offsetFlag are the offsets for the 39 | // internal reflect.Value fields. These values are valid before golang 40 | // commit ecccf07e7f9d which changed the format. The are also valid 41 | // after commit 82f48826c6c7 which changed the format again to mirror 42 | // the original format. Code in the init function updates these offsets 43 | // as necessary. 44 | offsetPtr = uintptr(ptrSize) 45 | offsetScalar = uintptr(0) 46 | offsetFlag = uintptr(ptrSize * 2) 47 | 48 | // flagKindWidth and flagKindShift indicate various bits that the 49 | // reflect package uses internally to track kind information. 50 | // 51 | // flagRO indicates whether or not the value field of a reflect.Value is 52 | // read-only. 53 | // 54 | // flagIndir indicates whether the value field of a reflect.Value is 55 | // the actual data or a pointer to the data. 56 | // 57 | // These values are valid before golang commit 90a7c3c86944 which 58 | // changed their positions. Code in the init function updates these 59 | // flags as necessary. 60 | flagKindWidth = uintptr(5) 61 | flagKindShift = uintptr(flagKindWidth - 1) 62 | flagRO = uintptr(1 << 0) 63 | flagIndir = uintptr(1 << 1) 64 | ) 65 | 66 | func init() { 67 | // Older versions of reflect.Value stored small integers directly in the 68 | // ptr field (which is named val in the older versions). Versions 69 | // between commits ecccf07e7f9d and 82f48826c6c7 added a new field named 70 | // scalar for this purpose which unfortunately came before the flag 71 | // field, so the offset of the flag field is different for those 72 | // versions. 73 | // 74 | // This code constructs a new reflect.Value from a known small integer 75 | // and checks if the size of the reflect.Value struct indicates it has 76 | // the scalar field. When it does, the offsets are updated accordingly. 77 | vv := reflect.ValueOf(0xf00) 78 | if unsafe.Sizeof(vv) == (ptrSize * 4) { 79 | offsetScalar = ptrSize * 2 80 | offsetFlag = ptrSize * 3 81 | } 82 | 83 | // Commit 90a7c3c86944 changed the flag positions such that the low 84 | // order bits are the kind. This code extracts the kind from the flags 85 | // field and ensures it's the correct type. When it's not, the flag 86 | // order has been changed to the newer format, so the flags are updated 87 | // accordingly. 88 | upf := unsafe.Pointer(uintptr(unsafe.Pointer(&vv)) + offsetFlag) 89 | upfv := *(*uintptr)(upf) 90 | flagKindMask := uintptr((1<>flagKindShift != uintptr(reflect.Int) { 92 | flagKindShift = 0 93 | flagRO = 1 << 5 94 | flagIndir = 1 << 6 95 | 96 | // Commit adf9b30e5594 modified the flags to separate the 97 | // flagRO flag into two bits which specifies whether or not the 98 | // field is embedded. This causes flagIndir to move over a bit 99 | // and means that flagRO is the combination of either of the 100 | // original flagRO bit and the new bit. 101 | // 102 | // This code detects the change by extracting what used to be 103 | // the indirect bit to ensure it's set. When it's not, the flag 104 | // order has been changed to the newer format, so the flags are 105 | // updated accordingly. 106 | if upfv&flagIndir == 0 { 107 | flagRO = 3 << 5 108 | flagIndir = 1 << 7 109 | } 110 | } 111 | } 112 | 113 | // unsafeReflectValue converts the passed reflect.Value into a one that bypasses 114 | // the typical safety restrictions preventing access to unaddressable and 115 | // unexported data. It works by digging the raw pointer to the underlying 116 | // value out of the protected value and generating a new unprotected (unsafe) 117 | // reflect.Value to it. 118 | // 119 | // This allows us to check for implementations of the Stringer and error 120 | // interfaces to be used for pretty printing ordinarily unaddressable and 121 | // inaccessible values such as unexported struct fields. 122 | func unsafeReflectValue(v reflect.Value) (rv reflect.Value) { 123 | indirects := 1 124 | vt := v.Type() 125 | upv := unsafe.Pointer(uintptr(unsafe.Pointer(&v)) + offsetPtr) 126 | rvf := *(*uintptr)(unsafe.Pointer(uintptr(unsafe.Pointer(&v)) + offsetFlag)) 127 | if rvf&flagIndir != 0 { 128 | vt = reflect.PtrTo(v.Type()) 129 | indirects++ 130 | } else if offsetScalar != 0 { 131 | // The value is in the scalar field when it's not one of the 132 | // reference types. 133 | switch vt.Kind() { 134 | case reflect.Uintptr: 135 | case reflect.Chan: 136 | case reflect.Func: 137 | case reflect.Map: 138 | case reflect.Ptr: 139 | case reflect.UnsafePointer: 140 | default: 141 | upv = unsafe.Pointer(uintptr(unsafe.Pointer(&v)) + 142 | offsetScalar) 143 | } 144 | } 145 | 146 | pv := reflect.NewAt(vt, upv) 147 | rv = pv 148 | for i := 0; i < indirects; i++ { 149 | rv = rv.Elem() 150 | } 151 | return rv 152 | } 153 | -------------------------------------------------------------------------------- /vendor/github.com/davecgh/go-spew/spew/bypasssafe.go: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2015-2016 Dave Collins 2 | // 3 | // Permission to use, copy, modify, and distribute this software for any 4 | // purpose with or without fee is hereby granted, provided that the above 5 | // copyright notice and this permission notice appear in all copies. 6 | // 7 | // THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 8 | // WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 9 | // MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 10 | // ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 11 | // WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 12 | // ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 13 | // OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 14 | 15 | // NOTE: Due to the following build constraints, this file will only be compiled 16 | // when the code is running on Google App Engine, compiled by GopherJS, or 17 | // "-tags safe" is added to the go build command line. The "disableunsafe" 18 | // tag is deprecated and thus should not be used. 19 | // +build js appengine safe disableunsafe 20 | 21 | package spew 22 | 23 | import "reflect" 24 | 25 | const ( 26 | // UnsafeDisabled is a build-time constant which specifies whether or 27 | // not access to the unsafe package is available. 28 | UnsafeDisabled = true 29 | ) 30 | 31 | // unsafeReflectValue typically converts the passed reflect.Value into a one 32 | // that bypasses the typical safety restrictions preventing access to 33 | // unaddressable and unexported data. However, doing this relies on access to 34 | // the unsafe package. This is a stub version which simply returns the passed 35 | // reflect.Value when the unsafe package is not available. 36 | func unsafeReflectValue(v reflect.Value) reflect.Value { 37 | return v 38 | } 39 | -------------------------------------------------------------------------------- /vendor/github.com/davecgh/go-spew/spew/spew.go: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2013-2016 Dave Collins 3 | * 4 | * Permission to use, copy, modify, and distribute this software for any 5 | * purpose with or without fee is hereby granted, provided that the above 6 | * copyright notice and this permission notice appear in all copies. 7 | * 8 | * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 9 | * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 10 | * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 11 | * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 12 | * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 13 | * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 14 | * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 15 | */ 16 | 17 | package spew 18 | 19 | import ( 20 | "fmt" 21 | "io" 22 | ) 23 | 24 | // Errorf is a wrapper for fmt.Errorf that treats each argument as if it were 25 | // passed with a default Formatter interface returned by NewFormatter. It 26 | // returns the formatted string as a value that satisfies error. See 27 | // NewFormatter for formatting details. 28 | // 29 | // This function is shorthand for the following syntax: 30 | // 31 | // fmt.Errorf(format, spew.NewFormatter(a), spew.NewFormatter(b)) 32 | func Errorf(format string, a ...interface{}) (err error) { 33 | return fmt.Errorf(format, convertArgs(a)...) 34 | } 35 | 36 | // Fprint is a wrapper for fmt.Fprint that treats each argument as if it were 37 | // passed with a default Formatter interface returned by NewFormatter. It 38 | // returns the number of bytes written and any write error encountered. See 39 | // NewFormatter for formatting details. 40 | // 41 | // This function is shorthand for the following syntax: 42 | // 43 | // fmt.Fprint(w, spew.NewFormatter(a), spew.NewFormatter(b)) 44 | func Fprint(w io.Writer, a ...interface{}) (n int, err error) { 45 | return fmt.Fprint(w, convertArgs(a)...) 46 | } 47 | 48 | // Fprintf is a wrapper for fmt.Fprintf that treats each argument as if it were 49 | // passed with a default Formatter interface returned by NewFormatter. It 50 | // returns the number of bytes written and any write error encountered. See 51 | // NewFormatter for formatting details. 52 | // 53 | // This function is shorthand for the following syntax: 54 | // 55 | // fmt.Fprintf(w, format, spew.NewFormatter(a), spew.NewFormatter(b)) 56 | func Fprintf(w io.Writer, format string, a ...interface{}) (n int, err error) { 57 | return fmt.Fprintf(w, format, convertArgs(a)...) 58 | } 59 | 60 | // Fprintln is a wrapper for fmt.Fprintln that treats each argument as if it 61 | // passed with a default Formatter interface returned by NewFormatter. See 62 | // NewFormatter for formatting details. 63 | // 64 | // This function is shorthand for the following syntax: 65 | // 66 | // fmt.Fprintln(w, spew.NewFormatter(a), spew.NewFormatter(b)) 67 | func Fprintln(w io.Writer, a ...interface{}) (n int, err error) { 68 | return fmt.Fprintln(w, convertArgs(a)...) 69 | } 70 | 71 | // Print is a wrapper for fmt.Print that treats each argument as if it were 72 | // passed with a default Formatter interface returned by NewFormatter. It 73 | // returns the number of bytes written and any write error encountered. See 74 | // NewFormatter for formatting details. 75 | // 76 | // This function is shorthand for the following syntax: 77 | // 78 | // fmt.Print(spew.NewFormatter(a), spew.NewFormatter(b)) 79 | func Print(a ...interface{}) (n int, err error) { 80 | return fmt.Print(convertArgs(a)...) 81 | } 82 | 83 | // Printf is a wrapper for fmt.Printf that treats each argument as if it were 84 | // passed with a default Formatter interface returned by NewFormatter. It 85 | // returns the number of bytes written and any write error encountered. See 86 | // NewFormatter for formatting details. 87 | // 88 | // This function is shorthand for the following syntax: 89 | // 90 | // fmt.Printf(format, spew.NewFormatter(a), spew.NewFormatter(b)) 91 | func Printf(format string, a ...interface{}) (n int, err error) { 92 | return fmt.Printf(format, convertArgs(a)...) 93 | } 94 | 95 | // Println is a wrapper for fmt.Println that treats each argument as if it were 96 | // passed with a default Formatter interface returned by NewFormatter. It 97 | // returns the number of bytes written and any write error encountered. See 98 | // NewFormatter for formatting details. 99 | // 100 | // This function is shorthand for the following syntax: 101 | // 102 | // fmt.Println(spew.NewFormatter(a), spew.NewFormatter(b)) 103 | func Println(a ...interface{}) (n int, err error) { 104 | return fmt.Println(convertArgs(a)...) 105 | } 106 | 107 | // Sprint is a wrapper for fmt.Sprint that treats each argument as if it were 108 | // passed with a default Formatter interface returned by NewFormatter. It 109 | // returns the resulting string. See NewFormatter for formatting details. 110 | // 111 | // This function is shorthand for the following syntax: 112 | // 113 | // fmt.Sprint(spew.NewFormatter(a), spew.NewFormatter(b)) 114 | func Sprint(a ...interface{}) string { 115 | return fmt.Sprint(convertArgs(a)...) 116 | } 117 | 118 | // Sprintf is a wrapper for fmt.Sprintf that treats each argument as if it were 119 | // passed with a default Formatter interface returned by NewFormatter. It 120 | // returns the resulting string. See NewFormatter for formatting details. 121 | // 122 | // This function is shorthand for the following syntax: 123 | // 124 | // fmt.Sprintf(format, spew.NewFormatter(a), spew.NewFormatter(b)) 125 | func Sprintf(format string, a ...interface{}) string { 126 | return fmt.Sprintf(format, convertArgs(a)...) 127 | } 128 | 129 | // Sprintln is a wrapper for fmt.Sprintln that treats each argument as if it 130 | // were passed with a default Formatter interface returned by NewFormatter. It 131 | // returns the resulting string. See NewFormatter for formatting details. 132 | // 133 | // This function is shorthand for the following syntax: 134 | // 135 | // fmt.Sprintln(spew.NewFormatter(a), spew.NewFormatter(b)) 136 | func Sprintln(a ...interface{}) string { 137 | return fmt.Sprintln(convertArgs(a)...) 138 | } 139 | 140 | // convertArgs accepts a slice of arguments and returns a slice of the same 141 | // length with each argument converted to a default spew Formatter interface. 142 | func convertArgs(args []interface{}) (formatters []interface{}) { 143 | formatters = make([]interface{}, len(args)) 144 | for index, arg := range args { 145 | formatters[index] = NewFormatter(arg) 146 | } 147 | return formatters 148 | } 149 | -------------------------------------------------------------------------------- /vendor/github.com/joho/godotenv/LICENCE: -------------------------------------------------------------------------------- 1 | Copyright (c) 2013 John Barton 2 | 3 | MIT License 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining 6 | a copy of this software and associated documentation files (the 7 | "Software"), to deal in the Software without restriction, including 8 | without limitation the rights to use, copy, modify, merge, publish, 9 | distribute, sublicense, and/or sell copies of the Software, and to 10 | permit persons to whom the Software is furnished to do so, subject to 11 | the following conditions: 12 | 13 | The above copyright notice and this permission notice shall be 14 | included in all copies or substantial portions of the Software. 15 | 16 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 17 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 18 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 19 | NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE 20 | LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 21 | OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION 22 | WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 23 | 24 | -------------------------------------------------------------------------------- /vendor/github.com/joho/godotenv/README.md: -------------------------------------------------------------------------------- 1 | # GoDotEnv [![Build Status](https://travis-ci.org/joho/godotenv.svg?branch=master)](https://travis-ci.org/joho/godotenv) [![Build status](https://ci.appveyor.com/api/projects/status/9v40vnfvvgde64u4?svg=true)](https://ci.appveyor.com/project/joho/godotenv) 2 | 3 | A Go (golang) port of the Ruby dotenv project (which loads env vars from a .env file) 4 | 5 | From the original Library: 6 | 7 | > Storing configuration in the environment is one of the tenets of a twelve-factor app. Anything that is likely to change between deployment environments–such as resource handles for databases or credentials for external services–should be extracted from the code into environment variables. 8 | > 9 | > But it is not always practical to set environment variables on development machines or continuous integration servers where multiple projects are run. Dotenv load variables from a .env file into ENV when the environment is bootstrapped. 10 | 11 | It can be used as a library (for loading in env for your own daemons etc) or as a bin command. 12 | 13 | There is test coverage and CI for both linuxish and windows environments, but I make no guarantees about the bin version working on windows. 14 | 15 | ## Installation 16 | 17 | As a library 18 | 19 | ```shell 20 | go get github.com/joho/godotenv 21 | ``` 22 | 23 | or if you want to use it as a bin command 24 | ```shell 25 | go get github.com/joho/godotenv/cmd/godotenv 26 | ``` 27 | 28 | ## Usage 29 | 30 | Add your application configuration to your `.env` file in the root of your project: 31 | 32 | ```shell 33 | S3_BUCKET=YOURS3BUCKET 34 | SECRET_KEY=YOURSECRETKEYGOESHERE 35 | ``` 36 | 37 | Then in your Go app you can do something like 38 | 39 | ```go 40 | package main 41 | 42 | import ( 43 | "github.com/joho/godotenv" 44 | "log" 45 | "os" 46 | ) 47 | 48 | func main() { 49 | err := godotenv.Load() 50 | if err != nil { 51 | log.Fatal("Error loading .env file") 52 | } 53 | 54 | s3Bucket := os.Getenv("S3_BUCKET") 55 | secretKey := os.Getenv("SECRET_KEY") 56 | 57 | // now do something with s3 or whatever 58 | } 59 | ``` 60 | 61 | If you're even lazier than that, you can just take advantage of the autoload package which will read in `.env` on import 62 | 63 | ```go 64 | import _ "github.com/joho/godotenv/autoload" 65 | ``` 66 | 67 | While `.env` in the project root is the default, you don't have to be constrained, both examples below are 100% legit 68 | 69 | ```go 70 | _ = godotenv.Load("somerandomfile") 71 | _ = godotenv.Load("filenumberone.env", "filenumbertwo.env") 72 | ``` 73 | 74 | If you want to be really fancy with your env file you can do comments and exports (below is a valid env file) 75 | 76 | ```shell 77 | # I am a comment and that is OK 78 | SOME_VAR=someval 79 | FOO=BAR # comments at line end are OK too 80 | export BAR=BAZ 81 | ``` 82 | 83 | Or finally you can do YAML(ish) style 84 | 85 | ```yaml 86 | FOO: bar 87 | BAR: baz 88 | ``` 89 | 90 | as a final aside, if you don't want godotenv munging your env you can just get a map back instead 91 | 92 | ```go 93 | var myEnv map[string]string 94 | myEnv, err := godotenv.Read() 95 | 96 | s3Bucket := myEnv["S3_BUCKET"] 97 | ``` 98 | 99 | ### Command Mode 100 | 101 | Assuming you've installed the command as above and you've got `$GOPATH/bin` in your `$PATH` 102 | 103 | ``` 104 | godotenv -f /some/path/to/.env some_command with some args 105 | ``` 106 | 107 | If you don't specify `-f` it will fall back on the default of loading `.env` in `PWD` 108 | 109 | ## Contributing 110 | 111 | Contributions are most welcome! The parser itself is pretty stupidly naive and I wouldn't be surprised if it breaks with edge cases. 112 | 113 | *code changes without tests will not be accepted* 114 | 115 | 1. Fork it 116 | 2. Create your feature branch (`git checkout -b my-new-feature`) 117 | 3. Commit your changes (`git commit -am 'Added some feature'`) 118 | 4. Push to the branch (`git push origin my-new-feature`) 119 | 5. Create new Pull Request 120 | 121 | ## CI 122 | 123 | Linux: [![wercker status](https://app.wercker.com/status/507594c2ec7e60f19403a568dfea0f78/m "wercker status")](https://app.wercker.com/project/bykey/507594c2ec7e60f19403a568dfea0f78) Windows: [![Build status](https://ci.appveyor.com/api/projects/status/9v40vnfvvgde64u4)](https://ci.appveyor.com/project/joho/godotenv) 124 | 125 | ## Who? 126 | 127 | The original library [dotenv](https://github.com/bkeepers/dotenv) was written by [Brandon Keepers](http://opensoul.org/), and this port was done by [John Barton](http://whoisjohnbarton.com) based off the tests/fixtures in the original library. 128 | -------------------------------------------------------------------------------- /vendor/github.com/joho/godotenv/autoload/autoload.go: -------------------------------------------------------------------------------- 1 | package autoload 2 | 3 | /* 4 | You can just read the .env file on import just by doing 5 | 6 | import _ "github.com/joho/godotenv/autoload" 7 | 8 | And bob's your mother's brother 9 | */ 10 | 11 | import "github.com/joho/godotenv" 12 | 13 | func init() { 14 | godotenv.Load() 15 | } 16 | -------------------------------------------------------------------------------- /vendor/github.com/joho/godotenv/godotenv.go: -------------------------------------------------------------------------------- 1 | // Package godotenv is a go port of the ruby dotenv library (https://github.com/bkeepers/dotenv) 2 | // 3 | // Examples/readme can be found on the github page at https://github.com/joho/godotenv 4 | // 5 | // The TL;DR is that you make a .env file that looks something like 6 | // 7 | // SOME_ENV_VAR=somevalue 8 | // 9 | // and then in your go code you can call 10 | // 11 | // godotenv.Load() 12 | // 13 | // and all the env vars declared in .env will be avaiable through os.Getenv("SOME_ENV_VAR") 14 | package godotenv 15 | 16 | import ( 17 | "bufio" 18 | "errors" 19 | "os" 20 | "os/exec" 21 | "strings" 22 | ) 23 | 24 | // Load will read your env file(s) and load them into ENV for this process. 25 | // 26 | // Call this function as close as possible to the start of your program (ideally in main) 27 | // 28 | // If you call Load without any args it will default to loading .env in the current path 29 | // 30 | // You can otherwise tell it which files to load (there can be more than one) like 31 | // 32 | // godotenv.Load("fileone", "filetwo") 33 | // 34 | // It's important to note that it WILL NOT OVERRIDE an env variable that already exists - consider the .env file to set dev vars or sensible defaults 35 | func Load(filenames ...string) (err error) { 36 | filenames = filenamesOrDefault(filenames) 37 | 38 | for _, filename := range filenames { 39 | err = loadFile(filename, false) 40 | if err != nil { 41 | return // return early on a spazout 42 | } 43 | } 44 | return 45 | } 46 | 47 | // Overload will read your env file(s) and load them into ENV for this process. 48 | // 49 | // Call this function as close as possible to the start of your program (ideally in main) 50 | // 51 | // If you call Overload without any args it will default to loading .env in the current path 52 | // 53 | // You can otherwise tell it which files to load (there can be more than one) like 54 | // 55 | // godotenv.Overload("fileone", "filetwo") 56 | // 57 | // It's important to note this WILL OVERRIDE an env variable that already exists - consider the .env file to forcefilly set all vars. 58 | func Overload(filenames ...string) (err error) { 59 | filenames = filenamesOrDefault(filenames) 60 | 61 | for _, filename := range filenames { 62 | err = loadFile(filename, true) 63 | if err != nil { 64 | return // return early on a spazout 65 | } 66 | } 67 | return 68 | } 69 | 70 | // Read all env (with same file loading semantics as Load) but return values as 71 | // a map rather than automatically writing values into env 72 | func Read(filenames ...string) (envMap map[string]string, err error) { 73 | filenames = filenamesOrDefault(filenames) 74 | envMap = make(map[string]string) 75 | 76 | for _, filename := range filenames { 77 | individualEnvMap, individualErr := readFile(filename) 78 | 79 | if individualErr != nil { 80 | err = individualErr 81 | return // return early on a spazout 82 | } 83 | 84 | for key, value := range individualEnvMap { 85 | envMap[key] = value 86 | } 87 | } 88 | 89 | return 90 | } 91 | 92 | // Exec loads env vars from the specified filenames (empty map falls back to default) 93 | // then executes the cmd specified. 94 | // 95 | // Simply hooks up os.Stdin/err/out to the command and calls Run() 96 | // 97 | // If you want more fine grained control over your command it's recommended 98 | // that you use `Load()` or `Read()` and the `os/exec` package yourself. 99 | func Exec(filenames []string, cmd string, cmdArgs []string) error { 100 | Load(filenames...) 101 | 102 | command := exec.Command(cmd, cmdArgs...) 103 | command.Stdin = os.Stdin 104 | command.Stdout = os.Stdout 105 | command.Stderr = os.Stderr 106 | return command.Run() 107 | } 108 | 109 | func filenamesOrDefault(filenames []string) []string { 110 | if len(filenames) == 0 { 111 | return []string{".env"} 112 | } 113 | return filenames 114 | } 115 | 116 | func loadFile(filename string, overload bool) error { 117 | envMap, err := readFile(filename) 118 | if err != nil { 119 | return err 120 | } 121 | 122 | currentEnv := map[string]bool{} 123 | rawEnv := os.Environ() 124 | for _, rawEnvLine := range rawEnv { 125 | key := strings.Split(rawEnvLine, "=")[0] 126 | currentEnv[key] = true 127 | } 128 | 129 | for key, value := range envMap { 130 | if !currentEnv[key] || overload { 131 | os.Setenv(key, value) 132 | } 133 | } 134 | 135 | return nil 136 | } 137 | 138 | func readFile(filename string) (envMap map[string]string, err error) { 139 | file, err := os.Open(filename) 140 | if err != nil { 141 | return 142 | } 143 | defer file.Close() 144 | 145 | envMap = make(map[string]string) 146 | 147 | var lines []string 148 | scanner := bufio.NewScanner(file) 149 | for scanner.Scan() { 150 | lines = append(lines, scanner.Text()) 151 | } 152 | 153 | if err = scanner.Err(); err != nil { 154 | return 155 | } 156 | 157 | for _, fullLine := range lines { 158 | if !isIgnoredLine(fullLine) { 159 | var key, value string 160 | key, value, err = parseLine(fullLine) 161 | 162 | if err != nil { 163 | return 164 | } 165 | envMap[key] = value 166 | } 167 | } 168 | return 169 | } 170 | 171 | func parseLine(line string) (key string, value string, err error) { 172 | if len(line) == 0 { 173 | err = errors.New("zero length string") 174 | return 175 | } 176 | 177 | // ditch the comments (but keep quoted hashes) 178 | if strings.Contains(line, "#") { 179 | segmentsBetweenHashes := strings.Split(line, "#") 180 | quotesAreOpen := false 181 | var segmentsToKeep []string 182 | for _, segment := range segmentsBetweenHashes { 183 | if strings.Count(segment, "\"") == 1 || strings.Count(segment, "'") == 1 { 184 | if quotesAreOpen { 185 | quotesAreOpen = false 186 | segmentsToKeep = append(segmentsToKeep, segment) 187 | } else { 188 | quotesAreOpen = true 189 | } 190 | } 191 | 192 | if len(segmentsToKeep) == 0 || quotesAreOpen { 193 | segmentsToKeep = append(segmentsToKeep, segment) 194 | } 195 | } 196 | 197 | line = strings.Join(segmentsToKeep, "#") 198 | } 199 | 200 | // now split key from value 201 | splitString := strings.SplitN(line, "=", 2) 202 | 203 | if len(splitString) != 2 { 204 | // try yaml mode! 205 | splitString = strings.SplitN(line, ":", 2) 206 | } 207 | 208 | if len(splitString) != 2 { 209 | err = errors.New("Can't separate key from value") 210 | return 211 | } 212 | 213 | // Parse the key 214 | key = splitString[0] 215 | if strings.HasPrefix(key, "export") { 216 | key = strings.TrimPrefix(key, "export") 217 | } 218 | key = strings.Trim(key, " ") 219 | 220 | // Parse the value 221 | value = splitString[1] 222 | 223 | // trim 224 | value = strings.Trim(value, " ") 225 | 226 | // check if we've got quoted values 227 | if value != "" { 228 | first := string(value[0:1]) 229 | last := string(value[len(value)-1:]) 230 | if first == last && strings.ContainsAny(first, `"'`) { 231 | // pull the quotes off the edges 232 | value = strings.Trim(value, `"'`) 233 | 234 | // expand quotes 235 | value = strings.Replace(value, `\"`, `"`, -1) 236 | // expand newlines 237 | value = strings.Replace(value, `\n`, "\n", -1) 238 | } 239 | } 240 | 241 | return 242 | } 243 | 244 | func isIgnoredLine(line string) bool { 245 | trimmedLine := strings.Trim(line, " \n\t") 246 | return len(trimmedLine) == 0 || strings.HasPrefix(trimmedLine, "#") 247 | } 248 | -------------------------------------------------------------------------------- /vendor/github.com/kr/fs/LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) 2012 The Go Authors. All rights reserved. 2 | 3 | Redistribution and use in source and binary forms, with or without 4 | modification, are permitted provided that the following conditions are 5 | met: 6 | 7 | * Redistributions of source code must retain the above copyright 8 | notice, this list of conditions and the following disclaimer. 9 | * Redistributions in binary form must reproduce the above 10 | copyright notice, this list of conditions and the following disclaimer 11 | in the documentation and/or other materials provided with the 12 | distribution. 13 | * Neither the name of Google Inc. nor the names of its 14 | contributors may be used to endorse or promote products derived from 15 | this software without specific prior written permission. 16 | 17 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 18 | "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 19 | LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 20 | A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 21 | OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 22 | SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 23 | LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 24 | DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 25 | THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 26 | (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 27 | OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 28 | -------------------------------------------------------------------------------- /vendor/github.com/kr/fs/Readme: -------------------------------------------------------------------------------- 1 | Filesystem Package 2 | 3 | http://godoc.org/github.com/kr/fs 4 | -------------------------------------------------------------------------------- /vendor/github.com/kr/fs/filesystem.go: -------------------------------------------------------------------------------- 1 | package fs 2 | 3 | import ( 4 | "io/ioutil" 5 | "os" 6 | "path/filepath" 7 | ) 8 | 9 | // FileSystem defines the methods of an abstract filesystem. 10 | type FileSystem interface { 11 | 12 | // ReadDir reads the directory named by dirname and returns a 13 | // list of directory entries. 14 | ReadDir(dirname string) ([]os.FileInfo, error) 15 | 16 | // Lstat returns a FileInfo describing the named file. If the file is a 17 | // symbolic link, the returned FileInfo describes the symbolic link. Lstat 18 | // makes no attempt to follow the link. 19 | Lstat(name string) (os.FileInfo, error) 20 | 21 | // Join joins any number of path elements into a single path, adding a 22 | // separator if necessary. The result is Cleaned; in particular, all 23 | // empty strings are ignored. 24 | // 25 | // The separator is FileSystem specific. 26 | Join(elem ...string) string 27 | } 28 | 29 | // fs represents a FileSystem provided by the os package. 30 | type fs struct{} 31 | 32 | func (f *fs) ReadDir(dirname string) ([]os.FileInfo, error) { return ioutil.ReadDir(dirname) } 33 | 34 | func (f *fs) Lstat(name string) (os.FileInfo, error) { return os.Lstat(name) } 35 | 36 | func (f *fs) Join(elem ...string) string { return filepath.Join(elem...) } 37 | -------------------------------------------------------------------------------- /vendor/github.com/kr/fs/walk.go: -------------------------------------------------------------------------------- 1 | // Package fs provides filesystem-related functions. 2 | package fs 3 | 4 | import ( 5 | "os" 6 | ) 7 | 8 | // Walker provides a convenient interface for iterating over the 9 | // descendants of a filesystem path. 10 | // Successive calls to the Step method will step through each 11 | // file or directory in the tree, including the root. The files 12 | // are walked in lexical order, which makes the output deterministic 13 | // but means that for very large directories Walker can be inefficient. 14 | // Walker does not follow symbolic links. 15 | type Walker struct { 16 | fs FileSystem 17 | cur item 18 | stack []item 19 | descend bool 20 | } 21 | 22 | type item struct { 23 | path string 24 | info os.FileInfo 25 | err error 26 | } 27 | 28 | // Walk returns a new Walker rooted at root. 29 | func Walk(root string) *Walker { 30 | return WalkFS(root, new(fs)) 31 | } 32 | 33 | // WalkFS returns a new Walker rooted at root on the FileSystem fs. 34 | func WalkFS(root string, fs FileSystem) *Walker { 35 | info, err := fs.Lstat(root) 36 | return &Walker{ 37 | fs: fs, 38 | stack: []item{{root, info, err}}, 39 | } 40 | } 41 | 42 | // Step advances the Walker to the next file or directory, 43 | // which will then be available through the Path, Stat, 44 | // and Err methods. 45 | // It returns false when the walk stops at the end of the tree. 46 | func (w *Walker) Step() bool { 47 | if w.descend && w.cur.err == nil && w.cur.info.IsDir() { 48 | list, err := w.fs.ReadDir(w.cur.path) 49 | if err != nil { 50 | w.cur.err = err 51 | w.stack = append(w.stack, w.cur) 52 | } else { 53 | for i := len(list) - 1; i >= 0; i-- { 54 | path := w.fs.Join(w.cur.path, list[i].Name()) 55 | w.stack = append(w.stack, item{path, list[i], nil}) 56 | } 57 | } 58 | } 59 | 60 | if len(w.stack) == 0 { 61 | return false 62 | } 63 | i := len(w.stack) - 1 64 | w.cur = w.stack[i] 65 | w.stack = w.stack[:i] 66 | w.descend = true 67 | return true 68 | } 69 | 70 | // Path returns the path to the most recent file or directory 71 | // visited by a call to Step. It contains the argument to Walk 72 | // as a prefix; that is, if Walk is called with "dir", which is 73 | // a directory containing the file "a", Path will return "dir/a". 74 | func (w *Walker) Path() string { 75 | return w.cur.path 76 | } 77 | 78 | // Stat returns info for the most recent file or directory 79 | // visited by a call to Step. 80 | func (w *Walker) Stat() os.FileInfo { 81 | return w.cur.info 82 | } 83 | 84 | // Err returns the error, if any, for the most recent attempt 85 | // by Step to visit a file or directory. If a directory has 86 | // an error, w will not descend into that directory. 87 | func (w *Walker) Err() error { 88 | return w.cur.err 89 | } 90 | 91 | // SkipDir causes the currently visited directory to be skipped. 92 | // If w is not on a directory, SkipDir has no effect. 93 | func (w *Walker) SkipDir() { 94 | w.descend = false 95 | } 96 | -------------------------------------------------------------------------------- /vendor/github.com/pkg/errors/LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) 2015, Dave Cheney 2 | All rights reserved. 3 | 4 | Redistribution and use in source and binary forms, with or without 5 | modification, are permitted provided that the following conditions are met: 6 | 7 | * Redistributions of source code must retain the above copyright notice, this 8 | list of conditions and the following disclaimer. 9 | 10 | * Redistributions in binary form must reproduce the above copyright notice, 11 | this list of conditions and the following disclaimer in the documentation 12 | and/or other materials provided with the distribution. 13 | 14 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 15 | AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 16 | IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 17 | DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE 18 | FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 19 | DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 20 | SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 21 | CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 22 | OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 23 | OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 24 | -------------------------------------------------------------------------------- /vendor/github.com/pkg/errors/README.md: -------------------------------------------------------------------------------- 1 | # errors [![Travis-CI](https://travis-ci.org/pkg/errors.svg)](https://travis-ci.org/pkg/errors) [![AppVeyor](https://ci.appveyor.com/api/projects/status/b98mptawhudj53ep/branch/master?svg=true)](https://ci.appveyor.com/project/davecheney/errors/branch/master) [![GoDoc](https://godoc.org/github.com/pkg/errors?status.svg)](http://godoc.org/github.com/pkg/errors) [![Report card](https://goreportcard.com/badge/github.com/pkg/errors)](https://goreportcard.com/report/github.com/pkg/errors) 2 | 3 | Package errors provides simple error handling primitives. 4 | 5 | `go get github.com/pkg/errors` 6 | 7 | The traditional error handling idiom in Go is roughly akin to 8 | ```go 9 | if err != nil { 10 | return err 11 | } 12 | ``` 13 | which applied recursively up the call stack results in error reports without context or debugging information. The errors package allows programmers to add context to the failure path in their code in a way that does not destroy the original value of the error. 14 | 15 | ## Adding context to an error 16 | 17 | The errors.Wrap function returns a new error that adds context to the original error. For example 18 | ```go 19 | _, err := ioutil.ReadAll(r) 20 | if err != nil { 21 | return errors.Wrap(err, "read failed") 22 | } 23 | ``` 24 | ## Retrieving the cause of an error 25 | 26 | Using `errors.Wrap` constructs a stack of errors, adding context to the preceding error. Depending on the nature of the error it may be necessary to reverse the operation of errors.Wrap to retrieve the original error for inspection. Any error value which implements this interface can be inspected by `errors.Cause`. 27 | ```go 28 | type causer interface { 29 | Cause() error 30 | } 31 | ``` 32 | `errors.Cause` will recursively retrieve the topmost error which does not implement `causer`, which is assumed to be the original cause. For example: 33 | ```go 34 | switch err := errors.Cause(err).(type) { 35 | case *MyError: 36 | // handle specifically 37 | default: 38 | // unknown error 39 | } 40 | ``` 41 | 42 | [Read the package documentation for more information](https://godoc.org/github.com/pkg/errors). 43 | 44 | ## Contributing 45 | 46 | We welcome pull requests, bug fixes and issue reports. With that said, the bar for adding new symbols to this package is intentionally set high. 47 | 48 | Before proposing a change, please discuss your change by raising an issue. 49 | 50 | ## Licence 51 | 52 | BSD-2-Clause 53 | -------------------------------------------------------------------------------- /vendor/github.com/pkg/errors/appveyor.yml: -------------------------------------------------------------------------------- 1 | version: build-{build}.{branch} 2 | 3 | clone_folder: C:\gopath\src\github.com\pkg\errors 4 | shallow_clone: true # for startup speed 5 | 6 | environment: 7 | GOPATH: C:\gopath 8 | 9 | platform: 10 | - x64 11 | 12 | # http://www.appveyor.com/docs/installed-software 13 | install: 14 | # some helpful output for debugging builds 15 | - go version 16 | - go env 17 | # pre-installed MinGW at C:\MinGW is 32bit only 18 | # but MSYS2 at C:\msys64 has mingw64 19 | - set PATH=C:\msys64\mingw64\bin;%PATH% 20 | - gcc --version 21 | - g++ --version 22 | 23 | build_script: 24 | - go install -v ./... 25 | 26 | test_script: 27 | - set PATH=C:\gopath\bin;%PATH% 28 | - go test -v ./... 29 | 30 | #artifacts: 31 | # - path: '%GOPATH%\bin\*.exe' 32 | deploy: off 33 | -------------------------------------------------------------------------------- /vendor/github.com/pkg/errors/errors.go: -------------------------------------------------------------------------------- 1 | // Package errors provides simple error handling primitives. 2 | // 3 | // The traditional error handling idiom in Go is roughly akin to 4 | // 5 | // if err != nil { 6 | // return err 7 | // } 8 | // 9 | // which applied recursively up the call stack results in error reports 10 | // without context or debugging information. The errors package allows 11 | // programmers to add context to the failure path in their code in a way 12 | // that does not destroy the original value of the error. 13 | // 14 | // Adding context to an error 15 | // 16 | // The errors.Wrap function returns a new error that adds context to the 17 | // original error by recording a stack trace at the point Wrap is called, 18 | // and the supplied message. For example 19 | // 20 | // _, err := ioutil.ReadAll(r) 21 | // if err != nil { 22 | // return errors.Wrap(err, "read failed") 23 | // } 24 | // 25 | // If additional control is required the errors.WithStack and errors.WithMessage 26 | // functions destructure errors.Wrap into its component operations of annotating 27 | // an error with a stack trace and an a message, respectively. 28 | // 29 | // Retrieving the cause of an error 30 | // 31 | // Using errors.Wrap constructs a stack of errors, adding context to the 32 | // preceding error. Depending on the nature of the error it may be necessary 33 | // to reverse the operation of errors.Wrap to retrieve the original error 34 | // for inspection. Any error value which implements this interface 35 | // 36 | // type causer interface { 37 | // Cause() error 38 | // } 39 | // 40 | // can be inspected by errors.Cause. errors.Cause will recursively retrieve 41 | // the topmost error which does not implement causer, which is assumed to be 42 | // the original cause. For example: 43 | // 44 | // switch err := errors.Cause(err).(type) { 45 | // case *MyError: 46 | // // handle specifically 47 | // default: 48 | // // unknown error 49 | // } 50 | // 51 | // causer interface is not exported by this package, but is considered a part 52 | // of stable public API. 53 | // 54 | // Formatted printing of errors 55 | // 56 | // All error values returned from this package implement fmt.Formatter and can 57 | // be formatted by the fmt package. The following verbs are supported 58 | // 59 | // %s print the error. If the error has a Cause it will be 60 | // printed recursively 61 | // %v see %s 62 | // %+v extended format. Each Frame of the error's StackTrace will 63 | // be printed in detail. 64 | // 65 | // Retrieving the stack trace of an error or wrapper 66 | // 67 | // New, Errorf, Wrap, and Wrapf record a stack trace at the point they are 68 | // invoked. This information can be retrieved with the following interface. 69 | // 70 | // type stackTracer interface { 71 | // StackTrace() errors.StackTrace 72 | // } 73 | // 74 | // Where errors.StackTrace is defined as 75 | // 76 | // type StackTrace []Frame 77 | // 78 | // The Frame type represents a call site in the stack trace. Frame supports 79 | // the fmt.Formatter interface that can be used for printing information about 80 | // the stack trace of this error. For example: 81 | // 82 | // if err, ok := err.(stackTracer); ok { 83 | // for _, f := range err.StackTrace() { 84 | // fmt.Printf("%+s:%d", f) 85 | // } 86 | // } 87 | // 88 | // stackTracer interface is not exported by this package, but is considered a part 89 | // of stable public API. 90 | // 91 | // See the documentation for Frame.Format for more details. 92 | package errors 93 | 94 | import ( 95 | "fmt" 96 | "io" 97 | ) 98 | 99 | // New returns an error with the supplied message. 100 | // New also records the stack trace at the point it was called. 101 | func New(message string) error { 102 | return &fundamental{ 103 | msg: message, 104 | stack: callers(), 105 | } 106 | } 107 | 108 | // Errorf formats according to a format specifier and returns the string 109 | // as a value that satisfies error. 110 | // Errorf also records the stack trace at the point it was called. 111 | func Errorf(format string, args ...interface{}) error { 112 | return &fundamental{ 113 | msg: fmt.Sprintf(format, args...), 114 | stack: callers(), 115 | } 116 | } 117 | 118 | // fundamental is an error that has a message and a stack, but no caller. 119 | type fundamental struct { 120 | msg string 121 | *stack 122 | } 123 | 124 | func (f *fundamental) Error() string { return f.msg } 125 | 126 | func (f *fundamental) Format(s fmt.State, verb rune) { 127 | switch verb { 128 | case 'v': 129 | if s.Flag('+') { 130 | io.WriteString(s, f.msg) 131 | f.stack.Format(s, verb) 132 | return 133 | } 134 | fallthrough 135 | case 's': 136 | io.WriteString(s, f.msg) 137 | case 'q': 138 | fmt.Fprintf(s, "%q", f.msg) 139 | } 140 | } 141 | 142 | // WithStack annotates err with a stack trace at the point WithStack was called. 143 | // If err is nil, WithStack returns nil. 144 | func WithStack(err error) error { 145 | if err == nil { 146 | return nil 147 | } 148 | return &withStack{ 149 | err, 150 | callers(), 151 | } 152 | } 153 | 154 | type withStack struct { 155 | error 156 | *stack 157 | } 158 | 159 | func (w *withStack) Cause() error { return w.error } 160 | 161 | func (w *withStack) Format(s fmt.State, verb rune) { 162 | switch verb { 163 | case 'v': 164 | if s.Flag('+') { 165 | fmt.Fprintf(s, "%+v", w.Cause()) 166 | w.stack.Format(s, verb) 167 | return 168 | } 169 | fallthrough 170 | case 's': 171 | io.WriteString(s, w.Error()) 172 | case 'q': 173 | fmt.Fprintf(s, "%q", w.Error()) 174 | } 175 | } 176 | 177 | // Wrap returns an error annotating err with a stack trace 178 | // at the point Wrap is called, and the supplied message. 179 | // If err is nil, Wrap returns nil. 180 | func Wrap(err error, message string) error { 181 | if err == nil { 182 | return nil 183 | } 184 | err = &withMessage{ 185 | cause: err, 186 | msg: message, 187 | } 188 | return &withStack{ 189 | err, 190 | callers(), 191 | } 192 | } 193 | 194 | // Wrapf returns an error annotating err with a stack trace 195 | // at the point Wrapf is call, and the format specifier. 196 | // If err is nil, Wrapf returns nil. 197 | func Wrapf(err error, format string, args ...interface{}) error { 198 | if err == nil { 199 | return nil 200 | } 201 | err = &withMessage{ 202 | cause: err, 203 | msg: fmt.Sprintf(format, args...), 204 | } 205 | return &withStack{ 206 | err, 207 | callers(), 208 | } 209 | } 210 | 211 | // WithMessage annotates err with a new message. 212 | // If err is nil, WithMessage returns nil. 213 | func WithMessage(err error, message string) error { 214 | if err == nil { 215 | return nil 216 | } 217 | return &withMessage{ 218 | cause: err, 219 | msg: message, 220 | } 221 | } 222 | 223 | type withMessage struct { 224 | cause error 225 | msg string 226 | } 227 | 228 | func (w *withMessage) Error() string { return w.msg + ": " + w.cause.Error() } 229 | func (w *withMessage) Cause() error { return w.cause } 230 | 231 | func (w *withMessage) Format(s fmt.State, verb rune) { 232 | switch verb { 233 | case 'v': 234 | if s.Flag('+') { 235 | fmt.Fprintf(s, "%+v\n", w.Cause()) 236 | io.WriteString(s, w.msg) 237 | return 238 | } 239 | fallthrough 240 | case 's', 'q': 241 | io.WriteString(s, w.Error()) 242 | } 243 | } 244 | 245 | // Cause returns the underlying cause of the error, if possible. 246 | // An error value has a cause if it implements the following 247 | // interface: 248 | // 249 | // type causer interface { 250 | // Cause() error 251 | // } 252 | // 253 | // If the error does not implement Cause, the original error will 254 | // be returned. If the error is nil, nil will be returned without further 255 | // investigation. 256 | func Cause(err error) error { 257 | type causer interface { 258 | Cause() error 259 | } 260 | 261 | for err != nil { 262 | cause, ok := err.(causer) 263 | if !ok { 264 | break 265 | } 266 | err = cause.Cause() 267 | } 268 | return err 269 | } 270 | -------------------------------------------------------------------------------- /vendor/github.com/pkg/errors/stack.go: -------------------------------------------------------------------------------- 1 | package errors 2 | 3 | import ( 4 | "fmt" 5 | "io" 6 | "path" 7 | "runtime" 8 | "strings" 9 | ) 10 | 11 | // Frame represents a program counter inside a stack frame. 12 | type Frame uintptr 13 | 14 | // pc returns the program counter for this frame; 15 | // multiple frames may have the same PC value. 16 | func (f Frame) pc() uintptr { return uintptr(f) - 1 } 17 | 18 | // file returns the full path to the file that contains the 19 | // function for this Frame's pc. 20 | func (f Frame) file() string { 21 | fn := runtime.FuncForPC(f.pc()) 22 | if fn == nil { 23 | return "unknown" 24 | } 25 | file, _ := fn.FileLine(f.pc()) 26 | return file 27 | } 28 | 29 | // line returns the line number of source code of the 30 | // function for this Frame's pc. 31 | func (f Frame) line() int { 32 | fn := runtime.FuncForPC(f.pc()) 33 | if fn == nil { 34 | return 0 35 | } 36 | _, line := fn.FileLine(f.pc()) 37 | return line 38 | } 39 | 40 | // Format formats the frame according to the fmt.Formatter interface. 41 | // 42 | // %s source file 43 | // %d source line 44 | // %n function name 45 | // %v equivalent to %s:%d 46 | // 47 | // Format accepts flags that alter the printing of some verbs, as follows: 48 | // 49 | // %+s path of source file relative to the compile time GOPATH 50 | // %+v equivalent to %+s:%d 51 | func (f Frame) Format(s fmt.State, verb rune) { 52 | switch verb { 53 | case 's': 54 | switch { 55 | case s.Flag('+'): 56 | pc := f.pc() 57 | fn := runtime.FuncForPC(pc) 58 | if fn == nil { 59 | io.WriteString(s, "unknown") 60 | } else { 61 | file, _ := fn.FileLine(pc) 62 | fmt.Fprintf(s, "%s\n\t%s", fn.Name(), file) 63 | } 64 | default: 65 | io.WriteString(s, path.Base(f.file())) 66 | } 67 | case 'd': 68 | fmt.Fprintf(s, "%d", f.line()) 69 | case 'n': 70 | name := runtime.FuncForPC(f.pc()).Name() 71 | io.WriteString(s, funcname(name)) 72 | case 'v': 73 | f.Format(s, 's') 74 | io.WriteString(s, ":") 75 | f.Format(s, 'd') 76 | } 77 | } 78 | 79 | // StackTrace is stack of Frames from innermost (newest) to outermost (oldest). 80 | type StackTrace []Frame 81 | 82 | func (st StackTrace) Format(s fmt.State, verb rune) { 83 | switch verb { 84 | case 'v': 85 | switch { 86 | case s.Flag('+'): 87 | for _, f := range st { 88 | fmt.Fprintf(s, "\n%+v", f) 89 | } 90 | case s.Flag('#'): 91 | fmt.Fprintf(s, "%#v", []Frame(st)) 92 | default: 93 | fmt.Fprintf(s, "%v", []Frame(st)) 94 | } 95 | case 's': 96 | fmt.Fprintf(s, "%s", []Frame(st)) 97 | } 98 | } 99 | 100 | // stack represents a stack of program counters. 101 | type stack []uintptr 102 | 103 | func (s *stack) Format(st fmt.State, verb rune) { 104 | switch verb { 105 | case 'v': 106 | switch { 107 | case st.Flag('+'): 108 | for _, pc := range *s { 109 | f := Frame(pc) 110 | fmt.Fprintf(st, "\n%+v", f) 111 | } 112 | } 113 | } 114 | } 115 | 116 | func (s *stack) StackTrace() StackTrace { 117 | f := make([]Frame, len(*s)) 118 | for i := 0; i < len(f); i++ { 119 | f[i] = Frame((*s)[i]) 120 | } 121 | return f 122 | } 123 | 124 | func callers() *stack { 125 | const depth = 32 126 | var pcs [depth]uintptr 127 | n := runtime.Callers(3, pcs[:]) 128 | var st stack = pcs[0:n] 129 | return &st 130 | } 131 | 132 | // funcname removes the path prefix component of a function's name reported by func.Name(). 133 | func funcname(name string) string { 134 | i := strings.LastIndex(name, "/") 135 | name = name[i+1:] 136 | i = strings.Index(name, ".") 137 | return name[i+1:] 138 | } 139 | 140 | func trimGOPATH(name, file string) string { 141 | // Here we want to get the source file path relative to the compile time 142 | // GOPATH. As of Go 1.6.x there is no direct way to know the compiled 143 | // GOPATH at runtime, but we can infer the number of path segments in the 144 | // GOPATH. We note that fn.Name() returns the function name qualified by 145 | // the import path, which does not include the GOPATH. Thus we can trim 146 | // segments from the beginning of the file path until the number of path 147 | // separators remaining is one more than the number of path separators in 148 | // the function name. For example, given: 149 | // 150 | // GOPATH /home/user 151 | // file /home/user/src/pkg/sub/file.go 152 | // fn.Name() pkg/sub.Type.Method 153 | // 154 | // We want to produce: 155 | // 156 | // pkg/sub/file.go 157 | // 158 | // From this we can easily see that fn.Name() has one less path separator 159 | // than our desired output. We count separators from the end of the file 160 | // path until it finds two more than in the function name and then move 161 | // one character forward to preserve the initial path segment without a 162 | // leading separator. 163 | const sep = "/" 164 | goal := strings.Count(name, sep) + 2 165 | i := len(file) 166 | for n := 0; n < goal; n++ { 167 | i = strings.LastIndex(file[:i], sep) 168 | if i == -1 { 169 | // not enough separators found, set i so that the slice expression 170 | // below leaves file unmodified 171 | i = -len(sep) 172 | break 173 | } 174 | } 175 | // get back to 0 or trim the leading separator 176 | file = file[i+len(sep):] 177 | return file 178 | } 179 | -------------------------------------------------------------------------------- /vendor/github.com/pkg/sftp/CONTRIBUTORS: -------------------------------------------------------------------------------- 1 | Dave Cheney 2 | Saulius Gurklys 3 | John Eikenberry 4 | -------------------------------------------------------------------------------- /vendor/github.com/pkg/sftp/LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) 2013, Dave Cheney 2 | All rights reserved. 3 | 4 | Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: 5 | 6 | * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. 7 | * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. 8 | 9 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 10 | -------------------------------------------------------------------------------- /vendor/github.com/pkg/sftp/README.md: -------------------------------------------------------------------------------- 1 | sftp 2 | ---- 3 | 4 | The `sftp` package provides support for file system operations on remote ssh 5 | servers using the SFTP subsystem. It also implements an SFTP server for serving 6 | files from the filesystem. 7 | 8 | [![UNIX Build Status](https://travis-ci.org/pkg/sftp.svg?branch=master)](https://travis-ci.org/pkg/sftp) [![GoDoc](http://godoc.org/github.com/pkg/sftp?status.svg)](http://godoc.org/github.com/pkg/sftp) 9 | 10 | usage and examples 11 | ------------------ 12 | 13 | See [godoc.org/github.com/pkg/sftp](http://godoc.org/github.com/pkg/sftp) for 14 | examples and usage. 15 | 16 | The basic operation of the package mirrors the facilities of the 17 | [os](http://golang.org/pkg/os) package. 18 | 19 | The Walker interface for directory traversal is heavily inspired by Keith 20 | Rarick's [fs](http://godoc.org/github.com/kr/fs) package. 21 | 22 | roadmap 23 | ------- 24 | 25 | * There is way too much duplication in the Client methods. If there was an 26 | unmarshal(interface{}) method this would reduce a heap of the duplication. 27 | 28 | contributing 29 | ------------ 30 | 31 | We welcome pull requests, bug fixes and issue reports. 32 | 33 | Before proposing a large change, first please discuss your change by raising an 34 | issue. 35 | 36 | For API/code bugs, please include a small, self contained code example to 37 | reproduce the issue. For pull requests, remember test coverage. 38 | 39 | We handle issues and pull requests with a 0 open philosophy. That means we will 40 | try to address the submission as soon as possible and will work toward a 41 | resolution. If progress can no longer be made (eg. unreproducible bug) or stops 42 | (eg. unresponsive submitter), we will close the bug. 43 | 44 | Please remember that we don't have infinite time and so it is a good guideline 45 | that the less time it takes us to work with you on your idea/issue the greater 46 | the chance we will have the time to do it. 47 | 48 | Thanks. 49 | -------------------------------------------------------------------------------- /vendor/github.com/pkg/sftp/attrs.go: -------------------------------------------------------------------------------- 1 | package sftp 2 | 3 | // ssh_FXP_ATTRS support 4 | // see http://tools.ietf.org/html/draft-ietf-secsh-filexfer-02#section-5 5 | 6 | import ( 7 | "os" 8 | "syscall" 9 | "time" 10 | ) 11 | 12 | const ( 13 | ssh_FILEXFER_ATTR_SIZE = 0x00000001 14 | ssh_FILEXFER_ATTR_UIDGID = 0x00000002 15 | ssh_FILEXFER_ATTR_PERMISSIONS = 0x00000004 16 | ssh_FILEXFER_ATTR_ACMODTIME = 0x00000008 17 | ssh_FILEXFER_ATTR_EXTENDED = 0x80000000 18 | ) 19 | 20 | // fileInfo is an artificial type designed to satisfy os.FileInfo. 21 | type fileInfo struct { 22 | name string 23 | size int64 24 | mode os.FileMode 25 | mtime time.Time 26 | sys interface{} 27 | } 28 | 29 | // Name returns the base name of the file. 30 | func (fi *fileInfo) Name() string { return fi.name } 31 | 32 | // Size returns the length in bytes for regular files; system-dependent for others. 33 | func (fi *fileInfo) Size() int64 { return fi.size } 34 | 35 | // Mode returns file mode bits. 36 | func (fi *fileInfo) Mode() os.FileMode { return fi.mode } 37 | 38 | // ModTime returns the last modification time of the file. 39 | func (fi *fileInfo) ModTime() time.Time { return fi.mtime } 40 | 41 | // IsDir returns true if the file is a directory. 42 | func (fi *fileInfo) IsDir() bool { return fi.Mode().IsDir() } 43 | 44 | func (fi *fileInfo) Sys() interface{} { return fi.sys } 45 | 46 | // FileStat holds the original unmarshalled values from a call to READDIR or *STAT. 47 | // It is exported for the purposes of accessing the raw values via os.FileInfo.Sys() 48 | type FileStat struct { 49 | Size uint64 50 | Mode uint32 51 | Mtime uint32 52 | Atime uint32 53 | UID uint32 54 | GID uint32 55 | Extended []StatExtended 56 | } 57 | 58 | // StatExtended contains additional, extended information for a FileStat. 59 | type StatExtended struct { 60 | ExtType string 61 | ExtData string 62 | } 63 | 64 | func fileInfoFromStat(st *FileStat, name string) os.FileInfo { 65 | fs := &fileInfo{ 66 | name: name, 67 | size: int64(st.Size), 68 | mode: toFileMode(st.Mode), 69 | mtime: time.Unix(int64(st.Mtime), 0), 70 | sys: st, 71 | } 72 | return fs 73 | } 74 | 75 | func fileStatFromInfo(fi os.FileInfo) (uint32, FileStat) { 76 | mtime := fi.ModTime().Unix() 77 | atime := mtime 78 | var flags uint32 = ssh_FILEXFER_ATTR_SIZE | 79 | ssh_FILEXFER_ATTR_PERMISSIONS | 80 | ssh_FILEXFER_ATTR_ACMODTIME 81 | 82 | fileStat := FileStat{ 83 | Size: uint64(fi.Size()), 84 | Mode: fromFileMode(fi.Mode()), 85 | Mtime: uint32(mtime), 86 | Atime: uint32(atime), 87 | } 88 | 89 | // os specific file stat decoding 90 | fileStatFromInfoOs(fi, &flags, &fileStat) 91 | 92 | return flags, fileStat 93 | } 94 | 95 | func unmarshalAttrs(b []byte) (*FileStat, []byte) { 96 | flags, b := unmarshalUint32(b) 97 | var fs FileStat 98 | if flags&ssh_FILEXFER_ATTR_SIZE == ssh_FILEXFER_ATTR_SIZE { 99 | fs.Size, b = unmarshalUint64(b) 100 | } 101 | if flags&ssh_FILEXFER_ATTR_UIDGID == ssh_FILEXFER_ATTR_UIDGID { 102 | fs.UID, b = unmarshalUint32(b) 103 | } 104 | if flags&ssh_FILEXFER_ATTR_UIDGID == ssh_FILEXFER_ATTR_UIDGID { 105 | fs.GID, b = unmarshalUint32(b) 106 | } 107 | if flags&ssh_FILEXFER_ATTR_PERMISSIONS == ssh_FILEXFER_ATTR_PERMISSIONS { 108 | fs.Mode, b = unmarshalUint32(b) 109 | } 110 | if flags&ssh_FILEXFER_ATTR_ACMODTIME == ssh_FILEXFER_ATTR_ACMODTIME { 111 | fs.Atime, b = unmarshalUint32(b) 112 | fs.Mtime, b = unmarshalUint32(b) 113 | } 114 | if flags&ssh_FILEXFER_ATTR_EXTENDED == ssh_FILEXFER_ATTR_EXTENDED { 115 | var count uint32 116 | count, b = unmarshalUint32(b) 117 | ext := make([]StatExtended, count, count) 118 | for i := uint32(0); i < count; i++ { 119 | var typ string 120 | var data string 121 | typ, b = unmarshalString(b) 122 | data, b = unmarshalString(b) 123 | ext[i] = StatExtended{typ, data} 124 | } 125 | fs.Extended = ext 126 | } 127 | return &fs, b 128 | } 129 | 130 | func marshalFileInfo(b []byte, fi os.FileInfo) []byte { 131 | // attributes variable struct, and also variable per protocol version 132 | // spec version 3 attributes: 133 | // uint32 flags 134 | // uint64 size present only if flag SSH_FILEXFER_ATTR_SIZE 135 | // uint32 uid present only if flag SSH_FILEXFER_ATTR_UIDGID 136 | // uint32 gid present only if flag SSH_FILEXFER_ATTR_UIDGID 137 | // uint32 permissions present only if flag SSH_FILEXFER_ATTR_PERMISSIONS 138 | // uint32 atime present only if flag SSH_FILEXFER_ACMODTIME 139 | // uint32 mtime present only if flag SSH_FILEXFER_ACMODTIME 140 | // uint32 extended_count present only if flag SSH_FILEXFER_ATTR_EXTENDED 141 | // string extended_type 142 | // string extended_data 143 | // ... more extended data (extended_type - extended_data pairs), 144 | // so that number of pairs equals extended_count 145 | 146 | flags, fileStat := fileStatFromInfo(fi) 147 | 148 | b = marshalUint32(b, flags) 149 | if flags&ssh_FILEXFER_ATTR_SIZE != 0 { 150 | b = marshalUint64(b, fileStat.Size) 151 | } 152 | if flags&ssh_FILEXFER_ATTR_UIDGID != 0 { 153 | b = marshalUint32(b, fileStat.UID) 154 | b = marshalUint32(b, fileStat.GID) 155 | } 156 | if flags&ssh_FILEXFER_ATTR_PERMISSIONS != 0 { 157 | b = marshalUint32(b, fileStat.Mode) 158 | } 159 | if flags&ssh_FILEXFER_ATTR_ACMODTIME != 0 { 160 | b = marshalUint32(b, fileStat.Atime) 161 | b = marshalUint32(b, fileStat.Mtime) 162 | } 163 | 164 | return b 165 | } 166 | 167 | // toFileMode converts sftp filemode bits to the os.FileMode specification 168 | func toFileMode(mode uint32) os.FileMode { 169 | var fm = os.FileMode(mode & 0777) 170 | switch mode & syscall.S_IFMT { 171 | case syscall.S_IFBLK: 172 | fm |= os.ModeDevice 173 | case syscall.S_IFCHR: 174 | fm |= os.ModeDevice | os.ModeCharDevice 175 | case syscall.S_IFDIR: 176 | fm |= os.ModeDir 177 | case syscall.S_IFIFO: 178 | fm |= os.ModeNamedPipe 179 | case syscall.S_IFLNK: 180 | fm |= os.ModeSymlink 181 | case syscall.S_IFREG: 182 | // nothing to do 183 | case syscall.S_IFSOCK: 184 | fm |= os.ModeSocket 185 | } 186 | if mode&syscall.S_ISGID != 0 { 187 | fm |= os.ModeSetgid 188 | } 189 | if mode&syscall.S_ISUID != 0 { 190 | fm |= os.ModeSetuid 191 | } 192 | if mode&syscall.S_ISVTX != 0 { 193 | fm |= os.ModeSticky 194 | } 195 | return fm 196 | } 197 | 198 | // fromFileMode converts from the os.FileMode specification to sftp filemode bits 199 | func fromFileMode(mode os.FileMode) uint32 { 200 | ret := uint32(0) 201 | 202 | if mode&os.ModeDevice != 0 { 203 | if mode&os.ModeCharDevice != 0 { 204 | ret |= syscall.S_IFCHR 205 | } else { 206 | ret |= syscall.S_IFBLK 207 | } 208 | } 209 | if mode&os.ModeDir != 0 { 210 | ret |= syscall.S_IFDIR 211 | } 212 | if mode&os.ModeSymlink != 0 { 213 | ret |= syscall.S_IFLNK 214 | } 215 | if mode&os.ModeNamedPipe != 0 { 216 | ret |= syscall.S_IFIFO 217 | } 218 | if mode&os.ModeSetgid != 0 { 219 | ret |= syscall.S_ISGID 220 | } 221 | if mode&os.ModeSetuid != 0 { 222 | ret |= syscall.S_ISUID 223 | } 224 | if mode&os.ModeSticky != 0 { 225 | ret |= syscall.S_ISVTX 226 | } 227 | if mode&os.ModeSocket != 0 { 228 | ret |= syscall.S_IFSOCK 229 | } 230 | 231 | if mode&os.ModeType == 0 { 232 | ret |= syscall.S_IFREG 233 | } 234 | ret |= uint32(mode & os.ModePerm) 235 | 236 | return ret 237 | } 238 | -------------------------------------------------------------------------------- /vendor/github.com/pkg/sftp/attrs_stubs.go: -------------------------------------------------------------------------------- 1 | // +build !cgo,!plan9 windows android 2 | 3 | package sftp 4 | 5 | import ( 6 | "os" 7 | ) 8 | 9 | func fileStatFromInfoOs(fi os.FileInfo, flags *uint32, fileStat *FileStat) { 10 | // todo 11 | } 12 | -------------------------------------------------------------------------------- /vendor/github.com/pkg/sftp/attrs_unix.go: -------------------------------------------------------------------------------- 1 | // +build darwin dragonfly freebsd !android,linux netbsd openbsd solaris 2 | // +build cgo 3 | 4 | package sftp 5 | 6 | import ( 7 | "os" 8 | "syscall" 9 | ) 10 | 11 | func fileStatFromInfoOs(fi os.FileInfo, flags *uint32, fileStat *FileStat) { 12 | if statt, ok := fi.Sys().(*syscall.Stat_t); ok { 13 | *flags |= ssh_FILEXFER_ATTR_UIDGID 14 | fileStat.UID = statt.Uid 15 | fileStat.GID = statt.Gid 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /vendor/github.com/pkg/sftp/conn.go: -------------------------------------------------------------------------------- 1 | package sftp 2 | 3 | import ( 4 | "encoding" 5 | "io" 6 | "sync" 7 | 8 | "github.com/pkg/errors" 9 | ) 10 | 11 | // conn implements a bidirectional channel on which client and server 12 | // connections are multiplexed. 13 | type conn struct { 14 | io.Reader 15 | io.WriteCloser 16 | sync.Mutex // used to serialise writes to sendPacket 17 | // sendPacketTest is needed to replicate packet issues in testing 18 | sendPacketTest func(w io.Writer, m encoding.BinaryMarshaler) error 19 | } 20 | 21 | func (c *conn) recvPacket() (uint8, []byte, error) { 22 | return recvPacket(c) 23 | } 24 | 25 | func (c *conn) sendPacket(m encoding.BinaryMarshaler) error { 26 | c.Lock() 27 | defer c.Unlock() 28 | if c.sendPacketTest != nil { 29 | return c.sendPacketTest(c, m) 30 | } 31 | return sendPacket(c, m) 32 | } 33 | 34 | type clientConn struct { 35 | conn 36 | wg sync.WaitGroup 37 | sync.Mutex // protects inflight 38 | inflight map[uint32]chan<- result // outstanding requests 39 | } 40 | 41 | // Close closes the SFTP session. 42 | func (c *clientConn) Close() error { 43 | defer c.wg.Wait() 44 | return c.conn.Close() 45 | } 46 | 47 | func (c *clientConn) loop() { 48 | defer c.wg.Done() 49 | err := c.recv() 50 | if err != nil { 51 | c.broadcastErr(err) 52 | } 53 | } 54 | 55 | // recv continuously reads from the server and forwards responses to the 56 | // appropriate channel. 57 | func (c *clientConn) recv() error { 58 | defer func() { 59 | c.conn.Lock() 60 | c.conn.Close() 61 | c.conn.Unlock() 62 | }() 63 | for { 64 | typ, data, err := c.recvPacket() 65 | if err != nil { 66 | return err 67 | } 68 | sid, _ := unmarshalUint32(data) 69 | c.Lock() 70 | ch, ok := c.inflight[sid] 71 | delete(c.inflight, sid) 72 | c.Unlock() 73 | if !ok { 74 | // This is an unexpected occurrence. Send the error 75 | // back to all listeners so that they terminate 76 | // gracefully. 77 | return errors.Errorf("sid: %v not fond", sid) 78 | } 79 | ch <- result{typ: typ, data: data} 80 | } 81 | } 82 | 83 | // result captures the result of receiving the a packet from the server 84 | type result struct { 85 | typ byte 86 | data []byte 87 | err error 88 | } 89 | 90 | type idmarshaler interface { 91 | id() uint32 92 | encoding.BinaryMarshaler 93 | } 94 | 95 | func (c *clientConn) sendPacket(p idmarshaler) (byte, []byte, error) { 96 | ch := make(chan result, 1) 97 | c.dispatchRequest(ch, p) 98 | s := <-ch 99 | return s.typ, s.data, s.err 100 | } 101 | 102 | func (c *clientConn) dispatchRequest(ch chan<- result, p idmarshaler) { 103 | c.Lock() 104 | c.inflight[p.id()] = ch 105 | c.Unlock() 106 | if err := c.conn.sendPacket(p); err != nil { 107 | c.Lock() 108 | delete(c.inflight, p.id()) 109 | c.Unlock() 110 | ch <- result{err: err} 111 | } 112 | } 113 | 114 | // broadcastErr sends an error to all goroutines waiting for a response. 115 | func (c *clientConn) broadcastErr(err error) { 116 | c.Lock() 117 | listeners := make([]chan<- result, 0, len(c.inflight)) 118 | for _, ch := range c.inflight { 119 | listeners = append(listeners, ch) 120 | } 121 | c.Unlock() 122 | for _, ch := range listeners { 123 | ch <- result{err: err} 124 | } 125 | } 126 | 127 | type serverConn struct { 128 | conn 129 | } 130 | 131 | func (s *serverConn) sendError(p ider, err error) error { 132 | return s.sendPacket(statusFromError(p, err)) 133 | } 134 | -------------------------------------------------------------------------------- /vendor/github.com/pkg/sftp/debug.go: -------------------------------------------------------------------------------- 1 | // +build debug 2 | 3 | package sftp 4 | 5 | import "log" 6 | 7 | func debug(fmt string, args ...interface{}) { 8 | log.Printf(fmt, args...) 9 | } 10 | -------------------------------------------------------------------------------- /vendor/github.com/pkg/sftp/packet-manager.go: -------------------------------------------------------------------------------- 1 | package sftp 2 | 3 | import "encoding" 4 | 5 | // The goal of the packetManager is to keep the outgoing packets in the same 6 | // order as the incoming. This is due to some sftp clients requiring this 7 | // behavior (eg. winscp). 8 | 9 | type packetSender interface { 10 | sendPacket(encoding.BinaryMarshaler) error 11 | } 12 | 13 | type packetManager struct { 14 | requests chan requestPacket 15 | responses chan responsePacket 16 | fini chan struct{} 17 | incoming requestPacketIDs 18 | outgoing responsePackets 19 | sender packetSender // connection object 20 | } 21 | 22 | func newPktMgr(sender packetSender) packetManager { 23 | s := packetManager{ 24 | requests: make(chan requestPacket, sftpServerWorkerCount), 25 | responses: make(chan responsePacket, sftpServerWorkerCount), 26 | fini: make(chan struct{}), 27 | incoming: make([]uint32, 0, sftpServerWorkerCount), 28 | outgoing: make([]responsePacket, 0, sftpServerWorkerCount), 29 | sender: sender, 30 | } 31 | go s.worker() 32 | return s 33 | } 34 | 35 | // register incoming packets to be handled 36 | // send id of 0 for packets without id 37 | func (s packetManager) incomingPacket(pkt requestPacket) { 38 | s.requests <- pkt // buffer == sftpServerWorkerCount 39 | } 40 | 41 | // register outgoing packets as being ready 42 | func (s packetManager) readyPacket(pkt responsePacket) { 43 | s.responses <- pkt 44 | } 45 | 46 | // shut down packetManager worker 47 | func (s packetManager) close() { 48 | close(s.fini) 49 | } 50 | 51 | // process packets 52 | func (s *packetManager) worker() { 53 | for { 54 | select { 55 | case pkt := <-s.requests: 56 | debug("incoming id: %v", pkt.id()) 57 | s.incoming = append(s.incoming, pkt.id()) 58 | if len(s.incoming) > 1 { 59 | s.incoming.Sort() 60 | } 61 | case pkt := <-s.responses: 62 | debug("outgoing pkt: %v", pkt.id()) 63 | s.outgoing = append(s.outgoing, pkt) 64 | if len(s.outgoing) > 1 { 65 | s.outgoing.Sort() 66 | } 67 | case <-s.fini: 68 | return 69 | } 70 | s.maybeSendPackets() 71 | } 72 | } 73 | 74 | // send as many packets as are ready 75 | func (s *packetManager) maybeSendPackets() { 76 | for { 77 | if len(s.outgoing) == 0 || len(s.incoming) == 0 { 78 | debug("break! -- outgoing: %v; incoming: %v", 79 | len(s.outgoing), len(s.incoming)) 80 | break 81 | } 82 | out := s.outgoing[0] 83 | in := s.incoming[0] 84 | // debug("incoming: %v", s.incoming) 85 | // debug("outgoing: %v", outfilter(s.outgoing)) 86 | if in == out.id() { 87 | s.sender.sendPacket(out) 88 | // pop off heads 89 | copy(s.incoming, s.incoming[1:]) // shift left 90 | s.incoming = s.incoming[:len(s.incoming)-1] // remove last 91 | copy(s.outgoing, s.outgoing[1:]) // shift left 92 | s.outgoing = s.outgoing[:len(s.outgoing)-1] // remove last 93 | } else { 94 | break 95 | } 96 | } 97 | } 98 | 99 | func outfilter(o []responsePacket) []uint32 { 100 | res := make([]uint32, 0, len(o)) 101 | for _, v := range o { 102 | res = append(res, v.id()) 103 | } 104 | return res 105 | } 106 | -------------------------------------------------------------------------------- /vendor/github.com/pkg/sftp/packet-manager_go1.8.go: -------------------------------------------------------------------------------- 1 | // +build go1.8 2 | 3 | package sftp 4 | 5 | import "sort" 6 | 7 | type responsePackets []responsePacket 8 | 9 | func (r responsePackets) Sort() { 10 | sort.Slice(r, func(i, j int) bool { 11 | return r[i].id() < r[j].id() 12 | }) 13 | } 14 | 15 | type requestPacketIDs []uint32 16 | 17 | func (r requestPacketIDs) Sort() { 18 | sort.Slice(r, func(i, j int) bool { 19 | return r[i] < r[j] 20 | }) 21 | } 22 | -------------------------------------------------------------------------------- /vendor/github.com/pkg/sftp/packet-manager_legacy.go: -------------------------------------------------------------------------------- 1 | // +build !go1.8 2 | 3 | package sftp 4 | 5 | import "sort" 6 | 7 | // for sorting/ordering outgoing 8 | type responsePackets []responsePacket 9 | 10 | func (r responsePackets) Len() int { return len(r) } 11 | func (r responsePackets) Swap(i, j int) { r[i], r[j] = r[j], r[i] } 12 | func (r responsePackets) Less(i, j int) bool { return r[i].id() < r[j].id() } 13 | func (r responsePackets) Sort() { sort.Sort(r) } 14 | 15 | // for sorting/ordering incoming 16 | type requestPacketIDs []uint32 17 | 18 | func (r requestPacketIDs) Len() int { return len(r) } 19 | func (r requestPacketIDs) Swap(i, j int) { r[i], r[j] = r[j], r[i] } 20 | func (r requestPacketIDs) Less(i, j int) bool { return r[i] < r[j] } 21 | func (r requestPacketIDs) Sort() { sort.Sort(r) } 22 | -------------------------------------------------------------------------------- /vendor/github.com/pkg/sftp/packet-typing.go: -------------------------------------------------------------------------------- 1 | package sftp 2 | 3 | import ( 4 | "encoding" 5 | 6 | "github.com/pkg/errors" 7 | ) 8 | 9 | // all incoming packets 10 | type requestPacket interface { 11 | encoding.BinaryUnmarshaler 12 | id() uint32 13 | } 14 | 15 | type responsePacket interface { 16 | encoding.BinaryMarshaler 17 | id() uint32 18 | } 19 | 20 | // interfaces to group types 21 | type hasPath interface { 22 | requestPacket 23 | getPath() string 24 | } 25 | 26 | type hasHandle interface { 27 | requestPacket 28 | getHandle() string 29 | } 30 | 31 | type isOpener interface { 32 | hasPath 33 | isOpener() 34 | } 35 | 36 | type notReadOnly interface { 37 | notReadOnly() 38 | } 39 | 40 | //// define types by adding methods 41 | // hasPath 42 | func (p sshFxpLstatPacket) getPath() string { return p.Path } 43 | func (p sshFxpStatPacket) getPath() string { return p.Path } 44 | func (p sshFxpRmdirPacket) getPath() string { return p.Path } 45 | func (p sshFxpReadlinkPacket) getPath() string { return p.Path } 46 | func (p sshFxpRealpathPacket) getPath() string { return p.Path } 47 | func (p sshFxpMkdirPacket) getPath() string { return p.Path } 48 | func (p sshFxpSetstatPacket) getPath() string { return p.Path } 49 | func (p sshFxpStatvfsPacket) getPath() string { return p.Path } 50 | func (p sshFxpRemovePacket) getPath() string { return p.Filename } 51 | func (p sshFxpRenamePacket) getPath() string { return p.Oldpath } 52 | func (p sshFxpSymlinkPacket) getPath() string { return p.Targetpath } 53 | 54 | // Openers implement hasPath and isOpener 55 | func (p sshFxpOpendirPacket) getPath() string { return p.Path } 56 | func (p sshFxpOpendirPacket) isOpener() {} 57 | func (p sshFxpOpenPacket) getPath() string { return p.Path } 58 | func (p sshFxpOpenPacket) isOpener() {} 59 | 60 | // hasHandle 61 | func (p sshFxpFstatPacket) getHandle() string { return p.Handle } 62 | func (p sshFxpFsetstatPacket) getHandle() string { return p.Handle } 63 | func (p sshFxpReadPacket) getHandle() string { return p.Handle } 64 | func (p sshFxpWritePacket) getHandle() string { return p.Handle } 65 | func (p sshFxpReaddirPacket) getHandle() string { return p.Handle } 66 | 67 | // notReadOnly 68 | func (p sshFxpWritePacket) notReadOnly() {} 69 | func (p sshFxpSetstatPacket) notReadOnly() {} 70 | func (p sshFxpFsetstatPacket) notReadOnly() {} 71 | func (p sshFxpRemovePacket) notReadOnly() {} 72 | func (p sshFxpMkdirPacket) notReadOnly() {} 73 | func (p sshFxpRmdirPacket) notReadOnly() {} 74 | func (p sshFxpRenamePacket) notReadOnly() {} 75 | func (p sshFxpSymlinkPacket) notReadOnly() {} 76 | 77 | // this has a handle, but is only used for close 78 | func (p sshFxpClosePacket) getHandle() string { return p.Handle } 79 | 80 | // some packets with ID are missing id() 81 | func (p sshFxpDataPacket) id() uint32 { return p.ID } 82 | func (p sshFxpStatusPacket) id() uint32 { return p.ID } 83 | func (p sshFxpStatResponse) id() uint32 { return p.ID } 84 | func (p sshFxpNamePacket) id() uint32 { return p.ID } 85 | func (p sshFxpHandlePacket) id() uint32 { return p.ID } 86 | func (p sshFxVersionPacket) id() uint32 { return 0 } 87 | 88 | // take raw incoming packet data and build packet objects 89 | func makePacket(p rxPacket) (requestPacket, error) { 90 | var pkt requestPacket 91 | switch p.pktType { 92 | case ssh_FXP_INIT: 93 | pkt = &sshFxInitPacket{} 94 | case ssh_FXP_LSTAT: 95 | pkt = &sshFxpLstatPacket{} 96 | case ssh_FXP_OPEN: 97 | pkt = &sshFxpOpenPacket{} 98 | case ssh_FXP_CLOSE: 99 | pkt = &sshFxpClosePacket{} 100 | case ssh_FXP_READ: 101 | pkt = &sshFxpReadPacket{} 102 | case ssh_FXP_WRITE: 103 | pkt = &sshFxpWritePacket{} 104 | case ssh_FXP_FSTAT: 105 | pkt = &sshFxpFstatPacket{} 106 | case ssh_FXP_SETSTAT: 107 | pkt = &sshFxpSetstatPacket{} 108 | case ssh_FXP_FSETSTAT: 109 | pkt = &sshFxpFsetstatPacket{} 110 | case ssh_FXP_OPENDIR: 111 | pkt = &sshFxpOpendirPacket{} 112 | case ssh_FXP_READDIR: 113 | pkt = &sshFxpReaddirPacket{} 114 | case ssh_FXP_REMOVE: 115 | pkt = &sshFxpRemovePacket{} 116 | case ssh_FXP_MKDIR: 117 | pkt = &sshFxpMkdirPacket{} 118 | case ssh_FXP_RMDIR: 119 | pkt = &sshFxpRmdirPacket{} 120 | case ssh_FXP_REALPATH: 121 | pkt = &sshFxpRealpathPacket{} 122 | case ssh_FXP_STAT: 123 | pkt = &sshFxpStatPacket{} 124 | case ssh_FXP_RENAME: 125 | pkt = &sshFxpRenamePacket{} 126 | case ssh_FXP_READLINK: 127 | pkt = &sshFxpReadlinkPacket{} 128 | case ssh_FXP_SYMLINK: 129 | pkt = &sshFxpSymlinkPacket{} 130 | case ssh_FXP_EXTENDED: 131 | pkt = &sshFxpExtendedPacket{} 132 | default: 133 | return nil, errors.Errorf("unhandled packet type: %s", p.pktType) 134 | } 135 | if err := pkt.UnmarshalBinary(p.pktBytes); err != nil { 136 | return nil, err 137 | } 138 | return pkt, nil 139 | } 140 | -------------------------------------------------------------------------------- /vendor/github.com/pkg/sftp/release.go: -------------------------------------------------------------------------------- 1 | // +build !debug 2 | 3 | package sftp 4 | 5 | func debug(fmt string, args ...interface{}) {} 6 | -------------------------------------------------------------------------------- /vendor/github.com/pkg/sftp/request-example.go: -------------------------------------------------------------------------------- 1 | package sftp 2 | 3 | // This serves as an example of how to implement the request server handler as 4 | // well as a dummy backend for testing. It implements an in-memory backend that 5 | // works as a very simple filesystem with simple flat key-value lookup system. 6 | 7 | import ( 8 | "bytes" 9 | "fmt" 10 | "io" 11 | "os" 12 | "path/filepath" 13 | "sync" 14 | "time" 15 | ) 16 | 17 | // InMemHandler returns a Hanlders object with the test handlers 18 | func InMemHandler() Handlers { 19 | root := &root{ 20 | files: make(map[string]*memFile), 21 | } 22 | root.memFile = newMemFile("/", true) 23 | return Handlers{root, root, root, root} 24 | } 25 | 26 | // Handlers 27 | func (fs *root) Fileread(r Request) (io.ReaderAt, error) { 28 | fs.filesLock.Lock() 29 | defer fs.filesLock.Unlock() 30 | file, err := fs.fetch(r.Filepath) 31 | if err != nil { 32 | return nil, err 33 | } 34 | if file.symlink != "" { 35 | file, err = fs.fetch(file.symlink) 36 | if err != nil { 37 | return nil, err 38 | } 39 | } 40 | return file.ReaderAt() 41 | } 42 | 43 | func (fs *root) Filewrite(r Request) (io.WriterAt, error) { 44 | fs.filesLock.Lock() 45 | defer fs.filesLock.Unlock() 46 | file, err := fs.fetch(r.Filepath) 47 | if err == os.ErrNotExist { 48 | dir, err := fs.fetch(filepath.Dir(r.Filepath)) 49 | if err != nil { 50 | return nil, err 51 | } 52 | if !dir.isdir { 53 | return nil, os.ErrInvalid 54 | } 55 | file = newMemFile(r.Filepath, false) 56 | fs.files[r.Filepath] = file 57 | } 58 | return file.WriterAt() 59 | } 60 | 61 | func (fs *root) Filecmd(r Request) error { 62 | fs.filesLock.Lock() 63 | defer fs.filesLock.Unlock() 64 | switch r.Method { 65 | case "SetStat": 66 | return nil 67 | case "Rename": 68 | file, err := fs.fetch(r.Filepath) 69 | if err != nil { 70 | return err 71 | } 72 | if _, ok := fs.files[r.Target]; ok { 73 | return &os.LinkError{Op: "rename", Old: r.Filepath, New: r.Target, 74 | Err: fmt.Errorf("dest file exists")} 75 | } 76 | fs.files[r.Target] = file 77 | delete(fs.files, r.Filepath) 78 | case "Rmdir", "Remove": 79 | _, err := fs.fetch(filepath.Dir(r.Filepath)) 80 | if err != nil { 81 | return err 82 | } 83 | delete(fs.files, r.Filepath) 84 | case "Mkdir": 85 | _, err := fs.fetch(filepath.Dir(r.Filepath)) 86 | if err != nil { 87 | return err 88 | } 89 | fs.files[r.Filepath] = newMemFile(r.Filepath, true) 90 | case "Symlink": 91 | _, err := fs.fetch(r.Filepath) 92 | if err != nil { 93 | return err 94 | } 95 | link := newMemFile(r.Target, false) 96 | link.symlink = r.Filepath 97 | fs.files[r.Target] = link 98 | } 99 | return nil 100 | } 101 | 102 | func (fs *root) Fileinfo(r Request) ([]os.FileInfo, error) { 103 | fs.filesLock.Lock() 104 | defer fs.filesLock.Unlock() 105 | switch r.Method { 106 | case "List": 107 | list := []os.FileInfo{} 108 | for fn, fi := range fs.files { 109 | if filepath.Dir(fn) == r.Filepath { 110 | list = append(list, fi) 111 | } 112 | } 113 | return list, nil 114 | case "Stat": 115 | file, err := fs.fetch(r.Filepath) 116 | if err != nil { 117 | return nil, err 118 | } 119 | return []os.FileInfo{file}, nil 120 | case "Readlink": 121 | file, err := fs.fetch(r.Filepath) 122 | if err != nil { 123 | return nil, err 124 | } 125 | if file.symlink != "" { 126 | file, err = fs.fetch(file.symlink) 127 | if err != nil { 128 | return nil, err 129 | } 130 | } 131 | return []os.FileInfo{file}, nil 132 | } 133 | return nil, nil 134 | } 135 | 136 | // In memory file-system-y thing that the Hanlders live on 137 | type root struct { 138 | *memFile 139 | files map[string]*memFile 140 | filesLock sync.Mutex 141 | } 142 | 143 | func (fs *root) fetch(path string) (*memFile, error) { 144 | if path == "/" { 145 | return fs.memFile, nil 146 | } 147 | if file, ok := fs.files[path]; ok { 148 | return file, nil 149 | } 150 | return nil, os.ErrNotExist 151 | } 152 | 153 | // Implements os.FileInfo, Reader and Writer interfaces. 154 | // These are the 3 interfaces necessary for the Handlers. 155 | type memFile struct { 156 | name string 157 | modtime time.Time 158 | symlink string 159 | isdir bool 160 | content []byte 161 | contentLock sync.RWMutex 162 | } 163 | 164 | // factory to make sure modtime is set 165 | func newMemFile(name string, isdir bool) *memFile { 166 | return &memFile{ 167 | name: name, 168 | modtime: time.Now(), 169 | isdir: isdir, 170 | } 171 | } 172 | 173 | // Have memFile fulfill os.FileInfo interface 174 | func (f *memFile) Name() string { return filepath.Base(f.name) } 175 | func (f *memFile) Size() int64 { return int64(len(f.content)) } 176 | func (f *memFile) Mode() os.FileMode { 177 | ret := os.FileMode(0644) 178 | if f.isdir { 179 | ret = os.FileMode(0755) | os.ModeDir 180 | } 181 | if f.symlink != "" { 182 | ret = os.FileMode(0777) | os.ModeSymlink 183 | } 184 | return ret 185 | } 186 | func (f *memFile) ModTime() time.Time { return f.modtime } 187 | func (f *memFile) IsDir() bool { return f.isdir } 188 | func (f *memFile) Sys() interface{} { 189 | return fakeFileInfoSys() 190 | } 191 | 192 | // Read/Write 193 | func (f *memFile) ReaderAt() (io.ReaderAt, error) { 194 | if f.isdir { 195 | return nil, os.ErrInvalid 196 | } 197 | return bytes.NewReader(f.content), nil 198 | } 199 | 200 | func (f *memFile) WriterAt() (io.WriterAt, error) { 201 | if f.isdir { 202 | return nil, os.ErrInvalid 203 | } 204 | return f, nil 205 | } 206 | func (f *memFile) WriteAt(p []byte, off int64) (int, error) { 207 | // fmt.Println(string(p), off) 208 | // mimic write delays, should be optional 209 | time.Sleep(time.Microsecond * time.Duration(len(p))) 210 | f.contentLock.Lock() 211 | defer f.contentLock.Unlock() 212 | plen := len(p) + int(off) 213 | if plen >= len(f.content) { 214 | nc := make([]byte, plen) 215 | copy(nc, f.content) 216 | f.content = nc 217 | } 218 | copy(f.content[off:], p) 219 | return len(p), nil 220 | } 221 | -------------------------------------------------------------------------------- /vendor/github.com/pkg/sftp/request-interfaces.go: -------------------------------------------------------------------------------- 1 | package sftp 2 | 3 | import ( 4 | "io" 5 | "os" 6 | ) 7 | 8 | // Interfaces are differentiated based on required returned values. 9 | // All input arguments are to be pulled from Request (the only arg). 10 | 11 | // FileReader should return an io.Reader for the filepath 12 | type FileReader interface { 13 | Fileread(Request) (io.ReaderAt, error) 14 | } 15 | 16 | // FileWriter should return an io.Writer for the filepath 17 | type FileWriter interface { 18 | Filewrite(Request) (io.WriterAt, error) 19 | } 20 | 21 | // FileCmder should return an error (rename, remove, setstate, etc.) 22 | type FileCmder interface { 23 | Filecmd(Request) error 24 | } 25 | 26 | // FileInfoer should return file listing info and errors (readdir, stat) 27 | // note stat requests would return a list of 1 28 | type FileInfoer interface { 29 | Fileinfo(Request) ([]os.FileInfo, error) 30 | } 31 | -------------------------------------------------------------------------------- /vendor/github.com/pkg/sftp/request-readme.md: -------------------------------------------------------------------------------- 1 | # Request Based SFTP API 2 | 3 | The request based API allows for custom backends in a way similar to the http 4 | package. In order to create a backend you need to implement 4 handler 5 | interfaces; one for reading, one for writing, one for misc commands and one for 6 | listing files. Each has 1 required method and in each case those methods take 7 | the Request as the only parameter and they each return something different. 8 | These 4 interfaces are enough to handle all the SFTP traffic in a simplified 9 | manner. 10 | 11 | The Request structure has 5 public fields which you will deal with. 12 | 13 | - Method (string) - string name of incoming call 14 | - Filepath (string) - path of file to act on 15 | - Attrs ([]byte) - byte string of file attribute data 16 | - Target (string) - target path for renames and sym-links 17 | 18 | Below are the methods and a brief description of what they need to do. 19 | 20 | ### Fileread(*Request) (io.Reader, error) 21 | 22 | Handler for "Get" method and returns an io.Reader for the file which the server 23 | then sends to the client. 24 | 25 | ### Filewrite(*Request) (io.Writer, error) 26 | 27 | Handler for "Put" method and returns an io.Writer for the file which the server 28 | then writes the uploaded file to. 29 | 30 | ### Filecmd(*Request) error 31 | 32 | Handles "SetStat", "Rename", "Rmdir", "Mkdir" and "Symlink" methods. Makes the 33 | appropriate changes and returns nil for success or an filesystem like error 34 | (eg. os.ErrNotExist). 35 | 36 | ### Fileinfo(*Request) ([]os.FileInfo, error) 37 | 38 | Handles "List", "Stat", "Readlink" methods. Gathers/creates FileInfo structs 39 | with the data on the files and returns in a list (list of 1 for Stat and 40 | Readlink). 41 | 42 | 43 | ## TODO 44 | 45 | - Add support for API users to see trace/debugging info of what is going on 46 | inside SFTP server. 47 | - Consider adding support for SFTP file append only mode. 48 | 49 | -------------------------------------------------------------------------------- /vendor/github.com/pkg/sftp/request-server.go: -------------------------------------------------------------------------------- 1 | package sftp 2 | 3 | import ( 4 | "encoding" 5 | "io" 6 | "os" 7 | "path/filepath" 8 | "strconv" 9 | "sync" 10 | "syscall" 11 | 12 | "github.com/pkg/errors" 13 | ) 14 | 15 | var maxTxPacket uint32 = 1 << 15 16 | 17 | type handleHandler func(string) string 18 | 19 | // Handlers contains the 4 SFTP server request handlers. 20 | type Handlers struct { 21 | FileGet FileReader 22 | FilePut FileWriter 23 | FileCmd FileCmder 24 | FileInfo FileInfoer 25 | } 26 | 27 | // RequestServer abstracts the sftp protocol with an http request-like protocol 28 | type RequestServer struct { 29 | serverConn 30 | Handlers Handlers 31 | pktChan chan requestPacket 32 | pktMgr packetManager 33 | openRequests map[string]Request 34 | openRequestLock sync.RWMutex 35 | handleCount int 36 | } 37 | 38 | // NewRequestServer creates/allocates/returns new RequestServer. 39 | // Normally there there will be one server per user-session. 40 | func NewRequestServer(rwc io.ReadWriteCloser, h Handlers) *RequestServer { 41 | svrConn := serverConn{ 42 | conn: conn{ 43 | Reader: rwc, 44 | WriteCloser: rwc, 45 | }, 46 | } 47 | return &RequestServer{ 48 | serverConn: svrConn, 49 | Handlers: h, 50 | pktChan: make(chan requestPacket, sftpServerWorkerCount), 51 | pktMgr: newPktMgr(&svrConn), 52 | openRequests: make(map[string]Request), 53 | } 54 | } 55 | 56 | func (rs *RequestServer) nextRequest(r Request) string { 57 | rs.openRequestLock.Lock() 58 | defer rs.openRequestLock.Unlock() 59 | rs.handleCount++ 60 | handle := strconv.Itoa(rs.handleCount) 61 | rs.openRequests[handle] = r 62 | return handle 63 | } 64 | 65 | func (rs *RequestServer) getRequest(handle string) (Request, bool) { 66 | rs.openRequestLock.RLock() 67 | defer rs.openRequestLock.RUnlock() 68 | r, ok := rs.openRequests[handle] 69 | return r, ok 70 | } 71 | 72 | func (rs *RequestServer) closeRequest(handle string) { 73 | rs.openRequestLock.Lock() 74 | defer rs.openRequestLock.Unlock() 75 | if r, ok := rs.openRequests[handle]; ok { 76 | r.close() 77 | delete(rs.openRequests, handle) 78 | } 79 | } 80 | 81 | // Close the read/write/closer to trigger exiting the main server loop 82 | func (rs *RequestServer) Close() error { return rs.conn.Close() } 83 | 84 | // Serve requests for user session 85 | func (rs *RequestServer) Serve() error { 86 | var wg sync.WaitGroup 87 | wg.Add(sftpServerWorkerCount) 88 | for i := 0; i < sftpServerWorkerCount; i++ { 89 | go func() { 90 | defer wg.Done() 91 | if err := rs.packetWorker(); err != nil { 92 | rs.conn.Close() // shuts down recvPacket 93 | } 94 | }() 95 | } 96 | 97 | var err error 98 | var pkt requestPacket 99 | var pktType uint8 100 | var pktBytes []byte 101 | for { 102 | pktType, pktBytes, err = rs.recvPacket() 103 | if err != nil { 104 | break 105 | } 106 | pkt, err = makePacket(rxPacket{fxp(pktType), pktBytes}) 107 | if err != nil { 108 | debug("makePacket err: %v", err) 109 | rs.conn.Close() // shuts down recvPacket 110 | break 111 | } 112 | rs.pktMgr.incomingPacket(pkt) 113 | rs.pktChan <- pkt 114 | } 115 | 116 | close(rs.pktChan) // shuts down sftpServerWorkers 117 | wg.Wait() // wait for all workers to exit 118 | rs.pktMgr.close() // shuts down packetManager 119 | return err 120 | } 121 | 122 | func (rs *RequestServer) packetWorker() error { 123 | for pkt := range rs.pktChan { 124 | var rpkt responsePacket 125 | switch pkt := pkt.(type) { 126 | case *sshFxInitPacket: 127 | rpkt = sshFxVersionPacket{sftpProtocolVersion, nil} 128 | case *sshFxpClosePacket: 129 | handle := pkt.getHandle() 130 | rs.closeRequest(handle) 131 | rpkt = statusFromError(pkt, nil) 132 | case *sshFxpRealpathPacket: 133 | rpkt = cleanPath(pkt) 134 | case isOpener: 135 | handle := rs.nextRequest(requestFromPacket(pkt)) 136 | rpkt = sshFxpHandlePacket{pkt.id(), handle} 137 | case hasHandle: 138 | handle := pkt.getHandle() 139 | request, ok := rs.getRequest(handle) 140 | request.update(pkt) 141 | if !ok { 142 | rpkt = statusFromError(pkt, syscall.EBADF) 143 | } else { 144 | rpkt = rs.handle(request, pkt) 145 | } 146 | case hasPath: 147 | request := requestFromPacket(pkt) 148 | rpkt = rs.handle(request, pkt) 149 | default: 150 | return errors.Errorf("unexpected packet type %T", pkt) 151 | } 152 | 153 | err := rs.sendPacket(rpkt) 154 | if err != nil { 155 | return err 156 | } 157 | } 158 | return nil 159 | } 160 | 161 | func cleanPath(pkt *sshFxpRealpathPacket) responsePacket { 162 | path := pkt.getPath() 163 | if !filepath.IsAbs(path) { 164 | path = "/" + path 165 | } // all paths are absolute 166 | 167 | cleaned_path := filepath.Clean(path) 168 | return &sshFxpNamePacket{ 169 | ID: pkt.id(), 170 | NameAttrs: []sshFxpNameAttr{{ 171 | Name: cleaned_path, 172 | LongName: cleaned_path, 173 | Attrs: emptyFileStat, 174 | }}, 175 | } 176 | } 177 | 178 | func (rs *RequestServer) handle(request Request, pkt requestPacket) responsePacket { 179 | // fmt.Println("Request Method: ", request.Method) 180 | rpkt, err := request.handle(rs.Handlers) 181 | if err != nil { 182 | err = errorAdapter(err) 183 | rpkt = statusFromError(pkt, err) 184 | } 185 | return rpkt 186 | } 187 | 188 | // Wrap underlying connection methods to use packetManager 189 | func (rs *RequestServer) sendPacket(m encoding.BinaryMarshaler) error { 190 | if pkt, ok := m.(responsePacket); ok { 191 | rs.pktMgr.readyPacket(pkt) 192 | } else { 193 | return errors.Errorf("unexpected packet type %T", m) 194 | } 195 | return nil 196 | } 197 | 198 | func (rs *RequestServer) sendError(p ider, err error) error { 199 | return rs.sendPacket(statusFromError(p, err)) 200 | } 201 | 202 | // os.ErrNotExist should convert to ssh_FX_NO_SUCH_FILE, but is not recognized 203 | // by statusFromError. So we convert to syscall.ENOENT which it does. 204 | func errorAdapter(err error) error { 205 | if err == os.ErrNotExist { 206 | return syscall.ENOENT 207 | } 208 | return err 209 | } 210 | -------------------------------------------------------------------------------- /vendor/github.com/pkg/sftp/request-unix.go: -------------------------------------------------------------------------------- 1 | // +build !windows 2 | 3 | package sftp 4 | 5 | import ( 6 | "errors" 7 | "syscall" 8 | ) 9 | 10 | func fakeFileInfoSys() interface{} { 11 | return &syscall.Stat_t{Uid: 65534, Gid: 65534} 12 | } 13 | 14 | func testOsSys(sys interface{}) error { 15 | fstat := sys.(*FileStat) 16 | if fstat.UID != uint32(65534) { 17 | return errors.New("Uid failed to match.") 18 | } 19 | if fstat.GID != uint32(65534) { 20 | return errors.New("Gid failed to match:") 21 | } 22 | return nil 23 | } 24 | -------------------------------------------------------------------------------- /vendor/github.com/pkg/sftp/request_windows.go: -------------------------------------------------------------------------------- 1 | package sftp 2 | 3 | import "syscall" 4 | 5 | func fakeFileInfoSys() interface{} { 6 | return syscall.Win32FileAttributeData{} 7 | } 8 | 9 | func testOsSys(sys interface{}) error { 10 | return nil 11 | } 12 | -------------------------------------------------------------------------------- /vendor/github.com/pkg/sftp/server_statvfs_darwin.go: -------------------------------------------------------------------------------- 1 | package sftp 2 | 3 | import ( 4 | "syscall" 5 | ) 6 | 7 | func statvfsFromStatfst(stat *syscall.Statfs_t) (*StatVFS, error) { 8 | return &StatVFS{ 9 | Bsize: uint64(stat.Bsize), 10 | Frsize: uint64(stat.Bsize), // fragment size is a linux thing; use block size here 11 | Blocks: stat.Blocks, 12 | Bfree: stat.Bfree, 13 | Bavail: stat.Bavail, 14 | Files: stat.Files, 15 | Ffree: stat.Ffree, 16 | Favail: stat.Ffree, // not sure how to calculate Favail 17 | Fsid: uint64(uint64(stat.Fsid.Val[1])<<32 | uint64(stat.Fsid.Val[0])), // endianness? 18 | Flag: uint64(stat.Flags), // assuming POSIX? 19 | Namemax: 1024, // man 2 statfs shows: #define MAXPATHLEN 1024 20 | }, nil 21 | } 22 | -------------------------------------------------------------------------------- /vendor/github.com/pkg/sftp/server_statvfs_impl.go: -------------------------------------------------------------------------------- 1 | // +build darwin linux,!gccgo 2 | 3 | // fill in statvfs structure with OS specific values 4 | // Statfs_t is different per-kernel, and only exists on some unixes (not Solaris for instance) 5 | 6 | package sftp 7 | 8 | import ( 9 | "syscall" 10 | ) 11 | 12 | func (p sshFxpExtendedPacketStatVFS) respond(svr *Server) error { 13 | stat := &syscall.Statfs_t{} 14 | if err := syscall.Statfs(p.Path, stat); err != nil { 15 | return svr.sendPacket(statusFromError(p, err)) 16 | } 17 | 18 | retPkt, err := statvfsFromStatfst(stat) 19 | if err != nil { 20 | return svr.sendPacket(statusFromError(p, err)) 21 | } 22 | retPkt.ID = p.ID 23 | 24 | return svr.sendPacket(retPkt) 25 | } 26 | -------------------------------------------------------------------------------- /vendor/github.com/pkg/sftp/server_statvfs_linux.go: -------------------------------------------------------------------------------- 1 | // +build !gccgo,linux 2 | 3 | package sftp 4 | 5 | import ( 6 | "syscall" 7 | ) 8 | 9 | func statvfsFromStatfst(stat *syscall.Statfs_t) (*StatVFS, error) { 10 | return &StatVFS{ 11 | Bsize: uint64(stat.Bsize), 12 | Frsize: uint64(stat.Frsize), 13 | Blocks: stat.Blocks, 14 | Bfree: stat.Bfree, 15 | Bavail: stat.Bavail, 16 | Files: stat.Files, 17 | Ffree: stat.Ffree, 18 | Favail: stat.Ffree, // not sure how to calculate Favail 19 | Flag: uint64(stat.Flags), // assuming POSIX? 20 | Namemax: uint64(stat.Namelen), 21 | }, nil 22 | } 23 | -------------------------------------------------------------------------------- /vendor/github.com/pkg/sftp/server_statvfs_stubs.go: -------------------------------------------------------------------------------- 1 | // +build !darwin,!linux gccgo 2 | 3 | package sftp 4 | 5 | import ( 6 | "syscall" 7 | ) 8 | 9 | func (p sshFxpExtendedPacketStatVFS) respond(svr *Server) error { 10 | return syscall.ENOTSUP 11 | } 12 | -------------------------------------------------------------------------------- /vendor/github.com/pkg/sftp/server_stubs.go: -------------------------------------------------------------------------------- 1 | // +build !cgo,!plan9 windows android 2 | 3 | package sftp 4 | 5 | import ( 6 | "os" 7 | "path" 8 | ) 9 | 10 | func runLs(dirname string, dirent os.FileInfo) string { 11 | return path.Join(dirname, dirent.Name()) 12 | } 13 | -------------------------------------------------------------------------------- /vendor/github.com/pkg/sftp/server_unix.go: -------------------------------------------------------------------------------- 1 | // +build darwin dragonfly freebsd !android,linux netbsd openbsd solaris 2 | // +build cgo 3 | 4 | package sftp 5 | 6 | import ( 7 | "fmt" 8 | "os" 9 | "path" 10 | "syscall" 11 | "time" 12 | ) 13 | 14 | func runLsTypeWord(dirent os.FileInfo) string { 15 | // find first character, the type char 16 | // b Block special file. 17 | // c Character special file. 18 | // d Directory. 19 | // l Symbolic link. 20 | // s Socket link. 21 | // p FIFO. 22 | // - Regular file. 23 | tc := '-' 24 | mode := dirent.Mode() 25 | if (mode & os.ModeDir) != 0 { 26 | tc = 'd' 27 | } else if (mode & os.ModeDevice) != 0 { 28 | tc = 'b' 29 | if (mode & os.ModeCharDevice) != 0 { 30 | tc = 'c' 31 | } 32 | } else if (mode & os.ModeSymlink) != 0 { 33 | tc = 'l' 34 | } else if (mode & os.ModeSocket) != 0 { 35 | tc = 's' 36 | } else if (mode & os.ModeNamedPipe) != 0 { 37 | tc = 'p' 38 | } 39 | 40 | // owner 41 | orc := '-' 42 | if (mode & 0400) != 0 { 43 | orc = 'r' 44 | } 45 | owc := '-' 46 | if (mode & 0200) != 0 { 47 | owc = 'w' 48 | } 49 | oxc := '-' 50 | ox := (mode & 0100) != 0 51 | setuid := (mode & os.ModeSetuid) != 0 52 | if ox && setuid { 53 | oxc = 's' 54 | } else if setuid { 55 | oxc = 'S' 56 | } else if ox { 57 | oxc = 'x' 58 | } 59 | 60 | // group 61 | grc := '-' 62 | if (mode & 040) != 0 { 63 | grc = 'r' 64 | } 65 | gwc := '-' 66 | if (mode & 020) != 0 { 67 | gwc = 'w' 68 | } 69 | gxc := '-' 70 | gx := (mode & 010) != 0 71 | setgid := (mode & os.ModeSetgid) != 0 72 | if gx && setgid { 73 | gxc = 's' 74 | } else if setgid { 75 | gxc = 'S' 76 | } else if gx { 77 | gxc = 'x' 78 | } 79 | 80 | // all / others 81 | arc := '-' 82 | if (mode & 04) != 0 { 83 | arc = 'r' 84 | } 85 | awc := '-' 86 | if (mode & 02) != 0 { 87 | awc = 'w' 88 | } 89 | axc := '-' 90 | ax := (mode & 01) != 0 91 | sticky := (mode & os.ModeSticky) != 0 92 | if ax && sticky { 93 | axc = 't' 94 | } else if sticky { 95 | axc = 'T' 96 | } else if ax { 97 | axc = 'x' 98 | } 99 | 100 | return fmt.Sprintf("%c%c%c%c%c%c%c%c%c%c", tc, orc, owc, oxc, grc, gwc, gxc, arc, awc, axc) 101 | } 102 | 103 | func runLsStatt(dirname string, dirent os.FileInfo, statt *syscall.Stat_t) string { 104 | // example from openssh sftp server: 105 | // crw-rw-rw- 1 root wheel 0 Jul 31 20:52 ttyvd 106 | // format: 107 | // {directory / char device / etc}{rwxrwxrwx} {number of links} owner group size month day [time (this year) | year (otherwise)] name 108 | 109 | typeword := runLsTypeWord(dirent) 110 | numLinks := statt.Nlink 111 | uid := statt.Uid 112 | gid := statt.Gid 113 | username := fmt.Sprintf("%d", uid) 114 | groupname := fmt.Sprintf("%d", gid) 115 | // TODO FIXME: uid -> username, gid -> groupname lookup for ls -l format output 116 | 117 | mtime := dirent.ModTime() 118 | monthStr := mtime.Month().String()[0:3] 119 | day := mtime.Day() 120 | year := mtime.Year() 121 | now := time.Now() 122 | isOld := mtime.Before(now.Add(-time.Hour * 24 * 365 / 2)) 123 | 124 | yearOrTime := fmt.Sprintf("%02d:%02d", mtime.Hour(), mtime.Minute()) 125 | if isOld { 126 | yearOrTime = fmt.Sprintf("%d", year) 127 | } 128 | 129 | return fmt.Sprintf("%s %4d %-8s %-8s %8d %s %2d %5s %s", typeword, numLinks, username, groupname, dirent.Size(), monthStr, day, yearOrTime, dirent.Name()) 130 | } 131 | 132 | // ls -l style output for a file, which is in the 'long output' section of a readdir response packet 133 | // this is a very simple (lazy) implementation, just enough to look almost like openssh in a few basic cases 134 | func runLs(dirname string, dirent os.FileInfo) string { 135 | dsys := dirent.Sys() 136 | if dsys == nil { 137 | } else if statt, ok := dsys.(*syscall.Stat_t); !ok { 138 | } else { 139 | return runLsStatt(dirname, dirent, statt) 140 | } 141 | 142 | return path.Join(dirname, dirent.Name()) 143 | } 144 | -------------------------------------------------------------------------------- /vendor/github.com/pkg/sftp/sftp.go: -------------------------------------------------------------------------------- 1 | // Package sftp implements the SSH File Transfer Protocol as described in 2 | // https://filezilla-project.org/specs/draft-ietf-secsh-filexfer-02.txt 3 | package sftp 4 | 5 | import ( 6 | "fmt" 7 | 8 | "github.com/pkg/errors" 9 | ) 10 | 11 | const ( 12 | ssh_FXP_INIT = 1 13 | ssh_FXP_VERSION = 2 14 | ssh_FXP_OPEN = 3 15 | ssh_FXP_CLOSE = 4 16 | ssh_FXP_READ = 5 17 | ssh_FXP_WRITE = 6 18 | ssh_FXP_LSTAT = 7 19 | ssh_FXP_FSTAT = 8 20 | ssh_FXP_SETSTAT = 9 21 | ssh_FXP_FSETSTAT = 10 22 | ssh_FXP_OPENDIR = 11 23 | ssh_FXP_READDIR = 12 24 | ssh_FXP_REMOVE = 13 25 | ssh_FXP_MKDIR = 14 26 | ssh_FXP_RMDIR = 15 27 | ssh_FXP_REALPATH = 16 28 | ssh_FXP_STAT = 17 29 | ssh_FXP_RENAME = 18 30 | ssh_FXP_READLINK = 19 31 | ssh_FXP_SYMLINK = 20 32 | ssh_FXP_STATUS = 101 33 | ssh_FXP_HANDLE = 102 34 | ssh_FXP_DATA = 103 35 | ssh_FXP_NAME = 104 36 | ssh_FXP_ATTRS = 105 37 | ssh_FXP_EXTENDED = 200 38 | ssh_FXP_EXTENDED_REPLY = 201 39 | ) 40 | 41 | const ( 42 | ssh_FX_OK = 0 43 | ssh_FX_EOF = 1 44 | ssh_FX_NO_SUCH_FILE = 2 45 | ssh_FX_PERMISSION_DENIED = 3 46 | ssh_FX_FAILURE = 4 47 | ssh_FX_BAD_MESSAGE = 5 48 | ssh_FX_NO_CONNECTION = 6 49 | ssh_FX_CONNECTION_LOST = 7 50 | ssh_FX_OP_UNSUPPORTED = 8 51 | 52 | // see draft-ietf-secsh-filexfer-13 53 | // https://tools.ietf.org/html/draft-ietf-secsh-filexfer-13#section-9.1 54 | ssh_FX_INVALID_HANDLE = 9 55 | ssh_FX_NO_SUCH_PATH = 10 56 | ssh_FX_FILE_ALREADY_EXISTS = 11 57 | ssh_FX_WRITE_PROTECT = 12 58 | ssh_FX_NO_MEDIA = 13 59 | ssh_FX_NO_SPACE_ON_FILESYSTEM = 14 60 | ssh_FX_QUOTA_EXCEEDED = 15 61 | ssh_FX_UNKNOWN_PRINCIPAL = 16 62 | ssh_FX_LOCK_CONFLICT = 17 63 | ssh_FX_DIR_NOT_EMPTY = 18 64 | ssh_FX_NOT_A_DIRECTORY = 19 65 | ssh_FX_INVALID_FILENAME = 20 66 | ssh_FX_LINK_LOOP = 21 67 | ssh_FX_CANNOT_DELETE = 22 68 | ssh_FX_INVALID_PARAMETER = 23 69 | ssh_FX_FILE_IS_A_DIRECTORY = 24 70 | ssh_FX_BYTE_RANGE_LOCK_CONFLICT = 25 71 | ssh_FX_BYTE_RANGE_LOCK_REFUSED = 26 72 | ssh_FX_DELETE_PENDING = 27 73 | ssh_FX_FILE_CORRUPT = 28 74 | ssh_FX_OWNER_INVALID = 29 75 | ssh_FX_GROUP_INVALID = 30 76 | ssh_FX_NO_MATCHING_BYTE_RANGE_LOCK = 31 77 | ) 78 | 79 | const ( 80 | ssh_FXF_READ = 0x00000001 81 | ssh_FXF_WRITE = 0x00000002 82 | ssh_FXF_APPEND = 0x00000004 83 | ssh_FXF_CREAT = 0x00000008 84 | ssh_FXF_TRUNC = 0x00000010 85 | ssh_FXF_EXCL = 0x00000020 86 | ) 87 | 88 | type fxp uint8 89 | 90 | func (f fxp) String() string { 91 | switch f { 92 | case ssh_FXP_INIT: 93 | return "SSH_FXP_INIT" 94 | case ssh_FXP_VERSION: 95 | return "SSH_FXP_VERSION" 96 | case ssh_FXP_OPEN: 97 | return "SSH_FXP_OPEN" 98 | case ssh_FXP_CLOSE: 99 | return "SSH_FXP_CLOSE" 100 | case ssh_FXP_READ: 101 | return "SSH_FXP_READ" 102 | case ssh_FXP_WRITE: 103 | return "SSH_FXP_WRITE" 104 | case ssh_FXP_LSTAT: 105 | return "SSH_FXP_LSTAT" 106 | case ssh_FXP_FSTAT: 107 | return "SSH_FXP_FSTAT" 108 | case ssh_FXP_SETSTAT: 109 | return "SSH_FXP_SETSTAT" 110 | case ssh_FXP_FSETSTAT: 111 | return "SSH_FXP_FSETSTAT" 112 | case ssh_FXP_OPENDIR: 113 | return "SSH_FXP_OPENDIR" 114 | case ssh_FXP_READDIR: 115 | return "SSH_FXP_READDIR" 116 | case ssh_FXP_REMOVE: 117 | return "SSH_FXP_REMOVE" 118 | case ssh_FXP_MKDIR: 119 | return "SSH_FXP_MKDIR" 120 | case ssh_FXP_RMDIR: 121 | return "SSH_FXP_RMDIR" 122 | case ssh_FXP_REALPATH: 123 | return "SSH_FXP_REALPATH" 124 | case ssh_FXP_STAT: 125 | return "SSH_FXP_STAT" 126 | case ssh_FXP_RENAME: 127 | return "SSH_FXP_RENAME" 128 | case ssh_FXP_READLINK: 129 | return "SSH_FXP_READLINK" 130 | case ssh_FXP_SYMLINK: 131 | return "SSH_FXP_SYMLINK" 132 | case ssh_FXP_STATUS: 133 | return "SSH_FXP_STATUS" 134 | case ssh_FXP_HANDLE: 135 | return "SSH_FXP_HANDLE" 136 | case ssh_FXP_DATA: 137 | return "SSH_FXP_DATA" 138 | case ssh_FXP_NAME: 139 | return "SSH_FXP_NAME" 140 | case ssh_FXP_ATTRS: 141 | return "SSH_FXP_ATTRS" 142 | case ssh_FXP_EXTENDED: 143 | return "SSH_FXP_EXTENDED" 144 | case ssh_FXP_EXTENDED_REPLY: 145 | return "SSH_FXP_EXTENDED_REPLY" 146 | default: 147 | return "unknown" 148 | } 149 | } 150 | 151 | type fx uint8 152 | 153 | func (f fx) String() string { 154 | switch f { 155 | case ssh_FX_OK: 156 | return "SSH_FX_OK" 157 | case ssh_FX_EOF: 158 | return "SSH_FX_EOF" 159 | case ssh_FX_NO_SUCH_FILE: 160 | return "SSH_FX_NO_SUCH_FILE" 161 | case ssh_FX_PERMISSION_DENIED: 162 | return "SSH_FX_PERMISSION_DENIED" 163 | case ssh_FX_FAILURE: 164 | return "SSH_FX_FAILURE" 165 | case ssh_FX_BAD_MESSAGE: 166 | return "SSH_FX_BAD_MESSAGE" 167 | case ssh_FX_NO_CONNECTION: 168 | return "SSH_FX_NO_CONNECTION" 169 | case ssh_FX_CONNECTION_LOST: 170 | return "SSH_FX_CONNECTION_LOST" 171 | case ssh_FX_OP_UNSUPPORTED: 172 | return "SSH_FX_OP_UNSUPPORTED" 173 | default: 174 | return "unknown" 175 | } 176 | } 177 | 178 | type unexpectedPacketErr struct { 179 | want, got uint8 180 | } 181 | 182 | func (u *unexpectedPacketErr) Error() string { 183 | return fmt.Sprintf("sftp: unexpected packet: want %v, got %v", fxp(u.want), fxp(u.got)) 184 | } 185 | 186 | func unimplementedPacketErr(u uint8) error { 187 | return errors.Errorf("sftp: unimplemented packet type: got %v", fxp(u)) 188 | } 189 | 190 | type unexpectedIDErr struct{ want, got uint32 } 191 | 192 | func (u *unexpectedIDErr) Error() string { 193 | return fmt.Sprintf("sftp: unexpected id: want %v, got %v", u.want, u.got) 194 | } 195 | 196 | func unimplementedSeekWhence(whence int) error { 197 | return errors.Errorf("sftp: unimplemented seek whence %v", whence) 198 | } 199 | 200 | func unexpectedCount(want, got uint32) error { 201 | return errors.Errorf("sftp: unexpected count: want %v, got %v", want, got) 202 | } 203 | 204 | type unexpectedVersionErr struct{ want, got uint32 } 205 | 206 | func (u *unexpectedVersionErr) Error() string { 207 | return fmt.Sprintf("sftp: unexpected server version: want %v, got %v", u.want, u.got) 208 | } 209 | 210 | // A StatusError is returned when an SFTP operation fails, and provides 211 | // additional information about the failure. 212 | type StatusError struct { 213 | Code uint32 214 | msg, lang string 215 | } 216 | 217 | func (s *StatusError) Error() string { return fmt.Sprintf("sftp: %q (%v)", s.msg, fx(s.Code)) } 218 | -------------------------------------------------------------------------------- /vendor/github.com/pmezard/go-difflib/LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) 2013, Patrick Mezard 2 | All rights reserved. 3 | 4 | Redistribution and use in source and binary forms, with or without 5 | modification, are permitted provided that the following conditions are 6 | met: 7 | 8 | Redistributions of source code must retain the above copyright 9 | notice, this list of conditions and the following disclaimer. 10 | Redistributions in binary form must reproduce the above copyright 11 | notice, this list of conditions and the following disclaimer in the 12 | documentation and/or other materials provided with the distribution. 13 | The names of its contributors may not be used to endorse or promote 14 | products derived from this software without specific prior written 15 | permission. 16 | 17 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS 18 | IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED 19 | TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A 20 | PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 21 | HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 22 | SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED 23 | TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR 24 | PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF 25 | LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING 26 | NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 27 | SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 28 | -------------------------------------------------------------------------------- /vendor/github.com/stretchr/testify/LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) 2012 - 2013 Mat Ryer and Tyler Bunnell 2 | 3 | Please consider promoting this project if you find it useful. 4 | 5 | Permission is hereby granted, free of charge, to any person 6 | obtaining a copy of this software and associated documentation 7 | files (the "Software"), to deal in the Software without restriction, 8 | including without limitation the rights to use, copy, modify, merge, 9 | publish, distribute, sublicense, and/or sell copies of the Software, 10 | and to permit persons to whom the Software is furnished to do so, 11 | subject to the following conditions: 12 | 13 | The above copyright notice and this permission notice shall be included 14 | in all copies or substantial portions of the Software. 15 | 16 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 17 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES 18 | OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. 19 | IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, 20 | DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT 21 | OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE 22 | OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 23 | -------------------------------------------------------------------------------- /vendor/github.com/stretchr/testify/assert/assertion_forward.go.tmpl: -------------------------------------------------------------------------------- 1 | {{.CommentWithoutT "a"}} 2 | func (a *Assertions) {{.DocInfo.Name}}({{.Params}}) bool { 3 | return {{.DocInfo.Name}}(a.t, {{.ForwardedParams}}) 4 | } 5 | -------------------------------------------------------------------------------- /vendor/github.com/stretchr/testify/assert/doc.go: -------------------------------------------------------------------------------- 1 | // Package assert provides a set of comprehensive testing tools for use with the normal Go testing system. 2 | // 3 | // Example Usage 4 | // 5 | // The following is a complete example using assert in a standard test function: 6 | // import ( 7 | // "testing" 8 | // "github.com/stretchr/testify/assert" 9 | // ) 10 | // 11 | // func TestSomething(t *testing.T) { 12 | // 13 | // var a string = "Hello" 14 | // var b string = "Hello" 15 | // 16 | // assert.Equal(t, a, b, "The two words should be the same.") 17 | // 18 | // } 19 | // 20 | // if you assert many times, use the format below: 21 | // 22 | // import ( 23 | // "testing" 24 | // "github.com/stretchr/testify/assert" 25 | // ) 26 | // 27 | // func TestSomething(t *testing.T) { 28 | // assert := assert.New(t) 29 | // 30 | // var a string = "Hello" 31 | // var b string = "Hello" 32 | // 33 | // assert.Equal(a, b, "The two words should be the same.") 34 | // } 35 | // 36 | // Assertions 37 | // 38 | // Assertions allow you to easily write test code, and are global funcs in the `assert` package. 39 | // All assertion functions take, as the first argument, the `*testing.T` object provided by the 40 | // testing framework. This allows the assertion funcs to write the failings and other details to 41 | // the correct place. 42 | // 43 | // Every assertion function also takes an optional string message as the final argument, 44 | // allowing custom error messages to be appended to the message the assertion method outputs. 45 | package assert 46 | -------------------------------------------------------------------------------- /vendor/github.com/stretchr/testify/assert/errors.go: -------------------------------------------------------------------------------- 1 | package assert 2 | 3 | import ( 4 | "errors" 5 | ) 6 | 7 | // AnError is an error instance useful for testing. If the code does not care 8 | // about error specifics, and only needs to return the error for example, this 9 | // error should be used to make the test code more readable. 10 | var AnError = errors.New("assert.AnError general error for testing") 11 | -------------------------------------------------------------------------------- /vendor/github.com/stretchr/testify/assert/forward_assertions.go: -------------------------------------------------------------------------------- 1 | package assert 2 | 3 | // Assertions provides assertion methods around the 4 | // TestingT interface. 5 | type Assertions struct { 6 | t TestingT 7 | } 8 | 9 | // New makes a new Assertions object for the specified TestingT. 10 | func New(t TestingT) *Assertions { 11 | return &Assertions{ 12 | t: t, 13 | } 14 | } 15 | 16 | //go:generate go run ../_codegen/main.go -output-package=assert -template=assertion_forward.go.tmpl 17 | -------------------------------------------------------------------------------- /vendor/github.com/stretchr/testify/assert/http_assertions.go: -------------------------------------------------------------------------------- 1 | package assert 2 | 3 | import ( 4 | "fmt" 5 | "net/http" 6 | "net/http/httptest" 7 | "net/url" 8 | "strings" 9 | ) 10 | 11 | // httpCode is a helper that returns HTTP code of the response. It returns -1 12 | // if building a new request fails. 13 | func httpCode(handler http.HandlerFunc, method, url string, values url.Values) int { 14 | w := httptest.NewRecorder() 15 | req, err := http.NewRequest(method, url+"?"+values.Encode(), nil) 16 | if err != nil { 17 | return -1 18 | } 19 | handler(w, req) 20 | return w.Code 21 | } 22 | 23 | // HTTPSuccess asserts that a specified handler returns a success status code. 24 | // 25 | // assert.HTTPSuccess(t, myHandler, "POST", "http://www.google.com", nil) 26 | // 27 | // Returns whether the assertion was successful (true) or not (false). 28 | func HTTPSuccess(t TestingT, handler http.HandlerFunc, method, url string, values url.Values) bool { 29 | code := httpCode(handler, method, url, values) 30 | if code == -1 { 31 | return false 32 | } 33 | return code >= http.StatusOK && code <= http.StatusPartialContent 34 | } 35 | 36 | // HTTPRedirect asserts that a specified handler returns a redirect status code. 37 | // 38 | // assert.HTTPRedirect(t, myHandler, "GET", "/a/b/c", url.Values{"a": []string{"b", "c"}} 39 | // 40 | // Returns whether the assertion was successful (true) or not (false). 41 | func HTTPRedirect(t TestingT, handler http.HandlerFunc, method, url string, values url.Values) bool { 42 | code := httpCode(handler, method, url, values) 43 | if code == -1 { 44 | return false 45 | } 46 | return code >= http.StatusMultipleChoices && code <= http.StatusTemporaryRedirect 47 | } 48 | 49 | // HTTPError asserts that a specified handler returns an error status code. 50 | // 51 | // assert.HTTPError(t, myHandler, "POST", "/a/b/c", url.Values{"a": []string{"b", "c"}} 52 | // 53 | // Returns whether the assertion was successful (true) or not (false). 54 | func HTTPError(t TestingT, handler http.HandlerFunc, method, url string, values url.Values) bool { 55 | code := httpCode(handler, method, url, values) 56 | if code == -1 { 57 | return false 58 | } 59 | return code >= http.StatusBadRequest 60 | } 61 | 62 | // HTTPBody is a helper that returns HTTP body of the response. It returns 63 | // empty string if building a new request fails. 64 | func HTTPBody(handler http.HandlerFunc, method, url string, values url.Values) string { 65 | w := httptest.NewRecorder() 66 | req, err := http.NewRequest(method, url+"?"+values.Encode(), nil) 67 | if err != nil { 68 | return "" 69 | } 70 | handler(w, req) 71 | return w.Body.String() 72 | } 73 | 74 | // HTTPBodyContains asserts that a specified handler returns a 75 | // body that contains a string. 76 | // 77 | // assert.HTTPBodyContains(t, myHandler, "www.google.com", nil, "I'm Feeling Lucky") 78 | // 79 | // Returns whether the assertion was successful (true) or not (false). 80 | func HTTPBodyContains(t TestingT, handler http.HandlerFunc, method, url string, values url.Values, str interface{}) bool { 81 | body := HTTPBody(handler, method, url, values) 82 | 83 | contains := strings.Contains(body, fmt.Sprint(str)) 84 | if !contains { 85 | Fail(t, fmt.Sprintf("Expected response body for \"%s\" to contain \"%s\" but found \"%s\"", url+"?"+values.Encode(), str, body)) 86 | } 87 | 88 | return contains 89 | } 90 | 91 | // HTTPBodyNotContains asserts that a specified handler returns a 92 | // body that does not contain a string. 93 | // 94 | // assert.HTTPBodyNotContains(t, myHandler, "www.google.com", nil, "I'm Feeling Lucky") 95 | // 96 | // Returns whether the assertion was successful (true) or not (false). 97 | func HTTPBodyNotContains(t TestingT, handler http.HandlerFunc, method, url string, values url.Values, str interface{}) bool { 98 | body := HTTPBody(handler, method, url, values) 99 | 100 | contains := strings.Contains(body, fmt.Sprint(str)) 101 | if contains { 102 | Fail(t, fmt.Sprintf("Expected response body for \"%s\" to NOT contain \"%s\" but found \"%s\"", url+"?"+values.Encode(), str, body)) 103 | } 104 | 105 | return !contains 106 | } 107 | -------------------------------------------------------------------------------- /vendor/github.com/urfave/cli/LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2016 Jeremy Saenz & Contributors 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /vendor/github.com/urfave/cli/appveyor.yml: -------------------------------------------------------------------------------- 1 | version: "{build}" 2 | 3 | os: Windows Server 2012 R2 4 | 5 | clone_folder: c:\gopath\src\github.com\urfave\cli 6 | 7 | environment: 8 | GOPATH: C:\gopath 9 | GOVERSION: 1.6 10 | PYTHON: C:\Python27-x64 11 | PYTHON_VERSION: 2.7.x 12 | PYTHON_ARCH: 64 13 | 14 | install: 15 | - set PATH=%GOPATH%\bin;C:\go\bin;%PATH% 16 | - go version 17 | - go env 18 | - go get github.com/urfave/gfmrun/... 19 | - go get -v -t ./... 20 | 21 | build_script: 22 | - python runtests vet 23 | - python runtests test 24 | - python runtests gfmrun 25 | -------------------------------------------------------------------------------- /vendor/github.com/urfave/cli/category.go: -------------------------------------------------------------------------------- 1 | package cli 2 | 3 | // CommandCategories is a slice of *CommandCategory. 4 | type CommandCategories []*CommandCategory 5 | 6 | // CommandCategory is a category containing commands. 7 | type CommandCategory struct { 8 | Name string 9 | Commands Commands 10 | } 11 | 12 | func (c CommandCategories) Less(i, j int) bool { 13 | return c[i].Name < c[j].Name 14 | } 15 | 16 | func (c CommandCategories) Len() int { 17 | return len(c) 18 | } 19 | 20 | func (c CommandCategories) Swap(i, j int) { 21 | c[i], c[j] = c[j], c[i] 22 | } 23 | 24 | // AddCommand adds a command to a category. 25 | func (c CommandCategories) AddCommand(category string, command Command) CommandCategories { 26 | for _, commandCategory := range c { 27 | if commandCategory.Name == category { 28 | commandCategory.Commands = append(commandCategory.Commands, command) 29 | return c 30 | } 31 | } 32 | return append(c, &CommandCategory{Name: category, Commands: []Command{command}}) 33 | } 34 | 35 | // VisibleCommands returns a slice of the Commands with Hidden=false 36 | func (c *CommandCategory) VisibleCommands() []Command { 37 | ret := []Command{} 38 | for _, command := range c.Commands { 39 | if !command.Hidden { 40 | ret = append(ret, command) 41 | } 42 | } 43 | return ret 44 | } 45 | -------------------------------------------------------------------------------- /vendor/github.com/urfave/cli/cli.go: -------------------------------------------------------------------------------- 1 | // Package cli provides a minimal framework for creating and organizing command line 2 | // Go applications. cli is designed to be easy to understand and write, the most simple 3 | // cli application can be written as follows: 4 | // func main() { 5 | // cli.NewApp().Run(os.Args) 6 | // } 7 | // 8 | // Of course this application does not do much, so let's make this an actual application: 9 | // func main() { 10 | // app := cli.NewApp() 11 | // app.Name = "greet" 12 | // app.Usage = "say a greeting" 13 | // app.Action = func(c *cli.Context) error { 14 | // println("Greetings") 15 | // } 16 | // 17 | // app.Run(os.Args) 18 | // } 19 | package cli 20 | 21 | //go:generate python ./generate-flag-types cli -i flag-types.json -o flag_generated.go 22 | -------------------------------------------------------------------------------- /vendor/github.com/urfave/cli/context.go: -------------------------------------------------------------------------------- 1 | package cli 2 | 3 | import ( 4 | "errors" 5 | "flag" 6 | "reflect" 7 | "strings" 8 | "syscall" 9 | ) 10 | 11 | // Context is a type that is passed through to 12 | // each Handler action in a cli application. Context 13 | // can be used to retrieve context-specific Args and 14 | // parsed command-line options. 15 | type Context struct { 16 | App *App 17 | Command Command 18 | shellComplete bool 19 | flagSet *flag.FlagSet 20 | setFlags map[string]bool 21 | parentContext *Context 22 | } 23 | 24 | // NewContext creates a new context. For use in when invoking an App or Command action. 25 | func NewContext(app *App, set *flag.FlagSet, parentCtx *Context) *Context { 26 | c := &Context{App: app, flagSet: set, parentContext: parentCtx} 27 | 28 | if parentCtx != nil { 29 | c.shellComplete = parentCtx.shellComplete 30 | } 31 | 32 | return c 33 | } 34 | 35 | // NumFlags returns the number of flags set 36 | func (c *Context) NumFlags() int { 37 | return c.flagSet.NFlag() 38 | } 39 | 40 | // Set sets a context flag to a value. 41 | func (c *Context) Set(name, value string) error { 42 | return c.flagSet.Set(name, value) 43 | } 44 | 45 | // GlobalSet sets a context flag to a value on the global flagset 46 | func (c *Context) GlobalSet(name, value string) error { 47 | return globalContext(c).flagSet.Set(name, value) 48 | } 49 | 50 | // IsSet determines if the flag was actually set 51 | func (c *Context) IsSet(name string) bool { 52 | if c.setFlags == nil { 53 | c.setFlags = make(map[string]bool) 54 | 55 | c.flagSet.Visit(func(f *flag.Flag) { 56 | c.setFlags[f.Name] = true 57 | }) 58 | 59 | c.flagSet.VisitAll(func(f *flag.Flag) { 60 | if _, ok := c.setFlags[f.Name]; ok { 61 | return 62 | } 63 | c.setFlags[f.Name] = false 64 | }) 65 | 66 | // XXX hack to support IsSet for flags with EnvVar 67 | // 68 | // There isn't an easy way to do this with the current implementation since 69 | // whether a flag was set via an environment variable is very difficult to 70 | // determine here. Instead, we intend to introduce a backwards incompatible 71 | // change in version 2 to add `IsSet` to the Flag interface to push the 72 | // responsibility closer to where the information required to determine 73 | // whether a flag is set by non-standard means such as environment 74 | // variables is avaliable. 75 | // 76 | // See https://github.com/urfave/cli/issues/294 for additional discussion 77 | flags := c.Command.Flags 78 | if c.Command.Name == "" { // cannot == Command{} since it contains slice types 79 | if c.App != nil { 80 | flags = c.App.Flags 81 | } 82 | } 83 | for _, f := range flags { 84 | eachName(f.GetName(), func(name string) { 85 | if isSet, ok := c.setFlags[name]; isSet || !ok { 86 | return 87 | } 88 | 89 | val := reflect.ValueOf(f) 90 | if val.Kind() == reflect.Ptr { 91 | val = val.Elem() 92 | } 93 | 94 | envVarValue := val.FieldByName("EnvVar") 95 | if !envVarValue.IsValid() { 96 | return 97 | } 98 | 99 | eachName(envVarValue.String(), func(envVar string) { 100 | envVar = strings.TrimSpace(envVar) 101 | if _, ok := syscall.Getenv(envVar); ok { 102 | c.setFlags[name] = true 103 | return 104 | } 105 | }) 106 | }) 107 | } 108 | } 109 | 110 | return c.setFlags[name] 111 | } 112 | 113 | // GlobalIsSet determines if the global flag was actually set 114 | func (c *Context) GlobalIsSet(name string) bool { 115 | ctx := c 116 | if ctx.parentContext != nil { 117 | ctx = ctx.parentContext 118 | } 119 | 120 | for ; ctx != nil; ctx = ctx.parentContext { 121 | if ctx.IsSet(name) { 122 | return true 123 | } 124 | } 125 | return false 126 | } 127 | 128 | // FlagNames returns a slice of flag names used in this context. 129 | func (c *Context) FlagNames() (names []string) { 130 | for _, flag := range c.Command.Flags { 131 | name := strings.Split(flag.GetName(), ",")[0] 132 | if name == "help" { 133 | continue 134 | } 135 | names = append(names, name) 136 | } 137 | return 138 | } 139 | 140 | // GlobalFlagNames returns a slice of global flag names used by the app. 141 | func (c *Context) GlobalFlagNames() (names []string) { 142 | for _, flag := range c.App.Flags { 143 | name := strings.Split(flag.GetName(), ",")[0] 144 | if name == "help" || name == "version" { 145 | continue 146 | } 147 | names = append(names, name) 148 | } 149 | return 150 | } 151 | 152 | // Parent returns the parent context, if any 153 | func (c *Context) Parent() *Context { 154 | return c.parentContext 155 | } 156 | 157 | // value returns the value of the flag coressponding to `name` 158 | func (c *Context) value(name string) interface{} { 159 | return c.flagSet.Lookup(name).Value.(flag.Getter).Get() 160 | } 161 | 162 | // Args contains apps console arguments 163 | type Args []string 164 | 165 | // Args returns the command line arguments associated with the context. 166 | func (c *Context) Args() Args { 167 | args := Args(c.flagSet.Args()) 168 | return args 169 | } 170 | 171 | // NArg returns the number of the command line arguments. 172 | func (c *Context) NArg() int { 173 | return len(c.Args()) 174 | } 175 | 176 | // Get returns the nth argument, or else a blank string 177 | func (a Args) Get(n int) string { 178 | if len(a) > n { 179 | return a[n] 180 | } 181 | return "" 182 | } 183 | 184 | // First returns the first argument, or else a blank string 185 | func (a Args) First() string { 186 | return a.Get(0) 187 | } 188 | 189 | // Tail returns the rest of the arguments (not the first one) 190 | // or else an empty string slice 191 | func (a Args) Tail() []string { 192 | if len(a) >= 2 { 193 | return []string(a)[1:] 194 | } 195 | return []string{} 196 | } 197 | 198 | // Present checks if there are any arguments present 199 | func (a Args) Present() bool { 200 | return len(a) != 0 201 | } 202 | 203 | // Swap swaps arguments at the given indexes 204 | func (a Args) Swap(from, to int) error { 205 | if from >= len(a) || to >= len(a) { 206 | return errors.New("index out of range") 207 | } 208 | a[from], a[to] = a[to], a[from] 209 | return nil 210 | } 211 | 212 | func globalContext(ctx *Context) *Context { 213 | if ctx == nil { 214 | return nil 215 | } 216 | 217 | for { 218 | if ctx.parentContext == nil { 219 | return ctx 220 | } 221 | ctx = ctx.parentContext 222 | } 223 | } 224 | 225 | func lookupGlobalFlagSet(name string, ctx *Context) *flag.FlagSet { 226 | if ctx.parentContext != nil { 227 | ctx = ctx.parentContext 228 | } 229 | for ; ctx != nil; ctx = ctx.parentContext { 230 | if f := ctx.flagSet.Lookup(name); f != nil { 231 | return ctx.flagSet 232 | } 233 | } 234 | return nil 235 | } 236 | 237 | func copyFlag(name string, ff *flag.Flag, set *flag.FlagSet) { 238 | switch ff.Value.(type) { 239 | case *StringSlice: 240 | default: 241 | set.Set(name, ff.Value.String()) 242 | } 243 | } 244 | 245 | func normalizeFlags(flags []Flag, set *flag.FlagSet) error { 246 | visited := make(map[string]bool) 247 | set.Visit(func(f *flag.Flag) { 248 | visited[f.Name] = true 249 | }) 250 | for _, f := range flags { 251 | parts := strings.Split(f.GetName(), ",") 252 | if len(parts) == 1 { 253 | continue 254 | } 255 | var ff *flag.Flag 256 | for _, name := range parts { 257 | name = strings.Trim(name, " ") 258 | if visited[name] { 259 | if ff != nil { 260 | return errors.New("Cannot use two forms of the same flag: " + name + " " + ff.Name) 261 | } 262 | ff = set.Lookup(name) 263 | } 264 | } 265 | if ff == nil { 266 | continue 267 | } 268 | for _, name := range parts { 269 | name = strings.Trim(name, " ") 270 | if !visited[name] { 271 | copyFlag(name, ff, set) 272 | } 273 | } 274 | } 275 | return nil 276 | } 277 | -------------------------------------------------------------------------------- /vendor/github.com/urfave/cli/errors.go: -------------------------------------------------------------------------------- 1 | package cli 2 | 3 | import ( 4 | "fmt" 5 | "io" 6 | "os" 7 | "strings" 8 | ) 9 | 10 | // OsExiter is the function used when the app exits. If not set defaults to os.Exit. 11 | var OsExiter = os.Exit 12 | 13 | // ErrWriter is used to write errors to the user. This can be anything 14 | // implementing the io.Writer interface and defaults to os.Stderr. 15 | var ErrWriter io.Writer = os.Stderr 16 | 17 | // MultiError is an error that wraps multiple errors. 18 | type MultiError struct { 19 | Errors []error 20 | } 21 | 22 | // NewMultiError creates a new MultiError. Pass in one or more errors. 23 | func NewMultiError(err ...error) MultiError { 24 | return MultiError{Errors: err} 25 | } 26 | 27 | // Error implements the error interface. 28 | func (m MultiError) Error() string { 29 | errs := make([]string, len(m.Errors)) 30 | for i, err := range m.Errors { 31 | errs[i] = err.Error() 32 | } 33 | 34 | return strings.Join(errs, "\n") 35 | } 36 | 37 | type ErrorFormatter interface { 38 | Format(s fmt.State, verb rune) 39 | } 40 | 41 | // ExitCoder is the interface checked by `App` and `Command` for a custom exit 42 | // code 43 | type ExitCoder interface { 44 | error 45 | ExitCode() int 46 | } 47 | 48 | // ExitError fulfills both the builtin `error` interface and `ExitCoder` 49 | type ExitError struct { 50 | exitCode int 51 | message interface{} 52 | } 53 | 54 | // NewExitError makes a new *ExitError 55 | func NewExitError(message interface{}, exitCode int) *ExitError { 56 | return &ExitError{ 57 | exitCode: exitCode, 58 | message: message, 59 | } 60 | } 61 | 62 | // Error returns the string message, fulfilling the interface required by 63 | // `error` 64 | func (ee *ExitError) Error() string { 65 | return fmt.Sprintf("%v", ee.message) 66 | } 67 | 68 | // ExitCode returns the exit code, fulfilling the interface required by 69 | // `ExitCoder` 70 | func (ee *ExitError) ExitCode() int { 71 | return ee.exitCode 72 | } 73 | 74 | // HandleExitCoder checks if the error fulfills the ExitCoder interface, and if 75 | // so prints the error to stderr (if it is non-empty) and calls OsExiter with the 76 | // given exit code. If the given error is a MultiError, then this func is 77 | // called on all members of the Errors slice and calls OsExiter with the last exit code. 78 | func HandleExitCoder(err error) { 79 | if err == nil { 80 | return 81 | } 82 | 83 | if exitErr, ok := err.(ExitCoder); ok { 84 | if err.Error() != "" { 85 | if _, ok := exitErr.(ErrorFormatter); ok { 86 | fmt.Fprintf(ErrWriter, "%+v\n", err) 87 | } else { 88 | fmt.Fprintln(ErrWriter, err) 89 | } 90 | } 91 | OsExiter(exitErr.ExitCode()) 92 | return 93 | } 94 | 95 | if multiErr, ok := err.(MultiError); ok { 96 | code := handleMultiError(multiErr) 97 | OsExiter(code) 98 | return 99 | } 100 | } 101 | 102 | func handleMultiError(multiErr MultiError) int { 103 | code := 1 104 | for _, merr := range multiErr.Errors { 105 | if multiErr2, ok := merr.(MultiError); ok { 106 | code = handleMultiError(multiErr2) 107 | } else { 108 | fmt.Fprintln(ErrWriter, merr) 109 | if exitErr, ok := merr.(ExitCoder); ok { 110 | code = exitErr.ExitCode() 111 | } 112 | } 113 | } 114 | return code 115 | } 116 | -------------------------------------------------------------------------------- /vendor/github.com/urfave/cli/flag-types.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "name": "Bool", 4 | "type": "bool", 5 | "value": false, 6 | "context_default": "false", 7 | "parser": "strconv.ParseBool(f.Value.String())" 8 | }, 9 | { 10 | "name": "BoolT", 11 | "type": "bool", 12 | "value": false, 13 | "doctail": " that is true by default", 14 | "context_default": "false", 15 | "parser": "strconv.ParseBool(f.Value.String())" 16 | }, 17 | { 18 | "name": "Duration", 19 | "type": "time.Duration", 20 | "doctail": " (see https://golang.org/pkg/time/#ParseDuration)", 21 | "context_default": "0", 22 | "parser": "time.ParseDuration(f.Value.String())" 23 | }, 24 | { 25 | "name": "Float64", 26 | "type": "float64", 27 | "context_default": "0", 28 | "parser": "strconv.ParseFloat(f.Value.String(), 64)" 29 | }, 30 | { 31 | "name": "Generic", 32 | "type": "Generic", 33 | "dest": false, 34 | "context_default": "nil", 35 | "context_type": "interface{}" 36 | }, 37 | { 38 | "name": "Int64", 39 | "type": "int64", 40 | "context_default": "0", 41 | "parser": "strconv.ParseInt(f.Value.String(), 0, 64)" 42 | }, 43 | { 44 | "name": "Int", 45 | "type": "int", 46 | "context_default": "0", 47 | "parser": "strconv.ParseInt(f.Value.String(), 0, 64)", 48 | "parser_cast": "int(parsed)" 49 | }, 50 | { 51 | "name": "IntSlice", 52 | "type": "*IntSlice", 53 | "dest": false, 54 | "context_default": "nil", 55 | "context_type": "[]int", 56 | "parser": "(f.Value.(*IntSlice)).Value(), error(nil)" 57 | }, 58 | { 59 | "name": "Int64Slice", 60 | "type": "*Int64Slice", 61 | "dest": false, 62 | "context_default": "nil", 63 | "context_type": "[]int64", 64 | "parser": "(f.Value.(*Int64Slice)).Value(), error(nil)" 65 | }, 66 | { 67 | "name": "String", 68 | "type": "string", 69 | "context_default": "\"\"", 70 | "parser": "f.Value.String(), error(nil)" 71 | }, 72 | { 73 | "name": "StringSlice", 74 | "type": "*StringSlice", 75 | "dest": false, 76 | "context_default": "nil", 77 | "context_type": "[]string", 78 | "parser": "(f.Value.(*StringSlice)).Value(), error(nil)" 79 | }, 80 | { 81 | "name": "Uint64", 82 | "type": "uint64", 83 | "context_default": "0", 84 | "parser": "strconv.ParseUint(f.Value.String(), 0, 64)" 85 | }, 86 | { 87 | "name": "Uint", 88 | "type": "uint", 89 | "context_default": "0", 90 | "parser": "strconv.ParseUint(f.Value.String(), 0, 64)", 91 | "parser_cast": "uint(parsed)" 92 | } 93 | ] 94 | -------------------------------------------------------------------------------- /vendor/github.com/urfave/cli/funcs.go: -------------------------------------------------------------------------------- 1 | package cli 2 | 3 | // BashCompleteFunc is an action to execute when the bash-completion flag is set 4 | type BashCompleteFunc func(*Context) 5 | 6 | // BeforeFunc is an action to execute before any subcommands are run, but after 7 | // the context is ready if a non-nil error is returned, no subcommands are run 8 | type BeforeFunc func(*Context) error 9 | 10 | // AfterFunc is an action to execute after any subcommands are run, but after the 11 | // subcommand has finished it is run even if Action() panics 12 | type AfterFunc func(*Context) error 13 | 14 | // ActionFunc is the action to execute when no subcommands are specified 15 | type ActionFunc func(*Context) error 16 | 17 | // CommandNotFoundFunc is executed if the proper command cannot be found 18 | type CommandNotFoundFunc func(*Context, string) 19 | 20 | // OnUsageErrorFunc is executed if an usage error occurs. This is useful for displaying 21 | // customized usage error messages. This function is able to replace the 22 | // original error messages. If this function is not set, the "Incorrect usage" 23 | // is displayed and the execution is interrupted. 24 | type OnUsageErrorFunc func(context *Context, err error, isSubcommand bool) error 25 | 26 | // FlagStringFunc is used by the help generation to display a flag, which is 27 | // expected to be a single line. 28 | type FlagStringFunc func(Flag) string 29 | -------------------------------------------------------------------------------- /vendor/github.com/urfave/cli/runtests: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | from __future__ import print_function 3 | 4 | import argparse 5 | import os 6 | import sys 7 | import tempfile 8 | 9 | from subprocess import check_call, check_output 10 | 11 | 12 | PACKAGE_NAME = os.environ.get( 13 | 'CLI_PACKAGE_NAME', 'github.com/urfave/cli' 14 | ) 15 | 16 | 17 | def main(sysargs=sys.argv[:]): 18 | targets = { 19 | 'vet': _vet, 20 | 'test': _test, 21 | 'gfmrun': _gfmrun, 22 | 'toc': _toc, 23 | 'gen': _gen, 24 | } 25 | 26 | parser = argparse.ArgumentParser() 27 | parser.add_argument( 28 | 'target', nargs='?', choices=tuple(targets.keys()), default='test' 29 | ) 30 | args = parser.parse_args(sysargs[1:]) 31 | 32 | targets[args.target]() 33 | return 0 34 | 35 | 36 | def _test(): 37 | if check_output('go version'.split()).split()[2] < 'go1.2': 38 | _run('go test -v .') 39 | return 40 | 41 | coverprofiles = [] 42 | for subpackage in ['', 'altsrc']: 43 | coverprofile = 'cli.coverprofile' 44 | if subpackage != '': 45 | coverprofile = '{}.coverprofile'.format(subpackage) 46 | 47 | coverprofiles.append(coverprofile) 48 | 49 | _run('go test -v'.split() + [ 50 | '-coverprofile={}'.format(coverprofile), 51 | ('{}/{}'.format(PACKAGE_NAME, subpackage)).rstrip('/') 52 | ]) 53 | 54 | combined_name = _combine_coverprofiles(coverprofiles) 55 | _run('go tool cover -func={}'.format(combined_name)) 56 | os.remove(combined_name) 57 | 58 | 59 | def _gfmrun(): 60 | go_version = check_output('go version'.split()).split()[2] 61 | if go_version < 'go1.3': 62 | print('runtests: skip on {}'.format(go_version), file=sys.stderr) 63 | return 64 | _run(['gfmrun', '-c', str(_gfmrun_count()), '-s', 'README.md']) 65 | 66 | 67 | def _vet(): 68 | _run('go vet ./...') 69 | 70 | 71 | def _toc(): 72 | _run('node_modules/.bin/markdown-toc -i README.md') 73 | _run('git diff --exit-code') 74 | 75 | 76 | def _gen(): 77 | go_version = check_output('go version'.split()).split()[2] 78 | if go_version < 'go1.5': 79 | print('runtests: skip on {}'.format(go_version), file=sys.stderr) 80 | return 81 | 82 | _run('go generate ./...') 83 | _run('git diff --exit-code') 84 | 85 | 86 | def _run(command): 87 | if hasattr(command, 'split'): 88 | command = command.split() 89 | print('runtests: {}'.format(' '.join(command)), file=sys.stderr) 90 | check_call(command) 91 | 92 | 93 | def _gfmrun_count(): 94 | with open('README.md') as infile: 95 | lines = infile.read().splitlines() 96 | return len(filter(_is_go_runnable, lines)) 97 | 98 | 99 | def _is_go_runnable(line): 100 | return line.startswith('package main') 101 | 102 | 103 | def _combine_coverprofiles(coverprofiles): 104 | combined = tempfile.NamedTemporaryFile( 105 | suffix='.coverprofile', delete=False 106 | ) 107 | combined.write('mode: set\n') 108 | 109 | for coverprofile in coverprofiles: 110 | with open(coverprofile, 'r') as infile: 111 | for line in infile.readlines(): 112 | if not line.startswith('mode: '): 113 | combined.write(line) 114 | 115 | combined.flush() 116 | name = combined.name 117 | combined.close() 118 | return name 119 | 120 | 121 | if __name__ == '__main__': 122 | sys.exit(main()) 123 | -------------------------------------------------------------------------------- /vendor/golang.org/x/crypto/LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) 2009 The Go Authors. All rights reserved. 2 | 3 | Redistribution and use in source and binary forms, with or without 4 | modification, are permitted provided that the following conditions are 5 | met: 6 | 7 | * Redistributions of source code must retain the above copyright 8 | notice, this list of conditions and the following disclaimer. 9 | * Redistributions in binary form must reproduce the above 10 | copyright notice, this list of conditions and the following disclaimer 11 | in the documentation and/or other materials provided with the 12 | distribution. 13 | * Neither the name of Google Inc. nor the names of its 14 | contributors may be used to endorse or promote products derived from 15 | this software without specific prior written permission. 16 | 17 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 18 | "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 19 | LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 20 | A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 21 | OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 22 | SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 23 | LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 24 | DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 25 | THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 26 | (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 27 | OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 28 | -------------------------------------------------------------------------------- /vendor/golang.org/x/crypto/PATENTS: -------------------------------------------------------------------------------- 1 | Additional IP Rights Grant (Patents) 2 | 3 | "This implementation" means the copyrightable works distributed by 4 | Google as part of the Go project. 5 | 6 | Google hereby grants to You a perpetual, worldwide, non-exclusive, 7 | no-charge, royalty-free, irrevocable (except as stated in this section) 8 | patent license to make, have made, use, offer to sell, sell, import, 9 | transfer and otherwise run, modify and propagate the contents of this 10 | implementation of Go, where such license applies only to those patent 11 | claims, both currently owned or controlled by Google and acquired in 12 | the future, licensable by Google that are necessarily infringed by this 13 | implementation of Go. This grant does not include claims that would be 14 | infringed only as a consequence of further modification of this 15 | implementation. If you or your agent or exclusive licensee institute or 16 | order or agree to the institution of patent litigation against any 17 | entity (including a cross-claim or counterclaim in a lawsuit) alleging 18 | that this implementation of Go or any code incorporated within this 19 | implementation of Go constitutes direct or contributory patent 20 | infringement, or inducement of patent infringement, then any patent 21 | rights granted to you under this License for this implementation of Go 22 | shall terminate as of the date such litigation is filed. 23 | -------------------------------------------------------------------------------- /vendor/golang.org/x/crypto/curve25519/const_amd64.h: -------------------------------------------------------------------------------- 1 | // Copyright 2012 The Go Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | // This code was translated into a form compatible with 6a from the public 6 | // domain sources in SUPERCOP: http://bench.cr.yp.to/supercop.html 7 | 8 | #define REDMASK51 0x0007FFFFFFFFFFFF 9 | -------------------------------------------------------------------------------- /vendor/golang.org/x/crypto/curve25519/const_amd64.s: -------------------------------------------------------------------------------- 1 | // Copyright 2012 The Go Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | // This code was translated into a form compatible with 6a from the public 6 | // domain sources in SUPERCOP: http://bench.cr.yp.to/supercop.html 7 | 8 | // +build amd64,!gccgo,!appengine 9 | 10 | // These constants cannot be encoded in non-MOVQ immediates. 11 | // We access them directly from memory instead. 12 | 13 | DATA ·_121666_213(SB)/8, $996687872 14 | GLOBL ·_121666_213(SB), 8, $8 15 | 16 | DATA ·_2P0(SB)/8, $0xFFFFFFFFFFFDA 17 | GLOBL ·_2P0(SB), 8, $8 18 | 19 | DATA ·_2P1234(SB)/8, $0xFFFFFFFFFFFFE 20 | GLOBL ·_2P1234(SB), 8, $8 21 | -------------------------------------------------------------------------------- /vendor/golang.org/x/crypto/curve25519/cswap_amd64.s: -------------------------------------------------------------------------------- 1 | // Copyright 2012 The Go Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | // This code was translated into a form compatible with 6a from the public 6 | // domain sources in SUPERCOP: http://bench.cr.yp.to/supercop.html 7 | 8 | // +build amd64,!gccgo,!appengine 9 | 10 | // func cswap(inout *[5]uint64, v uint64) 11 | TEXT ·cswap(SB),7,$0 12 | MOVQ inout+0(FP),DI 13 | MOVQ v+8(FP),SI 14 | 15 | CMPQ SI,$1 16 | MOVQ 0(DI),SI 17 | MOVQ 80(DI),DX 18 | MOVQ 8(DI),CX 19 | MOVQ 88(DI),R8 20 | MOVQ SI,R9 21 | CMOVQEQ DX,SI 22 | CMOVQEQ R9,DX 23 | MOVQ CX,R9 24 | CMOVQEQ R8,CX 25 | CMOVQEQ R9,R8 26 | MOVQ SI,0(DI) 27 | MOVQ DX,80(DI) 28 | MOVQ CX,8(DI) 29 | MOVQ R8,88(DI) 30 | MOVQ 16(DI),SI 31 | MOVQ 96(DI),DX 32 | MOVQ 24(DI),CX 33 | MOVQ 104(DI),R8 34 | MOVQ SI,R9 35 | CMOVQEQ DX,SI 36 | CMOVQEQ R9,DX 37 | MOVQ CX,R9 38 | CMOVQEQ R8,CX 39 | CMOVQEQ R9,R8 40 | MOVQ SI,16(DI) 41 | MOVQ DX,96(DI) 42 | MOVQ CX,24(DI) 43 | MOVQ R8,104(DI) 44 | MOVQ 32(DI),SI 45 | MOVQ 112(DI),DX 46 | MOVQ 40(DI),CX 47 | MOVQ 120(DI),R8 48 | MOVQ SI,R9 49 | CMOVQEQ DX,SI 50 | CMOVQEQ R9,DX 51 | MOVQ CX,R9 52 | CMOVQEQ R8,CX 53 | CMOVQEQ R9,R8 54 | MOVQ SI,32(DI) 55 | MOVQ DX,112(DI) 56 | MOVQ CX,40(DI) 57 | MOVQ R8,120(DI) 58 | MOVQ 48(DI),SI 59 | MOVQ 128(DI),DX 60 | MOVQ 56(DI),CX 61 | MOVQ 136(DI),R8 62 | MOVQ SI,R9 63 | CMOVQEQ DX,SI 64 | CMOVQEQ R9,DX 65 | MOVQ CX,R9 66 | CMOVQEQ R8,CX 67 | CMOVQEQ R9,R8 68 | MOVQ SI,48(DI) 69 | MOVQ DX,128(DI) 70 | MOVQ CX,56(DI) 71 | MOVQ R8,136(DI) 72 | MOVQ 64(DI),SI 73 | MOVQ 144(DI),DX 74 | MOVQ 72(DI),CX 75 | MOVQ 152(DI),R8 76 | MOVQ SI,R9 77 | CMOVQEQ DX,SI 78 | CMOVQEQ R9,DX 79 | MOVQ CX,R9 80 | CMOVQEQ R8,CX 81 | CMOVQEQ R9,R8 82 | MOVQ SI,64(DI) 83 | MOVQ DX,144(DI) 84 | MOVQ CX,72(DI) 85 | MOVQ R8,152(DI) 86 | MOVQ DI,AX 87 | MOVQ SI,DX 88 | RET 89 | -------------------------------------------------------------------------------- /vendor/golang.org/x/crypto/curve25519/doc.go: -------------------------------------------------------------------------------- 1 | // Copyright 2012 The Go Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | // Package curve25519 provides an implementation of scalar multiplication on 6 | // the elliptic curve known as curve25519. See http://cr.yp.to/ecdh.html 7 | package curve25519 // import "golang.org/x/crypto/curve25519" 8 | 9 | // basePoint is the x coordinate of the generator of the curve. 10 | var basePoint = [32]byte{9, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0} 11 | 12 | // ScalarMult sets dst to the product in*base where dst and base are the x 13 | // coordinates of group points and all values are in little-endian form. 14 | func ScalarMult(dst, in, base *[32]byte) { 15 | scalarMult(dst, in, base) 16 | } 17 | 18 | // ScalarBaseMult sets dst to the product in*base where dst and base are the x 19 | // coordinates of group points, base is the standard generator and all values 20 | // are in little-endian form. 21 | func ScalarBaseMult(dst, in *[32]byte) { 22 | ScalarMult(dst, in, &basePoint) 23 | } 24 | -------------------------------------------------------------------------------- /vendor/golang.org/x/crypto/curve25519/freeze_amd64.s: -------------------------------------------------------------------------------- 1 | // Copyright 2012 The Go Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | // This code was translated into a form compatible with 6a from the public 6 | // domain sources in SUPERCOP: http://bench.cr.yp.to/supercop.html 7 | 8 | // +build amd64,!gccgo,!appengine 9 | 10 | #include "const_amd64.h" 11 | 12 | // func freeze(inout *[5]uint64) 13 | TEXT ·freeze(SB),7,$0-8 14 | MOVQ inout+0(FP), DI 15 | 16 | MOVQ 0(DI),SI 17 | MOVQ 8(DI),DX 18 | MOVQ 16(DI),CX 19 | MOVQ 24(DI),R8 20 | MOVQ 32(DI),R9 21 | MOVQ $REDMASK51,AX 22 | MOVQ AX,R10 23 | SUBQ $18,R10 24 | MOVQ $3,R11 25 | REDUCELOOP: 26 | MOVQ SI,R12 27 | SHRQ $51,R12 28 | ANDQ AX,SI 29 | ADDQ R12,DX 30 | MOVQ DX,R12 31 | SHRQ $51,R12 32 | ANDQ AX,DX 33 | ADDQ R12,CX 34 | MOVQ CX,R12 35 | SHRQ $51,R12 36 | ANDQ AX,CX 37 | ADDQ R12,R8 38 | MOVQ R8,R12 39 | SHRQ $51,R12 40 | ANDQ AX,R8 41 | ADDQ R12,R9 42 | MOVQ R9,R12 43 | SHRQ $51,R12 44 | ANDQ AX,R9 45 | IMUL3Q $19,R12,R12 46 | ADDQ R12,SI 47 | SUBQ $1,R11 48 | JA REDUCELOOP 49 | MOVQ $1,R12 50 | CMPQ R10,SI 51 | CMOVQLT R11,R12 52 | CMPQ AX,DX 53 | CMOVQNE R11,R12 54 | CMPQ AX,CX 55 | CMOVQNE R11,R12 56 | CMPQ AX,R8 57 | CMOVQNE R11,R12 58 | CMPQ AX,R9 59 | CMOVQNE R11,R12 60 | NEGQ R12 61 | ANDQ R12,AX 62 | ANDQ R12,R10 63 | SUBQ R10,SI 64 | SUBQ AX,DX 65 | SUBQ AX,CX 66 | SUBQ AX,R8 67 | SUBQ AX,R9 68 | MOVQ SI,0(DI) 69 | MOVQ DX,8(DI) 70 | MOVQ CX,16(DI) 71 | MOVQ R8,24(DI) 72 | MOVQ R9,32(DI) 73 | RET 74 | -------------------------------------------------------------------------------- /vendor/golang.org/x/crypto/curve25519/mont25519_amd64.go: -------------------------------------------------------------------------------- 1 | // Copyright 2012 The Go Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | // +build amd64,!gccgo,!appengine 6 | 7 | package curve25519 8 | 9 | // These functions are implemented in the .s files. The names of the functions 10 | // in the rest of the file are also taken from the SUPERCOP sources to help 11 | // people following along. 12 | 13 | //go:noescape 14 | 15 | func cswap(inout *[5]uint64, v uint64) 16 | 17 | //go:noescape 18 | 19 | func ladderstep(inout *[5][5]uint64) 20 | 21 | //go:noescape 22 | 23 | func freeze(inout *[5]uint64) 24 | 25 | //go:noescape 26 | 27 | func mul(dest, a, b *[5]uint64) 28 | 29 | //go:noescape 30 | 31 | func square(out, in *[5]uint64) 32 | 33 | // mladder uses a Montgomery ladder to calculate (xr/zr) *= s. 34 | func mladder(xr, zr *[5]uint64, s *[32]byte) { 35 | var work [5][5]uint64 36 | 37 | work[0] = *xr 38 | setint(&work[1], 1) 39 | setint(&work[2], 0) 40 | work[3] = *xr 41 | setint(&work[4], 1) 42 | 43 | j := uint(6) 44 | var prevbit byte 45 | 46 | for i := 31; i >= 0; i-- { 47 | for j < 8 { 48 | bit := ((*s)[i] >> j) & 1 49 | swap := bit ^ prevbit 50 | prevbit = bit 51 | cswap(&work[1], uint64(swap)) 52 | ladderstep(&work) 53 | j-- 54 | } 55 | j = 7 56 | } 57 | 58 | *xr = work[1] 59 | *zr = work[2] 60 | } 61 | 62 | func scalarMult(out, in, base *[32]byte) { 63 | var e [32]byte 64 | copy(e[:], (*in)[:]) 65 | e[0] &= 248 66 | e[31] &= 127 67 | e[31] |= 64 68 | 69 | var t, z [5]uint64 70 | unpack(&t, base) 71 | mladder(&t, &z, &e) 72 | invert(&z, &z) 73 | mul(&t, &t, &z) 74 | pack(out, &t) 75 | } 76 | 77 | func setint(r *[5]uint64, v uint64) { 78 | r[0] = v 79 | r[1] = 0 80 | r[2] = 0 81 | r[3] = 0 82 | r[4] = 0 83 | } 84 | 85 | // unpack sets r = x where r consists of 5, 51-bit limbs in little-endian 86 | // order. 87 | func unpack(r *[5]uint64, x *[32]byte) { 88 | r[0] = uint64(x[0]) | 89 | uint64(x[1])<<8 | 90 | uint64(x[2])<<16 | 91 | uint64(x[3])<<24 | 92 | uint64(x[4])<<32 | 93 | uint64(x[5])<<40 | 94 | uint64(x[6]&7)<<48 95 | 96 | r[1] = uint64(x[6])>>3 | 97 | uint64(x[7])<<5 | 98 | uint64(x[8])<<13 | 99 | uint64(x[9])<<21 | 100 | uint64(x[10])<<29 | 101 | uint64(x[11])<<37 | 102 | uint64(x[12]&63)<<45 103 | 104 | r[2] = uint64(x[12])>>6 | 105 | uint64(x[13])<<2 | 106 | uint64(x[14])<<10 | 107 | uint64(x[15])<<18 | 108 | uint64(x[16])<<26 | 109 | uint64(x[17])<<34 | 110 | uint64(x[18])<<42 | 111 | uint64(x[19]&1)<<50 112 | 113 | r[3] = uint64(x[19])>>1 | 114 | uint64(x[20])<<7 | 115 | uint64(x[21])<<15 | 116 | uint64(x[22])<<23 | 117 | uint64(x[23])<<31 | 118 | uint64(x[24])<<39 | 119 | uint64(x[25]&15)<<47 120 | 121 | r[4] = uint64(x[25])>>4 | 122 | uint64(x[26])<<4 | 123 | uint64(x[27])<<12 | 124 | uint64(x[28])<<20 | 125 | uint64(x[29])<<28 | 126 | uint64(x[30])<<36 | 127 | uint64(x[31]&127)<<44 128 | } 129 | 130 | // pack sets out = x where out is the usual, little-endian form of the 5, 131 | // 51-bit limbs in x. 132 | func pack(out *[32]byte, x *[5]uint64) { 133 | t := *x 134 | freeze(&t) 135 | 136 | out[0] = byte(t[0]) 137 | out[1] = byte(t[0] >> 8) 138 | out[2] = byte(t[0] >> 16) 139 | out[3] = byte(t[0] >> 24) 140 | out[4] = byte(t[0] >> 32) 141 | out[5] = byte(t[0] >> 40) 142 | out[6] = byte(t[0] >> 48) 143 | 144 | out[6] ^= byte(t[1]<<3) & 0xf8 145 | out[7] = byte(t[1] >> 5) 146 | out[8] = byte(t[1] >> 13) 147 | out[9] = byte(t[1] >> 21) 148 | out[10] = byte(t[1] >> 29) 149 | out[11] = byte(t[1] >> 37) 150 | out[12] = byte(t[1] >> 45) 151 | 152 | out[12] ^= byte(t[2]<<6) & 0xc0 153 | out[13] = byte(t[2] >> 2) 154 | out[14] = byte(t[2] >> 10) 155 | out[15] = byte(t[2] >> 18) 156 | out[16] = byte(t[2] >> 26) 157 | out[17] = byte(t[2] >> 34) 158 | out[18] = byte(t[2] >> 42) 159 | out[19] = byte(t[2] >> 50) 160 | 161 | out[19] ^= byte(t[3]<<1) & 0xfe 162 | out[20] = byte(t[3] >> 7) 163 | out[21] = byte(t[3] >> 15) 164 | out[22] = byte(t[3] >> 23) 165 | out[23] = byte(t[3] >> 31) 166 | out[24] = byte(t[3] >> 39) 167 | out[25] = byte(t[3] >> 47) 168 | 169 | out[25] ^= byte(t[4]<<4) & 0xf0 170 | out[26] = byte(t[4] >> 4) 171 | out[27] = byte(t[4] >> 12) 172 | out[28] = byte(t[4] >> 20) 173 | out[29] = byte(t[4] >> 28) 174 | out[30] = byte(t[4] >> 36) 175 | out[31] = byte(t[4] >> 44) 176 | } 177 | 178 | // invert calculates r = x^-1 mod p using Fermat's little theorem. 179 | func invert(r *[5]uint64, x *[5]uint64) { 180 | var z2, z9, z11, z2_5_0, z2_10_0, z2_20_0, z2_50_0, z2_100_0, t [5]uint64 181 | 182 | square(&z2, x) /* 2 */ 183 | square(&t, &z2) /* 4 */ 184 | square(&t, &t) /* 8 */ 185 | mul(&z9, &t, x) /* 9 */ 186 | mul(&z11, &z9, &z2) /* 11 */ 187 | square(&t, &z11) /* 22 */ 188 | mul(&z2_5_0, &t, &z9) /* 2^5 - 2^0 = 31 */ 189 | 190 | square(&t, &z2_5_0) /* 2^6 - 2^1 */ 191 | for i := 1; i < 5; i++ { /* 2^20 - 2^10 */ 192 | square(&t, &t) 193 | } 194 | mul(&z2_10_0, &t, &z2_5_0) /* 2^10 - 2^0 */ 195 | 196 | square(&t, &z2_10_0) /* 2^11 - 2^1 */ 197 | for i := 1; i < 10; i++ { /* 2^20 - 2^10 */ 198 | square(&t, &t) 199 | } 200 | mul(&z2_20_0, &t, &z2_10_0) /* 2^20 - 2^0 */ 201 | 202 | square(&t, &z2_20_0) /* 2^21 - 2^1 */ 203 | for i := 1; i < 20; i++ { /* 2^40 - 2^20 */ 204 | square(&t, &t) 205 | } 206 | mul(&t, &t, &z2_20_0) /* 2^40 - 2^0 */ 207 | 208 | square(&t, &t) /* 2^41 - 2^1 */ 209 | for i := 1; i < 10; i++ { /* 2^50 - 2^10 */ 210 | square(&t, &t) 211 | } 212 | mul(&z2_50_0, &t, &z2_10_0) /* 2^50 - 2^0 */ 213 | 214 | square(&t, &z2_50_0) /* 2^51 - 2^1 */ 215 | for i := 1; i < 50; i++ { /* 2^100 - 2^50 */ 216 | square(&t, &t) 217 | } 218 | mul(&z2_100_0, &t, &z2_50_0) /* 2^100 - 2^0 */ 219 | 220 | square(&t, &z2_100_0) /* 2^101 - 2^1 */ 221 | for i := 1; i < 100; i++ { /* 2^200 - 2^100 */ 222 | square(&t, &t) 223 | } 224 | mul(&t, &t, &z2_100_0) /* 2^200 - 2^0 */ 225 | 226 | square(&t, &t) /* 2^201 - 2^1 */ 227 | for i := 1; i < 50; i++ { /* 2^250 - 2^50 */ 228 | square(&t, &t) 229 | } 230 | mul(&t, &t, &z2_50_0) /* 2^250 - 2^0 */ 231 | 232 | square(&t, &t) /* 2^251 - 2^1 */ 233 | square(&t, &t) /* 2^252 - 2^2 */ 234 | square(&t, &t) /* 2^253 - 2^3 */ 235 | 236 | square(&t, &t) /* 2^254 - 2^4 */ 237 | 238 | square(&t, &t) /* 2^255 - 2^5 */ 239 | mul(r, &t, &z11) /* 2^255 - 21 */ 240 | } 241 | -------------------------------------------------------------------------------- /vendor/golang.org/x/crypto/curve25519/mul_amd64.s: -------------------------------------------------------------------------------- 1 | // Copyright 2012 The Go Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | // This code was translated into a form compatible with 6a from the public 6 | // domain sources in SUPERCOP: http://bench.cr.yp.to/supercop.html 7 | 8 | // +build amd64,!gccgo,!appengine 9 | 10 | #include "const_amd64.h" 11 | 12 | // func mul(dest, a, b *[5]uint64) 13 | TEXT ·mul(SB),0,$16-24 14 | MOVQ dest+0(FP), DI 15 | MOVQ a+8(FP), SI 16 | MOVQ b+16(FP), DX 17 | 18 | MOVQ DX,CX 19 | MOVQ 24(SI),DX 20 | IMUL3Q $19,DX,AX 21 | MOVQ AX,0(SP) 22 | MULQ 16(CX) 23 | MOVQ AX,R8 24 | MOVQ DX,R9 25 | MOVQ 32(SI),DX 26 | IMUL3Q $19,DX,AX 27 | MOVQ AX,8(SP) 28 | MULQ 8(CX) 29 | ADDQ AX,R8 30 | ADCQ DX,R9 31 | MOVQ 0(SI),AX 32 | MULQ 0(CX) 33 | ADDQ AX,R8 34 | ADCQ DX,R9 35 | MOVQ 0(SI),AX 36 | MULQ 8(CX) 37 | MOVQ AX,R10 38 | MOVQ DX,R11 39 | MOVQ 0(SI),AX 40 | MULQ 16(CX) 41 | MOVQ AX,R12 42 | MOVQ DX,R13 43 | MOVQ 0(SI),AX 44 | MULQ 24(CX) 45 | MOVQ AX,R14 46 | MOVQ DX,R15 47 | MOVQ 0(SI),AX 48 | MULQ 32(CX) 49 | MOVQ AX,BX 50 | MOVQ DX,BP 51 | MOVQ 8(SI),AX 52 | MULQ 0(CX) 53 | ADDQ AX,R10 54 | ADCQ DX,R11 55 | MOVQ 8(SI),AX 56 | MULQ 8(CX) 57 | ADDQ AX,R12 58 | ADCQ DX,R13 59 | MOVQ 8(SI),AX 60 | MULQ 16(CX) 61 | ADDQ AX,R14 62 | ADCQ DX,R15 63 | MOVQ 8(SI),AX 64 | MULQ 24(CX) 65 | ADDQ AX,BX 66 | ADCQ DX,BP 67 | MOVQ 8(SI),DX 68 | IMUL3Q $19,DX,AX 69 | MULQ 32(CX) 70 | ADDQ AX,R8 71 | ADCQ DX,R9 72 | MOVQ 16(SI),AX 73 | MULQ 0(CX) 74 | ADDQ AX,R12 75 | ADCQ DX,R13 76 | MOVQ 16(SI),AX 77 | MULQ 8(CX) 78 | ADDQ AX,R14 79 | ADCQ DX,R15 80 | MOVQ 16(SI),AX 81 | MULQ 16(CX) 82 | ADDQ AX,BX 83 | ADCQ DX,BP 84 | MOVQ 16(SI),DX 85 | IMUL3Q $19,DX,AX 86 | MULQ 24(CX) 87 | ADDQ AX,R8 88 | ADCQ DX,R9 89 | MOVQ 16(SI),DX 90 | IMUL3Q $19,DX,AX 91 | MULQ 32(CX) 92 | ADDQ AX,R10 93 | ADCQ DX,R11 94 | MOVQ 24(SI),AX 95 | MULQ 0(CX) 96 | ADDQ AX,R14 97 | ADCQ DX,R15 98 | MOVQ 24(SI),AX 99 | MULQ 8(CX) 100 | ADDQ AX,BX 101 | ADCQ DX,BP 102 | MOVQ 0(SP),AX 103 | MULQ 24(CX) 104 | ADDQ AX,R10 105 | ADCQ DX,R11 106 | MOVQ 0(SP),AX 107 | MULQ 32(CX) 108 | ADDQ AX,R12 109 | ADCQ DX,R13 110 | MOVQ 32(SI),AX 111 | MULQ 0(CX) 112 | ADDQ AX,BX 113 | ADCQ DX,BP 114 | MOVQ 8(SP),AX 115 | MULQ 16(CX) 116 | ADDQ AX,R10 117 | ADCQ DX,R11 118 | MOVQ 8(SP),AX 119 | MULQ 24(CX) 120 | ADDQ AX,R12 121 | ADCQ DX,R13 122 | MOVQ 8(SP),AX 123 | MULQ 32(CX) 124 | ADDQ AX,R14 125 | ADCQ DX,R15 126 | MOVQ $REDMASK51,SI 127 | SHLQ $13,R9:R8 128 | ANDQ SI,R8 129 | SHLQ $13,R11:R10 130 | ANDQ SI,R10 131 | ADDQ R9,R10 132 | SHLQ $13,R13:R12 133 | ANDQ SI,R12 134 | ADDQ R11,R12 135 | SHLQ $13,R15:R14 136 | ANDQ SI,R14 137 | ADDQ R13,R14 138 | SHLQ $13,BP:BX 139 | ANDQ SI,BX 140 | ADDQ R15,BX 141 | IMUL3Q $19,BP,DX 142 | ADDQ DX,R8 143 | MOVQ R8,DX 144 | SHRQ $51,DX 145 | ADDQ R10,DX 146 | MOVQ DX,CX 147 | SHRQ $51,DX 148 | ANDQ SI,R8 149 | ADDQ R12,DX 150 | MOVQ DX,R9 151 | SHRQ $51,DX 152 | ANDQ SI,CX 153 | ADDQ R14,DX 154 | MOVQ DX,AX 155 | SHRQ $51,DX 156 | ANDQ SI,R9 157 | ADDQ BX,DX 158 | MOVQ DX,R10 159 | SHRQ $51,DX 160 | ANDQ SI,AX 161 | IMUL3Q $19,DX,DX 162 | ADDQ DX,R8 163 | ANDQ SI,R10 164 | MOVQ R8,0(DI) 165 | MOVQ CX,8(DI) 166 | MOVQ R9,16(DI) 167 | MOVQ AX,24(DI) 168 | MOVQ R10,32(DI) 169 | RET 170 | -------------------------------------------------------------------------------- /vendor/golang.org/x/crypto/curve25519/square_amd64.s: -------------------------------------------------------------------------------- 1 | // Copyright 2012 The Go Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | // This code was translated into a form compatible with 6a from the public 6 | // domain sources in SUPERCOP: http://bench.cr.yp.to/supercop.html 7 | 8 | // +build amd64,!gccgo,!appengine 9 | 10 | #include "const_amd64.h" 11 | 12 | // func square(out, in *[5]uint64) 13 | TEXT ·square(SB),7,$0-16 14 | MOVQ out+0(FP), DI 15 | MOVQ in+8(FP), SI 16 | 17 | MOVQ 0(SI),AX 18 | MULQ 0(SI) 19 | MOVQ AX,CX 20 | MOVQ DX,R8 21 | MOVQ 0(SI),AX 22 | SHLQ $1,AX 23 | MULQ 8(SI) 24 | MOVQ AX,R9 25 | MOVQ DX,R10 26 | MOVQ 0(SI),AX 27 | SHLQ $1,AX 28 | MULQ 16(SI) 29 | MOVQ AX,R11 30 | MOVQ DX,R12 31 | MOVQ 0(SI),AX 32 | SHLQ $1,AX 33 | MULQ 24(SI) 34 | MOVQ AX,R13 35 | MOVQ DX,R14 36 | MOVQ 0(SI),AX 37 | SHLQ $1,AX 38 | MULQ 32(SI) 39 | MOVQ AX,R15 40 | MOVQ DX,BX 41 | MOVQ 8(SI),AX 42 | MULQ 8(SI) 43 | ADDQ AX,R11 44 | ADCQ DX,R12 45 | MOVQ 8(SI),AX 46 | SHLQ $1,AX 47 | MULQ 16(SI) 48 | ADDQ AX,R13 49 | ADCQ DX,R14 50 | MOVQ 8(SI),AX 51 | SHLQ $1,AX 52 | MULQ 24(SI) 53 | ADDQ AX,R15 54 | ADCQ DX,BX 55 | MOVQ 8(SI),DX 56 | IMUL3Q $38,DX,AX 57 | MULQ 32(SI) 58 | ADDQ AX,CX 59 | ADCQ DX,R8 60 | MOVQ 16(SI),AX 61 | MULQ 16(SI) 62 | ADDQ AX,R15 63 | ADCQ DX,BX 64 | MOVQ 16(SI),DX 65 | IMUL3Q $38,DX,AX 66 | MULQ 24(SI) 67 | ADDQ AX,CX 68 | ADCQ DX,R8 69 | MOVQ 16(SI),DX 70 | IMUL3Q $38,DX,AX 71 | MULQ 32(SI) 72 | ADDQ AX,R9 73 | ADCQ DX,R10 74 | MOVQ 24(SI),DX 75 | IMUL3Q $19,DX,AX 76 | MULQ 24(SI) 77 | ADDQ AX,R9 78 | ADCQ DX,R10 79 | MOVQ 24(SI),DX 80 | IMUL3Q $38,DX,AX 81 | MULQ 32(SI) 82 | ADDQ AX,R11 83 | ADCQ DX,R12 84 | MOVQ 32(SI),DX 85 | IMUL3Q $19,DX,AX 86 | MULQ 32(SI) 87 | ADDQ AX,R13 88 | ADCQ DX,R14 89 | MOVQ $REDMASK51,SI 90 | SHLQ $13,R8:CX 91 | ANDQ SI,CX 92 | SHLQ $13,R10:R9 93 | ANDQ SI,R9 94 | ADDQ R8,R9 95 | SHLQ $13,R12:R11 96 | ANDQ SI,R11 97 | ADDQ R10,R11 98 | SHLQ $13,R14:R13 99 | ANDQ SI,R13 100 | ADDQ R12,R13 101 | SHLQ $13,BX:R15 102 | ANDQ SI,R15 103 | ADDQ R14,R15 104 | IMUL3Q $19,BX,DX 105 | ADDQ DX,CX 106 | MOVQ CX,DX 107 | SHRQ $51,DX 108 | ADDQ R9,DX 109 | ANDQ SI,CX 110 | MOVQ DX,R8 111 | SHRQ $51,DX 112 | ADDQ R11,DX 113 | ANDQ SI,R8 114 | MOVQ DX,R9 115 | SHRQ $51,DX 116 | ADDQ R13,DX 117 | ANDQ SI,R9 118 | MOVQ DX,AX 119 | SHRQ $51,DX 120 | ADDQ R15,DX 121 | ANDQ SI,AX 122 | MOVQ DX,R10 123 | SHRQ $51,DX 124 | IMUL3Q $19,DX,DX 125 | ADDQ DX,CX 126 | ANDQ SI,R10 127 | MOVQ CX,0(DI) 128 | MOVQ R8,8(DI) 129 | MOVQ R9,16(DI) 130 | MOVQ AX,24(DI) 131 | MOVQ R10,32(DI) 132 | RET 133 | -------------------------------------------------------------------------------- /vendor/golang.org/x/crypto/ed25519/ed25519.go: -------------------------------------------------------------------------------- 1 | // Copyright 2016 The Go Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | // Package ed25519 implements the Ed25519 signature algorithm. See 6 | // http://ed25519.cr.yp.to/. 7 | // 8 | // These functions are also compatible with the “Ed25519” function defined in 9 | // https://tools.ietf.org/html/draft-irtf-cfrg-eddsa-05. 10 | package ed25519 11 | 12 | // This code is a port of the public domain, “ref10” implementation of ed25519 13 | // from SUPERCOP. 14 | 15 | import ( 16 | "crypto" 17 | cryptorand "crypto/rand" 18 | "crypto/sha512" 19 | "crypto/subtle" 20 | "errors" 21 | "io" 22 | "strconv" 23 | 24 | "golang.org/x/crypto/ed25519/internal/edwards25519" 25 | ) 26 | 27 | const ( 28 | // PublicKeySize is the size, in bytes, of public keys as used in this package. 29 | PublicKeySize = 32 30 | // PrivateKeySize is the size, in bytes, of private keys as used in this package. 31 | PrivateKeySize = 64 32 | // SignatureSize is the size, in bytes, of signatures generated and verified by this package. 33 | SignatureSize = 64 34 | ) 35 | 36 | // PublicKey is the type of Ed25519 public keys. 37 | type PublicKey []byte 38 | 39 | // PrivateKey is the type of Ed25519 private keys. It implements crypto.Signer. 40 | type PrivateKey []byte 41 | 42 | // Public returns the PublicKey corresponding to priv. 43 | func (priv PrivateKey) Public() crypto.PublicKey { 44 | publicKey := make([]byte, PublicKeySize) 45 | copy(publicKey, priv[32:]) 46 | return PublicKey(publicKey) 47 | } 48 | 49 | // Sign signs the given message with priv. 50 | // Ed25519 performs two passes over messages to be signed and therefore cannot 51 | // handle pre-hashed messages. Thus opts.HashFunc() must return zero to 52 | // indicate the message hasn't been hashed. This can be achieved by passing 53 | // crypto.Hash(0) as the value for opts. 54 | func (priv PrivateKey) Sign(rand io.Reader, message []byte, opts crypto.SignerOpts) (signature []byte, err error) { 55 | if opts.HashFunc() != crypto.Hash(0) { 56 | return nil, errors.New("ed25519: cannot sign hashed message") 57 | } 58 | 59 | return Sign(priv, message), nil 60 | } 61 | 62 | // GenerateKey generates a public/private key pair using entropy from rand. 63 | // If rand is nil, crypto/rand.Reader will be used. 64 | func GenerateKey(rand io.Reader) (publicKey PublicKey, privateKey PrivateKey, err error) { 65 | if rand == nil { 66 | rand = cryptorand.Reader 67 | } 68 | 69 | privateKey = make([]byte, PrivateKeySize) 70 | publicKey = make([]byte, PublicKeySize) 71 | _, err = io.ReadFull(rand, privateKey[:32]) 72 | if err != nil { 73 | return nil, nil, err 74 | } 75 | 76 | digest := sha512.Sum512(privateKey[:32]) 77 | digest[0] &= 248 78 | digest[31] &= 127 79 | digest[31] |= 64 80 | 81 | var A edwards25519.ExtendedGroupElement 82 | var hBytes [32]byte 83 | copy(hBytes[:], digest[:]) 84 | edwards25519.GeScalarMultBase(&A, &hBytes) 85 | var publicKeyBytes [32]byte 86 | A.ToBytes(&publicKeyBytes) 87 | 88 | copy(privateKey[32:], publicKeyBytes[:]) 89 | copy(publicKey, publicKeyBytes[:]) 90 | 91 | return publicKey, privateKey, nil 92 | } 93 | 94 | // Sign signs the message with privateKey and returns a signature. It will 95 | // panic if len(privateKey) is not PrivateKeySize. 96 | func Sign(privateKey PrivateKey, message []byte) []byte { 97 | if l := len(privateKey); l != PrivateKeySize { 98 | panic("ed25519: bad private key length: " + strconv.Itoa(l)) 99 | } 100 | 101 | h := sha512.New() 102 | h.Write(privateKey[:32]) 103 | 104 | var digest1, messageDigest, hramDigest [64]byte 105 | var expandedSecretKey [32]byte 106 | h.Sum(digest1[:0]) 107 | copy(expandedSecretKey[:], digest1[:]) 108 | expandedSecretKey[0] &= 248 109 | expandedSecretKey[31] &= 63 110 | expandedSecretKey[31] |= 64 111 | 112 | h.Reset() 113 | h.Write(digest1[32:]) 114 | h.Write(message) 115 | h.Sum(messageDigest[:0]) 116 | 117 | var messageDigestReduced [32]byte 118 | edwards25519.ScReduce(&messageDigestReduced, &messageDigest) 119 | var R edwards25519.ExtendedGroupElement 120 | edwards25519.GeScalarMultBase(&R, &messageDigestReduced) 121 | 122 | var encodedR [32]byte 123 | R.ToBytes(&encodedR) 124 | 125 | h.Reset() 126 | h.Write(encodedR[:]) 127 | h.Write(privateKey[32:]) 128 | h.Write(message) 129 | h.Sum(hramDigest[:0]) 130 | var hramDigestReduced [32]byte 131 | edwards25519.ScReduce(&hramDigestReduced, &hramDigest) 132 | 133 | var s [32]byte 134 | edwards25519.ScMulAdd(&s, &hramDigestReduced, &expandedSecretKey, &messageDigestReduced) 135 | 136 | signature := make([]byte, SignatureSize) 137 | copy(signature[:], encodedR[:]) 138 | copy(signature[32:], s[:]) 139 | 140 | return signature 141 | } 142 | 143 | // Verify reports whether sig is a valid signature of message by publicKey. It 144 | // will panic if len(publicKey) is not PublicKeySize. 145 | func Verify(publicKey PublicKey, message, sig []byte) bool { 146 | if l := len(publicKey); l != PublicKeySize { 147 | panic("ed25519: bad public key length: " + strconv.Itoa(l)) 148 | } 149 | 150 | if len(sig) != SignatureSize || sig[63]&224 != 0 { 151 | return false 152 | } 153 | 154 | var A edwards25519.ExtendedGroupElement 155 | var publicKeyBytes [32]byte 156 | copy(publicKeyBytes[:], publicKey) 157 | if !A.FromBytes(&publicKeyBytes) { 158 | return false 159 | } 160 | edwards25519.FeNeg(&A.X, &A.X) 161 | edwards25519.FeNeg(&A.T, &A.T) 162 | 163 | h := sha512.New() 164 | h.Write(sig[:32]) 165 | h.Write(publicKey[:]) 166 | h.Write(message) 167 | var digest [64]byte 168 | h.Sum(digest[:0]) 169 | 170 | var hReduced [32]byte 171 | edwards25519.ScReduce(&hReduced, &digest) 172 | 173 | var R edwards25519.ProjectiveGroupElement 174 | var b [32]byte 175 | copy(b[:], sig[32:]) 176 | edwards25519.GeDoubleScalarMultVartime(&R, &hReduced, &A, &b) 177 | 178 | var checkR [32]byte 179 | R.ToBytes(&checkR) 180 | return subtle.ConstantTimeCompare(sig[:32], checkR[:]) == 1 181 | } 182 | -------------------------------------------------------------------------------- /vendor/golang.org/x/crypto/ssh/buffer.go: -------------------------------------------------------------------------------- 1 | // Copyright 2012 The Go Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | package ssh 6 | 7 | import ( 8 | "io" 9 | "sync" 10 | ) 11 | 12 | // buffer provides a linked list buffer for data exchange 13 | // between producer and consumer. Theoretically the buffer is 14 | // of unlimited capacity as it does no allocation of its own. 15 | type buffer struct { 16 | // protects concurrent access to head, tail and closed 17 | *sync.Cond 18 | 19 | head *element // the buffer that will be read first 20 | tail *element // the buffer that will be read last 21 | 22 | closed bool 23 | } 24 | 25 | // An element represents a single link in a linked list. 26 | type element struct { 27 | buf []byte 28 | next *element 29 | } 30 | 31 | // newBuffer returns an empty buffer that is not closed. 32 | func newBuffer() *buffer { 33 | e := new(element) 34 | b := &buffer{ 35 | Cond: newCond(), 36 | head: e, 37 | tail: e, 38 | } 39 | return b 40 | } 41 | 42 | // write makes buf available for Read to receive. 43 | // buf must not be modified after the call to write. 44 | func (b *buffer) write(buf []byte) { 45 | b.Cond.L.Lock() 46 | e := &element{buf: buf} 47 | b.tail.next = e 48 | b.tail = e 49 | b.Cond.Signal() 50 | b.Cond.L.Unlock() 51 | } 52 | 53 | // eof closes the buffer. Reads from the buffer once all 54 | // the data has been consumed will receive os.EOF. 55 | func (b *buffer) eof() error { 56 | b.Cond.L.Lock() 57 | b.closed = true 58 | b.Cond.Signal() 59 | b.Cond.L.Unlock() 60 | return nil 61 | } 62 | 63 | // Read reads data from the internal buffer in buf. Reads will block 64 | // if no data is available, or until the buffer is closed. 65 | func (b *buffer) Read(buf []byte) (n int, err error) { 66 | b.Cond.L.Lock() 67 | defer b.Cond.L.Unlock() 68 | 69 | for len(buf) > 0 { 70 | // if there is data in b.head, copy it 71 | if len(b.head.buf) > 0 { 72 | r := copy(buf, b.head.buf) 73 | buf, b.head.buf = buf[r:], b.head.buf[r:] 74 | n += r 75 | continue 76 | } 77 | // if there is a next buffer, make it the head 78 | if len(b.head.buf) == 0 && b.head != b.tail { 79 | b.head = b.head.next 80 | continue 81 | } 82 | 83 | // if at least one byte has been copied, return 84 | if n > 0 { 85 | break 86 | } 87 | 88 | // if nothing was read, and there is nothing outstanding 89 | // check to see if the buffer is closed. 90 | if b.closed { 91 | err = io.EOF 92 | break 93 | } 94 | // out of buffers, wait for producer 95 | b.Cond.Wait() 96 | } 97 | return 98 | } 99 | -------------------------------------------------------------------------------- /vendor/golang.org/x/crypto/ssh/connection.go: -------------------------------------------------------------------------------- 1 | // Copyright 2013 The Go Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | package ssh 6 | 7 | import ( 8 | "fmt" 9 | "net" 10 | ) 11 | 12 | // OpenChannelError is returned if the other side rejects an 13 | // OpenChannel request. 14 | type OpenChannelError struct { 15 | Reason RejectionReason 16 | Message string 17 | } 18 | 19 | func (e *OpenChannelError) Error() string { 20 | return fmt.Sprintf("ssh: rejected: %s (%s)", e.Reason, e.Message) 21 | } 22 | 23 | // ConnMetadata holds metadata for the connection. 24 | type ConnMetadata interface { 25 | // User returns the user ID for this connection. 26 | User() string 27 | 28 | // SessionID returns the sesson hash, also denoted by H. 29 | SessionID() []byte 30 | 31 | // ClientVersion returns the client's version string as hashed 32 | // into the session ID. 33 | ClientVersion() []byte 34 | 35 | // ServerVersion returns the server's version string as hashed 36 | // into the session ID. 37 | ServerVersion() []byte 38 | 39 | // RemoteAddr returns the remote address for this connection. 40 | RemoteAddr() net.Addr 41 | 42 | // LocalAddr returns the local address for this connection. 43 | LocalAddr() net.Addr 44 | } 45 | 46 | // Conn represents an SSH connection for both server and client roles. 47 | // Conn is the basis for implementing an application layer, such 48 | // as ClientConn, which implements the traditional shell access for 49 | // clients. 50 | type Conn interface { 51 | ConnMetadata 52 | 53 | // SendRequest sends a global request, and returns the 54 | // reply. If wantReply is true, it returns the response status 55 | // and payload. See also RFC4254, section 4. 56 | SendRequest(name string, wantReply bool, payload []byte) (bool, []byte, error) 57 | 58 | // OpenChannel tries to open an channel. If the request is 59 | // rejected, it returns *OpenChannelError. On success it returns 60 | // the SSH Channel and a Go channel for incoming, out-of-band 61 | // requests. The Go channel must be serviced, or the 62 | // connection will hang. 63 | OpenChannel(name string, data []byte) (Channel, <-chan *Request, error) 64 | 65 | // Close closes the underlying network connection 66 | Close() error 67 | 68 | // Wait blocks until the connection has shut down, and returns the 69 | // error causing the shutdown. 70 | Wait() error 71 | 72 | // TODO(hanwen): consider exposing: 73 | // RequestKeyChange 74 | // Disconnect 75 | } 76 | 77 | // DiscardRequests consumes and rejects all requests from the 78 | // passed-in channel. 79 | func DiscardRequests(in <-chan *Request) { 80 | for req := range in { 81 | if req.WantReply { 82 | req.Reply(false, nil) 83 | } 84 | } 85 | } 86 | 87 | // A connection represents an incoming connection. 88 | type connection struct { 89 | transport *handshakeTransport 90 | sshConn 91 | 92 | // The connection protocol. 93 | *mux 94 | } 95 | 96 | func (c *connection) Close() error { 97 | return c.sshConn.conn.Close() 98 | } 99 | 100 | // sshconn provides net.Conn metadata, but disallows direct reads and 101 | // writes. 102 | type sshConn struct { 103 | conn net.Conn 104 | 105 | user string 106 | sessionID []byte 107 | clientVersion []byte 108 | serverVersion []byte 109 | } 110 | 111 | func dup(src []byte) []byte { 112 | dst := make([]byte, len(src)) 113 | copy(dst, src) 114 | return dst 115 | } 116 | 117 | func (c *sshConn) User() string { 118 | return c.user 119 | } 120 | 121 | func (c *sshConn) RemoteAddr() net.Addr { 122 | return c.conn.RemoteAddr() 123 | } 124 | 125 | func (c *sshConn) Close() error { 126 | return c.conn.Close() 127 | } 128 | 129 | func (c *sshConn) LocalAddr() net.Addr { 130 | return c.conn.LocalAddr() 131 | } 132 | 133 | func (c *sshConn) SessionID() []byte { 134 | return dup(c.sessionID) 135 | } 136 | 137 | func (c *sshConn) ClientVersion() []byte { 138 | return dup(c.clientVersion) 139 | } 140 | 141 | func (c *sshConn) ServerVersion() []byte { 142 | return dup(c.serverVersion) 143 | } 144 | -------------------------------------------------------------------------------- /vendor/golang.org/x/crypto/ssh/doc.go: -------------------------------------------------------------------------------- 1 | // Copyright 2011 The Go Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | /* 6 | Package ssh implements an SSH client and server. 7 | 8 | SSH is a transport security protocol, an authentication protocol and a 9 | family of application protocols. The most typical application level 10 | protocol is a remote shell and this is specifically implemented. However, 11 | the multiplexed nature of SSH is exposed to users that wish to support 12 | others. 13 | 14 | References: 15 | [PROTOCOL.certkeys]: http://cvsweb.openbsd.org/cgi-bin/cvsweb/src/usr.bin/ssh/PROTOCOL.certkeys?rev=HEAD 16 | [SSH-PARAMETERS]: http://www.iana.org/assignments/ssh-parameters/ssh-parameters.xml#ssh-parameters-1 17 | 18 | This package does not fall under the stability promise of the Go language itself, 19 | so its API may be changed when pressing needs arise. 20 | */ 21 | package ssh // import "golang.org/x/crypto/ssh" 22 | -------------------------------------------------------------------------------- /vendor/golang.org/x/crypto/ssh/mac.go: -------------------------------------------------------------------------------- 1 | // Copyright 2012 The Go Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | package ssh 6 | 7 | // Message authentication support 8 | 9 | import ( 10 | "crypto/hmac" 11 | "crypto/sha1" 12 | "crypto/sha256" 13 | "hash" 14 | ) 15 | 16 | type macMode struct { 17 | keySize int 18 | etm bool 19 | new func(key []byte) hash.Hash 20 | } 21 | 22 | // truncatingMAC wraps around a hash.Hash and truncates the output digest to 23 | // a given size. 24 | type truncatingMAC struct { 25 | length int 26 | hmac hash.Hash 27 | } 28 | 29 | func (t truncatingMAC) Write(data []byte) (int, error) { 30 | return t.hmac.Write(data) 31 | } 32 | 33 | func (t truncatingMAC) Sum(in []byte) []byte { 34 | out := t.hmac.Sum(in) 35 | return out[:len(in)+t.length] 36 | } 37 | 38 | func (t truncatingMAC) Reset() { 39 | t.hmac.Reset() 40 | } 41 | 42 | func (t truncatingMAC) Size() int { 43 | return t.length 44 | } 45 | 46 | func (t truncatingMAC) BlockSize() int { return t.hmac.BlockSize() } 47 | 48 | var macModes = map[string]*macMode{ 49 | "hmac-sha2-256-etm@openssh.com": {32, true, func(key []byte) hash.Hash { 50 | return hmac.New(sha256.New, key) 51 | }}, 52 | "hmac-sha2-256": {32, false, func(key []byte) hash.Hash { 53 | return hmac.New(sha256.New, key) 54 | }}, 55 | "hmac-sha1": {20, false, func(key []byte) hash.Hash { 56 | return hmac.New(sha1.New, key) 57 | }}, 58 | "hmac-sha1-96": {20, false, func(key []byte) hash.Hash { 59 | return truncatingMAC{12, hmac.New(sha1.New, key)} 60 | }}, 61 | } 62 | -------------------------------------------------------------------------------- /vendor/golang.org/x/crypto/ssh/streamlocal.go: -------------------------------------------------------------------------------- 1 | package ssh 2 | 3 | import ( 4 | "errors" 5 | "io" 6 | "net" 7 | ) 8 | 9 | // streamLocalChannelOpenDirectMsg is a struct used for SSH_MSG_CHANNEL_OPEN message 10 | // with "direct-streamlocal@openssh.com" string. 11 | // 12 | // See openssh-portable/PROTOCOL, section 2.4. connection: Unix domain socket forwarding 13 | // https://github.com/openssh/openssh-portable/blob/master/PROTOCOL#L235 14 | type streamLocalChannelOpenDirectMsg struct { 15 | socketPath string 16 | reserved0 string 17 | reserved1 uint32 18 | } 19 | 20 | // forwardedStreamLocalPayload is a struct used for SSH_MSG_CHANNEL_OPEN message 21 | // with "forwarded-streamlocal@openssh.com" string. 22 | type forwardedStreamLocalPayload struct { 23 | SocketPath string 24 | Reserved0 string 25 | } 26 | 27 | // streamLocalChannelForwardMsg is a struct used for SSH2_MSG_GLOBAL_REQUEST message 28 | // with "streamlocal-forward@openssh.com"/"cancel-streamlocal-forward@openssh.com" string. 29 | type streamLocalChannelForwardMsg struct { 30 | socketPath string 31 | } 32 | 33 | // ListenUnix is similar to ListenTCP but uses a Unix domain socket. 34 | func (c *Client) ListenUnix(socketPath string) (net.Listener, error) { 35 | m := streamLocalChannelForwardMsg{ 36 | socketPath, 37 | } 38 | // send message 39 | ok, _, err := c.SendRequest("streamlocal-forward@openssh.com", true, Marshal(&m)) 40 | if err != nil { 41 | return nil, err 42 | } 43 | if !ok { 44 | return nil, errors.New("ssh: streamlocal-forward@openssh.com request denied by peer") 45 | } 46 | ch := c.forwards.add(&net.UnixAddr{Name: socketPath, Net: "unix"}) 47 | 48 | return &unixListener{socketPath, c, ch}, nil 49 | } 50 | 51 | func (c *Client) dialStreamLocal(socketPath string) (Channel, error) { 52 | msg := streamLocalChannelOpenDirectMsg{ 53 | socketPath: socketPath, 54 | } 55 | ch, in, err := c.OpenChannel("direct-streamlocal@openssh.com", Marshal(&msg)) 56 | if err != nil { 57 | return nil, err 58 | } 59 | go DiscardRequests(in) 60 | return ch, err 61 | } 62 | 63 | type unixListener struct { 64 | socketPath string 65 | 66 | conn *Client 67 | in <-chan forward 68 | } 69 | 70 | // Accept waits for and returns the next connection to the listener. 71 | func (l *unixListener) Accept() (net.Conn, error) { 72 | s, ok := <-l.in 73 | if !ok { 74 | return nil, io.EOF 75 | } 76 | ch, incoming, err := s.newCh.Accept() 77 | if err != nil { 78 | return nil, err 79 | } 80 | go DiscardRequests(incoming) 81 | 82 | return &chanConn{ 83 | Channel: ch, 84 | laddr: &net.UnixAddr{ 85 | Name: l.socketPath, 86 | Net: "unix", 87 | }, 88 | raddr: &net.UnixAddr{ 89 | Name: "@", 90 | Net: "unix", 91 | }, 92 | }, nil 93 | } 94 | 95 | // Close closes the listener. 96 | func (l *unixListener) Close() error { 97 | // this also closes the listener. 98 | l.conn.forwards.remove(&net.UnixAddr{Name: l.socketPath, Net: "unix"}) 99 | m := streamLocalChannelForwardMsg{ 100 | l.socketPath, 101 | } 102 | ok, _, err := l.conn.SendRequest("cancel-streamlocal-forward@openssh.com", true, Marshal(&m)) 103 | if err == nil && !ok { 104 | err = errors.New("ssh: cancel-streamlocal-forward@openssh.com failed") 105 | } 106 | return err 107 | } 108 | 109 | // Addr returns the listener's network address. 110 | func (l *unixListener) Addr() net.Addr { 111 | return &net.UnixAddr{ 112 | Name: l.socketPath, 113 | Net: "unix", 114 | } 115 | } 116 | -------------------------------------------------------------------------------- /vendor/vendor.json: -------------------------------------------------------------------------------- 1 | { 2 | "comment": "", 3 | "ignore": "test", 4 | "package": [ 5 | { 6 | "checksumSHA1": "dvabztWVQX8f6oMLRyv4dLH+TGY=", 7 | "path": "github.com/davecgh/go-spew/spew", 8 | "revision": "346938d642f2ec3594ed81d874461961cd0faa76", 9 | "revisionTime": "2016-10-29T20:57:26Z" 10 | }, 11 | { 12 | "checksumSHA1": "qzl/TNo4WnfR+PZRfk3iFHg68sM=", 13 | "path": "github.com/joho/godotenv", 14 | "revision": "325433c502d409f3c3dc820098fb0cfe38d98dc7", 15 | "revisionTime": "2017-03-28T20:01:54Z" 16 | }, 17 | { 18 | "checksumSHA1": "UE4lhbPYYWEhqHXQk24OA73m5+8=", 19 | "path": "github.com/joho/godotenv/autoload", 20 | "revision": "325433c502d409f3c3dc820098fb0cfe38d98dc7", 21 | "revisionTime": "2017-03-28T20:01:54Z" 22 | }, 23 | { 24 | "checksumSHA1": "KQhA4EQp4Ldwj9nJZnEURlE6aQw=", 25 | "path": "github.com/kr/fs", 26 | "revision": "2788f0dbd16903de03cb8186e5c7d97b69ad387b", 27 | "revisionTime": "2013-11-06T22:25:44Z" 28 | }, 29 | { 30 | "checksumSHA1": "ynJSWoF6v+3zMnh9R0QmmG6iGV8=", 31 | "path": "github.com/pkg/errors", 32 | "revision": "ff09b135c25aae272398c51a07235b90a75aa4f0", 33 | "revisionTime": "2017-03-16T20:15:38Z" 34 | }, 35 | { 36 | "checksumSHA1": "Mg9gF4U7PzZjuipPp5u+iufHuTA=", 37 | "path": "github.com/pkg/sftp", 38 | "revision": "cb456b384cd3aae0ca1838cd59af8b6bb3363d67", 39 | "revisionTime": "2017-04-07T09:08:41Z" 40 | }, 41 | { 42 | "checksumSHA1": "LuFv4/jlrmFNnDb/5SCSEPAM9vU=", 43 | "path": "github.com/pmezard/go-difflib/difflib", 44 | "revision": "792786c7400a136282c1664665ae0a8db921c6c2", 45 | "revisionTime": "2016-01-10T10:55:54Z" 46 | }, 47 | { 48 | "checksumSHA1": "JXUVA1jky8ZX8w09p2t5KLs97Nc=", 49 | "path": "github.com/stretchr/testify/assert", 50 | "revision": "4d4bfba8f1d1027c4fdbe371823030df51419987", 51 | "revisionTime": "2017-01-30T11:31:45Z" 52 | }, 53 | { 54 | "checksumSHA1": "K/CP3EBR0TUpw8DPpjRExGEYNkY=", 55 | "path": "github.com/urfave/cli", 56 | "revision": "8ba6f23b6e36d03666a14bd9421f5e3efcb59aca", 57 | "revisionTime": "2017-03-29T01:35:17Z" 58 | }, 59 | { 60 | "checksumSHA1": "C1KKOxFoW7/W/NFNpiXK+boguNo=", 61 | "path": "golang.org/x/crypto/curve25519", 62 | "revision": "9ef620b9ca2f82b55030ffd4f41327fa9e77a92c", 63 | "revisionTime": "2017-01-13T04:52:50Z" 64 | }, 65 | { 66 | "checksumSHA1": "wGb//LjBPNxYHqk+dcLo7BjPXK8=", 67 | "path": "golang.org/x/crypto/ed25519", 68 | "revision": "9ef620b9ca2f82b55030ffd4f41327fa9e77a92c", 69 | "revisionTime": "2017-01-13T04:52:50Z" 70 | }, 71 | { 72 | "checksumSHA1": "LXFcVx8I587SnWmKycSDEq9yvK8=", 73 | "path": "golang.org/x/crypto/ed25519/internal/edwards25519", 74 | "revision": "9ef620b9ca2f82b55030ffd4f41327fa9e77a92c", 75 | "revisionTime": "2017-01-13T04:52:50Z" 76 | }, 77 | { 78 | "checksumSHA1": "hOeJDDw3zHRgPF5lfjwG+19dzQQ=", 79 | "path": "golang.org/x/crypto/ssh", 80 | "revision": "9ef620b9ca2f82b55030ffd4f41327fa9e77a92c", 81 | "revisionTime": "2017-01-13T04:52:50Z" 82 | } 83 | ], 84 | "rootPath": "github.com/appleboy/drone-sftp-cache" 85 | } 86 | --------------------------------------------------------------------------------