Compare commits

..

9 Commits

Author SHA1 Message Date
Andrea Spacca
2a29083960 handle range with no limit 2023-02-11 11:47:51 +09:00
Andrea Spacca
158e5487ee refactor CloseCheck to avoid panic on nil 2023-02-11 11:47:01 +09:00
Andrea Spacca
806286ab35 refactor CloseCheck to avoid panic on nil, handle range in gdrive storage 2023-02-11 11:46:27 +09:00
Andrea Spacca
d49aee59ba refactor CloseCheck to avoid panic on nil, handle range with no limit 2023-02-11 11:45:57 +09:00
Andrea Spacca
e08225e5f8 refactor CloseCheck to avoid panic on nil, remove range/audio/video special handling on get handler 2023-02-11 11:45:17 +09:00
Andrea Spacca
8597f1d9eb bump gdrive dependecies 2023-02-11 11:44:12 +09:00
Andrea Spacca
9e8ce19cd1 proper param name in error 2023-02-11 11:43:41 +09:00
Vladislav Grubov
2bda0a1e55 Adds 'Accept-Ranges: bytes' header to handlers 2023-02-03 22:45:55 +03:00
Vladislav Grubov
d9369e8b39 Support Range header for GET 2023-01-28 22:23:57 +03:00
11 changed files with 1316 additions and 432 deletions

View File

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

View File

@@ -13,27 +13,16 @@ jobs:
fail-fast: false
matrix:
go_version:
- '1.18'
- '1.19'
- '1.20'
- tip
- 1.15.x
- 1.16.x
- 1.17.x
- 1.18.X
name: Test with ${{ matrix.go_version }}
steps:
- uses: actions/checkout@v2
- name: Install Go ${{ matrix.go_version }}
if: ${{ matrix.go_version != 'tip' }}
uses: actions/setup-go@master
- uses: actions/setup-go@v1
with:
go-version: ${{ matrix.go_version }}
check-latest: true
- name: Install Go ${{ matrix.go_version }}
if: ${{ matrix.go_version == 'tip' }}
run: |
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
@@ -44,10 +33,9 @@ jobs:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
- uses: actions/setup-go@master
- uses: actions/setup-go@v1
with:
go-version: '1.20'
check-latest: true
go-version: 1.18
- name: golangci-lint
uses: golangci/golangci-lint-action@v2
with:

View File

