130 lines
2.7 KiB
Go
130 lines
2.7 KiB
Go
package server
|
|
|
|
import (
|
|
"errors"
|
|
"io/fs"
|
|
"os"
|
|
"path/filepath"
|
|
"strings"
|
|
)
|
|
|
|
type SharedLocation struct {
|
|
// relative to config path at first,
|
|
// then [normalize] makes it absolute
|
|
Path string `toml:"path"`
|
|
// [normalize] removes trailing '/'
|
|
// so "/" becomes ""
|
|
Url string `toml:"url"`
|
|
ReadPassword string `toml:"read_password"`
|
|
WritePassword string `toml:"write_password"`
|
|
}
|
|
|
|
var ErrSharePathEmpty = errors.New("share.path is empty")
|
|
var ErrShareUrlEmpty = errors.New("share.url is empty")
|
|
var ErrShareUrlDoesntStartWithSlash = errors.New("share.url doesn't start with slash")
|
|
|
|
func (sh *SharedLocation) normalize(configDir string) error {
|
|
if sh.Path == "" {
|
|
return ErrSharePathEmpty
|
|
}
|
|
// get absolute path for configDir
|
|
configDirAbs, err := filepath.Abs(configDir)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
// resolve symlinks
|
|
configDirAbs, err = filepath.EvalSymlinks(configDirAbs)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
// fix separators
|
|
shPathOsSep, err := filepath.Localize(sh.Path)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
// join configDir with sh.Path
|
|
sh.Path = filepath.Join(configDirAbs, shPathOsSep)
|
|
|
|
if sh.Url == "" {
|
|
return ErrShareUrlEmpty
|
|
}
|
|
if sh.Url[0] != '/' {
|
|
return ErrShareUrlDoesntStartWithSlash
|
|
}
|
|
// remove trailing slash
|
|
sh.Url = strings.TrimRight(sh.Url, "/")
|
|
|
|
return nil
|
|
}
|
|
|
|
func (sh *SharedLocation) String() string {
|
|
var r, w string
|
|
if sh.ReadPassword == "" {
|
|
r = "pub"
|
|
} else {
|
|
r = "pas"
|
|
}
|
|
if sh.WritePassword == "" {
|
|
w = "no "
|
|
} else {
|
|
w = "pas"
|
|
}
|
|
access := "{ " + "r:" + r + ", w:" + w + " }"
|
|
url := sh.Url
|
|
if url == "" {
|
|
url = "/"
|
|
}
|
|
return "share " + access + " '" + url + "' -> '" + sh.Path + "'"
|
|
}
|
|
|
|
func (sh *SharedLocation) mkdir() error {
|
|
_, err := os.Stat(sh.Path)
|
|
if err == os.ErrNotExist {
|
|
return os.MkdirAll(sh.Path, 0o755)
|
|
}
|
|
return err
|
|
}
|
|
|
|
type UrlStatus uint32
|
|
|
|
const (
|
|
UrlFile = UrlStatus(iota)
|
|
UrlDir
|
|
UrlDoesntExistInShare
|
|
UrlShareNotFound
|
|
UrlForbidden
|
|
UrlIncorrect
|
|
)
|
|
|
|
// returns calculated path, or input url if it doesn't belong to any share
|
|
func (shares Shares) MapUrl(url string) (string, *SharedLocation, UrlStatus) {
|
|
if url == "" || url[0] != '/' {
|
|
return "", nil, UrlIncorrect
|
|
}
|
|
for _, sh := range shares {
|
|
pathInShare, ok := strings.CutPrefix(url, sh.Url)
|
|
if !ok {
|
|
continue
|
|
}
|
|
|
|
if pathInShare != "" && pathInShare[0] != '/' {
|
|
// url is not sh.Url or child of it
|
|
continue
|
|
}
|
|
pathAbs := filepath.Join(sh.Path, filepath.FromSlash(pathInShare))
|
|
|
|
fi, err := os.Stat(pathAbs)
|
|
switch {
|
|
case errors.Is(err, fs.ErrNotExist):
|
|
return pathAbs, sh, UrlDoesntExistInShare
|
|
case err != nil:
|
|
return pathAbs, sh, UrlForbidden
|
|
case fi.IsDir():
|
|
return pathAbs, sh, UrlDir
|
|
default:
|
|
return pathAbs, sh, UrlFile
|
|
}
|
|
}
|
|
return url, nil, UrlShareNotFound
|
|
}
|