Skip to content

Commit

Permalink
caldav: support more features for recurring events
Browse files Browse the repository at this point in the history
The protocol allows clients to ask the server to e.g. expand recurring
events to a set of simple events in a given time-frame, so that the
client does not have to calculate the recurrences.

This commit adds the foundation for support for expand [1],
limit-recurrence-set [2], and limit-freebusy-set [3] functionality.
However, the actual transformation of events returned to clients is not
yet implemented.

[1]: https://datatracker.ietf.org/doc/html/rfc4791#section-9.6.5
[2]: https://datatracker.ietf.org/doc/html/rfc4791#section-9.6.6
[3]: https://datatracker.ietf.org/doc/html/rfc4791#section-9.6.7
  • Loading branch information
bitfehler committed Aug 31, 2022
1 parent dc63df9 commit eaba278
Show file tree
Hide file tree
Showing 4 changed files with 79 additions and 19 deletions.
15 changes: 13 additions & 2 deletions caldav/caldav.go
Original file line number Diff line number Diff line change
Expand Up @@ -69,6 +69,17 @@ type Calendar struct {
SupportedComponentSet []string
}

type TimeRange struct {
Start, End time.Time
}

type CalendarDataRequest struct {
Comp CalendarCompRequest
Expand *TimeRange
LimitRecurrence *TimeRange
LimitFreeBusy *TimeRange
}

