166 lines
3.4 KiB
Go
166 lines
3.4 KiB
Go
package main
|
|
|
|
import (
|
|
"archive/tar"
|
|
"archive/zip"
|
|
"compress/bzip2"
|
|
"compress/gzip"
|
|
"crypto/sha256"
|
|
"encoding/hex"
|
|
"errors"
|
|
"io"
|
|
"sort"
|
|
"time"
|
|
"unicode"
|
|
|
|
"github.com/andreimarcu/linx-server/backends"
|
|
"github.com/andreimarcu/linx-server/expiry"
|
|
"github.com/dchest/uniuri"
|
|
"gopkg.in/h2non/filetype.v1"
|
|
)
|
|
|
|
var NotFoundErr = errors.New("File not found.")
|
|
|
|
func generateMetadata(fName string, exp time.Time, delKey string) (m backends.Metadata, err error) {
|
|
file, err := fileBackend.Open(fName)
|
|
if err != nil {
|
|
return
|
|
}
|
|
defer file.Close()
|
|
|
|
m.Size, err = fileBackend.Size(fName)
|
|
if err != nil {
|
|
return
|
|
}
|
|
|
|
m.Expiry = exp
|
|
|
|
if delKey == "" {
|
|
m.DeleteKey = uniuri.NewLen(30)
|
|
} else {
|
|
m.DeleteKey = delKey
|
|
}
|
|
|
|
// Get first 512 bytes for mimetype detection
|
|
header := make([]byte, 512)
|
|
file.Read(header)
|
|
|
|
kind, err := filetype.Match(header)
|
|
if err != nil {
|
|
m.Mimetype = "application/octet-stream"
|
|
} else {
|
|
m.Mimetype = kind.MIME.Value
|
|
}
|
|
|
|
if m.Mimetype == "" {
|
|
// Check if the file seems anything like text
|
|
if printable(header) {
|
|
m.Mimetype = "text/plain"
|
|
} else {
|
|
m.Mimetype = "application/octet-stream"
|
|
}
|
|
}
|
|
|
|
// Compute the sha256sum
|
|
hasher := sha256.New()
|
|
file.Seek(0, 0)
|
|
_, err = io.Copy(hasher, file)
|
|
if err == nil {
|
|
m.Sha256sum = hex.EncodeToString(hasher.Sum(nil))
|
|
}
|
|
file.Seek(0, 0)
|
|
|
|
// If archive, grab list of filenames
|
|
if m.Mimetype == "application/x-tar" {
|
|
tReadr := tar.NewReader(file)
|
|
for {
|
|
hdr, err := tReadr.Next()
|
|
if err == io.EOF || err != nil {
|
|
break
|
|
}
|
|
if hdr.Typeflag == tar.TypeDir || hdr.Typeflag == tar.TypeReg {
|
|
m.ArchiveFiles = append(m.ArchiveFiles, hdr.Name)
|
|
}
|
|
}
|
|
sort.Strings(m.ArchiveFiles)
|
|
} else if m.Mimetype == "application/x-gzip" {
|
|
gzf, err := gzip.NewReader(file)
|
|
if err == nil {
|
|
tReadr := tar.NewReader(gzf)
|
|
for {
|
|
hdr, err := tReadr.Next()
|
|
if err == io.EOF || err != nil {
|
|
break
|
|
}
|
|
if hdr.Typeflag == tar.TypeDir || hdr.Typeflag == tar.TypeReg {
|
|
m.ArchiveFiles = append(m.ArchiveFiles, hdr.Name)
|
|
}
|
|
}
|
|
sort.Strings(m.ArchiveFiles)
|
|
}
|
|
} else if m.Mimetype == "application/x-bzip" {
|
|
bzf := bzip2.NewReader(file)
|
|
tReadr := tar.NewReader(bzf)
|
|
for {
|
|
hdr, err := tReadr.Next()
|
|
if err == io.EOF || err != nil {
|
|
break
|
|
}
|
|
if hdr.Typeflag == tar.TypeDir || hdr.Typeflag == tar.TypeReg {
|
|
m.ArchiveFiles = append(m.ArchiveFiles, hdr.Name)
|
|
}
|
|
}
|
|
sort.Strings(m.ArchiveFiles)
|
|
} else if m.Mimetype == "application/zip" {
|
|
zf, err := zip.NewReader(file, m.Size)
|
|
if err == nil {
|
|
for _, f := range zf.File {
|
|
m.ArchiveFiles = append(m.ArchiveFiles, f.Name)
|
|
}
|
|
}
|
|
sort.Strings(m.ArchiveFiles)
|
|
}
|
|
|
|
return
|
|
}
|
|
|
|
func metadataWrite(filename string, metadata *backends.Metadata) error {
|
|
return metaBackend.Put(filename, metadata)
|
|
}
|
|
|
|
func metadataRead(filename string) (metadata backends.Metadata, err error) {
|
|
metadata, err = metaBackend.Get(filename)
|
|
if err != nil {
|
|
// Metadata does not exist, generate one
|
|
newMData, err := generateMetadata(filename, expiry.NeverExpire, "")
|
|
if err != nil {
|
|
return metadata, err
|
|
}
|
|
metadataWrite(filename, &newMData)
|
|
|
|
metadata, err = metaBackend.Get(filename)
|
|
}
|
|
|
|
return
|
|
}
|
|
|
|
func printable(data []byte) bool {
|
|
for i, b := range data {
|
|
r := rune(b)
|
|
|
|
// A null terminator that's not at the beginning of the file
|
|
if r == 0 && i == 0 {
|
|
return false
|
|
} else if r == 0 && i < 0 {
|
|
continue
|
|
}
|
|
|
|
if r > unicode.MaxASCII {
|
|
return false
|
|
}
|
|
|
|
}
|
|
|
|
return true
|
|
}
|