mirror of
https://github.com/opencloud-eu/opencloud.git
synced 2026-03-05 15:57:01 -05:00
284 lines
7.8 KiB
Go
284 lines
7.8 KiB
Go
package svc
|
|
|
|
import (
|
|
"encoding/xml"
|
|
merrors "github.com/asim/go-micro/v3/errors"
|
|
"github.com/go-chi/render"
|
|
"net/http"
|
|
"path"
|
|
"strings"
|
|
|
|
"github.com/owncloud/ocis/ocis-pkg/log"
|
|
"github.com/owncloud/ocis/ocis-pkg/service/grpc"
|
|
|
|
"github.com/go-chi/chi"
|
|
thumbnails "github.com/owncloud/ocis/thumbnails/pkg/proto/v0"
|
|
"github.com/owncloud/ocis/webdav/pkg/config"
|
|
"github.com/owncloud/ocis/webdav/pkg/dav/requests"
|
|
)
|
|
|
|
const (
|
|
TokenHeader = "X-Access-Token"
|
|
)
|
|
|
|
var (
|
|
codesEnum = map[int]string{
|
|
http.StatusBadRequest: "Sabre\\DAV\\Exception\\BadRequest",
|
|
http.StatusUnauthorized: "Sabre\\DAV\\Exception\\NotAuthenticated",
|
|
http.StatusNotFound: "Sabre\\DAV\\Exception\\NotFound",
|
|
http.StatusMethodNotAllowed: "Sabre\\DAV\\Exception\\MethodNotAllowed",
|
|
}
|
|
)
|
|
|
|
// Service defines the extension handlers.
|
|
type Service interface {
|
|
ServeHTTP(http.ResponseWriter, *http.Request)
|
|
Thumbnail(http.ResponseWriter, *http.Request)
|
|
}
|
|
|
|
// NewService returns a service implementation for Service.
|
|
func NewService(opts ...Option) Service {
|
|
options := newOptions(opts...)
|
|
|
|
m := chi.NewMux()
|
|
m.Use(options.Middleware...)
|
|
|
|
svc := Webdav{
|
|
config: options.Config,
|
|
log: options.Logger,
|
|
mux: m,
|
|
thumbnailsClient: thumbnails.NewThumbnailService("com.owncloud.api.thumbnails", grpc.DefaultClient),
|
|
}
|
|
|
|
m.Route(options.Config.HTTP.Root, func(r chi.Router) {
|
|
r.Get("/remote.php/dav/files/{user}/*", svc.Thumbnail)
|
|
r.Get("/remote.php/dav/public-files/{token}/*", svc.PublicThumbnail)
|
|
r.Head("/remote.php/dav/public-files/{token}/*", svc.PublicThumbnailHead)
|
|
})
|
|
|
|
return svc
|
|
}
|
|
|
|
// Webdav defines implements the business logic for Service.
|
|
type Webdav struct {
|
|
config *config.Config
|
|
log log.Logger
|
|
mux *chi.Mux
|
|
thumbnailsClient thumbnails.ThumbnailService
|
|
}
|
|
|
|
// ServeHTTP implements the Service interface.
|
|
func (g Webdav) ServeHTTP(w http.ResponseWriter, r *http.Request) {
|
|
g.mux.ServeHTTP(w, r)
|
|
}
|
|
|
|
// Thumbnail implements the Service interface.
|
|
func (g Webdav) Thumbnail(w http.ResponseWriter, r *http.Request) {
|
|
tr, err := requests.ParseThumbnailRequest(r)
|
|
if err != nil {
|
|
g.log.Error().Err(err).Msg("could not create Request")
|
|
renderError(w, r, errBadRequest(err.Error()))
|
|
return
|
|
}
|
|
|
|
t := r.Header.Get(TokenHeader)
|
|
rsp, err := g.thumbnailsClient.GetThumbnail(r.Context(), &thumbnails.GetThumbnailRequest{
|
|
Filepath: strings.TrimLeft(tr.Filepath, "/"),
|
|
ThumbnailType: extensionToThumbnailType(strings.TrimLeft(tr.Extension, ".")),
|
|
Width: tr.Width,
|
|
Height: tr.Height,
|
|
Source: &thumbnails.GetThumbnailRequest_Cs3Source{
|
|
Cs3Source: &thumbnails.CS3Source{
|
|
Path: path.Join(g.config.WebdavNamespace, tr.Filepath),
|
|
Authorization: t,
|
|
},
|
|
},
|
|
})
|
|
if err != nil {
|
|
g.log.Error().Err(err).Msg("could not get thumbnail")
|
|
e := merrors.Parse(err.Error())
|
|
switch e.Code {
|
|
case http.StatusNotFound:
|
|
renderError(w, r, errNotFound(notFoundMsg(tr.Filename)))
|
|
case http.StatusBadRequest:
|
|
renderError(w, r, errBadRequest(err.Error()))
|
|
default:
|
|
renderError(w, r, errInternalError(err.Error()))
|
|
}
|
|
return
|
|
}
|
|
|
|
if len(rsp.Thumbnail) == 0 {
|
|
renderError(w, r, errNotFound(""))
|
|
return
|
|
}
|
|
|
|
g.mustRender(w, r, newThumbnailResponse(rsp))
|
|
}
|
|
|
|
func (g Webdav) PublicThumbnail(w http.ResponseWriter, r *http.Request) {
|
|
tr, err := requests.ParseThumbnailRequest(r)
|
|
if err != nil {
|
|
g.log.Error().Err(err).Msg("could not create Request")
|
|
renderError(w, r, errBadRequest(err.Error()))
|
|
return
|
|
}
|
|
|
|
rsp, err := g.thumbnailsClient.GetThumbnail(r.Context(), &thumbnails.GetThumbnailRequest{
|
|
Filepath: strings.TrimLeft(tr.Filepath, "/"),
|
|
ThumbnailType: extensionToThumbnailType(strings.TrimLeft(tr.Extension, ".")),
|
|
Width: tr.Width,
|
|
Height: tr.Height,
|
|
Source: &thumbnails.GetThumbnailRequest_WebdavSource{
|
|
WebdavSource: &thumbnails.WebdavSource{
|
|
Url: g.config.OcisPublicURL + r.URL.RequestURI(),
|
|
IsPublicLink: true,
|
|
PublicLinkToken: tr.PublicLinkToken,
|
|
},
|
|
},
|
|
})
|
|
if err != nil {
|
|
g.log.Error().Err(err).Msg("could not get thumbnail")
|
|
e := merrors.Parse(err.Error())
|
|
switch e.Code {
|
|
case http.StatusNotFound:
|
|
renderError(w, r, errNotFound(notFoundMsg(tr.Filename)))
|
|
case http.StatusBadRequest:
|
|
renderError(w, r, errBadRequest(err.Error()))
|
|
default:
|
|
renderError(w, r, errInternalError(err.Error()))
|
|
}
|
|
return
|
|
}
|
|
|
|
if len(rsp.Thumbnail) == 0 {
|
|
renderError(w, r, errNotFound(""))
|
|
return
|
|
}
|
|
|
|
g.mustRender(w, r, newThumbnailResponse(rsp))
|
|
}
|
|
|
|
func (g Webdav) PublicThumbnailHead(w http.ResponseWriter, r *http.Request) {
|
|
tr, err := requests.ParseThumbnailRequest(r)
|
|
if err != nil {
|
|
g.log.Error().Err(err).Msg("could not create Request")
|
|
renderError(w, r, errBadRequest(err.Error()))
|
|
return
|
|
}
|
|
|
|
rsp, err := g.thumbnailsClient.GetThumbnail(r.Context(), &thumbnails.GetThumbnailRequest{
|
|
Filepath: strings.TrimLeft(tr.Filepath, "/"),
|
|
ThumbnailType: extensionToThumbnailType(strings.TrimLeft(tr.Extension, ".")),
|
|
Width: tr.Width,
|
|
Height: tr.Height,
|
|
Source: &thumbnails.GetThumbnailRequest_WebdavSource{
|
|
WebdavSource: &thumbnails.WebdavSource{
|
|
Url: g.config.OcisPublicURL + r.URL.RequestURI(),
|
|
IsPublicLink: true,
|
|
PublicLinkToken: tr.PublicLinkToken,
|
|
},
|
|
},
|
|
})
|
|
if err != nil {
|
|
e := merrors.Parse(err.Error())
|
|
switch e.Code {
|
|
case http.StatusNotFound:
|
|
renderError(w, r, errNotFound(notFoundMsg(tr.Filename)))
|
|
case http.StatusBadRequest:
|
|
g.log.Error().Err(err).Msg("could not get thumbnail")
|
|
renderError(w, r, errBadRequest(err.Error()))
|
|
default:
|
|
g.log.Error().Err(err).Msg("could not get thumbnail")
|
|
renderError(w, r, errInternalError(err.Error()))
|
|
}
|
|
return
|
|
}
|
|
|
|
if len(rsp.Thumbnail) == 0 {
|
|
renderError(w, r, errNotFound(""))
|
|
return
|
|
}
|
|
|
|
w.WriteHeader(http.StatusOK)
|
|
}
|
|
|
|
func extensionToThumbnailType(ext string) thumbnails.GetThumbnailRequest_ThumbnailType {
|
|
switch strings.ToUpper(ext) {
|
|
case "GIF", "PNG":
|
|
return thumbnails.GetThumbnailRequest_PNG
|
|
default:
|
|
return thumbnails.GetThumbnailRequest_JPG
|
|
}
|
|
}
|
|
|
|
func (g Webdav) mustRender(w http.ResponseWriter, r *http.Request, renderer render.Renderer) {
|
|
if err := render.Render(w, r, renderer); err != nil {
|
|
g.log.Err(err).Msg("failed to write response")
|
|
}
|
|
}
|
|
|
|
// http://www.webdav.org/specs/rfc4918.html#ELEMENT_error
|
|
type errResponse struct {
|
|
HTTPStatusCode int `json:"-" xml:"-"`
|
|
XMLName xml.Name `xml:"d:error"`
|
|
Xmlnsd string `xml:"xmlns:d,attr"`
|
|
Xmlnss string `xml:"xmlns:s,attr"`
|
|
Exception string `xml:"s:exception"`
|
|
Message string `xml:"s:message"`
|
|
InnerXML []byte `xml:",innerxml"`
|
|
}
|
|
|
|
func newErrResponse(statusCode int, msg string) *errResponse {
|
|
rsp := &errResponse{
|
|
HTTPStatusCode: statusCode,
|
|
Xmlnsd: "DAV",
|
|
Xmlnss: "http://sabredav.org/ns",
|
|
Exception: codesEnum[statusCode],
|
|
}
|
|
if msg != "" {
|
|
rsp.Message = msg
|
|
}
|
|
return rsp
|
|
}
|
|
|
|
func errInternalError(msg string) *errResponse {
|
|
return newErrResponse(http.StatusInternalServerError, msg)
|
|
}
|
|
|
|
func errBadRequest(msg string) *errResponse {
|
|
return newErrResponse(http.StatusBadRequest, msg)
|
|
}
|
|
|
|
func errNotFound(msg string) *errResponse {
|
|
return newErrResponse(http.StatusNotFound, msg)
|
|
}
|
|
|
|
type thumbnailResponse struct {
|
|
contentType string
|
|
thumbnail []byte
|
|
}
|
|
|
|
func (t *thumbnailResponse) Render(w http.ResponseWriter, _ *http.Request) error {
|
|
w.WriteHeader(http.StatusOK)
|
|
w.Header().Set("Content-Type", t.contentType)
|
|
_, err := w.Write(t.thumbnail)
|
|
return err
|
|
}
|
|
|
|
func newThumbnailResponse(rsp *thumbnails.GetThumbnailResponse) *thumbnailResponse {
|
|
return &thumbnailResponse{
|
|
contentType: rsp.Mimetype,
|
|
thumbnail: rsp.Thumbnail,
|
|
}
|
|
}
|
|
|
|
func renderError(w http.ResponseWriter, r *http.Request, err *errResponse) {
|
|
render.Status(r, err.HTTPStatusCode)
|
|
render.XML(w, r, err)
|
|
}
|
|
|
|
func notFoundMsg(name string) string {
|
|
return "File with name " + name + " could not be located"
|
|
}
|