mirror of
https://github.com/mickael-kerjean/filestash.git
synced 2026-10-20 17:11:32 +00:00
139 lines
3.4 KiB
Go
139 lines
3.4 KiB
Go
package files
|
|
|
|
import (
|
|
"archive/zip"
|
|
"context"
|
|
"fmt"
|
|
"io"
|
|
"net/http"
|
|
"os"
|
|
"path/filepath"
|
|
"strings"
|
|
"time"
|
|
|
|
. "github.com/mickael-kerjean/filestash/server/pkg/core"
|
|
. "github.com/mickael-kerjean/filestash/server/pkg/kernel"
|
|
. "github.com/mickael-kerjean/filestash/server/pkg/utils"
|
|
. "github.com/mickael-kerjean/filestash/server/pkg/permissions"
|
|
)
|
|
|
|
func FileExtract(ctx *App, res http.ResponseWriter, req *http.Request) {
|
|
if CanRead(ctx) == false {
|
|
Log.Debug("extract::permission 'permission denied'")
|
|
SendErrorResult(res, ErrPermissionDenied)
|
|
return
|
|
}
|
|
paths := req.URL.Query()["path"]
|
|
for _, auth := range Hooks.Get.AuthorisationMiddleware() {
|
|
for i := 0; i < len(paths); i++ {
|
|
if err := auth.Mkdir(ctx, paths[i]); err != nil {
|
|
Log.Debug("extract::permission::mkdir %s", err.Error())
|
|
SendErrorResult(res, ErrNotAuthorized)
|
|
return
|
|
} else if err := auth.Save(ctx, paths[i]); err != nil {
|
|
Log.Debug("extract::permission::Save %s", err.Error())
|
|
SendErrorResult(res, ErrNotAuthorized)
|
|
return
|
|
}
|
|
}
|
|
}
|
|
|
|
c, cancel := context.WithTimeout(ctx.Context, time.Duration(zip_timeout())*time.Second)
|
|
extractPath := func(base string, path string) (string, error) {
|
|
base = EnforceDirectory(filepath.Dir(base))
|
|
path = filepath.Join(base, path)
|
|
if strings.HasPrefix(path, base) == false {
|
|
return "", ErrFilesystemError
|
|
}
|
|
return path, nil
|
|
}
|
|
extractZip := func(path string) (err error) {
|
|
if err = c.Err(); err != nil {
|
|
cancel()
|
|
return ErrTimeout
|
|
}
|
|
|
|
zipFile, err := ctx.Backend.Cat(path)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
defer zipFile.Close()
|
|
f, err := os.CreateTemp("", "tmpzip.*.zip")
|
|
if err != nil {
|
|
Log.Debug("extract::create_temp '%s'", err.Error())
|
|
return nil
|
|
}
|
|
defer os.Remove(f.Name())
|
|
io.Copy(f, zipFile)
|
|
s, err := f.Stat()
|
|
if err != nil {
|
|
return err
|
|
}
|
|
r, err := zip.NewReader(f, s.Size())
|
|
if err != nil {
|
|
return err
|
|
}
|
|
isFolderAlreadyCreated := map[string]bool{
|
|
fmt.Sprintf("%s/", filepath.Dir(path)): true,
|
|
}
|
|
for _, f := range r.File {
|
|
time.Sleep(2 * time.Millisecond)
|
|
if err = c.Err(); err != nil {
|
|
cancel()
|
|
return ErrTimeout
|
|
}
|
|
// STEP1: ensure the underlying folders exists
|
|
spl := strings.Split(f.Name, "/")
|
|
for i, p := range spl {
|
|
if p == "" {
|
|
continue
|
|
}
|
|
p = strings.Join(spl[0:i], "/")
|
|
p, err = extractPath(path, p)
|
|
if strings.HasSuffix(p, "/") == false {
|
|
p += "/"
|
|
}
|
|
if isFolderAlreadyCreated[p] {
|
|
continue
|
|
}
|
|
isFolderAlreadyCreated[p] = true
|
|
if err := ctx.Backend.Mkdir(p); err != nil {
|
|
Log.Debug("extract::mkdir err %s", err.Error())
|
|
}
|
|
}
|
|
// STEP2: create the file
|
|
if f.FileInfo().IsDir() == false {
|
|
p, err := extractPath(path, f.Name)
|
|
if err != nil {
|
|
Log.Debug("extract::chroot %s", err.Error())
|
|
return err
|
|
}
|
|
rc, err := f.Open()
|
|
if err != nil {
|
|
Log.Debug("extract::fopen %s", err.Error())
|
|
return err
|
|
}
|
|
err = ctx.Backend.Save(p, rc)
|
|
rc.Close()
|
|
if err != nil {
|
|
Log.Debug("extract::save err %s", err.Error())
|
|
}
|
|
}
|
|
}
|
|
return nil
|
|
}
|
|
var err error
|
|
for i := 0; i < len(paths); i++ {
|
|
if paths[i], err = PathBuilder(ctx, paths[i]); err != nil {
|
|
Log.Debug("extract::path '%s'", err.Error())
|
|
SendErrorResult(res, err)
|
|
return
|
|
}
|
|
if err = extractZip(paths[i]); err != nil {
|
|
SendErrorResult(res, err)
|
|
return
|
|
}
|
|
}
|
|
SendSuccessResult(res, nil)
|
|
}
|