Skip to content

Commit

Permalink
fix: add support for more props on CalDAV MKCOL
Browse files Browse the repository at this point in the history
  • Loading branch information
DeepDiver1975 committed Feb 22, 2024
1 parent 25f1014 commit 36d2981
Show file tree
Hide file tree
Showing 6 changed files with 243 additions and 9 deletions.
2 changes: 2 additions & 0 deletions caldav/caldav.go
Original file line number Diff line number Diff line change
Expand Up @@ -67,8 +67,10 @@ type Calendar struct {
Path string
Name string
Description string
Color string
MaxResourceSize int64
SupportedComponentSet []string
TimeZone string
}

type CalendarCompRequest struct {
Expand Down
11 changes: 7 additions & 4 deletions caldav/elements.go
Original file line number Diff line number Diff line change
Expand Up @@ -230,8 +230,11 @@ func (r *reportReq) UnmarshalXML(d *xml.Decoder, start xml.StartElement) error {
}

type mkcolReq struct {
XMLName xml.Name `xml:"DAV: mkcol"`
ResourceType internal.ResourceType `xml:"set>prop>resourcetype"`
DisplayName string `xml:"set>prop>displayname"`
// TODO this could theoretically contain all addressbook properties?
XMLName xml.Name `xml:"DAV: mkcol"`
ResourceType internal.ResourceType `xml:"set>prop>resourcetype"`
DisplayName string `xml:"set>prop>displayname"`
Description string `xml:"set>prop>calendar-description"`
CalendarColor string `xml:"set>prop>calendar-color"`
CalemdarTimeZone string `xml:"set>prop>calendar-timezone"`
SupportedCalendarComponentSet supportedCalendarComponentSet `xml:"set>prop>supported-calendar-component-set"`
}
9 changes: 8 additions & 1 deletion caldav/server.go
Original file line number Diff line number Diff line change
Expand Up @@ -724,7 +724,14 @@ func (b *backend) Mkcol(r *http.Request) error {
return internal.HTTPErrorf(http.StatusBadRequest, "carddav: unexpected resource type")
}
cal.Name = m.DisplayName
// TODO ...
cal.Description = m.Description
cal.Color = strings.TrimSpace(m.CalendarColor)
cal.TimeZone = strings.TrimSpace(m.CalemdarTimeZone)

cal.SupportedComponentSet = make([]string, len(m.SupportedCalendarComponentSet.Comp))
for i, v := range m.SupportedCalendarComponentSet.Comp {
cal.SupportedComponentSet[i] = v.Name
}
}