@@ -47,18 +47,6 @@ $ curl --upload-file ./hello.txt https://transfer.sh/hello.txt -H "Max-Downloads
$ curl --upload-file ./hello.txt https://transfer.sh/hello.txt -H "Max-Days: 1" # Set the number of days before deletion
```
### 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 sever side with AES265 using "test" as password
```
### 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 sever side with AES265 using "test" as password
```
## Response Headers
### X-Url-Delete

View File

@@ -2,12 +2,11 @@ package cmd
import (
"fmt"
"github.com/dutchcoders/transfer.sh/server/storage"
"log"
"os"
"strings"
"github.com/dutchcoders/transfer.sh/server/storage"
"github.com/dutchcoders/transfer.sh/server"
"github.com/fatih/color"
"github.com/urfave/cli"
@@ -291,7 +290,7 @@ var globalFlags = []cli.Flag{
cli.IntFlag{
Name: "random-token-length",
Usage: "",
Value: 10,
Value: 6,
EnvVar: "RANDOM_TOKEN_LENGTH",
},
}

91
go.mod
View File

@@ -1,75 +1,40 @@
module github.com/dutchcoders/transfer.sh
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 v1.44.211
github.com/dutchcoders/go-clamd v0.0.0-20170520113014-b970184f4d9e
github.com/dutchcoders/go-virustotal v0.0.0-20140923143438-24cc8e6fa329
github.com/dutchcoders/transfer.sh-web v0.0.0-20221119114740-ca3a2621d2a6
github.com/elazarl/go-bindata-assetfs v1.0.1
github.com/fatih/color v1.14.1
github.com/golang/gddo v0.0.0-20210115222349-20d68f94ee1f
github.com/gorilla/handlers v1.5.1
github.com/gorilla/mux v1.8.0
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/tomasen/realip v0.0.0-20180522021738-f0c99a92ddce
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
)
go 1.15
require (
cloud.google.com/go/compute v1.18.0 // indirect
cloud.google.com/go/compute/metadata v0.2.3 // indirect
github.com/aymerick/douceur v0.2.0 // indirect
github.com/calebcase/tmpfile v1.0.3 // 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/PuerkitoBio/ghost v0.0.0-20160324114900-206e6e460e14
github.com/VojtechVitek/ratelimit v0.0.0-20160722140851-dc172bc0f6d2
github.com/aws/aws-sdk-go v1.37.14
github.com/cpuguy83/go-md2man/v2 v2.0.0 // indirect
github.com/dutchcoders/go-clamd v0.0.0-20170520113014-b970184f4d9e
github.com/dutchcoders/go-virustotal v0.0.0-20140923143438-24cc8e6fa329
github.com/dutchcoders/transfer.sh-web v0.0.0-20220824020025-7240e75c3bb8
github.com/elazarl/go-bindata-assetfs v1.0.1
github.com/fatih/color v1.10.0
github.com/garyburd/redigo v1.6.2 // indirect
github.com/golang/gddo v0.0.0-20210115222349-20d68f94ee1f
github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da // 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.0 // indirect
github.com/gorilla/css v1.0.0 // indirect
github.com/googleapis/enterprise-certificate-proxy v0.2.2 // indirect
github.com/gorilla/handlers v1.5.1
github.com/gorilla/mux v1.8.0
github.com/gorilla/securecookie v1.1.1 // indirect
github.com/jmespath/go-jmespath v0.4.0 // indirect
github.com/jtolio/eventkit v0.0.0-20230301123942-0cee1388f16f // indirect
github.com/jtolio/noiseconn v0.0.0-20230227223919-bddcd1327059 // indirect
github.com/klauspost/cpuid/v2 v2.2.4 // indirect
github.com/kr/pretty v0.3.1 // indirect
github.com/kr/text v0.2.0 // indirect
github.com/mattn/go-colorable v0.1.13 // indirect
github.com/mattn/go-isatty v0.0.17 // indirect
github.com/microcosm-cc/bluemonday v1.0.16
github.com/nu7hatch/gouuid v0.0.0-20131221200532-179d4d0c4d8d // indirect
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/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.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-20230227214838-9b19f0bdc514 // indirect
github.com/russross/blackfriday/v2 v2.1.0
github.com/skip2/go-qrcode v0.0.0-20200617195104-da1b6568686e
github.com/tomasen/realip v0.0.0-20180522021738-f0c99a92ddce
github.com/urfave/cli v1.22.5
golang.org/x/crypto v0.0.0-20220131195533-30dcbda58838
golang.org/x/net v0.6.0 // indirect
golang.org/x/oauth2 v0.5.0
google.golang.org/api v0.109.0
google.golang.org/genproto v0.0.0-20230209215440-0dfe4f8abfcc // 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
gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15
storj.io/common v0.0.0-20220405183405-ffdc3ab808c6
storj.io/uplink v1.8.2
)

1293
go.sum

File diff suppressed because it is too large Load Diff

View File

@@ -39,6 +39,7 @@ import (
"html"
htmlTemplate "html/template"
"io"
"io/ioutil"
"mime"
"net"
"net/http"
@@ -52,17 +53,13 @@ import (
textTemplate "text/template"
"time"
"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"
web "github.com/dutchcoders/transfer.sh-web"
"github.com/gorilla/mux"
"github.com/microcosm-cc/bluemonday"
blackfriday "github.com/russross/blackfriday/v2"
qrcode "github.com/skip2/go-qrcode"
"github.com/russross/blackfriday/v2"
"github.com/skip2/go-qrcode"
"golang.org/x/net/idna"
)
@@ -94,128 +91,6 @@ func initHTMLTemplates() *htmlTemplate.Template {
return templates
}
func attachEncryptionReader(reader io.ReadCloser, password string) (io.ReadCloser, error) {
if len(password) == 0 {
return reader, nil
}
return encrypt(reader, []byte(password))
}
func attachDecryptionReader(reader io.ReadCloser, password string) (io.ReadCloser, error) {
if len(password) == 0 {
return reader, nil
}
return decrypt(reader, []byte(password))
}
func decrypt(ciphertext io.ReadCloser, password []byte) (plaintext io.ReadCloser, err error) {
unarmored, err := armor.Decode(ciphertext)
if err != nil {
return
}
firstTimeCalled := true
var prompt = func(keys []openpgp.Key, symmetric bool) ([]byte, error) {
if firstTimeCalled {
firstTimeCalled = false
return password, nil
}
// Re-prompt still occurs if SKESK pasrsing fails (i.e. when decrypted cipher algo is invalid).
// For most (but not all) cases, inputting a wrong passwords is expected to trigger this error.
return nil, errors.New("gopenpgp: wrong password in symmetric decryption")
}
config := &packet.Config{
DefaultCipher: packet.CipherAES256,
}
var emptyKeyRing openpgp.EntityList
md, err := openpgp.ReadMessage(unarmored.Body, emptyKeyRing, prompt, config)
if err != nil {
// Parsing errors when reading the message are most likely caused by incorrect password, but we cannot know for sure
return
}
plaintext = io.NopCloser(md.UnverifiedBody)
return
}
type encryptWrapperReader struct {
plaintext io.Reader
encrypt io.WriteCloser
armored io.WriteCloser
buffer io.ReadWriter
plaintextReadZero bool
}
func (e *encryptWrapperReader) Read(p []byte) (n int, err error) {
p2 := make([]byte, len(p))
n, _ = e.plaintext.Read(p2)
if n == 0 {
if !e.plaintextReadZero {
err = e.encrypt.Close()
if err != nil {
return
}
err = e.armored.Close()
if err != nil {
return
}
e.plaintextReadZero = true
}
return e.buffer.Read(p)
}
return e.buffer.Read(p)
}
func (e *encryptWrapperReader) Close() error {
return nil
}
func NewEncryptWrapperReader(plaintext io.Reader, armored, encrypt io.WriteCloser, buffer io.ReadWriter) io.ReadCloser {
return &encryptWrapperReader{
plaintext: io.TeeReader(plaintext, encrypt),
encrypt: encrypt,
armored: armored,
buffer: buffer,
}
}
func encrypt(plaintext io.ReadCloser, password []byte) (ciphertext io.ReadCloser, err error) {
bufferReadWriter := new(bytes.Buffer)
armored, err := armor.Encode(bufferReadWriter, constants.PGPMessageHeader, nil)
if err != nil {
return
}
config := &packet.Config{
DefaultCipher: packet.CipherAES256,
Time: time.Now,
}
hints := &openpgp.FileHints{
IsBinary: true,
FileName: "",
ModTime: time.Unix(time.Now().Unix(), 0),
}
encryptWriter, err := openpgp.SymmetricallyEncrypt(armored, password, hints, config)
if err != nil {
return
}
ciphertext = NewEncryptWrapperReader(plaintext, armored, encryptWriter, bufferReadWriter)
return
}
func healthHandler(w http.ResponseWriter, _ *http.Request) {
_, _ = w.Write([]byte("Approaching Neutral Zone, all systems normal and functioning."))
}
@@ -443,7 +318,7 @@ func (s *Server) postHandler(w http.ResponseWriter, r *http.Request) {
return
}
file, err := os.CreateTemp(s.tempPath, "transfer-")
file, err := ioutil.TempFile(s.tempPath, "transfer-")
defer s.cleanTmpFile(file)
if err != nil {
@@ -488,7 +363,7 @@ func (s *Server) postHandler(w http.ResponseWriter, r *http.Request) {
}
}
metadata := metadataForRequest(contentType, contentLength, s.randomTokenLength, r)
metadata := metadataForRequest(contentType, s.randomTokenLength, r)
buffer := &bytes.Buffer{}
if err := json.NewEncoder(buffer).Encode(metadata); err != nil {
@@ -505,13 +380,7 @@ func (s *Server) postHandler(w http.ResponseWriter, r *http.Request) {
s.logger.Printf("Uploading %s %s %d %s", token, filename, contentLength, contentType)
reader, err := attachEncryptionReader(file, r.Header.Get("X-Encrypt-Password"))
if err != nil {
http.Error(w, "Could not crypt file", http.StatusInternalServerError)
return
}
if err = s.storage.Put(r.Context(), token, filename, reader, contentType, uint64(contentLength)); err != nil {
if err = s.storage.Put(r.Context(), token, filename, file, contentType, uint64(contentLength)); err != nil {
s.logger.Printf("Backend storage error: %s", err.Error())
http.Error(w, err.Error(), http.StatusInternalServerError)
return
@@ -549,8 +418,8 @@ func (s *Server) cleanTmpFile(f *os.File) {
type metadata struct {
// ContentType is the original uploading content type
ContentType string
// ContentLength is is the original uploading content length
ContentLength int64
// Secret as knowledge to delete file
// Secret string
// Downloads is the actual number of downloads
Downloads int
// MaxDownloads contains the maximum numbers of downloads
@@ -559,16 +428,11 @@ type metadata struct {
MaxDate time.Time
// DeletionToken contains the token to match against for deletion
DeletionToken string
// Encrypted contains if the file was encrypted
Encrypted bool
// DecryptedContentType is the original uploading content type
DecryptedContentType string
}
func metadataForRequest(contentType string, contentLength int64, randomTokenLength int, r *http.Request) metadata {
func metadataForRequest(contentType string, randomTokenLength int, r *http.Request) metadata {
metadata := metadata{
ContentType: strings.ToLower(contentType),
ContentLength: contentLength,
MaxDate: time.Time{},
Downloads: 0,
MaxDownloads: -1,
@@ -587,14 +451,6 @@ func metadataForRequest(contentType string, contentLength int64, randomTokenLeng
metadata.MaxDate = time.Now().Add(time.Hour * 24 * time.Duration(v))
}
if password := r.Header.Get("X-Encrypt-Password"); password != "" {
metadata.Encrypted = true
metadata.ContentType = "text/plain; charset=utf-8"
metadata.DecryptedContentType = contentType
} else {
metadata.Encrypted = false
}
return metadata
}
@@ -607,53 +463,34 @@ func (s *Server) putHandler(w http.ResponseWriter, r *http.Request) {
defer storage.CloseCheck(r.Body)
reader := r.Body
file, err := ioutil.TempFile(s.tempPath, "transfer-")
defer s.cleanTmpFile(file)
if err != nil {
s.logger.Printf("%s", err.Error())
http.Error(w, err.Error(), http.StatusInternalServerError)
return
}
if contentLength < 1 || s.performClamavPrescan {
file, err := os.CreateTemp(s.tempPath, "transfer-")
defer s.cleanTmpFile(file)
if err != nil {
s.logger.Printf("%s", err.Error())
http.Error(w, err.Error(), http.StatusInternalServerError)
return
}
// queue file to disk, because s3 needs content length
// and clamav prescan scans a file
n, err := io.Copy(file, r.Body)
if err != nil {
s.logger.Printf("%s", err.Error())
http.Error(w, err.Error(), http.StatusInternalServerError)
// queue file to disk, because s3 needs content length
// and clamav prescan scans a file
n, err := io.Copy(file, r.Body)
if err != nil {
s.logger.Printf("%s", err.Error())
http.Error(w, err.Error(), http.StatusInternalServerError)
return
}
return
}
_, err = file.Seek(0, io.SeekStart)
if err != nil {
s.logger.Printf("%s", err.Error())
http.Error(w, "Cannot reset cache file", http.StatusInternalServerError)
_, err = file.Seek(0, io.SeekStart)
if err != nil {
s.logger.Printf("%s", err.Error())
http.Error(w, "Cannot reset cache file", http.StatusInternalServerError)
return
}
return
}
if contentLength < 1 {
contentLength = n
if s.performClamavPrescan {
status, err := s.performScan(file.Name())
if err != nil {
s.logger.Printf("%s", err.Error())
http.Error(w, "Could not perform prescan", http.StatusInternalServerError)
return
}
if status != clamavScanStatusOK {
s.logger.Printf("prescan positive: %s", status)
http.Error(w, "Clamav prescan found a virus", http.StatusPreconditionFailed)
return
}
}
reader = file
}
if s.maxUploadSize > 0 && contentLength > s.maxUploadSize {
@@ -668,11 +505,26 @@ func (s *Server) putHandler(w http.ResponseWriter, r *http.Request) {
return
}
if s.performClamavPrescan {
status, err := s.performScan(file.Name())
if err != nil {
s.logger.Printf("%s", err.Error())
http.Error(w, "Could not perform prescan", http.StatusInternalServerError)
return
}
if status != clamavScanStatusOK {
s.logger.Printf("prescan positive: %s", status)
http.Error(w, "Clamav prescan found a virus", http.StatusPreconditionFailed)
return
}
}
contentType := mime.TypeByExtension(filepath.Ext(vars["filename"]))
token := token(s.randomTokenLength)
metadata := metadataForRequest(contentType, contentLength, s.randomTokenLength, r)
metadata := metadataForRequest(contentType, s.randomTokenLength, r)
buffer := &bytes.Buffer{}
if err := json.NewEncoder(buffer).Encode(metadata); err != nil {
@@ -691,13 +543,7 @@ func (s *Server) putHandler(w http.ResponseWriter, r *http.Request) {
s.logger.Printf("Uploading %s %s %d %s", token, filename, contentLength, contentType)
reader, err := attachEncryptionReader(reader, r.Header.Get("X-Encrypt-Password"))
if err != nil {
http.Error(w, "Could not crypt file", http.StatusInternalServerError)
return
}
if err = s.storage.Put(r.Context(), token, filename, reader, contentType, uint64(contentLength)); err != nil {
if err = s.storage.Put(r.Context(), token, filename, file, contentType, uint64(contentLength)); err != nil {
s.logger.Printf("Error putting new file: %s", err.Error())
http.Error(w, "Could not save file", http.StatusInternalServerError)
return
@@ -1185,6 +1031,8 @@ func (s *Server) getHandler(w http.ResponseWriter, r *http.Request) {
reader, contentLength, err := s.storage.Get(r.Context(), token, filename, rng)
defer storage.CloseCheck(reader)
rdr := io.Reader(reader)
if s.storage.IsNotExist(err) {
http.Error(w, http.StatusText(http.StatusNotFound), http.StatusNotFound)
return
@@ -1199,15 +1047,18 @@ func (s *Server) getHandler(w http.ResponseWriter, r *http.Request) {
w.Header().Set("Accept-Ranges", "bytes")
w.Header().Set("Content-Range", cr)
if rng.Limit > 0 {
reader = io.NopCloser(io.LimitReader(reader, int64(rng.Limit)))
rdr = io.LimitReader(reader, int64(rng.Limit))
}
}
}
var disposition string
if action == "inline" {
disposition = "inline"
/*
metadata.ContentType is unable to determine the type of the content,
metadata.ContentType is unable to determine the type of the content,
metadata.ContentType is unable to determine the type of the content,
So add text/plain in this case to fix XSS related issues/
*/
@@ -1220,7 +1071,9 @@ func (s *Server) getHandler(w http.ResponseWriter, r *http.Request) {
remainingDownloads, remainingDays := metadata.remainingLimitHeaderValues()
w.Header().Set("Content-Disposition", fmt.Sprintf(`%s; filename="%s"`, disposition, filename))
w.Header().Set("Content-Type", contentType)
w.Header().Set("Content-Length", strconv.FormatUint(contentLength, 10))
w.Header().Set("Content-Disposition", fmt.Sprintf("%s; filename=\"%s\"", disposition, filename))
w.Header().Set("Connection", "keep-alive")
w.Header().Set("Cache-Control", "no-store")
w.Header().Set("X-Remaining-Downloads", remainingDownloads)
@@ -1231,25 +1084,10 @@ func (s *Server) getHandler(w http.ResponseWriter, r *http.Request) {
}
if disposition == "inline" && canContainsXSS(contentType) {
reader = io.NopCloser(bluemonday.UGCPolicy().SanitizeReader(reader))
reader = ioutil.NopCloser(bluemonday.UGCPolicy().SanitizeReader(reader))
}
password := r.Header.Get("X-Decrypt-Password")
decryptionReader, err := attachDecryptionReader(reader, password)
if err != nil {
http.Error(w, "Could not decrypt file", http.StatusInternalServerError)
return
}
if metadata.Encrypted && len(password) > 0 {
contentType = metadata.DecryptedContentType
contentLength = uint64(metadata.ContentLength)
}
w.Header().Set("Content-Type", contentType)
w.Header().Set("Content-Length", strconv.FormatUint(contentLength, 10))
if _, err = io.Copy(w, decryptionReader); err != nil {
if _, err = io.Copy(w, rdr); err != nil {
s.logger.Printf("%s", err.Error())
http.Error(w, "Error occurred copying to output stream", http.StatusInternalServerError)
return

View File

@@ -21,13 +21,13 @@ import (
"github.com/tomasen/realip"
)
// IPFilterOptions for ipFilter. Allowed takes precedence over Blocked.
// IPs can be IPv4 or IPv6 and can optionally contain subnet
// masks (/24). Note however, determining if a given IP is
// included in a subnet requires a linear scan so is less performant
// than looking up single IPs.
//IPFilterOptions for ipFilter. Allowed takes precedence over Blocked.
//IPs can be IPv4 or IPv6 and can optionally contain subnet
//masks (/24). Note however, determining if a given IP is
//included in a subnet requires a linear scan so is less performant
//than looking up single IPs.
//
// This could be improved with some algorithmic magic.
//This could be improved with some algorithmic magic.
type IPFilterOptions struct {
//explicity allowed IPs
AllowedIPs []string
@@ -127,19 +127,19 @@ func (f *ipFilter) ToggleIP(str string, allowed bool) bool {
return false
}
// ToggleDefault alters the default setting
//ToggleDefault alters the default setting
func (f *ipFilter) ToggleDefault(allowed bool) {
f.mut.Lock()
f.defaultAllowed = allowed
f.mut.Unlock()
}
// Allowed returns if a given IP can pass through the filter
//Allowed returns if a given IP can pass through the filter
func (f *ipFilter) Allowed(ipstr string) bool {
return f.NetAllowed(net.ParseIP(ipstr))
}
// NetAllowed returns if a given net.IP can pass through the filter
//NetAllowed returns if a given net.IP can pass through the filter
func (f *ipFilter) NetAllowed(ip net.IP) bool {
//invalid ip
if ip == nil {
@@ -172,23 +172,23 @@ func (f *ipFilter) NetAllowed(ip net.IP) bool {
return f.defaultAllowed
}
// Blocked returns if a given IP can NOT pass through the filter
//Blocked returns if a given IP can NOT pass through the filter
func (f *ipFilter) Blocked(ip string) bool {
return !f.Allowed(ip)
}
// NetBlocked returns if a given net.IP can NOT pass through the filter
//NetBlocked returns if a given net.IP can NOT pass through the filter
func (f *ipFilter) NetBlocked(ip net.IP) bool {
return !f.NetAllowed(ip)
}
// Wrap the provided handler with simple IP blocking middleware
// using this IP filter and its configuration
//Wrap the provided handler with simple IP blocking middleware
//using this IP filter and its configuration
func (f *ipFilter) Wrap(next http.Handler) http.Handler {
return &ipFilterMiddleware{ipFilter: f, next: next}
}
// WrapIPFilter is equivalent to newIPFilter(opts) then Wrap(next)
//WrapIPFilter is equivalent to newIPFilter(opts) then Wrap(next)
func WrapIPFilter(next http.Handler, opts IPFilterOptions) http.Handler {
return newIPFilter(opts).Wrap(next)
}

View File

@@ -466,6 +466,8 @@ func (s *Server) Run() {
r.HandleFunc("/{action:(?:download|get|inline)}/{token}/{filename}", s.headHandler).Methods("HEAD")
r.HandleFunc("/{token}/{filename}", s.previewHandler).MatcherFunc(func(r *http.Request, rm *mux.RouteMatch) (match bool) {
match = false
// The file will show a preview page when opening the link in browser directly or
// from external link. If the referer url path and current path are the same it will be
// downloaded.

View File

@@ -4,9 +4,10 @@ import (
"context"
"fmt"
"io"
"regexp"
"strconv"
"time"
"regexp"
)
type Range struct {

View File

@@ -83,18 +83,15 @@ func (s *StorjStorage) Get(ctx context.Context, token string, filename string, r
s.logger.Printf("Getting file %s from Storj Bucket", filename)
var options *uplink.DownloadOptions
options := uplink.DownloadOptions{}
if rng != nil {
options = new(uplink.DownloadOptions)
options.Offset = int64(rng.Start)
if rng.Limit > 0 {
options.Length = int64(rng.Limit)
} else {
options.Length = -1
}
}
download, err := s.project.DownloadObject(fpath.WithTempData(ctx, "", true), s.bucket.Name, key, options)
download, err := s.project.DownloadObject(fpath.WithTempData(ctx, "", true), s.bucket.Name, key, &options)
if err != nil {
return nil, 0, err
}