Compare commits

..

18 Commits

Author SHA1 Message Date
Andrea Spacca
2d02ab465c increase default random-token-length to 10 2023-03-11 02:22:17 +09:00
Andrea Spacca
0635c3c132 Merge branch 'main' into gpg-encryption-support 2023-03-11 02:10:29 +09:00
Andrea Spacca
5cf967db4c linting fix 2023-02-11 12:50:40 +09:00
Andrea Spacca
cc7178aa19 Merge branch 'main' into gpg-encryption-support 2023-02-11 08:07:40 +09:00
Andrea Spacca
214dc7f100 Merge branch 'main' into gpg-encryption-support 2022-11-21 12:02:39 +09:00
Andrea Spacca
bcf0b17897 linting 2022-09-19 10:16:03 +09:00
Andrea Spacca
6bb554820a Update README.md
do not reference server public hosting version in encrypt/decrypt headers example
2022-09-14 02:54:50 +02:00
Andrea Spacca
7785efcb46 streaming in encrypt 2022-09-13 16:38:23 +09:00
Andrea Spacca
95ab94d875 fix typo 2022-09-12 13:05:20 +09:00
Andrea Spacca
31c9b9e2cb reduce I/O on decrypt 2022-09-12 13:03:36 +09:00
Andrea Spacca
690820036e remove exception in linting 2022-09-12 12:09:28 +09:00
Andrea Spacca
3c28c61aa6 warning 2022-09-12 12:07:46 +09:00
Andrea Spacca
c924289f55 refactor using protonmail opengpg 2022-09-12 12:02:02 +09:00
Andrea Spacca
038aba5ae9 Merge branch 'main' into gpg-encryption-support 2022-09-12 11:38:44 +09:00
Andrea Spacca
a7b600c562 refinement 2022-03-02 22:49:20 +09:00
Andrea Spacca
715f2b4cd9 Merge branch 'main' into gpg-encryption-support 2022-03-02 22:34:40 +09:00
Andrea Spacca
fbf9a4facc gpg encryption support 2018-10-06 19:24:40 +02:00
Andrea Spacca
0c844c1f11 gpg encryption support 2018-10-06 19:04:46 +02:00
20 changed files with 472 additions and 944 deletions

View File

@@ -105,9 +105,9 @@ jobs:
echo "ASSET_NAME=$_NAME" >> $GITHUB_ENV
- name: Set up Go
uses: actions/setup-go@v5
uses: actions/setup-go@v2
with:
go-version: ^1.22
go-version: ^1.18
- name: Get project dependencies
run: go mod download

View File

@@ -13,9 +13,9 @@ jobs:
fail-fast: false
matrix:
go_version:
- '1.22'
- '1.23'
- '1.24'
- '1.18'
- '1.19'
- '1.20'
- tip
name: Test with ${{ matrix.go_version }}
steps:
@@ -29,20 +29,16 @@ jobs:
- name: Install Go ${{ matrix.go_version }}
if: ${{ matrix.go_version == 'tip' }}
run: |
go install golang.org/dl/gotip@latest
`go env GOPATH`/bin/gotip download
- name: Vet and test no tip
if: ${{ matrix.go_version != 'tip' }}
curl -sL https://storage.googleapis.com/go-build-snap/go/linux-amd64/$(git ls-remote https://github.com/golang/go.git HEAD | awk '{print $1;}').tar.gz -o gotip.tar.gz
ls -lah gotip.tar.gz
mkdir -p ~/sdk/gotip
tar -C ~/sdk/gotip -xzf gotip.tar.gz
echo "PATH=$HOME/go/bin:$HOME/sdk/gotip/bin/:$PATH" >> $GITHUB_ENV
- name: Vet and test
run: |
go version
go vet ./...
go test ./...
- name: Vet and test gotip
if: ${{ matrix.go_version == 'tip' }}
run: |
`go env GOPATH`/bin/gotip version
`go env GOPATH`/bin/gotip vet ./...
`go env GOPATH`/bin/gotip test ./...
golangci:
name: Linting
runs-on: ubuntu-latest
@@ -50,7 +46,7 @@ jobs:
- uses: actions/checkout@v2
- uses: actions/setup-go@master
with:
go-version: '1.24'
go-version: '1.20'
check-latest: true
- name: golangci-lint
uses: golangci/golangci-lint-action@v2

View File