return b.Backend.CreateCalendar(r.Context(), &cal)
Expand Down
213 changes: 209 additions & 4 deletions caldav/server_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ package caldav
import (
"context"
"fmt"
"github.com/stretchr/testify/assert"
"io"
"io/ioutil"
"net/http/httptest"
Expand Down Expand Up @@ -33,7 +34,7 @@ func TestPropFindSupportedCalendarComponent(t *testing.T) {
req.Body = io.NopCloser(strings.NewReader(propFindSupportedCalendarComponentRequest))
req.Header.Set("Content-Type", "application/xml")
w := httptest.NewRecorder()
handler := Handler{Backend: testBackend{calendars: []Calendar{*calendar}}}
handler := Handler{Backend: &testBackend{calendars: []Calendar{*calendar}}}
handler.ServeHTTP(w, req)

res := w.Result()
Expand Down Expand Up @@ -68,7 +69,7 @@ func TestPropFindRoot(t *testing.T) {
req.Header.Set("Content-Type", "application/xml")
w := httptest.NewRecorder()
calendar := &Calendar{}
handler := Handler{Backend: testBackend{calendars: []Calendar{*calendar}}}
handler := Handler{Backend: &testBackend{calendars: []Calendar{*calendar}}}
handler.ServeHTTP(w, req)

res := w.Result()
Expand Down Expand Up @@ -118,7 +119,7 @@ func TestMultiCalendarBackend(t *testing.T) {
req := httptest.NewRequest("PROPFIND", "/user/calendars/", strings.NewReader(propFindUserPrincipal))
req.Header.Set("Content-Type", "application/xml")
w := httptest.NewRecorder()
handler := Handler{Backend: testBackend{
handler := Handler{Backend: &testBackend{
calendars: calendars,
objectMap: map[string][]CalendarObject{
calendarB.Path: []CalendarObject{object},
Expand Down Expand Up @@ -177,12 +178,216 @@ func TestMultiCalendarBackend(t *testing.T) {
}
}

var mkcolRequestData = `
<?xml version='1.0' encoding='UTF-8' ?>
<mkcol
xmlns="DAV:"
xmlns:CAL="urn:ietf:params:xml:ns:caldav"
xmlns:CARD="urn:ietf:params:xml:ns:carddav">
<set>
<prop>
<resourcetype>
<collection />
<CAL:calendar />
</resourcetype>
<displayname>Test calendar</displayname>
<CAL:calendar-description>A calendar for testing</CAL:calendar-description>
<n0:calendar-color
xmlns:n0="http://apple.com/ns/ical/">#009688FF
</n0:calendar-color>
<CAL:calendar-timezone>
<![CDATA[BEGIN:VCALENDAR
BEGIN:VTIMEZONE
TZID:Europe/Berlin
LAST-MODIFIED:20230104T023643Z
TZURL:https://www.tzurl.org/zoneinfo/Europe/Berlin
X-LIC-LOCATION:Europe/Berlin
X-PROLEPTIC-TZNAME:LMT
BEGIN:STANDARD
TZNAME:CET
TZOFFSETFROM:+005328
TZOFFSETTO:+0100
DTSTART:18930401T000632
END:STANDARD
BEGIN:DAYLIGHT
TZNAME:CEST
TZOFFSETFROM:+0100
TZOFFSETTO:+0200
DTSTART:19160430T230000
RDATE:19400401T020000
RDATE:19430329T020000
RDATE:19460414T020000
RDATE:19470406T030000
RDATE:19480418T020000
RDATE:19490410T020000
RDATE:19800406T020000
END:DAYLIGHT
BEGIN:STANDARD
TZNAME:CET
TZOFFSETFROM:+0200
TZOFFSETTO:+0100
DTSTART:19161001T010000
RDATE:19421102T030000
RDATE:19431004T030000
RDATE:19441002T030000
RDATE:19451118T030000
RDATE:19461007T030000
END:STANDARD
BEGIN:DAYLIGHT
TZNAME:CEST
TZOFFSETFROM:+0100
TZOFFSETTO:+0200
DTSTART:19170416T020000
RRULE:FREQ=YEARLY;UNTIL=19180415T010000Z;BYMONTH=4;BYDAY=3MO
END:DAYLIGHT
BEGIN:STANDARD
TZNAME:CET
TZOFFSETFROM:+0200
TZOFFSETTO:+0100
DTSTART:19170917T030000
RRULE:FREQ=YEARLY;UNTIL=19180916T010000Z;BYMONTH=9;BYDAY=3MO
END:STANDARD
BEGIN:DAYLIGHT
TZNAME:CEST
TZOFFSETFROM:+0100
TZOFFSETTO:+0200
DTSTART:19440403T020000
RRULE:FREQ=YEARLY;UNTIL=19450402T010000Z;BYMONTH=4;BYDAY=1MO
END:DAYLIGHT
BEGIN:DAYLIGHT
TZNAME:CEMT
TZOFFSETFROM:+0200
TZOFFSETTO:+0300
DTSTART:19450524T010000
RDATE:19470511T020000
END:DAYLIGHT
BEGIN:DAYLIGHT
TZNAME:CEST
TZOFFSETFROM:+0300
TZOFFSETTO:+0200
DTSTART:19450924T030000
RDATE:19470629T030000
END:DAYLIGHT
BEGIN:STANDARD
TZNAME:CET
TZOFFSETFROM:+0100
TZOFFSETTO:+0100
DTSTART:19460101T000000
RDATE:19800101T000000
END:STANDARD
BEGIN:STANDARD
TZNAME:CET
TZOFFSETFROM:+0200
TZOFFSETTO:+0100
DTSTART:19471005T030000
RRULE:FREQ=YEARLY;UNTIL=19491002T010000Z;BYMONTH=10;BYDAY=1SU
END:STANDARD
BEGIN:STANDARD
TZNAME:CET
TZOFFSETFROM:+0200
TZOFFSETTO:+0100
DTSTART:19800928T030000
RRULE:FREQ=YEARLY;UNTIL=19950924T010000Z;BYMONTH=9;BYDAY=-1SU
END:STANDARD
BEGIN:DAYLIGHT
TZNAME:CEST
TZOFFSETFROM:+0100
TZOFFSETTO:+0200
DTSTART:19810329T020000
RRULE:FREQ=YEARLY;BYMONTH=3;BYDAY=-1SU
END:DAYLIGHT
BEGIN:STANDARD
TZNAME:CET
TZOFFSETFROM:+0200
TZOFFSETTO:+0100
DTSTART:19961027T030000
RRULE:FREQ=YEARLY;BYMONTH=10;BYDAY=-1SU
END:STANDARD
END:VTIMEZONE
END:VCALENDAR
]]>
</CAL:calendar-timezone>
<CAL:supported-calendar-component-set>
<CAL:comp name="VEVENT" />
<CAL:comp name="VTODO" />
<CAL:comp name="VJOURNAL" />
</CAL:supported-calendar-component-set>
</prop>
</set>
</mkcol>`

func TestCreateCalendar(t *testing.T) {
tb := testBackend{
calendars: nil,
objectMap: nil,
}
b := backend{
Backend: &tb,
Prefix: "/dav",
}
req := httptest.NewRequest("MKCOL", "/dav/calendars/user0/test-calendar", strings.NewReader(mkcolRequestData))
req.Header.Set("Content-Type", "application/xml")

err := b.Mkcol(req)
assert.NoError(t, err)
assert.Len(t, tb.calendars, 1)
c := tb.calendars[0]
assert.Equal(t, "Test calendar", c.Name)
assert.Equal(t, "/dav/calendars/user0/test-calendar", c.Path)
assert.Equal(t, "A calendar for testing", c.Description)
assert.Equal(t, "#009688FF", c.Color)
assert.Equal(t, []string{"VEVENT", "VTODO", "VJOURNAL"}, c.SupportedComponentSet)
assert.Contains(t, c.TimeZone, "BEGIN:VCALENDAR")
}

var mkcolRequestDataMinimalBody = `
<?xml version='1.0' encoding='UTF-8' ?>
<mkcol
xmlns="DAV:"
xmlns:CAL="urn:ietf:params:xml:ns:caldav"
xmlns:CARD="urn:ietf:params:xml:ns:carddav">
<set>
<prop>
<resourcetype>
<collection />
<CAL:calendar />
</resourcetype>
<displayname>Test calendar</displayname>
</prop>
</set>
</mkcol>`

func TestCreateCalendarMinimalBody(t *testing.T) {
tb := testBackend{
calendars: nil,
objectMap: nil,
}
b := backend{
Backend: &tb,
Prefix: "/dav",
}
req := httptest.NewRequest("MKCOL", "/dav/calendars/user0/test-calendar", strings.NewReader(mkcolRequestDataMinimalBody))
req.Header.Set("Content-Type", "application/xml")

err := b.Mkcol(req)
assert.NoError(t, err)
assert.Len(t, tb.calendars, 1)
c := tb.calendars[0]
assert.Equal(t, "Test calendar", c.Name)
assert.Equal(t, "/dav/calendars/user0/test-calendar", c.Path)
assert.Equal(t, "", c.Description)
assert.Equal(t, "", c.Color)
assert.Equal(t, []string{}, c.SupportedComponentSet)
assert.Equal(t, "", c.TimeZone)
}

type testBackend struct {
calendars []Calendar
objectMap map[string][]CalendarObject
}

func (t testBackend) CreateCalendar(ctx context.Context, calendar *Calendar) error {
func (t *testBackend) CreateCalendar(ctx context.Context, calendar *Calendar) error {
t.calendars = append(t.calendars, *calendar)
return nil
}

Expand Down
1 change: 1 addition & 0 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -5,5 +5,6 @@ go 1.13
require (
github.com/emersion/go-ical v0.0.0-20220601085725-0864dccc089f
github.com/emersion/go-vcard v0.0.0-20230815062825-8fda7d206ec9
github.com/stretchr/testify v1.8.4 // indirect
github.com/teambition/rrule-go v1.8.2 // indirect
)
16 changes: 16 additions & 0 deletions go.sum
Original file line number Diff line number Diff line change
@@ -1,7 +1,23 @@
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/emersion/go-ical v0.0.0-20220601085725-0864dccc089f h1:feGUUxxvOtWVOhTko8Cbmp33a+tU0IMZxMEmnkoAISQ=
github.com/emersion/go-ical v0.0.0-20220601085725-0864dccc089f/go.mod h1:2MKFUgfNMULRxqZkadG1Vh44we3y5gJAtTBlVsx1BKQ=
github.com/emersion/go-vcard v0.0.0-20230815062825-8fda7d206ec9 h1:ATgqloALX6cHCranzkLb8/zjivwQ9DWWDCQRnxTPfaA=
github.com/emersion/go-vcard v0.0.0-20230815062825-8fda7d206ec9/go.mod h1:HMJKR5wlh/ziNp+sHEDV2ltblO4JD2+IdDOWtGcQBTM=
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw=
github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo=
github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU=
github.com/stretchr/testify v1.8.4 h1:CcVxjf3Q8PM0mHUKJCdn+eZZtm5yQwehR5yeSVQQcUk=
github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo=
github.com/teambition/rrule-go v1.7.2/go.mod h1:mBJ1Ht5uboJ6jexKdNUJg2NcwP8uUMNvStWXlJD3MvU=
github.com/teambition/rrule-go v1.8.2 h1:lIjpjvWTj9fFUZCmuoVDrKVOtdiyzbzc93qTmRVe/J8=
github.com/teambition/rrule-go v1.8.2/go.mod h1:Ieq5AbrKGciP1V//Wq8ktsTXwSwJHDD5mD/wLBGl3p4=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=

0 comments on commit 36d2981

Please sign in to comment.