Both the displayname and the description can be absent for both
calendars and address books. If this is the case they should not show up
in PROPFIND responses as empty string.
Now that the handling for multiple address books is in place, this
commit adds initial support for creation and deletion of address books.
These operations obviously require support from the backend, so the
interface gains two new methods. All properties of the address book
passed to `CreateAddressBook()` may be unset (e.g. when a client sends a
MKCOL request without a body), except for the path, which is always set.
It is up to the backend to put any desired default values in place.
This is the equivalent of #127 (and #140) for CardDAV and finally allows
backends to serve different address books to different users.
While I'm breaking the interface, correct one last instance of
"Addressbook" to "AddressBook" (in `AddressBookHomeSetPath`).
This is done properly in the carddav and caldav packages, but the custom
function does not know what the user intends to serve, so it must be
passed in from the user. Without this, certain clients (e.g. DAVx5)
will be unable to discover endpoints served this way.
Also slightly extend the supported methods returned on OPTIONS requests.
REPORT is properly supported, the others are mostly for not giving
clients the impression that the resources are read-only.
The `If-Match` and `If-None-Match` conditional headers can have either a
wildcard or a (quoted) ETag as value. However, the ETag _could_ be a
literal `*`, so care must be taken to allow these cases to be
distinguished. The values of these headers have to be handled by the
backend, so export a type that facilitates working with these values.
As the implementation evolves, it will be necessary to have more tests
to assert we don't break anything when making changes. This commit
introduces a test setup to test that server and client can handle the
address book discovery with various parameters. The test setup should be
easily extendable to cover even more ground as needed.
With this commit, the list of AddressObjects returned by `Filter()` will
always be a correct response to the query argument passed to it, even if
the input list contained objects with arbitraty properties present.
As is, the tests in `match_test.go` test wrong behavior. They request
"partial retrieval" (i.e. filtering of returned properties), but compare
the returned result to the original input. They essentially rely on the
fact that property filtering is currently not implemented.
To fix this, simply make all existing test queries request all
properties. If property filtering gets implemented (correctly), the
tests will then continue to work. New tests can be added for testing
the property filtering itself.
Allow the backend to provide a value for the `getcontentlength` property
as described in [RFC 2518 section 13.4][1].
The implementation treats is as optional, allthough it is a required
property per RFC. Most clients do perfectly fine without it, though.
Properly setting this in the backend makes the CardDAV collection
listable with clients that do require it, e.g. cadaver.
[1]: https://datatracker.ietf.org/doc/html/rfc2518#section-13.4
This does not implement any actual PROPPATCH logic, but makes the server
return a proper multistatus response with errors for each property
instead of a generic HTTP error.
It also adds the distinction between requests to the address book and
those to other resources. In CardDAV, only the address book itself has
properties that make sense to change via PROPPATCH. Those are responded
to with a 501, indicating that this needs further implementation.
Requests to other resources return 405 for each property, indicating
that the resources do not support PROPPATCH at all.
Currently, the user principal path and the home set path are both
hardcoded to "/", for both CalDAV and CardDAV. This poses a challenge if
one wishes to run a CardDAV and CalDAV server in the same server.
This commit introduces the concept of a UserPrincipalBackend. This
backend must provide the path of the current user's principal URL from
the given request context.
The CalDAV and CardDAV backends are extended to also function as
UserPrincipalBackend. In addition, they are required to supply the path
of the respective home set (`calendar-home-set` and
`addressbook-home-set`). The CardDAV and CalDAV servers act accordingly.
The individual servers will continue to work as before (including the
option of keeping everything at "/"). If one wishes to run CardDAV and
CalDAV in parallel, the new `webdav.ServeUserPrincipal()` can be used as
a convenience function to serve a common user principal URL for both
servers. The input for this function can be easily computed by the
application by getting the home set paths from the backends and using
`caldav.NewCalendarHomeSet()` and `carddav.NewAddressbookHomeSet()` to
create the home sets.
Note that the storage backend will have to know about these paths as
well. For any non-trivial use case, a storage backend should probably
have access to the same UserPrincipalBackend. That is, however, an
implementation detail and doesn't have to be reflected in the
interfaces.
Backends will need some way to signal that a precondition error occurred
(and specifying which one) without causing the server to return a 500.
This commit adds an exported function to create a specific error for
this. The existing error handling routine is slightly adapted to handle
this error in such a way that it returns the desired result.
Usage would be something like:
```
return "", carddav.NewPreconditionError(carddav.PreconditionNoUIDConflict)
```
which triggers the following HTTP response:
```
HTTP/1.1 409 Conflict.
Content-Type: text/xml; charset=utf-8.
Date: Thu, 10 Mar 2022 10:28:56 GMT.
Content-Length: 141.
Connection: close.
<?xml version="1.0" encoding="UTF-8"?>
<error xmlns="DAV:"><no-uid-conflict
xmlns="urn:ietf:params:xml:ns:carddav"></no-uid-conflict></error>
```
This response gets correctly recognized by e.g. Evolution (though it's
handling is not great).
The added error type is generic enough to be used for other stuff also.
As it is not exported (internal package), new functions for creating
such errors would have to be added.
Some clients (e.g. Evolution) will not work properly without this. It is
up to the underlying backend to actually provide this data, the headers
will only be set if it is available.