From 7dd64908d2875aa0c756fa4592cd5c671c97b5c7 Mon Sep 17 00:00:00 2001 From: Dan Berglund Date: Thu, 6 Jul 2023 12:12:07 +0200 Subject: [PATCH] caldav: handle PROPFIND on root It seems like the Reminders app in iOS/macOS does this request as the first thing when setting up an account, so it seems reasonable to handle it for us. This just returns the most basic current-user-principal now, but that should hopefully be enough to continue the process. --- caldav/server.go | 23 +++++++++++++++++++++++ caldav/server_test.go | 35 +++++++++++++++++++++++++++++++++-- 2 files changed, 56 insertions(+), 2 deletions(-) diff --git a/caldav/server.go b/caldav/server.go index a3b9289..bb86925 100644 --- a/caldav/server.go +++ b/caldav/server.go @@ -371,6 +371,12 @@ func (b *backend) PropFind(r *http.Request, propfind *internal.PropFind, depth i var resps []internal.Response switch resType { + case resourceTypeRoot: + resp, err := b.propFindRoot(r.Context(), propfind) + if err != nil { + return nil, err + } + resps = append(resps, *resp) case resourceTypeUserPrincipal: principalPath, err := b.Backend.CurrentUserPrincipal(r.Context()) if err != nil { @@ -453,6 +459,23 @@ func (b *backend) PropFind(r *http.Request, propfind *internal.PropFind, depth i return internal.NewMultiStatus(resps...), nil } +func (b *backend) propFindRoot(ctx context.Context, propfind *internal.PropFind) (*internal.Response, error) { + principalPath, err := b.Backend.CurrentUserPrincipal(ctx) + if err != nil { + return nil, err + } + + props := map[xml.Name]internal.PropFindFunc{ + internal.CurrentUserPrincipalName: func(*internal.RawXMLValue) (interface{}, error) { + return &internal.CurrentUserPrincipal{Href: internal.Href{Path: principalPath}}, nil + }, + internal.ResourceTypeName: func(*internal.RawXMLValue) (interface{}, error) { + return internal.NewResourceType(internal.CollectionName), nil + }, + } + return internal.NewPropFindResponse(principalPath, propfind, props) +} + func (b *backend) propFindUserPrincipal(ctx context.Context, propfind *internal.PropFind) (*internal.Response, error) { principalPath, err := b.Backend.CurrentUserPrincipal(ctx) if err != nil { diff --git a/caldav/server_test.go b/caldav/server_test.go index 0ee5fbd..c063871 100644 --- a/caldav/server_test.go +++ b/caldav/server_test.go @@ -50,6 +50,37 @@ func TestPropFindSupportedCalendarComponent(t *testing.T) { } } +var propFindUserPrincipal = ` + + + + + + + + +` + +func TestPropFindRoot(t *testing.T) { + req := httptest.NewRequest("PROPFIND", "/", strings.NewReader(propFindUserPrincipal)) + req.Header.Set("Content-Type", "application/xml") + w := httptest.NewRecorder() + calendar := &Calendar{} + handler := Handler{Backend: testBackend{calendar: calendar}} + handler.ServeHTTP(w, req) + + res := w.Result() + defer res.Body.Close() + data, err := ioutil.ReadAll(res.Body) + if err != nil { + t.Error(err) + } + resp := string(data) + if !strings.Contains(resp, `/user/`) { + t.Errorf("No user-principal returned when doing a PROPFIND against root, response:\n%s", resp) + } +} + type testBackend struct { calendar *Calendar } @@ -59,11 +90,11 @@ func (t testBackend) Calendar(ctx context.Context) (*Calendar, error) { } func (t testBackend) CalendarHomeSetPath(ctx context.Context) (string, error) { - return "", nil + return "/user/calendars/", nil } func (t testBackend) CurrentUserPrincipal(ctx context.Context) (string, error) { - return "", nil + return "/user/", nil } func (t testBackend) DeleteCalendarObject(ctx context.Context, path string) error {