mirror of
https://github.com/photoprism/photoprism.git
synced 2025-12-12 00:34:13 +01:00
Signed-off-by: Michael Mayer <michael@photoprism.app>
This commit is contained in:
@@ -1,26 +1,14 @@
|
||||
package server
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"net/http"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/gin-gonic/gin"
|
||||
"golang.org/x/net/webdav"
|
||||
|
||||
"github.com/photoprism/photoprism/internal/auto"
|
||||
"github.com/photoprism/photoprism/internal/config"
|
||||
"github.com/photoprism/photoprism/pkg/clean"
|
||||
"github.com/photoprism/photoprism/pkg/fs"
|
||||
"github.com/photoprism/photoprism/pkg/header"
|
||||
"github.com/photoprism/photoprism/pkg/txt"
|
||||
)
|
||||
|
||||
const WebDAVOriginals = "/originals"
|
||||
const WebDAVImport = "/import"
|
||||
const (
|
||||
WebDAVOriginals = "/originals"
|
||||
WebDAVImport = "/import"
|
||||
)
|
||||
|
||||
// registerWebDAVRoutes configures the built-in WebDAV server.
|
||||
func registerWebDAVRoutes(router *gin.Engine, conf *config.Config) {
|
||||
@@ -43,153 +31,3 @@ func registerWebDAVRoutes(router *gin.Engine, conf *config.Config) {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
var WebDAVHandler = func(c *gin.Context, router *gin.RouterGroup, srv *webdav.Handler) {
|
||||
srv.ServeHTTP(c.Writer, c.Request)
|
||||
}
|
||||
|
||||
// WebDAV handles requests to the /originals and /import endpoints.
|
||||
func WebDAV(filePath string, router *gin.RouterGroup, conf *config.Config) {
|
||||
if router == nil {
|
||||
log.Error("webdav: router is nil")
|
||||
return
|
||||
}
|
||||
|
||||
if conf == nil {
|
||||
log.Error("webdav: conf is nil")
|
||||
return
|
||||
}
|
||||
|
||||
// Native file system restricted to a specific directory.
|
||||
fileSystem := webdav.Dir(filePath)
|
||||
|
||||
// Request logger function.
|
||||
loggerFunc := func(r *http.Request, err error) {
|
||||
if err != nil {
|
||||
switch r.Method {
|
||||
case MethodPut, MethodPost, MethodPatch, MethodDelete, MethodCopy, MethodMove:
|
||||
log.Errorf("webdav: %s in %s %s", clean.Error(err), clean.Log(r.Method), clean.Log(r.URL.String()))
|
||||
case MethodPropfind:
|
||||
log.Tracef("webdav: %s in %s %s", clean.Error(err), clean.Log(r.Method), clean.Log(r.URL.String()))
|
||||
default:
|
||||
log.Debugf("webdav: %s in %s %s", clean.Error(err), clean.Log(r.Method), clean.Log(r.URL.String()))
|
||||
}
|
||||
} else {
|
||||
// Handle optional upload request headers.
|
||||
if r.Method == MethodPut {
|
||||
var fileName string
|
||||
|
||||
// Determine name and path of the uploaded file.
|
||||
if router.BasePath() == conf.BaseUri(WebDAVOriginals) {
|
||||
fileName = filepath.Join(conf.OriginalsPath(), strings.TrimPrefix(r.URL.Path, router.BasePath()))
|
||||
} else if router.BasePath() == conf.BaseUri(WebDAVImport) {
|
||||
fileName = filepath.Join(conf.ImportPath(), strings.TrimPrefix(r.URL.Path, router.BasePath()))
|
||||
}
|
||||
|
||||
if fs.FileExists(fileName) {
|
||||
// Flag the uploaded file as favorite if the "X-Favorite" header is set to "1".
|
||||
if r.Header.Get(header.XFavorite) == "1" {
|
||||
FlagUploadAsFavorite(fileName)
|
||||
}
|
||||
|
||||
// Set the file modification time based on the Unix timestamp found in the "X-OC-MTime" header.
|
||||
if mtimeUnix := txt.Int64(r.Header.Get(header.XModTime)); mtimeUnix <= 0 {
|
||||
// Ignore, as no Unix timestamp was provided.
|
||||
} else if mtime := time.Unix(mtimeUnix, 0); mtime.IsZero() || time.Now().Before(mtime) {
|
||||
log.Warnf("webdav: invalid modtime provided for %s", clean.Log(filepath.Base(fileName)))
|
||||
} else if mtimeErr := os.Chtimes(fileName, time.Time{}, mtime); mtimeErr != nil {
|
||||
log.Warnf("webdav: failed to set modtime for %s", clean.Log(filepath.Base(fileName)))
|
||||
} else {
|
||||
log.Infof("webdav: set modtime for %s", clean.Log(filepath.Base(fileName)))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
switch r.Method {
|
||||
case MethodPut, MethodPost, MethodPatch, MethodDelete, MethodCopy, MethodMove:
|
||||
log.Infof("webdav: %s %s", clean.Log(r.Method), clean.Log(r.URL.String()))
|
||||
|
||||
if router.BasePath() == conf.BaseUri(WebDAVOriginals) {
|
||||
auto.ShouldIndex()
|
||||
} else if router.BasePath() == conf.BaseUri(WebDAVImport) {
|
||||
auto.ShouldImport()
|
||||
}
|
||||
default:
|
||||
log.Tracef("webdav: %s %s", clean.Log(r.Method), clean.Log(r.URL.String()))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// WebDAV request handler.
|
||||
srv := &webdav.Handler{
|
||||
Prefix: router.BasePath(),
|
||||
FileSystem: fileSystem,
|
||||
LockSystem: webdav.NewMemLS(),
|
||||
Logger: loggerFunc,
|
||||
}
|
||||
|
||||
// Request handler wrapper function.
|
||||
handlerFunc := func(c *gin.Context) {
|
||||
WebDAVHandler(c, router, srv)
|
||||
}
|
||||
|
||||
// handleRead registers WebDAV methods used for browsing and downloading.
|
||||
handleRead := func(h func(*gin.Context)) {
|
||||
router.Handle(MethodHead, "/*path", h)
|
||||
router.Handle(MethodGet, "/*path", h)
|
||||
router.Handle(MethodOptions, "/*path", h)
|
||||
router.Handle(MethodLock, "/*path", h)
|
||||
router.Handle(MethodUnlock, "/*path", h)
|
||||
router.Handle(MethodPropfind, "/*path", h)
|
||||
}
|
||||
|
||||
// handleWrite registers WebDAV methods to may modify the file system.
|
||||
handleWrite := func(h func(*gin.Context)) {
|
||||
router.Handle(MethodPut, "/*path", h)
|
||||
router.Handle(MethodPost, "/*path", h)
|
||||
router.Handle(MethodPatch, "/*path", h)
|
||||
router.Handle(MethodDelete, "/*path", h)
|
||||
router.Handle(MethodMkcol, "/*path", h)
|
||||
router.Handle(MethodCopy, "/*path", h)
|
||||
router.Handle(MethodMove, "/*path", h)
|
||||
router.Handle(MethodProppatch, "/*path", h)
|
||||
}
|
||||
|
||||
// Handle supported WebDAV request methods.
|
||||
handleRead(handlerFunc)
|
||||
|
||||
// Only supported with read-only mode disabled.
|
||||
if conf.ReadOnly() {
|
||||
handleWrite(func(c *gin.Context) {
|
||||
_ = c.AbortWithError(http.StatusForbidden, fmt.Errorf("forbidden in read-only mode"))
|
||||
})
|
||||
} else {
|
||||
handleWrite(handlerFunc)
|
||||
}
|
||||
}
|
||||
|
||||
// FlagUploadAsFavorite adds the favorite flag to files uploaded via WebDAV.
|
||||
func FlagUploadAsFavorite(fileName string) {
|
||||
yamlName := fs.AbsPrefix(fileName, false) + fs.ExtYAML
|
||||
|
||||
// Abort if YAML file already exists to avoid overwriting metadata.
|
||||
if fs.FileExists(yamlName) {
|
||||
log.Warnf("webdav: %s already exists", clean.Log(filepath.Base(yamlName)))
|
||||
return
|
||||
}
|
||||
|
||||
// Make sure directory exists.
|
||||
if err := os.MkdirAll(filepath.Dir(yamlName), fs.ModeDir); err != nil {
|
||||
log.Errorf("webdav: %s", err.Error())
|
||||
return
|
||||
}
|
||||
|
||||
// Write YAML data to file.
|
||||
if err := os.WriteFile(yamlName, []byte("Favorite: true\n"), fs.ModeFile); err != nil {
|
||||
log.Errorf("webdav: %s", err.Error())
|
||||
return
|
||||
}
|
||||
|
||||
// Log success.
|
||||
log.Infof("webdav: flagged %s as favorite", clean.Log(filepath.Base(fileName)))
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user