go-webdav/caldav/caldav.go
Conrad Hoffmann e971269ffb Add function to validate calendar for CalDAV
CalDAV imposes a set of constraints on iCal Calendar objects. They are
spelled out in RFC 4791, section 4.1 [1]. Add an exported function to
validate a calendar according to those constraints, and return data that
is necessary for further CalDAV processing and which can only be
extracted if the calendar meets these constraints.

[1]: https://datatracker.ietf.org/doc/html/rfc4791#section-4.1
2022-05-13 09:20:23 +02:00

125 lines
3.1 KiB
Go

// Package caldav provides a client and server CalDAV implementation.
//
// CalDAV is defined in RFC 4791.
package caldav
import (
"fmt"
"time"
"github.com/emersion/go-ical"
"github.com/emersion/go-webdav"
"github.com/emersion/go-webdav/internal"
)
func NewCalendarHomeSet(path string) webdav.BackendSuppliedHomeSet {
return &calendarHomeSet{Href: internal.Href{Path: path}}
}
// ValidateCalendarObject checks the validity of a calendar object according to
// the contraints layed out in RFC 4791 section 4.1 and returns the only event
// type and UID occuring in this calendar, or an error if the calendar could
// not be validated.
func ValidateCalendarObject(cal *ical.Calendar) (eventType string, uid string, err error) {
// Calendar object resources contained in calendar collections
// MUST NOT specify the iCalendar METHOD property.
if prop := cal.Props.Get(ical.PropMethod); prop != nil {
return "", "", fmt.Errorf("calendar resource must not specify METHOD property")
}
for _, comp := range cal.Children {
// Calendar object resources contained in calendar collections
// MUST NOT contain more than one type of calendar component
// (e.g., VEVENT, VTODO, VJOURNAL, VFREEBUSY, etc.) with the
// exception of VTIMEZONE components, which MUST be specified
// for each unique TZID parameter value specified in the
// iCalendar object.
if comp.Name != ical.CompTimezone {
if eventType == "" {
eventType = comp.Name
}
if eventType != comp.Name {
return "", "", fmt.Errorf("conflicting event types in calendar: %s, %s", eventType, comp.Name)
}
// TODO check VTIMEZONE for each TZID?
}
// Calendar components in a calendar collection that have
// different UID property values MUST be stored in separate
// calendar object resources.
compUID, err := comp.Props.Text(ical.PropUID)
if err != nil {
return "", "", fmt.Errorf("error checking component UID: %v", err)
}
if uid == "" {
uid = compUID
}
if uid != compUID {
return "", "", fmt.Errorf("conflicting UID values in calendar: %s, %s", uid, compUID)
}
}
return eventType, uid, nil
}
type Calendar struct {
Path string
Name string
Description string
MaxResourceSize int64
SupportedComponentSet []string
}
type CalendarCompRequest struct {
Name string
AllProps bool
Props []string
AllComps bool
Comps []CalendarCompRequest
}
type CompFilter struct {
Name string
IsNotDefined bool
Start, End time.Time
Props []PropFilter
Comps []CompFilter
}
type ParamFilter struct {
Name string
IsNotDefined bool
TextMatch *TextMatch
}
type PropFilter struct {
Name string
IsNotDefined bool
Start, End time.Time
TextMatch *TextMatch
ParamFilter []ParamFilter
}
type TextMatch struct {
Text string
NegateCondition bool
}
type CalendarQuery struct {
CompRequest CalendarCompRequest
CompFilter CompFilter
}
type CalendarMultiGet struct {
Paths []string
CompRequest CalendarCompRequest
}
type CalendarObject struct {
Path string
ModTime time.Time
ETag string
Data *ical.Calendar
}