Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
14 changes: 13 additions & 1 deletion caldav/compatibility_hints.py
Original file line number Diff line number Diff line change
Expand Up @@ -965,7 +965,7 @@ class FeatureSet:
"links": ["https://datatracker.ietf.org/doc/html/rfc4791#section-5.3.4"],
},
"save.if-none-match": {
"description": "A PUT carrying If-None-Match: * to an existing object is refused with 412 Precondition Failed, and creates a missing object. RFC 9110 section 13.1.2 has '*' hold only when the resource has no current representation, so it means 'create, but never overwrite'; RFC 4791 section 5.3.2 tells CalDAV clients to send it when creating. save(no_overwrite=True) sends it, so the server enforces 'do not overwrite' in the same request rather than after a search. When 'unsupported', the server overwrites the existing object (SOGo ignores the header; Zimbra compares '*' as a literal etag, so the condition always holds) - the library then does not send it, nor anywhere else this is not supported ('full' or 'quirk'), and relies on the UID search before the PUT, as before. The default is 'full': RFC 4791 section 2 requires a CalDAV server to support ETags as RFC 2616 defines them, and RFC 2616 section 14.26 (now RFC 9110 section 13.1.2) says the server MUST NOT perform the method when '*' is given and the object exists.",
"description": "A PUT carrying If-None-Match: * to an existing object is refused with 412 Precondition Failed, and creates a missing object. RFC 9110 section 13.1.2 has '*' hold only when the resource has no current representation, so it means 'create, but never overwrite'; RFC 4791 section 5.3.2 tells CalDAV clients to send it when creating. save(no_overwrite=True) sends it, so the server enforces 'do not overwrite' in the same request rather than after a search. When 'unsupported', the server overwrites the existing object (SOGo ignores the header; Zimbra compares '*' as a literal etag, so the condition always holds) - the library then does not send it, nor anywhere else this is not supported ('full' or 'quirk'), and relies on the UID search before the PUT, as before. The default is 'full': RFC 4791 section 2 requires a CalDAV server to support ETags as RFC 2616 defines them, and RFC 2616 section 14.26 (now RFC 9110 section 13.1.2) says the server MUST NOT perform the method when '*' is given and the object exists. 'ungraceful' when the existing object is kept, but refused otherwise than with 412 (Bedework: 500 'duplicatename' for a new object under a name another object holds): the library cannot tell that from another error, so it treats it as unsupported and checks with a GET or a search.",
"default": {"support": "full"},
"links": ["https://datatracker.ietf.org/doc/html/rfc4791#section-5.3.2", "https://datatracker.ietf.org/doc/html/rfc4791#section-2", "https://datatracker.ietf.org/doc/html/rfc2616#section-14.26", "https://datatracker.ietf.org/doc/html/rfc9110#section-13.1.2"],
},
Expand Down Expand Up @@ -2025,10 +2025,16 @@ def __getattr__(name: str) -> Any:
"create-calendar.with-supported-component-types.multiple": {"support": "broken", "behaviour": "asked for VEVENT and VTODO, the calendar reports one concatenated name, ['VEVENTVTODO']"},
## Probed 2026-10-26 by caldav-server-tester
"save-load.icalendar.participant": {"support": "ungraceful", "behaviour": "an event with a PARTICIPANT is refused (403)"},
## Probed 2026-11-11 by caldav-server-tester
"save-load.icalendar.x-parameter": {"support": "unsupported", "behaviour": "X-CALDAV-SERVER-TESTER on SUMMARY is dropped, in an event"},
## Measured with caldav-server-tester, 2026-10-28.
"save.no-uid-conflict": {"support": "full"},
"save.no-uid-conflict.href-given": {"support": "unsupported"},
"save.no-uid-conflict.if-none-match": {"support": "quirk", "status": 412, "precondition": False},
## Probed 2026-11-11 by caldav-server-tester: a new object under a taken
## name, the case save(no_overwrite=True) guards, is refused with 500
## "duplicatename", with or without the header; the same UID gets 412.
"save.if-none-match": {"support": "ungraceful", "behaviour": "If-None-Match: * on an existing object answered 500 rather than 412, for another UID"},
## If tests are yielding unexpected results, try to increase this:
'search-cache': {'behaviour': 'delay', 'delay': 3},
'scheduling.calendar-user-address-set': {'support': 'full'},
Expand Down Expand Up @@ -2108,6 +2114,10 @@ def __getattr__(name: str) -> Any:
"save.no-uid-conflict": {"support": "full"},
"save.no-uid-conflict.href-given": {"support": "unsupported"},
"save.no-uid-conflict.if-none-match": {"support": "quirk", "status": 412, "precondition": False},
## Probed 2026-11-11 by caldav-server-tester: a new object under a taken
## name, the case save(no_overwrite=True) guards, is refused with 500
## "duplicatename", with or without the header; the same UID gets 412.
"save.if-none-match": {"support": "ungraceful", "behaviour": "If-None-Match: * on an existing object answered 500 rather than 412, for another UID"},
## Writes are asynchronous: a read-back issued immediately after a PUT may
## 404 or hand back the pre-write copy. However, in some cases the
## delay is small and varies, sometimes the caldav-server-tester tool will
Expand Down Expand Up @@ -3098,6 +3108,8 @@ def __getattr__(name: str) -> Any:
"save-load.icalendar.olson-tzid": {"support": "full", "behaviour": "the server adds a VTIMEZONE for Asia/Tokyo"},
## Probed 2026-11-07 by caldav-server-tester
"search.time-range.todo.overlap": {"support": "unsupported", "behaviour": "only tasks starting inside the range are found"},
## Probed 2026-11-11 by caldav-server-tester
"save-load.todo.status.rfc10051": {"support": "ungraceful", "behaviour": "PENDING is refused (401/403); FAILED is refused (401/403); the server does not advertise RFC 10051", "values": [], "map": {}, "refused": ["PENDING", "FAILED"]},
}

