diff --git a/carddav/server.go b/carddav/server.go index dd4be0e..494719c 100644 --- a/carddav/server.go +++ b/carddav/server.go @@ -19,7 +19,7 @@ type Backend interface { GetAddressObject(path string, req *AddressDataRequest) (*AddressObject, error) ListAddressObjects(req *AddressDataRequest) ([]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 } @@ -385,27 +385,32 @@ func (b *backend) Proppatch(r *http.Request, update *internal.Propertyupdate) (* 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 t, _, err := mime.ParseMediaType(r.Header.Get("Content-Type")) 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 { // 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 card, err := vcard.NewDecoder(r.Body).Decode() if err != nil { // 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 - 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 { diff --git a/internal/server.go b/internal/server.go index 5ecbe99..9532bac 100644 --- a/internal/server.go +++ b/internal/server.go @@ -79,7 +79,7 @@ type Backend interface { HeadGet(w http.ResponseWriter, r *http.Request) error Propfind(r *http.Request, pf *Propfind, depth Depth) (*Multistatus, 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 Mkcol(r *http.Request) 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: err = h.Backend.HeadGet(w, r) case http.MethodPut: - err = h.Backend.Put(r) + var href *Href + href, err = h.Backend.Put(r) if err == nil { // TODO: Last-Modified, ETag, Content-Type if the request has // 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) } case http.MethodDelete: diff --git a/server.go b/server.go index 3279f5d..f1a326f 100644 --- a/server.go +++ b/server.go @@ -187,18 +187,18 @@ func (b *backend) Proppatch(r *http.Request, update *internal.Propertyupdate) (* 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) if err != nil { - return err + return nil, err } defer wc.Close() 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 {