fix: resolve CalDAV PROPFIND returning empty property values (#153)

Root cause: XML namespace prefixes (e.g. "D", "C") were not being
resolved to their actual namespace URIs (e.g. "DAV:",
"urn:ietf:params:xml:ns:caldav") during PROPFIND parsing. This caused
all property match arms to fall through to the catch-all, producing
empty XML elements.

Changes:
- Add namespace-aware XML parsing (collect_ns_decls + resolve_name) to
  WebDavAdapter, used by all DAV protocol parsers (WebDAV, CalDAV,
  CardDAV)
- Add /.well-known/caldav -> /caldav/ redirect (RFC 6764)
- Add root /caldav/ PROPFIND response with current-user-principal and
  calendar-home-set discovery properties
- Add /caldav/principals/{username}/ PROPFIND handler (was 500 error)
- Add /caldav/{username}/ user calendar home handler (calendar-home-set
  target)
- Respect Depth header at root: depth 0 returns only root entry, depth
  1+ includes calendar children
- Fix pre-existing TRANSCODE_POOL_THREADS test compilation error
- Add 8 new tests covering namespace resolution, discovery properties,
  and principal responses

https://claude.ai/code/session_01T49VBJSimgo28APxbucHzq
This commit is contained in:
Claude
2026-03-02 20:40:41 +00:00
parent 991a98b1fc
commit b9de342223
7 changed files with 879 additions and 81 deletions
+5
View File
@@ -114,6 +114,7 @@ async fn main() -> Result<(), Box<dyn std::error::Error>> {
use oxicloud::interfaces::api::handlers::carddav_handler;
use oxicloud::interfaces::api::handlers::webdav_handler;
let caldav_router = caldav_handler::caldav_routes();
let well_known_router = caldav_handler::well_known_routes();
let carddav_router = carddav_handler::carddav_routes();
let webdav_router = webdav_handler::webdav_routes();
@@ -228,6 +229,8 @@ async fn main() -> Result<(), Box<dyn std::error::Error>> {
.nest("/api", public_api_routes)
// All other API routes are protected by auth middleware
.nest("/api", protected_api)
// RFC 6764 well-known discovery (public, no auth — just redirects)
.merge(well_known_router.clone())
// CalDAV/CardDAV/WebDAV protocols merged at top-level for client compatibility
.merge(caldav_protected)
.merge(carddav_protected)
@@ -251,6 +254,8 @@ async fn main() -> Result<(), Box<dyn std::error::Error>> {
app = Router::new()
.nest("/api", public_api_routes)
.nest("/api", api_routes)
// RFC 6764 well-known discovery (just redirects)
.merge(well_known_router)
// CalDAV/CardDAV/WebDAV protocols merged at top-level
.merge(caldav_router)
.merge(carddav_router)