## https://ox.io/
Expand Down
1 change: 1 addition & 0 deletions changes/+bedework-hints.fixed.md
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
On Bedework (3.10 and 5.0), `save(no_overwrite=True)` for a name another object already holds raised a 500 error instead of `ConsistencyError`. The server refuses such a PUT with 500 rather than 412, so `save.if-none-match` is now `ungraceful` there, and the library checks the name with a GET before the PUT instead of sending `If-None-Match: *`.
2 changes: 1 addition & 1 deletion changes/+todo-values-x-parameter-multiget-deleted.added.md
Original file line number Diff line number Diff line change
@@ -1 +1 @@
New server hints: `save-load.todo.status` (RFC 5545's statuses), its child `save-load.todo.status.rfc10051` (PENDING and FAILED) and `save-load.todo.priority`, with the extra keys `values`, `map` and `refused` naming what a task keeps, stores as another, and is refused with; `supported-task-modes`, whether a server advertises RFC 10051 with `CALDAV:supported-task-mode-set`; `save-load.icalendar.x-parameter`; and `multiget.deleted` (Zimbra still returns a deleted object from a calendar-multiget). `save-load.stable-url` has the extra key `uid_ics_components`, for a server naming only some components `<uid>.ics` (Ox: tasks). The Ox and Zimbra profiles carry what the probes found.
New server hints: `save-load.todo.status` (RFC 5545's statuses), its child `save-load.todo.status.rfc10051` (PENDING and FAILED) and `save-load.todo.priority`, with the extra keys `values`, `map` and `refused` naming what a task keeps, stores as another, and is refused with; `supported-task-modes`, whether a server advertises RFC 10051 with `CALDAV:supported-task-mode-set`; `save-load.icalendar.x-parameter`; and `multiget.deleted` (Zimbra still returns a deleted object from a calendar-multiget). `save-load.stable-url` has the extra key `uid_ics_components`, for a server naming only some components `<uid>.ics` (Ox: tasks). The Ox, Zimbra, Bedework 3.10 and GMX profiles carry what the probes found.
Loading