Skip to content
Open
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
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 *ical.Calendar
// ReadOnly reports that the current user may only read this calendar. It
// controls the DAV:current-user-privilege-set reported by the server.
ReadOnly bool
Expand Down
38 changes: 33 additions & 5 deletions caldav/elements.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,16 +8,22 @@ import (
"github.com/emersion/go-webdav/internal"
)

const namespace = "urn:ietf:params:xml:ns:caldav"
const (
namespace = "urn:ietf:params:xml:ns:caldav"
appleNamespace = "http://apple.com/ns/ical/"
)

var (
calendarHomeSetName = xml.Name{namespace, "calendar-home-set"}

calendarDescriptionName = xml.Name{namespace, "calendar-description"}
calendarTimezoneName = xml.Name{namespace, "calendar-timezone"}
supportedCalendarDataName = xml.Name{namespace, "supported-calendar-data"}
supportedCalendarComponentSetName = xml.Name{namespace, "supported-calendar-component-set"}
maxResourceSizeName = xml.Name{namespace, "max-resource-size"}

calendarColorName = xml.Name{appleNamespace, "calendar-color"}

calendarQueryName = xml.Name{namespace, "calendar-query"}
calendarMultigetName = xml.Name{namespace, "calendar-multiget"}

Expand All @@ -41,6 +47,17 @@ type calendarDescription struct {
Description string `xml:",chardata"`
}

// https://tools.ietf.org/html/rfc4791#section-5.2.2
type calendarTimezone struct {
XMLName xml.Name `xml:"urn:ietf:params:xml:ns:caldav calendar-timezone"`
Timezone string `xml:",chardata"`
}

type calendarColor struct {
XMLName xml.Name `xml:"http://apple.com/ns/ical/ calendar-color"`
Color string `xml:",chardata"`
}

// https://tools.ietf.org/html/rfc4791#section-5.2.4
type supportedCalendarData struct {
XMLName xml.Name `xml:"urn:ietf:params:xml:ns:caldav supported-calendar-data"`
Expand Down Expand Up @@ -237,8 +254,19 @@ 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"`
Set mkcolSet `xml:"DAV: set"`
}

type mkcolSet struct {
Prop mkcolProp `xml:"DAV: prop"`
}

type mkcolProp struct {
ResourceType internal.ResourceType `xml:"DAV: resourcetype"`
DisplayName string `xml:"DAV: displayname"`
CalendarDescription string `xml:"urn:ietf:params:xml:ns:caldav calendar-description"`
CalendarTimezone string `xml:"urn:ietf:params:xml:ns:caldav calendar-timezone"`
CalendarColor string `xml:"http://apple.com/ns/ical/ calendar-color"`
SupportedCalendarComponentSet supportedCalendarComponentSet `xml:"urn:ietf:params:xml:ns:caldav supported-calendar-component-set"`
}
50 changes: 46 additions & 4 deletions caldav/server.go
Original file line number Diff line number Diff line change
Expand Up @@ -559,13 +559,29 @@ func (b *backend) propFindCalendar(ctx context.Context, propfind *internal.PropF
Description: cal.Description,
})
}
if cal.Color != "" {
props[calendarColorName] = internal.PropFindValue(&calendarColor{
Color: cal.Color,
})
}
if cal.Timezone != nil {
props[calendarTimezoneName] = func(*internal.RawXMLValue) (interface{}, error) {
var buf bytes.Buffer
if err := ical.NewEncoder(&buf).Encode(cal.Timezone); err != nil {
return nil, err
}
return &calendarTimezone{
Timezone: buf.String(),
}, nil
}
}
if cal.MaxResourceSize > 0 {
props[maxResourceSizeName] = internal.PropFindValue(&maxResourceSize{
Size: cal.MaxResourceSize,
})
}

// TODO: CALDAV:calendar-timezone, CALDAV:supported-calendar-component-set, CALDAV:min-date-time, CALDAV:max-date-time, CALDAV:max-instances, CALDAV:max-attendees-per-instance
// TODO: CALDAV:min-date-time, CALDAV:max-date-time, CALDAV:max-instances, CALDAV:max-attendees-per-instance

return internal.NewPropFindResponse(cal.Path, propfind, props)
}
Expand Down Expand Up @@ -724,16 +740,42 @@ func (b *backend) Mkcol(r *http.Request) error {
return internal.HTTPErrorf(http.StatusBadRequest, "carddav: error parsing mkcol request: %s", err.Error())
}

if !m.ResourceType.Is(internal.CollectionName) || !m.ResourceType.Is(calendarName) {
prop := m.Set.Prop
if !prop.ResourceType.Is(internal.CollectionName) || !prop.ResourceType.Is(calendarName) {
return internal.HTTPErrorf(http.StatusBadRequest, "carddav: unexpected resource type")
}
cal.Name = m.DisplayName
// TODO ...
cal.Name = prop.DisplayName
cal.Description = prop.CalendarDescription
cal.Color = strings.TrimSpace(prop.CalendarColor)

if s := strings.TrimSpace(prop.CalendarTimezone); s != "" {
tz, err := decodeCalendarTimezone(s)
if err != nil {
return err
}
cal.Timezone = tz
}

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

return b.Backend.CreateCalendar(r.Context(), &cal)
}

func decodeCalendarTimezone(s string) (*ical.Calendar, error) {
cal, err := ical.NewDecoder(strings.NewReader(s)).Decode()
if err != nil {
return nil, NewPreconditionError(PreconditionValidCalendarData)
}
if len(cal.Children) != 1 || cal.Children[0].Name != ical.CompTimezone {
return nil, NewPreconditionError(PreconditionValidCalendarData)
}
return cal, nil
}

func (b *backend) Copy(r *http.Request, dest *internal.Href, recursive, overwrite bool) (created bool, err error) {
return false, internal.HTTPErrorf(http.StatusNotImplemented, "caldav: Copy not implemented")
}
Expand Down
Loading