Compare commits
24 Commits
Author | SHA1 | Date | |
---|---|---|---|
|
e97fdcb293 | ||
|
9fe5f9a5c9 | ||
|
788dfa203f | ||
|
440ccf3a57 | ||
|
224f8dca42 | ||
|
d16a6cc25e | ||
|
49a083d8a2 | ||
|
f38eb6fb77 | ||
|
8f5a0a80f3 | ||
|
a895846bbc | ||
|
3990c3c5b7 | ||
|
3ea4ffd0e3 | ||
|
88003018e6 | ||
|
9baefbc4bf | ||
|
e9148e0c20 | ||
|
2ecedb93c6 | ||
|
1ff9ccd01e | ||
|
ca17555c22 | ||
|
a7a3e685fe | ||
|
94f94f27ff | ||
|
69d979ca48 | ||
|
d9198e78db | ||
|
811679f8fb | ||
|
ce0c9b8915 |
@@ -6,7 +6,6 @@ bin
|
|||||||
*.pyc
|
*.pyc
|
||||||
*.egg-info
|
*.egg-info
|
||||||
.vagrant
|
.vagrant
|
||||||
.git
|
|
||||||
.tmp
|
.tmp
|
||||||
bower_components
|
bower_components
|
||||||
node_modules
|
node_modules
|
||||||
|
33
.github/build/friendly-filenames.json
vendored
Normal file
33
.github/build/friendly-filenames.json
vendored
Normal file
@@ -0,0 +1,33 @@
|
|||||||
|
{
|
||||||
|
"android-arm64": { "friendlyName": "android-arm64-v8a" },
|
||||||
|
"darwin-amd64": { "friendlyName": "darwin-amd64" },
|
||||||
|
"darwin-arm64": { "friendlyName": "darwin-arm64" },
|
||||||
|
"dragonfly-amd64": { "friendlyName": "dragonfly-amd64" },
|
||||||
|
"freebsd-386": { "friendlyName": "freebsd-386" },
|
||||||
|
"freebsd-amd64": { "friendlyName": "freebsd-amd64" },
|
||||||
|
"freebsd-arm64": { "friendlyName": "freebsd-arm64-v8a" },
|
||||||
|
"freebsd-arm7": { "friendlyName": "freebsd-arm32-v7a" },
|
||||||
|
"linux-386": { "friendlyName": "linux-386" },
|
||||||
|
"linux-amd64": { "friendlyName": "linux-amd64" },
|
||||||
|
"linux-arm5": { "friendlyName": "linux-arm32-v5" },
|
||||||
|
"linux-arm64": { "friendlyName": "linux-arm64-v8a" },
|
||||||
|
"linux-arm6": { "friendlyName": "linux-arm32-v6" },
|
||||||
|
"linux-arm7": { "friendlyName": "linux-armv7" },
|
||||||
|
"linux-mips64le": { "friendlyName": "linux-mips64le" },
|
||||||
|
"linux-mips64": { "friendlyName": "linux-mips64" },
|
||||||
|
"linux-mipslesoftfloat": { "friendlyName": "linux-mips32le-softfloat" },
|
||||||
|
"linux-mipsle": { "friendlyName": "linux-mips32le" },
|
||||||
|
"linux-mipssoftfloat": { "friendlyName": "linux-mips32-softfloat" },
|
||||||
|
"linux-mips": { "friendlyName": "linux-mips32" },
|
||||||
|
"linux-ppc64le": { "friendlyName": "linux-ppc64le" },
|
||||||
|
"linux-ppc64": { "friendlyName": "linux-ppc64" },
|
||||||
|
"linux-riscv64": { "friendlyName": "linux-riscv64" },
|
||||||
|
"linux-s390x": { "friendlyName": "linux-s390x" },
|
||||||
|
"openbsd-386": { "friendlyName": "openbsd-386" },
|
||||||
|
"openbsd-amd64": { "friendlyName": "openbsd-amd64" },
|
||||||
|
"openbsd-arm64": { "friendlyName": "openbsd-arm64-v8a" },
|
||||||
|
"openbsd-arm7": { "friendlyName": "openbsd-arm32-v7a" },
|
||||||
|
"windows-386": { "friendlyName": "windows-386" },
|
||||||
|
"windows-amd64": { "friendlyName": "windows-amd64" },
|
||||||
|
"windows-arm7": { "friendlyName": "windows-arm32-v7a" }
|
||||||
|
}
|
202
.github/workflows/release.yml
vendored
202
.github/workflows/release.yml
vendored
@@ -1,59 +1,171 @@
|
|||||||
name: release
|
name: Build and Release
|
||||||
|
|
||||||
on:
|
on:
|
||||||
push:
|
workflow_dispatch:
|
||||||
tags:
|
release:
|
||||||
- v*
|
types: [published]
|
||||||
jobs:
|
jobs:
|
||||||
build:
|
build:
|
||||||
runs-on: ubuntu-latest
|
|
||||||
strategy:
|
strategy:
|
||||||
matrix:
|
matrix:
|
||||||
GOOS: [ darwin, linux, windows ]
|
# Include amd64 on all platforms.
|
||||||
GOARCH: [ amd64 ]
|
goos: [windows, freebsd, openbsd, linux, dragonfly, darwin]
|
||||||
|
goarch: [amd64, 386]
|
||||||
|
exclude:
|
||||||
|
# Exclude i386 on darwin and dragonfly.
|
||||||
|
- goarch: 386
|
||||||
|
goos: dragonfly
|
||||||
|
- goarch: 386
|
||||||
|
goos: darwin
|
||||||
include:
|
include:
|
||||||
- GOOS: darwin
|
# BEIGIN MacOS ARM64
|
||||||
GOARCH: amd64
|
- goos: darwin
|
||||||
suffix: darwin-amd64
|
goarch: arm64
|
||||||
- GOOS: windows
|
# END MacOS ARM64
|
||||||
GOARCH: amd64
|
# BEGIN Linux ARM 5 6 7
|
||||||
suffix: windows-amd64.exe
|
- goos: linux
|
||||||
- GOOS: linux
|
goarch: arm
|
||||||
GOARCH: amd64
|
goarm: 7
|
||||||
suffix: linux-amd64
|
- goos: linux
|
||||||
- GOOS: linux
|
goarch: arm
|
||||||
GOARCH: arm
|
goarm: 6
|
||||||
suffix: linux-armv7
|
- goos: linux
|
||||||
steps:
|
goarch: arm
|
||||||
- uses: actions/checkout@v2
|
goarm: 5
|
||||||
- uses: actions/setup-go@v2
|
# END Linux ARM 5 6 7
|
||||||
with:
|
# BEGIN Android ARM 8
|
||||||
go-version: 1.16.x
|
- goos: android
|
||||||
- name: Build ${{ matrix.suffix }} (GOOS=${{ matrix.GOOS }}, GOARCH=${{ matrix.GOARCH }})
|
goarch: arm64
|
||||||
env:
|
# END Android ARM 8
|
||||||
GOOS: ${{ matrix.GOOS }}
|
# Windows ARM 7
|
||||||
GOARCH: ${{ matrix.GOARCH }}
|
- goos: windows
|
||||||
CGO_ENABLED: 0
|
goarch: arm
|
||||||
run: |
|
goarm: 7
|
||||||
go version
|
# BEGIN Other architectures
|
||||||
go build -tags netgo -ldflags '-a -s -w -extldflags "-static"' -o ./artifacts/transfersh-${GITHUB_REF##*/}-${{ matrix.suffix }}
|
# BEGIN riscv64 & ARM64
|
||||||
- uses: actions/upload-artifact@v2
|
- goos: linux
|
||||||
name: Upload artifacts
|
goarch: arm64
|
||||||
with:
|
- goos: linux
|
||||||
name: artifacts
|
goarch: riscv64
|
||||||
path: ./artifacts
|
# END riscv64 & ARM64
|
||||||
|
# BEGIN MIPS
|
||||||
|
- goos: linux
|
||||||
|
goarch: mips64
|
||||||
|
- goos: linux
|
||||||
|
goarch: mips64le
|
||||||
|
- goos: linux
|
||||||
|
goarch: mipsle
|
||||||
|
- goos: linux
|
||||||
|
goarch: mips
|
||||||
|
# END MIPS
|
||||||
|
# BEGIN PPC
|
||||||
|
- goos: linux
|
||||||
|
goarch: ppc64
|
||||||
|
- goos: linux
|
||||||
|
goarch: ppc64le
|
||||||
|
# END PPC
|
||||||
|
# BEGIN FreeBSD ARM
|
||||||
|
- goos: freebsd
|
||||||
|
goarch: arm64
|
||||||
|
- goos: freebsd
|
||||||
|
goarch: arm
|
||||||
|
goarm: 7
|
||||||
|
# END FreeBSD ARM
|
||||||
|
# BEGIN S390X
|
||||||
|
- goos: linux
|
||||||
|
goarch: s390x
|
||||||
|
# END S390X
|
||||||
|
# END Other architectures
|
||||||
|
# BEGIN OPENBSD ARM
|
||||||
|
- goos: openbsd
|
||||||
|
goarch: arm64
|
||||||
|
- goos: openbsd
|
||||||
|
goarch: arm
|
||||||
|
goarm: 7
|
||||||
|
# END OPENBSD ARM
|
||||||
|
fail-fast: false
|
||||||
|
|
||||||
release:
|
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
needs: [ build ]
|
env:
|
||||||
|
GOOS: ${{ matrix.goos }}
|
||||||
|
GOARCH: ${{ matrix.goarch }}
|
||||||
|
GOARM: ${{ matrix.goarm }}
|
||||||
|
CGO_ENABLED: 0
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/download-artifact@v2
|
- name: Checkout codebase
|
||||||
name: Download artifacts
|
uses: actions/checkout@v2
|
||||||
|
|
||||||
|
- name: Show workflow information
|
||||||
|
id: get_filename
|
||||||
|
run: |
|
||||||
|
export _NAME=$(jq ".[\"$GOOS-$GOARCH$GOARM$GOMIPS\"].friendlyName" -r < .github/build/friendly-filenames.json)
|
||||||
|
echo "GOOS: $GOOS, GOARCH: $GOARCH, GOARM: $GOARM, GOMIPS: $GOMIPS, RELEASE_NAME: $_NAME"
|
||||||
|
echo "::set-output name=ASSET_NAME::$_NAME"
|
||||||
|
echo "::set-output name=GIT_TAG::${GITHUB_REF##*/}"
|
||||||
|
echo "ASSET_NAME=$_NAME" >> $GITHUB_ENV
|
||||||
|
|
||||||
|
- name: Set up Go
|
||||||
|
uses: actions/setup-go@v2
|
||||||
with:
|
with:
|
||||||
name: artifacts
|
go-version: ^1.16
|
||||||
path: ./artifacts
|
|
||||||
- name: Publish artifacts
|
- name: Get project dependencies
|
||||||
|
run: go mod download
|
||||||
|
|
||||||
|
- name: Build Transfersh
|
||||||
|
run: |
|
||||||
|
mkdir -p build_assets
|
||||||
|
go build -tags netgo -ldflags "-X github.com/dutchcoders/transfer.sh/cmd.Version=${GITHUB_REF##*/} -a -s -w -extldflags '-static'" -o build_assets/transfersh-${GITHUB_REF##*/}-${ASSET_NAME}
|
||||||
|
|
||||||
|
- name: Build Mips softfloat Transfersh
|
||||||
|
if: matrix.goarch == 'mips' || matrix.goarch == 'mipsle'
|
||||||
|
run: |
|
||||||
|
GOMIPS=softfloat go build -tags netgo -ldflags "-X github.com/dutchcoders/transfer.sh/cmd.Version=${GITHUB_REF##*/} -a -s -w -extldflags '-static'" -o build_assets/transfersh-softfloat-${GITHUB_REF##*/}-${ASSET_NAME}
|
||||||
|
|
||||||
|
- name: Rename Windows Transfersh
|
||||||
|
if: matrix.goos == 'windows'
|
||||||
|
run: |
|
||||||
|
cd ./build_assets || exit 1
|
||||||
|
mv transfersh-${GITHUB_REF##*/}-${ASSET_NAME} transfersh-${GITHUB_REF##*/}-${ASSET_NAME}.exe
|
||||||
|
|
||||||
|
- name: Prepare to release
|
||||||
|
run: |
|
||||||
|
cp ${GITHUB_WORKSPACE}/README.md ./build_assets/README.md
|
||||||
|
cp ${GITHUB_WORKSPACE}/LICENSE ./build_assets/LICENSE
|
||||||
|
|
||||||
|
- name: Create Gzip archive
|
||||||
|
shell: bash
|
||||||
|
run: |
|
||||||
|
pushd build_assets || exit 1
|
||||||
|
touch -mt $(date +%Y01010000) *
|
||||||
|
tar zcvf transfersh-${GITHUB_REF##*/}-${ASSET_NAME}.tar.gz *
|
||||||
|
mv transfersh-${GITHUB_REF##*/}-${ASSET_NAME}.tar.gz ../
|
||||||
|
FILE=`find . -name "transfersh-${GITHUB_REF##*/}-${ASSET_NAME}*"`
|
||||||
|
DGST=$FILE.sha256sum
|
||||||
|
echo `sha256sum $FILE` > $DGST
|
||||||
|
popd || exit 1
|
||||||
|
FILE=./transfersh-${GITHUB_REF##*/}-${ASSET_NAME}.tar.gz
|
||||||
|
DGST=$FILE.sha256sum
|
||||||
|
echo `sha256sum $FILE` > $DGST
|
||||||
|
|
||||||
|
- name: Change the name
|
||||||
|
run: |
|
||||||
|
mv build_assets transfersh-${GITHUB_REF##*/}-${ASSET_NAME}
|
||||||
|
|
||||||
|
- name: Upload files to Artifacts
|
||||||
|
uses: actions/upload-artifact@v2
|
||||||
|
with:
|
||||||
|
name: transfersh-${{ steps.get_filename.outputs.GIT_TAG }}-${{ steps.get_filename.outputs.ASSET_NAME }}
|
||||||
|
path: |
|
||||||
|
./transfersh-${{ steps.get_filename.outputs.GIT_TAG }}-${{ steps.get_filename.outputs.ASSET_NAME }}/*
|
||||||
|
|
||||||
|
- name: Upload binaries to release
|
||||||
uses: softprops/action-gh-release@v1
|
uses: softprops/action-gh-release@v1
|
||||||
|
if: github.event_name == 'release'
|
||||||
with:
|
with:
|
||||||
files: './artifacts/*'
|
files: |
|
||||||
|
./transfersh-${{ steps.get_filename.outputs.GIT_TAG }}-${{ steps.get_filename.outputs.ASSET_NAME }}.tar.gz*
|
||||||
|
./transfersh-${{ steps.get_filename.outputs.GIT_TAG }}-${{ steps.get_filename.outputs.ASSET_NAME }}/transfersh-${{ steps.get_filename.outputs.GIT_TAG }}-${{ steps.get_filename.outputs.ASSET_NAME }}*
|
||||||
env:
|
env:
|
||||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||||
|
|
||||||
|
2
.gitignore
vendored
2
.gitignore
vendored
@@ -20,3 +20,5 @@ transfersh-server/run.sh
|
|||||||
.elasticbeanstalk/*
|
.elasticbeanstalk/*
|
||||||
!.elasticbeanstalk/*.cfg.yml
|
!.elasticbeanstalk/*.cfg.yml
|
||||||
!.elasticbeanstalk/*.global.yml
|
!.elasticbeanstalk/*.global.yml
|
||||||
|
|
||||||
|
!.github/build/
|
||||||
|
@@ -1,5 +1,5 @@
|
|||||||
# Default to Go 1.15
|
# Default to Go 1.16
|
||||||
ARG GO_VERSION=1.15
|
ARG GO_VERSION=1.16
|
||||||
FROM golang:${GO_VERSION}-alpine as build
|
FROM golang:${GO_VERSION}-alpine as build
|
||||||
|
|
||||||
# Necessary to run 'go get' and to compile the linked binary
|
# 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
|
ENV GO111MODULE=on
|
||||||
|
|
||||||
# build & install server
|
# build & install server
|
||||||
RUN go get -u ./... && CGO_ENABLED=0 go build -tags netgo -ldflags '-a -s -w -extldflags "-static"' -o /go/bin/transfersh github.com/dutchcoders/transfer.sh
|
RUN CGO_ENABLED=0 go build -tags netgo -ldflags "-X github.com/dutchcoders/transfer.sh/cmd.Version=$(git describe --tags) -a -s -w -extldflags '-static'" -o /go/bin/transfersh
|
||||||
|
|
||||||
FROM scratch AS final
|
FROM scratch AS final
|
||||||
LABEL maintainer="Andrea Spacca <andrea.spacca@gmail.com>"
|
LABEL maintainer="Andrea Spacca <andrea.spacca@gmail.com>"
|
||||||
|
2
LICENSE
2
LICENSE
@@ -1,6 +1,8 @@
|
|||||||
The MIT License (MIT)
|
The MIT License (MIT)
|
||||||
|
|
||||||
Copyright (c) 2014-2018 DutchCoders [https://github.com/dutchcoders/]
|
Copyright (c) 2014-2018 DutchCoders [https://github.com/dutchcoders/]
|
||||||
|
Copyright (c) 2018-2020 Andrea Spacca.
|
||||||
|
Copyright (c) 2020- Andrea Spacca and Stefan Benten.
|
||||||
|
|
||||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
of this software and associated documentation files (the "Software"), to deal
|
of this software and associated documentation files (the "Software"), to deal
|
||||||
|
@@ -1,4 +1,4 @@
|
|||||||
# transfer.sh [](https://gitter.im/dutchcoders/transfer.sh?utm_source=badge&utm_medium=badge&utm_campaign=&utm_campaign=pr-badge&utm_content=badge) [](https://goreportcard.com/report/github.com/dutchcoders/transfer.sh) [](https://hub.docker.com/r/dutchcoders/transfer.sh/) [](https://github.com/dutchcoders/transfer.sh/actions/workflows/test.yml?query=branch%3Amaster)
|
# transfer.sh [](https://goreportcard.com/report/github.com/dutchcoders/transfer.sh) [](https://hub.docker.com/r/dutchcoders/transfer.sh/) [](https://github.com/dutchcoders/transfer.sh/actions/workflows/test.yml?query=branch%3Amaster)
|
||||||
|
|
||||||
Easy and fast file sharing from the command-line. This code contains the server with everything you need to create your own instance.
|
Easy and fast file sharing from the command-line. This code contains the server with everything you need to create your own instance.
|
||||||
|
|
||||||
@@ -6,7 +6,7 @@ Transfer.sh currently supports the s3 (Amazon S3), gdrive (Google Drive), storj
|
|||||||
|
|
||||||
## Disclaimer
|
## Disclaimer
|
||||||
|
|
||||||
The service at https://transfersh.com is of unknown origin and reported as cloud malware.
|
The service at transfersh.com is of unknown origin and reported as cloud malware.
|
||||||
|
|
||||||
## Usage
|
## Usage
|
||||||
|
|
||||||
@@ -224,4 +224,7 @@ Contributions are welcome.
|
|||||||
## Copyright and license
|
## Copyright and license
|
||||||
|
|
||||||
Code and documentation copyright 2011-2018 Remco Verhoef.
|
Code and documentation copyright 2011-2018 Remco Verhoef.
|
||||||
|
Code and documentation copyright 2018-2020 Andrea Spacca.
|
||||||
|
Code and documentation copyright 2020- Andrea Spacca and Stefan Benten.
|
||||||
|
|
||||||
Code released under [the MIT license](LICENSE).
|
Code released under [the MIT license](LICENSE).
|
||||||
|
21
cmd/cmd.go
21
cmd/cmd.go
@@ -12,7 +12,8 @@ import (
|
|||||||
"google.golang.org/api/googleapi"
|
"google.golang.org/api/googleapi"
|
||||||
)
|
)
|
||||||
|
|
||||||
var Version = "1.2.4"
|
// Version is inject at build time
|
||||||
|
var Version = "0.0.0"
|
||||||
var helpTemplate = `NAME:
|
var helpTemplate = `NAME:
|
||||||
{{.Name}} - {{.Usage}}
|
{{.Name}} - {{.Usage}}
|
||||||
|
|
||||||
@@ -274,7 +275,7 @@ var globalFlags = []cli.Flag{
|
|||||||
Value: "",
|
Value: "",
|
||||||
EnvVar: "CORS_DOMAINS",
|
EnvVar: "CORS_DOMAINS",
|
||||||
},
|
},
|
||||||
cli.Int64Flag{
|
cli.IntFlag{
|
||||||
Name: "random-token-length",
|
Name: "random-token-length",
|
||||||
Usage: "",
|
Usage: "",
|
||||||
Value: 6,
|
Value: 6,
|
||||||
@@ -282,14 +283,16 @@ var globalFlags = []cli.Flag{
|
|||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Cmd wraps cli.app
|
||||||
type Cmd struct {
|
type Cmd struct {
|
||||||
*cli.App
|
*cli.App
|
||||||
}
|
}
|
||||||
|
|
||||||
func VersionAction(c *cli.Context) {
|
func versionAction(c *cli.Context) {
|
||||||
fmt.Println(color.YellowString(fmt.Sprintf("transfer.sh %s: Easy file sharing from the command line", Version)))
|
fmt.Println(color.YellowString(fmt.Sprintf("transfer.sh %s: Easy file sharing from the command line", Version)))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// New is the factory for transfer.sh
|
||||||
func New() *Cmd {
|
func New() *Cmd {
|
||||||
logger := log.New(os.Stdout, "[transfer.sh]", log.LstdFlags)
|
logger := log.New(os.Stdout, "[transfer.sh]", log.LstdFlags)
|
||||||
|
|
||||||
@@ -304,7 +307,7 @@ func New() *Cmd {
|
|||||||
app.Commands = []cli.Command{
|
app.Commands = []cli.Command{
|
||||||
{
|
{
|
||||||
Name: "version",
|
Name: "version",
|
||||||
Action: VersionAction,
|
Action: versionAction,
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -383,7 +386,7 @@ func New() *Cmd {
|
|||||||
options = append(options, server.RateLimit(v))
|
options = append(options, server.RateLimit(v))
|
||||||
}
|
}
|
||||||
|
|
||||||
v := c.Int64("random-token-length")
|
v := c.Int("random-token-length")
|
||||||
options = append(options, server.RandomTokenLength(v))
|
options = append(options, server.RandomTokenLength(v))
|
||||||
|
|
||||||
purgeDays := c.Int("purge-days")
|
purgeDays := c.Int("purge-days")
|
||||||
@@ -403,13 +406,13 @@ func New() *Cmd {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if c.Bool("force-https") {
|
if c.Bool("force-https") {
|
||||||
options = append(options, server.ForceHTTPs())
|
options = append(options, server.ForceHTTPS())
|
||||||
}
|
}
|
||||||
|
|
||||||
if httpAuthUser := c.String("http-auth-user"); httpAuthUser == "" {
|
if httpAuthUser := c.String("http-auth-user"); httpAuthUser == "" {
|
||||||
} else if httpAuthPass := c.String("http-auth-pass"); httpAuthPass == "" {
|
} else if httpAuthPass := c.String("http-auth-pass"); httpAuthPass == "" {
|
||||||
} else {
|
} else {
|
||||||
options = append(options, server.HttpAuthCredentials(httpAuthUser, httpAuthPass))
|
options = append(options, server.HTTPAuthCredentials(httpAuthUser, httpAuthPass))
|
||||||
}
|
}
|
||||||
|
|
||||||
applyIPFilter := false
|
applyIPFilter := false
|
||||||
@@ -445,13 +448,13 @@ func New() *Cmd {
|
|||||||
case "gdrive":
|
case "gdrive":
|
||||||
chunkSize := c.Int("gdrive-chunk-size")
|
chunkSize := c.Int("gdrive-chunk-size")
|
||||||
|
|
||||||
if clientJsonFilepath := c.String("gdrive-client-json-filepath"); clientJsonFilepath == "" {
|
if clientJSONFilepath := c.String("gdrive-client-json-filepath"); clientJSONFilepath == "" {
|
||||||
panic("client-json-filepath not set.")
|
panic("client-json-filepath not set.")
|
||||||
} else if localConfigPath := c.String("gdrive-local-config-path"); localConfigPath == "" {
|
} else if localConfigPath := c.String("gdrive-local-config-path"); localConfigPath == "" {
|
||||||
panic("local-config-path not set.")
|
panic("local-config-path not set.")
|
||||||
} else if basedir := c.String("basedir"); basedir == "" {
|
} else if basedir := c.String("basedir"); basedir == "" {
|
||||||
panic("basedir not set.")
|
panic("basedir not set.")
|
||||||
} else if storage, err := server.NewGDriveStorage(clientJsonFilepath, localConfigPath, basedir, chunkSize, logger); err != nil {
|
} else if storage, err := server.NewGDriveStorage(clientJSONFilepath, localConfigPath, basedir, chunkSize, logger); err != nil {
|
||||||
panic(err)
|
panic(err)
|
||||||
} else {
|
} else {
|
||||||
options = append(options, server.UseStorage(storage))
|
options = append(options, server.UseStorage(storage))
|
||||||
|
4
go.mod
4
go.mod
@@ -11,7 +11,7 @@ require (
|
|||||||
github.com/cpuguy83/go-md2man/v2 v2.0.0 // indirect
|
github.com/cpuguy83/go-md2man/v2 v2.0.0 // indirect
|
||||||
github.com/dutchcoders/go-clamd v0.0.0-20170520113014-b970184f4d9e
|
github.com/dutchcoders/go-clamd v0.0.0-20170520113014-b970184f4d9e
|
||||||
github.com/dutchcoders/go-virustotal v0.0.0-20140923143438-24cc8e6fa329
|
github.com/dutchcoders/go-virustotal v0.0.0-20140923143438-24cc8e6fa329
|
||||||
github.com/dutchcoders/transfer.sh-web v0.0.0-20210212072623-ac7014a9c3a7
|
github.com/dutchcoders/transfer.sh-web v0.0.0-20210819203540-bbdd40be1311
|
||||||
github.com/elazarl/go-bindata-assetfs v1.0.1
|
github.com/elazarl/go-bindata-assetfs v1.0.1
|
||||||
github.com/fatih/color v1.10.0
|
github.com/fatih/color v1.10.0
|
||||||
github.com/garyburd/redigo v1.6.2 // indirect
|
github.com/garyburd/redigo v1.6.2 // indirect
|
||||||
@@ -19,7 +19,7 @@ require (
|
|||||||
github.com/gorilla/handlers v1.5.1
|
github.com/gorilla/handlers v1.5.1
|
||||||
github.com/gorilla/mux v1.8.0
|
github.com/gorilla/mux v1.8.0
|
||||||
github.com/gorilla/securecookie v1.1.1 // indirect
|
github.com/gorilla/securecookie v1.1.1 // indirect
|
||||||
github.com/microcosm-cc/bluemonday v1.0.4
|
github.com/microcosm-cc/bluemonday v1.0.5
|
||||||
github.com/nu7hatch/gouuid v0.0.0-20131221200532-179d4d0c4d8d // indirect
|
github.com/nu7hatch/gouuid v0.0.0-20131221200532-179d4d0c4d8d // indirect
|
||||||
github.com/russross/blackfriday/v2 v2.1.0
|
github.com/russross/blackfriday/v2 v2.1.0
|
||||||
github.com/skip2/go-qrcode v0.0.0-20200617195104-da1b6568686e
|
github.com/skip2/go-qrcode v0.0.0-20200617195104-da1b6568686e
|
||||||
|
62
go.sum
62
go.sum
@@ -1,17 +1,14 @@
|
|||||||
cloud.google.com/go v0.16.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=
|
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.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.34.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=
|
||||||
cloud.google.com/go v0.38.0 h1:ROfEUZz+Gh5pa62DJWXSaonyu3StP6EA6lPEXPI6mCo=
|
|
||||||
cloud.google.com/go v0.38.0/go.mod h1:990N+gfupTy94rShfmMCWGDn0LpTmnzTp2qbd1dvSRU=
|
cloud.google.com/go v0.38.0/go.mod h1:990N+gfupTy94rShfmMCWGDn0LpTmnzTp2qbd1dvSRU=
|
||||||
cloud.google.com/go v0.44.1/go.mod h1:iSa0KzasP4Uvy3f1mN/7PiObzGgflwredwwASm/v6AU=
|
cloud.google.com/go v0.44.1/go.mod h1:iSa0KzasP4Uvy3f1mN/7PiObzGgflwredwwASm/v6AU=
|
||||||
cloud.google.com/go v0.44.2/go.mod h1:60680Gw3Yr4ikxnPRS/oxxkBccT6SA1yMk63TGekxKY=
|
cloud.google.com/go v0.44.2/go.mod h1:60680Gw3Yr4ikxnPRS/oxxkBccT6SA1yMk63TGekxKY=
|
||||||
cloud.google.com/go v0.45.1/go.mod h1:RpBamKRgapWJb87xiFSdk4g1CME7QZg3uwTez+TSTjc=
|
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.46.3/go.mod h1:a6bKKbmY7er1mI7TEI4lsAkts/mkhTSZK8w33B4RAg0=
|
||||||
cloud.google.com/go v0.50.0/go.mod h1:r9sluTvynVuxRIOHXQEHMFffphuXHOMZMycpNR5e6To=
|
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.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.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 v0.54.0/go.mod h1:1rq2OEkV3YMf6n/9ZvGWI3GWw0VoqH/1x2nd8Is/bPc=
|
||||||
cloud.google.com/go v0.56.0/go.mod h1:jr7tqZxxKOVYizybht9+26Z/gUq7tiRzu+ACVAMbKVk=
|
cloud.google.com/go v0.56.0/go.mod h1:jr7tqZxxKOVYizybht9+26Z/gUq7tiRzu+ACVAMbKVk=
|
||||||
cloud.google.com/go v0.57.0/go.mod h1:oXiQ6Rzq3RAkkY7N6t3TcE6jE+CIBBbA36lwQ1JyzZs=
|
cloud.google.com/go v0.57.0/go.mod h1:oXiQ6Rzq3RAkkY7N6t3TcE6jE+CIBBbA36lwQ1JyzZs=
|
||||||
@@ -61,7 +58,6 @@ github.com/btcsuite/goleveldb v0.0.0-20160330041536-7834afc9e8cd/go.mod h1:F+uVa
|
|||||||
github.com/btcsuite/snappy-go v0.0.0-20151229074030-0bdef8d06723/go.mod h1:8woku9dyThutzjeg+3xrA5iCpBRH8XEEg3lh6TiUghc=
|
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/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/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/calebcase/tmpfile v1.0.2-0.20200602150926-3af473ef8439/go.mod h1:iErLeG/iqJr8LaQ/gYRv4GXdqssi3jg4iSzvrA06/lw=
|
||||||
github.com/calebcase/tmpfile v1.0.2 h1:1AGuhKiUu4J6wxz6lxuF6ck3f8G2kaV6KSEny0RGCig=
|
github.com/calebcase/tmpfile v1.0.2 h1:1AGuhKiUu4J6wxz6lxuF6ck3f8G2kaV6KSEny0RGCig=
|
||||||
github.com/calebcase/tmpfile v1.0.2/go.mod h1:iErLeG/iqJr8LaQ/gYRv4GXdqssi3jg4iSzvrA06/lw=
|
github.com/calebcase/tmpfile v1.0.2/go.mod h1:iErLeG/iqJr8LaQ/gYRv4GXdqssi3jg4iSzvrA06/lw=
|
||||||
@@ -76,12 +72,10 @@ github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDk
|
|||||||
github.com/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f/go.mod h1:M8M6+tZqaGXZJjfX53e64911xZQV5JYwmTeXPW+k8Sc=
|
github.com/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f/go.mod h1:M8M6+tZqaGXZJjfX53e64911xZQV5JYwmTeXPW+k8Sc=
|
||||||
github.com/cncf/udpa/go v0.0.0-20200629203442-efcf912fb354/go.mod h1:WmhPx2Nbnhtbo57+VJT5O0JRkEi1Wbu0z5j0R8u5Hbk=
|
github.com/cncf/udpa/go v0.0.0-20200629203442-efcf912fb354/go.mod h1:WmhPx2Nbnhtbo57+VJT5O0JRkEi1Wbu0z5j0R8u5Hbk=
|
||||||
github.com/cncf/udpa/go v0.0.0-20201120205902-5459f2c99403/go.mod h1:WmhPx2Nbnhtbo57+VJT5O0JRkEi1Wbu0z5j0R8u5Hbk=
|
github.com/cncf/udpa/go v0.0.0-20201120205902-5459f2c99403/go.mod h1:WmhPx2Nbnhtbo57+VJT5O0JRkEi1Wbu0z5j0R8u5Hbk=
|
||||||
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-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 h1:EoUDS0afbrsXAZ9YQ9jdu/mZ2sXgT1/2yyNng4PGlyM=
|
||||||
github.com/cpuguy83/go-md2man/v2 v2.0.0/go.mod h1:maD7wRr/U5Z6m/iR4s+kqSMx2CaBsrgA7czyZG/E6dU=
|
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 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.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 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=
|
||||||
@@ -90,8 +84,10 @@ github.com/dutchcoders/go-clamd v0.0.0-20170520113014-b970184f4d9e h1:rcHHSQqzCg
|
|||||||
github.com/dutchcoders/go-clamd v0.0.0-20170520113014-b970184f4d9e/go.mod h1:Byz7q8MSzSPkouskHJhX0er2mZY/m0Vj5bMeMCkkyY4=
|
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=
|
github.com/dutchcoders/go-virustotal v0.0.0-20140923143438-24cc8e6fa329 h1:ERqCkG/uSyT74P1m/j9yR+so+7ynY4fbTvLY/Mr1ZMg=
|
||||||
github.com/dutchcoders/go-virustotal v0.0.0-20140923143438-24cc8e6fa329/go.mod h1:G5qOfE5bQZ5scycLpB7fYWgN4y3xdfXo+pYWM8z2epY=
|
github.com/dutchcoders/go-virustotal v0.0.0-20140923143438-24cc8e6fa329/go.mod h1:G5qOfE5bQZ5scycLpB7fYWgN4y3xdfXo+pYWM8z2epY=
|
||||||
github.com/dutchcoders/transfer.sh-web v0.0.0-20210212072623-ac7014a9c3a7 h1:zKJw+RxTDbypWVb0HfPs43bj/ee65Bl7rIDPO7HixrQ=
|
github.com/dutchcoders/transfer.sh-web v0.0.0-20210723094506-f0946ebceb7a h1:+N7J1NK7gxKZ+X4syY1HqafUudJiR8voJGcXWkxLgAw=
|
||||||
github.com/dutchcoders/transfer.sh-web v0.0.0-20210212072623-ac7014a9c3a7/go.mod h1:jTzXZabwihvQgvmySgD4f4GNszimkXK3o8x1ucH1z5Q=
|
github.com/dutchcoders/transfer.sh-web v0.0.0-20210723094506-f0946ebceb7a/go.mod h1:F6Q37CxDh2MHr5KXkcZmNB3tdkK7v+bgE+OpBY+9ilI=
|
||||||
|
github.com/dutchcoders/transfer.sh-web v0.0.0-20210819203540-bbdd40be1311 h1:/Rwuhcp8ZLUauWajAgMyy6AiVbobvD52I+/OnzThK0A=
|
||||||
|
github.com/dutchcoders/transfer.sh-web v0.0.0-20210819203540-bbdd40be1311/go.mod h1:F6Q37CxDh2MHr5KXkcZmNB3tdkK7v+bgE+OpBY+9ilI=
|
||||||
github.com/elazarl/go-bindata-assetfs v1.0.1 h1:m0kkaHRKEu7tUIUFVwhGGGYClXvyl4RE03qmvRTNfbw=
|
github.com/elazarl/go-bindata-assetfs v1.0.1 h1:m0kkaHRKEu7tUIUFVwhGGGYClXvyl4RE03qmvRTNfbw=
|
||||||
github.com/elazarl/go-bindata-assetfs v1.0.1/go.mod h1:v+YaWX3bdea5J/mo8dSETolEo7R71Vk1u8bnjau5yw4=
|
github.com/elazarl/go-bindata-assetfs v1.0.1/go.mod h1:v+YaWX3bdea5J/mo8dSETolEo7R71Vk1u8bnjau5yw4=
|
||||||
github.com/envoyproxy/go-control-plane v0.9.0/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4=
|
github.com/envoyproxy/go-control-plane v0.9.0/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4=
|
||||||
@@ -117,9 +113,7 @@ github.com/gogo/protobuf v1.3.2 h1:Ov1cvc58UF3b5XjBnZv7+opcTcQFZebYjWzi34vdm4Q=
|
|||||||
github.com/gogo/protobuf v1.3.2/go.mod h1:P1XiOD3dCwIKUDQYPy72D8LYyHL2YPYrpS2s69NZV8Q=
|
github.com/gogo/protobuf v1.3.2/go.mod h1:P1XiOD3dCwIKUDQYPy72D8LYyHL2YPYrpS2s69NZV8Q=
|
||||||
github.com/golang/gddo v0.0.0-20210115222349-20d68f94ee1f h1:16RtHeWGkJMc80Etb8RPCcKevXGldr57+LOyZt8zOlg=
|
github.com/golang/gddo v0.0.0-20210115222349-20d68f94ee1f h1:16RtHeWGkJMc80Etb8RPCcKevXGldr57+LOyZt8zOlg=
|
||||||
github.com/golang/gddo v0.0.0-20210115222349-20d68f94ee1f/go.mod h1:ijRvpgDJDI262hYq/IQVYgf8hd8IHUs93Ol0kvMBAx4=
|
github.com/golang/gddo v0.0.0-20210115222349-20d68f94ee1f/go.mod h1:ijRvpgDJDI262hYq/IQVYgf8hd8IHUs93Ol0kvMBAx4=
|
||||||
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/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-20190702054246-869f871628b6/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-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 h1:1r7pUrabqp18hOBcwBwiTsbnFeTZHV9eER/QT5JVZxY=
|
||||||
@@ -133,13 +127,10 @@ github.com/golang/mock v1.4.1/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt
|
|||||||
github.com/golang/mock v1.4.3/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt3cw=
|
github.com/golang/mock v1.4.3/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt3cw=
|
||||||
github.com/golang/mock v1.4.4/go.mod h1:l3mdAwkq5BuhzHwde/uurv3sEJeZMXNpwsxVWU71h+4=
|
github.com/golang/mock v1.4.4/go.mod h1:l3mdAwkq5BuhzHwde/uurv3sEJeZMXNpwsxVWU71h+4=
|
||||||
github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
|
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.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.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.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.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/protobuf v1.3.5/go.mod h1:6O5/vntMXwX2lRkT1hjjk0nAC1IDOTvTlVgjlRvqsdk=
|
||||||
github.com/golang/protobuf v1.4.0-rc.1/go.mod h1:ceaxUfeHdC40wWswd/P6IGgMaK3YpKi5j83Wpe3EHw8=
|
github.com/golang/protobuf v1.4.0-rc.1/go.mod h1:ceaxUfeHdC40wWswd/P6IGgMaK3YpKi5j83Wpe3EHw8=
|
||||||
github.com/golang/protobuf v1.4.0-rc.1.0.20200221234624-67d41d38c208/go.mod h1:xKAWHe0F5eneWXFV3EuXVDTCmh+JuBKY0li0aMyXATA=
|
github.com/golang/protobuf v1.4.0-rc.1.0.20200221234624-67d41d38c208/go.mod h1:xKAWHe0F5eneWXFV3EuXVDTCmh+JuBKY0li0aMyXATA=
|
||||||
@@ -155,10 +146,8 @@ github.com/google/btree v0.0.0-20180813153112-4030bb1f1f0c/go.mod h1:lNA+9X1NB3Z
|
|||||||
github.com/google/btree v1.0.0/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.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.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.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.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/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
||||||
github.com/google/go-cmp v0.4.1/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
github.com/google/go-cmp v0.4.1/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
||||||
github.com/google/go-cmp v0.5.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
github.com/google/go-cmp v0.5.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
||||||
@@ -197,7 +186,6 @@ github.com/gorilla/securecookie v1.1.1 h1:miw7JPhV+b/lAHSXz4qd/nN9jRiAFV5FwjeKyC
|
|||||||
github.com/gorilla/securecookie v1.1.1/go.mod h1:ra0sb63/xPlUeL+yeDciTfxMRAA+MP+HVt/4epWDjd4=
|
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/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.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.1/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8=
|
||||||
github.com/hashicorp/hcl v0.0.0-20170914154624-68e816d1c783/go.mod h1:oZtUIOe8dh44I2q6ScRibXws4Ajl+d+nod3AaR9vL5w=
|
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/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU=
|
||||||
@@ -216,7 +204,6 @@ github.com/kisielk/errcheck v1.5.0/go.mod h1:pFxgyoBC7bSaBwPgfKdkLd5X25qrDl4LWUI
|
|||||||
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/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/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.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo=
|
||||||
github.com/kr/pretty v0.2.0 h1:s5hAObm+yFO5uHYt5dYjxi2rXrsnmRpJx4OYvIWUaQs=
|
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/pretty v0.2.0/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI=
|
||||||
@@ -230,8 +217,8 @@ github.com/mattn/go-colorable v0.1.8/go.mod h1:u6P/XSegPjTcexA+o6vUJrdnUu04hMope
|
|||||||
github.com/mattn/go-isatty v0.0.2/go.mod h1:M+lRXTBqGeGNdLjl/ufCoiOlB5xdOkqRJdNxMWT7Zi4=
|
github.com/mattn/go-isatty v0.0.2/go.mod h1:M+lRXTBqGeGNdLjl/ufCoiOlB5xdOkqRJdNxMWT7Zi4=
|
||||||
github.com/mattn/go-isatty v0.0.12 h1:wuysRhFDzyxgEmMf5xjvJ2M9dZoWAXNNr5LSBS7uHXY=
|
github.com/mattn/go-isatty v0.0.12 h1:wuysRhFDzyxgEmMf5xjvJ2M9dZoWAXNNr5LSBS7uHXY=
|
||||||
github.com/mattn/go-isatty v0.0.12/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Kysco4FUpU=
|
github.com/mattn/go-isatty v0.0.12/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Kysco4FUpU=
|
||||||
github.com/microcosm-cc/bluemonday v1.0.4 h1:p0L+CTpo/PLFdkoPcJemLXG+fpMD7pYOoDEq1axMbGg=
|
github.com/microcosm-cc/bluemonday v1.0.5 h1:cF59UCKMmmUgqN1baLvqU/B1ZsMori+duLVTLpgiG3w=
|
||||||
github.com/microcosm-cc/bluemonday v1.0.4/go.mod h1:8iwZnFn2CDDNZ0r6UXhF4xawGvzaqzCRa1n3/lO3W2w=
|
github.com/microcosm-cc/bluemonday v1.0.5/go.mod h1:8iwZnFn2CDDNZ0r6UXhF4xawGvzaqzCRa1n3/lO3W2w=
|
||||||
github.com/mitchellh/mapstructure v0.0.0-20170523030023-d0303fe80992/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y=
|
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 h1:VhgPp6v9qf9Agr/56bj7Y/xa04UccTW04VP0Qed4vnQ=
|
||||||
github.com/nu7hatch/gouuid v0.0.0-20131221200532-179d4d0c4d8d/go.mod h1:YUTz3bUH2ZwIWBy3CJBeOBEugqcmXREj14T+iG/4k4U=
|
github.com/nu7hatch/gouuid v0.0.0-20131221200532-179d4d0c4d8d/go.mod h1:YUTz3bUH2ZwIWBy3CJBeOBEugqcmXREj14T+iG/4k4U=
|
||||||
@@ -239,27 +226,22 @@ github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+W
|
|||||||
github.com/onsi/ginkgo v1.7.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/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/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/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 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
|
||||||
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
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/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/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/russross/blackfriday/v2 v2.0.1/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=
|
||||||
github.com/russross/blackfriday/v2 v2.1.0 h1:JIOH55/0cWyOuilr9/qlrm0BSXldqnqwMsf35Ld67mk=
|
github.com/russross/blackfriday/v2 v2.1.0 h1:JIOH55/0cWyOuilr9/qlrm0BSXldqnqwMsf35Ld67mk=
|
||||||
github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=
|
github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=
|
||||||
github.com/shurcooL/sanitized_anchor_name v1.0.0 h1:PdmoCO6wvbs+7yrJyMORt4/BmY5IYyJwS/kOiWx8mHo=
|
github.com/shuLhan/go-bindata v4.0.0+incompatible/go.mod h1:pkcPAATLBDD2+SpAPnX5vEM90F7fcwHCvvLCMXcmw3g=
|
||||||
github.com/shurcooL/sanitized_anchor_name v1.0.0/go.mod h1:1NzhyTcUVG4SuEtjjoZeVRXNmyL/1OwPU0+IJeTBvfc=
|
github.com/shurcooL/sanitized_anchor_name v1.0.0/go.mod h1:1NzhyTcUVG4SuEtjjoZeVRXNmyL/1OwPU0+IJeTBvfc=
|
||||||
github.com/skip2/go-qrcode v0.0.0-20200617195104-da1b6568686e h1:MRM5ITcdelLK2j1vwZ3Je0FKVCfqOLp5zO6trqMLYs0=
|
github.com/skip2/go-qrcode v0.0.0-20200617195104-da1b6568686e h1:MRM5ITcdelLK2j1vwZ3Je0FKVCfqOLp5zO6trqMLYs0=
|
||||||
github.com/skip2/go-qrcode v0.0.0-20200617195104-da1b6568686e/go.mod h1:XV66xRDqSt+GTGFMVlhk3ULuV0y9ZmzeVGR4mloJI3M=
|
github.com/skip2/go-qrcode v0.0.0-20200617195104-da1b6568686e/go.mod h1:XV66xRDqSt+GTGFMVlhk3ULuV0y9ZmzeVGR4mloJI3M=
|
||||||
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.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/monkit/v3 v3.0.7/go.mod h1:kj1ViJhlyADa7DiA4xVnTuPA46lFKbM7mxQTrXCuJP4=
|
||||||
github.com/spacemonkeygo/monkit/v3 v3.0.10 h1:jJPiF4EdetQAHAyHyX0wYIv9c/Vl0oN3KJKtnakfR+A=
|
github.com/spacemonkeygo/monkit/v3 v3.0.10 h1:jJPiF4EdetQAHAyHyX0wYIv9c/Vl0oN3KJKtnakfR+A=
|
||||||
github.com/spacemonkeygo/monkit/v3 v3.0.10/go.mod h1:kj1ViJhlyADa7DiA4xVnTuPA46lFKbM7mxQTrXCuJP4=
|
github.com/spacemonkeygo/monkit/v3 v3.0.10/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/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/afero v0.0.0-20170901052352-ee1bd8ee15a1/go.mod h1:j4pytiNVoe2o6bmDsKpLACNPDBIoEAkihy7loJ1B0CQ=
|
||||||
github.com/spf13/cast v1.1.0/go.mod h1:r2rcYCSwa1IExKTDiTfzaxqT2FNHs8hODu4LnUfgKEg=
|
github.com/spf13/cast v1.1.0/go.mod h1:r2rcYCSwa1IExKTDiTfzaxqT2FNHs8hODu4LnUfgKEg=
|
||||||
@@ -267,9 +249,7 @@ github.com/spf13/jwalterweatherman v0.0.0-20170901151539-12bd96e66386/go.mod h1:
|
|||||||
github.com/spf13/pflag v1.0.1-0.20170901120850-7aff26db30c1/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4=
|
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/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/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
||||||
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.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/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA=
|
||||||
github.com/stretchr/testify v1.6.1 h1:hDPOHmpOpP40lSULcqw7IrRb/u7w6RpDC9399XyoNd0=
|
github.com/stretchr/testify v1.6.1 h1:hDPOHmpOpP40lSULcqw7IrRb/u7w6RpDC9399XyoNd0=
|
||||||
github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
||||||
@@ -288,15 +268,11 @@ github.com/zeebo/assert v1.3.0 h1:g7C04CbJuIDKNPFHmsk4hwZDO5O+kntRxzaUoNXj+IQ=
|
|||||||
github.com/zeebo/assert v1.3.0/go.mod h1:Pq9JiuJQpG8JLJdtkwrJESF0Foym2/D9XMU5ciN/wJ0=
|
github.com/zeebo/assert v1.3.0/go.mod h1:Pq9JiuJQpG8JLJdtkwrJESF0Foym2/D9XMU5ciN/wJ0=
|
||||||
github.com/zeebo/errs v1.2.2 h1:5NFypMTuSdoySVTqlNs1dEoU21QVamMQJxW/Fii5O7g=
|
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/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/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=
|
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.21.0/go.mod h1:mSImk1erAIZhrmZN+AvHh14ztQfjbGwt4TtuofqLduU=
|
||||||
go.opencensus.io v0.22.0/go.mod h1:+kGneAE2xo2IficOXnaByMWTGM9T73dGwxeWcUqIpI8=
|
go.opencensus.io v0.22.0/go.mod h1:+kGneAE2xo2IficOXnaByMWTGM9T73dGwxeWcUqIpI8=
|
||||||
go.opencensus.io v0.22.2/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw=
|
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.opencensus.io v0.22.3/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw=
|
||||||
go.opencensus.io v0.22.4/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw=
|
go.opencensus.io v0.22.4/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw=
|
||||||
go.opencensus.io v0.22.5/go.mod h1:5pWMHQbX5EPX2/62yrJeAkowc+lfs/XD7Uxpq3pI6kk=
|
go.opencensus.io v0.22.5/go.mod h1:5pWMHQbX5EPX2/62yrJeAkowc+lfs/XD7Uxpq3pI6kk=
|
||||||
@@ -308,7 +284,6 @@ golang.org/x/crypto v0.0.0-20190510104115-cbcb75029529/go.mod h1:yigFU9vqHzYiE8U
|
|||||||
golang.org/x/crypto v0.0.0-20190605123033-f99c8df09eb5/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
|
golang.org/x/crypto v0.0.0-20190605123033-f99c8df09eb5/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
|
||||||
golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/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-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/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
|
||||||
golang.org/x/crypto v0.0.0-20210415154028-4f45737414dc h1:+q90ECDSAQirdykUN6sPEiBXBsp8Csjcca8Oy7bgLTA=
|
golang.org/x/crypto v0.0.0-20210415154028-4f45737414dc h1:+q90ECDSAQirdykUN6sPEiBXBsp8Csjcca8Oy7bgLTA=
|
||||||
golang.org/x/crypto v0.0.0-20210415154028-4f45737414dc/go.mod h1:T9bdIzuCu7OtxOm1hfPfRQxPLYneinmdGuTeoZ9dtd4=
|
golang.org/x/crypto v0.0.0-20210415154028-4f45737414dc/go.mod h1:T9bdIzuCu7OtxOm1hfPfRQxPLYneinmdGuTeoZ9dtd4=
|
||||||
@@ -365,7 +340,6 @@ golang.org/x/net v0.0.0-20200114155413-6afb5195e5aa/go.mod h1:z5CRVTTTmAJ677TzLL
|
|||||||
golang.org/x/net v0.0.0-20200202094626-16171245cfb2/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-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-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/net v0.0.0-20200301022130-244492dfa37a/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||||
golang.org/x/net v0.0.0-20200324143707-d3edc9973b7e/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A=
|
golang.org/x/net v0.0.0-20200324143707-d3edc9973b7e/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A=
|
||||||
golang.org/x/net v0.0.0-20200501053045-e0ff5e5a1de5/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A=
|
golang.org/x/net v0.0.0-20200501053045-e0ff5e5a1de5/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A=
|
||||||
@@ -379,17 +353,14 @@ golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwY
|
|||||||
golang.org/x/net v0.0.0-20201031054903-ff519b6c9102/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=
|
golang.org/x/net v0.0.0-20201031054903-ff519b6c9102/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=
|
||||||
golang.org/x/net v0.0.0-20201110031124-69a78807bb2b/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=
|
golang.org/x/net v0.0.0-20201110031124-69a78807bb2b/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=
|
||||||
golang.org/x/net v0.0.0-20201209123823-ac852fbbde11/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
|
golang.org/x/net v0.0.0-20201209123823-ac852fbbde11/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
|
||||||
golang.org/x/net v0.0.0-20210119194325-5f4716e94777 h1:003p0dJM77cxMSyCPFphvZf/Y5/NXf5fzg6ufd1/Oew=
|
|
||||||
golang.org/x/net v0.0.0-20210119194325-5f4716e94777/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
|
golang.org/x/net v0.0.0-20210119194325-5f4716e94777/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
|
||||||
golang.org/x/net v0.0.0-20210226172049-e18ecbb05110 h1:qWPm9rbaAMKs8Bq/9LRpbMqxWRVUAQwMI9fVrssnTfw=
|
golang.org/x/net v0.0.0-20210226172049-e18ecbb05110 h1:qWPm9rbaAMKs8Bq/9LRpbMqxWRVUAQwMI9fVrssnTfw=
|
||||||
golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
|
golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
|
||||||
golang.org/x/oauth2 v0.0.0-20170912212905-13449ad91cb2/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
|
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-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-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-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-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/oauth2 v0.0.0-20200107190931-bf48bf16ab8d/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
|
||||||
golang.org/x/oauth2 v0.0.0-20200902213428-5d25da1a8d43/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A=
|
golang.org/x/oauth2 v0.0.0-20200902213428-5d25da1a8d43/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A=
|
||||||
golang.org/x/oauth2 v0.0.0-20201109201403-9fd604954f58/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A=
|
golang.org/x/oauth2 v0.0.0-20201109201403-9fd604954f58/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A=
|
||||||
@@ -402,16 +373,12 @@ golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJ
|
|||||||
golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/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-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-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-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-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/sync v0.0.0-20200317015054-43a5402ce75a/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||||
golang.org/x/sync v0.0.0-20200625203802-6e8e738ad208/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
golang.org/x/sync v0.0.0-20200625203802-6e8e738ad208/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||||
golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||||
golang.org/x/sync v0.0.0-20201207232520-09787c993a3a h1:DcqTD9SDLc+1P/r1EmRBwnVsrOwW+kk2vWf9n+1sGhs=
|
|
||||||
golang.org/x/sync v0.0.0-20201207232520-09787c993a3a/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
golang.org/x/sync v0.0.0-20201207232520-09787c993a3a/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||||
golang.org/x/sync v0.0.0-20210220032951-036812b2e83c h1:5KslGYwFpkhGh+Q16bwMP3cOontH8FOep7tGV86Y7SQ=
|
golang.org/x/sync v0.0.0-20210220032951-036812b2e83c h1:5KslGYwFpkhGh+Q16bwMP3cOontH8FOep7tGV86Y7SQ=
|
||||||
golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||||
@@ -435,7 +402,6 @@ golang.org/x/sys v0.0.0-20200122134326-e047566fdf82/go.mod h1:h1NjWce9XRLGQEsW7w
|
|||||||
golang.org/x/sys v0.0.0-20200202164722-d101bd2416d5/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-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-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-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-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
golang.org/x/sys v0.0.0-20200331124033-c3d80250170d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
golang.org/x/sys v0.0.0-20200331124033-c3d80250170d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
@@ -457,7 +423,6 @@ golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9sn
|
|||||||
golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
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.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.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/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk=
|
||||||
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
||||||
golang.org/x/text v0.3.4/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
golang.org/x/text v0.3.4/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
||||||
@@ -465,7 +430,6 @@ golang.org/x/text v0.3.5 h1:i6eZZ+zk0SOf0xgBpEpPD18qWcJda6q1sxt3S0kzyUQ=
|
|||||||
golang.org/x/text v0.3.5/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
golang.org/x/text v0.3.5/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
||||||
golang.org/x/time v0.0.0-20170424234030-8be79e1e0910/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
|
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-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-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/time v0.0.0-20191024005414-555d28b269f0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
|
||||||
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
||||||
@@ -518,7 +482,6 @@ golang.org/x/tools v0.0.0-20210106214847-113979e3529a/go.mod h1:emZCQorbCU4vsT4f
|
|||||||
golang.org/x/tools v0.1.0/go.mod h1:xkSsbof2nBLbhDlRMhhhyNLN/zl3eTqcnHD5viDpcZ0=
|
golang.org/x/tools v0.1.0/go.mod h1:xkSsbof2nBLbhDlRMhhhyNLN/zl3eTqcnHD5viDpcZ0=
|
||||||
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
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-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=
|
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||||
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1 h1:go1bK/D/BFZV2I8cIQd1NKEZ+0owSTG1fDTci4IqFcE=
|
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1 h1:go1bK/D/BFZV2I8cIQd1NKEZ+0owSTG1fDTci4IqFcE=
|
||||||
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||||
@@ -526,7 +489,6 @@ google.golang.org/api v0.0.0-20170921000349-586095a6e407/go.mod h1:4mhQ8q/RsB7i+
|
|||||||
google.golang.org/api v0.4.0/go.mod h1:8k5glujaEP+g9n7WNsDg8QP6cUVNI86fCNMcbazEtwE=
|
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.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.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.9.0/go.mod h1:o4eAsZoiT+ibD93RtjEohWalFOjRDx6CVaqeizhEnKg=
|
||||||
google.golang.org/api v0.13.0/go.mod h1:iLdEw5Ide6rF15KTC1Kkl0iskquN2gFfn9o9XIsbkAI=
|
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.14.0/go.mod h1:iLdEw5Ide6rF15KTC1Kkl0iskquN2gFfn9o9XIsbkAI=
|
||||||
@@ -534,7 +496,6 @@ google.golang.org/api v0.15.0/go.mod h1:iLdEw5Ide6rF15KTC1Kkl0iskquN2gFfn9o9XIsb
|
|||||||
google.golang.org/api v0.17.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE=
|
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.18.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE=
|
||||||
google.golang.org/api v0.19.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE=
|
google.golang.org/api v0.19.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/api v0.20.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE=
|
||||||
google.golang.org/api v0.22.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE=
|
google.golang.org/api v0.22.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE=
|
||||||
google.golang.org/api v0.24.0/go.mod h1:lIXQywCXRcnZPGlsd8NbLnOjtAoL6em04bJ9+z0MncE=
|
google.golang.org/api v0.24.0/go.mod h1:lIXQywCXRcnZPGlsd8NbLnOjtAoL6em04bJ9+z0MncE=
|
||||||
@@ -547,10 +508,8 @@ google.golang.org/api v0.40.0 h1:uWrpz12dpVPn7cojP82mk02XDgTJLDPc2KbVTxrWb4A=
|
|||||||
google.golang.org/api v0.40.0/go.mod h1:fYKFpnQN0DsDSKRVRcQSDQNtqWPfM9i+zNPxepjRCQ8=
|
google.golang.org/api v0.40.0/go.mod h1:fYKFpnQN0DsDSKRVRcQSDQNtqWPfM9i+zNPxepjRCQ8=
|
||||||
google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM=
|
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.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.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.1/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/appengine v1.6.5/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc=
|
||||||
google.golang.org/appengine v1.6.6/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc=
|
google.golang.org/appengine v1.6.6/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc=
|
||||||
google.golang.org/appengine v1.6.7 h1:FZR1q0exgwxzPzp/aF+VccGrSfxfPpkBqjIIEq3ru6c=
|
google.golang.org/appengine v1.6.7 h1:FZR1q0exgwxzPzp/aF+VccGrSfxfPpkBqjIIEq3ru6c=
|
||||||
@@ -560,7 +519,6 @@ google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoA
|
|||||||
google.golang.org/genproto v0.0.0-20190307195333-5fe7a883aa19/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE=
|
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=
|
google.golang.org/genproto v0.0.0-20190418145605-e7d98fc518a7/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE=
|
||||||
google.golang.org/genproto v0.0.0-20190425155659-357c62f0e4bb/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE=
|
google.golang.org/genproto v0.0.0-20190425155659-357c62f0e4bb/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE=
|
||||||
google.golang.org/genproto v0.0.0-20190502173448-54afdca5d873 h1:nfPFGzJkUDX6uBmpN/pSw7MbOAWegH5QDQuoXFHedLg=
|
|
||||||
google.golang.org/genproto v0.0.0-20190502173448-54afdca5d873/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE=
|
google.golang.org/genproto v0.0.0-20190502173448-54afdca5d873/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE=
|
||||||
google.golang.org/genproto v0.0.0-20190801165951-fa694d86fc64/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc=
|
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-20190819201941-24fa4b261c55/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc=
|
||||||
@@ -575,7 +533,6 @@ google.golang.org/genproto v0.0.0-20200204135345-fa8e72b47b90/go.mod h1:GmwEX6Z4
|
|||||||
google.golang.org/genproto v0.0.0-20200212174721-66ed5ce911ce/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c=
|
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-20200224152610-e50cd9704f63/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c=
|
||||||
google.golang.org/genproto v0.0.0-20200228133532-8c2c7df3a383/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c=
|
google.golang.org/genproto v0.0.0-20200228133532-8c2c7df3a383/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/genproto v0.0.0-20200305110556-506484158171/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c=
|
||||||
google.golang.org/genproto v0.0.0-20200312145019-da6875a35672/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c=
|
google.golang.org/genproto v0.0.0-20200312145019-da6875a35672/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c=
|
||||||
google.golang.org/genproto v0.0.0-20200331122359-1ee6d9798940/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c=
|
google.golang.org/genproto v0.0.0-20200331122359-1ee6d9798940/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c=
|
||||||
@@ -597,17 +554,14 @@ google.golang.org/genproto v0.0.0-20210218151259-fe80b386bf06 h1:Px6YyLaNKEo5eon
|
|||||||
google.golang.org/genproto v0.0.0-20210218151259-fe80b386bf06/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
|
google.golang.org/genproto v0.0.0-20210218151259-fe80b386bf06/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
|
||||||
google.golang.org/grpc v1.2.1-0.20170921194603-d4b75ebd4f9f/go.mod h1:yo6s7OP7yaDglbqo1J04qKzAhqBH6lvTonzMVmEdcZw=
|
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.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.20.1/go.mod h1:10oTOabMzJvdu6/UiuZezV6QK5dSlG84ov/aaiqXj38=
|
||||||
google.golang.org/grpc v1.21.1/go.mod h1:oYelfM1adQP15Ek0mdvEgi9Df8B9CZIaU1084ijfRaM=
|
google.golang.org/grpc v1.21.1/go.mod h1:oYelfM1adQP15Ek0mdvEgi9Df8B9CZIaU1084ijfRaM=
|
||||||
google.golang.org/grpc v1.23.0/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg=
|
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.25.1/go.mod h1:c3i+UQWmh7LiEpx4sFZnkU36qjEYZ0imhYfXVyQciAY=
|
||||||
google.golang.org/grpc v1.26.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk=
|
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.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.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=
|
google.golang.org/grpc v1.28.0/go.mod h1:rpkK4SK4GF4Ach/+MFLZUBavHOvF2JJB5uozKKal+60=
|
||||||
google.golang.org/grpc v1.29.1/go.mod h1:itym6AZVZYACWQqET3MqgPpjcuV5QH3BxFS3IjizoKk=
|
google.golang.org/grpc v1.29.1/go.mod h1:itym6AZVZYACWQqET3MqgPpjcuV5QH3BxFS3IjizoKk=
|
||||||
google.golang.org/grpc v1.30.0/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM8pak=
|
google.golang.org/grpc v1.30.0/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM8pak=
|
||||||
@@ -629,7 +583,6 @@ google.golang.org/protobuf v1.24.0/go.mod h1:r/3tXBNzIEhYS9I1OUVjXDlt8tc493IdKGj
|
|||||||
google.golang.org/protobuf v1.25.0 h1:Ejskq+SyPohKW+1uil0JJMtmHCgJPJ/qWTxr8qp+R4c=
|
google.golang.org/protobuf v1.25.0 h1:Ejskq+SyPohKW+1uil0JJMtmHCgJPJ/qWTxr8qp+R4c=
|
||||||
google.golang.org/protobuf v1.25.0/go.mod h1:9JNX74DMeImyA3h4bdi1ymwjUzf21/xIlbajtzgsN7c=
|
google.golang.org/protobuf v1.25.0/go.mod h1:9JNX74DMeImyA3h4bdi1ymwjUzf21/xIlbajtzgsN7c=
|
||||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
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-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 h1:YR8cESwS4TdDjEe65xsg0ogRM/Nc3DYOhEAlW+xobZo=
|
||||||
gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||||
@@ -637,7 +590,6 @@ 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/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/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.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=
|
gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||||
gopkg.in/yaml.v2 v2.2.8 h1:obN1ZagJSUGI0Ek/LBmuj4SNLPfIny3KsKFopxRdj10=
|
gopkg.in/yaml.v2 v2.2.8 h1:obN1ZagJSUGI0Ek/LBmuj4SNLPfIny3KsKFopxRdj10=
|
||||||
gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||||
|
@@ -2,6 +2,8 @@
|
|||||||
The MIT License (MIT)
|
The MIT License (MIT)
|
||||||
|
|
||||||
Copyright (c) 2014-2017 DutchCoders [https://github.com/dutchcoders/]
|
Copyright (c) 2014-2017 DutchCoders [https://github.com/dutchcoders/]
|
||||||
|
Copyright (c) 2018-2020 Andrea Spacca.
|
||||||
|
Copyright (c) 2020- Andrea Spacca and Stefan Benten.
|
||||||
|
|
||||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
of this software and associated documentation files (the "Software"), to deal
|
of this software and associated documentation files (the "Software"), to deal
|
||||||
@@ -30,7 +32,6 @@ import (
|
|||||||
|
|
||||||
"fmt"
|
"fmt"
|
||||||
"io"
|
"io"
|
||||||
"log"
|
|
||||||
"net/http"
|
"net/http"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
@@ -58,7 +59,7 @@ func (s *Server) scanHandler(w http.ResponseWriter, r *http.Request) {
|
|||||||
abort := make(chan bool)
|
abort := make(chan bool)
|
||||||
response, err := c.ScanStream(reader, abort)
|
response, err := c.ScanStream(reader, abort)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Printf("%s", err.Error())
|
s.logger.Printf("%s", err.Error())
|
||||||
http.Error(w, err.Error(), 500)
|
http.Error(w, err.Error(), 500)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
@@ -1,78 +0,0 @@
|
|||||||
/*
|
|
||||||
https://github.com/fs111/kurz.go/blob/master/src/codec.go
|
|
||||||
|
|
||||||
Originally written and Copyright (c) 2011 André Kelpe
|
|
||||||
Modifications Copyright (c) 2015 John Ko
|
|
||||||
|
|
||||||
Permission is hereby granted, free of charge, to any person obtaining a copy of
|
|
||||||
this software and associated documentation files (the "Software"), to deal in
|
|
||||||
the Software without restriction, including without limitation the rights to
|
|
||||||
use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
|
|
||||||
the Software, and to permit persons to whom the Software is furnished to do so,
|
|
||||||
subject to the following conditions:
|
|
||||||
|
|
||||||
The above copyright notice and this permission notice shall be included in all
|
|
||||||
copies or substantial portions of the Software.
|
|
||||||
|
|
||||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
||||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
|
|
||||||
FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
|
|
||||||
COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
|
|
||||||
IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
|
||||||
CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
|
||||||
*/
|
|
||||||
|
|
||||||
package server
|
|
||||||
|
|
||||||
import (
|
|
||||||
"math"
|
|
||||||
"math/rand"
|
|
||||||
"strings"
|
|
||||||
)
|
|
||||||
|
|
||||||
const (
|
|
||||||
// characters used for short-urls
|
|
||||||
SYMBOLS = "0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ"
|
|
||||||
|
|
||||||
// someone set us up the bomb !!
|
|
||||||
BASE = float64(len(SYMBOLS))
|
|
||||||
|
|
||||||
// init seed encode number
|
|
||||||
INIT_SEED = float64(-1)
|
|
||||||
)
|
|
||||||
|
|
||||||
// encodes a number into our *base* representation
|
|
||||||
// TODO can this be made better with some bitshifting?
|
|
||||||
func Encode(number float64, length int64) string {
|
|
||||||
if number == INIT_SEED {
|
|
||||||
seed := math.Pow(float64(BASE), float64(length))
|
|
||||||
number = seed + (rand.Float64() * seed) // start with seed to enforce desired length
|
|
||||||
}
|
|
||||||
|
|
||||||
rest := int64(math.Mod(number, BASE))
|
|
||||||
// strings are a bit weird in go...
|
|
||||||
result := string(SYMBOLS[rest])
|
|
||||||
if rest > 0 && number-float64(rest) != 0 {
|
|
||||||
newnumber := (number - float64(rest)) / BASE
|
|
||||||
result = Encode(newnumber, length) + result
|
|
||||||
} else {
|
|
||||||
// it would always be 1 because of starting with seed and we want to skip
|
|
||||||
return ""
|
|
||||||
}
|
|
||||||
|
|
||||||
return result
|
|
||||||
}
|
|
||||||
|
|
||||||
// Decodes a string given in our encoding and returns the decimal
|
|
||||||
// integer.
|
|
||||||
func Decode(input string) int64 {
|
|
||||||
const floatbase = float64(BASE)
|
|
||||||
l := len(input)
|
|
||||||
var sum int = 0
|
|
||||||
for index := l - 1; index > -1; index -= 1 {
|
|
||||||
current := string(input[index])
|
|
||||||
pos := strings.Index(SYMBOLS, current)
|
|
||||||
sum = sum + (pos * int(math.Pow(floatbase, float64((l-index-1)))))
|
|
||||||
}
|
|
||||||
return int64(sum)
|
|
||||||
}
|
|
@@ -1,15 +0,0 @@
|
|||||||
package server
|
|
||||||
|
|
||||||
import "testing"
|
|
||||||
|
|
||||||
func BenchmarkEncodeConcat(b *testing.B) {
|
|
||||||
for i := 0; i < b.N; i++ {
|
|
||||||
_ = Encode(INIT_SEED, 5) + Encode(INIT_SEED, 5)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func BenchmarkEncodeLonger(b *testing.B) {
|
|
||||||
for i := 0; i < b.N; i++ {
|
|
||||||
_ = Encode(INIT_SEED, 10)
|
|
||||||
}
|
|
||||||
}
|
|
@@ -2,6 +2,8 @@
|
|||||||
The MIT License (MIT)
|
The MIT License (MIT)
|
||||||
|
|
||||||
Copyright (c) 2014-2017 DutchCoders [https://github.com/dutchcoders/]
|
Copyright (c) 2014-2017 DutchCoders [https://github.com/dutchcoders/]
|
||||||
|
Copyright (c) 2018-2020 Andrea Spacca.
|
||||||
|
Copyright (c) 2020- Andrea Spacca and Stefan Benten.
|
||||||
|
|
||||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
of this software and associated documentation files (the "Software"), to deal
|
of this software and associated documentation files (the "Software"), to deal
|
||||||
@@ -40,7 +42,6 @@ import (
|
|||||||
html_template "html/template"
|
html_template "html/template"
|
||||||
"io"
|
"io"
|
||||||
"io/ioutil"
|
"io/ioutil"
|
||||||
"log"
|
|
||||||
"mime"
|
"mime"
|
||||||
"net/http"
|
"net/http"
|
||||||
"net/url"
|
"net/url"
|
||||||
@@ -122,10 +123,10 @@ func (s *Server) previewHandler(w http.ResponseWriter, r *http.Request) {
|
|||||||
token := vars["token"]
|
token := vars["token"]
|
||||||
filename := vars["filename"]
|
filename := vars["filename"]
|
||||||
|
|
||||||
metadata, err := s.CheckMetadata(token, filename, false)
|
metadata, err := s.checkMetadata(token, filename, false)
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Printf("Error metadata: %s", err.Error())
|
s.logger.Printf("Error metadata: %s", err.Error())
|
||||||
http.Error(w, http.StatusText(http.StatusNotFound), http.StatusNotFound)
|
http.Error(w, http.StatusText(http.StatusNotFound), http.StatusNotFound)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
@@ -197,9 +198,9 @@ func (s *Server) previewHandler(w http.ResponseWriter, r *http.Request) {
|
|||||||
ContentType string
|
ContentType string
|
||||||
Content html_template.HTML
|
Content html_template.HTML
|
||||||
Filename string
|
Filename string
|
||||||
Url string
|
URL string
|
||||||
UrlGet string
|
URLGet string
|
||||||
UrlRandomToken string
|
URLRandomToken string
|
||||||
Hostname string
|
Hostname string
|
||||||
WebAddress string
|
WebAddress string
|
||||||
ContentLength uint64
|
ContentLength uint64
|
||||||
@@ -237,16 +238,34 @@ func (s *Server) viewHandler(w http.ResponseWriter, r *http.Request) {
|
|||||||
hostname := getURL(r, s.proxyPort).Host
|
hostname := getURL(r, s.proxyPort).Host
|
||||||
webAddress := resolveWebAddress(r, s.proxyPath, s.proxyPort)
|
webAddress := resolveWebAddress(r, s.proxyPath, s.proxyPort)
|
||||||
|
|
||||||
|
maxUploadSize := ""
|
||||||
|
if s.maxUploadSize > 0 {
|
||||||
|
maxUploadSize = formatSize(s.maxUploadSize)
|
||||||
|
}
|
||||||
|
|
||||||
|
purgeTime := ""
|
||||||
|
if s.purgeDays > 0 {
|
||||||
|
purgeTime = s.purgeDays.String()
|
||||||
|
}
|
||||||
|
|
||||||
data := struct {
|
data := struct {
|
||||||
Hostname string
|
Hostname string
|
||||||
WebAddress string
|
WebAddress string
|
||||||
GAKey string
|
GAKey string
|
||||||
UserVoiceKey string
|
UserVoiceKey string
|
||||||
|
PurgeTime string
|
||||||
|
MaxUploadSize string
|
||||||
|
SampleToken string
|
||||||
|
SampleToken2 string
|
||||||
}{
|
}{
|
||||||
hostname,
|
hostname,
|
||||||
webAddress,
|
webAddress,
|
||||||
s.gaKey,
|
s.gaKey,
|
||||||
s.userVoiceKey,
|
s.userVoiceKey,
|
||||||
|
purgeTime,
|
||||||
|
maxUploadSize,
|
||||||
|
token(s.randomTokenLength),
|
||||||
|
token(s.randomTokenLength),
|
||||||
}
|
}
|
||||||
|
|
||||||
if acceptsHTML(r.Header) {
|
if acceptsHTML(r.Header) {
|
||||||
@@ -272,12 +291,12 @@ func sanitize(fileName string) string {
|
|||||||
|
|
||||||
func (s *Server) postHandler(w http.ResponseWriter, r *http.Request) {
|
func (s *Server) postHandler(w http.ResponseWriter, r *http.Request) {
|
||||||
if err := r.ParseMultipartForm(_24K); nil != err {
|
if err := r.ParseMultipartForm(_24K); nil != err {
|
||||||
log.Printf("%s", err.Error())
|
s.logger.Printf("%s", err.Error())
|
||||||
http.Error(w, "Error occurred copying to output stream", 500)
|
http.Error(w, "Error occurred copying to output stream", 500)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
token := Encode(INIT_SEED, s.randomTokenLength)
|
token := token(s.randomTokenLength)
|
||||||
|
|
||||||
w.Header().Set("Content-Type", "text/plain")
|
w.Header().Set("Content-Type", "text/plain")
|
||||||
|
|
||||||
@@ -290,7 +309,7 @@ func (s *Server) postHandler(w http.ResponseWriter, r *http.Request) {
|
|||||||
var err error
|
var err error
|
||||||
|
|
||||||
if f, err = fheader.Open(); err != nil {
|
if f, err = fheader.Open(); err != nil {
|
||||||
log.Printf("%s", err.Error())
|
s.logger.Printf("%s", err.Error())
|
||||||
http.Error(w, err.Error(), 500)
|
http.Error(w, err.Error(), 500)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
@@ -299,7 +318,7 @@ func (s *Server) postHandler(w http.ResponseWriter, r *http.Request) {
|
|||||||
|
|
||||||
n, err := io.CopyN(&b, f, _24K+1)
|
n, err := io.CopyN(&b, f, _24K+1)
|
||||||
if err != nil && err != io.EOF {
|
if err != nil && err != io.EOF {
|
||||||
log.Printf("%s", err.Error())
|
s.logger.Printf("%s", err.Error())
|
||||||
http.Error(w, err.Error(), 500)
|
http.Error(w, err.Error(), 500)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
@@ -310,14 +329,14 @@ func (s *Server) postHandler(w http.ResponseWriter, r *http.Request) {
|
|||||||
if n > _24K {
|
if n > _24K {
|
||||||
file, err = ioutil.TempFile(s.tempPath, "transfer-")
|
file, err = ioutil.TempFile(s.tempPath, "transfer-")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Fatal(err)
|
s.logger.Fatal(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
n, err = io.Copy(file, io.MultiReader(&b, f))
|
n, err = io.Copy(file, io.MultiReader(&b, f))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
cleanTmpFile(file)
|
s.cleanTmpFile(file)
|
||||||
|
|
||||||
log.Printf("%s", err.Error())
|
s.logger.Printf("%s", err.Error())
|
||||||
http.Error(w, err.Error(), 500)
|
http.Error(w, err.Error(), 500)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
@@ -330,32 +349,32 @@ func (s *Server) postHandler(w http.ResponseWriter, r *http.Request) {
|
|||||||
contentLength := n
|
contentLength := n
|
||||||
|
|
||||||
if s.maxUploadSize > 0 && contentLength > s.maxUploadSize {
|
if s.maxUploadSize > 0 && contentLength > s.maxUploadSize {
|
||||||
log.Print("Entity too large")
|
s.logger.Print("Entity too large")
|
||||||
http.Error(w, http.StatusText(http.StatusRequestEntityTooLarge), http.StatusRequestEntityTooLarge)
|
http.Error(w, http.StatusText(http.StatusRequestEntityTooLarge), http.StatusRequestEntityTooLarge)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
metadata := MetadataForRequest(contentType, s.randomTokenLength, r)
|
metadata := metadataForRequest(contentType, s.randomTokenLength, r)
|
||||||
|
|
||||||
buffer := &bytes.Buffer{}
|
buffer := &bytes.Buffer{}
|
||||||
if err := json.NewEncoder(buffer).Encode(metadata); err != nil {
|
if err := json.NewEncoder(buffer).Encode(metadata); err != nil {
|
||||||
log.Printf("%s", err.Error())
|
s.logger.Printf("%s", err.Error())
|
||||||
http.Error(w, errors.New("Could not encode metadata").Error(), 500)
|
http.Error(w, errors.New("Could not encode metadata").Error(), 500)
|
||||||
|
|
||||||
cleanTmpFile(file)
|
s.cleanTmpFile(file)
|
||||||
return
|
return
|
||||||
} else if err := s.storage.Put(token, fmt.Sprintf("%s.metadata", filename), buffer, "text/json", uint64(buffer.Len())); err != nil {
|
} else if err := s.storage.Put(token, fmt.Sprintf("%s.metadata", filename), buffer, "text/json", uint64(buffer.Len())); err != nil {
|
||||||
log.Printf("%s", err.Error())
|
s.logger.Printf("%s", err.Error())
|
||||||
http.Error(w, errors.New("Could not save metadata").Error(), 500)
|
http.Error(w, errors.New("Could not save metadata").Error(), 500)
|
||||||
|
|
||||||
cleanTmpFile(file)
|
s.cleanTmpFile(file)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
log.Printf("Uploading %s %s %d %s", token, filename, contentLength, contentType)
|
s.logger.Printf("Uploading %s %s %d %s", token, filename, contentLength, contentType)
|
||||||
|
|
||||||
if err = s.storage.Put(token, filename, reader, contentType, uint64(contentLength)); err != nil {
|
if err = s.storage.Put(token, filename, reader, contentType, uint64(contentLength)); err != nil {
|
||||||
log.Printf("Backend storage error: %s", err.Error())
|
s.logger.Printf("Backend storage error: %s", err.Error())
|
||||||
http.Error(w, err.Error(), 500)
|
http.Error(w, err.Error(), 500)
|
||||||
return
|
return
|
||||||
|
|
||||||
@@ -365,26 +384,26 @@ func (s *Server) postHandler(w http.ResponseWriter, r *http.Request) {
|
|||||||
relativeURL, _ := url.Parse(path.Join(s.proxyPath, token, filename))
|
relativeURL, _ := url.Parse(path.Join(s.proxyPath, token, filename))
|
||||||
fmt.Fprintln(w, getURL(r, s.proxyPort).ResolveReference(relativeURL).String())
|
fmt.Fprintln(w, getURL(r, s.proxyPort).ResolveReference(relativeURL).String())
|
||||||
|
|
||||||
cleanTmpFile(file)
|
s.cleanTmpFile(file)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func cleanTmpFile(f *os.File) {
|
func (s *Server) cleanTmpFile(f *os.File) {
|
||||||
if f != nil {
|
if f != nil {
|
||||||
err := f.Close()
|
err := f.Close()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Printf("Error closing tmpfile: %s (%s)", err, f.Name())
|
s.logger.Printf("Error closing tmpfile: %s (%s)", err, f.Name())
|
||||||
}
|
}
|
||||||
|
|
||||||
err = os.Remove(f.Name())
|
err = os.Remove(f.Name())
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Printf("Error removing tmpfile: %s (%s)", err, f.Name())
|
s.logger.Printf("Error removing tmpfile: %s (%s)", err, f.Name())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
type Metadata struct {
|
type metadata struct {
|
||||||
// ContentType is the original uploading content type
|
// ContentType is the original uploading content type
|
||||||
ContentType string
|
ContentType string
|
||||||
// Secret as knowledge to delete file
|
// Secret as knowledge to delete file
|
||||||
@@ -399,13 +418,13 @@ type Metadata struct {
|
|||||||
DeletionToken string
|
DeletionToken string
|
||||||
}
|
}
|
||||||
|
|
||||||
func MetadataForRequest(contentType string, randomTokenLength int64, r *http.Request) Metadata {
|
func metadataForRequest(contentType string, randomTokenLength int, r *http.Request) metadata {
|
||||||
metadata := Metadata{
|
metadata := metadata{
|
||||||
ContentType: strings.ToLower(contentType),
|
ContentType: strings.ToLower(contentType),
|
||||||
MaxDate: time.Time{},
|
MaxDate: time.Time{},
|
||||||
Downloads: 0,
|
Downloads: 0,
|
||||||
MaxDownloads: -1,
|
MaxDownloads: -1,
|
||||||
DeletionToken: Encode(INIT_SEED, randomTokenLength) + Encode(INIT_SEED, randomTokenLength),
|
DeletionToken: token(randomTokenLength) + token(randomTokenLength),
|
||||||
}
|
}
|
||||||
|
|
||||||
if v := r.Header.Get("Max-Downloads"); v == "" {
|
if v := r.Header.Get("Max-Downloads"); v == "" {
|
||||||
@@ -447,7 +466,7 @@ func (s *Server) putHandler(w http.ResponseWriter, r *http.Request) {
|
|||||||
|
|
||||||
n, err := io.CopyN(&b, f, _24K+1)
|
n, err := io.CopyN(&b, f, _24K+1)
|
||||||
if err != nil && err != io.EOF {
|
if err != nil && err != io.EOF {
|
||||||
log.Printf("Error putting new file: %s", err.Error())
|
s.logger.Printf("Error putting new file: %s", err.Error())
|
||||||
http.Error(w, err.Error(), 500)
|
http.Error(w, err.Error(), 500)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
@@ -457,16 +476,16 @@ func (s *Server) putHandler(w http.ResponseWriter, r *http.Request) {
|
|||||||
if n > _24K {
|
if n > _24K {
|
||||||
file, err = ioutil.TempFile(s.tempPath, "transfer-")
|
file, err = ioutil.TempFile(s.tempPath, "transfer-")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Printf("%s", err.Error())
|
s.logger.Printf("%s", err.Error())
|
||||||
http.Error(w, err.Error(), 500)
|
http.Error(w, err.Error(), 500)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
defer cleanTmpFile(file)
|
defer s.cleanTmpFile(file)
|
||||||
|
|
||||||
n, err = io.Copy(file, io.MultiReader(&b, f))
|
n, err = io.Copy(file, io.MultiReader(&b, f))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Printf("%s", err.Error())
|
s.logger.Printf("%s", err.Error())
|
||||||
http.Error(w, err.Error(), 500)
|
http.Error(w, err.Error(), 500)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
@@ -480,40 +499,40 @@ func (s *Server) putHandler(w http.ResponseWriter, r *http.Request) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if s.maxUploadSize > 0 && contentLength > s.maxUploadSize {
|
if s.maxUploadSize > 0 && contentLength > s.maxUploadSize {
|
||||||
log.Print("Entity too large")
|
s.logger.Print("Entity too large")
|
||||||
http.Error(w, http.StatusText(http.StatusRequestEntityTooLarge), http.StatusRequestEntityTooLarge)
|
http.Error(w, http.StatusText(http.StatusRequestEntityTooLarge), http.StatusRequestEntityTooLarge)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
if contentLength == 0 {
|
if contentLength == 0 {
|
||||||
log.Print("Empty content-length")
|
s.logger.Print("Empty content-length")
|
||||||
http.Error(w, errors.New("Could not upload empty file").Error(), 400)
|
http.Error(w, errors.New("Could not upload empty file").Error(), 400)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
contentType := mime.TypeByExtension(filepath.Ext(vars["filename"]))
|
contentType := mime.TypeByExtension(filepath.Ext(vars["filename"]))
|
||||||
|
|
||||||
token := Encode(INIT_SEED, s.randomTokenLength)
|
token := token(s.randomTokenLength)
|
||||||
|
|
||||||
metadata := MetadataForRequest(contentType, s.randomTokenLength, r)
|
metadata := metadataForRequest(contentType, s.randomTokenLength, r)
|
||||||
|
|
||||||
buffer := &bytes.Buffer{}
|
buffer := &bytes.Buffer{}
|
||||||
if err := json.NewEncoder(buffer).Encode(metadata); err != nil {
|
if err := json.NewEncoder(buffer).Encode(metadata); err != nil {
|
||||||
log.Printf("%s", err.Error())
|
s.logger.Printf("%s", err.Error())
|
||||||
http.Error(w, errors.New("Could not encode metadata").Error(), 500)
|
http.Error(w, errors.New("Could not encode metadata").Error(), 500)
|
||||||
return
|
return
|
||||||
} else if err := s.storage.Put(token, fmt.Sprintf("%s.metadata", filename), buffer, "text/json", uint64(buffer.Len())); err != nil {
|
} else if err := s.storage.Put(token, fmt.Sprintf("%s.metadata", filename), buffer, "text/json", uint64(buffer.Len())); err != nil {
|
||||||
log.Printf("%s", err.Error())
|
s.logger.Printf("%s", err.Error())
|
||||||
http.Error(w, errors.New("Could not save metadata").Error(), 500)
|
http.Error(w, errors.New("Could not save metadata").Error(), 500)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
log.Printf("Uploading %s %s %d %s", token, filename, contentLength, contentType)
|
s.logger.Printf("Uploading %s %s %d %s", token, filename, contentLength, contentType)
|
||||||
|
|
||||||
var err error
|
var err error
|
||||||
|
|
||||||
if err = s.storage.Put(token, filename, reader, contentType, uint64(contentLength)); err != nil {
|
if err = s.storage.Put(token, filename, reader, contentType, uint64(contentLength)); err != nil {
|
||||||
log.Printf("Error putting new file: %s", err.Error())
|
s.logger.Printf("Error putting new file: %s", err.Error())
|
||||||
http.Error(w, errors.New("Could not save file").Error(), 500)
|
http.Error(w, errors.New("Could not save file").Error(), 500)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
@@ -620,7 +639,7 @@ func getURL(r *http.Request, proxyPort string) *url.URL {
|
|||||||
return u
|
return u
|
||||||
}
|
}
|
||||||
|
|
||||||
func (metadata Metadata) remainingLimitHeaderValues() (remainingDownloads, remainingDays string) {
|
func (metadata metadata) remainingLimitHeaderValues() (remainingDownloads, remainingDays string) {
|
||||||
if metadata.MaxDate.IsZero() {
|
if metadata.MaxDate.IsZero() {
|
||||||
remainingDays = "n/a"
|
remainingDays = "n/a"
|
||||||
} else {
|
} else {
|
||||||
@@ -637,30 +656,29 @@ func (metadata Metadata) remainingLimitHeaderValues() (remainingDownloads, remai
|
|||||||
return remainingDownloads, remainingDays
|
return remainingDownloads, remainingDays
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *Server) Lock(token, filename string) error {
|
func (s *Server) lock(token, filename string) {
|
||||||
key := path.Join(token, filename)
|
key := path.Join(token, filename)
|
||||||
|
|
||||||
if _, ok := s.locks[key]; !ok {
|
lock, _ := s.locks.LoadOrStore(key, &sync.Mutex{})
|
||||||
s.locks[key] = &sync.Mutex{}
|
|
||||||
}
|
|
||||||
|
|
||||||
s.locks[key].Lock()
|
lock.(*sync.Mutex).Lock()
|
||||||
|
|
||||||
return nil
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *Server) Unlock(token, filename string) error {
|
func (s *Server) unlock(token, filename string) {
|
||||||
key := path.Join(token, filename)
|
key := path.Join(token, filename)
|
||||||
s.locks[key].Unlock()
|
|
||||||
|
|
||||||
return nil
|
lock, _ := s.locks.LoadOrStore(key, &sync.Mutex{})
|
||||||
|
|
||||||
|
lock.(*sync.Mutex).Unlock()
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *Server) CheckMetadata(token, filename string, increaseDownload bool) (Metadata, error) {
|
func (s *Server) checkMetadata(token, filename string, increaseDownload bool) (metadata, error) {
|
||||||
s.Lock(token, filename)
|
s.lock(token, filename)
|
||||||
defer s.Unlock(token, filename)
|
defer s.unlock(token, filename)
|
||||||
|
|
||||||
var metadata Metadata
|
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 err != nil {
|
if err != nil {
|
||||||
@@ -672,9 +690,9 @@ func (s *Server) CheckMetadata(token, filename string, increaseDownload bool) (M
|
|||||||
if err := json.NewDecoder(r).Decode(&metadata); err != nil {
|
if err := json.NewDecoder(r).Decode(&metadata); err != nil {
|
||||||
return metadata, err
|
return metadata, err
|
||||||
} else if metadata.MaxDownloads != -1 && metadata.Downloads >= metadata.MaxDownloads {
|
} else if metadata.MaxDownloads != -1 && metadata.Downloads >= metadata.MaxDownloads {
|
||||||
return metadata, errors.New("MaxDownloads expired.")
|
return metadata, errors.New("maxDownloads expired")
|
||||||
} else if !metadata.MaxDate.IsZero() && time.Now().After(metadata.MaxDate) {
|
} else if !metadata.MaxDate.IsZero() && time.Now().After(metadata.MaxDate) {
|
||||||
return metadata, errors.New("MaxDate expired.")
|
return metadata, errors.New("maxDate expired")
|
||||||
} else if metadata.MaxDownloads != -1 && increaseDownload {
|
} else if metadata.MaxDownloads != -1 && increaseDownload {
|
||||||
// todo(nl5887): mutex?
|
// todo(nl5887): mutex?
|
||||||
|
|
||||||
@@ -692,15 +710,15 @@ func (s *Server) CheckMetadata(token, filename string, increaseDownload bool) (M
|
|||||||
return metadata, nil
|
return metadata, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *Server) CheckDeletionToken(deletionToken, token, filename string) error {
|
func (s *Server) checkDeletionToken(deletionToken, token, filename string) error {
|
||||||
s.Lock(token, filename)
|
s.lock(token, filename)
|
||||||
defer s.Unlock(token, filename)
|
defer s.unlock(token, filename)
|
||||||
|
|
||||||
var metadata Metadata
|
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) {
|
if s.storage.IsNotExist(err) {
|
||||||
return errors.New("Metadata doesn't exist")
|
return errors.New("metadata doesn't exist")
|
||||||
} else if err != nil {
|
} else if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
@@ -710,7 +728,7 @@ func (s *Server) CheckDeletionToken(deletionToken, token, filename string) error
|
|||||||
if err := json.NewDecoder(r).Decode(&metadata); err != nil {
|
if err := json.NewDecoder(r).Decode(&metadata); err != nil {
|
||||||
return err
|
return err
|
||||||
} else if metadata.DeletionToken != deletionToken {
|
} else if metadata.DeletionToken != deletionToken {
|
||||||
return errors.New("Deletion token doesn't match.")
|
return errors.New("deletion token doesn't match")
|
||||||
}
|
}
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
@@ -723,7 +741,7 @@ func (s *Server) purgeHandler() {
|
|||||||
select {
|
select {
|
||||||
case <-ticker.C:
|
case <-ticker.C:
|
||||||
err := s.storage.Purge(s.purgeDays)
|
err := s.storage.Purge(s.purgeDays)
|
||||||
log.Printf("error cleaning up expired files: %v", err)
|
s.logger.Printf("error cleaning up expired files: %v", err)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}()
|
}()
|
||||||
@@ -736,8 +754,8 @@ func (s *Server) deleteHandler(w http.ResponseWriter, r *http.Request) {
|
|||||||
filename := vars["filename"]
|
filename := vars["filename"]
|
||||||
deletionToken := vars["deletionToken"]
|
deletionToken := vars["deletionToken"]
|
||||||
|
|
||||||
if err := s.CheckDeletionToken(deletionToken, token, filename); err != nil {
|
if err := s.checkDeletionToken(deletionToken, token, filename); err != nil {
|
||||||
log.Printf("Error metadata: %s", err.Error())
|
s.logger.Printf("Error metadata: %s", err.Error())
|
||||||
http.Error(w, http.StatusText(http.StatusNotFound), http.StatusNotFound)
|
http.Error(w, http.StatusText(http.StatusNotFound), http.StatusNotFound)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
@@ -747,7 +765,7 @@ func (s *Server) deleteHandler(w http.ResponseWriter, r *http.Request) {
|
|||||||
http.Error(w, http.StatusText(http.StatusNotFound), http.StatusNotFound)
|
http.Error(w, http.StatusText(http.StatusNotFound), http.StatusNotFound)
|
||||||
return
|
return
|
||||||
} else if err != nil {
|
} else if err != nil {
|
||||||
log.Printf("%s", err.Error())
|
s.logger.Printf("%s", err.Error())
|
||||||
http.Error(w, "Could not delete file.", 500)
|
http.Error(w, "Could not delete file.", 500)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
@@ -772,8 +790,8 @@ func (s *Server) zipHandler(w http.ResponseWriter, r *http.Request) {
|
|||||||
token := strings.Split(key, "/")[0]
|
token := strings.Split(key, "/")[0]
|
||||||
filename := sanitize(strings.Split(key, "/")[1])
|
filename := sanitize(strings.Split(key, "/")[1])
|
||||||
|
|
||||||
if _, err := s.CheckMetadata(token, filename, true); err != nil {
|
if _, err := s.checkMetadata(token, filename, true); err != nil {
|
||||||
log.Printf("Error metadata: %s", err.Error())
|
s.logger.Printf("Error metadata: %s", err.Error())
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -783,11 +801,11 @@ func (s *Server) zipHandler(w http.ResponseWriter, r *http.Request) {
|
|||||||
if s.storage.IsNotExist(err) {
|
if s.storage.IsNotExist(err) {
|
||||||
http.Error(w, "File not found", 404)
|
http.Error(w, "File not found", 404)
|
||||||
return
|
return
|
||||||
} else {
|
|
||||||
log.Printf("%s", err.Error())
|
|
||||||
http.Error(w, "Could not retrieve file.", 500)
|
|
||||||
return
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
s.logger.Printf("%s", err.Error())
|
||||||
|
http.Error(w, "Could not retrieve file.", 500)
|
||||||
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
defer reader.Close()
|
defer reader.Close()
|
||||||
@@ -802,20 +820,20 @@ func (s *Server) zipHandler(w http.ResponseWriter, r *http.Request) {
|
|||||||
fw, err := zw.CreateHeader(header)
|
fw, err := zw.CreateHeader(header)
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Printf("%s", err.Error())
|
s.logger.Printf("%s", err.Error())
|
||||||
http.Error(w, "Internal server error.", 500)
|
http.Error(w, "Internal server error.", 500)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
if _, err = io.Copy(fw, reader); err != nil {
|
if _, err = io.Copy(fw, reader); err != nil {
|
||||||
log.Printf("%s", err.Error())
|
s.logger.Printf("%s", err.Error())
|
||||||
http.Error(w, "Internal server error.", 500)
|
http.Error(w, "Internal server error.", 500)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if err := zw.Close(); err != nil {
|
if err := zw.Close(); err != nil {
|
||||||
log.Printf("%s", err.Error())
|
s.logger.Printf("%s", err.Error())
|
||||||
http.Error(w, "Internal server error.", 500)
|
http.Error(w, "Internal server error.", 500)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
@@ -844,8 +862,8 @@ func (s *Server) tarGzHandler(w http.ResponseWriter, r *http.Request) {
|
|||||||
token := strings.Split(key, "/")[0]
|
token := strings.Split(key, "/")[0]
|
||||||
filename := sanitize(strings.Split(key, "/")[1])
|
filename := sanitize(strings.Split(key, "/")[1])
|
||||||
|
|
||||||
if _, err := s.CheckMetadata(token, filename, true); err != nil {
|
if _, err := s.checkMetadata(token, filename, true); err != nil {
|
||||||
log.Printf("Error metadata: %s", err.Error())
|
s.logger.Printf("Error metadata: %s", err.Error())
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -854,11 +872,11 @@ func (s *Server) tarGzHandler(w http.ResponseWriter, r *http.Request) {
|
|||||||
if s.storage.IsNotExist(err) {
|
if s.storage.IsNotExist(err) {
|
||||||
http.Error(w, "File not found", 404)
|
http.Error(w, "File not found", 404)
|
||||||
return
|
return
|
||||||
} else {
|
|
||||||
log.Printf("%s", err.Error())
|
|
||||||
http.Error(w, "Could not retrieve file.", 500)
|
|
||||||
return
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
s.logger.Printf("%s", err.Error())
|
||||||
|
http.Error(w, "Could not retrieve file.", 500)
|
||||||
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
defer reader.Close()
|
defer reader.Close()
|
||||||
@@ -870,13 +888,13 @@ func (s *Server) tarGzHandler(w http.ResponseWriter, r *http.Request) {
|
|||||||
|
|
||||||
err = zw.WriteHeader(header)
|
err = zw.WriteHeader(header)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Printf("%s", err.Error())
|
s.logger.Printf("%s", err.Error())
|
||||||
http.Error(w, "Internal server error.", 500)
|
http.Error(w, "Internal server error.", 500)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
if _, err = io.Copy(zw, reader); err != nil {
|
if _, err = io.Copy(zw, reader); err != nil {
|
||||||
log.Printf("%s", err.Error())
|
s.logger.Printf("%s", err.Error())
|
||||||
http.Error(w, "Internal server error.", 500)
|
http.Error(w, "Internal server error.", 500)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
@@ -903,8 +921,8 @@ func (s *Server) tarHandler(w http.ResponseWriter, r *http.Request) {
|
|||||||
token := strings.Split(key, "/")[0]
|
token := strings.Split(key, "/")[0]
|
||||||
filename := strings.Split(key, "/")[1]
|
filename := strings.Split(key, "/")[1]
|
||||||
|
|
||||||
if _, err := s.CheckMetadata(token, filename, true); err != nil {
|
if _, err := s.checkMetadata(token, filename, true); err != nil {
|
||||||
log.Printf("Error metadata: %s", err.Error())
|
s.logger.Printf("Error metadata: %s", err.Error())
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -913,11 +931,11 @@ func (s *Server) tarHandler(w http.ResponseWriter, r *http.Request) {
|
|||||||
if s.storage.IsNotExist(err) {
|
if s.storage.IsNotExist(err) {
|
||||||
http.Error(w, "File not found", 404)
|
http.Error(w, "File not found", 404)
|
||||||
return
|
return
|
||||||
} else {
|
|
||||||
log.Printf("%s", err.Error())
|
|
||||||
http.Error(w, "Could not retrieve file.", 500)
|
|
||||||
return
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
s.logger.Printf("%s", err.Error())
|
||||||
|
http.Error(w, "Could not retrieve file.", 500)
|
||||||
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
defer reader.Close()
|
defer reader.Close()
|
||||||
@@ -929,13 +947,13 @@ func (s *Server) tarHandler(w http.ResponseWriter, r *http.Request) {
|
|||||||
|
|
||||||
err = zw.WriteHeader(header)
|
err = zw.WriteHeader(header)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Printf("%s", err.Error())
|
s.logger.Printf("%s", err.Error())
|
||||||
http.Error(w, "Internal server error.", 500)
|
http.Error(w, "Internal server error.", 500)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
if _, err = io.Copy(zw, reader); err != nil {
|
if _, err = io.Copy(zw, reader); err != nil {
|
||||||
log.Printf("%s", err.Error())
|
s.logger.Printf("%s", err.Error())
|
||||||
http.Error(w, "Internal server error.", 500)
|
http.Error(w, "Internal server error.", 500)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
@@ -948,10 +966,10 @@ func (s *Server) headHandler(w http.ResponseWriter, r *http.Request) {
|
|||||||
token := vars["token"]
|
token := vars["token"]
|
||||||
filename := vars["filename"]
|
filename := vars["filename"]
|
||||||
|
|
||||||
metadata, err := s.CheckMetadata(token, filename, false)
|
metadata, err := s.checkMetadata(token, filename, false)
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Printf("Error metadata: %s", err.Error())
|
s.logger.Printf("Error metadata: %s", err.Error())
|
||||||
http.Error(w, http.StatusText(http.StatusNotFound), http.StatusNotFound)
|
http.Error(w, http.StatusText(http.StatusNotFound), http.StatusNotFound)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
@@ -962,7 +980,7 @@ func (s *Server) headHandler(w http.ResponseWriter, r *http.Request) {
|
|||||||
http.Error(w, http.StatusText(http.StatusNotFound), http.StatusNotFound)
|
http.Error(w, http.StatusText(http.StatusNotFound), http.StatusNotFound)
|
||||||
return
|
return
|
||||||
} else if err != nil {
|
} else if err != nil {
|
||||||
log.Printf("%s", err.Error())
|
s.logger.Printf("%s", err.Error())
|
||||||
http.Error(w, "Could not retrieve file.", 500)
|
http.Error(w, "Could not retrieve file.", 500)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
@@ -983,10 +1001,10 @@ func (s *Server) getHandler(w http.ResponseWriter, r *http.Request) {
|
|||||||
token := vars["token"]
|
token := vars["token"]
|
||||||
filename := vars["filename"]
|
filename := vars["filename"]
|
||||||
|
|
||||||
metadata, err := s.CheckMetadata(token, filename, true)
|
metadata, err := s.checkMetadata(token, filename, true)
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Printf("Error metadata: %s", err.Error())
|
s.logger.Printf("Error metadata: %s", err.Error())
|
||||||
http.Error(w, http.StatusText(http.StatusNotFound), http.StatusNotFound)
|
http.Error(w, http.StatusText(http.StatusNotFound), http.StatusNotFound)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
@@ -997,7 +1015,7 @@ func (s *Server) getHandler(w http.ResponseWriter, r *http.Request) {
|
|||||||
http.Error(w, http.StatusText(http.StatusNotFound), http.StatusNotFound)
|
http.Error(w, http.StatusText(http.StatusNotFound), http.StatusNotFound)
|
||||||
return
|
return
|
||||||
} else if err != nil {
|
} else if err != nil {
|
||||||
log.Printf("%s", err.Error())
|
s.logger.Printf("%s", err.Error())
|
||||||
http.Error(w, "Could not retrieve file.", 500)
|
http.Error(w, "Could not retrieve file.", 500)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
@@ -1025,46 +1043,40 @@ func (s *Server) getHandler(w http.ResponseWriter, r *http.Request) {
|
|||||||
reader = ioutil.NopCloser(bluemonday.UGCPolicy().SanitizeReader(reader))
|
reader = ioutil.NopCloser(bluemonday.UGCPolicy().SanitizeReader(reader))
|
||||||
}
|
}
|
||||||
|
|
||||||
if w.Header().Get("Range") == "" {
|
if w.Header().Get("Range") != "" || strings.HasPrefix(metadata.ContentType, "video") || strings.HasPrefix(metadata.ContentType, "audio") {
|
||||||
if _, err = io.Copy(w, reader); err != nil {
|
file, err := ioutil.TempFile(s.tempPath, "range-")
|
||||||
log.Printf("%s", err.Error())
|
if err != nil {
|
||||||
|
s.logger.Printf("%s", err.Error())
|
||||||
http.Error(w, "Error occurred copying to output stream", 500)
|
http.Error(w, "Error occurred copying to output stream", 500)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
defer s.cleanTmpFile(file)
|
||||||
|
|
||||||
|
_, err = io.Copy(file, reader)
|
||||||
|
if err != nil {
|
||||||
|
s.logger.Printf("%s", err.Error())
|
||||||
|
http.Error(w, "Error occurred copying to output stream", 500)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
http.ServeContent(w, r, filename, time.Now(), file)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
file, err := ioutil.TempFile(s.tempPath, "range-")
|
if _, err = io.Copy(w, reader); err != nil {
|
||||||
if err != nil {
|
s.logger.Printf("%s", err.Error())
|
||||||
log.Printf("%s", err.Error())
|
|
||||||
http.Error(w, "Error occurred copying to output stream", 500)
|
http.Error(w, "Error occurred copying to output stream", 500)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
defer cleanTmpFile(file)
|
return
|
||||||
|
|
||||||
tee := io.TeeReader(reader, file)
|
|
||||||
for {
|
|
||||||
b := make([]byte, _5M)
|
|
||||||
_, err = tee.Read(b)
|
|
||||||
if err == io.EOF {
|
|
||||||
break
|
|
||||||
}
|
|
||||||
|
|
||||||
if err != nil {
|
|
||||||
log.Printf("%s", err.Error())
|
|
||||||
http.Error(w, "Error occurred copying to output stream", 500)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
http.ServeContent(w, r, filename, time.Now(), file)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// RedirectHandler handles redirect
|
||||||
func (s *Server) RedirectHandler(h http.Handler) http.HandlerFunc {
|
func (s *Server) RedirectHandler(h http.Handler) http.HandlerFunc {
|
||||||
return func(w http.ResponseWriter, r *http.Request) {
|
return func(w http.ResponseWriter, r *http.Request) {
|
||||||
if !s.forceHTTPs {
|
if !s.forceHTTPS {
|
||||||
// we don't want to enforce https
|
// we don't want to enforce https
|
||||||
} else if r.URL.Path == "/health.html" {
|
} else if r.URL.Path == "/health.html" {
|
||||||
// health check url won't redirect
|
// health check url won't redirect
|
||||||
@@ -1084,17 +1096,17 @@ func (s *Server) RedirectHandler(h http.Handler) http.HandlerFunc {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Create a log handler for every request it receives.
|
// LoveHandler Create a log handler for every request it receives.
|
||||||
func LoveHandler(h http.Handler) http.HandlerFunc {
|
func LoveHandler(h http.Handler) http.HandlerFunc {
|
||||||
return func(w http.ResponseWriter, r *http.Request) {
|
return func(w http.ResponseWriter, r *http.Request) {
|
||||||
w.Header().Set("x-made-with", "<3 by DutchCoders")
|
w.Header().Set("x-made-with", "<3 by DutchCoders")
|
||||||
w.Header().Set("x-served-by", "Proudly served by DutchCoders")
|
w.Header().Set("x-served-by", "Proudly served by DutchCoders")
|
||||||
w.Header().Set("Server", "Transfer.sh HTTP Server 1.0")
|
w.Header().Set("server", "Transfer.sh HTTP Server")
|
||||||
h.ServeHTTP(w, r)
|
h.ServeHTTP(w, r)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func IPFilterHandler(h http.Handler, ipFilterOptions *IPFilterOptions) http.HandlerFunc {
|
func ipFilterHandler(h http.Handler, ipFilterOptions *IPFilterOptions) http.HandlerFunc {
|
||||||
return func(w http.ResponseWriter, r *http.Request) {
|
return func(w http.ResponseWriter, r *http.Request) {
|
||||||
if ipFilterOptions == nil {
|
if ipFilterOptions == nil {
|
||||||
h.ServeHTTP(w, r)
|
h.ServeHTTP(w, r)
|
||||||
@@ -1105,7 +1117,7 @@ func IPFilterHandler(h http.Handler, ipFilterOptions *IPFilterOptions) http.Hand
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *Server) BasicAuthHandler(h http.Handler) http.HandlerFunc {
|
func (s *Server) basicAuthHandler(h http.Handler) http.HandlerFunc {
|
||||||
return func(w http.ResponseWriter, r *http.Request) {
|
return func(w http.ResponseWriter, r *http.Request) {
|
||||||
if s.AuthUser == "" || s.AuthPass == "" {
|
if s.AuthUser == "" || s.AuthPass == "" {
|
||||||
h.ServeHTTP(w, r)
|
h.ServeHTTP(w, r)
|
||||||
|
@@ -13,16 +13,16 @@ import (
|
|||||||
func Test(t *testing.T) { TestingT(t) }
|
func Test(t *testing.T) { TestingT(t) }
|
||||||
|
|
||||||
var (
|
var (
|
||||||
_ = Suite(&SuiteRedirectWithForceHTTPs{})
|
_ = Suite(&suiteRedirectWithForceHTTPS{})
|
||||||
_ = Suite(&SuiteRedirectWithoutForceHTTPs{})
|
_ = Suite(&suiteRedirectWithoutForceHTTPS{})
|
||||||
)
|
)
|
||||||
|
|
||||||
type SuiteRedirectWithForceHTTPs struct {
|
type suiteRedirectWithForceHTTPS struct {
|
||||||
handler http.HandlerFunc
|
handler http.HandlerFunc
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *SuiteRedirectWithForceHTTPs) SetUpTest(c *C) {
|
func (s *suiteRedirectWithForceHTTPS) SetUpTest(c *C) {
|
||||||
srvr, err := New(ForceHTTPs())
|
srvr, err := New(ForceHTTPS())
|
||||||
c.Assert(err, IsNil)
|
c.Assert(err, IsNil)
|
||||||
|
|
||||||
handler := http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
handler := http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||||
@@ -32,7 +32,7 @@ func (s *SuiteRedirectWithForceHTTPs) SetUpTest(c *C) {
|
|||||||
s.handler = srvr.RedirectHandler(handler)
|
s.handler = srvr.RedirectHandler(handler)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *SuiteRedirectWithForceHTTPs) TestHTTPs(c *C) {
|
func (s *suiteRedirectWithForceHTTPS) TestHTTPs(c *C) {
|
||||||
req := httptest.NewRequest("GET", "https://test/test", nil)
|
req := httptest.NewRequest("GET", "https://test/test", nil)
|
||||||
|
|
||||||
w := httptest.NewRecorder()
|
w := httptest.NewRecorder()
|
||||||
@@ -42,7 +42,7 @@ func (s *SuiteRedirectWithForceHTTPs) TestHTTPs(c *C) {
|
|||||||
c.Assert(resp.StatusCode, Equals, http.StatusOK)
|
c.Assert(resp.StatusCode, Equals, http.StatusOK)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *SuiteRedirectWithForceHTTPs) TestOnion(c *C) {
|
func (s *suiteRedirectWithForceHTTPS) TestOnion(c *C) {
|
||||||
req := httptest.NewRequest("GET", "http://test.onion/test", nil)
|
req := httptest.NewRequest("GET", "http://test.onion/test", nil)
|
||||||
|
|
||||||
w := httptest.NewRecorder()
|
w := httptest.NewRecorder()
|
||||||
@@ -52,7 +52,7 @@ func (s *SuiteRedirectWithForceHTTPs) TestOnion(c *C) {
|
|||||||
c.Assert(resp.StatusCode, Equals, http.StatusOK)
|
c.Assert(resp.StatusCode, Equals, http.StatusOK)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *SuiteRedirectWithForceHTTPs) TestXForwardedFor(c *C) {
|
func (s *suiteRedirectWithForceHTTPS) TestXForwardedFor(c *C) {
|
||||||
req := httptest.NewRequest("GET", "http://127.0.0.1/test", nil)
|
req := httptest.NewRequest("GET", "http://127.0.0.1/test", nil)
|
||||||
req.Header.Set("X-Forwarded-Proto", "https")
|
req.Header.Set("X-Forwarded-Proto", "https")
|
||||||
|
|
||||||
@@ -63,7 +63,7 @@ func (s *SuiteRedirectWithForceHTTPs) TestXForwardedFor(c *C) {
|
|||||||
c.Assert(resp.StatusCode, Equals, http.StatusOK)
|
c.Assert(resp.StatusCode, Equals, http.StatusOK)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *SuiteRedirectWithForceHTTPs) TestHTTP(c *C) {
|
func (s *suiteRedirectWithForceHTTPS) TestHTTP(c *C) {
|
||||||
req := httptest.NewRequest("GET", "http://127.0.0.1/test", nil)
|
req := httptest.NewRequest("GET", "http://127.0.0.1/test", nil)
|
||||||
|
|
||||||
w := httptest.NewRecorder()
|
w := httptest.NewRecorder()
|
||||||
@@ -74,11 +74,11 @@ func (s *SuiteRedirectWithForceHTTPs) TestHTTP(c *C) {
|
|||||||
c.Assert(resp.Header.Get("Location"), Equals, "https://127.0.0.1/test")
|
c.Assert(resp.Header.Get("Location"), Equals, "https://127.0.0.1/test")
|
||||||
}
|
}
|
||||||
|
|
||||||
type SuiteRedirectWithoutForceHTTPs struct {
|
type suiteRedirectWithoutForceHTTPS struct {
|
||||||
handler http.HandlerFunc
|
handler http.HandlerFunc
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *SuiteRedirectWithoutForceHTTPs) SetUpTest(c *C) {
|
func (s *suiteRedirectWithoutForceHTTPS) SetUpTest(c *C) {
|
||||||
srvr, err := New()
|
srvr, err := New()
|
||||||
c.Assert(err, IsNil)
|
c.Assert(err, IsNil)
|
||||||
|
|
||||||
@@ -89,7 +89,7 @@ func (s *SuiteRedirectWithoutForceHTTPs) SetUpTest(c *C) {
|
|||||||
s.handler = srvr.RedirectHandler(handler)
|
s.handler = srvr.RedirectHandler(handler)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *SuiteRedirectWithoutForceHTTPs) TestHTTP(c *C) {
|
func (s *suiteRedirectWithoutForceHTTPS) TestHTTP(c *C) {
|
||||||
req := httptest.NewRequest("GET", "http://127.0.0.1/test", nil)
|
req := httptest.NewRequest("GET", "http://127.0.0.1/test", nil)
|
||||||
|
|
||||||
w := httptest.NewRecorder()
|
w := httptest.NewRecorder()
|
||||||
@@ -99,7 +99,7 @@ func (s *SuiteRedirectWithoutForceHTTPs) TestHTTP(c *C) {
|
|||||||
c.Assert(resp.StatusCode, Equals, http.StatusOK)
|
c.Assert(resp.StatusCode, Equals, http.StatusOK)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *SuiteRedirectWithoutForceHTTPs) TestHTTPs(c *C) {
|
func (s *suiteRedirectWithoutForceHTTPS) TestHTTPs(c *C) {
|
||||||
req := httptest.NewRequest("GET", "https://127.0.0.1/test", nil)
|
req := httptest.NewRequest("GET", "https://127.0.0.1/test", nil)
|
||||||
|
|
||||||
w := httptest.NewRecorder()
|
w := httptest.NewRecorder()
|
||||||
|
@@ -21,7 +21,7 @@ import (
|
|||||||
"github.com/tomasen/realip"
|
"github.com/tomasen/realip"
|
||||||
)
|
)
|
||||||
|
|
||||||
//IPFilterOptions for IPFilter. Allowed takes precendence over Blocked.
|
//IPFilterOptions for ipFilter. Allowed takes precedence over Blocked.
|
||||||
//IPs can be IPv4 or IPv6 and can optionally contain subnet
|
//IPs can be IPv4 or IPv6 and can optionally contain subnet
|
||||||
//masks (/24). Note however, determining if a given IP is
|
//masks (/24). Note however, determining if a given IP is
|
||||||
//included in a subnet requires a linear scan so is less performant
|
//included in a subnet requires a linear scan so is less performant
|
||||||
@@ -43,7 +43,8 @@ type IPFilterOptions struct {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
type IPFilter struct {
|
// ipFilter
|
||||||
|
type ipFilter struct {
|
||||||
opts IPFilterOptions
|
opts IPFilterOptions
|
||||||
//mut protects the below
|
//mut protects the below
|
||||||
//rw since writes are rare
|
//rw since writes are rare
|
||||||
@@ -59,13 +60,12 @@ type subnet struct {
|
|||||||
allowed bool
|
allowed bool
|
||||||
}
|
}
|
||||||
|
|
||||||
//New constructs IPFilter instance.
|
func newIPFilter(opts IPFilterOptions) *ipFilter {
|
||||||
func NewIPFilter(opts IPFilterOptions) *IPFilter {
|
|
||||||
if opts.Logger == nil {
|
if opts.Logger == nil {
|
||||||
flags := log.LstdFlags
|
flags := log.LstdFlags
|
||||||
opts.Logger = log.New(os.Stdout, "", flags)
|
opts.Logger = log.New(os.Stdout, "", flags)
|
||||||
}
|
}
|
||||||
f := &IPFilter{
|
f := &ipFilter{
|
||||||
opts: opts,
|
opts: opts,
|
||||||
ips: map[string]bool{},
|
ips: map[string]bool{},
|
||||||
defaultAllowed: !opts.BlockByDefault,
|
defaultAllowed: !opts.BlockByDefault,
|
||||||
@@ -79,15 +79,15 @@ func NewIPFilter(opts IPFilterOptions) *IPFilter {
|
|||||||
return f
|
return f
|
||||||
}
|
}
|
||||||
|
|
||||||
func (f *IPFilter) AllowIP(ip string) bool {
|
func (f *ipFilter) AllowIP(ip string) bool {
|
||||||
return f.ToggleIP(ip, true)
|
return f.ToggleIP(ip, true)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (f *IPFilter) BlockIP(ip string) bool {
|
func (f *ipFilter) BlockIP(ip string) bool {
|
||||||
return f.ToggleIP(ip, false)
|
return f.ToggleIP(ip, false)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (f *IPFilter) ToggleIP(str string, allowed bool) bool {
|
func (f *ipFilter) ToggleIP(str string, allowed bool) bool {
|
||||||
//check if has subnet
|
//check if has subnet
|
||||||
if ip, net, err := net.ParseCIDR(str); err == nil {
|
if ip, net, err := net.ParseCIDR(str); err == nil {
|
||||||
// containing only one ip?
|
// containing only one ip?
|
||||||
@@ -128,19 +128,19 @@ func (f *IPFilter) ToggleIP(str string, allowed bool) bool {
|
|||||||
}
|
}
|
||||||
|
|
||||||
//ToggleDefault alters the default setting
|
//ToggleDefault alters the default setting
|
||||||
func (f *IPFilter) ToggleDefault(allowed bool) {
|
func (f *ipFilter) ToggleDefault(allowed bool) {
|
||||||
f.mut.Lock()
|
f.mut.Lock()
|
||||||
f.defaultAllowed = allowed
|
f.defaultAllowed = allowed
|
||||||
f.mut.Unlock()
|
f.mut.Unlock()
|
||||||
}
|
}
|
||||||
|
|
||||||
//Allowed returns if a given IP can pass through the filter
|
//Allowed returns if a given IP can pass through the filter
|
||||||
func (f *IPFilter) Allowed(ipstr string) bool {
|
func (f *ipFilter) Allowed(ipstr string) bool {
|
||||||
return f.NetAllowed(net.ParseIP(ipstr))
|
return f.NetAllowed(net.ParseIP(ipstr))
|
||||||
}
|
}
|
||||||
|
|
||||||
//NetAllowed returns if a given net.IP can pass through the filter
|
//NetAllowed returns if a given net.IP can pass through the filter
|
||||||
func (f *IPFilter) NetAllowed(ip net.IP) bool {
|
func (f *ipFilter) NetAllowed(ip net.IP) bool {
|
||||||
//invalid ip
|
//invalid ip
|
||||||
if ip == nil {
|
if ip == nil {
|
||||||
return false
|
return false
|
||||||
@@ -173,35 +173,35 @@ func (f *IPFilter) NetAllowed(ip net.IP) bool {
|
|||||||
}
|
}
|
||||||
|
|
||||||
//Blocked returns if a given IP can NOT pass through the filter
|
//Blocked returns if a given IP can NOT pass through the filter
|
||||||
func (f *IPFilter) Blocked(ip string) bool {
|
func (f *ipFilter) Blocked(ip string) bool {
|
||||||
return !f.Allowed(ip)
|
return !f.Allowed(ip)
|
||||||
}
|
}
|
||||||
|
|
||||||
//NetBlocked returns if a given net.IP can NOT pass through the filter
|
//NetBlocked returns if a given net.IP can NOT pass through the filter
|
||||||
func (f *IPFilter) NetBlocked(ip net.IP) bool {
|
func (f *ipFilter) NetBlocked(ip net.IP) bool {
|
||||||
return !f.NetAllowed(ip)
|
return !f.NetAllowed(ip)
|
||||||
}
|
}
|
||||||
|
|
||||||
//WrapIPFilter the provided handler with simple IP blocking middleware
|
//WrapIPFilter the provided handler with simple IP blocking middleware
|
||||||
//using this IP filter and its configuration
|
//using this IP filter and its configuration
|
||||||
func (f *IPFilter) Wrap(next http.Handler) http.Handler {
|
func (f *ipFilter) Wrap(next http.Handler) http.Handler {
|
||||||
return &ipFilterMiddleware{IPFilter: f, next: next}
|
return &ipFilterMiddleware{ipFilter: f, next: next}
|
||||||
}
|
}
|
||||||
|
|
||||||
//WrapIPFilter is equivalent to NewIPFilter(opts) then Wrap(next)
|
//WrapIPFilter is equivalent to newIPFilter(opts) then Wrap(next)
|
||||||
func WrapIPFilter(next http.Handler, opts IPFilterOptions) http.Handler {
|
func WrapIPFilter(next http.Handler, opts IPFilterOptions) http.Handler {
|
||||||
return NewIPFilter(opts).Wrap(next)
|
return newIPFilter(opts).Wrap(next)
|
||||||
}
|
}
|
||||||
|
|
||||||
type ipFilterMiddleware struct {
|
type ipFilterMiddleware struct {
|
||||||
*IPFilter
|
*ipFilter
|
||||||
next http.Handler
|
next http.Handler
|
||||||
}
|
}
|
||||||
|
|
||||||
func (m *ipFilterMiddleware) ServeHTTP(w http.ResponseWriter, r *http.Request) {
|
func (m *ipFilterMiddleware) ServeHTTP(w http.ResponseWriter, r *http.Request) {
|
||||||
remoteIP := realip.FromRequest(r)
|
remoteIP := realip.FromRequest(r)
|
||||||
|
|
||||||
if !m.IPFilter.Allowed(remoteIP) {
|
if !m.ipFilter.Allowed(remoteIP) {
|
||||||
//show simple forbidden text
|
//show simple forbidden text
|
||||||
http.Error(w, http.StatusText(http.StatusForbidden), http.StatusForbidden)
|
http.Error(w, http.StatusText(http.StatusForbidden), http.StatusForbidden)
|
||||||
return
|
return
|
||||||
|
@@ -48,6 +48,7 @@ import (
|
|||||||
"github.com/VojtechVitek/ratelimit/memory"
|
"github.com/VojtechVitek/ratelimit/memory"
|
||||||
"github.com/gorilla/mux"
|
"github.com/gorilla/mux"
|
||||||
|
|
||||||
|
// import pprof
|
||||||
_ "net/http/pprof"
|
_ "net/http/pprof"
|
||||||
|
|
||||||
"crypto/tls"
|
"crypto/tls"
|
||||||
@@ -59,28 +60,30 @@ import (
|
|||||||
"path/filepath"
|
"path/filepath"
|
||||||
)
|
)
|
||||||
|
|
||||||
const SERVER_INFO = "transfer.sh"
|
|
||||||
|
|
||||||
// parse request with maximum memory of _24Kilobits
|
// parse request with maximum memory of _24Kilobits
|
||||||
const _24K = (1 << 3) * 24
|
const _24K = (1 << 3) * 24
|
||||||
|
|
||||||
// parse request with maximum memory of _5Megabytes
|
// parse request with maximum memory of _5Megabytes
|
||||||
const _5M = (1 << 20) * 5
|
const _5M = (1 << 20) * 5
|
||||||
|
|
||||||
|
// OptionFn is the option function type
|
||||||
type OptionFn func(*Server)
|
type OptionFn func(*Server)
|
||||||
|
|
||||||
|
// ClamavHost sets clamav host
|
||||||
func ClamavHost(s string) OptionFn {
|
func ClamavHost(s string) OptionFn {
|
||||||
return func(srvr *Server) {
|
return func(srvr *Server) {
|
||||||
srvr.ClamAVDaemonHost = s
|
srvr.ClamAVDaemonHost = s
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// VirustotalKey sets virus total key
|
||||||
func VirustotalKey(s string) OptionFn {
|
func VirustotalKey(s string) OptionFn {
|
||||||
return func(srvr *Server) {
|
return func(srvr *Server) {
|
||||||
srvr.VirusTotalKey = s
|
srvr.VirusTotalKey = s
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Listener set listener
|
||||||
func Listener(s string) OptionFn {
|
func Listener(s string) OptionFn {
|
||||||
return func(srvr *Server) {
|
return func(srvr *Server) {
|
||||||
srvr.ListenerString = s
|
srvr.ListenerString = s
|
||||||
@@ -88,6 +91,7 @@ func Listener(s string) OptionFn {
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// CorsDomains sets CORS domains
|
||||||
func CorsDomains(s string) OptionFn {
|
func CorsDomains(s string) OptionFn {
|
||||||
return func(srvr *Server) {
|
return func(srvr *Server) {
|
||||||
srvr.CorsDomains = s
|
srvr.CorsDomains = s
|
||||||
@@ -95,18 +99,21 @@ func CorsDomains(s string) OptionFn {
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// GoogleAnalytics sets GA key
|
||||||
func GoogleAnalytics(gaKey string) OptionFn {
|
func GoogleAnalytics(gaKey string) OptionFn {
|
||||||
return func(srvr *Server) {
|
return func(srvr *Server) {
|
||||||
srvr.gaKey = gaKey
|
srvr.gaKey = gaKey
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// UserVoice sets UV key
|
||||||
func UserVoice(userVoiceKey string) OptionFn {
|
func UserVoice(userVoiceKey string) OptionFn {
|
||||||
return func(srvr *Server) {
|
return func(srvr *Server) {
|
||||||
srvr.userVoiceKey = userVoiceKey
|
srvr.userVoiceKey = userVoiceKey
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// TLSListener sets TLS listener and option
|
||||||
func TLSListener(s string, t bool) OptionFn {
|
func TLSListener(s string, t bool) OptionFn {
|
||||||
return func(srvr *Server) {
|
return func(srvr *Server) {
|
||||||
srvr.TLSListenerString = s
|
srvr.TLSListenerString = s
|
||||||
@@ -115,12 +122,14 @@ func TLSListener(s string, t bool) OptionFn {
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ProfileListener sets profile listener
|
||||||
func ProfileListener(s string) OptionFn {
|
func ProfileListener(s string) OptionFn {
|
||||||
return func(srvr *Server) {
|
return func(srvr *Server) {
|
||||||
srvr.ProfileListenerString = s
|
srvr.ProfileListenerString = s
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// WebPath sets web path
|
||||||
func WebPath(s string) OptionFn {
|
func WebPath(s string) OptionFn {
|
||||||
return func(srvr *Server) {
|
return func(srvr *Server) {
|
||||||
if s[len(s)-1:] != "/" {
|
if s[len(s)-1:] != "/" {
|
||||||
@@ -131,6 +140,7 @@ func WebPath(s string) OptionFn {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ProxyPath sets proxy path
|
||||||
func ProxyPath(s string) OptionFn {
|
func ProxyPath(s string) OptionFn {
|
||||||
return func(srvr *Server) {
|
return func(srvr *Server) {
|
||||||
if s[len(s)-1:] != "/" {
|
if s[len(s)-1:] != "/" {
|
||||||
@@ -141,12 +151,14 @@ func ProxyPath(s string) OptionFn {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ProxyPort sets proxy port
|
||||||
func ProxyPort(s string) OptionFn {
|
func ProxyPort(s string) OptionFn {
|
||||||
return func(srvr *Server) {
|
return func(srvr *Server) {
|
||||||
srvr.proxyPort = s
|
srvr.proxyPort = s
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// TempPath sets temp path
|
||||||
func TempPath(s string) OptionFn {
|
func TempPath(s string) OptionFn {
|
||||||
return func(srvr *Server) {
|
return func(srvr *Server) {
|
||||||
if s[len(s)-1:] != "/" {
|
if s[len(s)-1:] != "/" {
|
||||||
@@ -157,11 +169,12 @@ func TempPath(s string) OptionFn {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// LogFile sets log file
|
||||||
func LogFile(logger *log.Logger, s string) OptionFn {
|
func LogFile(logger *log.Logger, s string) OptionFn {
|
||||||
return func(srvr *Server) {
|
return func(srvr *Server) {
|
||||||
f, err := os.OpenFile(s, os.O_RDWR|os.O_CREATE|os.O_APPEND, 0666)
|
f, err := os.OpenFile(s, os.O_RDWR|os.O_CREATE|os.O_APPEND, 0666)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Fatalf("error opening file: %v", err)
|
logger.Fatalf("error opening file: %v", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
logger.SetOutput(f)
|
logger.SetOutput(f)
|
||||||
@@ -169,30 +182,36 @@ func LogFile(logger *log.Logger, s string) OptionFn {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Logger sets logger
|
||||||
func Logger(logger *log.Logger) OptionFn {
|
func Logger(logger *log.Logger) OptionFn {
|
||||||
return func(srvr *Server) {
|
return func(srvr *Server) {
|
||||||
srvr.logger = logger
|
srvr.logger = logger
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// MaxUploadSize sets max upload size
|
||||||
func MaxUploadSize(kbytes int64) OptionFn {
|
func MaxUploadSize(kbytes int64) OptionFn {
|
||||||
return func(srvr *Server) {
|
return func(srvr *Server) {
|
||||||
srvr.maxUploadSize = kbytes * 1024
|
srvr.maxUploadSize = kbytes * 1024
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// RateLimit set rate limit
|
||||||
func RateLimit(requests int) OptionFn {
|
func RateLimit(requests int) OptionFn {
|
||||||
return func(srvr *Server) {
|
return func(srvr *Server) {
|
||||||
srvr.rateLimitRequests = requests
|
srvr.rateLimitRequests = requests
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func RandomTokenLength(length int64) OptionFn {
|
// RandomTokenLength sets random token length
|
||||||
|
func RandomTokenLength(length int) OptionFn {
|
||||||
return func(srvr *Server) {
|
return func(srvr *Server) {
|
||||||
srvr.randomTokenLength = length
|
srvr.randomTokenLength = length
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Purge sets purge days and option
|
||||||
func Purge(days, interval int) OptionFn {
|
func Purge(days, interval int) OptionFn {
|
||||||
return func(srvr *Server) {
|
return func(srvr *Server) {
|
||||||
srvr.purgeDays = time.Duration(days) * time.Hour * 24
|
srvr.purgeDays = time.Duration(days) * time.Hour * 24
|
||||||
@@ -200,24 +219,28 @@ func Purge(days, interval int) OptionFn {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func ForceHTTPs() OptionFn {
|
// ForceHTTPS sets forcing https
|
||||||
|
func ForceHTTPS() OptionFn {
|
||||||
return func(srvr *Server) {
|
return func(srvr *Server) {
|
||||||
srvr.forceHTTPs = true
|
srvr.forceHTTPS = true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// EnableProfiler sets enable profiler
|
||||||
func EnableProfiler() OptionFn {
|
func EnableProfiler() OptionFn {
|
||||||
return func(srvr *Server) {
|
return func(srvr *Server) {
|
||||||
srvr.profilerEnabled = true
|
srvr.profilerEnabled = true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// UseStorage set storage to use
|
||||||
func UseStorage(s Storage) OptionFn {
|
func UseStorage(s Storage) OptionFn {
|
||||||
return func(srvr *Server) {
|
return func(srvr *Server) {
|
||||||
srvr.storage = s
|
srvr.storage = s
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// UseLetsEncrypt set letsencrypt usage
|
||||||
func UseLetsEncrypt(hosts []string) OptionFn {
|
func UseLetsEncrypt(hosts []string) OptionFn {
|
||||||
return func(srvr *Server) {
|
return func(srvr *Server) {
|
||||||
cacheDir := "./cache/"
|
cacheDir := "./cache/"
|
||||||
@@ -246,6 +269,7 @@ func UseLetsEncrypt(hosts []string) OptionFn {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// TLSConfig sets TLS config
|
||||||
func TLSConfig(cert, pk string) OptionFn {
|
func TLSConfig(cert, pk string) OptionFn {
|
||||||
certificate, err := tls.LoadX509KeyPair(cert, pk)
|
certificate, err := tls.LoadX509KeyPair(cert, pk)
|
||||||
return func(srvr *Server) {
|
return func(srvr *Server) {
|
||||||
@@ -257,13 +281,15 @@ func TLSConfig(cert, pk string) OptionFn {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func HttpAuthCredentials(user string, pass string) OptionFn {
|
// HTTPAuthCredentials sets basic http auth credentials
|
||||||
|
func HTTPAuthCredentials(user string, pass string) OptionFn {
|
||||||
return func(srvr *Server) {
|
return func(srvr *Server) {
|
||||||
srvr.AuthUser = user
|
srvr.AuthUser = user
|
||||||
srvr.AuthPass = pass
|
srvr.AuthPass = pass
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// FilterOptions sets ip filtering
|
||||||
func FilterOptions(options IPFilterOptions) OptionFn {
|
func FilterOptions(options IPFilterOptions) OptionFn {
|
||||||
for i, allowedIP := range options.AllowedIPs {
|
for i, allowedIP := range options.AllowedIPs {
|
||||||
options.AllowedIPs[i] = strings.TrimSpace(allowedIP)
|
options.AllowedIPs[i] = strings.TrimSpace(allowedIP)
|
||||||
@@ -278,6 +304,7 @@ func FilterOptions(options IPFilterOptions) OptionFn {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Server is the main application
|
||||||
type Server struct {
|
type Server struct {
|
||||||
AuthUser string
|
AuthUser string
|
||||||
AuthPass string
|
AuthPass string
|
||||||
@@ -288,7 +315,7 @@ type Server struct {
|
|||||||
|
|
||||||
profilerEnabled bool
|
profilerEnabled bool
|
||||||
|
|
||||||
locks map[string]*sync.Mutex
|
locks sync.Map
|
||||||
|
|
||||||
maxUploadSize int64
|
maxUploadSize int64
|
||||||
rateLimitRequests int
|
rateLimitRequests int
|
||||||
@@ -298,9 +325,9 @@ type Server struct {
|
|||||||
|
|
||||||
storage Storage
|
storage Storage
|
||||||
|
|
||||||
forceHTTPs bool
|
forceHTTPS bool
|
||||||
|
|
||||||
randomTokenLength int64
|
randomTokenLength int
|
||||||
|
|
||||||
ipFilterOptions *IPFilterOptions
|
ipFilterOptions *IPFilterOptions
|
||||||
|
|
||||||
@@ -327,9 +354,10 @@ type Server struct {
|
|||||||
LetsEncryptCache string
|
LetsEncryptCache string
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// New is the factory fot Server
|
||||||
func New(options ...OptionFn) (*Server, error) {
|
func New(options ...OptionFn) (*Server, error) {
|
||||||
s := &Server{
|
s := &Server{
|
||||||
locks: map[string]*sync.Mutex{},
|
locks: sync.Map{},
|
||||||
}
|
}
|
||||||
|
|
||||||
for _, optionFn := range options {
|
for _, optionFn := range options {
|
||||||
@@ -347,6 +375,7 @@ func init() {
|
|||||||
rand.Seed(int64(binary.LittleEndian.Uint64(seedBytes[:])))
|
rand.Seed(int64(binary.LittleEndian.Uint64(seedBytes[:])))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Run starts Server
|
||||||
func (s *Server) Run() {
|
func (s *Server) Run() {
|
||||||
listening := false
|
listening := false
|
||||||
|
|
||||||
@@ -402,7 +431,7 @@ func (s *Server) Run() {
|
|||||||
r.HandleFunc("/favicon.ico", staticHandler.ServeHTTP).Methods("GET")
|
r.HandleFunc("/favicon.ico", staticHandler.ServeHTTP).Methods("GET")
|
||||||
r.HandleFunc("/robots.txt", staticHandler.ServeHTTP).Methods("GET")
|
r.HandleFunc("/robots.txt", staticHandler.ServeHTTP).Methods("GET")
|
||||||
|
|
||||||
r.HandleFunc("/{filename:(?:favicon\\.ico|robots\\.txt|health\\.html)}", s.BasicAuthHandler(http.HandlerFunc(s.putHandler))).Methods("PUT")
|
r.HandleFunc("/{filename:(?:favicon\\.ico|robots\\.txt|health\\.html)}", s.basicAuthHandler(http.HandlerFunc(s.putHandler))).Methods("PUT")
|
||||||
|
|
||||||
r.HandleFunc("/health.html", healthHandler).Methods("GET")
|
r.HandleFunc("/health.html", healthHandler).Methods("GET")
|
||||||
r.HandleFunc("/", s.viewHandler).Methods("GET")
|
r.HandleFunc("/", s.viewHandler).Methods("GET")
|
||||||
@@ -446,10 +475,10 @@ func (s *Server) Run() {
|
|||||||
|
|
||||||
r.HandleFunc("/{filename}/virustotal", s.virusTotalHandler).Methods("PUT")
|
r.HandleFunc("/{filename}/virustotal", s.virusTotalHandler).Methods("PUT")
|
||||||
r.HandleFunc("/{filename}/scan", s.scanHandler).Methods("PUT")
|
r.HandleFunc("/{filename}/scan", s.scanHandler).Methods("PUT")
|
||||||
r.HandleFunc("/put/{filename}", s.BasicAuthHandler(http.HandlerFunc(s.putHandler))).Methods("PUT")
|
r.HandleFunc("/put/{filename}", s.basicAuthHandler(http.HandlerFunc(s.putHandler))).Methods("PUT")
|
||||||
r.HandleFunc("/upload/{filename}", s.BasicAuthHandler(http.HandlerFunc(s.putHandler))).Methods("PUT")
|
r.HandleFunc("/upload/{filename}", s.basicAuthHandler(http.HandlerFunc(s.putHandler))).Methods("PUT")
|
||||||
r.HandleFunc("/{filename}", s.BasicAuthHandler(http.HandlerFunc(s.putHandler))).Methods("PUT")
|
r.HandleFunc("/{filename}", s.basicAuthHandler(http.HandlerFunc(s.putHandler))).Methods("PUT")
|
||||||
r.HandleFunc("/", s.BasicAuthHandler(http.HandlerFunc(s.postHandler))).Methods("POST")
|
r.HandleFunc("/", s.basicAuthHandler(http.HandlerFunc(s.postHandler))).Methods("POST")
|
||||||
// r.HandleFunc("/{page}", viewHandler).Methods("GET")
|
// r.HandleFunc("/{page}", viewHandler).Methods("GET")
|
||||||
|
|
||||||
r.HandleFunc("/{token}/{filename}/{deletionToken}", s.deleteHandler).Methods("DELETE")
|
r.HandleFunc("/{token}/{filename}/{deletionToken}", s.deleteHandler).Methods("DELETE")
|
||||||
@@ -474,7 +503,7 @@ func (s *Server) Run() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
h := handlers.PanicHandler(
|
h := handlers.PanicHandler(
|
||||||
IPFilterHandler(
|
ipFilterHandler(
|
||||||
handlers.LogHandler(
|
handlers.LogHandler(
|
||||||
LoveHandler(
|
LoveHandler(
|
||||||
s.RedirectHandler(cors(r))),
|
s.RedirectHandler(cors(r))),
|
||||||
|
@@ -27,31 +27,43 @@ import (
|
|||||||
"storj.io/uplink"
|
"storj.io/uplink"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// Storage is the interface for storage operation
|
||||||
type Storage interface {
|
type Storage interface {
|
||||||
|
// Get retrieves a file from storage
|
||||||
Get(token string, filename string) (reader io.ReadCloser, contentLength uint64, err error)
|
Get(token string, filename string) (reader io.ReadCloser, contentLength uint64, err error)
|
||||||
|
// Head retrieves content length of a file from storage
|
||||||
Head(token string, filename string) (contentLength uint64, err error)
|
Head(token string, filename string) (contentLength uint64, err error)
|
||||||
|
// Put saves a file on storage
|
||||||
Put(token string, filename string, reader io.Reader, contentType string, contentLength uint64) error
|
Put(token string, filename string, reader io.Reader, contentType string, contentLength uint64) error
|
||||||
|
// Delete removes a file from storage
|
||||||
Delete(token string, filename string) error
|
Delete(token string, filename string) error
|
||||||
|
// IsNotExist indicates if a file doesn't exist on storage
|
||||||
IsNotExist(err error) bool
|
IsNotExist(err error) bool
|
||||||
|
// Purge cleans up the storage
|
||||||
Purge(days time.Duration) error
|
Purge(days time.Duration) error
|
||||||
|
|
||||||
|
// Type returns the storage type
|
||||||
Type() string
|
Type() string
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// LocalStorage is a local storage
|
||||||
type LocalStorage struct {
|
type LocalStorage struct {
|
||||||
Storage
|
Storage
|
||||||
basedir string
|
basedir string
|
||||||
logger *log.Logger
|
logger *log.Logger
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// NewLocalStorage is the factory for LocalStorage
|
||||||
func NewLocalStorage(basedir string, logger *log.Logger) (*LocalStorage, error) {
|
func NewLocalStorage(basedir string, logger *log.Logger) (*LocalStorage, error) {
|
||||||
return &LocalStorage{basedir: basedir, logger: logger}, nil
|
return &LocalStorage{basedir: basedir, logger: logger}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Type returns the storage type
|
||||||
func (s *LocalStorage) Type() string {
|
func (s *LocalStorage) Type() string {
|
||||||
return "local"
|
return "local"
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Head retrieves content length of a file from storage
|
||||||
func (s *LocalStorage) Head(token string, filename string) (contentLength uint64, err error) {
|
func (s *LocalStorage) Head(token string, filename string) (contentLength uint64, err error) {
|
||||||
path := filepath.Join(s.basedir, token, filename)
|
path := filepath.Join(s.basedir, token, filename)
|
||||||
|
|
||||||
@@ -65,6 +77,7 @@ func (s *LocalStorage) Head(token string, filename string) (contentLength uint64
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Get retrieves a file from storage
|
||||||
func (s *LocalStorage) Get(token string, filename string) (reader io.ReadCloser, 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)
|
path := filepath.Join(s.basedir, token, filename)
|
||||||
|
|
||||||
@@ -83,6 +96,7 @@ func (s *LocalStorage) Get(token string, filename string) (reader io.ReadCloser,
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Delete removes a file from storage
|
||||||
func (s *LocalStorage) Delete(token string, filename string) (err error) {
|
func (s *LocalStorage) Delete(token string, filename string) (err error) {
|
||||||
metadata := filepath.Join(s.basedir, token, fmt.Sprintf("%s.metadata", filename))
|
metadata := filepath.Join(s.basedir, token, fmt.Sprintf("%s.metadata", filename))
|
||||||
os.Remove(metadata)
|
os.Remove(metadata)
|
||||||
@@ -92,6 +106,7 @@ func (s *LocalStorage) Delete(token string, filename string) (err error) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Purge cleans up the storage
|
||||||
func (s *LocalStorage) Purge(days time.Duration) (err error) {
|
func (s *LocalStorage) Purge(days time.Duration) (err error) {
|
||||||
err = filepath.Walk(s.basedir,
|
err = filepath.Walk(s.basedir,
|
||||||
func(path string, info os.FileInfo, err error) error {
|
func(path string, info os.FileInfo, err error) error {
|
||||||
@@ -113,6 +128,7 @@ func (s *LocalStorage) Purge(days time.Duration) (err error) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// IsNotExist indicates if a file doesn't exist on storage
|
||||||
func (s *LocalStorage) IsNotExist(err error) bool {
|
func (s *LocalStorage) IsNotExist(err error) bool {
|
||||||
if err == nil {
|
if err == nil {
|
||||||
return false
|
return false
|
||||||
@@ -121,6 +137,7 @@ func (s *LocalStorage) IsNotExist(err error) bool {
|
|||||||
return os.IsNotExist(err)
|
return os.IsNotExist(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Put saves a file on storage
|
||||||
func (s *LocalStorage) Put(token string, filename string, reader io.Reader, contentType string, contentLength uint64) error {
|
func (s *LocalStorage) Put(token string, filename string, reader io.Reader, contentType string, contentLength uint64) error {
|
||||||
var f io.WriteCloser
|
var f io.WriteCloser
|
||||||
var err error
|
var err error
|
||||||
@@ -144,6 +161,7 @@ func (s *LocalStorage) Put(token string, filename string, reader io.Reader, cont
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// S3Storage is a storage backed by AWS S3
|
||||||
type S3Storage struct {
|
type S3Storage struct {
|
||||||
Storage
|
Storage
|
||||||
bucket string
|
bucket string
|
||||||
@@ -154,6 +172,7 @@ type S3Storage struct {
|
|||||||
noMultipart bool
|
noMultipart bool
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// NewS3Storage is the factory for S3Storage
|
||||||
func NewS3Storage(accessKey, secretKey, bucketName string, purgeDays int, region, endpoint string, disableMultipart bool, forcePathStyle bool, logger *log.Logger) (*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)
|
sess := getAwsSession(accessKey, secretKey, region, endpoint, forcePathStyle)
|
||||||
|
|
||||||
@@ -167,10 +186,12 @@ func NewS3Storage(accessKey, secretKey, bucketName string, purgeDays int, region
|
|||||||
}, nil
|
}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Type returns the storage type
|
||||||
func (s *S3Storage) Type() string {
|
func (s *S3Storage) Type() string {
|
||||||
return "s3"
|
return "s3"
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Head retrieves content length of a file from storage
|
||||||
func (s *S3Storage) Head(token string, filename string) (contentLength uint64, err error) {
|
func (s *S3Storage) Head(token string, filename string) (contentLength uint64, err error) {
|
||||||
key := fmt.Sprintf("%s/%s", token, filename)
|
key := fmt.Sprintf("%s/%s", token, filename)
|
||||||
|
|
||||||
@@ -192,11 +213,13 @@ func (s *S3Storage) Head(token string, filename string) (contentLength uint64, e
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Purge cleans up the storage
|
||||||
func (s *S3Storage) Purge(days time.Duration) (err error) {
|
func (s *S3Storage) Purge(days time.Duration) (err error) {
|
||||||
// NOOP expiration is set at upload time
|
// NOOP expiration is set at upload time
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// IsNotExist indicates if a file doesn't exist on storage
|
||||||
func (s *S3Storage) IsNotExist(err error) bool {
|
func (s *S3Storage) IsNotExist(err error) bool {
|
||||||
if err == nil {
|
if err == nil {
|
||||||
return false
|
return false
|
||||||
@@ -212,6 +235,7 @@ func (s *S3Storage) IsNotExist(err error) bool {
|
|||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Get retrieves a file from storage
|
||||||
func (s *S3Storage) Get(token string, filename string) (reader io.ReadCloser, 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)
|
key := fmt.Sprintf("%s/%s", token, filename)
|
||||||
|
|
||||||
@@ -233,6 +257,7 @@ func (s *S3Storage) Get(token string, filename string) (reader io.ReadCloser, co
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Delete removes a file from storage
|
||||||
func (s *S3Storage) Delete(token string, filename string) (err error) {
|
func (s *S3Storage) Delete(token string, filename string) (err error) {
|
||||||
metadata := fmt.Sprintf("%s/%s.metadata", token, filename)
|
metadata := fmt.Sprintf("%s/%s.metadata", token, filename)
|
||||||
deleteRequest := &s3.DeleteObjectInput{
|
deleteRequest := &s3.DeleteObjectInput{
|
||||||
@@ -256,6 +281,7 @@ func (s *S3Storage) Delete(token string, filename string) (err error) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Put saves a file on storage
|
||||||
func (s *S3Storage) Put(token string, filename string, reader io.Reader, contentType string, contentLength uint64) (err error) {
|
func (s *S3Storage) Put(token string, filename string, reader io.Reader, contentType string, contentLength uint64) (err error) {
|
||||||
key := fmt.Sprintf("%s/%s", token, filename)
|
key := fmt.Sprintf("%s/%s", token, filename)
|
||||||
|
|
||||||
@@ -273,27 +299,34 @@ func (s *S3Storage) Put(token string, filename string, reader io.Reader, content
|
|||||||
u.LeavePartsOnError = false
|
u.LeavePartsOnError = false
|
||||||
})
|
})
|
||||||
|
|
||||||
|
var expire *time.Time
|
||||||
|
if s.purgeDays.Hours() > 0 {
|
||||||
|
expire = aws.Time(time.Now().Add(s.purgeDays))
|
||||||
|
}
|
||||||
|
|
||||||
_, err = uploader.Upload(&s3manager.UploadInput{
|
_, err = uploader.Upload(&s3manager.UploadInput{
|
||||||
Bucket: aws.String(s.bucket),
|
Bucket: aws.String(s.bucket),
|
||||||
Key: aws.String(key),
|
Key: aws.String(key),
|
||||||
Body: reader,
|
Body: reader,
|
||||||
Expires: aws.Time(time.Now().Add(s.purgeDays)),
|
Expires: expire,
|
||||||
})
|
})
|
||||||
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// GDrive is a storage backed by GDrive
|
||||||
type GDrive struct {
|
type GDrive struct {
|
||||||
service *drive.Service
|
service *drive.Service
|
||||||
rootId string
|
rootID string
|
||||||
basedir string
|
basedir string
|
||||||
localConfigPath string
|
localConfigPath string
|
||||||
chunkSize int
|
chunkSize int
|
||||||
logger *log.Logger
|
logger *log.Logger
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewGDriveStorage(clientJsonFilepath string, localConfigPath string, basedir string, chunkSize int, logger *log.Logger) (*GDrive, error) {
|
// NewGDriveStorage is the factory for GDrive
|
||||||
b, err := ioutil.ReadFile(clientJsonFilepath)
|
func NewGDriveStorage(clientJSONFilepath string, localConfigPath string, basedir string, chunkSize int, logger *log.Logger) (*GDrive, error) {
|
||||||
|
b, err := ioutil.ReadFile(clientJSONFilepath)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
@@ -310,7 +343,7 @@ func NewGDriveStorage(clientJsonFilepath string, localConfigPath string, basedir
|
|||||||
}
|
}
|
||||||
|
|
||||||
chunkSize = chunkSize * 1024 * 1024
|
chunkSize = chunkSize * 1024 * 1024
|
||||||
storage := &GDrive{service: srv, basedir: basedir, rootId: "", localConfigPath: localConfigPath, chunkSize: chunkSize, logger: logger}
|
storage := &GDrive{service: srv, basedir: basedir, rootID: "", localConfigPath: localConfigPath, chunkSize: chunkSize, logger: logger}
|
||||||
err = storage.setupRoot()
|
err = storage.setupRoot()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
@@ -319,26 +352,26 @@ func NewGDriveStorage(clientJsonFilepath string, localConfigPath string, basedir
|
|||||||
return storage, nil
|
return storage, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
const GDriveRootConfigFile = "root_id.conf"
|
const gdriveRootConfigFile = "root_id.conf"
|
||||||
const GDriveTokenJsonFile = "token.json"
|
const gdriveTokenJSONFile = "token.json"
|
||||||
const GDriveDirectoryMimeType = "application/vnd.google-apps.folder"
|
const gdriveDirectoryMimeType = "application/vnd.google-apps.folder"
|
||||||
|
|
||||||
func (s *GDrive) setupRoot() error {
|
func (s *GDrive) setupRoot() error {
|
||||||
rootFileConfig := filepath.Join(s.localConfigPath, GDriveRootConfigFile)
|
rootFileConfig := filepath.Join(s.localConfigPath, gdriveRootConfigFile)
|
||||||
|
|
||||||
rootId, err := ioutil.ReadFile(rootFileConfig)
|
rootID, err := ioutil.ReadFile(rootFileConfig)
|
||||||
if err != nil && !os.IsNotExist(err) {
|
if err != nil && !os.IsNotExist(err) {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
if string(rootId) != "" {
|
if string(rootID) != "" {
|
||||||
s.rootId = string(rootId)
|
s.rootID = string(rootID)
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
dir := &drive.File{
|
dir := &drive.File{
|
||||||
Name: s.basedir,
|
Name: s.basedir,
|
||||||
MimeType: GDriveDirectoryMimeType,
|
MimeType: gdriveDirectoryMimeType,
|
||||||
}
|
}
|
||||||
|
|
||||||
di, err := s.service.Files.Create(dir).Fields("id").Do()
|
di, err := s.service.Files.Create(dir).Fields("id").Do()
|
||||||
@@ -346,8 +379,8 @@ func (s *GDrive) setupRoot() error {
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
s.rootId = di.Id
|
s.rootID = di.Id
|
||||||
err = ioutil.WriteFile(rootFileConfig, []byte(s.rootId), os.FileMode(0600))
|
err = ioutil.WriteFile(rootFileConfig, []byte(s.rootID), os.FileMode(0600))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
@@ -363,13 +396,13 @@ func (s *GDrive) list(nextPageToken string, q string) (*drive.FileList, error) {
|
|||||||
return s.service.Files.List().Fields("nextPageToken, files(id, name, mimeType)").Q(q).PageToken(nextPageToken).Do()
|
return s.service.Files.List().Fields("nextPageToken, files(id, name, mimeType)").Q(q).PageToken(nextPageToken).Do()
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *GDrive) findId(filename string, token string) (string, error) {
|
func (s *GDrive) findID(filename string, token string) (string, error) {
|
||||||
filename = strings.Replace(filename, `'`, `\'`, -1)
|
filename = strings.Replace(filename, `'`, `\'`, -1)
|
||||||
filename = strings.Replace(filename, `"`, `\"`, -1)
|
filename = strings.Replace(filename, `"`, `\"`, -1)
|
||||||
|
|
||||||
fileId, tokenId, nextPageToken := "", "", ""
|
fileID, tokenID, nextPageToken := "", "", ""
|
||||||
|
|
||||||
q := fmt.Sprintf("'%s' in parents and name='%s' and mimeType='%s' and trashed=false", s.rootId, token, GDriveDirectoryMimeType)
|
q := fmt.Sprintf("'%s' in parents and name='%s' and mimeType='%s' and trashed=false", s.rootID, token, gdriveDirectoryMimeType)
|
||||||
l, err := s.list(nextPageToken, q)
|
l, err := s.list(nextPageToken, q)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return "", err
|
return "", err
|
||||||
@@ -377,7 +410,7 @@ func (s *GDrive) findId(filename string, token string) (string, error) {
|
|||||||
|
|
||||||
for 0 < len(l.Files) {
|
for 0 < len(l.Files) {
|
||||||
for _, fi := range l.Files {
|
for _, fi := range l.Files {
|
||||||
tokenId = fi.Id
|
tokenID = fi.Id
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -386,15 +419,18 @@ func (s *GDrive) findId(filename string, token string) (string, error) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
l, err = s.list(l.NextPageToken, q)
|
l, err = s.list(l.NextPageToken, q)
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if filename == "" {
|
if filename == "" {
|
||||||
return tokenId, nil
|
return tokenID, nil
|
||||||
} else if tokenId == "" {
|
} else if tokenID == "" {
|
||||||
return "", fmt.Errorf("Cannot find file %s/%s", token, filename)
|
return "", fmt.Errorf("Cannot find file %s/%s", token, filename)
|
||||||
}
|
}
|
||||||
|
|
||||||
q = fmt.Sprintf("'%s' in parents and name='%s' and mimeType!='%s' and trashed=false", tokenId, filename, GDriveDirectoryMimeType)
|
q = fmt.Sprintf("'%s' in parents and name='%s' and mimeType!='%s' and trashed=false", tokenID, filename, gdriveDirectoryMimeType)
|
||||||
l, err = s.list(nextPageToken, q)
|
l, err = s.list(nextPageToken, q)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return "", err
|
return "", err
|
||||||
@@ -403,7 +439,7 @@ func (s *GDrive) findId(filename string, token string) (string, error) {
|
|||||||
for 0 < len(l.Files) {
|
for 0 < len(l.Files) {
|
||||||
for _, fi := range l.Files {
|
for _, fi := range l.Files {
|
||||||
|
|
||||||
fileId = fi.Id
|
fileID = fi.Id
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -412,28 +448,33 @@ func (s *GDrive) findId(filename string, token string) (string, error) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
l, err = s.list(l.NextPageToken, q)
|
l, err = s.list(l.NextPageToken, q)
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if fileId == "" {
|
if fileID == "" {
|
||||||
return "", fmt.Errorf("Cannot find file %s/%s", token, filename)
|
return "", fmt.Errorf("Cannot find file %s/%s", token, filename)
|
||||||
}
|
}
|
||||||
|
|
||||||
return fileId, nil
|
return fileID, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Type returns the storage type
|
||||||
func (s *GDrive) Type() string {
|
func (s *GDrive) Type() string {
|
||||||
return "gdrive"
|
return "gdrive"
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Head retrieves content length of a file from storage
|
||||||
func (s *GDrive) Head(token string, filename string) (contentLength uint64, err error) {
|
func (s *GDrive) Head(token string, filename string) (contentLength uint64, err error) {
|
||||||
var fileId string
|
var fileID string
|
||||||
fileId, err = s.findId(filename, token)
|
fileID, err = s.findID(filename, token)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
var fi *drive.File
|
var fi *drive.File
|
||||||
if fi, err = s.service.Files.Get(fileId).Fields("size").Do(); err != nil {
|
if fi, err = s.service.Files.Get(fileID).Fields("size").Do(); err != nil {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -442,15 +483,16 @@ func (s *GDrive) Head(token string, filename string) (contentLength uint64, err
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Get retrieves a file from storage
|
||||||
func (s *GDrive) Get(token string, filename string) (reader io.ReadCloser, contentLength uint64, err error) {
|
func (s *GDrive) Get(token string, filename string) (reader io.ReadCloser, contentLength uint64, err error) {
|
||||||
var fileId string
|
var fileID string
|
||||||
fileId, err = s.findId(filename, token)
|
fileID, err = s.findID(filename, token)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
var fi *drive.File
|
var fi *drive.File
|
||||||
fi, err = s.service.Files.Get(fileId).Fields("size", "md5Checksum").Do()
|
fi, err = s.service.Files.Get(fileID).Fields("size", "md5Checksum").Do()
|
||||||
if !s.hasChecksum(fi) {
|
if !s.hasChecksum(fi) {
|
||||||
err = fmt.Errorf("Cannot find file %s/%s", token, filename)
|
err = fmt.Errorf("Cannot find file %s/%s", token, filename)
|
||||||
return
|
return
|
||||||
@@ -460,7 +502,7 @@ func (s *GDrive) Get(token string, filename string) (reader io.ReadCloser, conte
|
|||||||
|
|
||||||
ctx := context.Background()
|
ctx := context.Background()
|
||||||
var res *http.Response
|
var res *http.Response
|
||||||
res, err = s.service.Files.Get(fileId).Context(ctx).Download()
|
res, err = s.service.Files.Get(fileID).Context(ctx).Download()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
@@ -470,25 +512,27 @@ func (s *GDrive) Get(token string, filename string) (reader io.ReadCloser, conte
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Delete removes a file from storage
|
||||||
func (s *GDrive) Delete(token string, filename string) (err error) {
|
func (s *GDrive) Delete(token string, filename string) (err error) {
|
||||||
metadata, _ := s.findId(fmt.Sprintf("%s.metadata", filename), token)
|
metadata, _ := s.findID(fmt.Sprintf("%s.metadata", filename), token)
|
||||||
s.service.Files.Delete(metadata).Do()
|
s.service.Files.Delete(metadata).Do()
|
||||||
|
|
||||||
var fileId string
|
var fileID string
|
||||||
fileId, err = s.findId(filename, token)
|
fileID, err = s.findID(filename, token)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
err = s.service.Files.Delete(fileId).Do()
|
err = s.service.Files.Delete(fileID).Do()
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Purge cleans up the storage
|
||||||
func (s *GDrive) Purge(days time.Duration) (err error) {
|
func (s *GDrive) Purge(days time.Duration) (err error) {
|
||||||
nextPageToken := ""
|
nextPageToken := ""
|
||||||
|
|
||||||
expirationDate := time.Now().Add(-1 * days).Format(time.RFC3339)
|
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)
|
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)
|
l, err := s.list(nextPageToken, q)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
@@ -507,32 +551,39 @@ func (s *GDrive) Purge(days time.Duration) (err error) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
l, err = s.list(l.NextPageToken, q)
|
l, err = s.list(l.NextPageToken, q)
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// IsNotExist indicates if a file doesn't exist on storage
|
||||||
func (s *GDrive) IsNotExist(err error) bool {
|
func (s *GDrive) IsNotExist(err error) bool {
|
||||||
if err != nil {
|
if err == nil {
|
||||||
if e, ok := err.(*googleapi.Error); ok {
|
return false
|
||||||
return e.Code == http.StatusNotFound
|
}
|
||||||
}
|
|
||||||
|
if e, ok := err.(*googleapi.Error); ok {
|
||||||
|
return e.Code == http.StatusNotFound
|
||||||
}
|
}
|
||||||
|
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Put saves a file on storage
|
||||||
func (s *GDrive) Put(token string, filename string, reader io.Reader, contentType string, contentLength uint64) error {
|
func (s *GDrive) Put(token string, filename string, reader io.Reader, contentType string, contentLength uint64) error {
|
||||||
dirId, err := s.findId("", token)
|
dirID, err := s.findID("", token)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
if dirId == "" {
|
if dirID == "" {
|
||||||
dir := &drive.File{
|
dir := &drive.File{
|
||||||
Name: token,
|
Name: token,
|
||||||
Parents: []string{s.rootId},
|
Parents: []string{s.rootID},
|
||||||
MimeType: GDriveDirectoryMimeType,
|
MimeType: gdriveDirectoryMimeType,
|
||||||
}
|
}
|
||||||
|
|
||||||
di, err := s.service.Files.Create(dir).Fields("id").Do()
|
di, err := s.service.Files.Create(dir).Fields("id").Do()
|
||||||
@@ -540,13 +591,13 @@ func (s *GDrive) Put(token string, filename string, reader io.Reader, contentTyp
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
dirId = di.Id
|
dirID = di.Id
|
||||||
}
|
}
|
||||||
|
|
||||||
// Instantiate empty drive file
|
// Instantiate empty drive file
|
||||||
dst := &drive.File{
|
dst := &drive.File{
|
||||||
Name: filename,
|
Name: filename,
|
||||||
Parents: []string{dirId},
|
Parents: []string{dirID},
|
||||||
MimeType: contentType,
|
MimeType: contentType,
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -562,7 +613,7 @@ func (s *GDrive) Put(token string, filename string, reader io.Reader, contentTyp
|
|||||||
|
|
||||||
// Retrieve a token, saves the token, then returns the generated client.
|
// Retrieve a token, saves the token, then returns the generated client.
|
||||||
func getGDriveClient(config *oauth2.Config, localConfigPath string, logger *log.Logger) *http.Client {
|
func getGDriveClient(config *oauth2.Config, localConfigPath string, logger *log.Logger) *http.Client {
|
||||||
tokenFile := filepath.Join(localConfigPath, GDriveTokenJsonFile)
|
tokenFile := filepath.Join(localConfigPath, gdriveTokenJSONFile)
|
||||||
tok, err := gDriveTokenFromFile(tokenFile)
|
tok, err := gDriveTokenFromFile(tokenFile)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
tok = getGDriveTokenFromWeb(config, logger)
|
tok = getGDriveTokenFromWeb(config, logger)
|
||||||
@@ -614,6 +665,7 @@ func saveGDriveToken(path string, token *oauth2.Token, logger *log.Logger) {
|
|||||||
json.NewEncoder(f).Encode(token)
|
json.NewEncoder(f).Encode(token)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// StorjStorage is a storage backed by Storj
|
||||||
type StorjStorage struct {
|
type StorjStorage struct {
|
||||||
Storage
|
Storage
|
||||||
project *uplink.Project
|
project *uplink.Project
|
||||||
@@ -622,6 +674,7 @@ type StorjStorage struct {
|
|||||||
logger *log.Logger
|
logger *log.Logger
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// NewStorjStorage is the factory for StorjStorage
|
||||||
func NewStorjStorage(access, bucket string, purgeDays int, logger *log.Logger) (*StorjStorage, error) {
|
func NewStorjStorage(access, bucket string, purgeDays int, logger *log.Logger) (*StorjStorage, error) {
|
||||||
var instance StorjStorage
|
var instance StorjStorage
|
||||||
var err error
|
var err error
|
||||||
@@ -652,10 +705,12 @@ func NewStorjStorage(access, bucket string, purgeDays int, logger *log.Logger) (
|
|||||||
return &instance, nil
|
return &instance, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Type returns the storage type
|
||||||
func (s *StorjStorage) Type() string {
|
func (s *StorjStorage) Type() string {
|
||||||
return "storj"
|
return "storj"
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Head retrieves content length of a file from storage
|
||||||
func (s *StorjStorage) Head(token string, filename string) (contentLength uint64, err error) {
|
func (s *StorjStorage) Head(token string, filename string) (contentLength uint64, err error) {
|
||||||
key := storj.JoinPaths(token, filename)
|
key := storj.JoinPaths(token, filename)
|
||||||
|
|
||||||
@@ -671,6 +726,7 @@ func (s *StorjStorage) Head(token string, filename string) (contentLength uint64
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Get retrieves a file from storage
|
||||||
func (s *StorjStorage) Get(token string, filename string) (reader io.ReadCloser, contentLength uint64, err error) {
|
func (s *StorjStorage) Get(token string, filename string) (reader io.ReadCloser, contentLength uint64, err error) {
|
||||||
key := storj.JoinPaths(token, filename)
|
key := storj.JoinPaths(token, filename)
|
||||||
|
|
||||||
@@ -689,6 +745,7 @@ func (s *StorjStorage) Get(token string, filename string) (reader io.ReadCloser,
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Delete removes a file from storage
|
||||||
func (s *StorjStorage) Delete(token string, filename string) (err error) {
|
func (s *StorjStorage) Delete(token string, filename string) (err error) {
|
||||||
key := storj.JoinPaths(token, filename)
|
key := storj.JoinPaths(token, filename)
|
||||||
|
|
||||||
@@ -701,11 +758,13 @@ func (s *StorjStorage) Delete(token string, filename string) (err error) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Purge cleans up the storage
|
||||||
func (s *StorjStorage) Purge(days time.Duration) (err error) {
|
func (s *StorjStorage) Purge(days time.Duration) (err error) {
|
||||||
// NOOP expiration is set at upload time
|
// NOOP expiration is set at upload time
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Put saves a file on storage
|
||||||
func (s *StorjStorage) Put(token string, filename string, reader io.Reader, contentType string, contentLength uint64) (err error) {
|
func (s *StorjStorage) Put(token string, filename string, reader io.Reader, contentType string, contentLength uint64) (err error) {
|
||||||
key := storj.JoinPaths(token, filename)
|
key := storj.JoinPaths(token, filename)
|
||||||
|
|
||||||
@@ -713,7 +772,12 @@ func (s *StorjStorage) Put(token string, filename string, reader io.Reader, cont
|
|||||||
|
|
||||||
ctx := context.TODO()
|
ctx := context.TODO()
|
||||||
|
|
||||||
writer, err := s.project.UploadObject(ctx, s.bucket.Name, key, &uplink.UploadOptions{Expires: time.Now().Add(s.purgeDays)})
|
var uploadOptions *uplink.UploadOptions
|
||||||
|
if s.purgeDays.Hours() > 0 {
|
||||||
|
uploadOptions = &uplink.UploadOptions{Expires: time.Now().Add(s.purgeDays)}
|
||||||
|
}
|
||||||
|
|
||||||
|
writer, err := s.project.UploadObject(ctx, s.bucket.Name, key, uploadOptions)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
@@ -735,6 +799,7 @@ func (s *StorjStorage) Put(token string, filename string, reader io.Reader, cont
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// IsNotExist indicates if a file doesn't exist on storage
|
||||||
func (s *StorjStorage) IsNotExist(err error) bool {
|
func (s *StorjStorage) IsNotExist(err error) bool {
|
||||||
return errors.Is(err, uplink.ErrObjectNotFound)
|
return errors.Is(err, uplink.ErrObjectNotFound)
|
||||||
}
|
}
|
||||||
|
45
server/token.go
Normal file
45
server/token.go
Normal file
@@ -0,0 +1,45 @@
|
|||||||
|
/*
|
||||||
|
The MIT License (MIT)
|
||||||
|
|
||||||
|
Copyright (c) 2020- Andrea Spacca and Stefan Benten.
|
||||||
|
|
||||||
|
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
of this software and associated documentation files (the "Software"), to deal
|
||||||
|
in the Software without restriction, including without limitation the rights
|
||||||
|
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
copies of the Software, and to permit persons to whom the Software is
|
||||||
|
furnished to do so, subject to the following conditions:
|
||||||
|
|
||||||
|
The above copyright notice and this permission notice shall be included in
|
||||||
|
all copies or substantial portions of the Software.
|
||||||
|
|
||||||
|
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||||
|
THE SOFTWARE.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package server
|
||||||
|
|
||||||
|
import (
|
||||||
|
"math/rand"
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
// SYMBOLS characters used for short-urls
|
||||||
|
SYMBOLS = "0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ"
|
||||||
|
)
|
||||||
|
|
||||||
|
// generate a token
|
||||||
|
func token(length int) string {
|
||||||
|
result := ""
|
||||||
|
for i := 0; i < length; i++ {
|
||||||
|
x := rand.Intn(len(SYMBOLS) - 1)
|
||||||
|
result = string(SYMBOLS[x]) + result
|
||||||
|
}
|
||||||
|
|
||||||
|
return result
|
||||||
|
}
|
15
server/token_test.go
Normal file
15
server/token_test.go
Normal file
@@ -0,0 +1,15 @@
|
|||||||
|
package server
|
||||||
|
|
||||||
|
import "testing"
|
||||||
|
|
||||||
|
func BenchmarkTokenConcat(b *testing.B) {
|
||||||
|
for i := 0; i < b.N; i++ {
|
||||||
|
_ = token(5) + token(5)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func BenchmarkTokenLonger(b *testing.B) {
|
||||||
|
for i := 0; i < b.N; i++ {
|
||||||
|
_ = token(10)
|
||||||
|
}
|
||||||
|
}
|
@@ -2,6 +2,8 @@
|
|||||||
The MIT License (MIT)
|
The MIT License (MIT)
|
||||||
|
|
||||||
Copyright (c) 2014-2017 DutchCoders [https://github.com/dutchcoders/]
|
Copyright (c) 2014-2017 DutchCoders [https://github.com/dutchcoders/]
|
||||||
|
Copyright (c) 2018-2020 Andrea Spacca.
|
||||||
|
Copyright (c) 2020- Andrea Spacca and Stefan Benten.
|
||||||
|
|
||||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
of this software and associated documentation files (the "Software"), to deal
|
of this software and associated documentation files (the "Software"), to deal
|
||||||
@@ -25,6 +27,7 @@ THE SOFTWARE.
|
|||||||
package server
|
package server
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"fmt"
|
||||||
"math"
|
"math"
|
||||||
"net/http"
|
"net/http"
|
||||||
"net/mail"
|
"net/mail"
|
||||||
@@ -47,8 +50,7 @@ func getAwsSession(accessKey, secretKey, region, endpoint string, forcePathStyle
|
|||||||
}
|
}
|
||||||
|
|
||||||
func formatNumber(format string, s uint64) string {
|
func formatNumber(format string, s uint64) string {
|
||||||
|
return renderFloat(format, float64(s))
|
||||||
return RenderFloat(format, float64(s))
|
|
||||||
}
|
}
|
||||||
|
|
||||||
var renderFloatPrecisionMultipliers = [10]float64{
|
var renderFloatPrecisionMultipliers = [10]float64{
|
||||||
@@ -77,7 +79,7 @@ var renderFloatPrecisionRounders = [10]float64{
|
|||||||
0.0000000005,
|
0.0000000005,
|
||||||
}
|
}
|
||||||
|
|
||||||
func RenderFloat(format string, n float64) string {
|
func renderFloat(format string, n float64) string {
|
||||||
// Special cases:
|
// Special cases:
|
||||||
// NaN = "NaN"
|
// NaN = "NaN"
|
||||||
// +Inf = "+Infinity"
|
// +Inf = "+Infinity"
|
||||||
@@ -125,7 +127,7 @@ func RenderFloat(format string, n float64) string {
|
|||||||
// +0000
|
// +0000
|
||||||
if formatDirectiveIndices[0] == 0 {
|
if formatDirectiveIndices[0] == 0 {
|
||||||
if formatDirectiveChars[formatDirectiveIndices[0]] != '+' {
|
if formatDirectiveChars[formatDirectiveIndices[0]] != '+' {
|
||||||
panic("RenderFloat(): invalid positive sign directive")
|
panic("renderFloat(): invalid positive sign directive")
|
||||||
}
|
}
|
||||||
positiveStr = "+"
|
positiveStr = "+"
|
||||||
formatDirectiveIndices = formatDirectiveIndices[1:]
|
formatDirectiveIndices = formatDirectiveIndices[1:]
|
||||||
@@ -139,7 +141,7 @@ func RenderFloat(format string, n float64) string {
|
|||||||
// 000,000.00
|
// 000,000.00
|
||||||
if len(formatDirectiveIndices) == 2 {
|
if len(formatDirectiveIndices) == 2 {
|
||||||
if (formatDirectiveIndices[1] - formatDirectiveIndices[0]) != 4 {
|
if (formatDirectiveIndices[1] - formatDirectiveIndices[0]) != 4 {
|
||||||
panic("RenderFloat(): thousands separator directive must be followed by 3 digit-specifiers")
|
panic("renderFloat(): thousands separator directive must be followed by 3 digit-specifiers")
|
||||||
}
|
}
|
||||||
thousandStr = string(formatDirectiveChars[formatDirectiveIndices[0]])
|
thousandStr = string(formatDirectiveChars[formatDirectiveIndices[0]])
|
||||||
formatDirectiveIndices = formatDirectiveIndices[1:]
|
formatDirectiveIndices = formatDirectiveIndices[1:]
|
||||||
@@ -199,8 +201,8 @@ func RenderFloat(format string, n float64) string {
|
|||||||
return signStr + intStr + decimalStr + fracStr
|
return signStr + intStr + decimalStr + fracStr
|
||||||
}
|
}
|
||||||
|
|
||||||
func RenderInteger(format string, n int) string {
|
func renderInteger(format string, n int) string {
|
||||||
return RenderFloat(format, float64(n))
|
return renderFloat(format, float64(n))
|
||||||
}
|
}
|
||||||
|
|
||||||
// Request.RemoteAddress contains port, which we want to remove i.e.:
|
// Request.RemoteAddress contains port, which we want to remove i.e.:
|
||||||
@@ -253,3 +255,27 @@ func acceptsHTML(hdr http.Header) bool {
|
|||||||
|
|
||||||
return (false)
|
return (false)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func formatSize(size int64) string {
|
||||||
|
sizeFloat := float64(size)
|
||||||
|
base := math.Log(sizeFloat) / math.Log(1024)
|
||||||
|
|
||||||
|
sizeOn := math.Pow(1024, base-math.Floor(base))
|
||||||
|
|
||||||
|
var round float64
|
||||||
|
pow := math.Pow(10, float64(2))
|
||||||
|
digit := pow * sizeOn
|
||||||
|
round = math.Floor(digit)
|
||||||
|
|
||||||
|
newVal := round / pow
|
||||||
|
|
||||||
|
var suffixes [5]string
|
||||||
|
suffixes[0] = "B"
|
||||||
|
suffixes[1] = "KB"
|
||||||
|
suffixes[2] = "MB"
|
||||||
|
suffixes[3] = "GB"
|
||||||
|
suffixes[4] = "TB"
|
||||||
|
|
||||||
|
getSuffix := suffixes[int(math.Floor(base))]
|
||||||
|
return fmt.Sprintf("%s %s", strconv.FormatFloat(newVal, 'f', -1, 64), getSuffix)
|
||||||
|
}
|
||||||
|
@@ -29,7 +29,6 @@ import (
|
|||||||
"io"
|
"io"
|
||||||
"net/http"
|
"net/http"
|
||||||
|
|
||||||
_ "github.com/PuerkitoBio/ghost/handlers"
|
|
||||||
"github.com/gorilla/mux"
|
"github.com/gorilla/mux"
|
||||||
|
|
||||||
virustotal "github.com/dutchcoders/go-virustotal"
|
virustotal "github.com/dutchcoders/go-virustotal"
|
||||||
|
Reference in New Issue
Block a user