Multiple fixes

This commit is contained in:
Stefan Benten 2021-07-26 10:59:28 +02:00 committed by GitHub
commit f38eb6fb77
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
16 changed files with 218 additions and 200 deletions

View file

@ -6,7 +6,6 @@ bin
*.pyc *.pyc
*.egg-info *.egg-info
.vagrant .vagrant
.git
.tmp .tmp
bower_components bower_components
node_modules node_modules

View file

@ -35,7 +35,7 @@ jobs:
CGO_ENABLED: 0 CGO_ENABLED: 0
run: | run: |
go version go version
go build -tags netgo -ldflags '-a -s -w -extldflags "-static"' -o ./artifacts/transfersh-${GITHUB_REF##*/}-${{ matrix.suffix }} go build -tags netgo -ldflags "-X github.com/dutchcoders/transfer.sh/cmd.Version=${GITHUB_REF##*/} -a -s -w -extldflags '-static'" -o ./artifacts/transfersh-${GITHUB_REF##*/}-${{ matrix.suffix }}
- uses: actions/upload-artifact@v2 - uses: actions/upload-artifact@v2
name: Upload artifacts name: Upload artifacts
with: with:

View file

@ -1,5 +1,5 @@
# Default to Go 1.15 # Default to Go 1.16
ARG GO_VERSION=1.15 ARG GO_VERSION=1.16
FROM golang:${GO_VERSION}-alpine as build FROM golang:${GO_VERSION}-alpine as build
# Necessary to run 'go get' and to compile the linked binary # Necessary to run 'go get' and to compile the linked binary
@ -12,7 +12,7 @@ WORKDIR /go/src/github.com/dutchcoders/transfer.sh
ENV GO111MODULE=on ENV GO111MODULE=on
# build & install server # build & install server
RUN go get -u ./... && CGO_ENABLED=0 go build -tags netgo -ldflags '-a -s -w -extldflags "-static"' -o /go/bin/transfersh github.com/dutchcoders/transfer.sh RUN CGO_ENABLED=0 go build -tags netgo -ldflags "-X github.com/dutchcoders/transfer.sh/cmd.Version=$(git describe --tags) -a -s -w -extldflags '-static'" -o /go/bin/transfersh
FROM scratch AS final FROM scratch AS final
LABEL maintainer="Andrea Spacca <andrea.spacca@gmail.com>" LABEL maintainer="Andrea Spacca <andrea.spacca@gmail.com>"

View file

@ -1,6 +1,8 @@
The MIT License (MIT) The MIT License (MIT)
Copyright (c) 2014-2018 DutchCoders [https://github.com/dutchcoders/] Copyright (c) 2014-2018 DutchCoders [https://github.com/dutchcoders/]
Copyright (c) 2018-2020 Andrea Spacca.
Copyright (c) 2020- Andrea Spacca and Stefan Benten.
Permission is hereby granted, free of charge, to any person obtaining a copy Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal of this software and associated documentation files (the "Software"), to deal

View file

@ -224,4 +224,7 @@ Contributions are welcome.
## Copyright and license ## Copyright and license
Code and documentation copyright 2011-2018 Remco Verhoef. Code and documentation copyright 2011-2018 Remco Verhoef.
Code and documentation copyright 2018-2020 Andrea Spacca.
Code and documentation copyright 2020- Andrea Spacca and Stefan Benten.
Code released under [the MIT license](LICENSE). Code released under [the MIT license](LICENSE).

View file

@ -12,7 +12,7 @@ import (
"google.golang.org/api/googleapi" "google.golang.org/api/googleapi"
) )
var Version = "1.2.4" var Version = "0.0.0"
var helpTemplate = `NAME: var helpTemplate = `NAME:
{{.Name}} - {{.Usage}} {{.Name}} - {{.Usage}}
@ -274,7 +274,7 @@ var globalFlags = []cli.Flag{
Value: "", Value: "",
EnvVar: "CORS_DOMAINS", EnvVar: "CORS_DOMAINS",
}, },
cli.Int64Flag{ cli.IntFlag{
Name: "random-token-length", Name: "random-token-length",
Usage: "", Usage: "",
Value: 6, Value: 6,
@ -383,7 +383,7 @@ func New() *Cmd {
options = append(options, server.RateLimit(v)) options = append(options, server.RateLimit(v))
} }
v := c.Int64("random-token-length") v := c.Int("random-token-length")
options = append(options, server.RandomTokenLength(v)) options = append(options, server.RandomTokenLength(v))
purgeDays := c.Int("purge-days") purgeDays := c.Int("purge-days")

2
go.mod
View file

@ -11,7 +11,7 @@ require (
github.com/cpuguy83/go-md2man/v2 v2.0.0 // indirect github.com/cpuguy83/go-md2man/v2 v2.0.0 // indirect
github.com/dutchcoders/go-clamd v0.0.0-20170520113014-b970184f4d9e github.com/dutchcoders/go-clamd v0.0.0-20170520113014-b970184f4d9e
github.com/dutchcoders/go-virustotal v0.0.0-20140923143438-24cc8e6fa329 github.com/dutchcoders/go-virustotal v0.0.0-20140923143438-24cc8e6fa329
github.com/dutchcoders/transfer.sh-web v0.0.0-20210212072623-ac7014a9c3a7 github.com/dutchcoders/transfer.sh-web v0.0.0-20210723094506-f0946ebceb7a
github.com/elazarl/go-bindata-assetfs v1.0.1 github.com/elazarl/go-bindata-assetfs v1.0.1
github.com/fatih/color v1.10.0 github.com/fatih/color v1.10.0
github.com/garyburd/redigo v1.6.2 // indirect github.com/garyburd/redigo v1.6.2 // indirect

9
go.sum
View file

@ -92,6 +92,14 @@ github.com/dutchcoders/go-virustotal v0.0.0-20140923143438-24cc8e6fa329 h1:ERqCk
github.com/dutchcoders/go-virustotal v0.0.0-20140923143438-24cc8e6fa329/go.mod h1:G5qOfE5bQZ5scycLpB7fYWgN4y3xdfXo+pYWM8z2epY= github.com/dutchcoders/go-virustotal v0.0.0-20140923143438-24cc8e6fa329/go.mod h1:G5qOfE5bQZ5scycLpB7fYWgN4y3xdfXo+pYWM8z2epY=
github.com/dutchcoders/transfer.sh-web v0.0.0-20210212072623-ac7014a9c3a7 h1:zKJw+RxTDbypWVb0HfPs43bj/ee65Bl7rIDPO7HixrQ= github.com/dutchcoders/transfer.sh-web v0.0.0-20210212072623-ac7014a9c3a7 h1:zKJw+RxTDbypWVb0HfPs43bj/ee65Bl7rIDPO7HixrQ=
github.com/dutchcoders/transfer.sh-web v0.0.0-20210212072623-ac7014a9c3a7/go.mod h1:jTzXZabwihvQgvmySgD4f4GNszimkXK3o8x1ucH1z5Q= github.com/dutchcoders/transfer.sh-web v0.0.0-20210212072623-ac7014a9c3a7/go.mod h1:jTzXZabwihvQgvmySgD4f4GNszimkXK3o8x1ucH1z5Q=
github.com/dutchcoders/transfer.sh-web v0.0.0-20210717081259-8b8af59a0fae h1:JalbO1PKAsbSYQBW6Q4aXbgj2w4bWofdrHxRRwqRgDc=
github.com/dutchcoders/transfer.sh-web v0.0.0-20210717081259-8b8af59a0fae/go.mod h1:F6Q37CxDh2MHr5KXkcZmNB3tdkK7v+bgE+OpBY+9ilI=
github.com/dutchcoders/transfer.sh-web v0.0.0-20210723091746-c17d678a22f3 h1:CM9FGBPXLXhvKo0TuO4CKKdZLah6esP1SAfPzZDjEB0=
github.com/dutchcoders/transfer.sh-web v0.0.0-20210723091746-c17d678a22f3/go.mod h1:F6Q37CxDh2MHr5KXkcZmNB3tdkK7v+bgE+OpBY+9ilI=
github.com/dutchcoders/transfer.sh-web v0.0.0-20210723093538-596fe60a5dd5 h1:M6GI6DvsFgBGpp3+V+VB5okP1BGXISltz7acVYWWFOQ=
github.com/dutchcoders/transfer.sh-web v0.0.0-20210723093538-596fe60a5dd5/go.mod h1:F6Q37CxDh2MHr5KXkcZmNB3tdkK7v+bgE+OpBY+9ilI=
github.com/dutchcoders/transfer.sh-web v0.0.0-20210723094506-f0946ebceb7a h1:+N7J1NK7gxKZ+X4syY1HqafUudJiR8voJGcXWkxLgAw=
github.com/dutchcoders/transfer.sh-web v0.0.0-20210723094506-f0946ebceb7a/go.mod h1:F6Q37CxDh2MHr5KXkcZmNB3tdkK7v+bgE+OpBY+9ilI=
github.com/elazarl/go-bindata-assetfs v1.0.1 h1:m0kkaHRKEu7tUIUFVwhGGGYClXvyl4RE03qmvRTNfbw= github.com/elazarl/go-bindata-assetfs v1.0.1 h1:m0kkaHRKEu7tUIUFVwhGGGYClXvyl4RE03qmvRTNfbw=
github.com/elazarl/go-bindata-assetfs v1.0.1/go.mod h1:v+YaWX3bdea5J/mo8dSETolEo7R71Vk1u8bnjau5yw4= github.com/elazarl/go-bindata-assetfs v1.0.1/go.mod h1:v+YaWX3bdea5J/mo8dSETolEo7R71Vk1u8bnjau5yw4=
github.com/envoyproxy/go-control-plane v0.9.0/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= github.com/envoyproxy/go-control-plane v0.9.0/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4=
@ -249,6 +257,7 @@ github.com/russross/blackfriday/v2 v2.0.1 h1:lPqVAte+HuHNfhJ/0LC98ESWRz8afy9tM/0
github.com/russross/blackfriday/v2 v2.0.1/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= github.com/russross/blackfriday/v2 v2.0.1/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=
github.com/russross/blackfriday/v2 v2.1.0 h1:JIOH55/0cWyOuilr9/qlrm0BSXldqnqwMsf35Ld67mk= github.com/russross/blackfriday/v2 v2.1.0 h1:JIOH55/0cWyOuilr9/qlrm0BSXldqnqwMsf35Ld67mk=
github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=
github.com/shuLhan/go-bindata v4.0.0+incompatible/go.mod h1:pkcPAATLBDD2+SpAPnX5vEM90F7fcwHCvvLCMXcmw3g=
github.com/shurcooL/sanitized_anchor_name v1.0.0 h1:PdmoCO6wvbs+7yrJyMORt4/BmY5IYyJwS/kOiWx8mHo= github.com/shurcooL/sanitized_anchor_name v1.0.0 h1:PdmoCO6wvbs+7yrJyMORt4/BmY5IYyJwS/kOiWx8mHo=
github.com/shurcooL/sanitized_anchor_name v1.0.0/go.mod h1:1NzhyTcUVG4SuEtjjoZeVRXNmyL/1OwPU0+IJeTBvfc= github.com/shurcooL/sanitized_anchor_name v1.0.0/go.mod h1:1NzhyTcUVG4SuEtjjoZeVRXNmyL/1OwPU0+IJeTBvfc=
github.com/skip2/go-qrcode v0.0.0-20200617195104-da1b6568686e h1:MRM5ITcdelLK2j1vwZ3Je0FKVCfqOLp5zO6trqMLYs0= github.com/skip2/go-qrcode v0.0.0-20200617195104-da1b6568686e h1:MRM5ITcdelLK2j1vwZ3Je0FKVCfqOLp5zO6trqMLYs0=

View file

@ -2,6 +2,8 @@
The MIT License (MIT) The MIT License (MIT)
Copyright (c) 2014-2017 DutchCoders [https://github.com/dutchcoders/] Copyright (c) 2014-2017 DutchCoders [https://github.com/dutchcoders/]
Copyright (c) 2018-2020 Andrea Spacca.
Copyright (c) 2020- Andrea Spacca and Stefan Benten.
Permission is hereby granted, free of charge, to any person obtaining a copy Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal of this software and associated documentation files (the "Software"), to deal
@ -30,7 +32,6 @@ import (
"fmt" "fmt"
"io" "io"
"log"
"net/http" "net/http"
"time" "time"
@ -58,7 +59,7 @@ func (s *Server) scanHandler(w http.ResponseWriter, r *http.Request) {
abort := make(chan bool) abort := make(chan bool)
response, err := c.ScanStream(reader, abort) response, err := c.ScanStream(reader, abort)
if err != nil { if err != nil {
log.Printf("%s", err.Error()) s.logger.Printf("%s", err.Error())
http.Error(w, err.Error(), 500) http.Error(w, err.Error(), 500)
return return
} }

View file

@ -1,78 +0,0 @@
/*
https://github.com/fs111/kurz.go/blob/master/src/codec.go
Originally written and Copyright (c) 2011 André Kelpe
Modifications Copyright (c) 2015 John Ko
Permission is hereby granted, free of charge, to any person obtaining a copy of
this software and associated documentation files (the "Software"), to deal in
the Software without restriction, including without limitation the rights to
use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
the Software, and to permit persons to whom the Software is furnished to do so,
subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
package server
import (
"math"
"math/rand"
"strings"
)
const (
// characters used for short-urls
SYMBOLS = "0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ"
// someone set us up the bomb !!
BASE = float64(len(SYMBOLS))
// init seed encode number
INIT_SEED = float64(-1)
)
// encodes a number into our *base* representation
// TODO can this be made better with some bitshifting?
func Encode(number float64, length int64) string {
if number == INIT_SEED {
seed := math.Pow(float64(BASE), float64(length))
number = seed + (rand.Float64() * seed) // start with seed to enforce desired length
}
rest := int64(math.Mod(number, BASE))
// strings are a bit weird in go...
result := string(SYMBOLS[rest])
if rest > 0 && number-float64(rest) != 0 {
newnumber := (number - float64(rest)) / BASE
result = Encode(newnumber, length) + result
} else {
// it would always be 1 because of starting with seed and we want to skip
return ""
}
return result
}
// Decodes a string given in our encoding and returns the decimal
// integer.
func Decode(input string) int64 {
const floatbase = float64(BASE)
l := len(input)
var sum int = 0
for index := l - 1; index > -1; index -= 1 {
current := string(input[index])
pos := strings.Index(SYMBOLS, current)
sum = sum + (pos * int(math.Pow(floatbase, float64((l-index-1)))))
}
return int64(sum)
}

View file

@ -1,15 +0,0 @@
package server
import "testing"
func BenchmarkEncodeConcat(b *testing.B) {
for i := 0; i < b.N; i++ {
_ = Encode(INIT_SEED, 5) + Encode(INIT_SEED, 5)
}
}
func BenchmarkEncodeLonger(b *testing.B) {
for i := 0; i < b.N; i++ {
_ = Encode(INIT_SEED, 10)
}
}

View file

@ -2,6 +2,8 @@
The MIT License (MIT) The MIT License (MIT)
Copyright (c) 2014-2017 DutchCoders [https://github.com/dutchcoders/] Copyright (c) 2014-2017 DutchCoders [https://github.com/dutchcoders/]
Copyright (c) 2018-2020 Andrea Spacca.
Copyright (c) 2020- Andrea Spacca and Stefan Benten.
Permission is hereby granted, free of charge, to any person obtaining a copy Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal of this software and associated documentation files (the "Software"), to deal
@ -40,7 +42,6 @@ import (
html_template "html/template" html_template "html/template"
"io" "io"
"io/ioutil" "io/ioutil"
"log"
"mime" "mime"
"net/http" "net/http"
"net/url" "net/url"
@ -125,7 +126,7 @@ func (s *Server) previewHandler(w http.ResponseWriter, r *http.Request) {
metadata, err := s.CheckMetadata(token, filename, false) metadata, err := s.CheckMetadata(token, filename, false)
if err != nil { if err != nil {
log.Printf("Error metadata: %s", err.Error()) s.logger.Printf("Error metadata: %s", err.Error())
http.Error(w, http.StatusText(http.StatusNotFound), http.StatusNotFound) http.Error(w, http.StatusText(http.StatusNotFound), http.StatusNotFound)
return return
} }
@ -237,16 +238,34 @@ func (s *Server) viewHandler(w http.ResponseWriter, r *http.Request) {
hostname := getURL(r, s.proxyPort).Host hostname := getURL(r, s.proxyPort).Host
webAddress := resolveWebAddress(r, s.proxyPath, s.proxyPort) webAddress := resolveWebAddress(r, s.proxyPath, s.proxyPort)
maxUploadSize := ""
if s.maxUploadSize > 0 {
maxUploadSize = formatSize(s.maxUploadSize)
}
purgeTime := ""
if s.purgeDays > 0 {
purgeTime = s.purgeDays.String()
}
data := struct { data := struct {
Hostname string Hostname string
WebAddress string WebAddress string
GAKey string GAKey string
UserVoiceKey string UserVoiceKey string
PurgeTime string
MaxUploadSize string
SampleToken string
SampleToken2 string
}{ }{
hostname, hostname,
webAddress, webAddress,
s.gaKey, s.gaKey,
s.userVoiceKey, s.userVoiceKey,
purgeTime,
maxUploadSize,
Token(s.randomTokenLength),
Token(s.randomTokenLength),
} }
if acceptsHTML(r.Header) { if acceptsHTML(r.Header) {
@ -272,12 +291,12 @@ func sanitize(fileName string) string {
func (s *Server) postHandler(w http.ResponseWriter, r *http.Request) { func (s *Server) postHandler(w http.ResponseWriter, r *http.Request) {
if err := r.ParseMultipartForm(_24K); nil != err { if err := r.ParseMultipartForm(_24K); nil != err {
log.Printf("%s", err.Error()) s.logger.Printf("%s", err.Error())
http.Error(w, "Error occurred copying to output stream", 500) http.Error(w, "Error occurred copying to output stream", 500)
return return
} }
token := Encode(INIT_SEED, s.randomTokenLength) token := Token(s.randomTokenLength)
w.Header().Set("Content-Type", "text/plain") w.Header().Set("Content-Type", "text/plain")
@ -290,7 +309,7 @@ func (s *Server) postHandler(w http.ResponseWriter, r *http.Request) {
var err error var err error
if f, err = fheader.Open(); err != nil { if f, err = fheader.Open(); err != nil {
log.Printf("%s", err.Error()) s.logger.Printf("%s", err.Error())
http.Error(w, err.Error(), 500) http.Error(w, err.Error(), 500)
return return
} }
@ -299,7 +318,7 @@ func (s *Server) postHandler(w http.ResponseWriter, r *http.Request) {
n, err := io.CopyN(&b, f, _24K+1) n, err := io.CopyN(&b, f, _24K+1)
if err != nil && err != io.EOF { if err != nil && err != io.EOF {
log.Printf("%s", err.Error()) s.logger.Printf("%s", err.Error())
http.Error(w, err.Error(), 500) http.Error(w, err.Error(), 500)
return return
} }
@ -310,14 +329,14 @@ func (s *Server) postHandler(w http.ResponseWriter, r *http.Request) {
if n > _24K { if n > _24K {
file, err = ioutil.TempFile(s.tempPath, "transfer-") file, err = ioutil.TempFile(s.tempPath, "transfer-")
if err != nil { if err != nil {
log.Fatal(err) s.logger.Fatal(err)
} }
n, err = io.Copy(file, io.MultiReader(&b, f)) n, err = io.Copy(file, io.MultiReader(&b, f))
if err != nil { if err != nil {
cleanTmpFile(file) s.cleanTmpFile(file)
log.Printf("%s", err.Error()) s.logger.Printf("%s", err.Error())
http.Error(w, err.Error(), 500) http.Error(w, err.Error(), 500)
return return
} }
@ -330,7 +349,7 @@ func (s *Server) postHandler(w http.ResponseWriter, r *http.Request) {
contentLength := n contentLength := n
if s.maxUploadSize > 0 && contentLength > s.maxUploadSize { if s.maxUploadSize > 0 && contentLength > s.maxUploadSize {
log.Print("Entity too large") s.logger.Print("Entity too large")
http.Error(w, http.StatusText(http.StatusRequestEntityTooLarge), http.StatusRequestEntityTooLarge) http.Error(w, http.StatusText(http.StatusRequestEntityTooLarge), http.StatusRequestEntityTooLarge)
return return
} }
@ -339,23 +358,23 @@ func (s *Server) postHandler(w http.ResponseWriter, r *http.Request) {
buffer := &bytes.Buffer{} buffer := &bytes.Buffer{}
if err := json.NewEncoder(buffer).Encode(metadata); err != nil { if err := json.NewEncoder(buffer).Encode(metadata); err != nil {
log.Printf("%s", err.Error()) s.logger.Printf("%s", err.Error())
http.Error(w, errors.New("Could not encode metadata").Error(), 500) http.Error(w, errors.New("Could not encode metadata").Error(), 500)
cleanTmpFile(file) s.cleanTmpFile(file)
return return
} else if err := s.storage.Put(token, fmt.Sprintf("%s.metadata", filename), buffer, "text/json", uint64(buffer.Len())); err != nil { } else if err := s.storage.Put(token, fmt.Sprintf("%s.metadata", filename), buffer, "text/json", uint64(buffer.Len())); err != nil {
log.Printf("%s", err.Error()) s.logger.Printf("%s", err.Error())
http.Error(w, errors.New("Could not save metadata").Error(), 500) http.Error(w, errors.New("Could not save metadata").Error(), 500)
cleanTmpFile(file) s.cleanTmpFile(file)
return return
} }
log.Printf("Uploading %s %s %d %s", token, filename, contentLength, contentType) s.logger.Printf("Uploading %s %s %d %s", token, filename, contentLength, contentType)
if err = s.storage.Put(token, filename, reader, contentType, uint64(contentLength)); err != nil { if err = s.storage.Put(token, filename, reader, contentType, uint64(contentLength)); err != nil {
log.Printf("Backend storage error: %s", err.Error()) s.logger.Printf("Backend storage error: %s", err.Error())
http.Error(w, err.Error(), 500) http.Error(w, err.Error(), 500)
return return
@ -365,21 +384,21 @@ func (s *Server) postHandler(w http.ResponseWriter, r *http.Request) {
relativeURL, _ := url.Parse(path.Join(s.proxyPath, token, filename)) relativeURL, _ := url.Parse(path.Join(s.proxyPath, token, filename))
fmt.Fprintln(w, getURL(r, s.proxyPort).ResolveReference(relativeURL).String()) fmt.Fprintln(w, getURL(r, s.proxyPort).ResolveReference(relativeURL).String())
cleanTmpFile(file) s.cleanTmpFile(file)
} }
} }
} }
func cleanTmpFile(f *os.File) { func (s *Server) cleanTmpFile(f *os.File) {
if f != nil { if f != nil {
err := f.Close() err := f.Close()
if err != nil { if err != nil {
log.Printf("Error closing tmpfile: %s (%s)", err, f.Name()) s.logger.Printf("Error closing tmpfile: %s (%s)", err, f.Name())
} }
err = os.Remove(f.Name()) err = os.Remove(f.Name())
if err != nil { if err != nil {
log.Printf("Error removing tmpfile: %s (%s)", err, f.Name()) s.logger.Printf("Error removing tmpfile: %s (%s)", err, f.Name())
} }
} }
} }
@ -399,13 +418,13 @@ type Metadata struct {
DeletionToken string DeletionToken string
} }
func MetadataForRequest(contentType string, randomTokenLength int64, r *http.Request) Metadata { func MetadataForRequest(contentType string, randomTokenLength int, r *http.Request) Metadata {
metadata := Metadata{ metadata := Metadata{
ContentType: strings.ToLower(contentType), ContentType: strings.ToLower(contentType),
MaxDate: time.Time{}, MaxDate: time.Time{},
Downloads: 0, Downloads: 0,
MaxDownloads: -1, MaxDownloads: -1,
DeletionToken: Encode(INIT_SEED, randomTokenLength) + Encode(INIT_SEED, randomTokenLength), DeletionToken: Token(randomTokenLength) + Token(randomTokenLength),
} }
if v := r.Header.Get("Max-Downloads"); v == "" { if v := r.Header.Get("Max-Downloads"); v == "" {
@ -447,7 +466,7 @@ func (s *Server) putHandler(w http.ResponseWriter, r *http.Request) {
n, err := io.CopyN(&b, f, _24K+1) n, err := io.CopyN(&b, f, _24K+1)
if err != nil && err != io.EOF { if err != nil && err != io.EOF {
log.Printf("Error putting new file: %s", err.Error()) s.logger.Printf("Error putting new file: %s", err.Error())
http.Error(w, err.Error(), 500) http.Error(w, err.Error(), 500)
return return
} }
@ -457,16 +476,16 @@ func (s *Server) putHandler(w http.ResponseWriter, r *http.Request) {
if n > _24K { if n > _24K {
file, err = ioutil.TempFile(s.tempPath, "transfer-") file, err = ioutil.TempFile(s.tempPath, "transfer-")
if err != nil { if err != nil {
log.Printf("%s", err.Error()) s.logger.Printf("%s", err.Error())
http.Error(w, err.Error(), 500) http.Error(w, err.Error(), 500)
return return
} }
defer cleanTmpFile(file) defer s.cleanTmpFile(file)
n, err = io.Copy(file, io.MultiReader(&b, f)) n, err = io.Copy(file, io.MultiReader(&b, f))
if err != nil { if err != nil {
log.Printf("%s", err.Error()) s.logger.Printf("%s", err.Error())
http.Error(w, err.Error(), 500) http.Error(w, err.Error(), 500)
return return
} }
@ -480,40 +499,40 @@ func (s *Server) putHandler(w http.ResponseWriter, r *http.Request) {
} }
if s.maxUploadSize > 0 && contentLength > s.maxUploadSize { if s.maxUploadSize > 0 && contentLength > s.maxUploadSize {
log.Print("Entity too large") s.logger.Print("Entity too large")
http.Error(w, http.StatusText(http.StatusRequestEntityTooLarge), http.StatusRequestEntityTooLarge) http.Error(w, http.StatusText(http.StatusRequestEntityTooLarge), http.StatusRequestEntityTooLarge)
return return
} }
if contentLength == 0 { if contentLength == 0 {
log.Print("Empty content-length") s.logger.Print("Empty content-length")
http.Error(w, errors.New("Could not upload empty file").Error(), 400) http.Error(w, errors.New("Could not upload empty file").Error(), 400)
return return
} }
contentType := mime.TypeByExtension(filepath.Ext(vars["filename"])) contentType := mime.TypeByExtension(filepath.Ext(vars["filename"]))
token := Encode(INIT_SEED, s.randomTokenLength) token := Token(s.randomTokenLength)
metadata := MetadataForRequest(contentType, s.randomTokenLength, r) metadata := MetadataForRequest(contentType, s.randomTokenLength, r)
buffer := &bytes.Buffer{} buffer := &bytes.Buffer{}
if err := json.NewEncoder(buffer).Encode(metadata); err != nil { if err := json.NewEncoder(buffer).Encode(metadata); err != nil {
log.Printf("%s", err.Error()) s.logger.Printf("%s", err.Error())
http.Error(w, errors.New("Could not encode metadata").Error(), 500) http.Error(w, errors.New("Could not encode metadata").Error(), 500)
return return
} else if err := s.storage.Put(token, fmt.Sprintf("%s.metadata", filename), buffer, "text/json", uint64(buffer.Len())); err != nil { } else if err := s.storage.Put(token, fmt.Sprintf("%s.metadata", filename), buffer, "text/json", uint64(buffer.Len())); err != nil {
log.Printf("%s", err.Error()) s.logger.Printf("%s", err.Error())
http.Error(w, errors.New("Could not save metadata").Error(), 500) http.Error(w, errors.New("Could not save metadata").Error(), 500)
return return
} }
log.Printf("Uploading %s %s %d %s", token, filename, contentLength, contentType) s.logger.Printf("Uploading %s %s %d %s", token, filename, contentLength, contentType)
var err error var err error
if err = s.storage.Put(token, filename, reader, contentType, uint64(contentLength)); err != nil { if err = s.storage.Put(token, filename, reader, contentType, uint64(contentLength)); err != nil {
log.Printf("Error putting new file: %s", err.Error()) s.logger.Printf("Error putting new file: %s", err.Error())
http.Error(w, errors.New("Could not save file").Error(), 500) http.Error(w, errors.New("Could not save file").Error(), 500)
return return
} }
@ -637,23 +656,22 @@ func (metadata Metadata) remainingLimitHeaderValues() (remainingDownloads, remai
return remainingDownloads, remainingDays return remainingDownloads, remainingDays
} }
func (s *Server) Lock(token, filename string) error { func (s *Server) Lock(token, filename string) {
key := path.Join(token, filename) key := path.Join(token, filename)
if _, ok := s.locks[key]; !ok { lock, _ := s.locks.LoadOrStore(key, &sync.Mutex{})
s.locks[key] = &sync.Mutex{}
}
s.locks[key].Lock() lock.(*sync.Mutex).Lock()
return nil return
} }
func (s *Server) Unlock(token, filename string) error { func (s *Server) Unlock(token, filename string) {
key := path.Join(token, filename) key := path.Join(token, filename)
s.locks[key].Unlock()
return nil lock, _ := s.locks.LoadOrStore(key, &sync.Mutex{})
lock.(*sync.Mutex).Unlock()
} }
func (s *Server) CheckMetadata(token, filename string, increaseDownload bool) (Metadata, error) { func (s *Server) CheckMetadata(token, filename string, increaseDownload bool) (Metadata, error) {
@ -723,7 +741,7 @@ func (s *Server) purgeHandler() {
select { select {
case <-ticker.C: case <-ticker.C:
err := s.storage.Purge(s.purgeDays) err := s.storage.Purge(s.purgeDays)
log.Printf("error cleaning up expired files: %v", err) s.logger.Printf("error cleaning up expired files: %v", err)
} }
} }
}() }()
@ -737,7 +755,7 @@ func (s *Server) deleteHandler(w http.ResponseWriter, r *http.Request) {
deletionToken := vars["deletionToken"] deletionToken := vars["deletionToken"]
if err := s.CheckDeletionToken(deletionToken, token, filename); err != nil { if err := s.CheckDeletionToken(deletionToken, token, filename); err != nil {
log.Printf("Error metadata: %s", err.Error()) s.logger.Printf("Error metadata: %s", err.Error())
http.Error(w, http.StatusText(http.StatusNotFound), http.StatusNotFound) http.Error(w, http.StatusText(http.StatusNotFound), http.StatusNotFound)
return return
} }
@ -747,7 +765,7 @@ func (s *Server) deleteHandler(w http.ResponseWriter, r *http.Request) {
http.Error(w, http.StatusText(http.StatusNotFound), http.StatusNotFound) http.Error(w, http.StatusText(http.StatusNotFound), http.StatusNotFound)
return return
} else if err != nil { } else if err != nil {
log.Printf("%s", err.Error()) s.logger.Printf("%s", err.Error())
http.Error(w, "Could not delete file.", 500) http.Error(w, "Could not delete file.", 500)
return return
} }
@ -773,7 +791,7 @@ func (s *Server) zipHandler(w http.ResponseWriter, r *http.Request) {
filename := sanitize(strings.Split(key, "/")[1]) filename := sanitize(strings.Split(key, "/")[1])
if _, err := s.CheckMetadata(token, filename, true); err != nil { if _, err := s.CheckMetadata(token, filename, true); err != nil {
log.Printf("Error metadata: %s", err.Error()) s.logger.Printf("Error metadata: %s", err.Error())
continue continue
} }
@ -784,7 +802,7 @@ func (s *Server) zipHandler(w http.ResponseWriter, r *http.Request) {
http.Error(w, "File not found", 404) http.Error(w, "File not found", 404)
return return
} else { } else {
log.Printf("%s", err.Error()) s.logger.Printf("%s", err.Error())
http.Error(w, "Could not retrieve file.", 500) http.Error(w, "Could not retrieve file.", 500)
return return
} }
@ -802,20 +820,20 @@ func (s *Server) zipHandler(w http.ResponseWriter, r *http.Request) {
fw, err := zw.CreateHeader(header) fw, err := zw.CreateHeader(header)
if err != nil { if err != nil {
log.Printf("%s", err.Error()) s.logger.Printf("%s", err.Error())
http.Error(w, "Internal server error.", 500) http.Error(w, "Internal server error.", 500)
return return
} }
if _, err = io.Copy(fw, reader); err != nil { if _, err = io.Copy(fw, reader); err != nil {
log.Printf("%s", err.Error()) s.logger.Printf("%s", err.Error())
http.Error(w, "Internal server error.", 500) http.Error(w, "Internal server error.", 500)
return return
} }
} }
if err := zw.Close(); err != nil { if err := zw.Close(); err != nil {
log.Printf("%s", err.Error()) s.logger.Printf("%s", err.Error())
http.Error(w, "Internal server error.", 500) http.Error(w, "Internal server error.", 500)
return return
} }
@ -845,7 +863,7 @@ func (s *Server) tarGzHandler(w http.ResponseWriter, r *http.Request) {
filename := sanitize(strings.Split(key, "/")[1]) filename := sanitize(strings.Split(key, "/")[1])
if _, err := s.CheckMetadata(token, filename, true); err != nil { if _, err := s.CheckMetadata(token, filename, true); err != nil {
log.Printf("Error metadata: %s", err.Error()) s.logger.Printf("Error metadata: %s", err.Error())
continue continue
} }
@ -855,7 +873,7 @@ func (s *Server) tarGzHandler(w http.ResponseWriter, r *http.Request) {
http.Error(w, "File not found", 404) http.Error(w, "File not found", 404)
return return
} else { } else {
log.Printf("%s", err.Error()) s.logger.Printf("%s", err.Error())
http.Error(w, "Could not retrieve file.", 500) http.Error(w, "Could not retrieve file.", 500)
return return
} }
@ -870,13 +888,13 @@ func (s *Server) tarGzHandler(w http.ResponseWriter, r *http.Request) {
err = zw.WriteHeader(header) err = zw.WriteHeader(header)
if err != nil { if err != nil {
log.Printf("%s", err.Error()) s.logger.Printf("%s", err.Error())
http.Error(w, "Internal server error.", 500) http.Error(w, "Internal server error.", 500)
return return
} }
if _, err = io.Copy(zw, reader); err != nil { if _, err = io.Copy(zw, reader); err != nil {
log.Printf("%s", err.Error()) s.logger.Printf("%s", err.Error())
http.Error(w, "Internal server error.", 500) http.Error(w, "Internal server error.", 500)
return return
} }
@ -904,7 +922,7 @@ func (s *Server) tarHandler(w http.ResponseWriter, r *http.Request) {
filename := strings.Split(key, "/")[1] filename := strings.Split(key, "/")[1]
if _, err := s.CheckMetadata(token, filename, true); err != nil { if _, err := s.CheckMetadata(token, filename, true); err != nil {
log.Printf("Error metadata: %s", err.Error()) s.logger.Printf("Error metadata: %s", err.Error())
continue continue
} }
@ -914,7 +932,7 @@ func (s *Server) tarHandler(w http.ResponseWriter, r *http.Request) {
http.Error(w, "File not found", 404) http.Error(w, "File not found", 404)
return return
} else { } else {
log.Printf("%s", err.Error()) s.logger.Printf("%s", err.Error())
http.Error(w, "Could not retrieve file.", 500) http.Error(w, "Could not retrieve file.", 500)
return return
} }
@ -929,13 +947,13 @@ func (s *Server) tarHandler(w http.ResponseWriter, r *http.Request) {
err = zw.WriteHeader(header) err = zw.WriteHeader(header)
if err != nil { if err != nil {
log.Printf("%s", err.Error()) s.logger.Printf("%s", err.Error())
http.Error(w, "Internal server error.", 500) http.Error(w, "Internal server error.", 500)
return return
} }
if _, err = io.Copy(zw, reader); err != nil { if _, err = io.Copy(zw, reader); err != nil {
log.Printf("%s", err.Error()) s.logger.Printf("%s", err.Error())
http.Error(w, "Internal server error.", 500) http.Error(w, "Internal server error.", 500)
return return
} }
@ -951,7 +969,7 @@ func (s *Server) headHandler(w http.ResponseWriter, r *http.Request) {
metadata, err := s.CheckMetadata(token, filename, false) metadata, err := s.CheckMetadata(token, filename, false)
if err != nil { if err != nil {
log.Printf("Error metadata: %s", err.Error()) s.logger.Printf("Error metadata: %s", err.Error())
http.Error(w, http.StatusText(http.StatusNotFound), http.StatusNotFound) http.Error(w, http.StatusText(http.StatusNotFound), http.StatusNotFound)
return return
} }
@ -962,7 +980,7 @@ func (s *Server) headHandler(w http.ResponseWriter, r *http.Request) {
http.Error(w, http.StatusText(http.StatusNotFound), http.StatusNotFound) http.Error(w, http.StatusText(http.StatusNotFound), http.StatusNotFound)
return return
} else if err != nil { } else if err != nil {
log.Printf("%s", err.Error()) s.logger.Printf("%s", err.Error())
http.Error(w, "Could not retrieve file.", 500) http.Error(w, "Could not retrieve file.", 500)
return return
} }
@ -986,7 +1004,7 @@ func (s *Server) getHandler(w http.ResponseWriter, r *http.Request) {
metadata, err := s.CheckMetadata(token, filename, true) metadata, err := s.CheckMetadata(token, filename, true)
if err != nil { if err != nil {
log.Printf("Error metadata: %s", err.Error()) s.logger.Printf("Error metadata: %s", err.Error())
http.Error(w, http.StatusText(http.StatusNotFound), http.StatusNotFound) http.Error(w, http.StatusText(http.StatusNotFound), http.StatusNotFound)
return return
} }
@ -997,7 +1015,7 @@ func (s *Server) getHandler(w http.ResponseWriter, r *http.Request) {
http.Error(w, http.StatusText(http.StatusNotFound), http.StatusNotFound) http.Error(w, http.StatusText(http.StatusNotFound), http.StatusNotFound)
return return
} else if err != nil { } else if err != nil {
log.Printf("%s", err.Error()) s.logger.Printf("%s", err.Error())
http.Error(w, "Could not retrieve file.", 500) http.Error(w, "Could not retrieve file.", 500)
return return
} }
@ -1025,41 +1043,34 @@ func (s *Server) getHandler(w http.ResponseWriter, r *http.Request) {
reader = ioutil.NopCloser(bluemonday.UGCPolicy().SanitizeReader(reader)) reader = ioutil.NopCloser(bluemonday.UGCPolicy().SanitizeReader(reader))
} }
if w.Header().Get("Range") == "" { if w.Header().Get("Range") != "" || strings.HasPrefix(metadata.ContentType, "video") || strings.HasPrefix(metadata.ContentType, "audio") {
if _, err = io.Copy(w, reader); err != nil { file, err := ioutil.TempFile(s.tempPath, "range-")
log.Printf("%s", err.Error()) if err != nil {
s.logger.Printf("%s", err.Error())
http.Error(w, "Error occurred copying to output stream", 500) http.Error(w, "Error occurred copying to output stream", 500)
return return
} }
defer s.cleanTmpFile(file)
_, err = io.Copy(file, reader)
if err != nil {
s.logger.Printf("%s", err.Error())
http.Error(w, "Error occurred copying to output stream", 500)
return
}
http.ServeContent(w, r, filename, time.Now(), file)
return return
} }
file, err := ioutil.TempFile(s.tempPath, "range-") if _, err = io.Copy(w, reader); err != nil {
if err != nil { s.logger.Printf("%s", err.Error())
log.Printf("%s", err.Error())
http.Error(w, "Error occurred copying to output stream", 500) http.Error(w, "Error occurred copying to output stream", 500)
return return
} }
defer cleanTmpFile(file) return
tee := io.TeeReader(reader, file)
for {
b := make([]byte, _5M)
_, err = tee.Read(b)
if err == io.EOF {
break
}
if err != nil {
log.Printf("%s", err.Error())
http.Error(w, "Error occurred copying to output stream", 500)
return
}
}
http.ServeContent(w, r, filename, time.Now(), file)
} }
func (s *Server) RedirectHandler(h http.Handler) http.HandlerFunc { func (s *Server) RedirectHandler(h http.Handler) http.HandlerFunc {

View file

@ -161,7 +161,7 @@ func LogFile(logger *log.Logger, s string) OptionFn {
return func(srvr *Server) { return func(srvr *Server) {
f, err := os.OpenFile(s, os.O_RDWR|os.O_CREATE|os.O_APPEND, 0666) f, err := os.OpenFile(s, os.O_RDWR|os.O_CREATE|os.O_APPEND, 0666)
if err != nil { if err != nil {
log.Fatalf("error opening file: %v", err) logger.Fatalf("error opening file: %v", err)
} }
logger.SetOutput(f) logger.SetOutput(f)
@ -187,7 +187,7 @@ func RateLimit(requests int) OptionFn {
} }
} }
func RandomTokenLength(length int64) OptionFn { func RandomTokenLength(length int) OptionFn {
return func(srvr *Server) { return func(srvr *Server) {
srvr.randomTokenLength = length srvr.randomTokenLength = length
} }
@ -288,7 +288,7 @@ type Server struct {
profilerEnabled bool profilerEnabled bool
locks map[string]*sync.Mutex locks sync.Map
maxUploadSize int64 maxUploadSize int64
rateLimitRequests int rateLimitRequests int
@ -300,7 +300,7 @@ type Server struct {
forceHTTPs bool forceHTTPs bool
randomTokenLength int64 randomTokenLength int
ipFilterOptions *IPFilterOptions ipFilterOptions *IPFilterOptions
@ -329,7 +329,7 @@ type Server struct {
func New(options ...OptionFn) (*Server, error) { func New(options ...OptionFn) (*Server, error) {
s := &Server{ s := &Server{
locks: map[string]*sync.Mutex{}, locks: sync.Map{},
} }
for _, optionFn := range options { for _, optionFn := range options {

45
server/token.go Normal file
View file

@ -0,0 +1,45 @@
/*
The MIT License (MIT)
Copyright (c) 2020- Andrea Spacca and Stefan Benten.
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.
*/
package server
import (
"math/rand"
)
const (
// characters used for short-urls
SYMBOLS = "0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ"
)
// generate a token
func Token(length int) string {
result := ""
for i := 0; i < length; i++ {
x := rand.Intn(len(SYMBOLS) - 1)
result = string(SYMBOLS[x]) + result
}
return result
}

15
server/token_test.go Normal file
View file

@ -0,0 +1,15 @@
package server
import "testing"
func BenchmarkTokenConcat(b *testing.B) {
for i := 0; i < b.N; i++ {
_ = Token(5) + Token(5)
}
}
func BenchmarkTokenLonger(b *testing.B) {
for i := 0; i < b.N; i++ {
_ = Token(10)
}
}

View file

@ -2,6 +2,8 @@
The MIT License (MIT) The MIT License (MIT)
Copyright (c) 2014-2017 DutchCoders [https://github.com/dutchcoders/] Copyright (c) 2014-2017 DutchCoders [https://github.com/dutchcoders/]
Copyright (c) 2018-2020 Andrea Spacca.
Copyright (c) 2020- Andrea Spacca and Stefan Benten.
Permission is hereby granted, free of charge, to any person obtaining a copy Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal of this software and associated documentation files (the "Software"), to deal
@ -25,6 +27,7 @@ THE SOFTWARE.
package server package server
import ( import (
"fmt"
"math" "math"
"net/http" "net/http"
"net/mail" "net/mail"
@ -47,7 +50,6 @@ func getAwsSession(accessKey, secretKey, region, endpoint string, forcePathStyle
} }
func formatNumber(format string, s uint64) string { func formatNumber(format string, s uint64) string {
return RenderFloat(format, float64(s)) return RenderFloat(format, float64(s))
} }
@ -253,3 +255,27 @@ func acceptsHTML(hdr http.Header) bool {
return (false) return (false)
} }
func formatSize(size int64) string {
sizeFloat := float64(size)
base := math.Log(sizeFloat) / math.Log(1024)
sizeOn := math.Pow(1024, base-math.Floor(base))
var round float64
pow := math.Pow(10, float64(2))
digit := pow * sizeOn
round = math.Floor(digit)
newVal := round / pow
var suffixes [5]string
suffixes[0] = "B"
suffixes[1] = "KB"
suffixes[2] = "MB"
suffixes[3] = "GB"
suffixes[4] = "TB"
getSuffix := suffixes[int(math.Floor(base))]
return fmt.Sprintf("%s %s", strconv.FormatFloat(newVal, 'f', -1, 64), getSuffix)
}