|
|
@@ -58,6 +58,12 @@ import (
|
|
|
|
"github.com/microcosm-cc/bluemonday"
|
|
|
|
"github.com/microcosm-cc/bluemonday"
|
|
|
|
blackfriday "github.com/russross/blackfriday/v2"
|
|
|
|
blackfriday "github.com/russross/blackfriday/v2"
|
|
|
|
"github.com/skip2/go-qrcode"
|
|
|
|
"github.com/skip2/go-qrcode"
|
|
|
|
|
|
|
|
//lint:ignore SA1019 Ignore the deprecation warnings
|
|
|
|
|
|
|
|
"golang.org/x/crypto/openpgp"
|
|
|
|
|
|
|
|
//lint:ignore SA1019 Ignore the deprecation warnings
|
|
|
|
|
|
|
|
"golang.org/x/crypto/openpgp/armor"
|
|
|
|
|
|
|
|
//lint:ignore SA1019 Ignore the deprecation warnings
|
|
|
|
|
|
|
|
"golang.org/x/crypto/openpgp/packet"
|
|
|
|
)
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
|
|
const getPathPart = "get"
|
|
|
|
const getPathPart = "get"
|
|
|
@@ -65,6 +71,10 @@ const getPathPart = "get"
|
|
|
|
var (
|
|
|
|
var (
|
|
|
|
htmlTemplates = initHTMLTemplates()
|
|
|
|
htmlTemplates = initHTMLTemplates()
|
|
|
|
textTemplates = initTextTemplates()
|
|
|
|
textTemplates = initTextTemplates()
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
packetConfig = &packet.Config{
|
|
|
|
|
|
|
|
DefaultCipher: packet.CipherAES256,
|
|
|
|
|
|
|
|
}
|
|
|
|
)
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
|
|
func stripPrefix(path string) string {
|
|
|
|
func stripPrefix(path string) string {
|
|
|
@@ -88,6 +98,96 @@ func initHTMLTemplates() *html_template.Template {
|
|
|
|
return templates
|
|
|
|
return templates
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
func transformEncryptionReader(reader io.ReadCloser, password string) (io.Reader, error) {
|
|
|
|
|
|
|
|
if len(password) == 0 {
|
|
|
|
|
|
|
|
return reader, nil
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
return encrypt(reader, password, packetConfig)
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
func transformDecryptionReader(reader io.ReadCloser, password string) (io.Reader, error) {
|
|
|
|
|
|
|
|
if len(password) == 0 {
|
|
|
|
|
|
|
|
return reader, nil
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
return decrypt(reader, password, packetConfig)
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
func decrypt(ciphertext io.ReadCloser, password string, packetConfig *packet.Config) (plaintext io.Reader, err error) {
|
|
|
|
|
|
|
|
content, err := ioutil.ReadAll(ciphertext)
|
|
|
|
|
|
|
|
if err != nil {
|
|
|
|
|
|
|
|
return
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
decbuf := bytes.NewBuffer(content)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
armorBlock, err := armor.Decode(decbuf)
|
|
|
|
|
|
|
|
if err != nil {
|
|
|
|
|
|
|
|
return
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
failed := false
|
|
|
|
|
|
|
|
prompt := func(keys []openpgp.Key, symmetric bool) ([]byte, error) {
|
|
|
|
|
|
|
|
// If the given passphrase isn't correct, the function will be called again, forever.
|
|
|
|
|
|
|
|
// This method will fail fast.
|
|
|
|
|
|
|
|
// Ref: https://godoc.org/golang.org/x/crypto/openpgp#PromptFunction
|
|
|
|
|
|
|
|
if failed {
|
|
|
|
|
|
|
|
return nil, errors.New("decryption failed")
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
failed = true
|
|
|
|
|
|
|
|
return []byte(password), nil
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
md, err := openpgp.ReadMessage(armorBlock.Body, nil, prompt, packetConfig)
|
|
|
|
|
|
|
|
if err != nil {
|
|
|
|
|
|
|
|
return
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
plaintext = md.UnverifiedBody
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
return
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
func encrypt(plaintext io.ReadCloser, password string, packetConfig *packet.Config) (ciphertext io.Reader, err error) {
|
|
|
|
|
|
|
|
encbuf := bytes.NewBuffer(nil)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
w, err := armor.Encode(encbuf, "PGP MESSAGE", nil)
|
|
|
|
|
|
|
|
if err != nil {
|
|
|
|
|
|
|
|
safeClose(w)
|
|
|
|
|
|
|
|
return
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
pt, err := openpgp.SymmetricallyEncrypt(w, []byte(password), nil, packetConfig)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
if err != nil {
|
|
|
|
|
|
|
|
safeClose(pt)
|
|
|
|
|
|
|
|
safeClose(w)
|
|
|
|
|
|
|
|
return
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
content, err := ioutil.ReadAll(plaintext)
|
|
|
|
|
|
|
|
if err != nil {
|
|
|
|
|
|
|
|
safeClose(pt)
|
|
|
|
|
|
|
|
safeClose(w)
|
|
|
|
|
|
|
|
return
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
_, err = pt.Write(content)
|
|
|
|
|
|
|
|
if err != nil {
|
|
|
|
|
|
|
|
safeClose(pt)
|
|
|
|
|
|
|
|
safeClose(w)
|
|
|
|
|
|
|
|
return
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// Close writers to force-flush their buffer
|
|
|
|
|
|
|
|
safeClose(pt)
|
|
|
|
|
|
|
|
safeClose(w)
|
|
|
|
|
|
|
|
ciphertext = bytes.NewReader(encbuf.Bytes())
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
return
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
func healthHandler(w http.ResponseWriter, r *http.Request) {
|
|
|
|
func healthHandler(w http.ResponseWriter, r *http.Request) {
|
|
|
|
_, _ = w.Write([]byte("Approaching Neutral Zone, all systems normal and functioning."))
|
|
|
|
_, _ = w.Write([]byte("Approaching Neutral Zone, all systems normal and functioning."))
|
|
|
|
}
|
|
|
|
}
|
|
|
@@ -360,7 +460,7 @@ func (s *Server) postHandler(w http.ResponseWriter, r *http.Request) {
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
metadata := metadataForRequest(contentType, s.randomTokenLength, r)
|
|
|
|
metadata := metadataForRequest(contentType, contentLength, 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 {
|
|
|
@@ -377,7 +477,13 @@ func (s *Server) postHandler(w http.ResponseWriter, r *http.Request) {
|
|
|
|
|
|
|
|
|
|
|
|
s.logger.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(r.Context(), token, filename, file, contentType, uint64(contentLength)); err != nil {
|
|
|
|
reader, err := transformEncryptionReader(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 {
|
|
|
|
s.logger.Printf("Backend storage error: %s", err.Error())
|
|
|
|
s.logger.Printf("Backend storage error: %s", err.Error())
|
|
|
|
http.Error(w, err.Error(), http.StatusInternalServerError)
|
|
|
|
http.Error(w, err.Error(), http.StatusInternalServerError)
|
|
|
|
return
|
|
|
|
return
|
|
|
@@ -415,8 +521,8 @@ func (s *Server) cleanTmpFile(f *os.File) {
|
|
|
|
type metadata struct {
|
|
|
|
type metadata struct {
|
|
|
|
// ContentType is the original uploading content type
|
|
|
|
// ContentType is the original uploading content type
|
|
|
|
ContentType string
|
|
|
|
ContentType string
|
|
|
|
// Secret as knowledge to delete file
|
|
|
|
// ContentLength is is the original uploading content length
|
|
|
|
// Secret string
|
|
|
|
ContentLength int64
|
|
|
|
// Downloads is the actual number of downloads
|
|
|
|
// Downloads is the actual number of downloads
|
|
|
|
Downloads int
|
|
|
|
Downloads int
|
|
|
|
// MaxDownloads contains the maximum numbers of downloads
|
|
|
|
// MaxDownloads contains the maximum numbers of downloads
|
|
|
@@ -425,11 +531,16 @@ type metadata struct {
|
|
|
|
MaxDate time.Time
|
|
|
|
MaxDate time.Time
|
|
|
|
// DeletionToken contains the token to match against for deletion
|
|
|
|
// DeletionToken contains the token to match against for deletion
|
|
|
|
DeletionToken string
|
|
|
|
DeletionToken string
|
|
|
|
|
|
|
|
// Encrypted contains if the file was encrypted
|
|
|
|
|
|
|
|
Encrypted bool
|
|
|
|
|
|
|
|
// DecryptedContentType is the original uploading content type
|
|
|
|
|
|
|
|
DecryptedContentType string
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
func metadataForRequest(contentType string, randomTokenLength int, r *http.Request) metadata {
|
|
|
|
func metadataForRequest(contentType string, contentLength int64, randomTokenLength int, r *http.Request) metadata {
|
|
|
|
metadata := metadata{
|
|
|
|
metadata := metadata{
|
|
|
|
ContentType: strings.ToLower(contentType),
|
|
|
|
ContentType: strings.ToLower(contentType),
|
|
|
|
|
|
|
|
ContentLength: contentLength,
|
|
|
|
MaxDate: time.Time{},
|
|
|
|
MaxDate: time.Time{},
|
|
|
|
Downloads: 0,
|
|
|
|
Downloads: 0,
|
|
|
|
MaxDownloads: -1,
|
|
|
|
MaxDownloads: -1,
|
|
|
@@ -448,6 +559,14 @@ func metadataForRequest(contentType string, randomTokenLength int, r *http.Reque
|
|
|
|
metadata.MaxDate = time.Now().Add(time.Hour * 24 * time.Duration(v))
|
|
|
|
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
|
|
|
|
return metadata
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
@@ -458,7 +577,7 @@ func (s *Server) putHandler(w http.ResponseWriter, r *http.Request) {
|
|
|
|
|
|
|
|
|
|
|
|
contentLength := r.ContentLength
|
|
|
|
contentLength := r.ContentLength
|
|
|
|
|
|
|
|
|
|
|
|
defer CloseCheck(r.Body.Close)
|
|
|
|
defer safeClose(r.Body)
|
|
|
|
|
|
|
|
|
|
|
|
file, err := ioutil.TempFile(s.tempPath, "transfer-")
|
|
|
|
file, err := ioutil.TempFile(s.tempPath, "transfer-")
|
|
|
|
defer s.cleanTmpFile(file)
|
|
|
|
defer s.cleanTmpFile(file)
|
|
|
@@ -521,7 +640,7 @@ func (s *Server) putHandler(w http.ResponseWriter, r *http.Request) {
|
|
|
|
|
|
|
|
|
|
|
|
token := token(s.randomTokenLength)
|
|
|
|
token := token(s.randomTokenLength)
|
|
|
|
|
|
|
|
|
|
|
|
metadata := metadataForRequest(contentType, s.randomTokenLength, r)
|
|
|
|
metadata := metadataForRequest(contentType, contentLength, 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 {
|
|
|
@@ -540,7 +659,13 @@ func (s *Server) putHandler(w http.ResponseWriter, r *http.Request) {
|
|
|
|
|
|
|
|
|
|
|
|
s.logger.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(r.Context(), token, filename, file, contentType, uint64(contentLength)); err != nil {
|
|
|
|
reader, err := transformEncryptionReader(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 {
|
|
|
|
s.logger.Printf("Error putting new file: %s", err.Error())
|
|
|
|
s.logger.Printf("Error putting new file: %s", err.Error())
|
|
|
|
http.Error(w, "Could not save file", http.StatusInternalServerError)
|
|
|
|
http.Error(w, "Could not save file", http.StatusInternalServerError)
|
|
|
|
return
|
|
|
|
return
|
|
|
@@ -651,6 +776,7 @@ func (metadata metadata) remainingLimitHeaderValues() (remainingDownloads, remai
|
|
|
|
remainingDays = strconv.Itoa(int(timeDifference.Hours()/24) + 1)
|
|
|
|
remainingDays = strconv.Itoa(int(timeDifference.Hours()/24) + 1)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
if metadata.MaxDownloads == -1 {
|
|
|
|
if metadata.MaxDownloads == -1 {
|
|
|
|
remainingDownloads = "n/a"
|
|
|
|
remainingDownloads = "n/a"
|
|
|
|
} else {
|
|
|
|
} else {
|
|
|
@@ -683,7 +809,7 @@ func (s *Server) checkMetadata(ctx context.Context, token, filename string, incr
|
|
|
|
var metadata metadata
|
|
|
|
var metadata metadata
|
|
|
|
|
|
|
|
|
|
|
|
r, _, err := s.storage.Get(ctx, token, fmt.Sprintf("%s.metadata", filename))
|
|
|
|
r, _, err := s.storage.Get(ctx, token, fmt.Sprintf("%s.metadata", filename))
|
|
|
|
defer CloseCheck(r.Close)
|
|
|
|
defer safeClose(r)
|
|
|
|
|
|
|
|
|
|
|
|
if err != nil {
|
|
|
|
if err != nil {
|
|
|
|
return metadata, err
|
|
|
|
return metadata, err
|
|
|
@@ -719,7 +845,7 @@ func (s *Server) checkDeletionToken(ctx context.Context, deletionToken, token, f
|
|
|
|
var metadata metadata
|
|
|
|
var metadata metadata
|
|
|
|
|
|
|
|
|
|
|
|
r, _, err := s.storage.Get(ctx, token, fmt.Sprintf("%s.metadata", filename))
|
|
|
|
r, _, err := s.storage.Get(ctx, token, fmt.Sprintf("%s.metadata", filename))
|
|
|
|
defer CloseCheck(r.Close)
|
|
|
|
defer safeClose(r)
|
|
|
|
|
|
|
|
|
|
|
|
if s.storage.IsNotExist(err) {
|
|
|
|
if s.storage.IsNotExist(err) {
|
|
|
|
return errors.New("metadata doesn't exist")
|
|
|
|
return errors.New("metadata doesn't exist")
|
|
|
@@ -798,7 +924,7 @@ func (s *Server) zipHandler(w http.ResponseWriter, r *http.Request) {
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
reader, _, err := s.storage.Get(r.Context(), token, filename)
|
|
|
|
reader, _, err := s.storage.Get(r.Context(), token, filename)
|
|
|
|
defer CloseCheck(reader.Close)
|
|
|
|
defer safeClose(reader)
|
|
|
|
|
|
|
|
|
|
|
|
if err != nil {
|
|
|
|
if err != nil {
|
|
|
|
if s.storage.IsNotExist(err) {
|
|
|
|
if s.storage.IsNotExist(err) {
|
|
|
@@ -852,10 +978,10 @@ func (s *Server) tarGzHandler(w http.ResponseWriter, r *http.Request) {
|
|
|
|
w.Header().Set("Connection", "close")
|
|
|
|
w.Header().Set("Connection", "close")
|
|
|
|
|
|
|
|
|
|
|
|
gw := gzip.NewWriter(w)
|
|
|
|
gw := gzip.NewWriter(w)
|
|
|
|
defer CloseCheck(gw.Close)
|
|
|
|
defer safeClose(gw)
|
|
|
|
|
|
|
|
|
|
|
|
zw := tar.NewWriter(gw)
|
|
|
|
zw := tar.NewWriter(gw)
|
|
|
|
defer CloseCheck(zw.Close)
|
|
|
|
defer safeClose(zw)
|
|
|
|
|
|
|
|
|
|
|
|
for _, key := range strings.Split(files, ",") {
|
|
|
|
for _, key := range strings.Split(files, ",") {
|
|
|
|
key = resolveKey(key, s.proxyPath)
|
|
|
|
key = resolveKey(key, s.proxyPath)
|
|
|
@@ -869,7 +995,7 @@ func (s *Server) tarGzHandler(w http.ResponseWriter, r *http.Request) {
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
reader, contentLength, err := s.storage.Get(r.Context(), token, filename)
|
|
|
|
reader, contentLength, err := s.storage.Get(r.Context(), token, filename)
|
|
|
|
defer CloseCheck(reader.Close)
|
|
|
|
defer safeClose(reader)
|
|
|
|
|
|
|
|
|
|
|
|
if err != nil {
|
|
|
|
if err != nil {
|
|
|
|
if s.storage.IsNotExist(err) {
|
|
|
|
if s.storage.IsNotExist(err) {
|
|
|
@@ -914,7 +1040,7 @@ func (s *Server) tarHandler(w http.ResponseWriter, r *http.Request) {
|
|
|
|
w.Header().Set("Connection", "close")
|
|
|
|
w.Header().Set("Connection", "close")
|
|
|
|
|
|
|
|
|
|
|
|
zw := tar.NewWriter(w)
|
|
|
|
zw := tar.NewWriter(w)
|
|
|
|
defer CloseCheck(zw.Close)
|
|
|
|
defer safeClose(zw)
|
|
|
|
|
|
|
|
|
|
|
|
for _, key := range strings.Split(files, ",") {
|
|
|
|
for _, key := range strings.Split(files, ",") {
|
|
|
|
key = resolveKey(key, s.proxyPath)
|
|
|
|
key = resolveKey(key, s.proxyPath)
|
|
|
@@ -928,7 +1054,7 @@ func (s *Server) tarHandler(w http.ResponseWriter, r *http.Request) {
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
reader, contentLength, err := s.storage.Get(r.Context(), token, filename)
|
|
|
|
reader, contentLength, err := s.storage.Get(r.Context(), token, filename)
|
|
|
|
defer CloseCheck(reader.Close)
|
|
|
|
defer safeClose(reader)
|
|
|
|
|
|
|
|
|
|
|
|
if err != nil {
|
|
|
|
if err != nil {
|
|
|
|
if s.storage.IsNotExist(err) {
|
|
|
|
if s.storage.IsNotExist(err) {
|
|
|
@@ -1012,7 +1138,7 @@ func (s *Server) getHandler(w http.ResponseWriter, r *http.Request) {
|
|
|
|
|
|
|
|
|
|
|
|
contentType := metadata.ContentType
|
|
|
|
contentType := metadata.ContentType
|
|
|
|
reader, contentLength, err := s.storage.Get(r.Context(), token, filename)
|
|
|
|
reader, contentLength, err := s.storage.Get(r.Context(), token, filename)
|
|
|
|
defer CloseCheck(reader.Close)
|
|
|
|
defer safeClose(reader)
|
|
|
|
|
|
|
|
|
|
|
|
if s.storage.IsNotExist(err) {
|
|
|
|
if s.storage.IsNotExist(err) {
|
|
|
|
http.Error(w, http.StatusText(http.StatusNotFound), http.StatusNotFound)
|
|
|
|
http.Error(w, http.StatusText(http.StatusNotFound), http.StatusNotFound)
|
|
|
@@ -1024,7 +1150,6 @@ func (s *Server) getHandler(w http.ResponseWriter, r *http.Request) {
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
var disposition string
|
|
|
|
var disposition string
|
|
|
|
|
|
|
|
|
|
|
|
if action == "inline" {
|
|
|
|
if action == "inline" {
|
|
|
|
disposition = "inline"
|
|
|
|
disposition = "inline"
|
|
|
|
} else {
|
|
|
|
} else {
|
|
|
@@ -1033,9 +1158,7 @@ func (s *Server) getHandler(w http.ResponseWriter, r *http.Request) {
|
|
|
|
|
|
|
|
|
|
|
|
remainingDownloads, remainingDays := metadata.remainingLimitHeaderValues()
|
|
|
|
remainingDownloads, remainingDays := metadata.remainingLimitHeaderValues()
|
|
|
|
|
|
|
|
|
|
|
|
w.Header().Set("Content-Type", contentType)
|
|
|
|
w.Header().Set("Content-Disposition", fmt.Sprintf(`%s; filename="%s"`, disposition, filename))
|
|
|
|
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("Connection", "keep-alive")
|
|
|
|
w.Header().Set("X-Remaining-Downloads", remainingDownloads)
|
|
|
|
w.Header().Set("X-Remaining-Downloads", remainingDownloads)
|
|
|
|
w.Header().Set("X-Remaining-Days", remainingDays)
|
|
|
|
w.Header().Set("X-Remaining-Days", remainingDays)
|
|
|
@@ -1065,7 +1188,22 @@ func (s *Server) getHandler(w http.ResponseWriter, r *http.Request) {
|
|
|
|
return
|
|
|
|
return
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
if _, err = io.Copy(w, reader); err != nil {
|
|
|
|
password := r.Header.Get("X-Decrypt-Password");
|
|
|
|
|
|
|
|
decryptionReader, err := transformDecryptionReader(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 {
|
|
|
|
s.logger.Printf("%s", err.Error())
|
|
|
|
s.logger.Printf("%s", err.Error())
|
|
|
|
http.Error(w, "Error occurred copying to output stream", http.StatusInternalServerError)
|
|
|
|
http.Error(w, "Error occurred copying to output stream", http.StatusInternalServerError)
|
|
|
|
return
|
|
|
|
return
|
|
|
|