type CalendarCompRequest struct {
Name string

Expand Down Expand Up @@ -107,13 +118,13 @@ type TextMatch struct {
}

type CalendarQuery struct {
CompRequest CalendarCompRequest
DataRequest CalendarDataRequest
CompFilter CompFilter
}

type CalendarMultiGet struct {
Paths []string
CompRequest CalendarCompRequest
DataRequest CalendarDataRequest
}

type CalendarObject struct {
Expand Down
8 changes: 4 additions & 4 deletions caldav/client.go
Original file line number Diff line number Diff line change
Expand Up @@ -141,8 +141,8 @@ func encodeCalendarCompReq(c *CalendarCompRequest) (*comp, error) {
return &encoded, nil
}

func encodeCalendarReq(c *CalendarCompRequest) (*internal.Prop, error) {
compReq, err := encodeCalendarCompReq(c)
func encodeCalendarReq(c *CalendarDataRequest) (*internal.Prop, error) {
compReq, err := encodeCalendarCompReq(&c.Comp)
if err != nil {
return nil, err
}
Expand Down Expand Up @@ -215,7 +215,7 @@ func decodeCalendarObjectList(ms *internal.MultiStatus) ([]CalendarObject, error
}

func (c *Client) QueryCalendar(calendar string, query *CalendarQuery) ([]CalendarObject, error) {
propReq, err := encodeCalendarReq(&query.CompRequest)
propReq, err := encodeCalendarReq(&query.DataRequest)
if err != nil {
return nil, err
}
Expand All @@ -237,7 +237,7 @@ func (c *Client) QueryCalendar(calendar string, query *CalendarQuery) ([]Calenda
}

func (c *Client) MultiGetCalendar(path string, multiGet *CalendarMultiGet) ([]CalendarObject, error) {
propReq, err := encodeCalendarReq(&multiGet.CompRequest)
propReq, err := encodeCalendarReq(&multiGet.DataRequest)
if err != nil {
return nil, err
}
Expand Down
29 changes: 26 additions & 3 deletions caldav/elements.go
Original file line number Diff line number Diff line change
Expand Up @@ -177,9 +177,11 @@ func (t *dateWithUTCTime) MarshalText() ([]byte, error) {

// Request variant of https://tools.ietf.org/html/rfc4791#section-9.6
type calendarDataReq struct {
XMLName xml.Name `xml:"urn:ietf:params:xml:ns:caldav calendar-data"`
Comp *comp `xml:"comp,omitempty"`
// TODO: expand, limit-recurrence-set, limit-freebusy-set
XMLName xml.Name `xml:"urn:ietf:params:xml:ns:caldav calendar-data"`
Comp *comp `xml:"comp,omitempty"`
Expand *expand `xml:"expand,omitempty`
LimitRecurrence *limitRecurrenceSet `xml:"limit-recurrence-set,omitempty`
LimitFreeBusy *limitFreeBusySet `xml:"limit-freebusy-set,omitempty`
}

// https://tools.ietf.org/html/rfc4791#section-9.6.1
Expand All @@ -201,6 +203,27 @@ type prop struct {
// TODO: novalue
}

// https://tools.ietf.org/html/rfc4791#section-9.6.5
type expand struct {
XMLName xml.Name `xml:"urn:ietf:params:xml:ns:caldav expand"`
Start dateWithUTCTime `xml:"start,attr,omitempty"`
End dateWithUTCTime `xml:"end,attr,omitempty"`
}

// https://tools.ietf.org/html/rfc4791#section-9.6.6
type limitRecurrenceSet struct {
XMLName xml.Name `xml:"urn:ietf:params:xml:ns:caldav limit-recurrence-set"`
Start dateWithUTCTime `xml:"start,attr,omitempty"`
End dateWithUTCTime `xml:"end,attr,omitempty"`
}

// https://tools.ietf.org/html/rfc4791#section-9.6.7
type limitFreeBusySet struct {
XMLName xml.Name `xml:"urn:ietf:params:xml:ns:caldav limit-freebusy-set"`
Start dateWithUTCTime `xml:"start,attr,omitempty"`
End dateWithUTCTime `xml:"end,attr,omitempty"`
}

// Response variant of https://tools.ietf.org/html/rfc4791#section-9.6
type calendarDataResp struct {
XMLName xml.Name `xml:"urn:ietf:params:xml:ns:caldav calendar-data"`
Expand Down
46 changes: 36 additions & 10 deletions caldav/server.go
Original file line number Diff line number Diff line change
Expand Up @@ -188,14 +188,28 @@ func decodeComp(comp *comp) (*CalendarCompRequest, error) {
return req, nil
}

func decodeCalendarDataReq(calendarData *calendarDataReq) (*CalendarCompRequest, error) {
if calendarData.Comp == nil {
return &CalendarCompRequest{
AllProps: true,
AllComps: true,
}, nil
}
return decodeComp(calendarData.Comp)
func decodeCalendarDataReq(calendarData *calendarDataReq) (*CalendarDataRequest, error) {
if calendarData == nil {
return nil, internal.HTTPErrorf(http.StatusBadRequest, "caldav: unexpected missing calendar-data in request")
}
if calendarData.Expand != nil && calendarData.LimitRecurrence != nil {
return nil, internal.HTTPErrorf(http.StatusBadRequest, "caldav: only one of expand or limit-recurrence-set can be specified in calendar-data")
}

var comp = &CalendarCompRequest{}
if calendarData.Comp != nil {
var err error
comp, err = decodeComp(calendarData.Comp)
if err != nil {
return nil, err
}
}

// TODO expand, limit-recurrence-set, limit-freebusy-set

return &CalendarDataRequest{
Comp: *comp,
}, nil
}

func (h *Handler) handleQuery(r *http.Request, w http.ResponseWriter, query *calendarQuery) error {
Expand All @@ -207,6 +221,18 @@ func (h *Handler) handleQuery(r *http.Request, w http.ResponseWriter, query *cal
}
q.CompFilter = *cf

if query.Prop != nil {
var calendarData calendarDataReq
if err := query.Prop.Decode(&calendarData); err != nil && !internal.IsNotFound(err) {
return err
}
decoded, err := decodeCalendarDataReq(&calendarData)
if err != nil {
return err
}
q.DataRequest = *decoded
}

cos, err := h.Backend.QueryCalendarObjects(r.Context(), &q)
if err != nil {
return err
Expand All @@ -233,7 +259,7 @@ func (h *Handler) handleQuery(r *http.Request, w http.ResponseWriter, query *cal
}

func (h *Handler) handleMultiget(ctx context.Context, w http.ResponseWriter, multiget *calendarMultiget) error {
var dataReq CalendarCompRequest
var dataReq CalendarDataRequest
if multiget.Prop != nil {
var calendarData calendarDataReq
if err := multiget.Prop.Decode(&calendarData); err != nil && !internal.IsNotFound(err) {
Expand All @@ -248,7 +274,7 @@ func (h *Handler) handleMultiget(ctx context.Context, w http.ResponseWriter, mul

var resps []internal.Response
for _, href := range multiget.Hrefs {
co, err := h.Backend.GetCalendarObject(ctx, href.Path, &dataReq)
co, err := h.Backend.GetCalendarObject(ctx, href.Path, &dataReq.Comp)
if err != nil {
resp := internal.NewErrorResponse(href.Path, err)
resps = append(resps, *resp)
Expand Down

0 comments on commit eaba278

Please sign in to comment.