Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

caldav: support more features for recurring events #105

Draft
wants to merge 1 commit into
base: master
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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
25 changes: 19 additions & 6 deletions caldav/client.go
Original file line number Diff line number Diff line change
Expand Up @@ -141,17 +141,30 @@ func encodeCalendarCompReq(c *CalendarCompRequest) (*comp, error) {
return &encoded, nil
}

func encodeCalendarReq(c *CalendarCompRequest) (*internal.Prop, error) {
compReq, err := encodeCalendarCompReq(c)
func encodeCalendarDataReq(d *CalendarDataRequest) (*calendarDataReq, error) {
encodedComp, err := encodeCalendarCompReq(&d.Comp)
if err != nil {
return nil, err
}

calDataReq := calendarDataReq{Comp: compReq}
encoded := calendarDataReq{
Comp: encodedComp,
}

// TODO expand, limit-recurrence, free-busy

return &encoded, nil
}

func encodeCalendarReq(c *CalendarDataRequest) (*internal.Prop, error) {
calDataReq, err := encodeCalendarDataReq(c)
if err != nil {
return nil, err
}

getLastModReq := internal.NewRawXMLElement(internal.GetLastModifiedName, nil, nil)
getETagReq := internal.NewRawXMLElement(internal.GetETagName, nil, nil)
return internal.EncodeProp(&calDataReq, getLastModReq, getETagReq)
return internal.EncodeProp(calDataReq, getLastModReq, getETagReq)
}

func encodeCompFilter(filter *CompFilter) *compFilter {
Expand Down Expand Up @@ -215,7 +228,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 +250,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
66 changes: 55 additions & 11 deletions caldav/server.go
Original file line number Diff line number Diff line change
Expand Up @@ -188,25 +188,69 @@ 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
}
}

result := &CalendarDataRequest{
Comp: *comp,
}

if calendarData.Expand != nil {
result.Expand = &TimeRange{
Start: time.Time(calendarData.Expand.Start),
End: time.Time(calendarData.Expand.End),
}
}
if calendarData.LimitRecurrence != nil {
result.Expand = &TimeRange{
Start: time.Time(calendarData.LimitRecurrence.Start),
End: time.Time(calendarData.LimitRecurrence.End),
}
}
if calendarData.LimitFreeBusy != nil {
result.Expand = &TimeRange{
Start: time.Time(calendarData.LimitFreeBusy.Start),
End: time.Time(calendarData.LimitFreeBusy.End),
}
}

return result, nil
}

func (h *Handler) handleQuery(r *http.Request, w http.ResponseWriter, query *calendarQuery) error {
var q CalendarQuery
// TODO: calendar-data in query.Prop
cf, err := decodeCompFilter(&query.Filter.CompFilter)
if err != nil {
return err
}
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 +277,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 +292,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