Commitdfef7de6Recorded13 Mar 2026Repositorysigil-caldav

Fix CalDAV namespace, SXML whitespace, and JMAP dict key syntax

Message

CalDAV: Use correct namespace URI urn:ietf:params:xml:ns:caldav (was urn:ietf:params:caldav) which caused 403 on Fastmail REPORT requests. Add guard around calendar-home-set discovery with fallback deriving home from principal URL. Fix find-text-in-tree to skip whitespace-only strings. Guard find-elements against non-list SXML nodes.

JMAP: Fix broken (string->keyword "..."): syntax in email.sgl and send.sgl by using dict string key syntax and #{} literals.

Changed
 src/sigil/caldav/calendar.sgl |  4 ++--
 src/sigil/caldav/client.sgl   | 42 ++++++++++++++++++++++++++----------------
 src/sigil/caldav/event.sgl    |  2 +-
 3 files changed, 29 insertions(+), 19 deletions(-)
Diff
src/sigil/caldav/calendar.sglmodified
@@ -35,7 +35,7 @@
35
(let* ((home (caldav-client-calendar-home client))
36
(response (caldav-propfind client home
37
'(d:propfind (@ (xmlns:d "DAV:")
38
(xmlns:cal "urn:ietf:params:caldav")
+38
(xmlns:cal "urn:ietf:params:xml:ns:caldav")
39
(xmlns:cs "http://calendarserver.org/ns/")
40
(xmlns:ic "http://apple.com/ns/ical/"))
41
(d:prop
@@ -125,7 +125,7 @@
125
"<?xml version=\"1.0\" encoding=\"utf-8\" ?>\n"
126
(sxml->xml
127
`(cal:mkcalendar (@ (xmlns:d "DAV:")
128
(xmlns:cal "urn:ietf:params:caldav"))
+128
(xmlns:cal "urn:ietf:params:xml:ns:caldav"))
129
(d:set
130
(d:prop
131
(d:displayname ,name)
src/sigil/caldav/client.sglmodified
@@ -201,7 +201,7 @@
201
(define (find-elements sxml tag)
202
(let ((results '()))
203
(define (walk node)
204
(when (and (pair? node) (sxml-element? node))
+204
(when (and (list? node) (pair? node) (sxml-element? node))
205
(when (eq? (sxml-tag node) tag)
206
(set! results (cons node results)))
207
(for-each walk (sxml-content node))))
@@ -277,19 +277,26 @@
277
(resolve-url url principal-href))
278
279
;; Step 2: Find calendar-home-set
280
(let* ((home-response
281
(caldav-propfind client
282
(caldav-client-principal-url client)
283
'(d:propfind (@ (xmlns:d "DAV:")
284
(xmlns:cal "urn:ietf:params:caldav"))
285
(d:prop (cal:calendar-home-set)))))
286
(home-entries (parse-multistatus home-response))
287
(home-href
288
(and (pair? home-entries)
289
(let ((prop (dict-ref (car home-entries)
290
(string->keyword "cal:calendar-home-set") #f)))
291
(and prop (pair? prop)
292
(find-text-in-tree prop))))))
+280
(let* ((home-href
+281
(guard (e (else #f))
+282
(let* ((home-response
+283
(caldav-propfind client
+284
(caldav-client-principal-url client)
+285
'(d:propfind (@ (xmlns:d "DAV:")
+286
(xmlns:cal "urn:ietf:params:xml:ns:caldav"))
+287
(d:prop (cal:calendar-home-set)))))
+288
(home-entries (parse-multistatus home-response)))
+289
(and (pair? home-entries)
+290
(let ((prop (dict-ref (car home-entries)
+291
(string->keyword "cal:calendar-home-set") #f)))
+292
(and prop (pair? prop)
+293
(find-text-in-tree prop)))))))
+294
;; Fallback: derive calendar home from principal URL
+295
;; e.g. /dav/principals/user/X/ -> /dav/calendars/user/X/
+296
(home-href (or home-href
+297
(let ((principal (caldav-client-principal-url client)))
+298
(and (string-contains? principal "/principals/")
+299
(string-replace principal "/principals/" "/calendars/"))))))
300
(unless home-href
301
(error "caldav-connect: could not discover calendar home"))
302
(set-caldav-client-calendar-home! client
@@ -297,12 +304,15 @@
304
305
client))))
306
300
;; Find the first text string in a nested SXML tree
+307
;; Find the first non-whitespace text string in a nested SXML tree
308
;; Used to extract href text from nested elements like
309
;; (d:current-user-principal (d:href "/principal/"))
310
(define (find-text-in-tree tree)
311
(cond
305
((string? tree) tree)
+312
((string? tree)
+313
(if (string=? (string-trim tree) "")
+314
#f
+315
tree))
316
((pair? tree)
317
(let loop ((items tree))
318
(if (null? items)
src/sigil/caldav/event.sglmodified
@@ -55,7 +55,7 @@
55
'()))
56
(body `(cal:calendar-query
57
(@ (xmlns:d "DAV:")
58
(xmlns:cal "urn:ietf:params:caldav"))
+58
(xmlns:cal "urn:ietf:params:xml:ns:caldav"))
59
(d:prop
60
(d:getetag)
61
(cal:calendar-data))