alist/server/webdav/file.go

203 lines
4.8 KiB
Go

// Copyright 2014 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package webdav
import (
"context"
"fmt"
"github.com/Xhofe/alist/conf"
"github.com/Xhofe/alist/drivers"
"github.com/Xhofe/alist/model"
"github.com/Xhofe/alist/utils"
log "github.com/sirupsen/logrus"
"net/http"
"path"
"path/filepath"
"strings"
"time"
)
type FileSystem struct{}
func ParsePath(rawPath string) (*model.Account, string, drivers.Driver, error) {
var path, name string
switch model.AccountsCount() {
case 0:
return nil, "", nil, fmt.Errorf("no accounts,please add one first")
case 1:
path = rawPath
break
default:
paths := strings.Split(rawPath, "/")
path = "/" + strings.Join(paths[2:], "/")
name = paths[1]
}
account, ok := model.GetAccount(name)
if !ok {
return nil, "", nil, fmt.Errorf("no [%s] account", name)
}
driver, ok := drivers.GetDriver(account.Type)
if !ok {
return nil, "", nil, fmt.Errorf("no [%s] driver", account.Type)
}
return &account, path, driver, nil
}
func (fs *FileSystem) File(rawPath string) (*model.File, error) {
rawPath = utils.ParsePath(rawPath)
if model.AccountsCount() > 1 && rawPath == "/" {
now := time.Now()
return &model.File{
Name: "root",
Size: 0,
Type: conf.FOLDER,
Driver: "root",
UpdatedAt: &now,
}, nil
}
account, path_, driver, err := ParsePath(rawPath)
if err != nil {
return nil, err
}
return driver.File(path_, account)
}
func (fs *FileSystem) Files(rawPath string) ([]model.File, error) {
rawPath = utils.ParsePath(rawPath)
if model.AccountsCount() > 1 && rawPath == "/" {
files, err := model.GetAccountFiles()
if err != nil {
return nil, err
}
return files, nil
}
account, path_, driver, err := ParsePath(rawPath)
if err != nil {
return nil, err
}
return driver.Files(path_, account)
}
func GetPW(path string) string {
if !conf.CheckDown {
return ""
}
meta, err := model.GetMetaByPath(path)
if err == nil {
if meta.Password != "" {
utils.Get16MD5Encode(meta.Password)
}
return ""
} else {
if !conf.CheckParent {
return ""
}
if path == "/" {
return ""
}
return GetPW(utils.Dir(path))
}
}
func (fs *FileSystem) Link(r *http.Request, rawPath string) (string, error) {
rawPath = utils.ParsePath(rawPath)
log.Debugf("get link path: %s", rawPath)
if model.AccountsCount() > 1 && rawPath == "/" {
// error
}
account, path_, driver, err := ParsePath(rawPath)
if err != nil {
return "", err
}
link := ""
protocol := "http"
if r.TLS != nil {
protocol = "https"
}
if driver.Config().OnlyProxy || account.WebdavProxy {
link = fmt.Sprintf("%s://%s/p%s", protocol, r.Host, rawPath)
if conf.CheckDown {
pw := GetPW(utils.Dir(rawPath))
link += "?pw" + pw
}
} else {
link, err = driver.Link(path_, account)
}
log.Debugf("webdav get link: %s", link)
return link, err
}
func (fs *FileSystem) CreateDirectory(ctx context.Context, reqPath string) (interface{}, error) {
return nil, nil
}
// slashClean is equivalent to but slightly more efficient than
// path.Clean("/" + name).
func slashClean(name string) string {
if name == "" || name[0] != '/' {
name = "/" + name
}
return path.Clean(name)
}
// moveFiles moves files and/or directories from src to dst.
//
// See section 9.9.4 for when various HTTP status codes apply.
func moveFiles(ctx context.Context, fs *FileSystem, src FileInfo, dst string, overwrite bool) (status int, err error) {
return http.StatusNoContent, nil
}
// copyFiles copies files and/or directories from src to dst.
//
// See section 9.8.5 for when various HTTP status codes apply.
func copyFiles(ctx context.Context, fs *FileSystem, src FileInfo, dst string, overwrite bool, depth int, recursion int) (status int, err error) {
return http.StatusNoContent, nil
}
// walkFS traverses filesystem fs starting at name up to depth levels.
//
// Allowed values for depth are 0, 1 or infiniteDepth. For each visited node,
// walkFS calls walkFn. If a visited file system node is a directory and
// walkFn returns filepath.SkipDir, walkFS will skip traversal of this node.
func walkFS(
ctx context.Context,
fs *FileSystem,
depth int,
name string,
info FileInfo,
walkFn func(reqPath string, info FileInfo, err error) error) error {
// This implementation is based on Walk's code in the standard path/filepath package.
err := walkFn(name, info, nil)
if err != nil {
if info.IsDir() && err == filepath.SkipDir {
return nil
}
return err
}
if !info.IsDir() || depth == 0 {
return nil
}
if depth == 1 {
depth = 0
}
files, err := fs.Files(name)
if err != nil {
return err
}
for _, fileInfo := range files {
filename := path.Join(name, fileInfo.Name)
err = walkFS(ctx, fs, depth, filename, &fileInfo, walkFn)
if err != nil {
if !fileInfo.IsDir() || err != filepath.SkipDir {
return err
}
}
}
return nil
}