carddav: allow created address book objects to have a different path

Closes: https://github.com/emersion/go-webdav/issues/32
This commit is contained in:
Simon Ser 2020-01-30 15:20:10 +01:00
parent 2e5aa7653b
commit dd1527b97e
No known key found for this signature in database
GPG Key ID: 0FDE7BE0E88F5E48
3 changed files with 21 additions and 13 deletions

View File

@ -19,7 +19,7 @@ type Backend interface {
GetAddressObject(path string, req *AddressDataRequest) (*AddressObject, error) GetAddressObject(path string, req *AddressDataRequest) (*AddressObject, error)
ListAddressObjects(req *AddressDataRequest) ([]AddressObject, error) ListAddressObjects(req *AddressDataRequest) ([]AddressObject, error)
QueryAddressObjects(query *AddressBookQuery) ([]AddressObject, error) QueryAddressObjects(query *AddressBookQuery) ([]AddressObject, error)
PutAddressObject(path string, card vcard.Card) error PutAddressObject(path string, card vcard.Card) (loc string, err error)
DeleteAddressObject(path string) error DeleteAddressObject(path string) error
} }
@ -385,27 +385,32 @@ func (b *backend) Proppatch(r *http.Request, update *internal.Propertyupdate) (*
return nil, internal.HTTPErrorf(http.StatusForbidden, "carddav: PROPPATCH is unsupported") return nil, internal.HTTPErrorf(http.StatusForbidden, "carddav: PROPPATCH is unsupported")
} }
func (b *backend) Put(r *http.Request) error { func (b *backend) Put(r *http.Request) (*internal.Href, error) {
// TODO: add support for If-None-Match // TODO: add support for If-None-Match
t, _, err := mime.ParseMediaType(r.Header.Get("Content-Type")) t, _, err := mime.ParseMediaType(r.Header.Get("Content-Type"))
if err != nil { if err != nil {
return internal.HTTPErrorf(http.StatusBadRequest, "carddav: malformed Content-Type: %v", err) return nil, internal.HTTPErrorf(http.StatusBadRequest, "carddav: malformed Content-Type: %v", err)
} }
if t != vcard.MIMEType { if t != vcard.MIMEType {
// TODO: send CARDDAV:supported-address-data error // TODO: send CARDDAV:supported-address-data error
return internal.HTTPErrorf(http.StatusBadRequest, "carddav: unsupporetd Content-Type %q", t) return nil, internal.HTTPErrorf(http.StatusBadRequest, "carddav: unsupporetd Content-Type %q", t)
} }
// TODO: check CARDDAV:max-resource-size precondition // TODO: check CARDDAV:max-resource-size precondition
card, err := vcard.NewDecoder(r.Body).Decode() card, err := vcard.NewDecoder(r.Body).Decode()
if err != nil { if err != nil {
// TODO: send CARDDAV:valid-address-data error // TODO: send CARDDAV:valid-address-data error
return internal.HTTPErrorf(http.StatusBadRequest, "carddav: failed to parse vCard: %v", err) return nil, internal.HTTPErrorf(http.StatusBadRequest, "carddav: failed to parse vCard: %v", err)
} }
// TODO: add support for the CARDDAV:no-uid-conflict error // TODO: add support for the CARDDAV:no-uid-conflict error
return b.Backend.PutAddressObject(r.URL.Path, card) loc, err := b.Backend.PutAddressObject(r.URL.Path, card)
if err != nil {
return nil, err
}
return &internal.Href{Path: loc}, nil
} }
func (b *backend) Delete(r *http.Request) error { func (b *backend) Delete(r *http.Request) error {

View File

@ -79,7 +79,7 @@ type Backend interface {
HeadGet(w http.ResponseWriter, r *http.Request) error HeadGet(w http.ResponseWriter, r *http.Request) error
Propfind(r *http.Request, pf *Propfind, depth Depth) (*Multistatus, error) Propfind(r *http.Request, pf *Propfind, depth Depth) (*Multistatus, error)
Proppatch(r *http.Request, pu *Propertyupdate) (*Response, error) Proppatch(r *http.Request, pu *Propertyupdate) (*Response, error)
Put(r *http.Request) error Put(r *http.Request) (*Href, error)
Delete(r *http.Request) error Delete(r *http.Request) error
Mkcol(r *http.Request) error Mkcol(r *http.Request) error
Copy(r *http.Request, dest *Href, recursive, overwrite bool) (created bool, err error) Copy(r *http.Request, dest *Href, recursive, overwrite bool) (created bool, err error)
@ -101,11 +101,14 @@ func (h *Handler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
case http.MethodGet, http.MethodHead: case http.MethodGet, http.MethodHead:
err = h.Backend.HeadGet(w, r) err = h.Backend.HeadGet(w, r)
case http.MethodPut: case http.MethodPut:
err = h.Backend.Put(r) var href *Href
href, err = h.Backend.Put(r)
if err == nil { if err == nil {
// TODO: Last-Modified, ETag, Content-Type if the request has // TODO: Last-Modified, ETag, Content-Type if the request has
// been copied verbatim // been copied verbatim
// TODO: Location if the server has mutated the href if href != nil {
w.Header().Set("Location", (*url.URL)(href).String())
}
w.WriteHeader(http.StatusCreated) w.WriteHeader(http.StatusCreated)
} }
case http.MethodDelete: case http.MethodDelete:

View File

@ -187,18 +187,18 @@ func (b *backend) Proppatch(r *http.Request, update *internal.Propertyupdate) (*
return nil, internal.HTTPErrorf(http.StatusForbidden, "webdav: PROPPATCH is unsupported") return nil, internal.HTTPErrorf(http.StatusForbidden, "webdav: PROPPATCH is unsupported")
} }
func (b *backend) Put(r *http.Request) error { func (b *backend) Put(r *http.Request) (*internal.Href, error) {
wc, err := b.FileSystem.Create(r.URL.Path) wc, err := b.FileSystem.Create(r.URL.Path)
if err != nil { if err != nil {
return err return nil, err
} }
defer wc.Close() defer wc.Close()
if _, err := io.Copy(wc, r.Body); err != nil { if _, err := io.Copy(wc, r.Body); err != nil {
return err return nil, err
} }
return wc.Close() return nil, wc.Close()
} }
func (b *backend) Delete(r *http.Request) error { func (b *backend) Delete(r *http.Request) error {