From 4cc86a1225d97a7e7c17e3f0ab433a2f249b4dff Mon Sep 17 00:00:00 2001 From: Simon Ser Date: Tue, 3 May 2022 17:53:06 +0200 Subject: [PATCH] webdav: add ServePrincipal --- elements.go | 32 +++++++++++++++++++++++++++ server.go | 62 +++++++++++++++++++++++++++++++++++++++++++++++++++++ webdav.go | 6 ++++++ 3 files changed, 100 insertions(+) create mode 100644 elements.go diff --git a/elements.go b/elements.go new file mode 100644 index 0000000..70f9e9b --- /dev/null +++ b/elements.go @@ -0,0 +1,32 @@ +package webdav + +import ( + "encoding/xml" + + "github.com/emersion/go-webdav/internal" +) + +var ( + principalName = xml.Name{"DAV:", "principal"} + principalAlternateURISetName = xml.Name{"DAV:", "alternate-URI-set"} + principalURLName = xml.Name{"DAV:", "principal-URL"} + groupMembershipName = xml.Name{"DAV:", "group-membership"} +) + +// https://datatracker.ietf.org/doc/html/rfc3744#section-4.1 +type principalAlternateURISet struct { + XMLName xml.Name `xml:"DAV: alternate-URI-set"` + Hrefs []internal.Href `xml:"href"` +} + +// https://datatracker.ietf.org/doc/html/rfc3744#section-4.2 +type principalURL struct { + XMLName xml.Name `xml:"DAV: principal-URL"` + Href internal.Href `xml:"href"` +} + +// https://datatracker.ietf.org/doc/html/rfc3744#section-4.4 +type groupMembership struct { + XMLName xml.Name `xml:"DAV: group-membership"` + Hrefs []internal.Href `xml:"href"` +} diff --git a/server.go b/server.go index 811ef10..9342863 100644 --- a/server.go +++ b/server.go @@ -6,6 +6,7 @@ import ( "net/http" "os" "strconv" + "strings" "github.com/emersion/go-webdav/internal" ) @@ -234,3 +235,64 @@ func (b *backend) Move(r *http.Request, dest *internal.Href, overwrite bool) (cr } return created, err } + +// ServePrincipal replies to a request on a principal URI. +func ServePrincipal(w http.ResponseWriter, r *http.Request, principal *Principal) { + switch r.Method { + case http.MethodOptions: + caps := append([]string{"1", "3"}) + allow := []string{http.MethodOptions, "PROPFIND"} + w.Header().Add("DAV", strings.Join(caps, ", ")) + w.Header().Add("Allow", strings.Join(allow, ", ")) + w.WriteHeader(http.StatusNoContent) + case "PROPFIND": + if err := servePrincipalPropfind(w, r, principal); err != nil { + internal.ServeError(w, err) + } + default: + http.Error(w, "unsupported method", http.StatusMethodNotAllowed) + } +} + +func servePrincipalPropfind(w http.ResponseWriter, r *http.Request, principal *Principal) error { + var propfind internal.Propfind + if err := internal.DecodeXMLRequest(r, &propfind); err != nil { + return err + } + + // TODO: handle Depth + + props := map[xml.Name]internal.PropfindFunc{ + internal.ResourceTypeName: func(*internal.RawXMLValue) (interface{}, error) { + return internal.NewResourceType(principalName), nil + }, + internal.DisplayNameName: func(*internal.RawXMLValue) (interface{}, error) { + return &internal.DisplayName{Name: principal.Name}, nil + }, + internal.CurrentUserPrincipalName: func(*internal.RawXMLValue) (interface{}, error) { + // TODO: allow serving a principal different from the current user's + return &internal.CurrentUserPrincipal{Href: internal.Href{Path: principal.Path}}, nil + }, + principalAlternateURISetName: func(*internal.RawXMLValue) (interface{}, error) { + return &principalAlternateURISet{}, nil // TODO: allow customizing + }, + principalURLName: func(*internal.RawXMLValue) (interface{}, error) { + return &principalURL{ + Href: internal.Href{Path: principal.Path}, + }, nil + }, + groupMembershipName: func(*internal.RawXMLValue) (interface{}, error) { + return &groupMembership{}, nil // TODO: allow customizing + }, + } + + // TODO: allow adding more props such as CardDAV/CalDAV home sets + + resp, err := internal.NewPropfindResponse(r.URL.Path, &propfind, props) + if err != nil { + return err + } + + ms := internal.NewMultistatus(*resp) + return internal.ServeMultistatus(w, ms) +} diff --git a/webdav.go b/webdav.go index 4dd369c..baad56e 100644 --- a/webdav.go +++ b/webdav.go @@ -7,6 +7,12 @@ import ( "time" ) +// Principal is a DAV principal as defined in RFC 3744 section 2. +type Principal struct { + Path string + Name string +} + type FileInfo struct { Path string Size int64