@@ -0,0 +1,89 @@ | |||
name: Deploy multi-architecture Docker images for transfer.sh with buildx | |||
on: | |||
schedule: | |||
- cron: '0 0 * * *' # everyday at midnight UTC | |||
pull_request: | |||
branches: master | |||
push: | |||
branches: master | |||
tags: | |||
- v* | |||
jobs: | |||
buildx: | |||
runs-on: ubuntu-latest | |||
steps: | |||
- | |||
name: Checkout | |||
uses: actions/checkout@v2 | |||
- | |||
name: Prepare | |||
id: prepare | |||
run: | | |||
DOCKER_IMAGE=dutchcoders/transfer.sh | |||
DOCKER_PLATFORMS=linux/amd64,linux/arm/v7,linux/arm64,linux/386 | |||
VERSION=edge | |||
if [[ $GITHUB_REF == refs/tags/* ]]; then | |||
VERSION=${GITHUB_REF#refs/tags/v} | |||
fi | |||
if [ "${{ github.event_name }}" = "schedule" ]; then | |||
VERSION=nightly | |||
fi | |||
TAGS="--tag ${DOCKER_IMAGE}:${VERSION}" | |||
if [ $VERSION = edge -o $VERSION = nightly ]; then | |||
TAGS="$TAGS --tag ${DOCKER_IMAGE}:latest" | |||
fi | |||
echo ::set-output name=docker_image::${DOCKER_IMAGE} | |||
echo ::set-output name=version::${VERSION} | |||
echo ::set-output name=buildx_args::--platform ${DOCKER_PLATFORMS} \ | |||
--build-arg VERSION=${VERSION} \ | |||
--build-arg BUILD_DATE=$(date -u +'%Y-%m-%dT%H:%M:%SZ') \ | |||
--build-arg VCS_REF=${GITHUB_SHA::8} \ | |||
${TAGS} . | |||
- | |||
name: Set up QEMU | |||
uses: docker/setup-qemu-action@v1 | |||
with: | |||
platforms: all | |||
- | |||
name: Set up Docker Buildx | |||
id: buildx | |||
uses: docker/setup-buildx-action@v1 | |||
with: | |||
version: latest | |||
- | |||
name: Available platforms | |||
run: echo ${{ steps.buildx.outputs.platforms }} | |||
- | |||
name: Docker Buildx (build) | |||
run: | | |||
docker buildx build --no-cache --pull --output "type=image,push=false" ${{ steps.prepare.outputs.buildx_args }} | |||
- | |||
name: Docker Login | |||
if: success() && github.event_name != 'pull_request' | |||
env: | |||
DOCKER_USERNAME: ${{ secrets.DOCKER_USERNAME }} | |||
DOCKER_PASSWORD: ${{ secrets.DOCKER_PASSWORD }} | |||
run: | | |||
echo "${DOCKER_PASSWORD}" | docker login --username "${DOCKER_USERNAME}" --password-stdin | |||
- | |||
name: Docker Buildx (push) | |||
if: success() && github.event_name != 'pull_request' | |||
run: | | |||
docker buildx build --output "type=image,push=true" ${{ steps.prepare.outputs.buildx_args }} | |||
- | |||
name: Docker Check Manifest | |||
if: always() && github.event_name != 'pull_request' | |||
run: | | |||
docker run --rm mplatform/mquery ${{ steps.prepare.outputs.docker_image }}:${{ steps.prepare.outputs.version }} | |||
- | |||
name: Clear | |||
if: always() && github.event_name != 'pull_request' | |||
run: | | |||
rm -f ${HOME}/.docker/config.json |
@@ -8,9 +8,9 @@ services: | |||
- docker | |||
go: | |||
- 1.11.x | |||
- 1.12.x | |||
- 1.13.x | |||
- 1.14.x | |||
- 1.15.x | |||
- tip | |||
env: | |||
@@ -25,18 +25,6 @@ install: | |||
script: | |||
- go test ./... | |||
jobs: | |||
include: | |||
- stage: Fuzz regression | |||
go: 1.12.x | |||
dist: bionic | |||
script: ./fuzzit.sh local-regression | |||
- stage: Fuzz | |||
if: branch = master AND type IN (push) | |||
go: 1.12.x | |||
dist: bionic | |||
script: ./fuzzit.sh fuzzing | |||
before_deploy: | |||
- mkdir -p release | |||
- "GOOS=linux GOARCH=amd64 CGO_ENABLED=0 go build -ldflags -a -tags netgo -ldflags '-s -w -extldflags -static' -o release/transfersh-$TRAVIS_TAG-linux-amd64" | |||
@@ -56,5 +44,5 @@ deploy: | |||
skip_cleanup: true | |||
on: | |||
tags: true | |||
go: 1.12.x | |||
go: 1.15.x | |||
overwrite: true |
@@ -1,5 +1,5 @@ | |||
# Default to Go 1.12 | |||
ARG GO_VERSION=1.12 | |||
# Default to Go 1.13 | |||
ARG GO_VERSION=1.13 | |||
FROM golang:${GO_VERSION}-alpine as build | |||
# Necessary to run 'go get' and to compile the linked binary | |||
@@ -12,7 +12,7 @@ WORKDIR /go/src/github.com/dutchcoders/transfer.sh | |||
ENV GO111MODULE=on | |||
# build & install server | |||
RUN go get -u ./... && CGO_ENABLED=0 GOOS=linux GOARCH=amd64 go build -ldflags -a -tags netgo -ldflags '-w -extldflags "-static"' -o /go/bin/transfersh github.com/dutchcoders/transfer.sh | |||
RUN go get -u ./... && CGO_ENABLED=0 go build -ldflags -a -tags netgo -ldflags '-w -extldflags "-static"' -o /go/bin/transfersh github.com/dutchcoders/transfer.sh | |||
FROM scratch AS final | |||
LABEL maintainer="Andrea Spacca <andrea.spacca@gmail.com>" | |||
@@ -1,4 +1,4 @@ | |||
# transfer.sh [![Gitter](https://badges.gitter.im/Join%20Chat.svg)](https://gitter.im/dutchcoders/transfer.sh?utm_source=badge&utm_medium=badge&utm_campaign=&utm_campaign=pr-badge&utm_content=badge) [![Go Report Card](https://goreportcard.com/badge/github.com/dutchcoders/transfer.sh)](https://goreportcard.com/report/github.com/dutchcoders/transfer.sh) [![Docker pulls](https://img.shields.io/docker/pulls/dutchcoders/transfer.sh.svg)](https://hub.docker.com/r/dutchcoders/transfer.sh/) [![Build Status](https://travis-ci.org/dutchcoders/transfer.sh.svg?branch=master)](https://travis-ci.org/dutchcoders/transfer.sh) [![Fuzzit Status](https://app.fuzzit.dev/badge?org_id=transfer.sh)](https://app.fuzzit.dev/orgs/transfer.sh/dashboard) | |||
# transfer.sh [![Gitter](https://badges.gitter.im/Join%20Chat.svg)](https://gitter.im/dutchcoders/transfer.sh?utm_source=badge&utm_medium=badge&utm_campaign=&utm_campaign=pr-badge&utm_content=badge) [![Go Report Card](https://goreportcard.com/badge/github.com/dutchcoders/transfer.sh)](https://goreportcard.com/report/github.com/dutchcoders/transfer.sh) [![Docker pulls](https://img.shields.io/docker/pulls/dutchcoders/transfer.sh.svg)](https://hub.docker.com/r/dutchcoders/transfer.sh/) [![Build Status](https://travis-ci.com/dutchcoders/transfer.sh.svg?branch=master)](https://travis-ci.com/dutchcoders/transfer.sh) | |||
Easy and fast file sharing from the command-line. This code contains the server with everything you need to create your own instance. | |||
@@ -8,6 +8,8 @@ Transfer.sh currently supports the s3 (Amazon S3), gdrive (Google Drive) provide | |||
This project repository has no relation with the service at https://transfer.sh that's managed by https://storj.io. | |||
So far we cannot address any issue related to the service at https://transfer.sh. | |||
The service at https://transfersh.com is of unknown origin and reported as cloud malware. | |||
## Usage | |||
@@ -76,40 +78,49 @@ https://transfer.sh/1lDau/test.txt --> https://transfer.sh/inline/1lDau/test.txt | |||
Parameter | Description | Value | Env | |||
--- | --- | --- | --- | |||
listener | port to use for http (:80) | | | |||
profile-listener | port to use for profiler (:6060)| | | |||
force-https | redirect to https | false | | |||
tls-listener | port to use for https (:443) | | | |||
tls-listener-only | flag to enable tls listener only | | | |||
tls-cert-file | path to tls certificate | | | |||
tls-private-key | path to tls private key | | | |||
http-auth-user | user for basic http auth on upload | | | |||
http-auth-pass | pass for basic http auth on upload | | | |||
ip-whitelist | comma separated list of ips allowed to connect to the service | | | |||
ip-blacklist | comma separated list of ips not allowed to connect to the service | | | |||
temp-path | path to temp folder | system temp | | |||
web-path | path to static web files (for development or custom front end) | | | |||
proxy-path | path prefix when service is run behind a proxy | | | |||
ga-key | google analytics key for the front end | | | |||
uservoice-key | user voice key for the front end | | | |||
provider | which storage provider to use | (s3, gdrive or local) | | |||
aws-access-key | aws access key | | AWS_ACCESS_KEY | |||
aws-secret-key | aws access key | | AWS_SECRET_KEY | |||
bucket | aws bucket | | BUCKET | |||
s3-endpoint | Custom S3 endpoint. | | | |||
s3-region | region of the s3 bucket | eu-west-1 | S3_REGION | |||
s3-no-multipart | disables s3 multipart upload | false | | | |||
s3-path-style | Forces path style URLs, required for Minio. | false | | | |||
basedir | path storage for local/gdrive provider| | | |||
gdrive-client-json-filepath | path to oauth client json config for gdrive provider| | | |||
gdrive-local-config-path | path to store local transfer.sh config cache for gdrive provider| | | |||
gdrive-chunk-size | chunk size for gdrive upload in megabytes, must be lower than available memory (8 MB) | | | |||
lets-encrypt-hosts | hosts to use for lets encrypt certificates (comma seperated) | | | |||
log | path to log file| | | |||
listener | port to use for http (:80) | | LISTENER | | |||
profile-listener | port to use for profiler (:6060) | | PROFILE_LISTENER | | |||
force-https | redirect to https | false | FORCE_HTTPS | |||
tls-listener | port to use for https (:443) | | TLS_LISTENER | | |||
tls-listener-only | flag to enable tls listener only | | TLS_LISTENER_ONLY | | |||
tls-cert-file | path to tls certificate | | TLS_CERT_FILE | | |||
tls-private-key | path to tls private key | | TLS_PRIVATE_KEY | | |||
http-auth-user | user for basic http auth on upload | | HTTP_AUTH_USER | | |||
http-auth-pass | pass for basic http auth on upload | | HTTP_AUTH_PASS | | |||
ip-whitelist | comma separated list of ips allowed to connect to the service | | IP_WHITELIST | | |||
ip-blacklist | comma separated list of ips not allowed to connect to the service | | IP_BLACKLIST | | |||
temp-path | path to temp folder | system temp | TEMP_PATH | | |||
web-path | path to static web files (for development or custom front end) | | WEB_PATH | | |||
proxy-path | path prefix when service is run behind a proxy | | PROXY_PATH | | |||
proxy-port | port of the proxy when the service is run behind a proxy | | PROXY_PORT | | |||
ga-key | google analytics key for the front end | | GA_KEY | | |||
provider | which storage provider to use | (s3, storj, gdrive or local) | | |||
uservoice-key | user voice key for the front end | | USERVOICE_KEY | | |||
aws-access-key | aws access key | | AWS_ACCESS_KEY | | |||
aws-secret-key | aws access key | | AWS_SECRET_KEY | | |||
bucket | aws bucket | | BUCKET | | |||
s3-endpoint | Custom S3 endpoint. | | S3_ENDPOINT | | |||
s3-region | region of the s3 bucket | eu-west-1 | S3_REGION | | |||
s3-no-multipart | disables s3 multipart upload | false | S3_NO_MULTIPART | | |||
s3-path-style | Forces path style URLs, required for Minio. | false | S3_PATH_STYLE | | |||
storj-access | Access for the project | | STORJ_ACCESS | | |||
storj-bucket | Bucket to use within the project | | STORJ_BUCKET | | |||
basedir | path storage for local/gdrive provider | | BASEDIR | | |||
gdrive-client-json-filepath | path to oauth client json config for gdrive provider | | GDRIVE_CLIENT_JSON_FILEPATH | | |||
gdrive-local-config-path | path to store local transfer.sh config cache for gdrive provider| | GDRIVE_LOCAL_CONFIG_PATH | | |||
gdrive-chunk-size | chunk size for gdrive upload in megabytes, must be lower than available memory (8 MB) | | GDRIVE_CHUNK_SIZE | | |||
lets-encrypt-hosts | hosts to use for lets encrypt certificates (comma seperated) | | HOSTS | | |||
log | path to log file| | LOG | | |||
cors-domains | comma separated list of domains for CORS, setting it enable CORS | | CORS_DOMAINS | | |||
clamav-host | host for clamav feature | | CLAMAV_HOST | | |||
rate-limit | request per minute | | RATE_LIMIT | | |||
max-upload-size | max upload size in kilobytes | | MAX_UPLOAD_SIZE | | |||
purge-days | number of days after the uploads are purged automatically | | PURGE_DAYS | | |||
purge-interval | interval in hours to run the automatic purge for (not applicable to S3 and Storj) | | PURGE_INTERVAL | | |||
If you want to use TLS using lets encrypt certificates, set lets-encrypt-hosts to your domain, set tls-listener to :443 and enable force-https. | |||
If you want to use TLS using your own certificates, set tls-listener to :443, force-https, tls-cert=file and tls-private-key. | |||
If you want to use TLS using your own certificates, set tls-listener to :443, force-https, tls-cert-file and tls-private-key. | |||
## Development | |||
@@ -121,13 +132,10 @@ go run main.go --provider=local --listener :8080 --temp-path=/tmp/ --basedir=/tm | |||
## Build | |||
If on go < 1.11 | |||
```bash | |||
go get -u -v ./... | |||
``` | |||
```bash | |||
go build -o transfersh main.go | |||
$ git clone git@github.com:dutchcoders/transfer.sh.git | |||
$ cd transfer.sh | |||
$ go build -o transfersh main.go | |||
``` | |||
## Docker | |||
@@ -151,7 +159,35 @@ If you specify the s3-region, you don't need to set the endpoint URL since the c | |||
### Custom S3 providers | |||
To use a custom non-AWS S3 provider, you need to specify the endpoint as definied from your cloud provider. | |||
To use a custom non-AWS S3 provider, you need to specify the endpoint as defined from your cloud provider. | |||
## Storj Network Provider | |||
To use the Storj Network as storage provider you need to specify the following flags: | |||
- provider `--provider storj` | |||
- storj-access _(either via flag or environment variable STORJ_ACCESS)_ | |||
- storj-bucket _(either via flag or environment variable STORJ_BUCKET)_ | |||
### Creating Bucket and Scope | |||
In preparation you need to create a scope (or copy it from the uplink configuration) and a bucket. | |||
To get started, download the latest uplink from the release page: https://github.com/storj/storj/releases | |||
After extracting, execute `uplink setup`. The Wizard asks for Satellite to use, the API Key | |||
(which you can retrieve via the Satellite UI), as well as an Encryption Key. | |||
Once the uplink is setup create the bucket using the following schema: | |||
`uplink mb sj://<BUCKET>` where <BUCKET> is your desired name. | |||
Afterwards you can copy the SCOPE out of the configuration file of the uplink and then start the startup of the | |||
transfer.sh endpoint. For enhanced security its recommended to provide both the scope and the bucket name as ENV Variables. | |||
Example: | |||
``` | |||
export STORJ_BUCKET=transfersh | |||
export STORJ_ACCESS=<SCOPE> | |||
transfer.sh --provider storj | |||
``` | |||
## Google Drive Usage | |||
@@ -12,7 +12,7 @@ import ( | |||
"google.golang.org/api/googleapi" | |||
) | |||
var Version = "1.1.2" | |||
var Version = "1.2.1" | |||
var helpTemplate = `NAME: | |||
{{.Name}} - {{.Usage}} | |||
@@ -34,67 +34,86 @@ VERSION: | |||
var globalFlags = []cli.Flag{ | |||
cli.StringFlag{ | |||
Name: "listener", | |||
Usage: "127.0.0.1:8080", | |||
Value: "127.0.0.1:8080", | |||
Name: "listener", | |||
Usage: "127.0.0.1:8080", | |||
Value: "127.0.0.1:8080", | |||
EnvVar: "LISTENER", | |||
}, | |||
// redirect to https? | |||
// hostnames | |||
cli.StringFlag{ | |||
Name: "profile-listener", | |||
Usage: "127.0.0.1:6060", | |||
Value: "", | |||
Name: "profile-listener", | |||
Usage: "127.0.0.1:6060", | |||
Value: "", | |||
EnvVar: "PROFILE_LISTENER", | |||
}, | |||
cli.BoolFlag{ | |||
Name: "force-https", | |||
Usage: "", | |||
Name: "force-https", | |||
Usage: "", | |||
EnvVar: "FORCE_HTTPS", | |||
}, | |||
cli.StringFlag{ | |||
Name: "tls-listener", | |||
Usage: "127.0.0.1:8443", | |||
Value: "", | |||
Name: "tls-listener", | |||
Usage: "127.0.0.1:8443", | |||
Value: "", | |||
EnvVar: "TLS_LISTENER", | |||
}, | |||
cli.BoolFlag{ | |||
Name: "tls-listener-only", | |||
Usage: "", | |||
Name: "tls-listener-only", | |||
Usage: "", | |||
EnvVar: "TLS_LISTENER_ONLY", | |||
}, | |||
cli.StringFlag{ | |||
Name: "tls-cert-file", | |||
Value: "", | |||
EnvVar: "TLS_CERT_FILE", | |||
}, | |||
cli.StringFlag{ | |||
Name: "tls-cert-file", | |||
Value: "", | |||
Name: "tls-private-key", | |||
Value: "", | |||
EnvVar: "TLS_PRIVATE_KEY", | |||
}, | |||
cli.StringFlag{ | |||
Name: "tls-private-key", | |||
Value: "", | |||
Name: "temp-path", | |||
Usage: "path to temp files", | |||
Value: os.TempDir(), | |||
EnvVar: "TEMP_PATH", | |||
}, | |||
cli.StringFlag{ | |||
Name: "temp-path", | |||
Usage: "path to temp files", | |||
Value: os.TempDir(), | |||
Name: "web-path", | |||
Usage: "path to static web files", | |||
Value: "", | |||
EnvVar: "WEB_PATH", | |||
}, | |||
cli.StringFlag{ | |||
Name: "web-path", | |||
Usage: "path to static web files", | |||
Value: "", | |||
Name: "proxy-path", | |||
Usage: "path prefix when service is run behind a proxy", | |||
Value: "", | |||
EnvVar: "PROXY_PATH", | |||
}, | |||
cli.StringFlag{ | |||
Name: "proxy-path", | |||
Usage: "path prefix when service is run behind a proxy", | |||
Value: "", | |||
Name: "proxy-port", | |||
Usage: "port of the proxy when the service is run behind a proxy", | |||
Value: "", | |||
EnvVar: "PROXY_PORT", | |||
}, | |||
cli.StringFlag{ | |||
Name: "ga-key", | |||
Usage: "key for google analytics (front end)", | |||
Value: "", | |||
Name: "ga-key", | |||
Usage: "key for google analytics (front end)", | |||
Value: "", | |||
EnvVar: "GA_KEY", | |||
}, | |||
cli.StringFlag{ | |||
Name: "uservoice-key", | |||
Usage: "key for user voice (front end)", | |||
Value: "", | |||
Name: "uservoice-key", | |||
Usage: "key for user voice (front end)", | |||
Value: "", | |||
EnvVar: "USERVOICE_KEY", | |||
}, | |||
cli.StringFlag{ | |||
Name: "provider", | |||
Usage: "s3|gdrive|local", | |||
Value: "", | |||
Name: "provider", | |||
Usage: "s3|gdrive|local", | |||
Value: "", | |||
EnvVar: "PROVIDER", | |||
}, | |||
cli.StringFlag{ | |||
Name: "s3-endpoint", | |||
@@ -127,33 +146,68 @@ var globalFlags = []cli.Flag{ | |||
EnvVar: "BUCKET", | |||
}, | |||
cli.BoolFlag{ | |||
Name: "s3-no-multipart", | |||
Usage: "Disables S3 Multipart Puts", | |||
Name: "s3-no-multipart", | |||
Usage: "Disables S3 Multipart Puts", | |||
EnvVar: "S3_NO_MULTIPART", | |||
}, | |||
cli.BoolFlag{ | |||
Name: "s3-path-style", | |||
Usage: "Forces path style URLs, required for Minio.", | |||
Name: "s3-path-style", | |||
Usage: "Forces path style URLs, required for Minio.", | |||
EnvVar: "S3_PATH_STYLE", | |||
}, | |||
cli.StringFlag{ | |||
Name: "gdrive-client-json-filepath", | |||
Usage: "", | |||
Value: "", | |||
Name: "gdrive-client-json-filepath", | |||
Usage: "", | |||
Value: "", | |||
EnvVar: "GDRIVE_CLIENT_JSON_FILEPATH", | |||
}, | |||
cli.StringFlag{ | |||
Name: "gdrive-local-config-path", | |||
Usage: "", | |||
Value: "", | |||
Name: "gdrive-local-config-path", | |||
Usage: "", | |||
Value: "", | |||
EnvVar: "GDRIVE_LOCAL_CONFIG_PATH", | |||
}, | |||
cli.IntFlag{ | |||
Name: "gdrive-chunk-size", | |||
Usage: "", | |||
Value: googleapi.DefaultUploadChunkSize / 1024 / 1024, | |||
Name: "gdrive-chunk-size", | |||
Usage: "", | |||
Value: googleapi.DefaultUploadChunkSize / 1024 / 1024, | |||
EnvVar: "GDRIVE_CHUNK_SIZE", | |||
}, | |||
cli.StringFlag{ | |||
Name: "storj-access", | |||
Usage: "Access for the project", | |||
Value: "", | |||
EnvVar: "STORJ_ACCESS", | |||
}, | |||
cli.StringFlag{ | |||
Name: "storj-bucket", | |||
Usage: "Bucket to use within the project", | |||
Value: "", | |||
EnvVar: "STORJ_BUCKET", | |||
}, | |||
cli.IntFlag{ | |||
Name: "rate-limit", | |||
Usage: "requests per minute", | |||
Value: 0, | |||
EnvVar: "", | |||
EnvVar: "RATE_LIMIT", | |||
}, | |||
cli.IntFlag{ | |||
Name: "purge-days", | |||
Usage: "number of days after uploads are purged automatically", | |||
Value: 0, | |||
EnvVar: "PURGE_DAYS", | |||
}, | |||
cli.IntFlag{ | |||
Name: "purge-interval", | |||
Usage: "interval in hours to run the automatic purge for", | |||
Value: 0, | |||
EnvVar: "PURGE_INTERVAL", | |||
}, | |||
cli.Int64Flag{ | |||
Name: "max-upload-size", | |||
Usage: "max limit for upload, in kilobytes", | |||
Value: 0, | |||
EnvVar: "MAX_UPLOAD_SIZE", | |||
}, | |||
cli.StringFlag{ | |||
Name: "lets-encrypt-hosts", | |||
@@ -162,14 +216,16 @@ var globalFlags = []cli.Flag{ | |||
EnvVar: "HOSTS", | |||
}, | |||
cli.StringFlag{ | |||
Name: "log", | |||
Usage: "/var/log/transfersh.log", | |||
Value: "", | |||
Name: "log", | |||
Usage: "/var/log/transfersh.log", | |||
Value: "", | |||
EnvVar: "LOG", | |||
}, | |||
cli.StringFlag{ | |||
Name: "basedir", | |||
Usage: "path to storage", | |||
Value: "", | |||
Name: "basedir", | |||
Usage: "path to storage", | |||
Value: "", | |||
EnvVar: "BASEDIR", | |||
}, | |||
cli.StringFlag{ | |||
Name: "clamav-host", | |||
@@ -184,28 +240,39 @@ var globalFlags = []cli.Flag{ | |||
EnvVar: "VIRUSTOTAL_KEY", | |||
}, | |||
cli.BoolFlag{ | |||
Name: "profiler", | |||
Usage: "enable profiling", | |||
Name: "profiler", | |||
Usage: "enable profiling", | |||
EnvVar: "PROFILER", | |||
}, | |||
cli.StringFlag{ | |||
Name: "http-auth-user", | |||
Usage: "user for http basic auth", | |||
Value: "", | |||
Name: "http-auth-user", | |||
Usage: "user for http basic auth", | |||
Value: "", | |||
EnvVar: "HTTP_AUTH_USER", | |||
}, | |||
cli.StringFlag{ | |||
Name: "http-auth-pass", | |||
Usage: "pass for http basic auth", | |||
Value: "", | |||
Name: "http-auth-pass", | |||
Usage: "pass for http basic auth", | |||
Value: "", | |||
EnvVar: "HTTP_AUTH_PASS", | |||
}, | |||
cli.StringFlag{ | |||
Name: "ip-whitelist", | |||
Usage: "comma separated list of ips allowed to connect to the service", | |||
Value: "", | |||
Name: "ip-whitelist", | |||
Usage: "comma separated list of ips allowed to connect to the service", | |||
Value: "", | |||
EnvVar: "IP_WHITELIST", | |||
}, | |||
cli.StringFlag{ | |||
Name: "ip-blacklist", | |||
Usage: "comma separated list of ips not allowed to connect to the service", | |||
Value: "", | |||
Name: "ip-blacklist", | |||
Usage: "comma separated list of ips not allowed to connect to the service", | |||
Value: "", | |||
EnvVar: "IP_BLACKLIST", | |||
}, | |||
cli.StringFlag{ | |||
Name: "cors-domains", | |||
Usage: "comma separated list of domains allowed for CORS requests", | |||
Value: "", | |||
EnvVar: "CORS_DOMAINS", | |||
}, | |||
} | |||
@@ -214,7 +281,7 @@ type Cmd struct { | |||
} | |||
func VersionAction(c *cli.Context) { | |||
fmt.Println(color.YellowString(fmt.Sprintf("transfer.sh: Easy file sharing from the command line"))) | |||
fmt.Println(color.YellowString(fmt.Sprintf("transfer.sh %s: Easy file sharing from the command line", Version))) | |||
} | |||
func New() *Cmd { | |||
@@ -245,6 +312,10 @@ func New() *Cmd { | |||
options = append(options, server.Listener(v)) | |||
} | |||
if v := c.String("cors-domains"); v != "" { | |||
options = append(options, server.CorsDomains(v)) | |||
} | |||
if v := c.String("tls-listener"); v == "" { | |||
} else if c.Bool("tls-listener-only") { | |||
options = append(options, server.TLSListener(v, true)) | |||
@@ -264,6 +335,10 @@ func New() *Cmd { | |||
options = append(options, server.ProxyPath(v)) | |||
} | |||
if v := c.String("proxy-port"); v != "" { | |||
options = append(options, server.ProxyPort(v)) | |||
} | |||
if v := c.String("ga-key"); v != "" { | |||
options = append(options, server.GoogleAnalytics(v)) | |||
} | |||
@@ -294,10 +369,20 @@ func New() *Cmd { | |||
options = append(options, server.ClamavHost(v)) | |||
} | |||
if v := c.Int64("max-upload-size"); v > 0 { | |||
options = append(options, server.MaxUploadSize(v)) | |||
} | |||
if v := c.Int("rate-limit"); v > 0 { | |||
options = append(options, server.RateLimit(v)) | |||
} | |||
purgeDays := c.Int("purge-days") | |||
purgeInterval := c.Int("purge-interval") | |||
if purgeDays > 0 && purgeInterval > 0 { | |||
options = append(options, server.Purge(purgeDays, purgeInterval)) | |||
} | |||
if cert := c.String("tls-cert-file"); cert == "" { | |||
} else if pk := c.String("tls-private-key"); pk == "" { | |||
} else { | |||
@@ -343,7 +428,7 @@ func New() *Cmd { | |||
panic("secret-key not set.") | |||
} else if bucket := c.String("bucket"); bucket == "" { | |||
panic("bucket not set.") | |||
} else if storage, err := server.NewS3Storage(accessKey, secretKey, bucket, c.String("s3-region"), c.String("s3-endpoint"), logger, c.Bool("s3-no-multipart"), c.Bool("s3-path-style")); err != nil { | |||
} else if storage, err := server.NewS3Storage(accessKey, secretKey, bucket, purgeDays, c.String("s3-region"), c.String("s3-endpoint"), c.Bool("s3-no-multipart"), c.Bool("s3-path-style"), logger); err != nil { | |||
panic(err) | |||
} else { | |||
options = append(options, server.UseStorage(storage)) | |||
@@ -362,6 +447,16 @@ func New() *Cmd { | |||
} else { | |||
options = append(options, server.UseStorage(storage)) | |||
} | |||
case "storj": | |||
if access := c.String("storj-access"); access == "" { | |||
panic("storj-access not set.") | |||
} else if bucket := c.String("storj-bucket"); bucket == "" { | |||
panic("storj-bucket not set.") | |||
} else if storage, err := server.NewStorjStorage(access, bucket, purgeDays, logger); err != nil { | |||
panic(err) | |||
} else { | |||
options = append(options, server.UseStorage(storage)) | |||
} | |||
case "local": | |||
if v := c.String("basedir"); v == "" { | |||
panic("basedir not set.") | |||
@@ -1,33 +0,0 @@ | |||
#!/bin/bash | |||
set -xe | |||
# Validate arguments | |||
if [ "$#" -ne 1 ]; then | |||
echo "Usage: $0 <fuzz-type>" | |||
exit 1 | |||
fi | |||
# Configure | |||
NAME=transfersh | |||
ROOT=./server | |||
TYPE=$1 | |||
# Setup | |||
export GOFUZZ111MODULE="on" | |||
go get -u github.com/dvyukov/go-fuzz/go-fuzz github.com/dvyukov/go-fuzz/go-fuzz-build | |||
go get -d -v -u ./... | |||
if [ ! -f fuzzit ]; then | |||
wget -q -O fuzzit https://github.com/fuzzitdev/fuzzit/releases/download/v2.4.72/fuzzit_Linux_x86_64 | |||
chmod a+x fuzzit | |||
fi | |||
# Fuzz | |||
function fuzz { | |||
FUNC=Fuzz$1 | |||
TARGET=$2 | |||
DIR=${3:-$ROOT} | |||
go-fuzz-build -libfuzzer -func $FUNC -o fuzzer.a $DIR | |||
clang -fsanitize=fuzzer fuzzer.a -o fuzzer | |||
./fuzzit create job --type $TYPE $NAME/$TARGET fuzzer | |||
} | |||
fuzz LocalStorage local-storage |
@@ -3,39 +3,36 @@ module github.com/dutchcoders/transfer.sh | |||
go 1.12 | |||
require ( | |||
cloud.google.com/go v0.46.3 // indirect | |||
cloud.google.com/go v0.54.0 // indirect | |||
github.com/PuerkitoBio/ghost v0.0.0-20160324114900-206e6e460e14 | |||
github.com/VojtechVitek/ratelimit v0.0.0-20160722140851-dc172bc0f6d2 | |||
github.com/aws/aws-sdk-go v1.25.7 | |||
github.com/aws/aws-sdk-go v1.29.24 | |||
github.com/cpuguy83/go-md2man/v2 v2.0.0 // indirect | |||
github.com/dutchcoders/go-clamd v0.0.0-20170520113014-b970184f4d9e | |||
github.com/dutchcoders/go-virustotal v0.0.0-20140923143438-24cc8e6fa329 | |||
github.com/dutchcoders/transfer.sh-web v0.0.0-20190716184912-96e06a2276ba | |||
github.com/elazarl/go-bindata-assetfs v1.0.0 | |||
github.com/fatih/color v1.7.0 | |||
github.com/fatih/color v1.9.0 | |||
github.com/garyburd/redigo v1.6.0 // indirect | |||
github.com/golang/gddo v0.0.0-20190904175337-72a348e765d2 | |||
github.com/golang/groupcache v0.0.0-20191002201903-404acd9df4cc // indirect | |||
github.com/gorilla/mux v1.7.3 | |||
github.com/golang/gddo v0.0.0-20200310004957-95ce5a452273 | |||
github.com/golang/protobuf v1.3.5 // indirect | |||
github.com/gorilla/handlers v1.4.2 | |||
github.com/gorilla/mux v1.7.4 | |||
github.com/gorilla/securecookie v1.1.1 // indirect | |||
github.com/hashicorp/golang-lru v0.5.3 // indirect | |||
github.com/mattn/go-colorable v0.1.4 // indirect | |||
github.com/mattn/go-isatty v0.0.9 // indirect | |||
github.com/jmespath/go-jmespath v0.3.0 // indirect | |||
github.com/mattn/go-colorable v0.1.6 // indirect | |||
github.com/microcosm-cc/bluemonday v1.0.2 | |||
github.com/nu7hatch/gouuid v0.0.0-20131221200532-179d4d0c4d8d // indirect | |||
github.com/russross/blackfriday/v2 v2.0.1 | |||
github.com/skip2/go-qrcode v0.0.0-20190110000554-dc11ecdae0a9 | |||
github.com/stretchr/testify v1.4.0 // indirect | |||
github.com/skip2/go-qrcode v0.0.0-20191027152451-9434209cb086 | |||
github.com/tomasen/realip v0.0.0-20180522021738-f0c99a92ddce | |||
github.com/urfave/cli v1.22.1 | |||
go.opencensus.io v0.22.1 // indirect | |||
golang.org/x/crypto v0.0.0-20191002192127-34f69633bfdc | |||
golang.org/x/net v0.0.0-20191007182048-72f939374954 | |||
golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45 | |||
golang.org/x/sys v0.0.0-20191007154456-ef33b2fb2c41 // indirect | |||
google.golang.org/api v0.10.0 | |||
google.golang.org/appengine v1.6.4 // indirect | |||
google.golang.org/genproto v0.0.0-20191007162740-aa923e3a3354 // indirect | |||
google.golang.org/grpc v1.24.0 // indirect | |||
github.com/urfave/cli v1.22.3 | |||
golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9 | |||
golang.org/x/net v0.0.0-20200301022130-244492dfa37a | |||
golang.org/x/oauth2 v0.0.0-20200107190931-bf48bf16ab8d | |||
google.golang.org/api v0.20.0 | |||
google.golang.org/grpc v1.28.0 // indirect | |||
gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15 | |||
storj.io/common v0.0.0-20201207172416-78f4e59925c3 | |||
storj.io/uplink v1.4.2 | |||
) |
@@ -1,3 +1,4 @@ | |||
cloud.google.com/go v0.16.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= | |||
cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= | |||
cloud.google.com/go v0.34.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= | |||
cloud.google.com/go v0.38.0 h1:ROfEUZz+Gh5pa62DJWXSaonyu3StP6EA6lPEXPI6mCo= | |||
@@ -7,26 +8,62 @@ cloud.google.com/go v0.44.2/go.mod h1:60680Gw3Yr4ikxnPRS/oxxkBccT6SA1yMk63TGekxK | |||
cloud.google.com/go v0.45.1/go.mod h1:RpBamKRgapWJb87xiFSdk4g1CME7QZg3uwTez+TSTjc= | |||
cloud.google.com/go v0.46.3 h1:AVXDdKsrtX33oR9fbCMu/+c1o8Ofjq6Ku/MInaLVg5Y= | |||
cloud.google.com/go v0.46.3/go.mod h1:a6bKKbmY7er1mI7TEI4lsAkts/mkhTSZK8w33B4RAg0= | |||
cloud.google.com/go v0.50.0/go.mod h1:r9sluTvynVuxRIOHXQEHMFffphuXHOMZMycpNR5e6To= | |||
cloud.google.com/go v0.52.0/go.mod h1:pXajvRH/6o3+F9jDHZWQ5PbGhn+o8w9qiu/CffaVdO4= | |||
cloud.google.com/go v0.53.0/go.mod h1:fp/UouUEsRkN6ryDKNW/Upv/JBKnv6WDthjR6+vze6M= | |||
cloud.google.com/go v0.54.0 h1:3ithwDMr7/3vpAMXiH+ZQnYbuIsh+OPhUPMFC9enmn0= | |||
cloud.google.com/go v0.54.0/go.mod h1:1rq2OEkV3YMf6n/9ZvGWI3GWw0VoqH/1x2nd8Is/bPc= | |||
cloud.google.com/go/bigquery v1.0.1/go.mod h1:i/xbL2UlR5RvWAURpBYZTtm/cXjCha9lbfbpx4poX+o= | |||
cloud.google.com/go/bigquery v1.3.0/go.mod h1:PjpwJnslEMmckchkHFfq+HTD2DmtT67aNFKH1/VBDHE= | |||
cloud.google.com/go/bigquery v1.4.0/go.mod h1:S8dzgnTigyfTmLBfrtrhyYhwRxG72rYxvftPBK2Dvzc= | |||
cloud.google.com/go/datastore v1.0.0/go.mod h1:LXYbyblFSglQ5pkeyhO+Qmw7ukd3C+pD7TKLgZqpHYE= | |||
cloud.google.com/go/datastore v1.1.0/go.mod h1:umbIZjpQpHh4hmRpGhH4tLFup+FVzqBi1b3c64qFpCk= | |||
cloud.google.com/go/pubsub v1.0.1/go.mod h1:R0Gpsv3s54REJCy4fxDixWD93lHJMoZTyQ2kNxGRt3I= | |||
cloud.google.com/go/pubsub v1.1.0/go.mod h1:EwwdRX2sKPjnvnqCa270oGRyludottCI76h+R3AArQw= | |||
cloud.google.com/go/pubsub v1.2.0/go.mod h1:jhfEVHT8odbXTkndysNHCcx0awwzvfOlguIAii9o8iA= | |||
cloud.google.com/go/storage v1.0.0/go.mod h1:IhtSnM/ZTZV8YYJWCY8RULGVqBDmpoyjwiyrjsg+URw= | |||
cloud.google.com/go/storage v1.5.0/go.mod h1:tpKbwo567HUNpVclU5sGELwQWBDZ8gh0ZeosJ0Rtdos= | |||
cloud.google.com/go/storage v1.6.0/go.mod h1:N7U0C8pVQ/+NIKOBQyamJIeKQKkZ+mxpohlUTyfDhBk= | |||
dmitri.shuralyov.com/gpu/mtl v0.0.0-20190408044501-666a987793e9/go.mod h1:H6x//7gZCb22OMCxBHrMx7a5I7Hp++hsVxbQ4BYO7hU= | |||
github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= | |||
github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym/WlBOVXweHU+Q+/VP0lqqI8lqeDx9IjBqo= | |||
github.com/PuerkitoBio/ghost v0.0.0-20160324114900-206e6e460e14 h1:3zOOc7WdrATDXof+h/rBgMsg0sAmZIEVHft1UbWHh94= | |||
github.com/PuerkitoBio/ghost v0.0.0-20160324114900-206e6e460e14/go.mod h1:+VFiaivV54Sa94ijzA/ZHQLoHuoUIS9hIqCK6f/76Zw= | |||
github.com/VojtechVitek/ratelimit v0.0.0-20160722140851-dc172bc0f6d2 h1:sIvihcW4qpN5qGSjmrsDDAbLpEq5tuHjJJfWY0Hud5Y= | |||
github.com/VojtechVitek/ratelimit v0.0.0-20160722140851-dc172bc0f6d2/go.mod h1:3YwJE8rEisS9eraee0hygGG4G3gqX8H8Nyu+nPTUnGU= | |||
github.com/aws/aws-sdk-go v1.23.8 h1:G/azJoBN0pnhB3B+0eeC4yyVFYIIad6bbzg6wwtImqk= | |||
github.com/aws/aws-sdk-go v1.23.8/go.mod h1:KmX6BPdI08NWTb3/sm4ZGu5ShLoqVDhKgpiN924inxo= | |||
github.com/aws/aws-sdk-go v1.25.7 h1:MRnnec09yF/nL/lfpMsYqHHyXUUt4P9LofFZA2D93PE= | |||
github.com/aws/aws-sdk-go v1.25.7/go.mod h1:KmX6BPdI08NWTb3/sm4ZGu5ShLoqVDhKgpiN924inxo= | |||
github.com/aead/siphash v1.0.1/go.mod h1:Nywa3cDsYNNK3gaciGTWPwHt0wlpNV15vwmswBAUSII= | |||
github.com/aws/aws-sdk-go v1.29.24 h1:KOnds/LwADMDBaALL4UB98ZR+TUR1A1mYmAYbdLixLA= | |||
github.com/aws/aws-sdk-go v1.29.24/go.mod h1:1KvfttTE3SPKMpo8g2c6jL3ZKfXtFvKscTgahTma5Xg= | |||
github.com/bradfitz/gomemcache v0.0.0-20170208213004-1952afaa557d/go.mod h1:PmM6Mmwb0LSuEubjR8N7PtNe1KxZLtOUHtbeikc5h60= | |||
github.com/btcsuite/btcd v0.20.1-beta/go.mod h1:wVuoA8VJLEcwgqHBwHmzLRazpKxTv13Px/pDuV7OomQ= | |||
github.com/btcsuite/btclog v0.0.0-20170628155309-84c8d2346e9f/go.mod h1:TdznJufoqS23FtqVCzL0ZqgP5MqXbb4fg/WgDys70nA= | |||
github.com/btcsuite/btcutil v0.0.0-20190425235716-9e5f4b9a998d/go.mod h1:+5NJ2+qvTyV9exUAL/rxXi3DcLg2Ts+ymUAY5y4NvMg= | |||
github.com/btcsuite/btcutil v1.0.3-0.20201124182144-4031bdc69ded h1:WcPFZzCIqGt/TdFJHsOiX5dIlB/MUzrftltMhpjzfA8= | |||
github.com/btcsuite/btcutil v1.0.3-0.20201124182144-4031bdc69ded/go.mod h1:0DVlHczLPewLcPGEIeUEzfOJhqGPQ0mJJRDBtD307+o= | |||
github.com/btcsuite/go-socks v0.0.0-20170105172521-4720035b7bfd/go.mod h1:HHNXQzUsZCxOoE+CPiyCTO6x34Zs86zZUiwtpXoGdtg= | |||
github.com/btcsuite/goleveldb v0.0.0-20160330041536-7834afc9e8cd/go.mod h1:F+uVaaLLH7j4eDXPRvw78tMflu7Ie2bzYOH4Y8rRKBY= | |||
github.com/btcsuite/snappy-go v0.0.0-20151229074030-0bdef8d06723/go.mod h1:8woku9dyThutzjeg+3xrA5iCpBRH8XEEg3lh6TiUghc= | |||
github.com/btcsuite/websocket v0.0.0-20150119174127-31079b680792/go.mod h1:ghJtEyQwv5/p4Mg4C0fgbePVuGr935/5ddU9Z3TmDRY= | |||
github.com/btcsuite/winsvc v1.0.0/go.mod h1:jsenWakMcC0zFBFurPLEAyrnc/teJEM1O46fmI40EZs= | |||
github.com/calebcase/tmpfile v1.0.2-0.20200602150926-3af473ef8439 h1:fqGdSbbWVbQqNCQXd/dyZ7Bl+u8R2X7QCiNzwgyUa/M= | |||
github.com/calebcase/tmpfile v1.0.2-0.20200602150926-3af473ef8439/go.mod h1:iErLeG/iqJr8LaQ/gYRv4GXdqssi3jg4iSzvrA06/lw= | |||
github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU= | |||
github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU= | |||
github.com/chzyer/logex v1.1.10/go.mod h1:+Ywpsq7O8HXn0nuIou7OrIPyXbp3wmkHB+jjWRnGsAI= | |||
github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e/go.mod h1:nSuG5e5PlCu98SY8svDHJxuZscDgtXS6KTTbou5AhLI= | |||
github.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1/go.mod h1:Q3SI9o4m/ZMnBNeIyt5eFwwo7qiLfzFZmjNmxjkiQlU= | |||
github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw= | |||
github.com/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f/go.mod h1:M8M6+tZqaGXZJjfX53e64911xZQV5JYwmTeXPW+k8Sc= | |||
github.com/cpuguy83/go-md2man/v2 v2.0.0-20190314233015-f79a8a8ca69d h1:U+s90UTSYgptZMwQh2aRr3LuazLJIa+Pg3Kc1ylSYVY= | |||
github.com/cpuguy83/go-md2man/v2 v2.0.0-20190314233015-f79a8a8ca69d/go.mod h1:maD7wRr/U5Z6m/iR4s+kqSMx2CaBsrgA7czyZG/E6dU= | |||
github.com/cpuguy83/go-md2man/v2 v2.0.0 h1:EoUDS0afbrsXAZ9YQ9jdu/mZ2sXgT1/2yyNng4PGlyM= | |||
github.com/cpuguy83/go-md2man/v2 v2.0.0/go.mod h1:maD7wRr/U5Z6m/iR4s+kqSMx2CaBsrgA7czyZG/E6dU= | |||
github.com/davecgh/go-spew v0.0.0-20171005155431-ecdeabc65495/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= | |||
github.com/davecgh/go-spew v1.1.0 h1:ZDRjVQ15GmhC3fiQ8ni8+OwkZQO4DARzQgrnXU1Liz8= | |||
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= | |||
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= | |||
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= | |||
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= | |||
github.com/dutchcoders/go-clamd v0.0.0-20170520113014-b970184f4d9e h1:rcHHSQqzCgvlwP0I/fQ8rQMn/MpHE5gWSLdtpxtP6KQ= | |||
github.com/dutchcoders/go-clamd v0.0.0-20170520113014-b970184f4d9e/go.mod h1:Byz7q8MSzSPkouskHJhX0er2mZY/m0Vj5bMeMCkkyY4= | |||
github.com/dutchcoders/go-virustotal v0.0.0-20140923143438-24cc8e6fa329 h1:ERqCkG/uSyT74P1m/j9yR+so+7ynY4fbTvLY/Mr1ZMg= | |||
@@ -35,102 +72,197 @@ github.com/dutchcoders/transfer.sh-web v0.0.0-20190716184912-96e06a2276ba h1:474 | |||
github.com/dutchcoders/transfer.sh-web v0.0.0-20190716184912-96e06a2276ba/go.mod h1:UjR1zlrq/R2Sef7e4q3TeJm4HcbLh4vRzlCEGJP+wLg= | |||
github.com/elazarl/go-bindata-assetfs v1.0.0 h1:G/bYguwHIzWq9ZoyUQqrjTmJbbYn3j3CKKpKinvZLFk= | |||
github.com/elazarl/go-bindata-assetfs v1.0.0/go.mod h1:v+YaWX3bdea5J/mo8dSETolEo7R71Vk1u8bnjau5yw4= | |||
github.com/fatih/color v1.7.0 h1:DkWD4oS2D8LGGgTQ6IvwJJXSL5Vp2ffcQg58nFV38Ys= | |||
github.com/fatih/color v1.7.0/go.mod h1:Zm6kSWBoL9eyXnKyktHP6abPY2pDugNf5KwzbycvMj4= | |||
github.com/envoyproxy/go-control-plane v0.9.0/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= | |||
github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= | |||
github.com/envoyproxy/go-control-plane v0.9.4/go.mod h1:6rpuAdCZL397s3pYoYcLgu1mIlRU8Am5FuJP05cCM98= | |||
github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c= | |||
github.com/fatih/color v1.9.0 h1:8xPHl4/q1VyqGIPif1F+1V3Y3lSmrq01EabUW3CoW5s= | |||
github.com/fatih/color v1.9.0/go.mod h1:eQcE1qtQxscV5RaZvpXrrb8Drkc3/DdQ+uUYCNjL+zU= | |||
github.com/fsnotify/fsnotify v1.4.3-0.20170329110642-4da3e2cfbabc/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo= | |||
github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo= | |||
github.com/garyburd/redigo v1.1.1-0.20170914051019-70e1b1943d4f/go.mod h1:NR3MbYisc3/PwhQ00EMzDiPmrwpPxAn5GI05/YaO1SY= | |||
github.com/garyburd/redigo v1.6.0 h1:0VruCpn7yAIIu7pWVClQC8wxCJEcG3nyzpMSHKi1PQc= | |||
github.com/garyburd/redigo v1.6.0/go.mod h1:NR3MbYisc3/PwhQ00EMzDiPmrwpPxAn5GI05/YaO1SY= | |||
github.com/golang/gddo v0.0.0-20190815223733-287de01127ef h1:4NNI5xhPnmBogD0yj/BV20wSHOg+7YcxX+JyX1tGVn8= | |||
github.com/golang/gddo v0.0.0-20190815223733-287de01127ef/go.mod h1:xEhNfoBDX1hzLm2Nf80qUvZ2sVwoMZ8d6IE2SrsQfh4= | |||
github.com/golang/gddo v0.0.0-20190904175337-72a348e765d2 h1:xisWqjiKEff2B0KfFYGpCqc3M3zdTz+OHQHRc09FeYk= | |||
github.com/golang/gddo v0.0.0-20190904175337-72a348e765d2/go.mod h1:xEhNfoBDX1hzLm2Nf80qUvZ2sVwoMZ8d6IE2SrsQfh4= | |||
github.com/go-gl/glfw v0.0.0-20190409004039-e6da0acd62b1/go.mod h1:vR7hzQXu2zJy9AVAgeJqvqgH9Q5CA+iKCZ2gyEVpxRU= | |||
github.com/go-gl/glfw/v3.3/glfw v0.0.0-20191125211704-12ad95a8df72/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8= | |||
github.com/go-gl/glfw/v3.3/glfw v0.0.0-20200222043503-6f7a984d4dc4/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8= | |||
github.com/go-sql-driver/mysql v1.5.0/go.mod h1:DCzpHaOWr8IXmIStZouvnhqoel9Qv2LBy8hT2VhHyBg= | |||
github.com/go-stack/stack v1.6.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY= | |||
github.com/gogo/protobuf v1.2.1 h1:/s5zKNz0uPFCZ5hddgPdo2TK2TVrUNMn0OOX8/aZMTE= | |||
github.com/gogo/protobuf v1.2.1/go.mod h1:hp+jE20tsWTFYpLwKvXlhS1hjn+gTNwPg2I6zVXpSg4= | |||
github.com/golang/gddo v0.0.0-20200310004957-95ce5a452273 h1:fHbEOLXdSMRJrzJwQkKixEg3Uq2RIcNQF0Y7iiQ+gRk= | |||
github.com/golang/gddo v0.0.0-20200310004957-95ce5a452273/go.mod h1:sam69Hju0uq+5uvLJUMDlsKlQ21Vrs1Kd/1YFPNYdOU= | |||
github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b h1:VKtxabqXZkF25pY9ekfRL6a582T4P37/31XEstQ5p58= | |||
github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q= | |||
github.com/golang/groupcache v0.0.0-20190702054246-869f871628b6 h1:ZgQEtGgCBiWRM39fZuwSd1LwSqqSW0hOdXCYYDX0R3I= | |||
github.com/golang/groupcache v0.0.0-20190702054246-869f871628b6/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= | |||
github.com/golang/groupcache v0.0.0-20191002201903-404acd9df4cc h1:55rEp52jU6bkyslZ1+C/7NGfpQsEc6pxGLAGDOctqbw= | |||
github.com/golang/groupcache v0.0.0-20191002201903-404acd9df4cc/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= | |||
github.com/golang/groupcache v0.0.0-20191227052852-215e87163ea7/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= | |||
github.com/golang/groupcache v0.0.0-20200121045136-8c9f03a8e57e h1:1r7pUrabqp18hOBcwBwiTsbnFeTZHV9eER/QT5JVZxY= | |||
github.com/golang/groupcache v0.0.0-20200121045136-8c9f03a8e57e/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= | |||
github.com/golang/lint v0.0.0-20170918230701-e5d664eb928e/go.mod h1:tluoj9z5200jBnyusfRPU2LqT6J+DAorxEvtC7LHB+E= | |||
github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A= | |||
github.com/golang/mock v1.2.0/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A= | |||
github.com/golang/mock v1.3.1/go.mod h1:sBzyDLLjw3U8JLTeZvSv8jJB+tU5PVekmnlKIyFUx0Y= | |||
github.com/golang/mock v1.4.0/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt3cw= | |||
github.com/golang/mock v1.4.1/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt3cw= | |||
github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= | |||
github.com/golang/protobuf v1.3.1 h1:YF8+flBXS5eO826T4nzqPrxfhQThhXl0YzfuUPu4SBg= | |||
github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= | |||
github.com/golang/protobuf v1.3.2 h1:6nsPYzhq5kReh6QImI3k5qWzO4PEbvbIW2cwSfR/6xs= | |||
github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= | |||
github.com/golang/protobuf v1.3.3/go.mod h1:vzj43D7+SQXF/4pzW/hwtAqwc6iTitCiVSaWz5lYuqw= | |||
github.com/golang/protobuf v1.3.4/go.mod h1:vzj43D7+SQXF/4pzW/hwtAqwc6iTitCiVSaWz5lYuqw= | |||
github.com/golang/protobuf v1.3.5 h1:F768QJ1E9tib+q5Sc8MkdJi1RxLTbRcTf8LJV56aRls= | |||
github.com/golang/protobuf v1.3.5/go.mod h1:6O5/vntMXwX2lRkT1hjjk0nAC1IDOTvTlVgjlRvqsdk= | |||
github.com/golang/snappy v0.0.0-20170215233205-553a64147049/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q= | |||
github.com/google/btree v0.0.0-20180813153112-4030bb1f1f0c/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ= | |||
github.com/google/btree v1.0.0/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ= | |||
github.com/google/go-cmp v0.1.1-0.20171103154506-982329095285/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M= | |||
github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M= | |||
github.com/google/go-cmp v0.3.0 h1:crn/baboCvb5fXaQ0IJ1SGTsTVrWpDsCWC8EGETZijY= | |||
github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= | |||
github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= | |||
github.com/google/go-cmp v0.4.0 h1:xsAVV57WRhGj6kEIi8ReJzQlHHqcBYCElAvkovg3B/4= | |||
github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= | |||
github.com/google/martian v2.1.0+incompatible/go.mod h1:9I4somxYTbIHy5NJKHRl3wXiIaQGbYVAs8BPL6v8lEs= | |||
github.com/google/pprof v0.0.0-20181206194817-3ea8567a2e57/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc= | |||
github.com/google/pprof v0.0.0-20190515194954-54271f7e092f/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc= | |||
github.com/google/pprof v0.0.0-20191218002539-d4f498aebedc/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= | |||
github.com/google/pprof v0.0.0-20200212024743-f11f1df84d12/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= | |||
github.com/google/pprof v0.0.0-20200229191704-1ebb73c60ed3/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= | |||
github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI= | |||
github.com/googleapis/gax-go v2.0.0+incompatible h1:j0GKcs05QVmm7yesiZq2+9cxHkNK9YM6zKx4D2qucQU= | |||
github.com/googleapis/gax-go v2.0.0+incompatible/go.mod h1:SFVmujtThgffbyetf+mdk2eWhX2bMyUtNHzFKcPA9HY= | |||
github.com/googleapis/gax-go/v2 v2.0.4/go.mod h1:0Wqv26UfaUD9n4G6kQubkQ+KchISgw+vpHVxEJEs9eg= | |||
github.com/googleapis/gax-go/v2 v2.0.5 h1:sjZBwGj9Jlw33ImPtvFviGYvseOtDM7hkSKB7+Tv3SM= | |||
github.com/googleapis/gax-go/v2 v2.0.5/go.mod h1:DWXyrwAJ9X0FpwwEdw+IPEYBICEFu5mhpdKc/us6bOk= | |||
github.com/gorilla/mux v1.7.3 h1:gnP5JzjVOuiZD07fKKToCAOjS0yOpj/qPETTXCCS6hw= | |||
github.com/gorilla/mux v1.7.3/go.mod h1:1lud6UwP+6orDFRuTfBEV8e9/aOM/c4fVVCaMa2zaAs= | |||
github.com/gorilla/handlers v1.4.2 h1:0QniY0USkHQ1RGCLfKxeNHK9bkDHGRYGNDFBCS+YARg= | |||
github.com/gorilla/handlers v1.4.2/go.mod h1:Qkdc/uu4tH4g6mTK6auzZ766c4CA0Ng8+o/OAirnOIQ= | |||
github.com/gorilla/mux v1.7.4 h1:VuZ8uybHlWmqV03+zRzdwKL4tUnIp1MAQtp1mIFE1bc= | |||
github.com/gorilla/mux v1.7.4/go.mod h1:DVbg23sWSpFRCP0SfiEN6jmj59UnW/n46BH5rLB71So= | |||
github.com/gorilla/securecookie v1.1.1 h1:miw7JPhV+b/lAHSXz4qd/nN9jRiAFV5FwjeKyCS8BvQ= | |||
github.com/gorilla/securecookie v1.1.1/go.mod h1:ra0sb63/xPlUeL+yeDciTfxMRAA+MP+HVt/4epWDjd4= | |||
github.com/gregjones/httpcache v0.0.0-20170920190843-316c5e0ff04e/go.mod h1:FecbI9+v66THATjSRHfNgh1IVFe/9kFxbXtjV0ctIMA= | |||
github.com/hashicorp/golang-lru v0.5.0/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8= | |||
github.com/hashicorp/golang-lru v0.5.1 h1:0hERBMJE1eitiLkihrMvRVBYAkpHzc/J3QdDN+dAcgU= | |||
github.com/hashicorp/golang-lru v0.5.1/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8= | |||
github.com/hashicorp/golang-lru v0.5.3/go.mod h1:iADmTwqILo4mZ8BN3D2Q6+9jd8WM5uGBxy+E8yxSoD4= | |||
github.com/hashicorp/hcl v0.0.0-20170914154624-68e816d1c783/go.mod h1:oZtUIOe8dh44I2q6ScRibXws4Ajl+d+nod3AaR9vL5w= | |||
github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU= | |||
github.com/ianlancetaylor/demangle v0.0.0-20181102032728-5e5cf60278f6/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc= | |||
github.com/inconshreveable/log15 v0.0.0-20170622235902-74a0988b5f80/go.mod h1:cOaXtrgN4ScfRrD9Bre7U1thNq5RtJ8ZoP4iXVGRj6o= | |||
github.com/jessevdk/go-flags v0.0.0-20141203071132-1679536dcc89/go.mod h1:4FA24M0QyGHXBuZZK/XkWh8h0e1EYbRYJSGM75WSRxI= | |||
github.com/jmespath/go-jmespath v0.0.0-20180206201540-c2b33e8439af h1:pmfjZENx5imkbgOkpRUYLnmbU7UEFbjtDA2hxJ1ichM= | |||
github.com/jmespath/go-jmespath v0.0.0-20180206201540-c2b33e8439af/go.mod h1:Nht3zPeWKUH0NzdCt2Blrr5ys8VGpn0CEB0cQHVjt7k= | |||
github.com/jmespath/go-jmespath v0.3.0 h1:OS12ieG61fsCg5+qLJ+SsW9NicxNkg3b25OyT2yCeUc= | |||
github.com/jmespath/go-jmespath v0.3.0/go.mod h1:9QtRXoHjLGCJ5IBSaohpXITPlowMeeYCZ7fLUTSywik= | |||
github.com/jrick/logrotate v1.0.0/go.mod h1:LNinyqDIJnpAur+b8yyulnQw/wDuN1+BYKlTRt3OuAQ= | |||
github.com/jstemmer/go-junit-report v0.0.0-20190106144839-af01ea7f8024/go.mod h1:6v2b51hI/fHJwM22ozAgKL4VKDeJcHhJFhtBdhmNjmU= | |||
github.com/jstemmer/go-junit-report v0.9.1/go.mod h1:Brl9GWCQeLvo8nXZwPNNblvFj/XSXhF0NWZEnDohbsk= | |||
github.com/kisielk/errcheck v1.1.0/go.mod h1:EZBBE59ingxPouuu3KfxchcWSUPOHkagtvWXihfKN4Q= | |||
github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck= | |||
github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck= | |||
github.com/kkdai/bstream v0.0.0-20161212061736-f391b8402d23/go.mod h1:J+Gs4SYgM6CZQHDETBtE9HaSEkGmuNXF86RwHhHUvq4= | |||
github.com/kr/pretty v0.1.0 h1:L/CwN0zerZDmRFUapSPitk6f+Q3+0za1rQkzVuMiMFI= | |||
github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= | |||
github.com/kr/pretty v0.2.0 h1:s5hAObm+yFO5uHYt5dYjxi2rXrsnmRpJx4OYvIWUaQs= | |||
github.com/kr/pretty v0.2.0/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI= | |||
github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= | |||
github.com/kr/text v0.1.0 h1:45sCR5RtlFHMR4UwH9sdQ5TC8v0qDQCHnXt+kaKSTVE= | |||
github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= | |||
github.com/mattn/go-colorable v0.1.2 h1:/bC9yWikZXAL9uJdulbSfyVNIR3n3trXl+v8+1sx8mU= | |||
github.com/mattn/go-colorable v0.1.2/go.mod h1:U0ppj6V5qS13XJ6of8GYAs25YV2eR4EVcfRqFIhoBtE= | |||
github.com/magiconair/properties v1.7.4-0.20170902060319-8d7837e64d3c/go.mod h1:PppfXfuXeibc/6YijjN8zIbojt8czPbwD3XqdrwzmxQ= | |||
github.com/mattn/go-colorable v0.0.10-0.20170816031813-ad5389df28cd/go.mod h1:9vuHe8Xs5qXnSaW/c/ABM9alt+Vo+STaOChaDxuIBZU= | |||
github.com/mattn/go-colorable v0.1.4 h1:snbPLB8fVfU9iwbbo30TPtbLRzwWu6aJS6Xh4eaaviA= | |||
github.com/mattn/go-colorable v0.1.4/go.mod h1:U0ppj6V5qS13XJ6of8GYAs25YV2eR4EVcfRqFIhoBtE= | |||
github.com/mattn/go-colorable v0.1.6 h1:6Su7aK7lXmJ/U79bYtBjLNaha4Fs1Rg9plHpcH+vvnE= | |||
github.com/mattn/go-colorable v0.1.6/go.mod h1:u6P/XSegPjTcexA+o6vUJrdnUu04hMope9wVRipJSqc= | |||
github.com/mattn/go-isatty v0.0.2/go.mod h1:M+lRXTBqGeGNdLjl/ufCoiOlB5xdOkqRJdNxMWT7Zi4= | |||
github.com/mattn/go-isatty v0.0.8/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s= | |||
github.com/mattn/go-isatty v0.0.9 h1:d5US/mDsogSGW37IV293h//ZFaeajb69h+EHFsv2xGg= | |||
github.com/mattn/go-isatty v0.0.9/go.mod h1:YNRxwqDuOph6SZLI9vUUz6OYw3QyUt7WiY2yME+cCiQ= | |||
github.com/mattn/go-isatty v0.0.11/go.mod h1:PhnuNfih5lzO57/f3n+odYbM4JtupLOxQOAqxQCu2WE= | |||
github.com/mattn/go-isatty v0.0.12 h1:wuysRhFDzyxgEmMf5xjvJ2M9dZoWAXNNr5LSBS7uHXY= | |||
github.com/mattn/go-isatty v0.0.12/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Kysco4FUpU= | |||
github.com/microcosm-cc/bluemonday v1.0.2 h1:5lPfLTTAvAbtS0VqT+94yOtFnGfUWYyx0+iToC3Os3s= | |||
github.com/microcosm-cc/bluemonday v1.0.2/go.mod h1:iVP4YcDBq+n/5fb23BhYFvIMq/leAFZyRl6bYmGDlGc= | |||
github.com/mitchellh/mapstructure v0.0.0-20170523030023-d0303fe80992/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y= | |||
github.com/nu7hatch/gouuid v0.0.0-20131221200532-179d4d0c4d8d h1:VhgPp6v9qf9Agr/56bj7Y/xa04UccTW04VP0Qed4vnQ= | |||
github.com/nu7hatch/gouuid v0.0.0-20131221200532-179d4d0c4d8d/go.mod h1:YUTz3bUH2ZwIWBy3CJBeOBEugqcmXREj14T+iG/4k4U= | |||
github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= | |||
github.com/onsi/ginkgo v1.7.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= | |||
github.com/onsi/gomega v1.4.3/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1CpauHY= | |||
github.com/pelletier/go-toml v1.0.1-0.20170904195809-1d6b12b7cb29/go.mod h1:5z9KED0ma1S8pY6P1sdut58dfprrGBbd/94hg7ilaic= | |||
github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4= | |||
github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= | |||
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= | |||
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= | |||
github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= | |||
github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4= | |||
github.com/russross/blackfriday/v2 v2.0.1 h1:lPqVAte+HuHNfhJ/0LC98ESWRz8afy9tM/0RK8m9o+Q= | |||
github.com/russross/blackfriday/v2 v2.0.1/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= | |||
github.com/shurcooL/sanitized_anchor_name v1.0.0 h1:PdmoCO6wvbs+7yrJyMORt4/BmY5IYyJwS/kOiWx8mHo= | |||
github.com/shurcooL/sanitized_anchor_name v1.0.0/go.mod h1:1NzhyTcUVG4SuEtjjoZeVRXNmyL/1OwPU0+IJeTBvfc= | |||
github.com/skip2/go-qrcode v0.0.0-20190110000554-dc11ecdae0a9 h1:lpEzuenPuO1XNTeikEmvqYFcU37GVLl8SRNblzyvGBE= | |||
github.com/skip2/go-qrcode v0.0.0-20190110000554-dc11ecdae0a9/go.mod h1:PLPIyL7ikehBD1OAjmKKiOEhbvWyHGaNDjquXMcYABo= | |||
github.com/skip2/go-qrcode v0.0.0-20191027152451-9434209cb086 h1:RYiqpb2ii2Z6J4x0wxK46kvPBbFuZcdhS+CIztmYgZs= | |||
github.com/skip2/go-qrcode v0.0.0-20191027152451-9434209cb086/go.mod h1:PLPIyL7ikehBD1OAjmKKiOEhbvWyHGaNDjquXMcYABo= | |||
github.com/spacemonkeygo/monkit/v3 v3.0.4 h1:Ay+PZirv+qfd4sqcT+X/U3BnC7AcIaqp/IXh0oV36k8= | |||
github.com/spacemonkeygo/monkit/v3 v3.0.4/go.mod h1:JcK1pCbReQsOsMKF/POFSZCq7drXFybgGmbc27tuwes= | |||
github.com/spacemonkeygo/monkit/v3 v3.0.7 h1:LsGdIXl8mccqJrYEh4Uf4sLVGu/g0tjhNqQzdn9MzVk= | |||
github.com/spacemonkeygo/monkit/v3 v3.0.7/go.mod h1:kj1ViJhlyADa7DiA4xVnTuPA46lFKbM7mxQTrXCuJP4= | |||
github.com/spacemonkeygo/monotime v0.0.0-20180824235756-e3f48a95f98a h1:8+cCjxhToanKmxLIbuyBNe2EnpgwhiivsIaRJstDRFA= | |||
github.com/spacemonkeygo/monotime v0.0.0-20180824235756-e3f48a95f98a/go.mod h1:ul4bvvnCOPZgq8w0nTkSmWVg/hauVpFS97Am1YM1XXo= | |||
github.com/spf13/afero v0.0.0-20170901052352-ee1bd8ee15a1/go.mod h1:j4pytiNVoe2o6bmDsKpLACNPDBIoEAkihy7loJ1B0CQ= | |||
github.com/spf13/cast v1.1.0/go.mod h1:r2rcYCSwa1IExKTDiTfzaxqT2FNHs8hODu4LnUfgKEg= | |||
github.com/spf13/jwalterweatherman v0.0.0-20170901151539-12bd96e66386/go.mod h1:cQK4TGJAtQXfYWX+Ddv3mKDzgVb68N+wFjFa4jdeBTo= | |||
github.com/spf13/pflag v1.0.1-0.20170901120850-7aff26db30c1/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4= | |||
github.com/spf13/viper v1.0.0/go.mod h1:A8kyI5cUJhb8N+3pkfONlcEcZbueH6nhAm0Fq7SrnBM= | |||
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= | |||
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= | |||
github.com/stretchr/testify v1.4.0 h1:2E4SXV/wtOkTonXsotYi4li6zVWxYlZuYNCXe9XRJyk= | |||
github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4= | |||
github.com/stretchr/testify v1.5.1 h1:nOGnQDM7FYENwehXlg/kFVnos3rEvtKTjRvOWSzb6H4= | |||
github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA= | |||
github.com/tomasen/realip v0.0.0-20180522021738-f0c99a92ddce h1:fb190+cK2Xz/dvi9Hv8eCYJYvIGUTN2/KLq1pT6CjEc= | |||
github.com/tomasen/realip v0.0.0-20180522021738-f0c99a92ddce/go.mod h1:o8v6yHRoik09Xen7gje4m9ERNah1d1PPsVq1VEx9vE4= | |||
github.com/urfave/cli v1.21.0 h1:wYSSj06510qPIzGSua9ZqsncMmWE3Zr55KBERygyrxE= | |||
github.com/urfave/cli v1.21.0/go.mod h1:lxDj6qX9Q6lWQxIrbrT0nwecwUtRnhVZAJjJZrVUZZQ= | |||
github.com/urfave/cli v1.22.1 h1:+mkCCcOFKPnCmVYVcURKps1Xe+3zP90gSYGNfRkjoIY= | |||
github.com/urfave/cli v1.22.1/go.mod h1:Gos4lmkARVdJ6EkW0WaNv/tZAAMe9V7XWyB60NtXRu0= | |||
github.com/urfave/cli v1.22.3 h1:FpNT6zq26xNpHZy08emi755QwzLPs6Pukqjlc7RfOMU= | |||
github.com/urfave/cli v1.22.3/go.mod h1:Gos4lmkARVdJ6EkW0WaNv/tZAAMe9V7XWyB60NtXRu0= | |||
github.com/vivint/infectious v0.0.0-20200605153912-25a574ae18a3 h1:zMsHhfK9+Wdl1F7sIKLyx3wrOFofpb3rWFbA4HgcK5k= | |||
github.com/vivint/infectious v0.0.0-20200605153912-25a574ae18a3/go.mod h1:R0Gbuw7ElaGSLOZUSwBm/GgVwMd30jWxBDdAyMOeTuc= | |||
github.com/zeebo/admission/v3 v3.0.2/go.mod h1:BP3isIv9qa2A7ugEratNq1dnl2oZRXaQUGdU7WXKtbw= | |||
github.com/zeebo/assert v1.1.0 h1:hU1L1vLTHsnO8x8c9KAR5GmM5QscxHg5RNU5z5qbUWY= | |||
github.com/zeebo/assert v1.1.0/go.mod h1:Pq9JiuJQpG8JLJdtkwrJESF0Foym2/D9XMU5ciN/wJ0= | |||
github.com/zeebo/errs v1.2.2 h1:5NFypMTuSdoySVTqlNs1dEoU21QVamMQJxW/Fii5O7g= | |||
github.com/zeebo/errs v1.2.2/go.mod h1:sgbWHsvVuTPHcqJJGQ1WhI5KbWlHYz+2+2C/LSEtCw4= | |||
github.com/zeebo/float16 v0.1.0 h1:kRqxv5og6z1emEyz5FpW0/BVHe5VfxEAw6b1ljCZlUc= | |||
github.com/zeebo/float16 v0.1.0/go.mod h1:fssGvvXu+XS8MH57cKmyrLB/cqioYeYX/2mXCN3a5wo= | |||
github.com/zeebo/incenc v0.0.0-20180505221441-0d92902eec54 h1:+cwNE5KJ3pika4HuzmDHkDlK5myo0G9Sv+eO7WWxnUQ= | |||
github.com/zeebo/incenc v0.0.0-20180505221441-0d92902eec54/go.mod h1:EI8LcOBDlSL3POyqwC1eJhOYlMBMidES+613EtmmT5w= | |||
go.opencensus.io v0.21.0 h1:mU6zScU4U1YAFPHEHYk+3JC4SY7JxgkqS10ZOSyksNg= | |||
go.opencensus.io v0.21.0/go.mod h1:mSImk1erAIZhrmZN+AvHh14ztQfjbGwt4TtuofqLduU= | |||
go.opencensus.io v0.22.0/go.mod h1:+kGneAE2xo2IficOXnaByMWTGM9T73dGwxeWcUqIpI8= | |||
go.opencensus.io v0.22.1 h1:8dP3SGL7MPB94crU3bEPplMPe83FI4EouesJUeFHv50= | |||
go.opencensus.io v0.22.1/go.mod h1:Ap50jQcDJrx6rB6VgeeFPtuPIf3wMRvRfrfYDO6+BmA= | |||
go.opencensus.io v0.22.2/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw= | |||
go.opencensus.io v0.22.3 h1:8sGtKOrtQqkN1bp2AtX+misvLIlOmsEsNd+9NIcPEm8= | |||
go.opencensus.io v0.22.3/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw= | |||
go.uber.org/atomic v1.7.0 h1:ADUqmZGgLDDfbSL9ZmPxKTybcoEYHgpYfELNoN+7hsw= | |||
go.uber.org/atomic v1.7.0/go.mod h1:fEN4uk6kAWBTFdckzkM89CLk9XfWZrxpCo0nPH17wJc= | |||
go.uber.org/multierr v1.6.0 h1:y6IPFStTAIT5Ytl7/XYmHvzXQ7S3g/IeZW9hyZ5thw4= | |||
go.uber.org/multierr v1.6.0/go.mod h1:cdWPpRnG4AhwMwsgIHip0KRBQjJy5kYEpYjJxpXp9iU= | |||
go.uber.org/zap v1.10.0 h1:ORx85nbTijNz8ljznvCMR1ZBIPKFn3jQrag10X2AsuM= | |||
go.uber.org/zap v1.10.0/go.mod h1:vwi/ZaCAaUcBkycHslxD9B2zi4UTXhF60s6SWpuDF0Q= | |||
golang.org/x/crypto v0.0.0-20170930174604-9419663f5a44/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= | |||
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= | |||
golang.org/x/crypto v0.0.0-20190510104115-cbcb75029529/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= | |||
golang.org/x/crypto v0.0.0-20190605123033-f99c8df09eb5/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= | |||
golang.org/x/crypto v0.0.0-20190820162420-60c769a6c586 h1:7KByu05hhLed2MO29w7p1XfZvZ13m8mub3shuVftRs0= | |||
golang.org/x/crypto v0.0.0-20190820162420-60c769a6c586/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= | |||
golang.org/x/crypto v0.0.0-20191002192127-34f69633bfdc h1:c0o/qxkaO2LF5t6fQrT4b5hzyggAkLLlCUjqfRxd8Q4= | |||
golang.org/x/crypto v0.0.0-20191002192127-34f69633bfdc/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= | |||
golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= | |||
golang.org/x/crypto v0.0.0-20200115085410-6d4e4cb37c7d/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= | |||
golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9 h1:psW17arqaxU48Z5kZ0CQnkZWQJsqcURM6tKiBApRjXI= | |||
golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= | |||
golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= | |||
golang.org/x/exp v0.0.0-20190306152737-a1d7652674e8/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= | |||
golang.org/x/exp v0.0.0-20190510132918-efd6b22b2522/go.mod h1:ZjyILWgesfNpC6sMxTJOJm9Kp84zZh5NQWvqDGG3Qr8= | |||
golang.org/x/exp v0.0.0-20190829153037-c13cbed26979/go.mod h1:86+5VVa7VpoJ4kLfm080zCjGlMRFzhUhsZKEZO7MGek= | |||
golang.org/x/exp v0.0.0-20191030013958-a1ab85dbe136/go.mod h1:JXzH8nQsPlswgeRAPE3MuO9GYsAcnJvJ4vnMwN/5qkY= | |||
golang.org/x/exp v0.0.0-20191129062945-2f5052295587/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4= | |||
golang.org/x/exp v0.0.0-20191227195350-da58074b4299/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4= | |||
golang.org/x/exp v0.0.0-20200119233911-0405dc783f0a/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4= | |||
golang.org/x/exp v0.0.0-20200207192155-f17229e696bd/go.mod h1:J/WKrq2StrnmMY6+EHIKF9dgMWnmCNThgcyBT1FY9mM= | |||
golang.org/x/exp v0.0.0-20200224162631-6cc2880d07d6/go.mod h1:3jZMyOhIsHpP37uCMkUooju7aAi5cS1Q23tOzKc+0MU= | |||
golang.org/x/image v0.0.0-20190227222117-0694c2d4d067/go.mod h1:kZ7UVZpmo3dzQBMxlp+ypCbDeSB+sBbTgSJuh5dn5js= | |||
golang.org/x/image v0.0.0-20190802002840-cff245a6509b/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0= | |||
golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= | |||
@@ -139,12 +271,20 @@ golang.org/x/lint v0.0.0-20190301231843-5614ed5bae6f/go.mod h1:UVdnD1Gm6xHRNCYTk | |||
golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= | |||
golang.org/x/lint v0.0.0-20190409202823-959b441ac422/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= | |||
golang.org/x/lint v0.0.0-20190909230951-414d861bb4ac/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= | |||
golang.org/x/lint v0.0.0-20190930215403-16217165b5de/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= | |||
golang.org/x/lint v0.0.0-20191125180803-fdd1cda4f05f/go.mod h1:5qLYkcX4OjUUV8bRuDixDT3tpyyb+LUpUlRWLxfhWrs= | |||
golang.org/x/lint v0.0.0-20200130185559-910be7a94367/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY= | |||
golang.org/x/lint v0.0.0-20200302205851-738671d3881b/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY= | |||
golang.org/x/mobile v0.0.0-20190312151609-d3739f865fa6/go.mod h1:z+o9i4GpDbdi3rU15maQ/Ox0txvL9dWGYEHz965HBQE= | |||
golang.org/x/mobile v0.0.0-20190719004257-d2bd2a29d028/go.mod h1:E/iHnbuqvinMTCcRqshq8CkpyQDoeVncDDYHnLhea+o= | |||
golang.org/x/mod v0.0.0-20190513183733-4bf6d317e70e/go.mod h1:mXi4GBBbnImb6dmsKGUJ2LatrhH/nqhxcFungHvyanc= | |||
golang.org/x/mod v0.1.0/go.mod h1:0QHyrYULN0/3qlju5TqG8bIK38QM8yzMo5ekMj3DlcY= | |||
golang.org/x/mod v0.1.1-0.20191105210325-c90efee705ee/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg= | |||
golang.org/x/mod v0.1.1-0.20191107180719-034126e5016b/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg= | |||
golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= | |||
golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= | |||
golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= | |||
golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= | |||
golang.org/x/net v0.0.0-20181220203305-927f97764cc3/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= | |||
golang.org/x/net v0.0.0-20190108225652-1e06a53dbb7e/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= | |||
golang.org/x/net v0.0.0-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= | |||
@@ -154,20 +294,37 @@ golang.org/x/net v0.0.0-20190501004415-9ce7a6920f09/go.mod h1:t9HGtf8HONx5eT2rtn | |||
golang.org/x/net v0.0.0-20190503192946-f4e77d36d62c/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= | |||
golang.org/x/net v0.0.0-20190603091049-60506f45cf65/go.mod h1:HSz+uSET+XFnRR8LxR5pz3Of3rY3CfYBVs4xY44aLks= | |||
golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= | |||
golang.org/x/net v0.0.0-20190813141303-74dc4d7220e7 h1:fHDIZ2oxGnUZRN6WgWFCbYBjH9uqVPRCUVUDhs0wnbA= | |||
golang.org/x/net v0.0.0-20190813141303-74dc4d7220e7/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= | |||
golang.org/x/net v0.0.0-20191007182048-72f939374954 h1:JGZucVF/L/TotR719NbujzadOZ2AgnYlqphQGHDCKaU= | |||
golang.org/x/net v0.0.0-20191007182048-72f939374954/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= | |||
golang.org/x/net v0.0.0-20190724013045-ca1201d0de80/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= | |||
golang.org/x/net v0.0.0-20190923162816-aa69164e4478/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= | |||
golang.org/x/net v0.0.0-20191209160850-c0dbc17a3553/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= | |||
golang.org/x/net v0.0.0-20200114155413-6afb5195e5aa/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= | |||
golang.org/x/net v0.0.0-20200202094626-16171245cfb2/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= | |||
golang.org/x/net v0.0.0-20200222125558-5a598a2470a0/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= | |||
golang.org/x/net v0.0.0-20200226121028-0de0cce0169b/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= | |||
golang.org/x/net v0.0.0-20200301022130-244492dfa37a h1:GuSPYbZzB5/dcLNCwLQLsg3obCJtX9IJhpXkvY7kzk0= | |||
golang.org/x/net v0.0.0-20200301022130-244492dfa37a/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= | |||
golang.org/x/oauth2 v0.0.0-20170912212905-13449ad91cb2/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= | |||
golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= | |||
golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= | |||
golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45 h1:SVwTIAaPC2U/AvvLNZ2a7OVsmBpC8L5BlwK1whH3hm0= | |||
golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= | |||
golang.org/x/oauth2 v0.0.0-20191202225959-858c2ad4c8b6/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= | |||
golang.org/x/oauth2 v0.0.0-20200107190931-bf48bf16ab8d h1:TzXSXBo42m9gQenoE3b9BGiEpg5IG2JkU5FkPIawgtw= | |||
golang.org/x/oauth2 v0.0.0-20200107190931-bf48bf16ab8d/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= | |||
golang.org/x/sync v0.0.0-20170517211232-f52d1811a629/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= | |||
golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= | |||
golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= | |||
golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= | |||
golang.org/x/sync v0.0.0-20190227155943-e225da77a7e6/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= | |||
golang.org/x/sync v0.0.0-20190423024810-112230192c58 h1:8gQV6CLnAEikrhgkHFbMAEhagSSnXWGV915qUMm9mrU= | |||
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= | |||
golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e h1:vcxGaoTs7kV8m5Np9uUNQin4BrLOthgV7252N8V+FwY= | |||
golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= | |||
golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= | |||
golang.org/x/sync v0.0.0-20200317015054-43a5402ce75a h1:WXEvlFVvvGxCJLG6REjsT03iWnKLEWinaScsxF2Vm2o= | |||
golang.org/x/sync v0.0.0-20200317015054-43a5402ce75a/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= | |||
golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= | |||
golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= | |||
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= | |||
golang.org/x/sys v0.0.0-20190222072716-a9d3bda3a223/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= | |||
golang.org/x/sys v0.0.0-20190312061237-fead79001313/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= | |||
@@ -176,16 +333,34 @@ golang.org/x/sys v0.0.0-20190502145724-3ef323f4f1fd/go.mod h1:h1NjWce9XRLGQEsW7w | |||
golang.org/x/sys v0.0.0-20190507160741-ecd444e8653b/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= | |||
golang.org/x/sys v0.0.0-20190606165138-5da285871e9c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= | |||
golang.org/x/sys v0.0.0-20190624142023-c5567b49c5d0/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= | |||
golang.org/x/sys v0.0.0-20190813064441-fde4db37ae7a h1:aYOabOQFp6Vj6W1F80affTUvO9UxmJRx8K0gsfABByQ= | |||
golang.org/x/sys v0.0.0-20190813064441-fde4db37ae7a/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= | |||
golang.org/x/sys v0.0.0-20191007154456-ef33b2fb2c41 h1:OC2BiV9nQHWgVMNbxZ5/eZKWnnd3Z4H9W5zdNvC4EBc= | |||
golang.org/x/sys v0.0.0-20191007154456-ef33b2fb2c41/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= | |||
golang.org/x/sys v0.0.0-20190726091711-fc99dfbffb4e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= | |||
golang.org/x/sys v0.0.0-20191001151750-bb3f8db39f24/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= | |||
golang.org/x/sys v0.0.0-20191026070338-33540a1f6037/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= | |||
golang.org/x/sys v0.0.0-20191204072324-ce4227a45e2e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= | |||
golang.org/x/sys v0.0.0-20191210023423-ac6580df4449/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= | |||
golang.org/x/sys v0.0.0-20191228213918-04cbcbbfeed8/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= | |||
golang.org/x/sys v0.0.0-20200113162924-86b910548bc1/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= | |||
golang.org/x/sys v0.0.0-20200116001909-b77594299b42/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= | |||
golang.org/x/sys v0.0.0-20200122134326-e047566fdf82/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= | |||
golang.org/x/sys v0.0.0-20200202164722-d101bd2416d5/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= | |||
golang.org/x/sys v0.0.0-20200212091648-12a6c2dcc1e4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= | |||
golang.org/x/sys v0.0.0-20200223170610-d5e6a3e2c0ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= | |||
golang.org/x/sys v0.0.0-20200302150141-5c8b2ff67527 h1:uYVVQ9WP/Ds2ROhcaGPeIdVq0RIXVLwsHlnvJ+cT1So= | |||
golang.org/x/sys v0.0.0-20200302150141-5c8b2ff67527/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= | |||
golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= | |||
golang.org/x/sys v0.0.0-20200610111108-226ff32320da h1:bGb80FudwxpeucJUjPYJXuJ8Hk91vNtfvrymzwiei38= | |||
golang.org/x/sys v0.0.0-20200610111108-226ff32320da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= | |||
golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= | |||
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= | |||
golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= | |||
golang.org/x/text v0.3.2 h1:tW2bmiBqwgJj/UpqtC8EpXEZVYOwU0yG4iWbprSVAcs= | |||
golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk= | |||
golang.org/x/time v0.0.0-20170424234030-8be79e1e0910/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= | |||
golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= | |||
golang.org/x/time v0.0.0-20190308202827-9d24e82272b4 h1:SvFZT6jyqRaOeXpc5h/JSfZenJ2O330aBsf7JfSUXmQ= | |||
golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= | |||
golang.org/x/time v0.0.0-20191024005414-555d28b269f0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= | |||
golang.org/x/tools v0.0.0-20180221164845-07fd8470d635/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= | |||
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= | |||
golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= | |||
golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY= | |||
@@ -200,21 +375,47 @@ golang.org/x/tools v0.0.0-20190621195816-6e04913cbbac/go.mod h1:/rFqwRUd4F7ZHNgw | |||
golang.org/x/tools v0.0.0-20190628153133-6cdbf07be9d0/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= | |||
golang.org/x/tools v0.0.0-20190816200558-6889da9d5479/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= | |||
golang.org/x/tools v0.0.0-20190911174233-4f2ddba30aff/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= | |||
golang.org/x/tools v0.0.0-20191012152004-8de300cfc20a/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= | |||
golang.org/x/tools v0.0.0-20191113191852-77e3bb0ad9e7/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= | |||
golang.org/x/tools v0.0.0-20191115202509-3a792d9c32b2/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= | |||
golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= | |||
golang.org/x/tools v0.0.0-20191125144606-a911d9008d1f/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= | |||
golang.org/x/tools v0.0.0-20191130070609-6e064ea0cf2d/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= | |||
golang.org/x/tools v0.0.0-20191216173652-a0e659d51361/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= | |||
golang.org/x/tools v0.0.0-20191227053925-7b8e75db28f4/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= | |||
golang.org/x/tools v0.0.0-20200117161641-43d50277825c/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= | |||
golang.org/x/tools v0.0.0-20200122220014-bf1340f18c4a/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= | |||
golang.org/x/tools v0.0.0-20200130002326-2f3ba24bd6e7/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= | |||
golang.org/x/tools v0.0.0-20200204074204-1cc6d1ef6c74/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= | |||
golang.org/x/tools v0.0.0-20200207183749-b753a1ba74fa/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= | |||
golang.org/x/tools v0.0.0-20200212150539-ea181f53ac56/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= | |||
golang.org/x/tools v0.0.0-20200224181240-023911ca70b2/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= | |||
golang.org/x/tools v0.0.0-20200304193943-95d2e580d8eb/go.mod h1:o4KQGtdN14AW+yjsvvwRTJJuXz8XRtIHtEnmAXLyFUw= | |||
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= | |||
golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= | |||
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543 h1:E7g+9GITq07hpfrRu66IVDexMakfv52eLZ2CXBWiKr4= | |||
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= | |||
google.golang.org/api v0.0.0-20170921000349-586095a6e407/go.mod h1:4mhQ8q/RsB7i+udVvVy5NUi08OU8ZlA0gRVgrF7VFY0= | |||
google.golang.org/api v0.4.0/go.mod h1:8k5glujaEP+g9n7WNsDg8QP6cUVNI86fCNMcbazEtwE= | |||
google.golang.org/api v0.7.0/go.mod h1:WtwebWUNSVBH/HAw79HIFXZNqEvBhG+Ra+ax0hx3E3M= | |||
google.golang.org/api v0.8.0/go.mod h1:o4eAsZoiT+ibD93RtjEohWalFOjRDx6CVaqeizhEnKg= | |||
google.golang.org/api v0.9.0 h1:jbyannxz0XFD3zdjgrSUsaJbgpH4eTrkdhRChkHPfO8= | |||
google.golang.org/api v0.9.0/go.mod h1:o4eAsZoiT+ibD93RtjEohWalFOjRDx6CVaqeizhEnKg= | |||
google.golang.org/api v0.10.0 h1:7tmAxx3oKE98VMZ+SBZzvYYWRQ9HODBxmC8mXUsraSQ= | |||
google.golang.org/api v0.10.0/go.mod h1:o4eAsZoiT+ibD93RtjEohWalFOjRDx6CVaqeizhEnKg= | |||
google.golang.org/api v0.13.0/go.mod h1:iLdEw5Ide6rF15KTC1Kkl0iskquN2gFfn9o9XIsbkAI= | |||
google.golang.org/api v0.14.0/go.mod h1:iLdEw5Ide6rF15KTC1Kkl0iskquN2gFfn9o9XIsbkAI= | |||
google.golang.org/api v0.15.0/go.mod h1:iLdEw5Ide6rF15KTC1Kkl0iskquN2gFfn9o9XIsbkAI= | |||
google.golang.org/api v0.17.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE= | |||
google.golang.org/api v0.18.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE= | |||
google.golang.org/api v0.20.0 h1:jz2KixHX7EcCPiQrySzPdnYT7DbINAypCqKZ1Z7GM40= | |||
google.golang.org/api v0.20.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE= | |||
google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM= | |||
google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= | |||
google.golang.org/appengine v1.5.0 h1:KxkO13IPW4Lslp2bz+KHP2E3gtFlrIGNThxkZQ3g+4c= | |||
google.golang.org/appengine v1.5.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= | |||
google.golang.org/appengine v1.6.1/go.mod h1:i06prIuMbXzDqacNJfV5OdTW448YApPu5ww/cMBSeb0= | |||
google.golang.org/appengine v1.6.4 h1:WiKh4+/eMB2HaY7QhCfW/R7MuRAoA8QMCSJA6jP5/fo= | |||
google.golang.org/appengine v1.6.4/go.mod h1:i06prIuMbXzDqacNJfV5OdTW448YApPu5ww/cMBSeb0= | |||
google.golang.org/appengine v1.6.5 h1:tycE03LOZYQNhDpS27tcQdAzLCVMaj7QT2SXxebnpCM= | |||
google.golang.org/appengine v1.6.5/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc= | |||
google.golang.org/genproto v0.0.0-20170918111702-1e559d0a00ee/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc= | |||
google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc= | |||
google.golang.org/genproto v0.0.0-20190307195333-5fe7a883aa19/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= | |||
google.golang.org/genproto v0.0.0-20190418145605-e7d98fc518a7/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= | |||
@@ -224,20 +425,40 @@ google.golang.org/genproto v0.0.0-20190502173448-54afdca5d873/go.mod h1:VzzqZJRn | |||
google.golang.org/genproto v0.0.0-20190801165951-fa694d86fc64/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc= | |||
google.golang.org/genproto v0.0.0-20190819201941-24fa4b261c55/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc= | |||
google.golang.org/genproto v0.0.0-20190911173649-1774047e7e51/go.mod h1:IbNlFCBrqXvoKpeg0TB2l7cyZUmoaFKYIwrEpbDKLA8= | |||
google.golang.org/genproto v0.0.0-20191007162740-aa923e3a3354 h1:KJxw2DvYTCIxlEY4yqWyLdvFGlci4EKTCbrZwfyxDME= | |||
google.golang.org/genproto v0.0.0-20191007162740-aa923e3a3354/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= | |||
google.golang.org/genproto v0.0.0-20191108220845-16a3f7862a1a/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= | |||
google.golang.org/genproto v0.0.0-20191115194625-c23dd37a84c9/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= | |||
google.golang.org/genproto v0.0.0-20191216164720-4f79533eabd1/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= | |||
google.golang.org/genproto v0.0.0-20191230161307-f3c370f40bfb/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= | |||
google.golang.org/genproto v0.0.0-20200115191322-ca5a22157cba/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= | |||
google.golang.org/genproto v0.0.0-20200122232147-0452cf42e150/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= | |||
google.golang.org/genproto v0.0.0-20200204135345-fa8e72b47b90/go.mod h1:GmwEX6Z4W5gMy59cAlVYjN9JhxgbQH6Gn+gFDQe2lzA= | |||
google.golang.org/genproto v0.0.0-20200212174721-66ed5ce911ce/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= | |||
google.golang.org/genproto v0.0.0-20200224152610-e50cd9704f63/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= | |||
google.golang.org/genproto v0.0.0-20200305110556-506484158171 h1:xes2Q2k+d/+YNXVw0FpZkIDJiaux4OVrRKXRAzH6A0U= | |||
google.golang.org/genproto v0.0.0-20200305110556-506484158171/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= | |||
google.golang.org/grpc v1.2.1-0.20170921194603-d4b75ebd4f9f/go.mod h1:yo6s7OP7yaDglbqo1J04qKzAhqBH6lvTonzMVmEdcZw= | |||
google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c= | |||
google.golang.org/grpc v1.20.1 h1:Hz2g2wirWK7H0qIIhGIqRGTuMwTE8HEKFnDZZ7lm9NU= | |||
google.golang.org/grpc v1.20.1/go.mod h1:10oTOabMzJvdu6/UiuZezV6QK5dSlG84ov/aaiqXj38= | |||
google.golang.org/grpc v1.21.1/go.mod h1:oYelfM1adQP15Ek0mdvEgi9Df8B9CZIaU1084ijfRaM= | |||
google.golang.org/grpc v1.24.0 h1:vb/1TCsVn3DcJlQ0Gs1yB1pKI6Do2/QNwxdKqmc/b0s= | |||
google.golang.org/grpc v1.24.0/go.mod h1:XDChyiUovWa60DnaeDeZmSW86xtLtjtZbwvSiRnRtcA= | |||
google.golang.org/grpc v1.23.0/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg= | |||
google.golang.org/grpc v1.25.1/go.mod h1:c3i+UQWmh7LiEpx4sFZnkU36qjEYZ0imhYfXVyQciAY= | |||
google.golang.org/grpc v1.26.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk= | |||
google.golang.org/grpc v1.27.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk= | |||
google.golang.org/grpc v1.27.1 h1:zvIju4sqAGvwKspUQOhwnpcqSbzi7/H6QomNNjTL4sk= | |||
google.golang.org/grpc v1.27.1/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk= | |||
google.golang.org/grpc v1.27.1/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk= | |||
google.golang.org/grpc v1.28.0 h1:bO/TA4OxCOummhSf10siHuG7vJOiwh7SpRpFZDkOgl4= | |||
google.golang.org/grpc v1.28.0/go.mod h1:rpkK4SK4GF4Ach/+MFLZUBavHOvF2JJB5uozKKal+60= | |||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= | |||
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127 h1:qIbj1fsPNlZgppZ+VLlY7N33q108Sa+fhmuc+sWQYwY= | |||
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= | |||
gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15 h1:YR8cESwS4TdDjEe65xsg0ogRM/Nc3DYOhEAlW+xobZo= | |||
gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= | |||
gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI= | |||
gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys= | |||
gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw= | |||
gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= | |||
gopkg.in/yaml.v2 v2.2.2 h1:ZCJp+EgiOT7lHqUV2J862kp8Qj64Jo6az82+3Td9dZw= | |||
gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= | |||
honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= | |||
@@ -245,4 +466,13 @@ honnef.co/go/tools v0.0.0-20190106161140-3f1c8253044a/go.mod h1:rf3lG4BRIbNafJWh | |||
honnef.co/go/tools v0.0.0-20190418001031-e561f6794a2a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= | |||
honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= | |||
honnef.co/go/tools v0.0.1-2019.2.3/go.mod h1:a3bituU0lyd329TUQxRnasdCoJDkEUEAqEt0JzvZhAg= | |||
honnef.co/go/tools v0.0.1-2020.1.3/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9vFzvIQ3k= | |||
rsc.io/binaryregexp v0.2.0/go.mod h1:qTv7/COck+e2FymRvadv62gMdZztPaShugOCi3I+8D8= | |||
rsc.io/quote/v3 v3.1.0/go.mod h1:yEA65RcK8LyAZtP9Kv3t0HmxON59tX3rD+tICJqUlj0= | |||
rsc.io/sampler v1.3.0/go.mod h1:T1hPZKmBbMNahiBKFy5HrXp6adAjACjK9JXDnKaTXpA= | |||
storj.io/common v0.0.0-20201207172416-78f4e59925c3 h1:D+rAQBzjl0Mw3VQ+1Sjv5/53I7JaIymMrkDW5DYBgRE= | |||
storj.io/common v0.0.0-20201207172416-78f4e59925c3/go.mod h1:6sepaQTRLuygvA+GNPzdgRPOB1+wFfjde76KBWofbMY= | |||
storj.io/drpc v0.0.16 h1:9sxypc5lKi/0D69cR21BR0S21+IvXfON8L5nXMVNTwQ= | |||
storj.io/drpc v0.0.16/go.mod h1:zdmQ93nx4Z35u11pQ+GAnBy4DGOK3HJCSOfeh2RryTo= | |||
storj.io/uplink v1.4.2 h1:Kj4ubuSQfoLRC0n1cJXt1Tgns0WF1DRNMs8Luh6+rY8= | |||
storj.io/uplink v1.4.2/go.mod h1:VoxYTP5AzJ+gnzsqptuB5Ra8Old+fVVbwLCmi4jr5y4= |
@@ -102,7 +102,7 @@ func (s *Server) previewHandler(w http.ResponseWriter, r *http.Request) { | |||
token := vars["token"] | |||
filename := vars["filename"] | |||
_, err := s.CheckMetadata(token, filename, false) | |||
metadata, err := s.CheckMetadata(token, filename, false) | |||
if err != nil { | |||
log.Printf("Error metadata: %s", err.Error()) | |||
@@ -110,7 +110,8 @@ func (s *Server) previewHandler(w http.ResponseWriter, r *http.Request) { | |||
return | |||
} | |||
contentType, contentLength, err := s.storage.Head(token, filename) | |||
contentType := metadata.ContentType | |||
contentLength, err := s.storage.Head(token, filename) | |||
if err != nil { | |||
http.Error(w, http.StatusText(404), 404) | |||
return | |||
@@ -130,7 +131,7 @@ func (s *Server) previewHandler(w http.ResponseWriter, r *http.Request) { | |||
templatePath = "download.markdown.html" | |||
var reader io.ReadCloser | |||
if reader, _, _, err = s.storage.Get(token, filename); err != nil { | |||
if reader, _, err = s.storage.Get(token, filename); err != nil { | |||
http.Error(w, err.Error(), http.StatusInternalServerError) | |||
return | |||
} | |||
@@ -157,9 +158,9 @@ func (s *Server) previewHandler(w http.ResponseWriter, r *http.Request) { | |||
} | |||
relativeURL, _ := url.Parse(path.Join(s.proxyPath, token, filename)) | |||
resolvedURL := resolveURL(r, relativeURL) | |||
resolvedURL := resolveURL(r, relativeURL, s.proxyPort) | |||
relativeURLGet, _ := url.Parse(path.Join(s.proxyPath, getPathPart, token, filename)) | |||
resolvedURLGet := resolveURL(r, relativeURLGet) | |||
resolvedURLGet := resolveURL(r, relativeURLGet, s.proxyPort) | |||
var png []byte | |||
png, err = qrcode.Encode(resolvedURL, qrcode.High, 150) | |||
if err != nil { | |||
@@ -169,8 +170,8 @@ func (s *Server) previewHandler(w http.ResponseWriter, r *http.Request) { | |||
qrCode := base64.StdEncoding.EncodeToString(png) | |||
hostname := getURL(r).Host | |||
webAddress := resolveWebAddress(r, s.proxyPath) | |||
hostname := getURL(r, s.proxyPort).Host | |||
webAddress := resolveWebAddress(r, s.proxyPath, s.proxyPort) | |||
data := struct { | |||
ContentType string | |||
@@ -211,8 +212,8 @@ func (s *Server) previewHandler(w http.ResponseWriter, r *http.Request) { | |||
func (s *Server) viewHandler(w http.ResponseWriter, r *http.Request) { | |||
// vars := mux.Vars(r) | |||
hostname := getURL(r).Host | |||
webAddress := resolveWebAddress(r, s.proxyPath) | |||
hostname := getURL(r, s.proxyPort).Host | |||
webAddress := resolveWebAddress(r, s.proxyPath, s.proxyPort) | |||
data := struct { | |||
Hostname string | |||
@@ -310,6 +311,12 @@ func (s *Server) postHandler(w http.ResponseWriter, r *http.Request) { | |||
contentLength := n | |||
if s.maxUploadSize > 0 && contentLength > s.maxUploadSize { | |||
log.Print("Entity too large") | |||
http.Error(w, http.StatusText(http.StatusRequestEntityTooLarge), http.StatusRequestEntityTooLarge) | |||
return | |||
} | |||
metadata := MetadataForRequest(contentType, r) | |||
buffer := &bytes.Buffer{} | |||
@@ -338,7 +345,7 @@ func (s *Server) postHandler(w http.ResponseWriter, r *http.Request) { | |||
filename = url.PathEscape(filename) | |||
relativeURL, _ := url.Parse(path.Join(s.proxyPath, token, filename)) | |||
fmt.Fprintln(w, getURL(r).ResolveReference(relativeURL).String()) | |||
fmt.Fprintln(w, getURL(r, s.proxyPort).ResolveReference(relativeURL).String()) | |||
cleanTmpFile(file) | |||
} | |||
@@ -454,6 +461,12 @@ func (s *Server) putHandler(w http.ResponseWriter, r *http.Request) { | |||
contentLength = n | |||
} | |||
if s.maxUploadSize > 0 && contentLength > s.maxUploadSize { | |||
log.Print("Entity too large") | |||
http.Error(w, http.StatusText(http.StatusRequestEntityTooLarge), http.StatusRequestEntityTooLarge) | |||
return | |||
} | |||
if contentLength == 0 { | |||
log.Print("Empty content-length") | |||
http.Error(w, errors.New("Could not upload empty file").Error(), 400) | |||
@@ -499,15 +512,15 @@ func (s *Server) putHandler(w http.ResponseWriter, r *http.Request) { | |||
relativeURL, _ := url.Parse(path.Join(s.proxyPath, token, filename)) | |||
deleteURL, _ := url.Parse(path.Join(s.proxyPath, token, filename, metadata.DeletionToken)) | |||
w.Header().Set("X-Url-Delete", resolveURL(r, deleteURL)) | |||
w.Header().Set("X-Url-Delete", resolveURL(r, deleteURL, s.proxyPort)) | |||
fmt.Fprint(w, resolveURL(r, relativeURL)) | |||
fmt.Fprint(w, resolveURL(r, relativeURL, s.proxyPort)) | |||
} | |||
func resolveURL(r *http.Request, u *url.URL) string { | |||
func resolveURL(r *http.Request, u *url.URL, proxyPort string) string { | |||
r.URL.Path = "" | |||
return getURL(r).ResolveReference(u).String() | |||
return getURL(r, proxyPort).ResolveReference(u).String() | |||
} | |||
func resolveKey(key, proxyPath string) string { | |||
@@ -524,8 +537,8 @@ func resolveKey(key, proxyPath string) string { | |||
return key | |||
} | |||
func resolveWebAddress(r *http.Request, proxyPath string) string { | |||
url := getURL(r) | |||
func resolveWebAddress(r *http.Request, proxyPath string, proxyPort string) string { | |||
url := getURL(r, proxyPort) | |||
var webAddress string | |||
@@ -543,8 +556,22 @@ func resolveWebAddress(r *http.Request, proxyPath string) string { | |||
return webAddress | |||
} | |||
func getURL(r *http.Request) *url.URL { | |||
u, _ := url.Parse(r.URL.String()) | |||
// Similar to the logic found here: | |||
// https://github.com/golang/go/blob/release-branch.go1.14/src/net/http/clone.go#L22-L33 | |||
func cloneURL(u *url.URL) *url.URL { | |||
c := &url.URL{} | |||
*c = *u | |||
if u.User != nil { | |||
c.User = &url.Userinfo{} | |||
*c.User = *u.User | |||
} | |||
return c | |||
} | |||
func getURL(r *http.Request, proxyPort string) *url.URL { | |||
u := cloneURL(r.URL) | |||
if r.TLS != nil { | |||
u.Scheme = "https" | |||
@@ -554,16 +581,25 @@ func getURL(r *http.Request) *url.URL { | |||
u.Scheme = "http" | |||
} | |||
if u.Host != "" { | |||
} else if host, port, err := net.SplitHostPort(r.Host); err != nil { | |||
u.Host = r.Host | |||
} else { | |||
if port == "80" && u.Scheme == "http" { | |||
u.Host = host | |||
} else if port == "443" && u.Scheme == "https" { | |||
if u.Host == "" { | |||
host, port, err := net.SplitHostPort(r.Host) | |||
if err != nil { | |||
host = r.Host | |||
port = "" | |||
} | |||
if len(proxyPort) != 0 { | |||
port = proxyPort | |||
} | |||
if len(port) == 0 { | |||
u.Host = host | |||
} else { | |||
u.Host = net.JoinHostPort(host, port) | |||
if port == "80" && u.Scheme == "http" { | |||
u.Host = host | |||
} else if port == "443" && u.Scheme == "https" { | |||
u.Host = host | |||
} else { | |||
u.Host = net.JoinHostPort(host, port) | |||
} | |||
} | |||
} | |||
@@ -612,10 +648,8 @@ func (s *Server) CheckMetadata(token, filename string, increaseDownload bool) (M | |||
var metadata Metadata | |||
r, _, _, err := s.storage.Get(token, fmt.Sprintf("%s.metadata", filename)) | |||
if s.storage.IsNotExist(err) { | |||
return metadata, nil | |||
} else if err != nil { | |||
r, _, err := s.storage.Get(token, fmt.Sprintf("%s.metadata", filename)) | |||
if err != nil { | |||
return metadata, err | |||
} | |||
@@ -652,7 +686,7 @@ func (s *Server) CheckDeletionToken(deletionToken, token, filename string) error | |||
var metadata Metadata | |||
r, _, _, err := s.storage.Get(token, fmt.Sprintf("%s.metadata", filename)) | |||
r, _, err := s.storage.Get(token, fmt.Sprintf("%s.metadata", filename)) | |||
if s.storage.IsNotExist(err) { | |||
return nil | |||
} else if err != nil { | |||
@@ -670,6 +704,19 @@ func (s *Server) CheckDeletionToken(deletionToken, token, filename string) error | |||
return nil | |||
} | |||
func (s *Server) purgeHandler() { | |||
ticker := time.NewTicker(s.purgeInterval) | |||
go func() { | |||
for { | |||
select { | |||
case <-ticker.C: | |||
err := s.storage.Purge(s.purgeDays) | |||
log.Printf("error cleaning up expired files: %v", err) | |||
} | |||
} | |||
}() | |||
} | |||
func (s *Server) deleteHandler(w http.ResponseWriter, r *http.Request) { | |||
vars := mux.Vars(r) | |||
@@ -718,7 +765,7 @@ func (s *Server) zipHandler(w http.ResponseWriter, r *http.Request) { | |||
continue | |||
} | |||
reader, _, _, err := s.storage.Get(token, filename) | |||
reader, _, err := s.storage.Get(token, filename) | |||
if err != nil { | |||
if s.storage.IsNotExist(err) { | |||
@@ -790,7 +837,7 @@ func (s *Server) tarGzHandler(w http.ResponseWriter, r *http.Request) { | |||
continue | |||
} | |||
reader, _, contentLength, err := s.storage.Get(token, filename) | |||
reader, contentLength, err := s.storage.Get(token, filename) | |||
if err != nil { | |||
if s.storage.IsNotExist(err) { | |||
http.Error(w, "File not found", 404) | |||
@@ -849,7 +896,7 @@ func (s *Server) tarHandler(w http.ResponseWriter, r *http.Request) { | |||
continue | |||
} | |||
reader, _, contentLength, err := s.storage.Get(token, filename) | |||
reader, contentLength, err := s.storage.Get(token, filename) | |||
if err != nil { | |||
if s.storage.IsNotExist(err) { | |||
http.Error(w, "File not found", 404) | |||
@@ -897,7 +944,8 @@ func (s *Server) headHandler(w http.ResponseWriter, r *http.Request) { | |||
return | |||
} | |||
contentType, contentLength, err := s.storage.Head(token, filename) | |||
contentType := metadata.ContentType | |||
contentLength, err := s.storage.Head(token, filename) | |||
if s.storage.IsNotExist(err) { | |||
http.Error(w, http.StatusText(http.StatusNotFound), http.StatusNotFound) | |||
return | |||
@@ -931,7 +979,8 @@ func (s *Server) getHandler(w http.ResponseWriter, r *http.Request) { | |||
return | |||
} | |||
reader, contentType, contentLength, err := s.storage.Get(token, filename) | |||
contentType := metadata.ContentType | |||
reader, contentLength, err := s.storage.Get(token, filename) | |||
if s.storage.IsNotExist(err) { | |||
http.Error(w, http.StatusText(http.StatusNotFound), http.StatusNotFound) | |||
return | |||
@@ -1008,7 +1057,7 @@ func (s *Server) RedirectHandler(h http.Handler) http.HandlerFunc { | |||
} else if r.Header.Get("X-Forwarded-Proto") == "https" { | |||
} else if r.URL.Scheme == "https" { | |||
} else { | |||
u := getURL(r) | |||
u := getURL(r, s.proxyPort) | |||
u.Scheme = "https" | |||
http.Redirect(w, r, u.String(), http.StatusPermanentRedirect) | |||
@@ -25,7 +25,10 @@ THE SOFTWARE. | |||
package server | |||
import ( | |||
crypto_rand "crypto/rand" | |||
"encoding/binary" | |||
"errors" | |||
gorillaHandlers "github.com/gorilla/handlers" | |||
"log" | |||
"math/rand" | |||
"mime" | |||
@@ -85,6 +88,13 @@ func Listener(s string) OptionFn { | |||
} | |||
func CorsDomains(s string) OptionFn { | |||
return func(srvr *Server) { | |||
srvr.CorsDomains = s | |||
} | |||
} | |||
func GoogleAnalytics(gaKey string) OptionFn { | |||
return func(srvr *Server) { | |||
srvr.gaKey = gaKey | |||
@@ -131,6 +141,12 @@ func ProxyPath(s string) OptionFn { | |||
} | |||
} | |||
func ProxyPort(s string) OptionFn { | |||
return func(srvr *Server) { | |||
srvr.proxyPort = s | |||
} | |||
} | |||
func TempPath(s string) OptionFn { | |||
return func(srvr *Server) { | |||
if s[len(s)-1:] != "/" { | |||
@@ -159,12 +175,25 @@ func Logger(logger *log.Logger) OptionFn { | |||
} | |||
} | |||
func MaxUploadSize(kbytes int64) OptionFn { | |||
return func(srvr *Server) { | |||
srvr.maxUploadSize = kbytes * 1024 | |||
} | |||
} | |||
func RateLimit(requests int) OptionFn { | |||
return func(srvr *Server) { | |||
srvr.rateLimitRequests = requests | |||
} | |||
} | |||
func Purge(days, interval int) OptionFn { | |||
return func(srvr *Server) { | |||
srvr.purgeDays = time.Duration(days) * time.Hour * 24 | |||
srvr.purgeInterval = time.Duration(interval) * time.Hour | |||
} | |||
} | |||
func ForceHTTPs() OptionFn { | |||
return func(srvr *Server) { | |||
srvr.forceHTTPs = true | |||
@@ -255,8 +284,12 @@ type Server struct { | |||
locks map[string]*sync.Mutex | |||
maxUploadSize int64 | |||
rateLimitRequests int | |||
purgeDays time.Duration | |||
purgeInterval time.Duration | |||
storage Storage | |||
forceHTTPs bool | |||
@@ -270,11 +303,13 @@ type Server struct { | |||
webPath string | |||
proxyPath string | |||
proxyPort string | |||
gaKey string | |||
userVoiceKey string | |||
TLSListenerOnly bool | |||
CorsDomains string | |||
ListenerString string | |||
TLSListenerString string | |||
ProfileListenerString string | |||
@@ -297,7 +332,11 @@ func New(options ...OptionFn) (*Server, error) { | |||
} | |||
func init() { | |||
rand.Seed(time.Now().UTC().UnixNano()) | |||
var seedBytes [8]byte | |||
if _, err := crypto_rand.Read(seedBytes[:]); err != nil { | |||
panic("cannot obtain cryptographically secure seed") | |||
} | |||
rand.Seed(int64(binary.LittleEndian.Uint64(seedBytes[:]))) | |||
} | |||
func (s *Server) Run() { | |||
@@ -413,11 +452,24 @@ func (s *Server) Run() { | |||
s.logger.Printf("Transfer.sh server started.\nusing temp folder: %s\nusing storage provider: %s", s.tempPath, s.storage.Type()) | |||
var cors func(http.Handler) http.Handler | |||
if len(s.CorsDomains) > 0 { | |||
cors = gorillaHandlers.CORS( | |||
gorillaHandlers.AllowedHeaders([]string{"*"}), | |||
gorillaHandlers.AllowedOrigins(strings.Split(s.CorsDomains, ",")), | |||
gorillaHandlers.AllowedMethods([]string{"GET", "HEAD", "POST", "PUT", "DELETE", "OPTIONS"}), | |||
) | |||
} else { | |||
cors = func(h http.Handler) http.Handler { | |||
return h | |||
} | |||
} | |||
h := handlers.PanicHandler( | |||
IPFilterHandler( | |||
handlers.LogHandler( | |||
LoveHandler( | |||
s.RedirectHandler(r)), | |||
s.RedirectHandler(cors(r))), | |||
handlers.NewLogOptions(s.logger.Printf, "_default_"), | |||
), | |||
s.ipFilterOptions, | |||
@@ -458,6 +510,10 @@ func (s *Server) Run() { | |||
s.logger.Printf("---------------------------") | |||
if s.purgeDays > 0 { | |||
go s.purgeHandler() | |||
} | |||
term := make(chan os.Signal, 1) | |||
signal.Notify(term, os.Interrupt) | |||
signal.Notify(term, syscall.SIGTERM) | |||
@@ -1,90 +0,0 @@ | |||
// +build gofuzz | |||
package server | |||
import ( | |||
"bytes" | |||
"io" | |||
"math/rand" | |||
"reflect" | |||
) | |||
const applicationOctetStream = "application/octet-stream" | |||
// FuzzLocalStorage tests the Local Storage. | |||
func FuzzLocalStorage(fuzz []byte) int { | |||
var fuzzLength = uint64(len(fuzz)) | |||
if fuzzLength == 0 { | |||
return -1 | |||
} | |||
storage, err := NewLocalStorage("/tmp", nil) | |||
if err != nil { | |||
panic("unable to create local storage") | |||
} | |||
token := Encode(10000000 + int64(rand.Intn(1000000000))) | |||
filename := Encode(10000000 + int64(rand.Intn(1000000000))) + ".bin" | |||
input := bytes.NewReader(fuzz) | |||
err = storage.Put(token, filename, input, applicationOctetStream, fuzzLength) | |||
if err != nil { | |||
panic("unable to save file") | |||
} | |||
contentType, contentLength, err := storage.Head(token, filename) | |||
if err != nil { | |||
panic("not visible through head") | |||
} | |||
if contentType != applicationOctetStream { | |||
panic("incorrect content type") | |||
} | |||
if contentLength != fuzzLength { | |||
panic("incorrect content length") | |||
} | |||
output, contentType, contentLength, err := storage.Get(token, filename) | |||
if err != nil { | |||
panic("not visible through get") | |||
} | |||
if contentType != applicationOctetStream { | |||
panic("incorrect content type") | |||
} | |||
if contentLength != fuzzLength { | |||
panic("incorrect content length") | |||
} | |||
var length uint64 | |||
b := make([]byte, len(fuzz)) | |||
for { | |||
n, err := output.Read(b) | |||
length += uint64(n) | |||
if err == io.EOF { | |||
break | |||
} | |||
} | |||
if !reflect.DeepEqual(b, fuzz) { | |||
panic("incorrect content body") | |||
} | |||
if length != fuzzLength { | |||
panic("incorrect content length") | |||
} | |||
err = storage.Delete(token, filename) | |||
if err != nil { | |||
panic("unable to delete file") | |||
} | |||
_, _, err = storage.Head(token, filename) | |||
if !storage.IsNotExist(err) { | |||
panic("file not deleted") | |||
} | |||
return 1 | |||
} |
@@ -2,16 +2,8 @@ package server | |||
import ( | |||
"encoding/json" | |||
"errors" | |||
"fmt" | |||
"io" | |||
"io/ioutil" | |||
"log" | |||
"mime" | |||
"net/http" | |||
"os" | |||
"path/filepath" | |||
"strings" | |||
"github.com/aws/aws-sdk-go/aws" | |||
"github.com/aws/aws-sdk-go/aws/awserr" | |||
"github.com/aws/aws-sdk-go/aws/session" | |||
@@ -22,14 +14,26 @@ import ( | |||
"golang.org/x/oauth2/google" | |||
"google.golang.org/api/drive/v3" | |||
"google.golang.org/api/googleapi" | |||
"io" | |||
"io/ioutil" | |||
"log" | |||
"net/http" | |||
"os" | |||
"path/filepath" | |||
"strings" | |||
"time" | |||
"storj.io/common/storj" | |||
"storj.io/uplink" | |||
) | |||
type Storage interface { | |||
Get(token string, filename string) (reader io.ReadCloser, contentType string, contentLength uint64, err error) | |||
Head(token string, filename string) (contentType string, contentLength uint64, err error) | |||
Get(token string, filename string) (reader io.ReadCloser, contentLength uint64, err error) | |||
Head(token string, filename string) (contentLength uint64, err error) | |||
Put(token string, filename string, reader io.Reader, contentType string, contentLength uint64) error | |||
Delete(token string, filename string) error | |||
IsNotExist(err error) bool | |||
Purge(days time.Duration) error | |||
Type() string | |||
} | |||
@@ -48,7 +52,7 @@ func (s *LocalStorage) Type() string { | |||
return "local" | |||
} | |||
func (s *LocalStorage) Head(token string, filename string) (contentType string, contentLength uint64, err error) { | |||
func (s *LocalStorage) Head(token string, filename string) (contentLength uint64, err error) { | |||
path := filepath.Join(s.basedir, token, filename) | |||
var fi os.FileInfo | |||
@@ -58,12 +62,10 @@ func (s *LocalStorage) Head(token string, filename string) (contentType string, | |||
contentLength = uint64(fi.Size()) | |||
contentType = mime.TypeByExtension(filepath.Ext(filename)) | |||
return | |||
} | |||
func (s *LocalStorage) Get(token string, filename string) (reader io.ReadCloser, contentType string, contentLength uint64, err error) { | |||
func (s *LocalStorage) Get(token string, filename string) (reader io.ReadCloser, contentLength uint64, err error) { | |||
path := filepath.Join(s.basedir, token, filename) | |||
// content type , content length | |||
@@ -78,8 +80,6 @@ func (s *LocalStorage) Get(token string, filename string) (reader io.ReadCloser, | |||
contentLength = uint64(fi.Size()) | |||
contentType = mime.TypeByExtension(filepath.Ext(filename)) | |||
return | |||
} | |||
@@ -92,6 +92,27 @@ func (s *LocalStorage) Delete(token string, filename string) (err error) { | |||
return | |||
} | |||
func (s *LocalStorage) Purge(days time.Duration) (err error) { | |||
err = filepath.Walk(s.basedir, | |||
func(path string, info os.FileInfo, err error) error { | |||
if err != nil { | |||
return err | |||
} | |||
if info.IsDir() { | |||
return nil | |||
} | |||
if info.ModTime().Before(time.Now().Add(-1 * days)) { | |||
err = os.Remove(path) | |||
return err | |||
} | |||
return nil | |||
}) | |||
return | |||
} | |||
func (s *LocalStorage) IsNotExist(err error) bool { | |||
if err == nil { | |||
return false | |||
@@ -129,20 +150,28 @@ type S3Storage struct { | |||
session *session.Session | |||
s3 *s3.S3 | |||
logger *log.Logger | |||
purgeDays time.Duration | |||
noMultipart bool | |||
} | |||
func NewS3Storage(accessKey, secretKey, bucketName, region, endpoint string, logger *log.Logger, disableMultipart bool, forcePathStyle bool) (*S3Storage, error) { | |||
func NewS3Storage(accessKey, secretKey, bucketName string, purgeDays int, region, endpoint string, disableMultipart bool, forcePathStyle bool, logger *log.Logger) (*S3Storage, error) { | |||
sess := getAwsSession(accessKey, secretKey, region, endpoint, forcePathStyle) | |||
return &S3Storage{bucket: bucketName, s3: s3.New(sess), session: sess, logger: logger, noMultipart: disableMultipart}, nil | |||
return &S3Storage{ | |||
bucket: bucketName, | |||
s3: s3.New(sess), | |||
session: sess, | |||
logger: logger, | |||
noMultipart: disableMultipart, | |||
purgeDays: time.Duration(purgeDays*24) * time.Hour, | |||
}, nil | |||
} | |||
func (s *S3Storage) Type() string { | |||
return "s3" | |||
} | |||
func (s *S3Storage) Head(token string, filename string) (contentType string, contentLength uint64, err error) { | |||
func (s *S3Storage) Head(token string, filename string) (contentLength uint64, err error) { | |||
key := fmt.Sprintf("%s/%s", token, filename) | |||
headRequest := &s3.HeadObjectInput{ | |||
@@ -156,10 +185,6 @@ func (s *S3Storage) Head(token string, filename string) (contentType string, con | |||
return | |||
} | |||
if response.ContentType != nil { | |||
contentType = *response.ContentType | |||
} | |||
if response.ContentLength != nil { | |||
contentLength = uint64(*response.ContentLength) | |||
} | |||
@@ -167,6 +192,11 @@ func (s *S3Storage) Head(token string, filename string) (contentType string, con | |||
return | |||
} | |||
func (s *S3Storage) Purge(days time.Duration) (err error) { | |||
// NOOP expiration is set at upload time | |||
return nil | |||
} | |||
func (s *S3Storage) IsNotExist(err error) bool { | |||
if err == nil { | |||
return false | |||
@@ -182,7 +212,7 @@ func (s *S3Storage) IsNotExist(err error) bool { | |||
return false | |||
} | |||
func (s *S3Storage) Get(token string, filename string) (reader io.ReadCloser, contentType string, contentLength uint64, err error) { | |||
func (s *S3Storage) Get(token string, filename string) (reader io.ReadCloser, contentLength uint64, err error) { | |||
key := fmt.Sprintf("%s/%s", token, filename) | |||
getRequest := &s3.GetObjectInput{ | |||
@@ -195,10 +225,6 @@ func (s *S3Storage) Get(token string, filename string) (reader io.ReadCloser, co | |||
return | |||
} | |||
if response.ContentType != nil { | |||
contentType = *response.ContentType | |||
} | |||
if response.ContentLength != nil { | |||
contentLength = uint64(*response.ContentLength) | |||
} | |||
@@ -248,9 +274,10 @@ func (s *S3Storage) Put(token string, filename string, reader io.Reader, content | |||
}) | |||
_, err = uploader.Upload(&s3manager.UploadInput{ | |||
Bucket: aws.String(s.bucket), | |||
Key: aws.String(key), | |||
Body: reader, | |||
Bucket: aws.String(s.bucket), | |||
Key: aws.String(key), | |||
Body: reader, | |||
Expires: aws.Time(time.Now().Add(s.purgeDays)), | |||
}) | |||
return | |||
@@ -398,7 +425,7 @@ func (s *GDrive) Type() string { | |||
return "gdrive" | |||
} | |||
func (s *GDrive) Head(token string, filename string) (contentType string, contentLength uint64, err error) { | |||
func (s *GDrive) Head(token string, filename string) (contentLength uint64, err error) { | |||
var fileId string | |||
fileId, err = s.findId(filename, token) | |||
if err != nil { | |||
@@ -406,18 +433,16 @@ func (s *GDrive) Head(token string, filename string) (contentType string, conten | |||
} | |||
var fi *drive.File | |||
if fi, err = s.service.Files.Get(fileId).Fields("mimeType", "size").Do(); err != nil { | |||
if fi, err = s.service.Files.Get(fileId).Fields("size").Do(); err != nil { | |||
return | |||
} | |||
contentLength = uint64(fi.Size) | |||
contentType = fi.MimeType | |||
return | |||
} | |||
func (s *GDrive) Get(token string, filename string) (reader io.ReadCloser, contentType string, contentLength uint64, err error) { | |||
func (s *GDrive) Get(token string, filename string) (reader io.ReadCloser, contentLength uint64, err error) { | |||
var fileId string | |||
fileId, err = s.findId(filename, token) | |||
if err != nil { | |||
@@ -425,14 +450,13 @@ func (s *GDrive) Get(token string, filename string) (reader io.ReadCloser, conte | |||
} | |||
var fi *drive.File | |||
fi, err = s.service.Files.Get(fileId).Fields("mimeType", "size", "md5Checksum").Do() | |||
fi, err = s.service.Files.Get(fileId).Fields("size", "md5Checksum").Do() | |||
if !s.hasChecksum(fi) { | |||
err = fmt.Errorf("Cannot find file %s/%s", token, filename) | |||
return | |||
} | |||
contentLength = uint64(fi.Size) | |||
contentType = fi.MimeType | |||
ctx := context.Background() | |||
var res *http.Response | |||
@@ -460,6 +484,34 @@ func (s *GDrive) Delete(token string, filename string) (err error) { | |||
return | |||
} | |||
func (s *GDrive) Purge(days time.Duration) (err error) { | |||
nextPageToken := "" | |||
expirationDate := time.Now().Add(-1 * days).Format(time.RFC3339) | |||
q := fmt.Sprintf("'%s' in parents and modifiedTime < '%s' and mimeType!='%s' and trashed=false", s.rootId, expirationDate, GDriveDirectoryMimeType) | |||
l, err := s.list(nextPageToken, q) | |||
if err != nil { | |||
return err | |||
} | |||
for 0 < len(l.Files) { | |||
for _, fi := range l.Files { | |||
err = s.service.Files.Delete(fi.Id).Do() | |||
if err != nil { | |||
return | |||
} | |||
} | |||
if l.NextPageToken == "" { | |||
break | |||
} | |||
l, err = s.list(l.NextPageToken, q) | |||
} | |||
return | |||
} | |||
func (s *GDrive) IsNotExist(err error) bool { | |||
if err != nil { | |||
if e, ok := err.(*googleapi.Error); ok { | |||
@@ -561,3 +613,128 @@ func saveGDriveToken(path string, token *oauth2.Token, logger *log.Logger) { | |||
json.NewEncoder(f).Encode(token) | |||
} | |||
type StorjStorage struct { | |||
Storage | |||
project *uplink.Project | |||
bucket *uplink.Bucket | |||
purgeDays time.Duration | |||
logger *log.Logger | |||
} | |||
func NewStorjStorage(access, bucket string, purgeDays int, logger *log.Logger) (*StorjStorage, error) { | |||
var instance StorjStorage | |||
var err error | |||
ctx := context.TODO() | |||
parsedAccess, err := uplink.ParseAccess(access) | |||
if err != nil { | |||
return nil, err | |||
} | |||
instance.project, err = uplink.OpenProject(ctx, parsedAccess) | |||
if err != nil { | |||
return nil, err | |||
} | |||
instance.bucket, err = instance.project.EnsureBucket(ctx, bucket) | |||
if err != nil { | |||
//Ignoring the error to return the one that occurred first, but try to clean up. | |||
_ = instance.project.Close() | |||
return nil, err | |||
} | |||
instance.purgeDays = time.Duration(purgeDays*24) * time.Hour | |||
instance.logger = logger | |||
return &instance, nil | |||
} | |||
func (s *StorjStorage) Type() string { | |||
return "storj" | |||
} | |||
func (s *StorjStorage) Head(token string, filename string) (contentLength uint64, err error) { | |||
key := storj.JoinPaths(token, filename) | |||
ctx := context.TODO() | |||
obj, err := s.project.StatObject(ctx, s.bucket.Name, key) | |||
if err != nil { | |||
return 0, err | |||
} | |||
contentLength = uint64(obj.System.ContentLength) | |||
return | |||
} | |||
func (s *StorjStorage) Get(token string, filename string) (reader io.ReadCloser, contentLength uint64, err error) { | |||
key := storj.JoinPaths(token, filename) | |||
s.logger.Printf("Getting file %s from Storj Bucket", filename) | |||
ctx := context.TODO() | |||
download, err := s.project.DownloadObject(ctx, s.bucket.Name, key, nil) | |||
if err != nil { | |||
return nil, 0, err | |||
} | |||
contentLength = uint64(download.Info().System.ContentLength) | |||
reader = download | |||
return | |||
} | |||
func (s *StorjStorage) Delete(token string, filename string) (err error) { | |||
key := storj.JoinPaths(token, filename) | |||
s.logger.Printf("Deleting file %s from Storj Bucket", filename) | |||
ctx := context.TODO() | |||
_, err = s.project.DeleteObject(ctx, s.bucket.Name, key) | |||
return | |||
} | |||
func (s *StorjStorage) Purge(days time.Duration) (err error) { | |||
// NOOP expiration is set at upload time | |||
return nil | |||
} | |||
func (s *StorjStorage) Put(token string, filename string, reader io.Reader, contentType string, contentLength uint64) (err error) { | |||
key := storj.JoinPaths(token, filename) | |||
s.logger.Printf("Uploading file %s to Storj Bucket", filename) | |||
ctx := context.TODO() | |||
writer, err := s.project.UploadObject(ctx, s.bucket.Name, key, &uplink.UploadOptions{Expires: time.Now().Add(s.purgeDays)}) | |||
if err != nil { | |||
return err | |||
} | |||
n, err := io.Copy(writer, reader) | |||
if err != nil || uint64(n) != contentLength { | |||
//Ignoring the error to return the one that occurred first, but try to clean up. | |||
_ = writer.Abort() | |||
return err | |||
} | |||
err = writer.SetCustomMetadata(ctx, uplink.CustomMetadata{"content-type": contentType}) | |||
if err != nil { | |||
//Ignoring the error to return the one that occurred first, but try to clean up. | |||
_ = writer.Abort() | |||
return err | |||
} | |||
err = writer.Commit() | |||
return err | |||
} | |||
func (s *StorjStorage) IsNotExist(err error) bool { | |||
return errors.Is(err, uplink.ErrObjectNotFound) | |||
} |