@@ -1,17 +1,15 @@
# Default to Go 1.24
ARG GO_VERSION=1.24
# Default to Go 1.17
ARG GO_VERSION=1.17
FROM golang:${GO_VERSION}-alpine as build
# Necessary to run 'go get' and to compile the linked binary
RUN apk add git musl-dev mailcap
RUN apk add git musl-dev
ADD . /go/src/github.com/dutchcoders/transfer.sh
WORKDIR /go/src/github.com/dutchcoders/transfer.sh
COPY go.mod go.sum ./
RUN go mod download
COPY . .
ENV GO111MODULE=on
# build & install server
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
@@ -31,7 +29,6 @@ FROM scratch AS final
LABEL maintainer="Andrea Spacca <andrea.spacca@gmail.com>"
ARG RUNAS
COPY --from=build /etc/mime.types /etc/mime.types
COPY --from=build /tmp/empty /tmp
COPY --from=build /tmp/useradd/* /etc/
COPY --from=build --chown=${RUNAS} /go/bin/transfersh /go/bin/transfersh

334
README.md
View File

@@ -4,150 +4,76 @@ Easy and fast file sharing from the command-line. This code contains the server
Transfer.sh currently supports the s3 (Amazon S3), gdrive (Google Drive), storj (Storj) providers, and local file system (local).
<br />
---
<br />
## Disclaimer
@stefanbenten happens to be a maintainer of this repository _and_ the person who host a well known public installation of the software in the repo.
The two are anyway unrelated, and the repo is not the place to direct requests and issues for any of the pubblic installation.
No third-party public installation of the software in the repo will be advertised or mentioned in the repo itself, for security reasons.
The official position of me, @aspacca, as maintainer of the repo, is that if you want to use the software you should host your own installation.
<br />
---
<br />
The service at transfersh.com is of unknown origin and reported as cloud malware.
## Usage
This section outlines how to use transfer.sh
<br />
### Upload
### Upload:
```bash
$ curl -v --upload-file ./hello.txt https://transfer.sh/hello.txt
```
<br />
### Encrypt & Upload
### Encrypt & Upload:
```bash
$ gpg --armor --symmetric --output - /tmp/hello.txt | curl --upload-file - https://transfer.sh/test.txt
$ cat /tmp/hello.txt|gpg -ac -o-|curl -X PUT --upload-file "-" https://transfer.sh/test.txt
````
### Download & Decrypt:
```bash
$ curl https://transfer.sh/1lDau/test.txt|gpg -o- > /tmp/hello.txt
```
<br />
### Download & Decrypt
```bash
$ curl https://transfer.sh/1lDau/test.txt | gpg --decrypt --output /tmp/hello.txt
```
<br />
### Upload to Virustotal
### Upload to Virustotal:
```bash
$ curl -X PUT --upload-file nhgbhhj https://transfer.sh/test.txt/virustotal
```
<br />
### Deleting
```bash
$ curl -X DELETE <X-Url-Delete Response Header URL>
```
<br />
---
<br />
## Request Headers
This section explains how to handle request headers with curl:
<br />
### Max-Downloads
```bash
$ curl --upload-file ./hello.txt https://transfer.sh/hello.txt -H "Max-Downloads: 1" # Limit the number of downloads
```
<br />
### Max-Days
```bash
$ curl --upload-file ./hello.txt https://transfer.sh/hello.txt -H "Max-Days: 1" # Set the number of days before deletion
```
<br />
### X-Encrypt-Password
#### Beware, use this feature only on your self-hosted server: trusting a third-party service for server side encryption is at your own risk
```bash
$ curl --upload-file ./hello.txt https://your-transfersh-instance.tld/hello.txt -H "X-Encrypt-Password: test" # Encrypt the content server side with AES256 using "test" as password
$ curl --upload-file ./hello.txt https://your-transfersh-instance.tld/hello.txt -H "X-Encrypt-Password: test" # Encrypt the content sever side with AES265 using "test" as password
```
<br />
### X-Decrypt-Password
#### Beware, use this feature only on your self-hosted server: trusting a third-party service for server side encryption is at your own risk
```bash
$ curl https://your-transfersh-instance.tld/BAYh0/hello.txt -H "X-Decrypt-Password: test" # Decrypt the content server side with AES256 using "test" as password
$ curl https://your-transfersh-instance.tld/BAYh0/hello.txt -H "X-Decrypt-Password: test" # Decrypt the content sever side with AES265 using "test" as password
```
<br />
---
<br />
## Response Headers
This section explains how to handle response headers:
<br />
### X-Url-Delete
The URL used to request the deletion of a file and returned as a response header:
The URL used to request the deletion of a file and returned as a response header.
```bash
curl -sD - --upload-file ./hello.txt https://transfer.sh/hello.txt | grep -i -E 'transfer\.sh|x-url-delete'
x-url-delete: https://transfer.sh/hello.txt/BAYh0/hello.txt/PDw0NHPcqU
https://transfer.sh/hello.txt/BAYh0/hello.txt
```
<br />
---
<br />
## Examples
See good usage examples on [examples.md](examples.md)
<br />
## Link aliases
Create direct download link:
@@ -158,72 +84,57 @@ Inline file:
https://transfer.sh/1lDau/test.txt --> https://transfer.sh/inline/1lDau/test.txt
<br />
---
<br />
## Usage
Parameter | Description | Value | Env
--- |-----------------------------------------------------------------------------------------------|-------------------------------|-------------------------------|
listener | port to use for http (:80) | | LISTENER |
profile-listener | port to use for profiler (:6060) | | PROFILE_LISTENER |
force-https | redirect to https | false | FORCE_HTTPS |
tls-listener | port to use for https (:443) | | TLS_LISTENER |
tls-listener-only | flag to enable tls listener only | | TLS_LISTENER_ONLY |
tls-cert-file | path to tls certificate | | TLS_CERT_FILE |
tls-private-key | path to tls private key | | TLS_PRIVATE_KEY |
http-auth-user | user for basic http auth on upload | | HTTP_AUTH_USER |
http-auth-pass | pass for basic http auth on upload | | HTTP_AUTH_PASS |
http-auth-htpasswd | htpasswd file path for basic http auth on upload | | HTTP_AUTH_HTPASSWD |
http-auth-ip-whitelist | comma separated list of allowed ips to upload without auth challenge | | HTTP_AUTH_IP_WHITELIST |
virustotal-key | VirusTotal API key | | VIRUSTOTAL_KEY |
ip-whitelist | comma separated list of ips allowed to connect to the service | | IP_WHITELIST |
ip-blacklist | comma separated list of ips not allowed to connect to the service | | IP_BLACKLIST |
temp-path | path to temp folder | system temp | TEMP_PATH |
web-path | path to static web files (for development or custom front end) | | WEB_PATH |
proxy-path | path prefix when service is run behind a proxy (a `/` prefix will be trimmed) | | PROXY_PATH |
proxy-port | port of the proxy when the service is run behind a proxy | | PROXY_PORT |
email-contact | email contact for the front end | | EMAIL_CONTACT |
ga-key | google analytics key for the front end | | GA_KEY |
provider | which storage provider to use | (s3, storj, gdrive or local) | |
uservoice-key | user voice key for the front end | | USERVOICE_KEY |
aws-access-key | aws access key | | AWS_ACCESS_KEY |
aws-secret-key | aws access key | | AWS_SECRET_KEY |
bucket | aws bucket | | BUCKET |
s3-endpoint | Custom S3 endpoint. | | S3_ENDPOINT |
s3-region | region of the s3 bucket | eu-west-1 | S3_REGION |
s3-no-multipart | disables s3 multipart upload | false | S3_NO_MULTIPART |
s3-path-style | Forces path style URLs, required for Minio. | false | S3_PATH_STYLE |
storj-access | Access for the project | | STORJ_ACCESS |
storj-bucket | Bucket to use within the project | | STORJ_BUCKET |
basedir | path storage for local/gdrive provider | | BASEDIR |
gdrive-client-json-filepath | path to oauth client json config for gdrive provider | | GDRIVE_CLIENT_JSON_FILEPATH |
gdrive-local-config-path | path to store local transfer.sh config cache for gdrive provider | | GDRIVE_LOCAL_CONFIG_PATH |
gdrive-chunk-size | chunk size for gdrive upload in megabytes, must be lower than available memory (8 MB) | | GDRIVE_CHUNK_SIZE |
lets-encrypt-hosts | hosts to use for lets encrypt certificates (comma separated) | | HOSTS |
log | path to log file | | LOG |
cors-domains | comma separated list of domains for CORS, setting it enable CORS | | CORS_DOMAINS |
clamav-host | host for clamav feature | | CLAMAV_HOST |
perform-clamav-prescan | prescan every upload using clamav (clamav-host must be local clamd unix socket) | | PERFORM_CLAMAV_PRESCAN |
rate-limit | request per minute | | RATE_LIMIT |
max-upload-size | max upload size in kilobytes | | MAX_UPLOAD_SIZE |
purge-days | number of days after the uploads are purged automatically | | PURGE_DAYS |
purge-interval | interval (hours) to run automatic purge for (excluding S3 and Storj) | | PURGE_INTERVAL |
random-token-length | length of random token for upload path (double the size for delete path) | 6 | RANDOM_TOKEN_LENGTH |
Parameter | Description | Value | Env
--- | --- | --- | ---
listener | port to use for http (:80) | | LISTENER |
profile-listener | port to use for profiler (:6060) | | PROFILE_LISTENER |
force-https | redirect to https | false | FORCE_HTTPS
tls-listener | port to use for https (:443) | | TLS_LISTENER |
tls-listener-only | flag to enable tls listener only | | TLS_LISTENER_ONLY |
tls-cert-file | path to tls certificate | | TLS_CERT_FILE |
tls-private-key | path to tls private key | | TLS_PRIVATE_KEY |
http-auth-user | user for basic http auth on upload | | HTTP_AUTH_USER |
http-auth-pass | pass for basic http auth on upload | | HTTP_AUTH_PASS |
ip-whitelist | comma separated list of ips allowed to connect to the service | | IP_WHITELIST |
ip-blacklist | comma separated list of ips not allowed to connect to the service | | IP_BLACKLIST |
temp-path | path to temp folder | system temp | TEMP_PATH |
web-path | path to static web files (for development or custom front end) | | WEB_PATH |
proxy-path | path prefix when service is run behind a proxy | | PROXY_PATH |
proxy-port | port of the proxy when the service is run behind a proxy | | PROXY_PORT |
email-contact | email contact for the front end | | EMAIL_CONTACT |
ga-key | google analytics key for the front end | | GA_KEY |
provider | which storage provider to use | (s3, storj, gdrive or local) |
uservoice-key | user voice key for the front end | | USERVOICE_KEY |
aws-access-key | aws access key | | AWS_ACCESS_KEY |
aws-secret-key | aws access key | | AWS_SECRET_KEY |
bucket | aws bucket | | BUCKET |
s3-endpoint | Custom S3 endpoint. | | S3_ENDPOINT |
s3-region | region of the s3 bucket | eu-west-1 | S3_REGION |
s3-no-multipart | disables s3 multipart upload | false | S3_NO_MULTIPART |
s3-path-style | Forces path style URLs, required for Minio. | false | S3_PATH_STYLE |
storj-access | Access for the project | | STORJ_ACCESS |
storj-bucket | Bucket to use within the project | | STORJ_BUCKET |
basedir | path storage for local/gdrive provider | | BASEDIR |
gdrive-client-json-filepath | path to oauth client json config for gdrive provider | | GDRIVE_CLIENT_JSON_FILEPATH |
gdrive-local-config-path | path to store local transfer.sh config cache for gdrive provider| | GDRIVE_LOCAL_CONFIG_PATH |
gdrive-chunk-size | chunk size for gdrive upload in megabytes, must be lower than available memory (8 MB) | | GDRIVE_CHUNK_SIZE |
lets-encrypt-hosts | hosts to use for lets encrypt certificates (comma seperated) | | HOSTS |
log | path to log file| | LOG |
cors-domains | comma separated list of domains for CORS, setting it enable CORS | | CORS_DOMAINS |
clamav-host | host for clamav feature | | CLAMAV_HOST |
perform-clamav-prescan | prescan every upload through clamav feature (clamav-host must be a local clamd unix socket) | | PERFORM_CLAMAV_PRESCAN |
rate-limit | request per minute | | RATE_LIMIT |
max-upload-size | max upload size in kilobytes | | MAX_UPLOAD_SIZE |
purge-days | number of days after the uploads are purged automatically | | PURGE_DAYS |
purge-interval | interval in hours to run the automatic purge for (not applicable to S3 and Storj) | | PURGE_INTERVAL |
random-token-length | length of the random token for the upload path (double the size for delete path) | 6 | RANDOM_TOKEN_LENGTH |
If you want to use TLS using lets encrypt certificates, set lets-encrypt-hosts to your domain, set tls-listener to :443 and enable force-https.
If you want to use TLS using your own certificates, set tls-listener to :443, force-https, tls-cert-file and tls-private-key.
<br />
---
<br />
## Development
Switched to GO111MODULE
@@ -232,12 +143,6 @@ Switched to GO111MODULE
go run main.go --provider=local --listener :8080 --temp-path=/tmp/ --basedir=/tmp/
```
<br />
---
<br />
## Build
```bash
@@ -246,69 +151,23 @@ $ cd transfer.sh
$ go build -o transfersh main.go
```
<br />
---
<br />
## Docker
For easy deployment, we've created an official Docker container. There are two variants, differing only by which user runs the process.
The default one will run as `root`:
> [!WARNING]
> It is discouraged to use `latest` tag for WatchTower or similar tools. The `latest` tag can reference unreleased developer, test builds, and patch releases for older versions. Use an actual version tag until transfer.sh supports major or minor version tags.
```bash
docker run --publish 8080:8080 dutchcoders/transfer.sh:latest --provider local --basedir /tmp/
```
<br />
### No root
The `-noroot` tags indicate image builds that run with least priviledge to reduce the attack surface might an application get compromised.
> [!NOTE]
> Using `-noroot` is **recommended**
<br />
The one tagged with the suffix `-noroot` will use `5000` as both UID and GID:
```bash
docker run --publish 8080:8080 dutchcoders/transfer.sh:latest-noroot --provider local --basedir /tmp/
```
<br />
> [!NOTE]
> Development history details at:
> - https://github.com/dutchcoders/transfer.sh/pull/418
<br />
### Tags
Name | Usage
--|--
latest| Latest CI build, can be nightly, at commit, at tag, etc.
latest-noroot| Latest CI build, can be nightly, at commit, at tag, etc. using [no root]
nightly| Scheduled CI build every midnight UTC
nightly-noroot| Scheduled CI build every midnight UTC using [no root]
edge| Latest CI build after every commit on `main`
edge-noroot| Latest CI build after every commit on `main` using [no root]
v`x.y.z`| CI build after tagging a release
v`x.y.z`-noroot| CI build after tagging a release using [no root]
<br />
### Building the Container
You can also build the container yourself. This allows you to choose which UID/GID will be used, e.g. when using NFS mounts:
```bash
# Build arguments:
# * RUNAS: If empty, the container will run as root.
@@ -319,35 +178,21 @@ You can also build the container yourself. This allows you to choose which UID/G
docker build -t transfer.sh-noroot --build-arg RUNAS=doesntmatter --build-arg PUID=1337 --build-arg PGID=1338 .
```
<br />
---
<br />
## S3 Usage
For the usage with a AWS S3 Bucket, you just need to specify the following options:
- provider `--provider s3`
- aws-access-key _(either via flag or environment variable `AWS_ACCESS_KEY`)_
- aws-secret-key _(either via flag or environment variable `AWS_SECRET_KEY`)_
- bucket _(either via flag or environment variable `BUCKET`)_
- s3-region _(either via flag or environment variable `S3_REGION`)_
- provider
- aws-access-key
- aws-secret-key
- bucket
- s3-region
If you specify the s3-region, you don't need to set the endpoint URL since the correct endpoint will used automatically.
<br />
### Custom S3 providers
To use a custom non-AWS S3 provider, you need to specify the endpoint as defined from your cloud provider.
<br />
---
<br />
## Storj Network Provider
To use the Storj Network as a storage provider you need to specify the following flags:
@@ -355,8 +200,6 @@ To use the Storj Network as a storage provider you need to specify the following
- storj-access _(either via flag or environment variable STORJ_ACCESS)_
- storj-bucket _(either via flag or environment variable STORJ_BUCKET)_
<br />
### Creating Bucket and Scope
You need to create an access grant (or copy it from the uplink configuration) and a bucket in preparation.
@@ -371,19 +214,12 @@ Afterwards, you can copy the access grant and then start the startup of the tran
It is recommended to provide both the access grant and the bucket name as ENV Variables for enhanced security.
Example:
```
export STORJ_BUCKET=<BUCKET NAME>
export STORJ_ACCESS=<ACCESS GRANT>
transfer.sh --provider storj
```
<br />
---
<br />
## Google Drive Usage
For the usage with Google drive, you need to specify the following options:
@@ -392,40 +228,27 @@ For the usage with Google drive, you need to specify the following options:
- gdrive-local-config-path
- basedir
<br />
### Creating Gdrive Client Json
You need to create an OAuth Client id from console.cloud.google.com, download the file, and place it into a safe directory.
<br />
### Usage example
```go run main.go --provider gdrive --basedir /tmp/ --gdrive-client-json-filepath /[credential_dir] --gdrive-local-config-path [directory_to_save_config] ```
<br />
---
<br />
## Shell functions
### Bash, ash and zsh (multiple files uploaded as zip archive)
### Bash and zsh (multiple files uploaded as zip archive)
##### Add this to .bashrc or .zshrc or its equivalent
```bash
transfer() (if [ $# -eq 0 ]; then printf "No arguments specified.\nUsage:\n transfer <file|directory>\n ... | transfer <file_name>\n">&2; return 1; fi; file_name=$(basename "$1"); if [ -t 0 ]; then file="$1"; if [ ! -e "$file" ]; then echo "$file: No such file or directory">&2; return 1; fi; if [ -d "$file" ]; then cd "$file" || return 1; file_name="$file_name.zip"; set -- zip -r -q - .; else set -- cat "$file"; fi; else set -- cat; fi; url=$("$@" | curl --silent --show-error --progress-bar --upload-file "-" "https://transfer.sh/$file_name"); echo "$url"; )
transfer(){ if [ $# -eq 0 ];then echo "No arguments specified.\nUsage:\n transfer <file|directory>\n ... | transfer <file_name>">&2;return 1;fi;if tty -s;then file="$1";file_name=$(basename "$file");if [ ! -e "$file" ];then echo "$file: No such file or directory">&2;return 1;fi;if [ -d "$file" ];then file_name="$file_name.zip" ,;(cd "$file"&&zip -r -q - .)|curl --progress-bar --upload-file "-" "https://transfer.sh/$file_name"|tee /dev/null,;else cat "$file"|curl --progress-bar --upload-file "-" "https://transfer.sh/$file_name"|tee /dev/null;fi;else file_name=$1;curl --progress-bar --upload-file "-" "https://transfer.sh/$file_name"|tee /dev/null;fi;}
```
<br />
#### Now you can use transfer function
```
$ transfer hello.txt
```
<br />
### Bash and zsh (with delete url, delete token output and prompt before uploading)
##### Add this to .bashrc or .zshrc or its equivalent
@@ -577,22 +400,10 @@ tauN5dE3fWJe
https://transfer.sh/MYkuqn/image.img
```
<br />
---
<br />
## Contributions
Contributions are welcome.
<br />
---
<br />
## Creators
**Remco Verhoef**
@@ -601,22 +412,11 @@ Contributions are welcome.
**Uvis Grinfelds**
<br />
## Maintainer
---
**Andrea Spacca**
<br />
## Maintainers
- **Andrea Spacca**
- **Stefan Benten**
<br />
---
<br />
**Stefan Benten**
## Copyright and License

View File

@@ -1,7 +1,6 @@
package cmd
import (
"errors"
"fmt"
"log"
"os"
@@ -11,7 +10,7 @@ import (
"github.com/dutchcoders/transfer.sh/server"
"github.com/fatih/color"
"github.com/urfave/cli/v2"
"github.com/urfave/cli"
"google.golang.org/api/googleapi"
)
@@ -37,275 +36,263 @@ VERSION:
`{{ "\n"}}`
var globalFlags = []cli.Flag{
&cli.StringFlag{
Name: "listener",
Usage: "127.0.0.1:8080",
Value: "127.0.0.1:8080",
EnvVars: []string{"LISTENER"},
cli.StringFlag{
Name: "listener",
Usage: "127.0.0.1:8080",
Value: "127.0.0.1:8080",
EnvVar: "LISTENER",
},
// redirect to https?
// hostnames
&cli.StringFlag{
Name: "profile-listener",
Usage: "127.0.0.1:6060",
Value: "",
EnvVars: []string{"PROFILE_LISTENER"},
cli.StringFlag{
Name: "profile-listener",
Usage: "127.0.0.1:6060",
Value: "",
EnvVar: "PROFILE_LISTENER",
},
&cli.BoolFlag{
Name: "force-https",
Usage: "",
EnvVars: []string{"FORCE_HTTPS"},
cli.BoolFlag{
Name: "force-https",
Usage: "",
EnvVar: "FORCE_HTTPS",
},
&cli.StringFlag{
Name: "tls-listener",
Usage: "127.0.0.1:8443",
Value: "",
EnvVars: []string{"TLS_LISTENER"},
cli.StringFlag{
Name: "tls-listener",
Usage: "127.0.0.1:8443",
Value: "",
EnvVar: "TLS_LISTENER",
},
&cli.BoolFlag{
Name: "tls-listener-only",
Usage: "",
EnvVars: []string{"TLS_LISTENER_ONLY"},
cli.BoolFlag{
Name: "tls-listener-only",
Usage: "",
EnvVar: "TLS_LISTENER_ONLY",
},
&cli.StringFlag{
Name: "tls-cert-file",
Value: "",
EnvVars: []string{"TLS_CERT_FILE"},
cli.StringFlag{
Name: "tls-cert-file",
Value: "",
EnvVar: "TLS_CERT_FILE",
},
&cli.StringFlag{
Name: "tls-private-key",
Value: "",
EnvVars: []string{"TLS_PRIVATE_KEY"},
cli.StringFlag{
Name: "tls-private-key",
Value: "",
EnvVar: "TLS_PRIVATE_KEY",
},
&cli.StringFlag{
Name: "temp-path",
Usage: "path to temp files",
Value: os.TempDir(),
EnvVars: []string{"TEMP_PATH"},
cli.StringFlag{
Name: "temp-path",
Usage: "path to temp files",
Value: os.TempDir(),
EnvVar: "TEMP_PATH",
},
&cli.StringFlag{
Name: "web-path",
Usage: "path to static web files",
Value: "",
EnvVars: []string{"WEB_PATH"},
cli.StringFlag{
Name: "web-path",
Usage: "path to static web files",
Value: "",
EnvVar: "WEB_PATH",
},
&cli.StringFlag{
Name: "proxy-path",
Usage: "path prefix when service is run behind a proxy",
Value: "",
EnvVars: []string{"PROXY_PATH"},
cli.StringFlag{
Name: "proxy-path",
Usage: "path prefix when service is run behind a proxy",
Value: "",
EnvVar: "PROXY_PATH",
},
&cli.StringFlag{
Name: "proxy-port",
Usage: "port of the proxy when the service is run behind a proxy",
Value: "",
EnvVars: []string{"PROXY_PORT"},
cli.StringFlag{
Name: "proxy-port",
Usage: "port of the proxy when the service is run behind a proxy",
Value: "",
EnvVar: "PROXY_PORT",
},
&cli.StringFlag{
Name: "email-contact",
Usage: "email address to link in Contact Us (front end)",
Value: "",
EnvVars: []string{"EMAIL_CONTACT"},
cli.StringFlag{
Name: "email-contact",
Usage: "email address to link in Contact Us (front end)",
Value: "",
EnvVar: "EMAIL_CONTACT",
},
&cli.StringFlag{
Name: "ga-key",
Usage: "key for google analytics (front end)",
Value: "",
EnvVars: []string{"GA_KEY"},
cli.StringFlag{
Name: "ga-key",
Usage: "key for google analytics (front end)",
Value: "",
EnvVar: "GA_KEY",
},
&cli.StringFlag{
Name: "uservoice-key",
Usage: "key for user voice (front end)",
Value: "",
EnvVars: []string{"USERVOICE_KEY"},
cli.StringFlag{
Name: "uservoice-key",
Usage: "key for user voice (front end)",
Value: "",
EnvVar: "USERVOICE_KEY",
},
&cli.StringFlag{
Name: "provider",
Usage: "s3|gdrive|local",
Value: "",
EnvVars: []string{"PROVIDER"},
cli.StringFlag{
Name: "provider",
Usage: "s3|gdrive|local",
Value: "",
EnvVar: "PROVIDER",
},
&cli.StringFlag{
Name: "s3-endpoint",
Usage: "",
Value: "",
EnvVars: []string{"S3_ENDPOINT"},
cli.StringFlag{
Name: "s3-endpoint",
Usage: "",
Value: "",
EnvVar: "S3_ENDPOINT",
},
&cli.StringFlag{
Name: "s3-region",
Usage: "",
Value: "eu-west-1",
EnvVars: []string{"S3_REGION"},
cli.StringFlag{
Name: "s3-region",
Usage: "",
Value: "eu-west-1",
EnvVar: "S3_REGION",
},
&cli.StringFlag{
Name: "aws-access-key",
Usage: "",
Value: "",
EnvVars: []string{"AWS_ACCESS_KEY"},
cli.StringFlag{
Name: "aws-access-key",
Usage: "",
Value: "",
EnvVar: "AWS_ACCESS_KEY",
},
&cli.StringFlag{
Name: "aws-secret-key",
Usage: "",
Value: "",
EnvVars: []string{"AWS_SECRET_KEY"},
cli.StringFlag{
Name: "aws-secret-key",
Usage: "",
Value: "",
EnvVar: "AWS_SECRET_KEY",
},
&cli.StringFlag{
Name: "bucket",
Usage: "",
Value: "",
EnvVars: []string{"BUCKET"},
cli.StringFlag{
Name: "bucket",
Usage: "",
Value: "",
EnvVar: "BUCKET",
},
&cli.BoolFlag{
Name: "s3-no-multipart",
Usage: "Disables S3 Multipart Puts",
EnvVars: []string{"S3_NO_MULTIPART"},
cli.BoolFlag{
Name: "s3-no-multipart",
Usage: "Disables S3 Multipart Puts",
EnvVar: "S3_NO_MULTIPART",
},
&cli.BoolFlag{
Name: "s3-path-style",
Usage: "Forces path style URLs, required for Minio.",
EnvVars: []string{"S3_PATH_STYLE"},
cli.BoolFlag{
Name: "s3-path-style",
Usage: "Forces path style URLs, required for Minio.",
EnvVar: "S3_PATH_STYLE",
},
&cli.StringFlag{
Name: "gdrive-client-json-filepath",
Usage: "",
Value: "",
EnvVars: []string{"GDRIVE_CLIENT_JSON_FILEPATH"},
cli.StringFlag{
Name: "gdrive-client-json-filepath",
Usage: "",
Value: "",
EnvVar: "GDRIVE_CLIENT_JSON_FILEPATH",
},
&cli.StringFlag{
Name: "gdrive-local-config-path",
Usage: "",
Value: "",
EnvVars: []string{"GDRIVE_LOCAL_CONFIG_PATH"},
cli.StringFlag{
Name: "gdrive-local-config-path",
Usage: "",
Value: "",
EnvVar: "GDRIVE_LOCAL_CONFIG_PATH",
},
&cli.IntFlag{
Name: "gdrive-chunk-size",
Usage: "",
Value: googleapi.DefaultUploadChunkSize / 1024 / 1024,
EnvVars: []string{"GDRIVE_CHUNK_SIZE"},
cli.IntFlag{
Name: "gdrive-chunk-size",
Usage: "",
Value: googleapi.DefaultUploadChunkSize / 1024 / 1024,
EnvVar: "GDRIVE_CHUNK_SIZE",
},
&cli.StringFlag{
Name: "storj-access",
Usage: "Access for the project",
Value: "",
EnvVars: []string{"STORJ_ACCESS"},
cli.StringFlag{
Name: "storj-access",
Usage: "Access for the project",
Value: "",
EnvVar: "STORJ_ACCESS",
},
&cli.StringFlag{
Name: "storj-bucket",
Usage: "Bucket to use within the project",
Value: "",
EnvVars: []string{"STORJ_BUCKET"},
cli.StringFlag{
Name: "storj-bucket",
Usage: "Bucket to use within the project",
Value: "",
EnvVar: "STORJ_BUCKET",
},
&cli.IntFlag{
Name: "rate-limit",
Usage: "requests per minute",
Value: 0,
EnvVars: []string{"RATE_LIMIT"},
cli.IntFlag{
Name: "rate-limit",
Usage: "requests per minute",
Value: 0,
EnvVar: "RATE_LIMIT",
},
&cli.IntFlag{
Name: "purge-days",
Usage: "number of days after uploads are purged automatically",
Value: 0,
EnvVars: []string{"PURGE_DAYS"},
cli.IntFlag{
Name: "purge-days",
Usage: "number of days after uploads are purged automatically",
Value: 0,
EnvVar: "PURGE_DAYS",
},
&cli.IntFlag{
Name: "purge-interval",
Usage: "interval in hours to run the automatic purge for",
Value: 0,
EnvVars: []string{"PURGE_INTERVAL"},
cli.IntFlag{
Name: "purge-interval",
Usage: "interval in hours to run the automatic purge for",
Value: 0,
EnvVar: "PURGE_INTERVAL",
},
&cli.Int64Flag{
Name: "max-upload-size",
Usage: "max limit for upload, in kilobytes",
Value: 0,
EnvVars: []string{"MAX_UPLOAD_SIZE"},
cli.Int64Flag{
Name: "max-upload-size",
Usage: "max limit for upload, in kilobytes",
Value: 0,
EnvVar: "MAX_UPLOAD_SIZE",
},
&cli.StringFlag{
Name: "lets-encrypt-hosts",
Usage: "host1, host2",
Value: "",
EnvVars: []string{"HOSTS"},
cli.StringFlag{
Name: "lets-encrypt-hosts",
Usage: "host1, host2",
Value: "",
EnvVar: "HOSTS",
},
&cli.StringFlag{
Name: "log",
Usage: "/var/log/transfersh.log",
Value: "",
EnvVars: []string{"LOG"},
cli.StringFlag{
Name: "log",
Usage: "/var/log/transfersh.log",
Value: "",
EnvVar: "LOG",
},
&cli.StringFlag{
Name: "basedir",
Usage: "path to storage",
Value: "",
EnvVars: []string{"BASEDIR"},
cli.StringFlag{
Name: "basedir",
Usage: "path to storage",
Value: "",
EnvVar: "BASEDIR",
},
&cli.StringFlag{
Name: "clamav-host",
Usage: "clamav-host",
Value: "",
EnvVars: []string{"CLAMAV_HOST"},
cli.StringFlag{
Name: "clamav-host",
Usage: "clamav-host",
Value: "",
EnvVar: "CLAMAV_HOST",
},
&cli.BoolFlag{
Name: "perform-clamav-prescan",
Usage: "perform-clamav-prescan",
EnvVars: []string{"PERFORM_CLAMAV_PRESCAN"},
cli.BoolFlag{
Name: "perform-clamav-prescan",
Usage: "perform-clamav-prescan",
EnvVar: "PERFORM_CLAMAV_PRESCAN",
},
&cli.StringFlag{
Name: "virustotal-key",
Usage: "virustotal-key",
Value: "",
EnvVars: []string{"VIRUSTOTAL_KEY"},
cli.StringFlag{
Name: "virustotal-key",
Usage: "virustotal-key",
Value: "",
EnvVar: "VIRUSTOTAL_KEY",
},
&cli.BoolFlag{
Name: "profiler",
Usage: "enable profiling",
EnvVars: []string{"PROFILER"},
cli.BoolFlag{
Name: "profiler",
Usage: "enable profiling",
EnvVar: "PROFILER",
},
&cli.StringFlag{
Name: "http-auth-user",
Usage: "user for http basic auth",
Value: "",
EnvVars: []string{"HTTP_AUTH_USER"},
cli.StringFlag{
Name: "http-auth-user",
Usage: "user for http basic auth",
Value: "",
EnvVar: "HTTP_AUTH_USER",
},
&cli.StringFlag{
Name: "http-auth-pass",
Usage: "pass for http basic auth",
Value: "",
EnvVars: []string{"HTTP_AUTH_PASS"},
cli.StringFlag{
Name: "http-auth-pass",
Usage: "pass for http basic auth",
Value: "",
EnvVar: "HTTP_AUTH_PASS",
},
&cli.StringFlag{
Name: "http-auth-htpasswd",
Usage: "htpasswd file http basic auth",
Value: "",
EnvVars: []string{"HTTP_AUTH_HTPASSWD"},
cli.StringFlag{
Name: "ip-whitelist",
Usage: "comma separated list of ips allowed to connect to the service",
Value: "",
EnvVar: "IP_WHITELIST",
},
&cli.StringFlag{
Name: "http-auth-ip-whitelist",
Usage: "comma separated list of ips allowed to upload without being challenged an http auth",
Value: "",
EnvVars: []string{"HTTP_AUTH_IP_WHITELIST"},
cli.StringFlag{
Name: "ip-blacklist",
Usage: "comma separated list of ips not allowed to connect to the service",
Value: "",
EnvVar: "IP_BLACKLIST",
},
&cli.StringFlag{
Name: "ip-whitelist",
Usage: "comma separated list of ips allowed to connect to the service",
Value: "",
EnvVars: []string{"IP_WHITELIST"},
cli.StringFlag{
Name: "cors-domains",
Usage: "comma separated list of domains allowed for CORS requests",
Value: "",
EnvVar: "CORS_DOMAINS",
},
&cli.StringFlag{
Name: "ip-blacklist",
Usage: "comma separated list of ips not allowed to connect to the service",
Value: "",
EnvVars: []string{"IP_BLACKLIST"},
},
&cli.StringFlag{
Name: "cors-domains",
Usage: "comma separated list of domains allowed for CORS requests",
Value: "",
EnvVars: []string{"CORS_DOMAINS"},
},
&cli.IntFlag{
Name: "random-token-length",
Usage: "",
Value: 10,
EnvVars: []string{"RANDOM_TOKEN_LENGTH"},
cli.IntFlag{
Name: "random-token-length",
Usage: "",
Value: 10,
EnvVar: "RANDOM_TOKEN_LENGTH",
},
}
@@ -314,9 +301,8 @@ type Cmd struct {
*cli.App
}
func versionCommand(_ *cli.Context) error {
func versionCommand(_ *cli.Context) {
fmt.Println(color.YellowString("transfer.sh %s: Easy file sharing from the command line", Version))
return nil
}
// New is the factory for transfer.sh
@@ -325,13 +311,13 @@ func New() *Cmd {
app := cli.NewApp()
app.Name = "transfer.sh"
app.Authors = []*cli.Author{}
app.Author = ""
app.Usage = "transfer.sh"
app.Description = `Easy file sharing from the command line`
app.Version = Version
app.Flags = globalFlags
app.CustomAppHelpTemplate = helpTemplate
app.Commands = []*cli.Command{
app.Commands = []cli.Command{
{
Name: "version",
Action: versionCommand,
@@ -342,7 +328,7 @@ func New() *Cmd {
return nil
}
app.Action = func(c *cli.Context) error {
app.Action = func(c *cli.Context) {
var options []server.OptionFn
if v := c.String("listener"); v != "" {
options = append(options, server.Listener(v))
@@ -411,7 +397,7 @@ func New() *Cmd {
if v := c.Bool("perform-clamav-prescan"); v {
if c.String("clamav-host") == "" {
return errors.New("clamav-host not set")
panic("clamav-host not set")
}
options = append(options, server.PerformClamavPrescan(v))
@@ -454,17 +440,6 @@ func New() *Cmd {
options = append(options, server.HTTPAuthCredentials(httpAuthUser, httpAuthPass))
}
if httpAuthHtpasswd := c.String("http-auth-htpasswd"); httpAuthHtpasswd != "" {
options = append(options, server.HTTPAuthHtpasswd(httpAuthHtpasswd))
}
if httpAuthIPWhitelist := c.String("http-auth-ip-whitelist"); httpAuthIPWhitelist != "" {
ipFilterOptions := server.IPFilterOptions{}
ipFilterOptions.AllowedIPs = strings.Split(httpAuthIPWhitelist, ",")
ipFilterOptions.BlockByDefault = true
options = append(options, server.HTTPAUTHFilterOptions(ipFilterOptions))
}
applyIPFilter := false
ipFilterOptions := server.IPFilterOptions{}
if ipWhitelist := c.String("ip-whitelist"); ipWhitelist != "" {
@@ -485,13 +460,13 @@ func New() *Cmd {
switch provider := c.String("provider"); provider {
case "s3":
if accessKey := c.String("aws-access-key"); accessKey == "" {
return errors.New("access-key not set.")
panic("access-key not set.")
} else if secretKey := c.String("aws-secret-key"); secretKey == "" {
return errors.New("secret-key not set.")
panic("secret-key not set.")
} else if bucket := c.String("bucket"); bucket == "" {
return errors.New("bucket not set.")
} else if store, err := storage.NewS3Storage(c.Context, accessKey, secretKey, bucket, purgeDays, c.String("s3-region"), c.String("s3-endpoint"), c.Bool("s3-no-multipart"), c.Bool("s3-path-style"), logger); err != nil {
return err
panic("bucket not set.")
} else if store, err := storage.NewS3Storage(accessKey, secretKey, bucket, purgeDays, c.String("s3-region"), c.String("s3-endpoint"), c.Bool("s3-no-multipart"), c.Bool("s3-path-style"), logger); err != nil {
panic(err)
} else {
options = append(options, server.UseStorage(store))
}
@@ -499,36 +474,36 @@ func New() *Cmd {
chunkSize := c.Int("gdrive-chunk-size") * 1024 * 1024
if clientJSONFilepath := c.String("gdrive-client-json-filepath"); clientJSONFilepath == "" {
return errors.New("gdrive-client-json-filepath not set.")
panic("gdrive-client-json-filepath not set.")
} else if localConfigPath := c.String("gdrive-local-config-path"); localConfigPath == "" {
return errors.New("gdrive-local-config-path not set.")
panic("gdrive-local-config-path not set.")
} else if basedir := c.String("basedir"); basedir == "" {
return errors.New("basedir not set.")
} else if store, err := storage.NewGDriveStorage(c.Context, clientJSONFilepath, localConfigPath, basedir, chunkSize, logger); err != nil {
return err
panic("basedir not set.")
} else if store, err := storage.NewGDriveStorage(clientJSONFilepath, localConfigPath, basedir, chunkSize, logger); err != nil {
panic(err)
} else {
options = append(options, server.UseStorage(store))
}
case "storj":
if access := c.String("storj-access"); access == "" {
return errors.New("storj-access not set.")
panic("storj-access not set.")
} else if bucket := c.String("storj-bucket"); bucket == "" {
return errors.New("storj-bucket not set.")
} else if store, err := storage.NewStorjStorage(c.Context, access, bucket, purgeDays, logger); err != nil {
return err
panic("storj-bucket not set.")
} else if store, err := storage.NewStorjStorage(access, bucket, purgeDays, logger); err != nil {
panic(err)
} else {
options = append(options, server.UseStorage(store))
}
case "local":
if v := c.String("basedir"); v == "" {
return errors.New("basedir not set.")
panic("basedir not set.")
} else if store, err := storage.NewLocalStorage(v, logger); err != nil {
return err
panic(err)
} else {
options = append(options, server.UseStorage(store))
}
default:
return errors.New("Provider not set or invalid.")
panic("Provider not set or invalid.")
}
srvr, err := server.New(
@@ -537,11 +512,10 @@ func New() *Cmd {
if err != nil {
logger.Println(color.RedString("Error starting server: %s", err.Error()))
return err
return
}
srvr.Run()
return nil
}
return &Cmd{

View File

@@ -6,7 +6,6 @@
* [Encrypting and decrypting](#encrypting-and-decrypting)
* [Scanning for viruses](#scanning-for-viruses)
* [Uploading and copy download command](#uploading-and-copy-download-command)
* [Uploading and displaying URL and deletion token](#uploading-and-displaying-url-and-deletion-token)
## Aliases
<a name="aliases"/>
@@ -148,12 +147,12 @@ $ transfer /tmp/hello.txt | mail -s "Hello World" user@yourmaildomain.com
### Encrypting files with password using gpg
```bash
$ gpg --armor --symmetric --output - /tmp/hello.txt | curl --upload-file - https://transfer.sh/test.txt
$ cat /tmp/hello.txt | gpg -ac -o- | curl -X PUT --upload-file "-" https://transfer.sh/test.txt
```
### Downloading and decrypting
```bash
$ curl https://transfer.sh/1lDau/test.txt | gpg --decrypt --output /tmp/hello.txt
$ curl https://transfer.sh/1lDau/test.txt | gpg -o- > /tmp/hello.txt
```
### Import keys from [keybase](https://keybase.io/)
@@ -176,58 +175,6 @@ $ curl -X PUT --upload-file ./eicar.com https://transfer.sh/eicar.com/scan
```bash
$ curl -X PUT --upload-file nhgbhhj https://transfer.sh/test.txt/virustotal
```
### Upload encrypted password protected files
By default files upload for only 1 download, you can specify download limit using -D flag like `transfer-encrypted -D 50 %file/folder%`
#### One line for bashrc
```bash
transfer-encrypted() { if [ $# -eq 0 ]; then echo "No arguments specified.\nUsage:\n transfer <file|directory>\n ... | transfer <file_name>" >&2; return 1; fi; while getopts ":D:" opt; do case $opt in D) max_downloads=$OPTARG;; \?) echo "Invalid option: -$OPTARG" >&2;; esac; done; shift "$((OPTIND - 1))"; file="$1"; file_name=$(basename "$file"); if [ ! -e "$file" ]; then echo "$file: No such file or directory" >&2; return 1; fi; if [ -d "$file" ]; then file_name="$file_name.zip"; (cd "$file" && zip -r -q - .) | openssl aes-256-cbc -pbkdf2 -e > "tmp-$file_name" && cat "tmp-$file_name" | curl -H "Max-Downloads: $max_downloads" -w '\n' --upload-file "tmp-$file_name" "https://transfer.sh/$file_name" | tee /dev/null; rm "tmp-$file_name"; else cat "$file" | openssl aes-256-cbc -pbkdf2 -e > "tmp-$file" && cat "tmp-$file" | curl -H "Max-Downloads: $max_downloads" -w '\n' --upload-file - "https://transfer.sh/$file_name" | tee /dev/null; rm "tmp-$file"; fi; }
```
#### Human readable code
```bash
transfer-encrypted() {
if [ $# -eq 0 ]; then
echo "No arguments specified.\nUsage:\n transfer <file|directory>\n ... | transfer <file_name>" >&2
return 1
fi
while getopts ":D:" opt; do
case $opt in
D)
max_downloads=$OPTARG
;;
\?)
echo "Invalid option: -$OPTARG" >&2
;;
esac
done
shift "$((OPTIND - 1))"
file="$1"
file_name=$(basename "$file")
if [ ! -e "$file" ]; then
echo "$file: No such file or directory" >&2
return 1
fi
if [ -d "$file" ]; then
file_name="$file_name.zip"
(cd "$file" && zip -r -q - .) | openssl aes-256-cbc -pbkdf2 -e > "tmp-$file_name" && cat "tmp-$file_name" | curl -H "Max-Downloads: $max_downloads" -w '\n' --upload-file "tmp-$file_name" "https://transfer.sh/$file_name" | tee /dev/null
rm "tmp-$file_name"
else
cat "$file" | openssl aes-256-cbc -pbkdf2 -e > "tmp-$file" && cat "tmp-$file" | curl -H "Max-Downloads: $max_downloads" -w '\n' --upload-file - "https://transfer.sh/$file_name" | tee /dev/null
rm "tmp-$file"
fi
}
```
#### Decrypt using
```bash
curl -s https://transfer.sh/some/file | openssl aes-256-cbc -pbkdf2 -d > output_filename
```
## Uploading and copy download command
Download commands can be automatically copied to the clipboard after files are uploaded using transfer.sh.
@@ -312,34 +259,5 @@ https://transfer.sh/y0qr2c/a.log
wget https://transfer.sh/y0qr2c/a.log
3) Windows download command:
Invoke-WebRequest -Uri https://transfer.sh/y0qr2c/a.log -OutFile a.log
```
## Uploading and displaying URL and deletion token
```bash
# tempfile
URLFILE=$HOME/temp/transfersh.url
# insert number of downloads and days saved
if [ -f $1 ]; then
read -p "Allowed number of downloads: " num_down
read -p "Number of days on server: " num_save
# transfer
curl -sD - -H "Max-Downloads: $num_down" -H "Max-Days: $num_save"--progress-bar --upload-file $1 https://transfer.sh/$(basename $1) | grep -i -E 'transfer\.sh|x-url-delete' &> $URLFILE
# display URL and deletion token
if [ -f $URLFILE ]; then
URL=$(tail -n1 $URLFILE)
TOKEN=$(grep delete $URLFILE | awk -F "/" '{print $NF}')
echo "*********************************"
echo "Data is saved in $URLFILE"
echo "**********************************"
echo "URL is: $URL"
echo "Deletion Token is: $TOKEN"
echo "**********************************"
else
echo "NO URL-File found !!"
fi
else
echo "!!!!!!"
echo "\"$1\" not found !!"
echo "!!!!!!"
fi
Invoke-WebRequest -Uri https://transfer.sh/y0qr2c/a.log -OutFile a.log
```

View File

@@ -44,8 +44,6 @@
tls-private-key = mkOption { type = types.nullOr types.str; description = "path to tls private key "; };
http-auth-user = mkOption { type = types.nullOr types.str; description = "user for basic http auth on upload"; };
http-auth-pass = mkOption { type = types.nullOr types.str; description = "pass for basic http auth on upload"; };
http-auth-htpasswd = mkOption { type = types.nullOr types.str; description = "htpasswd file path for basic http auth on upload"; };
http-auth-ip-whitelist = mkOption { type = types.nullOr types.str; description = "comma separated list of ips allowed to upload without being challenged an http auth"; };
ip-whitelist = mkOption { type = types.nullOr types.str; description = "comma separated list of ips allowed to connect to the service"; };
ip-blacklist = mkOption { type = types.nullOr types.str; description = "comma separated list of ips not allowed to connect to the service"; };
temp-path = mkOption { type = types.nullOr types.str; description = "path to temp folder"; };

56
go.mod
View File

@@ -1,19 +1,15 @@
module github.com/dutchcoders/transfer.sh
go 1.22.0
go 1.18
require (
github.com/ProtonMail/go-crypto v0.0.0-20230217124315-7d5c6f04bbb8
github.com/ProtonMail/gopenpgp/v2 v2.5.2
github.com/PuerkitoBio/ghost v0.0.0-20160324114900-206e6e460e14
github.com/VojtechVitek/ratelimit v0.0.0-20160722140851-dc172bc0f6d2
github.com/aws/aws-sdk-go-v2 v1.18.0
github.com/aws/aws-sdk-go-v2/config v1.18.25
github.com/aws/aws-sdk-go-v2/credentials v1.13.24
github.com/aws/aws-sdk-go-v2/feature/s3/manager v1.11.67
github.com/aws/aws-sdk-go-v2/service/s3 v1.33.1
github.com/aws/aws-sdk-go v1.44.211
github.com/dutchcoders/go-clamd v0.0.0-20170520113014-b970184f4d9e
github.com/Aetherinox/go-virustotal v0.0.0-20250520084801-0eb8c8f901c8
github.com/dutchcoders/go-virustotal v0.0.0-20140923143438-24cc8e6fa329
github.com/dutchcoders/transfer.sh-web v0.0.0-20221119114740-ca3a2621d2a6
github.com/elazarl/go-bindata-assetfs v1.0.1
github.com/fatih/color v1.14.1
@@ -23,50 +19,33 @@ require (
github.com/microcosm-cc/bluemonday v1.0.23
github.com/russross/blackfriday/v2 v2.1.0
github.com/skip2/go-qrcode v0.0.0-20200617195104-da1b6568686e
github.com/tg123/go-htpasswd v1.2.1
github.com/tomasen/realip v0.0.0-20180522021738-f0c99a92ddce
github.com/urfave/cli/v2 v2.25.3
golang.org/x/crypto v0.21.0
golang.org/x/net v0.23.0
golang.org/x/oauth2 v0.7.0
golang.org/x/text v0.14.0
google.golang.org/api v0.114.0
github.com/urfave/cli v1.22.12
golang.org/x/crypto v0.6.0
golang.org/x/net v0.8.0
golang.org/x/oauth2 v0.5.0
google.golang.org/api v0.111.0
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c
storj.io/common v0.0.0-20230301105927-7f966760c100
storj.io/uplink v1.10.0
)
require (
cloud.google.com/go/compute v1.19.1 // indirect
cloud.google.com/go/compute v1.18.0 // indirect
cloud.google.com/go/compute/metadata v0.2.3 // indirect
github.com/GehirnInc/crypt v0.0.0-20200316065508-bb7000b8a962 // indirect
github.com/aws/aws-sdk-go-v2/aws/protocol/eventstream v1.4.10 // indirect
github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.13.3 // indirect
github.com/aws/aws-sdk-go-v2/internal/configsources v1.1.33 // indirect
github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.4.27 // indirect
github.com/aws/aws-sdk-go-v2/internal/ini v1.3.34 // indirect
github.com/aws/aws-sdk-go-v2/internal/v4a v1.0.25 // indirect
github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.9.11 // indirect
github.com/aws/aws-sdk-go-v2/service/internal/checksum v1.1.28 // indirect
github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.9.27 // indirect
github.com/aws/aws-sdk-go-v2/service/internal/s3shared v1.14.2 // indirect
github.com/aws/aws-sdk-go-v2/service/sso v1.12.10 // indirect
github.com/aws/aws-sdk-go-v2/service/ssooidc v1.14.10 // indirect
github.com/aws/aws-sdk-go-v2/service/sts v1.19.0 // indirect
github.com/aws/smithy-go v1.13.5 // indirect
github.com/aymerick/douceur v0.2.0 // indirect
github.com/calebcase/tmpfile v1.0.3 // indirect
github.com/cloudflare/circl v1.6.1 // indirect
github.com/cloudflare/circl v1.1.0 // indirect
github.com/cpuguy83/go-md2man/v2 v2.0.2 // indirect
github.com/felixge/httpsnoop v1.0.3 // indirect
github.com/flynn/noise v1.0.0 // indirect
github.com/garyburd/redigo v1.6.4 // indirect
github.com/gogo/protobuf v1.3.2 // indirect
github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da // indirect
github.com/golang/protobuf v1.5.3 // indirect
github.com/golang/protobuf v1.5.2 // indirect
github.com/google/uuid v1.3.0 // indirect
github.com/googleapis/enterprise-certificate-proxy v0.2.3 // indirect
github.com/googleapis/gax-go/v2 v2.7.1 // indirect
github.com/googleapis/gax-go/v2 v2.7.0 // indirect
github.com/gorilla/css v1.0.0 // indirect
github.com/gorilla/securecookie v1.1.1 // indirect
github.com/jmespath/go-jmespath v0.4.0 // indirect
@@ -81,17 +60,16 @@ require (
github.com/rogpeppe/go-internal v1.9.0 // indirect
github.com/spacemonkeygo/monkit/v3 v3.0.19 // indirect
github.com/vivint/infectious v0.0.0-20200605153912-25a574ae18a3 // indirect
github.com/xrash/smetrics v0.0.0-20201216005158-039620a65673 // indirect
github.com/zeebo/blake3 v0.2.3 // indirect
github.com/zeebo/errs v1.3.0 // indirect
go.opencensus.io v0.24.0 // indirect
golang.org/x/sync v0.1.0 // indirect
golang.org/x/sys v0.18.0 // indirect
golang.org/x/sys v0.6.0 // indirect
golang.org/x/text v0.8.0 // indirect
google.golang.org/appengine v1.6.7 // indirect
google.golang.org/genproto v0.0.0-20230410155749-daa745c078e1 // indirect
google.golang.org/grpc v1.56.3 // indirect
google.golang.org/protobuf v1.33.0 // indirect
gopkg.in/yaml.v2 v2.4.0 // indirect
google.golang.org/genproto v0.0.0-20230227214838-9b19f0bdc514 // indirect
google.golang.org/grpc v1.53.0 // indirect
google.golang.org/protobuf v1.28.1 // indirect
storj.io/drpc v0.0.33-0.20230204035225-c9649dee8f2a // indirect
storj.io/picobuf v0.0.1 // indirect
)

116
go.sum
View File

@@ -1,16 +1,14 @@
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.110.0 h1:Zc8gqp3+a9/Eyph2KDmcGaPtbKRIoqq4YTlL4NMD0Ys=
cloud.google.com/go/compute v1.19.1 h1:am86mquDUgjGNWxiGn+5PGLbmgiWXlE/yNWpIpNvuXY=
cloud.google.com/go/compute v1.19.1/go.mod h1:6ylj3a05WF8leseCdIf77NK0g1ey+nj5IKd5/kvShxE=
cloud.google.com/go/compute v1.18.0 h1:FEigFqoDbys2cvFkZ9Fjq4gnHBP55anJ0yQyau2f9oY=
cloud.google.com/go/compute v1.18.0/go.mod h1:1X7yHxec2Ga+Ss6jPyjxRxpu2uu7PLgsOVXvgU0yacs=
cloud.google.com/go/compute/metadata v0.2.3 h1:mg4jlk7mCAj6xXp9UJ4fjI9VUI5rubuGBW5aJ7UnBMY=
cloud.google.com/go/compute/metadata v0.2.3/go.mod h1:VAV5nSsACxMJvgaAuX6Pk2AawlZn8kiOGuCv6gTkwuA=
cloud.google.com/go/longrunning v0.4.1 h1:v+yFJOfKC3yZdY6ZUI933pIYdhyhV8S3NpWrXWmg7jM=
cloud.google.com/go/longrunning v0.4.1/go.mod h1:4iWDqhBZ70CvZ6BfETbvam3T8FMvLK+eFj0E6AaRQTo=
github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
github.com/BurntSushi/toml v1.2.1/go.mod h1:CxXYINrC8qIiEnFrOxCa7Jy5BFHlXnUU2pbicEuybxQ=
github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym/WlBOVXweHU+Q+/VP0lqqI8lqeDx9IjBqo=
github.com/GehirnInc/crypt v0.0.0-20200316065508-bb7000b8a962 h1:KeNholpO2xKjgaaSyd+DyQRrsQjhbSeS7qe4nEw8aQw=
github.com/GehirnInc/crypt v0.0.0-20200316065508-bb7000b8a962/go.mod h1:kC29dT1vFpj7py2OvG1khBdQpo3kInWP+6QipLbdngo=
github.com/ProtonMail/go-crypto v0.0.0-20230124153114-0acdc8ae009b/go.mod h1:I0gYDMZ6Z5GRU7l58bNFSkPTFN6Yl12dsUlAZ8xy98g=
github.com/ProtonMail/go-crypto v0.0.0-20230217124315-7d5c6f04bbb8 h1:wPbRQzjjwFc0ih8puEVAOFGELsn1zoIIYdxvML7mDxA=
github.com/ProtonMail/go-crypto v0.0.0-20230217124315-7d5c6f04bbb8/go.mod h1:I0gYDMZ6Z5GRU7l58bNFSkPTFN6Yl12dsUlAZ8xy98g=
@@ -21,44 +19,8 @@ github.com/PuerkitoBio/ghost v0.0.0-20160324114900-206e6e460e14 h1:3zOOc7WdrATDX
github.com/PuerkitoBio/ghost v0.0.0-20160324114900-206e6e460e14/go.mod h1:+VFiaivV54Sa94ijzA/ZHQLoHuoUIS9hIqCK6f/76Zw=
github.com/VojtechVitek/ratelimit v0.0.0-20160722140851-dc172bc0f6d2 h1:sIvihcW4qpN5qGSjmrsDDAbLpEq5tuHjJJfWY0Hud5Y=
github.com/VojtechVitek/ratelimit v0.0.0-20160722140851-dc172bc0f6d2/go.mod h1:3YwJE8rEisS9eraee0hygGG4G3gqX8H8Nyu+nPTUnGU=
github.com/aws/aws-sdk-go-v2 v1.18.0 h1:882kkTpSFhdgYRKVZ/VCgf7sd0ru57p2JCxz4/oN5RY=
github.com/aws/aws-sdk-go-v2 v1.18.0/go.mod h1:uzbQtefpm44goOPmdKyAlXSNcwlRgF3ePWVW6EtJvvw=
github.com/aws/aws-sdk-go-v2/aws/protocol/eventstream v1.4.10 h1:dK82zF6kkPeCo8J1e+tGx4JdvDIQzj7ygIoLg8WMuGs=
github.com/aws/aws-sdk-go-v2/aws/protocol/eventstream v1.4.10/go.mod h1:VeTZetY5KRJLuD/7fkQXMU6Mw7H5m/KP2J5Iy9osMno=
github.com/aws/aws-sdk-go-v2/config v1.18.25 h1:JuYyZcnMPBiFqn87L2cRppo+rNwgah6YwD3VuyvaW6Q=
github.com/aws/aws-sdk-go-v2/config v1.18.25/go.mod h1:dZnYpD5wTW/dQF0rRNLVypB396zWCcPiBIvdvSWHEg4=
github.com/aws/aws-sdk-go-v2/credentials v1.13.24 h1:PjiYyls3QdCrzqUN35jMWtUK1vqVZ+zLfdOa/UPFDp0=
github.com/aws/aws-sdk-go-v2/credentials v1.13.24/go.mod h1:jYPYi99wUOPIFi0rhiOvXeSEReVOzBqFNOX5bXYoG2o=
github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.13.3 h1:jJPgroehGvjrde3XufFIJUZVK5A2L9a3KwSFgKy9n8w=
github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.13.3/go.mod h1:4Q0UFP0YJf0NrsEuEYHpM9fTSEVnD16Z3uyEF7J9JGM=
github.com/aws/aws-sdk-go-v2/feature/s3/manager v1.11.67 h1:fI9/5BDEaAv/pv1VO1X1n3jfP9it+IGqWsCuuBQI8wM=
github.com/aws/aws-sdk-go-v2/feature/s3/manager v1.11.67/go.mod h1:zQClPRIwQZfJlZq6WZve+s4Tb4JW+3V6eS+4+KrYeP8=
github.com/aws/aws-sdk-go-v2/internal/configsources v1.1.33 h1:kG5eQilShqmJbv11XL1VpyDbaEJzWxd4zRiCG30GSn4=
github.com/aws/aws-sdk-go-v2/internal/configsources v1.1.33/go.mod h1:7i0PF1ME/2eUPFcjkVIwq+DOygHEoK92t5cDqNgYbIw=
github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.4.27 h1:vFQlirhuM8lLlpI7imKOMsjdQLuN9CPi+k44F/OFVsk=
github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.4.27/go.mod h1:UrHnn3QV/d0pBZ6QBAEQcqFLf8FAzLmoUfPVIueOvoM=
github.com/aws/aws-sdk-go-v2/internal/ini v1.3.34 h1:gGLG7yKaXG02/jBlg210R7VgQIotiQntNhsCFejawx8=
github.com/aws/aws-sdk-go-v2/internal/ini v1.3.34/go.mod h1:Etz2dj6UHYuw+Xw830KfzCfWGMzqvUTCjUj5b76GVDc=
github.com/aws/aws-sdk-go-v2/internal/v4a v1.0.25 h1:AzwRi5OKKwo4QNqPf7TjeO+tK8AyOK3GVSwmRPo7/Cs=
github.com/aws/aws-sdk-go-v2/internal/v4a v1.0.25/go.mod h1:SUbB4wcbSEyCvqBxv/O/IBf93RbEze7U7OnoTlpPB+g=
github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.9.11 h1:y2+VQzC6Zh2ojtV2LoC0MNwHWc6qXv/j2vrQtlftkdA=
github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.9.11/go.mod h1:iV4q2hsqtNECrfmlXyord9u4zyuFEJX9eLgLpSPzWA8=
github.com/aws/aws-sdk-go-v2/service/internal/checksum v1.1.28 h1:vGWm5vTpMr39tEZfQeDiDAMgk+5qsnvRny3FjLpnH5w=
github.com/aws/aws-sdk-go-v2/service/internal/checksum v1.1.28/go.mod h1:spfrICMD6wCAhjhzHuy6DOZZ+LAIY10UxhUmLzpJTTs=
github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.9.27 h1:0iKliEXAcCa2qVtRs7Ot5hItA2MsufrphbRFlz1Owxo=
github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.9.27/go.mod h1:EOwBD4J4S5qYszS5/3DpkejfuK+Z5/1uzICfPaZLtqw=
github.com/aws/aws-sdk-go-v2/service/internal/s3shared v1.14.2 h1:NbWkRxEEIRSCqxhsHQuMiTH7yo+JZW1gp8v3elSVMTQ=
github.com/aws/aws-sdk-go-v2/service/internal/s3shared v1.14.2/go.mod h1:4tfW5l4IAB32VWCDEBxCRtR9T4BWy4I4kr1spr8NgZM=
github.com/aws/aws-sdk-go-v2/service/s3 v1.33.1 h1:O+9nAy9Bb6bJFTpeNFtd9UfHbgxO1o4ZDAM9rQp5NsY=
github.com/aws/aws-sdk-go-v2/service/s3 v1.33.1/go.mod h1:J9kLNzEiHSeGMyN7238EjJmBpCniVzFda75Gxl/NqB8=
github.com/aws/aws-sdk-go-v2/service/sso v1.12.10 h1:UBQjaMTCKwyUYwiVnUt6toEJwGXsLBI6al083tpjJzY=
github.com/aws/aws-sdk-go-v2/service/sso v1.12.10/go.mod h1:ouy2P4z6sJN70fR3ka3wD3Ro3KezSxU6eKGQI2+2fjI=
github.com/aws/aws-sdk-go-v2/service/ssooidc v1.14.10 h1:PkHIIJs8qvq0e5QybnZoG1K/9QTrLr9OsqCIo59jOBA=
github.com/aws/aws-sdk-go-v2/service/ssooidc v1.14.10/go.mod h1:AFvkxc8xfBe8XA+5St5XIHHrQQtkxqrRincx4hmMHOk=
github.com/aws/aws-sdk-go-v2/service/sts v1.19.0 h1:2DQLAKDteoEDI8zpCzqBMaZlJuoE9iTYD0gFmXVax9E=
github.com/aws/aws-sdk-go-v2/service/sts v1.19.0/go.mod h1:BgQOMsg8av8jset59jelyPW7NoZcZXLVpDsXunGDrk8=
github.com/aws/smithy-go v1.13.5 h1:hgz0X/DX0dGqTYpGALqXJoRKRj5oQ7150i5FdTePzO8=
github.com/aws/smithy-go v1.13.5/go.mod h1:Tg+OJXh4MB2R/uN61Ko2f6hTZwB/ZYGOtib8J3gBHzA=
github.com/aws/aws-sdk-go v1.44.211 h1:YNr5DwdzG/8y9Tl0QrPTnC99aFUHgT5hhy6GpnnzHK4=
github.com/aws/aws-sdk-go v1.44.211/go.mod h1:aVsgQcEevwlmQ7qHE9I3h+dtQgpqhFB+i8Phjh7fkwI=
github.com/aymerick/douceur v0.2.0 h1:Mv+mAeH1Q+n9Fr+oyamOlAkUNPWPlA8PPGR0QAaYuPk=
github.com/aymerick/douceur v0.2.0/go.mod h1:wlT5vV2O3h55X9m7iVYN0TBM0NH/MmbLnd30/FjWUq4=
github.com/bradfitz/gomemcache v0.0.0-20170208213004-1952afaa557d/go.mod h1:PmM6Mmwb0LSuEubjR8N7PtNe1KxZLtOUHtbeikc5h60=
@@ -67,9 +29,8 @@ github.com/calebcase/tmpfile v1.0.3 h1:BZrOWZ79gJqQ3XbAQlihYZf/YCV0H4KPIdM5K5oMp
github.com/calebcase/tmpfile v1.0.3/go.mod h1:UAUc01aHeC+pudPagY/lWvt2qS9ZO5Zzof6/tIUzqeI=
github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU=
github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw=
github.com/cloudflare/circl v1.1.0 h1:bZgT/A+cikZnKIwn7xL2OBj012Bmvho/o6RpRvv3GKY=
github.com/cloudflare/circl v1.1.0/go.mod h1:prBCrKB9DV4poKZY1l9zBXg2QJY7mvgRvtMxxK7fi4I=
github.com/cloudflare/circl v1.6.1 h1:zqIqSPIndyBh1bjLVVDHMPpVKqp8Su/V+6MeDzzQBQ0=
github.com/cloudflare/circl v1.6.1/go.mod h1:uddAzsPgqdMAYatqJ0lsjX1oECcQLIlRpzZh3pJrofs=
github.com/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f/go.mod h1:M8M6+tZqaGXZJjfX53e64911xZQV5JYwmTeXPW+k8Sc=
github.com/cpuguy83/go-md2man/v2 v2.0.2 h1:p1EgwI/C7NhT0JmVkwCD2ZBK8j4aeHQX2pMHHBfMQ6w=
github.com/cpuguy83/go-md2man/v2 v2.0.2/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o=
@@ -78,11 +39,10 @@ github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSs
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/dsnet/try v0.0.3 h1:ptR59SsrcFUYbT/FhAbKTV6iLkeD6O18qfIWRml2fqI=
github.com/dsnet/try v0.0.3/go.mod h1:WBM8tRpUmnXXhY1U6/S8dt6UWdHTQ7y8A5YSkRCkq40=
github.com/dutchcoders/go-clamd v0.0.0-20170520113014-b970184f4d9e h1:rcHHSQqzCgvlwP0I/fQ8rQMn/MpHE5gWSLdtpxtP6KQ=
github.com/dutchcoders/go-clamd v0.0.0-20170520113014-b970184f4d9e/go.mod h1:Byz7q8MSzSPkouskHJhX0er2mZY/m0Vj5bMeMCkkyY4=
github.com/Aetherinox/go-virustotal v0.0.0-20250520084801-0eb8c8f901c8 h1:wEwYJxNLG29OesabDdAJWFBIO42HOL4x5kjvGuZLIyk=
github.com/Aetherinox/go-virustotal v0.0.0-20250520084801-0eb8c8f901c8/go.mod h1:myGG2GhfY2AgAPe8lFZw6Y1+IxhU+ED7ilotbpdQsDw=
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/transfer.sh-web v0.0.0-20221119114740-ca3a2621d2a6 h1:7uTRy44YpQi6/mtDq0N9zeQRCGEh93o7gKq/usGgpF8=
github.com/dutchcoders/transfer.sh-web v0.0.0-20221119114740-ca3a2621d2a6/go.mod h1:F6Q37CxDh2MHr5KXkcZmNB3tdkK7v+bgE+OpBY+9ilI=
github.com/elazarl/go-bindata-assetfs v1.0.1 h1:m0kkaHRKEu7tUIUFVwhGGGYClXvyl4RE03qmvRTNfbw=
@@ -124,8 +84,8 @@ github.com/golang/protobuf v1.4.0/go.mod h1:jodUvKwWbYaEsadDk5Fwe5c77LiNKVO9IDvq
github.com/golang/protobuf v1.4.1/go.mod h1:U8fpvMrcmy5pZrNK1lt4xCsGvpyWQ/VVv6QDs8UjoX8=
github.com/golang/protobuf v1.4.3/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI=
github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk=
github.com/golang/protobuf v1.5.3 h1:KhyjKVUg7Usr/dYsdSqoFveMYd5ko72D+zANwlG1mmg=
github.com/golang/protobuf v1.5.3/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY=
github.com/golang/protobuf v1.5.2 h1:ROPKBNFfQgOUMifHyP+KYbvpjbdoFNs+aK7DXlji0Tw=
github.com/golang/protobuf v1.5.2/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY=
github.com/golang/snappy v0.0.0-20170215233205-553a64147049/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q=
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=
@@ -135,19 +95,16 @@ github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/
github.com/google/go-cmp v0.5.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
github.com/google/go-cmp v0.5.3/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
github.com/google/go-cmp v0.5.8/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
github.com/google/go-cmp v0.5.9 h1:O2Tfq5qg4qc4AmwVlvv0oLiVAGB7enBSJ2x2DqQFi38=
github.com/google/go-cmp v0.5.9/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
github.com/google/pprof v0.0.0-20211108044417-e9b028704de0 h1:rsq1yB2xiFLDYYaYdlGBsSkwVzsCo500wMhxvW5A/bk=
github.com/google/pprof v0.0.0-20211108044417-e9b028704de0/go.mod h1:KgnwoLYCZ8IQu3XUZ8Nc/bM9CCZFOyjUNOSygVozoDg=
github.com/google/uuid v1.1.2/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
github.com/google/uuid v1.3.0 h1:t6JiXgmwXMjEs8VusXIJk2BXHsn+wx8BZdTaoZ5fu7I=
github.com/google/uuid v1.3.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
github.com/googleapis/enterprise-certificate-proxy v0.2.3 h1:yk9/cqRKtT9wXZSsRH9aurXEpJX+U6FLtpYTdC3R06k=
github.com/googleapis/enterprise-certificate-proxy v0.2.3/go.mod h1:AwSRAtLfXpU5Nm3pW+v7rGDHp09LsPtGY9MduiEsR9k=
github.com/googleapis/gax-go v2.0.0+incompatible/go.mod h1:SFVmujtThgffbyetf+mdk2eWhX2bMyUtNHzFKcPA9HY=
github.com/googleapis/gax-go/v2 v2.7.1 h1:gF4c0zjUP2H/s/hEGyLA3I0fA2ZWjzYiONAD6cvPr8A=
github.com/googleapis/gax-go/v2 v2.7.1/go.mod h1:4orTrqY6hXxxaUL4LHIPl6lGo8vAE38/qKbhSAKP6QI=
github.com/googleapis/gax-go/v2 v2.7.0 h1:IcsPKeInNvYi7eqSaDjiZqDDKu5rsmunY0Y1YupQSSQ=
github.com/googleapis/gax-go/v2 v2.7.0/go.mod h1:TEop28CZZQ2y+c0VxMUmu1lV+fQx57QpBWsYpwqHJx8=
github.com/gorilla/css v1.0.0 h1:BQqNyPTi50JCFMTw/b67hByjMVXZRwGha6wxVGkeihY=
github.com/gorilla/css v1.0.0/go.mod h1:Dn721qIggHpt4+EFCcTLTU/vk5ySda2ReITrtgBl60c=
github.com/gorilla/handlers v1.5.1 h1:9lRY6j8DEeeBT10CvO9hGW0gmky0BprnvDI5vfhUHH4=
@@ -222,22 +179,17 @@ github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/
github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU=
github.com/stretchr/testify v1.8.1 h1:w7B6lhMri9wdJUVmEZPGGhZzrYTPvgJArz7wNPgYKsk=
github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4=
github.com/tg123/go-htpasswd v1.2.1 h1:i4wfsX1KvvkyoMiHZzjS0VzbAPWfxzI8INcZAKtutoU=
github.com/tg123/go-htpasswd v1.2.1/go.mod h1:erHp1B86KXdwQf1X5ZrLb7erXZnWueEQezb2dql4q58=
github.com/tomasen/realip v0.0.0-20180522021738-f0c99a92ddce h1:fb190+cK2Xz/dvi9Hv8eCYJYvIGUTN2/KLq1pT6CjEc=
github.com/tomasen/realip v0.0.0-20180522021738-f0c99a92ddce/go.mod h1:o8v6yHRoik09Xen7gje4m9ERNah1d1PPsVq1VEx9vE4=
github.com/urfave/cli/v2 v2.25.3 h1:VJkt6wvEBOoSjPFQvOkv6iWIrsJyCrKGtCtxXWwmGeY=
github.com/urfave/cli/v2 v2.25.3/go.mod h1:GHupkWPMM0M/sj1a2b4wUrWBPzazNrIjouW6fmdJLxc=
github.com/urfave/cli v1.22.12 h1:igJgVw1JdKH+trcLWLeLwZjU9fEfPesQ+9/e4MQ44S8=
github.com/urfave/cli v1.22.12/go.mod h1:sSBEIC79qR6OvcmsD4U3KABeOTxDqQtdDnaFuUN30b8=
github.com/vivint/infectious v0.0.0-20200605153912-25a574ae18a3 h1:zMsHhfK9+Wdl1F7sIKLyx3wrOFofpb3rWFbA4HgcK5k=
github.com/vivint/infectious v0.0.0-20200605153912-25a574ae18a3/go.mod h1:R0Gbuw7ElaGSLOZUSwBm/GgVwMd30jWxBDdAyMOeTuc=
github.com/xrash/smetrics v0.0.0-20201216005158-039620a65673 h1:bAn7/zixMGCfxrRTfdpNzjtPYqr8smhKouy9mxVdGPU=
github.com/xrash/smetrics v0.0.0-20201216005158-039620a65673/go.mod h1:N3UwUGtsrSj3ccvlPHLoLsHnpR27oXr4ZE984MbSER8=
github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY=
github.com/zeebo/assert v1.1.0/go.mod h1:Pq9JiuJQpG8JLJdtkwrJESF0Foym2/D9XMU5ciN/wJ0=
github.com/zeebo/assert v1.3.1 h1:vukIABvugfNMZMQO1ABsyQDJDTVQbn+LWSMy1ol1h6A=
github.com/zeebo/assert v1.3.1/go.mod h1:Pq9JiuJQpG8JLJdtkwrJESF0Foym2/D9XMU5ciN/wJ0=
github.com/zeebo/blake3 v0.2.3 h1:TFoLXsjeXqRNFxSbk35Dk4YtszE/MQQGK10BH4ptoTg=
github.com/zeebo/blake3 v0.2.3/go.mod h1:mjJjZpnsyIVtVgTOSpJ9vmRE4wgDeyt2HU3qXvvKCaQ=
github.com/zeebo/errs v1.3.0 h1:hmiaKqgYZzcVgRL1Vkc1Mn2914BbzB0IBxs+ebeutGs=
@@ -252,8 +204,8 @@ golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8U
golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
golang.org/x/crypto v0.0.0-20210322153248-0c34fe9e7dc2/go.mod h1:T9bdIzuCu7OtxOm1hfPfRQxPLYneinmdGuTeoZ9dtd4=
golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
golang.org/x/crypto v0.21.0 h1:X31++rzVUdKhX5sWmSOFZxx8UW/ldWx55cbf08iNAMA=
golang.org/x/crypto v0.21.0/go.mod h1:0BP7YvVV9gBbVKyeTG0Gyn+gZm94bibOW5BjDEYAOMs=
golang.org/x/crypto v0.6.0 h1:qfktjS5LUO+fFKeJXZ+ikTRijMmljikvG68fpMMruSc=
golang.org/x/crypto v0.6.0/go.mod h1:OFC/31mSvZgRz0V1QTNCzfAI1aIRzbiufJtkMIlEp58=
golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
golang.org/x/exp v0.0.0-20190731235908-ec7cb31e5a56/go.mod h1:JhuoJpWY28nO4Vef9tZUw9qufEGTyX1+7lmHxV5q5G4=
golang.org/x/image v0.0.0-20190227222117-0694c2d4d067/go.mod h1:kZ7UVZpmo3dzQBMxlp+ypCbDeSB+sBbTgSJuh5dn5js=
@@ -280,12 +232,13 @@ golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwY
golang.org/x/net v0.0.0-20201110031124-69a78807bb2b/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=
golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c=
golang.org/x/net v0.23.0 h1:7EYJ93RZ9vYSZAIb2x3lnuvqO5zneoD6IvWjuhfxjTs=
golang.org/x/net v0.23.0/go.mod h1:JKghWKKOSdJwpW2GEx0Ja7fmaKnMsbu+MWVZTokSYmg=
golang.org/x/net v0.1.0/go.mod h1:Cx3nUiGt4eDBEyega/BKRp+/AlGL8hYe7U9odMt2Cco=
golang.org/x/net v0.8.0 h1:Zrh2ngAOFYneWTAIAPethzeaQLuHwhuBkuV6ZiRnUaQ=
golang.org/x/net v0.8.0/go.mod h1:QVkue5JL9kW//ek3r6jTKnTFis1tRmNAW2P1shuFdJc=
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.7.0 h1:qe6s0zUXlPX80/dITx3440hWZ7GwMwgDDyrSGTPJG/g=
golang.org/x/oauth2 v0.7.0/go.mod h1:hPLQkd9LyjfXTiRohC/41GhcFqxisoUQ99sCUOHO9x4=
golang.org/x/oauth2 v0.5.0 h1:HuArIo48skDwlrvM3sEdHXElYslAMsf3KwRkkW4MC4s=
golang.org/x/oauth2 v0.5.0/go.mod h1:9/XBHVqLaWO3/BRHs5jbpYCnOZVjj5V0ndyaAM7KB4I=
golang.org/x/sync v0.0.0-20170517211232-f52d1811a629/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
@@ -309,16 +262,19 @@ golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBc
golang.org/x/sys v0.0.0-20220704084225-05e143d24a9e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.18.0 h1:DBdB3niSjOA/O0blCZBqDefyWNYveAYMNF1Wum0DYQ4=
golang.org/x/sys v0.18.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
golang.org/x/sys v0.1.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.6.0 h1:MVltZSvRTcU2ljQOhs94SXPftV6DCNnZViHeQps87pQ=
golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
golang.org/x/term v0.1.0/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
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.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ=
golang.org/x/text v0.14.0 h1:ScX5w1eTa3QqT8oi6+ziP7dTV1S2+ALU0bI+0zXKWiQ=
golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU=
golang.org/x/text v0.4.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8=
golang.org/x/text v0.8.0 h1:57P1ETyNKtuIjB4SRd15iJxuhj8Gc416Y78H3qgMh68=
golang.org/x/text v0.8.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8=
golang.org/x/time v0.0.0-20170424234030-8be79e1e0910/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-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
@@ -335,8 +291,8 @@ golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8T
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
google.golang.org/api v0.0.0-20170921000349-586095a6e407/go.mod h1:4mhQ8q/RsB7i+udVvVy5NUi08OU8ZlA0gRVgrF7VFY0=
google.golang.org/api v0.114.0 h1:1xQPji6cO2E2vLiI+C/XiFAnsn1WV3mjaEwGLhi3grE=
google.golang.org/api v0.114.0/go.mod h1:ifYI2ZsFK6/uGddGfAD5BMxlnkBqCmqHSDUVi45N5Yg=
google.golang.org/api v0.111.0 h1:bwKi+z2BsdwYFRKrqwutM+axAlYLz83gt5pDSXCJT+0=
google.golang.org/api v0.111.0/go.mod h1:qtFHvU9mhgTJegR31csQ+rwxyUTHOKFqCKWp1J0fdw0=
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.6.5/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc=
@@ -346,16 +302,16 @@ google.golang.org/genproto v0.0.0-20170918111702-1e559d0a00ee/go.mod h1:JiN7NxoA
google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc=
google.golang.org/genproto v0.0.0-20190819201941-24fa4b261c55/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc=
google.golang.org/genproto v0.0.0-20200526211855-cb27e3aa2013/go.mod h1:NbSheEEYHJ7i3ixzK3sjbqSGDJWnxyFXZblF3eUsNvo=
google.golang.org/genproto v0.0.0-20230410155749-daa745c078e1 h1:KpwkzHKEF7B9Zxg18WzOa7djJ+Ha5DzthMyZYQfEn2A=
google.golang.org/genproto v0.0.0-20230410155749-daa745c078e1/go.mod h1:nKE/iIaLqn2bQwXBg8f1g2Ylh6r5MN5CmZvuzZCgsCU=
google.golang.org/genproto v0.0.0-20230227214838-9b19f0bdc514 h1:rtNKfB++wz5mtDY2t5C8TXlU5y52ojSu7tZo0z7u8eQ=
google.golang.org/genproto v0.0.0-20230227214838-9b19f0bdc514/go.mod h1:TvhZT5f700eVlTNwND1xoEZQeWTB2RY/65kplwl/bFA=
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.23.0/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg=
google.golang.org/grpc v1.25.1/go.mod h1:c3i+UQWmh7LiEpx4sFZnkU36qjEYZ0imhYfXVyQciAY=
google.golang.org/grpc v1.27.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk=
google.golang.org/grpc v1.33.2/go.mod h1:JMHMWHQWaTccqQQlmk3MJZS+GWXOdAesneDmEnv2fbc=
google.golang.org/grpc v1.56.3 h1:8I4C0Yq1EjstUzUJzpcRVbuYA2mODtEmpWiQoN/b2nc=
google.golang.org/grpc v1.56.3/go.mod h1:I9bI3vqKfayGqPUAwGdOSu7kt6oIJLixfffKrpXqQ9s=
google.golang.org/grpc v1.53.0 h1:LAv2ds7cmFV/XTS3XG1NneeENYrXGmorPxsBbptIjNc=
google.golang.org/grpc v1.53.0/go.mod h1:OnIrk0ipVdj4N5d9IUoFUx72/VlD7+jUsHwZgwSMQpw=
google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8=
google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0=
google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM=
@@ -367,8 +323,8 @@ google.golang.org/protobuf v1.23.1-0.20200526195155-81db48ad09cc/go.mod h1:EGpAD
google.golang.org/protobuf v1.25.0/go.mod h1:9JNX74DMeImyA3h4bdi1ymwjUzf21/xIlbajtzgsN7c=
google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw=
google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc=
google.golang.org/protobuf v1.33.0 h1:uNO2rsAINq/JlFpSdYEKIZ0uKD/R9cpdv0T+yoGwGmI=
google.golang.org/protobuf v1.33.0/go.mod h1:c6P6GXX6sHbq/GpV6MGZEdwhWPcYBgnhAHhKbcUYpos=
google.golang.org/protobuf v1.28.1 h1:d0NfwRgPtno5B1Wa6L2DAG+KivqkdutMf1UhdNx175w=
google.golang.org/protobuf v1.28.1/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk=

View File

@@ -30,8 +30,8 @@ import (
"errors"
"fmt"
"io"
"io/ioutil"
"net/http"
"os"
"time"
"github.com/dutchcoders/go-clamd"
@@ -50,7 +50,7 @@ func (s *Server) scanHandler(w http.ResponseWriter, r *http.Request) {
s.logger.Printf("Scanning %s %d %s", filename, contentLength, contentType)
file, err := os.CreateTemp(s.tempPath, "clamav-")
file, err := ioutil.TempFile(s.tempPath, "clamav-")
defer s.cleanTmpFile(file)
if err != nil {
s.logger.Printf("%s", err.Error())

View File

@@ -51,15 +51,12 @@ import (
"sync"
textTemplate "text/template"
"time"
"unicode"
"github.com/ProtonMail/go-crypto/openpgp"
"github.com/ProtonMail/go-crypto/openpgp/armor"
"github.com/ProtonMail/go-crypto/openpgp/packet"
"github.com/ProtonMail/gopenpgp/v2/constants"
"github.com/dutchcoders/transfer.sh/server/storage"
"github.com/tg123/go-htpasswd"
"github.com/tomasen/realip"
web "github.com/dutchcoders/transfer.sh-web"
"github.com/gorilla/mux"
@@ -67,9 +64,6 @@ import (
blackfriday "github.com/russross/blackfriday/v2"
qrcode "github.com/skip2/go-qrcode"
"golang.org/x/net/idna"
"golang.org/x/text/runes"
"golang.org/x/text/transform"
"golang.org/x/text/unicode/norm"
)
const getPathPart = "get"
@@ -249,8 +243,6 @@ func canContainsXSS(contentType string) bool {
/* The preview handler will show a preview of the content for browsers (accept type text/html), and referer is not transfer.sh */
func (s *Server) previewHandler(w http.ResponseWriter, r *http.Request) {
w.Header().Set("Vary", "Range, Referer, X-Decrypt-Password")
vars := mux.Vars(r)
token := vars["token"]
@@ -378,7 +370,7 @@ func (s *Server) viewHandler(w http.ResponseWriter, r *http.Request) {
purgeTime := ""
if s.purgeDays > 0 {
purgeTime = formatDurationDays(s.purgeDays)
purgeTime = s.purgeDays.String()
}
data := struct {
@@ -403,7 +395,6 @@ func (s *Server) viewHandler(w http.ResponseWriter, r *http.Request) {
token(s.randomTokenLength),
}
w.Header().Set("Vary", "Accept")
if acceptsHTML(r.Header) {
if err := htmlTemplates.ExecuteTemplate(w, "index.html", data); err != nil {
http.Error(w, err.Error(), http.StatusInternalServerError)
@@ -422,24 +413,7 @@ func (s *Server) notFoundHandler(w http.ResponseWriter, _ *http.Request) {
}
func sanitize(fileName string) string {
t := transform.Chain(
norm.NFD,
runes.Remove(runes.In(unicode.Cc)),
runes.Remove(runes.In(unicode.Cf)),
runes.Remove(runes.In(unicode.Co)),
runes.Remove(runes.In(unicode.Cs)),
runes.Remove(runes.In(unicode.Other)),
runes.Remove(runes.In(unicode.Zl)),
runes.Remove(runes.In(unicode.Zp)),
norm.NFC)
newName, _, err := transform.String(t, fileName)
if err != nil {
return path.Base(fileName)
}
if len(newName) == 0 {
newName = "_"
}
return path.Base(newName)
return path.Base(fileName)
}
func (s *Server) postHandler(w http.ResponseWriter, r *http.Request) {
@@ -771,7 +745,7 @@ func resolveWebAddress(r *http.Request, proxyPath string, proxyPort string) stri
webAddress = fmt.Sprintf("%s://%s/%s",
rUrl.ResolveReference(rUrl).Scheme,
rUrl.ResolveReference(rUrl).Host,
strings.TrimPrefix(proxyPath, "/"))
proxyPath)
}
return webAddress
@@ -1181,7 +1155,6 @@ func (s *Server) headHandler(w http.ResponseWriter, r *http.Request) {
w.Header().Set("Connection", "close")
w.Header().Set("X-Remaining-Downloads", remainingDownloads)
w.Header().Set("X-Remaining-Days", remainingDays)
w.Header().Set("Vary", "Range, Referer, X-Decrypt-Password")
if s.storage.IsRangeSupported() {
w.Header().Set("Accept-Ranges", "bytes")
@@ -1239,7 +1212,7 @@ func (s *Server) getHandler(w http.ResponseWriter, r *http.Request) {
So add text/plain in this case to fix XSS related issues/
*/
if strings.TrimSpace(contentType) == "" {
contentType = "text/plain; charset=utf-8"
contentType = "text/plain"
}
} else {
disposition = "attachment"
@@ -1253,8 +1226,16 @@ func (s *Server) getHandler(w http.ResponseWriter, r *http.Request) {
w.Header().Set("X-Remaining-Downloads", remainingDownloads)
w.Header().Set("X-Remaining-Days", remainingDays)
if rng != nil && rng.ContentRange() != "" {
w.WriteHeader(http.StatusPartialContent)
}
if disposition == "inline" && canContainsXSS(contentType) {
reader = io.NopCloser(bluemonday.UGCPolicy().SanitizeReader(reader))
}
password := r.Header.Get("X-Decrypt-Password")
reader, err = attachDecryptionReader(reader, password)
decryptionReader, err := attachDecryptionReader(reader, password)
if err != nil {
http.Error(w, "Could not decrypt file", http.StatusInternalServerError)
return
@@ -1267,17 +1248,8 @@ func (s *Server) getHandler(w http.ResponseWriter, r *http.Request) {
w.Header().Set("Content-Type", contentType)
w.Header().Set("Content-Length", strconv.FormatUint(contentLength, 10))
w.Header().Set("Vary", "Range, Referer, X-Decrypt-Password")
if rng != nil && rng.ContentRange() != "" {
w.WriteHeader(http.StatusPartialContent)
}
if disposition == "inline" && canContainsXSS(contentType) {
reader = io.NopCloser(bluemonday.UGCPolicy().SanitizeReader(reader))
}
if _, err = io.Copy(w, reader); err != nil {
if _, err = io.Copy(w, decryptionReader); err != nil {
s.logger.Printf("%s", err.Error())
http.Error(w, "Error occurred copying to output stream", http.StatusInternalServerError)
return
@@ -1340,55 +1312,27 @@ func ipFilterHandler(h http.Handler, ipFilterOptions *IPFilterOptions) http.Hand
if ipFilterOptions == nil {
h.ServeHTTP(w, r)
} else {
WrapIPFilter(h, ipFilterOptions).ServeHTTP(w, r)
WrapIPFilter(h, *ipFilterOptions).ServeHTTP(w, r)
}
}
}
func (s *Server) basicAuthHandler(h http.Handler) http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) {
if s.authUser == "" && s.authPass == "" && s.authHtpasswd == "" {
if s.AuthUser == "" || s.AuthPass == "" {
h.ServeHTTP(w, r)
return
}
if s.htpasswdFile == nil && s.authHtpasswd != "" {
htpasswdFile, err := htpasswd.New(s.authHtpasswd, htpasswd.DefaultSystems, nil)
if err != nil {
http.Error(w, err.Error(), http.StatusInternalServerError)
return
}
s.htpasswdFile = htpasswdFile
}
if s.authIPFilter == nil && s.authIPFilterOptions != nil {
s.authIPFilter = newIPFilter(s.authIPFilterOptions)
}
w.Header().Set("WWW-Authenticate", "Basic realm=\"Restricted\"")
var authorized bool
if s.authIPFilter != nil {
remoteIP := realip.FromRequest(r)
authorized = s.authIPFilter.Allowed(remoteIP)
}
username, password, authOK := r.BasicAuth()
if !authOK && !authorized {
if !authOK {
http.Error(w, "Not authorized", http.StatusUnauthorized)
return
}
if !authorized && username == s.authUser && password == s.authPass {
authorized = true
}
if !authorized && s.htpasswdFile != nil {
authorized = s.htpasswdFile.Match(username, password)
}
if !authorized {
if username != s.AuthUser || password != s.AuthPass {
http.Error(w, "Not authorized", http.StatusUnauthorized)
return
}

View File

@@ -26,7 +26,7 @@ func (s *suiteRedirectWithForceHTTPS) SetUpTest(c *C) {
c.Assert(err, IsNil)
handler := http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
_, _ = fmt.Fprintln(w, "Hello, client")
fmt.Fprintln(w, "Hello, client")
})
s.handler = srvr.RedirectHandler(handler)
@@ -83,7 +83,7 @@ func (s *suiteRedirectWithoutForceHTTPS) SetUpTest(c *C) {
c.Assert(err, IsNil)
handler := http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
_, _ = fmt.Fprintln(w, "Hello, client")
fmt.Fprintln(w, "Hello, client")
})
s.handler = srvr.RedirectHandler(handler)

View File

@@ -45,6 +45,7 @@ type IPFilterOptions struct {
// ipFilter
type ipFilter struct {
opts IPFilterOptions
//mut protects the below
//rw since writes are rare
mut sync.RWMutex
@@ -59,12 +60,13 @@ type subnet struct {
allowed bool
}
func newIPFilter(opts *IPFilterOptions) *ipFilter {
func newIPFilter(opts IPFilterOptions) *ipFilter {
if opts.Logger == nil {
flags := log.LstdFlags
opts.Logger = log.New(os.Stdout, "", flags)
}
f := &ipFilter{
opts: opts,
ips: map[string]bool{},
defaultAllowed: !opts.BlockByDefault,
}
@@ -187,7 +189,7 @@ func (f *ipFilter) Wrap(next http.Handler) http.Handler {
}
// 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)
}

View File

@@ -49,7 +49,6 @@ import (
"github.com/VojtechVitek/ratelimit/memory"
gorillaHandlers "github.com/gorilla/handlers"
"github.com/gorilla/mux"
"github.com/tg123/go-htpasswd"
"golang.org/x/crypto/acme/autocert"
web "github.com/dutchcoders/transfer.sh-web"
@@ -144,7 +143,7 @@ func ProfileListener(s string) OptionFn {
func WebPath(s string) OptionFn {
return func(srvr *Server) {
if s[len(s)-1:] != "/" {
s = filepath.Join(s, "")
s = s + string(filepath.Separator)
}
srvr.webPath = s
@@ -155,7 +154,7 @@ func WebPath(s string) OptionFn {
func ProxyPath(s string) OptionFn {
return func(srvr *Server) {
if s[len(s)-1:] != "/" {
s = filepath.Join(s, "")
s = s + string(filepath.Separator)
}
srvr.proxyPath = s
@@ -173,7 +172,7 @@ func ProxyPort(s string) OptionFn {
func TempPath(s string) OptionFn {
return func(srvr *Server) {
if s[len(s)-1:] != "/" {
s = filepath.Join(s, "")
s = s + string(filepath.Separator)
}
srvr.tempPath = s
@@ -274,8 +273,9 @@ func UseLetsEncrypt(hosts []string) OptionFn {
},
}
srvr.tlsConfig = m.TLSConfig()
srvr.tlsConfig.GetCertificate = m.GetCertificate
srvr.tlsConfig = &tls.Config{
GetCertificate: m.GetCertificate,
}
}
}
@@ -294,26 +294,8 @@ func TLSConfig(cert, pk string) OptionFn {
// HTTPAuthCredentials sets basic http auth credentials
func HTTPAuthCredentials(user string, pass string) OptionFn {
return func(srvr *Server) {
srvr.authUser = user
srvr.authPass = pass
}
}
// HTTPAuthHtpasswd sets basic http auth htpasswd file
func HTTPAuthHtpasswd(htpasswdPath string) OptionFn {
return func(srvr *Server) {
srvr.authHtpasswd = htpasswdPath
}
}
// HTTPAUTHFilterOptions sets basic http auth ips whitelist
func HTTPAUTHFilterOptions(options IPFilterOptions) OptionFn {
for i, allowedIP := range options.AllowedIPs {
options.AllowedIPs[i] = strings.TrimSpace(allowedIP)
}
return func(srvr *Server) {
srvr.authIPFilterOptions = &options
srvr.AuthUser = user
srvr.AuthPass = pass
}
}
@@ -334,13 +316,8 @@ func FilterOptions(options IPFilterOptions) OptionFn {
// Server is the main application
type Server struct {
authUser string
authPass string
authHtpasswd string
authIPFilterOptions *IPFilterOptions
htpasswdFile *htpasswd.File
authIPFilter *ipFilter
AuthUser string
AuthPass string
logger *log.Logger
@@ -402,15 +379,12 @@ func New(options ...OptionFn) (*Server, error) {
return s, nil
}
var theRand *rand.Rand
func init() {
var seedBytes [8]byte
if _, err := cryptoRand.Read(seedBytes[:]); err != nil {
panic("cannot obtain cryptographically secure seed")
}
theRand = rand.New(rand.NewSource(int64(binary.LittleEndian.Uint64(seedBytes[:]))))
rand.Seed(int64(binary.LittleEndian.Uint64(seedBytes[:])))
}
// Run starts Server
@@ -436,8 +410,8 @@ func (s *Server) Run() {
fs = http.Dir(s.webPath)
htmlTemplates, _ = htmlTemplates.ParseGlob(filepath.Join(s.webPath, "*.html"))
textTemplates, _ = textTemplates.ParseGlob(filepath.Join(s.webPath, "*.txt"))
htmlTemplates, _ = htmlTemplates.ParseGlob(s.webPath + "*.html")
textTemplates, _ = textTemplates.ParseGlob(s.webPath + "*.txt")
} else {
fs = &assetfs.AssetFS{
Asset: web.Asset,

View File

@@ -5,6 +5,7 @@ import (
"encoding/json"
"fmt"
"io"
"io/ioutil"
"log"
"net/http"
"os"
@@ -34,9 +35,11 @@ const gDriveTokenJSONFile = "token.json"
const gDriveDirectoryMimeType = "application/vnd.google-apps.folder"
// NewGDriveStorage is the factory for GDrive
func NewGDriveStorage(ctx context.Context, clientJSONFilepath string, localConfigPath string, basedir string, chunkSize int, logger *log.Logger) (*GDrive, error) {
func NewGDriveStorage(clientJSONFilepath string, localConfigPath string, basedir string, chunkSize int, logger *log.Logger) (*GDrive, error) {
b, err := os.ReadFile(clientJSONFilepath)
ctx := context.TODO()
b, err := ioutil.ReadFile(clientJSONFilepath)
if err != nil {
return nil, err
}
@@ -66,7 +69,7 @@ func NewGDriveStorage(ctx context.Context, clientJSONFilepath string, localConfi
func (s *GDrive) setupRoot() error {
rootFileConfig := filepath.Join(s.localConfigPath, gDriveRootConfigFile)
rootID, err := os.ReadFile(rootFileConfig)
rootID, err := ioutil.ReadFile(rootFileConfig)
if err != nil && !os.IsNotExist(err) {
return err
}
@@ -87,7 +90,7 @@ func (s *GDrive) setupRoot() error {
}
s.rootID = di.Id
err = os.WriteFile(rootFileConfig, []byte(s.rootID), os.FileMode(0600))
err = ioutil.WriteFile(rootFileConfig, []byte(s.rootID), os.FileMode(0600))
if err != nil {
return err
}

View File

@@ -2,48 +2,38 @@ package storage
import (
"context"
"errors"
"fmt"
"io"
"log"
"time"
"github.com/aws/aws-sdk-go-v2/aws"
"github.com/aws/aws-sdk-go-v2/config"
"github.com/aws/aws-sdk-go-v2/credentials"
"github.com/aws/aws-sdk-go-v2/feature/s3/manager"
"github.com/aws/aws-sdk-go-v2/service/s3"
"github.com/aws/aws-sdk-go-v2/service/s3/types"
"github.com/aws/aws-sdk-go/aws"
"github.com/aws/aws-sdk-go/aws/awserr"
"github.com/aws/aws-sdk-go/aws/credentials"
"github.com/aws/aws-sdk-go/aws/session"
"github.com/aws/aws-sdk-go/service/s3"
"github.com/aws/aws-sdk-go/service/s3/s3manager"
)
// S3Storage is a storage backed by AWS S3
type S3Storage struct {
Storage
bucket string
s3 *s3.Client
session *session.Session
s3 *s3.S3
logger *log.Logger
purgeDays time.Duration
noMultipart bool
}
// NewS3Storage is the factory for S3Storage
func NewS3Storage(ctx context.Context, accessKey, secretKey, bucketName string, purgeDays int, region, endpoint string, disableMultipart bool, forcePathStyle bool, logger *log.Logger) (*S3Storage, error) {
cfg, err := getAwsConfig(ctx, accessKey, secretKey)
if err != nil {
return nil, err
}
client := s3.NewFromConfig(cfg, func(o *s3.Options) {
o.Region = region
o.UsePathStyle = forcePathStyle
if len(endpoint) > 0 {
o.EndpointResolver = s3.EndpointResolverFromURL(endpoint)
}
})
func NewS3Storage(accessKey, secretKey, bucketName string, purgeDays int, region, endpoint string, disableMultipart bool, forcePathStyle bool, logger *log.Logger) (*S3Storage, error) {
sess := getAwsSession(accessKey, secretKey, region, endpoint, forcePathStyle)
return &S3Storage{
bucket: bucketName,
s3: client,
s3: s3.New(sess),
session: sess,
logger: logger,
noMultipart: disableMultipart,
purgeDays: time.Duration(purgeDays*24) * time.Hour,
@@ -65,12 +55,14 @@ func (s *S3Storage) Head(ctx context.Context, token string, filename string) (co
}
// content type , content length
response, err := s.s3.HeadObject(ctx, headRequest)
response, err := s.s3.HeadObjectWithContext(ctx, headRequest)
if err != nil {
return
}
contentLength = uint64(response.ContentLength)
if response.ContentLength != nil {
contentLength = uint64(*response.ContentLength)
}
return
}
@@ -87,8 +79,14 @@ func (s *S3Storage) IsNotExist(err error) bool {
return false
}
var nkerr *types.NoSuchKey
return errors.As(err, &nkerr)
if aerr, ok := err.(awserr.Error); ok {
switch aerr.Code() {
case s3.ErrCodeNoSuchKey:
return true
}
}
return false
}
// Get retrieves a file from storage
@@ -104,12 +102,14 @@ func (s *S3Storage) Get(ctx context.Context, token string, filename string, rng
getRequest.Range = aws.String(rng.Range())
}
response, err := s.s3.GetObject(ctx, getRequest)
response, err := s.s3.GetObjectWithContext(ctx, getRequest)
if err != nil {
return
}
contentLength = uint64(response.ContentLength)
if response.ContentLength != nil {
contentLength = uint64(*response.ContentLength)
}
if rng != nil && response.ContentRange != nil {
rng.SetContentRange(*response.ContentRange)
}
@@ -126,7 +126,7 @@ func (s *S3Storage) Delete(ctx context.Context, token string, filename string) (
Key: aws.String(metadata),
}
_, err = s.s3.DeleteObject(ctx, deleteRequest)
_, err = s.s3.DeleteObjectWithContext(ctx, deleteRequest)
if err != nil {
return
}
@@ -137,7 +137,7 @@ func (s *S3Storage) Delete(ctx context.Context, token string, filename string) (
Key: aws.String(key),
}
_, err = s.s3.DeleteObject(ctx, deleteRequest)
_, err = s.s3.DeleteObjectWithContext(ctx, deleteRequest)
return
}
@@ -155,7 +155,7 @@ func (s *S3Storage) Put(ctx context.Context, token string, filename string, read
}
// Create an uploader with the session and custom options
uploader := manager.NewUploader(s.s3, func(u *manager.Uploader) {
uploader := s3manager.NewUploader(s.session, func(u *s3manager.Uploader) {
u.Concurrency = concurrency // default is 5
u.LeavePartsOnError = false
})
@@ -165,7 +165,7 @@ func (s *S3Storage) Put(ctx context.Context, token string, filename string, read
expire = aws.Time(time.Now().Add(s.purgeDays))
}
_, err = uploader.Upload(ctx, &s3.PutObjectInput{
_, err = uploader.UploadWithContext(ctx, &s3manager.UploadInput{
Bucket: aws.String(s.bucket),
Key: aws.String(key),
Body: reader,
@@ -178,14 +178,11 @@ func (s *S3Storage) Put(ctx context.Context, token string, filename string, read
func (s *S3Storage) IsRangeSupported() bool { return true }
func getAwsConfig(ctx context.Context, accessKey, secretKey string) (aws.Config, error) {
return config.LoadDefaultConfig(ctx,
config.WithCredentialsProvider(credentials.StaticCredentialsProvider{
Value: aws.Credentials{
AccessKeyID: accessKey,
SecretAccessKey: secretKey,
SessionToken: "",
},
}),
)
func getAwsSession(accessKey, secretKey, region, endpoint string, forcePathStyle bool) *session.Session {
return session.Must(session.NewSession(&aws.Config{
Region: aws.String(region),
Endpoint: aws.String(endpoint),
Credentials: credentials.NewStaticCredentials(accessKey, secretKey, ""),
S3ForcePathStyle: aws.Bool(forcePathStyle),
}))
}

View File

@@ -22,11 +22,13 @@ type StorjStorage struct {
}
// NewStorjStorage is the factory for StorjStorage
func NewStorjStorage(ctx context.Context, 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 err error
ctx = fpath.WithTempData(ctx, "", true)
pCtx := context.TODO()
ctx := fpath.WithTempData(pCtx, "", true)
uplConf := &uplink.Config{
UserAgent: "transfer-sh",

View File

@@ -25,7 +25,7 @@ THE SOFTWARE.
package server
import (
"strings"
"math/rand"
)
const (
@@ -35,13 +35,11 @@ const (
// generate a token
func token(length int) string {
var builder strings.Builder
builder.Grow(length)
result := ""
for i := 0; i < length; i++ {
x := theRand.Intn(len(SYMBOLS) - 1)
builder.WriteByte(SYMBOLS[x])
x := rand.Intn(len(SYMBOLS) - 1)
result = string(SYMBOLS[x]) + result
}
return builder.String()
return result
}

View File

@@ -32,7 +32,6 @@ import (
"net/http"
"strconv"
"strings"
"time"
"github.com/golang/gddo/httputil/header"
)
@@ -234,11 +233,3 @@ func formatSize(size int64) string {
getSuffix := suffixes[int(math.Floor(base))]
return fmt.Sprintf("%s %s", strconv.FormatFloat(newVal, 'f', -1, 64), getSuffix)
}
func formatDurationDays(durationDays time.Duration) string {
days := int(durationDays.Hours() / 24)
if days == 1 {
return fmt.Sprintf("%d day", days)
}
return fmt.Sprintf("%d days", days)
}

View File

@@ -30,7 +30,7 @@ import (
"github.com/gorilla/mux"
"github.com/Aetherinox/go-virustotal"
"github.com/dutchcoders/go-virustotal"
)
func (s *Server) virusTotalHandler(w http.ResponseWriter, r *http.Request) {