dave/app/fs.go

100 lines
3 KiB
Go
Raw Normal View History

package app
import (
"context"
"github.com/abbot/go-http-auth"
"golang.org/x/net/webdav"
"os"
"path"
"path/filepath"
"strings"
)
// This file is an extension of golang.org/x/net/webdav/file.go.
// Dir is specialization of webdav.Dir with respect of an authenticated
// user to allow configuration access.
type Dir struct {
Config *Config
}
// resolve tries to gain authentication information and suffixes the BaseDir with the
// username of the authentication information. If none authentication information can
// achieved during the process, the BaseDir is used
func (d Dir) resolve(ctx context.Context, name string) string {
// This implementation is based on Dir.Open's code in the standard net/http package.
if filepath.Separator != '/' && strings.IndexRune(name, filepath.Separator) >= 0 ||
strings.Contains(name, "\x00") {
return ""
}
dir := string(d.Config.Dir)
if dir == "" {
dir = "."
}
authInfo := auth.FromContext(ctx)
if authInfo != nil && authInfo.Authenticated {
userInfo := d.Config.Users[authInfo.Username]
if userInfo != nil && userInfo.Subdir != nil {
return filepath.Join(dir, authInfo.Username, filepath.FromSlash(path.Clean("/"+name)))
}
}
return filepath.Join(dir, filepath.FromSlash(path.Clean("/"+name)))
}
// Mkdir resolves the physical file and delegates this to an os.Mkdir execution
func (d Dir) Mkdir(ctx context.Context, name string, perm os.FileMode) error {
if name = d.resolve(ctx, name); name == "" {
return os.ErrNotExist
}
return os.Mkdir(name, perm)
}
// OpenFile resolves the physical file and delegates this to an os.OpenFile execution
func (d Dir) OpenFile(ctx context.Context, name string, flag int, perm os.FileMode) (webdav.File, error) {
if name = d.resolve(ctx, name); name == "" {
return nil, os.ErrNotExist
}
f, err := os.OpenFile(name, flag, perm)
if err != nil {
return nil, err
}
return f, nil
}
// RemoveAll resolves the physical file and delegates this to an os.RemoveAll execution
func (d Dir) RemoveAll(ctx context.Context, name string) error {
if name = d.resolve(ctx, name); name == "" {
return os.ErrNotExist
}
if name == filepath.Clean(string(d.Config.Dir)) {
// Prohibit removing the virtual root directory.
return os.ErrInvalid
}
return os.RemoveAll(name)
}
// Rename resolves the physical file and delegates this to an os.Rename execution
func (d Dir) Rename(ctx context.Context, oldName, newName string) error {
if oldName = d.resolve(ctx, oldName); oldName == "" {
return os.ErrNotExist
}
if newName = d.resolve(ctx, newName); newName == "" {
return os.ErrNotExist
}
if root := filepath.Clean(string(d.Config.Dir)); root == oldName || root == newName {
// Prohibit renaming from or to the virtual root directory.
return os.ErrInvalid
}
return os.Rename(oldName, newName)
}
// Stat resolves the physical file and delegates this to an os.Stat execution
func (d Dir) Stat(ctx context.Context, name string) (os.FileInfo, error) {
if name = d.resolve(ctx, name); name == "" {
return nil, os.ErrNotExist
}
return os.Stat(name)
}