Files
filestash/server/ctrl/webdav.go
2024-06-26 23:47:07 +10:00

141 lines
3.8 KiB
Go

package ctrl
import (
"net/http"
"path/filepath"
"strings"
. "github.com/mickael-kerjean/filestash/server/common"
"github.com/mickael-kerjean/filestash/server/model"
"github.com/mickael-kerjean/net/webdav"
)
func WebdavHandler(ctx *App, res http.ResponseWriter, req *http.Request) {
if ctx.Share.Id == "" {
http.NotFound(res, req)
return
}
// https://github.com/golang/net/blob/master/webdav/webdav.go#L49-L68
canRead := model.CanRead(ctx)
canWrite := model.CanEdit(ctx)
canUpload := model.CanUpload(ctx)
switch req.Method {
case "OPTIONS", "HEAD", "GET":
if canRead == false {
SendErrorResult(res, ErrPermissionDenied)
return
}
case "MKCOL", "DELETE", "COPY", "MOVE", "PROPPATCH":
if canWrite == false {
SendErrorResult(res, ErrPermissionDenied)
return
}
case "PROPFIND":
if canRead == false {
SendErrorResult(res, ErrPermissionDenied)
return
}
case "PUT":
if canWrite == false || canUpload == false {
SendErrorResult(res, ErrPermissionDenied)
return
}
case "LOCK", "UNLOCK":
if canWrite == false || canUpload == false {
SendErrorResult(res, ErrPermissionDenied)
return
}
default:
SendErrorResult(res, ErrNotImplemented)
return
}
h := &webdav.Handler{
Prefix: "/s/" + ctx.Share.Id,
FileSystem: model.NewWebdavFs(ctx.Backend, ctx.Share.Backend, ctx.Share.Path, req),
LockSystem: model.NewWebdavLock(),
}
h.ServeHTTP(res, req)
}
/*
* OSX ask for a lot of crap while mounting as a network drive. To avoid wasting resources with such
* an imbecile and considering we can't even see the source code they are running, the best approach we
* could go on is: "crap in, crap out" where useless request coming in are identified and answer appropriatly
*/
func WebdavBlacklist(fn HandlerFunc) HandlerFunc {
return HandlerFunc(func(ctx *App, res http.ResponseWriter, req *http.Request) {
base := filepath.Base(req.URL.String())
if req.Method == "PUT" || req.Method == "MKCOL" {
if strings.HasPrefix(base, "._") {
res.WriteHeader(http.StatusMethodNotAllowed)
res.Write([]byte(""))
return
} else if base == ".DS_Store" {
res.WriteHeader(http.StatusMethodNotAllowed)
res.Write([]byte(""))
return
} else if base == ".localized" {
res.WriteHeader(http.StatusMethodNotAllowed)
res.Write([]byte(""))
return
}
} else if req.Method == "PROPFIND" {
if strings.HasPrefix(base, "._") {
res.WriteHeader(http.StatusForbidden)
return
} else if base == ".DS_Store" {
res.WriteHeader(http.StatusForbidden)
res.Write([]byte(""))
return
} else if base == ".localized" {
res.WriteHeader(http.StatusForbidden)
return
} else if base == ".ql_disablethumbnails" {
res.WriteHeader(http.StatusForbidden)
res.Write([]byte(""))
return
} else if base == ".ql_disablecache" {
res.WriteHeader(http.StatusForbidden)
return
} else if base == ".hidden" {
res.WriteHeader(http.StatusForbidden)
return
} else if base == ".Spotlight-V100" {
res.WriteHeader(http.StatusForbidden)
return
} else if base == ".metadata_never_index" {
res.WriteHeader(http.StatusForbidden)
return
} else if base == "Contents" {
res.WriteHeader(http.StatusForbidden)
return
} else if base == ".metadata_never_index_unless_rootfs" {
res.WriteHeader(http.StatusForbidden)
return
}
} else if req.Method == "GET" {
if base == ".DS_Store" {
res.WriteHeader(http.StatusForbidden)
res.Write([]byte(""))
return
}
} else if req.Method == "DELETE" {
if base == ".DS_Store" {
res.WriteHeader(http.StatusForbidden)
res.Write([]byte(""))
return
}
} else if req.Method == "LOCK" || req.Method == "UNLOCK" {
if base == ".DS_Store" {
res.WriteHeader(http.StatusMethodNotAllowed)
res.Write([]byte(""))
return
}
}
fn(ctx, res, req)
})
}