# Conflicts: # README.md # go.sumtags/v1.2.0
@@ -0,0 +1,89 @@ | |||
name: Deploy multi-architecture Docker images for transfer.sh with buildx | |||
on: | |||
schedule: | |||
- cron: '0 0 * * *' # everyday at midnight UTC | |||
pull_request: | |||
branches: master | |||
push: | |||
branches: master | |||
tags: | |||
- v* | |||
jobs: | |||
buildx: | |||
runs-on: ubuntu-latest | |||
steps: | |||
- | |||
name: Checkout | |||
uses: actions/checkout@v2 | |||
- | |||
name: Prepare | |||
id: prepare | |||
run: | | |||
DOCKER_IMAGE=dutchcoders/transfer.sh | |||
DOCKER_PLATFORMS=linux/amd64,linux/arm/v7,linux/arm64,linux/386 | |||
VERSION=edge | |||
if [[ $GITHUB_REF == refs/tags/* ]]; then | |||
VERSION=${GITHUB_REF#refs/tags/v} | |||
fi | |||
if [ "${{ github.event_name }}" = "schedule" ]; then | |||
VERSION=nightly | |||
fi | |||
TAGS="--tag ${DOCKER_IMAGE}:${VERSION}" | |||
if [ $VERSION = edge -o $VERSION = nightly ]; then | |||
TAGS="$TAGS --tag ${DOCKER_IMAGE}:latest" | |||
fi | |||
echo ::set-output name=docker_image::${DOCKER_IMAGE} | |||
echo ::set-output name=version::${VERSION} | |||
echo ::set-output name=buildx_args::--platform ${DOCKER_PLATFORMS} \ | |||
--build-arg VERSION=${VERSION} \ | |||
--build-arg BUILD_DATE=$(date -u +'%Y-%m-%dT%H:%M:%SZ') \ | |||
--build-arg VCS_REF=${GITHUB_SHA::8} \ | |||
${TAGS} . | |||
- | |||
name: Set up QEMU | |||
uses: docker/setup-qemu-action@v1 | |||
with: | |||
platforms: all | |||
- | |||
name: Set up Docker Buildx | |||
id: buildx | |||
uses: docker/setup-buildx-action@v1 | |||
with: | |||
version: latest | |||
- | |||
name: Available platforms | |||
run: echo ${{ steps.buildx.outputs.platforms }} | |||
- | |||
name: Docker Buildx (build) | |||
run: | | |||
docker buildx build --no-cache --pull --output "type=image,push=false" ${{ steps.prepare.outputs.buildx_args }} | |||
- | |||
name: Docker Login | |||
if: success() && github.event_name != 'pull_request' | |||
env: | |||
DOCKER_USERNAME: ${{ secrets.DOCKER_USERNAME }} | |||
DOCKER_PASSWORD: ${{ secrets.DOCKER_PASSWORD }} | |||
run: | | |||
echo "${DOCKER_PASSWORD}" | docker login --username "${DOCKER_USERNAME}" --password-stdin | |||
- | |||
name: Docker Buildx (push) | |||
if: success() && github.event_name != 'pull_request' | |||
run: | | |||
docker buildx build --output "type=image,push=true" ${{ steps.prepare.outputs.buildx_args }} | |||
- | |||
name: Docker Check Manifest | |||
if: always() && github.event_name != 'pull_request' | |||
run: | | |||
docker run --rm mplatform/mquery ${{ steps.prepare.outputs.docker_image }}:${{ steps.prepare.outputs.version }} | |||
- | |||
name: Clear | |||
if: always() && github.event_name != 'pull_request' | |||
run: | | |||
rm -f ${HOME}/.docker/config.json |
@@ -12,7 +12,7 @@ WORKDIR /go/src/github.com/dutchcoders/transfer.sh | |||
ENV GO111MODULE=on | |||
# build & install server | |||
RUN go get -u ./... && CGO_ENABLED=0 GOOS=linux GOARCH=amd64 go build -ldflags -a -tags netgo -ldflags '-w -extldflags "-static"' -o /go/bin/transfersh github.com/dutchcoders/transfer.sh | |||
RUN go get -u ./... && CGO_ENABLED=0 go build -ldflags -a -tags netgo -ldflags '-w -extldflags "-static"' -o /go/bin/transfersh github.com/dutchcoders/transfer.sh | |||
FROM scratch AS final | |||
LABEL maintainer="Andrea Spacca <andrea.spacca@gmail.com>" | |||
@@ -76,42 +76,46 @@ https://transfer.sh/1lDau/test.txt --> https://transfer.sh/inline/1lDau/test.txt | |||
Parameter | Description | Value | Env | |||
--- | --- | --- | --- | |||
listener | port to use for http (:80) | | | |||
profile-listener | port to use for profiler (:6060)| | | |||
force-https | redirect to https | false | | |||
tls-listener | port to use for https (:443) | | | |||
tls-listener-only | flag to enable tls listener only | | | |||
tls-cert-file | path to tls certificate | | | |||
tls-private-key | path to tls private key | | | |||
http-auth-user | user for basic http auth on upload | | | |||
http-auth-pass | pass for basic http auth on upload | | | |||
ip-whitelist | comma separated list of ips allowed to connect to the service | | | |||
ip-blacklist | comma separated list of ips not allowed to connect to the service | | | |||
temp-path | path to temp folder | system temp | | |||
web-path | path to static web files (for development or custom front end) | | | |||
proxy-path | path prefix when service is run behind a proxy | | | |||
ga-key | google analytics key for the front end | | | |||
uservoice-key | user voice key for the front end | | | |||
listener | port to use for http (:80) | | LISTENER | | |||
profile-listener | port to use for profiler (:6060) | | PROFILE_LISTENER | | |||
force-https | redirect to https | false | FORCE_HTTPS | |||
tls-listener | port to use for https (:443) | | TLS_LISTENER | | |||
tls-listener-only | flag to enable tls listener only | | TLS_LISTENER_ONLY | | |||
tls-cert-file | path to tls certificate | | TLS_CERT_FILE | | |||
tls-private-key | path to tls private key | | TLS_PRIVATE_KEY | | |||
http-auth-user | user for basic http auth on upload | | HTTP_AUTH_USER | | |||
http-auth-pass | pass for basic http auth on upload | | HTTP_AUTH_PASS | | |||
ip-whitelist | comma separated list of ips allowed to connect to the service | | IP_WHITELIST | | |||
ip-blacklist | comma separated list of ips not allowed to connect to the service | | IP_BLACKLIST | | |||
temp-path | path to temp folder | system temp | TEMP_PATH | | |||
web-path | path to static web files (for development or custom front end) | | WEB_PATH | | |||
proxy-path | path prefix when service is run behind a proxy | | PROXY_PATH | | |||
proxy-port | port of the proxy when the service is run behind a proxy | | PROXY_PORT | | |||
ga-key | google analytics key for the front end | | GA_KEY | | |||
provider | which storage provider to use | (s3, storj, gdrive or local) | | |||
aws-access-key | aws access key | | AWS_ACCESS_KEY | |||
aws-secret-key | aws access key | | AWS_SECRET_KEY | |||
bucket | aws bucket | | BUCKET | |||
s3-endpoint | Custom S3 endpoint. | | | |||
s3-region | region of the s3 bucket | eu-west-1 | S3_REGION | |||
s3-no-multipart | disables s3 multipart upload | false | | | |||
s3-path-style | Forces path style URLs, required for Minio. | false | | | |||
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| | | |||
gdrive-client-json-filepath | path to oauth client json config for gdrive provider| | | |||
gdrive-local-config-path | path to store local transfer.sh config cache for gdrive provider| | | |||
gdrive-chunk-size | chunk size for gdrive upload in megabytes, must be lower than available memory (8 MB) | | | |||
lets-encrypt-hosts | hosts to use for lets encrypt certificates (comma seperated) | | | |||
log | path to log file| | | |||
uservoice-key | user voice key for the front end | | USERVOICE_KEY | | |||
aws-access-key | aws access key | | AWS_ACCESS_KEY | | |||
aws-secret-key | aws access key | | AWS_SECRET_KEY | | |||
bucket | aws bucket | | BUCKET | | |||
s3-endpoint | Custom S3 endpoint. | | S3_ENDPOINT | | |||
s3-region | region of the s3 bucket | eu-west-1 | S3_REGION | | |||
s3-no-multipart | disables s3 multipart upload | false | S3_NO_MULTIPART | | |||
s3-path-style | Forces path style URLs, required for Minio. | false | S3_PATH_STYLE | | |||
storj-access | Access for the project | | STORJ_ACCESS | | |||
storj-bucket | Bucket to use within the project | | STORJ_BUCKET | | |||
basedir | path storage for local/gdrive provider | | BASEDIR | | |||
gdrive-client-json-filepath | path to oauth client json config for gdrive provider | | GDRIVE_CLIENT_JSON_FILEPATH | | |||
gdrive-local-config-path | path to store local transfer.sh config cache for gdrive provider| | GDRIVE_LOCAL_CONFIG_PATH | | |||
gdrive-chunk-size | chunk size for gdrive upload in megabytes, must be lower than available memory (8 MB) | | GDRIVE_CHUNK_SIZE | | |||
lets-encrypt-hosts | hosts to use for lets encrypt certificates (comma seperated) | | HOSTS | | |||
log | path to log file| | LOG | | |||
cors-domains | comma separated list of domains for CORS, setting it enable CORS | | CORS_DOMAINS | | |||
clamav-host | host for clamav feature | | CLAMAV_HOST | | |||
rate-limit | request per minute | | RATE_LIMIT | | |||
If you want to use TLS using lets encrypt certificates, set lets-encrypt-hosts to your domain, set tls-listener to :443 and enable force-https. | |||
If you want to use TLS using your own certificates, set tls-listener to :443, force-https, tls-cert=file and tls-private-key. | |||
If you want to use TLS using your own certificates, set tls-listener to :443, force-https, tls-cert-file and tls-private-key. | |||
## Development | |||
@@ -12,7 +12,7 @@ import ( | |||
"google.golang.org/api/googleapi" | |||
) | |||
var Version = "1.1.4" | |||
var Version = "1.1.7" | |||
var helpTemplate = `NAME: | |||
{{.Name}} - {{.Usage}} | |||
@@ -37,6 +37,7 @@ var globalFlags = []cli.Flag{ | |||
Name: "listener", | |||
Usage: "127.0.0.1:8080", | |||
Value: "127.0.0.1:8080", | |||
EnvVar: "LISTENER", | |||
}, | |||
// redirect to https? | |||
// hostnames | |||
@@ -44,57 +45,75 @@ var globalFlags = []cli.Flag{ | |||
Name: "profile-listener", | |||
Usage: "127.0.0.1:6060", | |||
Value: "", | |||
EnvVar: "PROFILE_LISTENER", | |||
}, | |||
cli.BoolFlag{ | |||
Name: "force-https", | |||
Usage: "", | |||
EnvVar: "FORCE_HTTPS", | |||
}, | |||
cli.StringFlag{ | |||
Name: "tls-listener", | |||
Usage: "127.0.0.1:8443", | |||
Value: "", | |||
EnvVar: "TLS_LISTENER", | |||
}, | |||
cli.BoolFlag{ | |||
Name: "tls-listener-only", | |||
Usage: "", | |||
EnvVar: "TLS_LISTENER_ONLY", | |||
}, | |||
cli.StringFlag{ | |||
Name: "tls-cert-file", | |||
Value: "", | |||
EnvVar: "TLS_CERT_FILE", | |||
}, | |||
cli.StringFlag{ | |||
Name: "tls-private-key", | |||
Value: "", | |||
EnvVar: "TLS_PRIVATE_KEY", | |||
}, | |||
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: "", | |||
EnvVar: "WEB_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: "", | |||
EnvVar: "PROXY_PORT", | |||
}, | |||
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: "", | |||
EnvVar: "USERVOICE_KEY", | |||
}, | |||
cli.StringFlag{ | |||
Name: "provider", | |||
Usage: "s3|gdrive|local", | |||
Value: "", | |||
EnvVar: "PROVIDER", | |||
}, | |||
cli.StringFlag{ | |||
Name: "s3-endpoint", | |||
@@ -129,25 +148,30 @@ var globalFlags = []cli.Flag{ | |||
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.", | |||
EnvVar: "S3_PATH_STYLE", | |||
}, | |||
cli.StringFlag{ | |||
Name: "gdrive-client-json-filepath", | |||
Usage: "", | |||
Value: "", | |||
EnvVar: "GDRIVE_CLIENT_JSON_FILEPATH", | |||
}, | |||
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, | |||
EnvVar: "GDRIVE_CHUNK_SIZE", | |||
}, | |||
cli.StringFlag{ | |||
Name: "storj-access", | |||
@@ -165,7 +189,7 @@ var globalFlags = []cli.Flag{ | |||
Name: "rate-limit", | |||
Usage: "requests per minute", | |||
Value: 0, | |||
EnvVar: "", | |||
EnvVar: "RATE_LIMIT", | |||
}, | |||
cli.StringFlag{ | |||
Name: "lets-encrypt-hosts", | |||
@@ -177,11 +201,13 @@ var globalFlags = []cli.Flag{ | |||
Name: "log", | |||
Usage: "/var/log/transfersh.log", | |||
Value: "", | |||
EnvVar: "LOG", | |||
}, | |||
cli.StringFlag{ | |||
Name: "basedir", | |||
Usage: "path to storage", | |||
Value: "", | |||
EnvVar: "BASEDIR", | |||
}, | |||
cli.StringFlag{ | |||
Name: "clamav-host", | |||
@@ -198,26 +224,37 @@ var globalFlags = []cli.Flag{ | |||
cli.BoolFlag{ | |||
Name: "profiler", | |||
Usage: "enable profiling", | |||
EnvVar: "PROFILER", | |||
}, | |||
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: "", | |||
EnvVar: "HTTP_AUTH_PASS", | |||
}, | |||
cli.StringFlag{ | |||
Name: "ip-whitelist", | |||
Usage: "comma separated list of ips allowed to connect to the service", | |||
Value: "", | |||
EnvVar: "IP_WHITELIST", | |||
}, | |||
cli.StringFlag{ | |||
Name: "ip-blacklist", | |||
Usage: "comma separated list of ips not allowed to connect to the service", | |||
Value: "", | |||
EnvVar: "IP_BLACKLIST", | |||
}, | |||
cli.StringFlag{ | |||
Name: "cors-domains", | |||
Usage: "comma separated list of domains allowed for CORS requests", | |||
Value: "", | |||
EnvVar: "CORS_DOMAINS", | |||
}, | |||
} | |||
@@ -226,7 +263,7 @@ type Cmd struct { | |||
} | |||
func VersionAction(c *cli.Context) { | |||
fmt.Println(color.YellowString(fmt.Sprintf("transfer.sh: Easy file sharing from the command line"))) | |||
fmt.Println(color.YellowString(fmt.Sprintf("transfer.sh %s: Easy file sharing from the command line", Version))) | |||
} | |||
func New() *Cmd { | |||
@@ -257,6 +294,10 @@ func New() *Cmd { | |||
options = append(options, server.Listener(v)) | |||
} | |||
if v := c.String("cors-domains"); v != "" { | |||
options = append(options, server.CorsDomains(v)) | |||
} | |||
if v := c.String("tls-listener"); v == "" { | |||
} else if c.Bool("tls-listener-only") { | |||
options = append(options, server.TLSListener(v, true)) | |||
@@ -276,6 +317,10 @@ func New() *Cmd { | |||
options = append(options, server.ProxyPath(v)) | |||
} | |||
if v := c.String("proxy-port"); v != "" { | |||
options = append(options, server.ProxyPort(v)) | |||
} | |||
if v := c.String("ga-key"); v != "" { | |||
options = append(options, server.GoogleAnalytics(v)) | |||
} | |||
@@ -16,6 +16,7 @@ require ( | |||
github.com/garyburd/redigo v1.6.0 // indirect | |||
github.com/golang/gddo v0.0.0-20200310004957-95ce5a452273 | |||
github.com/golang/protobuf v1.3.5 // indirect | |||
github.com/gorilla/handlers v1.4.2 | |||
github.com/gorilla/mux v1.7.4 | |||
github.com/gorilla/securecookie v1.1.1 // indirect | |||
github.com/jmespath/go-jmespath v0.3.0 // indirect | |||
@@ -136,6 +136,8 @@ github.com/googleapis/gax-go v2.0.0+incompatible/go.mod h1:SFVmujtThgffbyetf+mdk | |||
github.com/googleapis/gax-go/v2 v2.0.4/go.mod h1:0Wqv26UfaUD9n4G6kQubkQ+KchISgw+vpHVxEJEs9eg= | |||
github.com/googleapis/gax-go/v2 v2.0.5 h1:sjZBwGj9Jlw33ImPtvFviGYvseOtDM7hkSKB7+Tv3SM= | |||
github.com/googleapis/gax-go/v2 v2.0.5/go.mod h1:DWXyrwAJ9X0FpwwEdw+IPEYBICEFu5mhpdKc/us6bOk= | |||
github.com/gorilla/handlers v1.4.2 h1:0QniY0USkHQ1RGCLfKxeNHK9bkDHGRYGNDFBCS+YARg= | |||
github.com/gorilla/handlers v1.4.2/go.mod h1:Qkdc/uu4tH4g6mTK6auzZ766c4CA0Ng8+o/OAirnOIQ= | |||
github.com/gorilla/mux v1.7.4 h1:VuZ8uybHlWmqV03+zRzdwKL4tUnIp1MAQtp1mIFE1bc= | |||
github.com/gorilla/mux v1.7.4/go.mod h1:DVbg23sWSpFRCP0SfiEN6jmj59UnW/n46BH5rLB71So= | |||
github.com/gorilla/securecookie v1.1.1 h1:miw7JPhV+b/lAHSXz4qd/nN9jRiAFV5FwjeKyCS8BvQ= | |||
@@ -158,9 +158,9 @@ func (s *Server) previewHandler(w http.ResponseWriter, r *http.Request) { | |||
} | |||
relativeURL, _ := url.Parse(path.Join(s.proxyPath, token, filename)) | |||
resolvedURL := resolveURL(r, relativeURL) | |||
resolvedURL := resolveURL(r, relativeURL, s.proxyPort) | |||
relativeURLGet, _ := url.Parse(path.Join(s.proxyPath, getPathPart, token, filename)) | |||
resolvedURLGet := resolveURL(r, relativeURLGet) | |||
resolvedURLGet := resolveURL(r, relativeURLGet, s.proxyPort) | |||
var png []byte | |||
png, err = qrcode.Encode(resolvedURL, qrcode.High, 150) | |||
if err != nil { | |||
@@ -170,8 +170,8 @@ func (s *Server) previewHandler(w http.ResponseWriter, r *http.Request) { | |||
qrCode := base64.StdEncoding.EncodeToString(png) | |||
hostname := getURL(r).Host | |||
webAddress := resolveWebAddress(r, s.proxyPath) | |||
hostname := getURL(r, s.proxyPort).Host | |||
webAddress := resolveWebAddress(r, s.proxyPath, s.proxyPort) | |||
data := struct { | |||
ContentType string | |||
@@ -212,8 +212,8 @@ func (s *Server) previewHandler(w http.ResponseWriter, r *http.Request) { | |||
func (s *Server) viewHandler(w http.ResponseWriter, r *http.Request) { | |||
// vars := mux.Vars(r) | |||
hostname := getURL(r).Host | |||
webAddress := resolveWebAddress(r, s.proxyPath) | |||
hostname := getURL(r, s.proxyPort).Host | |||
webAddress := resolveWebAddress(r, s.proxyPath, s.proxyPort) | |||
data := struct { | |||
Hostname string | |||
@@ -339,7 +339,7 @@ func (s *Server) postHandler(w http.ResponseWriter, r *http.Request) { | |||
filename = url.PathEscape(filename) | |||
relativeURL, _ := url.Parse(path.Join(s.proxyPath, token, filename)) | |||
fmt.Fprintln(w, getURL(r).ResolveReference(relativeURL).String()) | |||
fmt.Fprintln(w, getURL(r, s.proxyPort).ResolveReference(relativeURL).String()) | |||
cleanTmpFile(file) | |||
} | |||
@@ -500,15 +500,15 @@ func (s *Server) putHandler(w http.ResponseWriter, r *http.Request) { | |||
relativeURL, _ := url.Parse(path.Join(s.proxyPath, token, filename)) | |||
deleteURL, _ := url.Parse(path.Join(s.proxyPath, token, filename, metadata.DeletionToken)) | |||
w.Header().Set("X-Url-Delete", resolveURL(r, deleteURL)) | |||
w.Header().Set("X-Url-Delete", resolveURL(r, deleteURL, s.proxyPort)) | |||
fmt.Fprint(w, resolveURL(r, relativeURL)) | |||
fmt.Fprint(w, resolveURL(r, relativeURL, s.proxyPort)) | |||
} | |||
func resolveURL(r *http.Request, u *url.URL) string { | |||
func resolveURL(r *http.Request, u *url.URL, proxyPort string) string { | |||
r.URL.Path = "" | |||
return getURL(r).ResolveReference(u).String() | |||
return getURL(r, proxyPort).ResolveReference(u).String() | |||
} | |||
func resolveKey(key, proxyPath string) string { | |||
@@ -525,8 +525,8 @@ func resolveKey(key, proxyPath string) string { | |||
return key | |||
} | |||
func resolveWebAddress(r *http.Request, proxyPath string) string { | |||
url := getURL(r) | |||
func resolveWebAddress(r *http.Request, proxyPath string, proxyPort string) string { | |||
url := getURL(r, proxyPort) | |||
var webAddress string | |||
@@ -544,7 +544,7 @@ func resolveWebAddress(r *http.Request, proxyPath string) string { | |||
return webAddress | |||
} | |||
func getURL(r *http.Request) *url.URL { | |||
func getURL(r *http.Request, proxyPort string) *url.URL { | |||
u, _ := url.Parse(r.URL.String()) | |||
if r.TLS != nil { | |||
@@ -555,16 +555,25 @@ func getURL(r *http.Request) *url.URL { | |||
u.Scheme = "http" | |||
} | |||
if u.Host != "" { | |||
} else if host, port, err := net.SplitHostPort(r.Host); err != nil { | |||
u.Host = r.Host | |||
} else { | |||
if port == "80" && u.Scheme == "http" { | |||
u.Host = host | |||
} else if port == "443" && u.Scheme == "https" { | |||
if u.Host == "" { | |||
host, port, err := net.SplitHostPort(r.Host) | |||
if err != nil { | |||
host = r.Host | |||
port = "" | |||
} | |||
if len(proxyPort) != 0 { | |||
port = proxyPort | |||
} | |||
if len(port) == 0 { | |||
u.Host = host | |||
} else { | |||
u.Host = net.JoinHostPort(host, port) | |||
if port == "80" && u.Scheme == "http" { | |||
u.Host = host | |||
} else if port == "443" && u.Scheme == "https" { | |||
u.Host = host | |||
} else { | |||
u.Host = net.JoinHostPort(host, port) | |||
} | |||
} | |||
} | |||
@@ -614,9 +623,7 @@ func (s *Server) CheckMetadata(token, filename string, increaseDownload bool) (M | |||
var metadata Metadata | |||
r, _, err := s.storage.Get(token, fmt.Sprintf("%s.metadata", filename)) | |||
if s.storage.IsNotExist(err) { | |||
return metadata, nil | |||
} else if err != nil { | |||
if err != nil { | |||
return metadata, err | |||
} | |||
@@ -1011,7 +1018,7 @@ func (s *Server) RedirectHandler(h http.Handler) http.HandlerFunc { | |||
} else if r.Header.Get("X-Forwarded-Proto") == "https" { | |||
} else if r.URL.Scheme == "https" { | |||
} else { | |||
u := getURL(r) | |||
u := getURL(r, s.proxyPort) | |||
u.Scheme = "https" | |||
http.Redirect(w, r, u.String(), http.StatusPermanentRedirect) | |||
@@ -26,7 +26,10 @@ package server | |||
import ( | |||
"errors" | |||
gorillaHandlers "github.com/gorilla/handlers" | |||
"log" | |||
crypto_rand "crypto/rand" | |||
"encoding/binary" | |||
"math/rand" | |||
"mime" | |||
"net/http" | |||
@@ -85,6 +88,13 @@ func Listener(s string) OptionFn { | |||
} | |||
func CorsDomains(s string) OptionFn { | |||
return func(srvr *Server) { | |||
srvr.CorsDomains = s | |||
} | |||
} | |||
func GoogleAnalytics(gaKey string) OptionFn { | |||
return func(srvr *Server) { | |||
srvr.gaKey = gaKey | |||
@@ -131,6 +141,12 @@ func ProxyPath(s string) OptionFn { | |||
} | |||
} | |||
func ProxyPort(s string) OptionFn { | |||
return func(srvr *Server) { | |||
srvr.proxyPort = s | |||
} | |||
} | |||
func TempPath(s string) OptionFn { | |||
return func(srvr *Server) { | |||
if s[len(s)-1:] != "/" { | |||
@@ -270,11 +286,13 @@ type Server struct { | |||
webPath string | |||
proxyPath string | |||
proxyPort string | |||
gaKey string | |||
userVoiceKey string | |||
TLSListenerOnly bool | |||
CorsDomains string | |||
ListenerString string | |||
TLSListenerString string | |||
ProfileListenerString string | |||
@@ -297,7 +315,11 @@ func New(options ...OptionFn) (*Server, error) { | |||
} | |||
func init() { | |||
rand.Seed(time.Now().UTC().UnixNano()) | |||
var seedBytes [8]byte | |||
if _, err := crypto_rand.Read(seedBytes[:]); err != nil { | |||
panic("cannot obtain cryptographically secure seed") | |||
} | |||
rand.Seed(int64(binary.LittleEndian.Uint64(seedBytes[:]))) | |||
} | |||
func (s *Server) Run() { | |||
@@ -413,11 +435,24 @@ func (s *Server) Run() { | |||
s.logger.Printf("Transfer.sh server started.\nusing temp folder: %s\nusing storage provider: %s", s.tempPath, s.storage.Type()) | |||
var cors func(http.Handler) http.Handler | |||
if len(s.CorsDomains) > 0 { | |||
cors = gorillaHandlers.CORS( | |||
gorillaHandlers.AllowedHeaders([]string{"*"}), | |||
gorillaHandlers.AllowedOrigins(strings.Split(s.CorsDomains, ",")), | |||
gorillaHandlers.AllowedMethods([]string{"GET", "HEAD", "POST", "PUT", "DELETE", "OPTIONS"}), | |||
) | |||
} else { | |||
cors = func(h http.Handler) http.Handler { | |||
return h | |||
} | |||
} | |||
h := handlers.PanicHandler( | |||
IPFilterHandler( | |||
handlers.LogHandler( | |||
LoveHandler( | |||
s.RedirectHandler(r)), | |||
s.RedirectHandler(cors(r))), | |||
handlers.NewLogOptions(s.logger.Printf, "_default_"), | |||
), | |||
s.ipFilterOptions, | |||