diff --git a/src/calanonsync/caldav.go b/src/calanonsync/caldav.go index 76d237c..05a835a 100644 --- a/src/calanonsync/caldav.go +++ b/src/calanonsync/caldav.go @@ -28,6 +28,7 @@ type ICal struct { eventData []string // sub slice } +// Retrieve the summary (aka title) of the event. func (i ICal) Summary() string { for _, s := range i.eventData { if strings.HasPrefix(s, "SUMMARY:") { @@ -37,6 +38,7 @@ func (i ICal) Summary() string { return "" } +// Retrieve the UID of the event. func (i ICal) UID() string { for _, s := range i.eventData { if strings.HasPrefix(s, "UID:") { @@ -46,6 +48,10 @@ func (i ICal) UID() string { return "" } +// Internal helper function to retrieve a time from the given field within +// the event data of the ICal structure. Timezone information are ignored. +// The time is therefore either a local time or a UTC time, depending on +// the notation. func (i ICal) getTimeField(f string) time.Time { for _, s := range i.eventData { if strings.HasPrefix(s, f) { @@ -67,10 +73,12 @@ func (i ICal) getTimeField(f string) time.Time { return time.Time{} } +// Retrieve the start time of the event. func (i ICal) Start() time.Time { return i.getTimeField("DTSTART") } +// Retrieve the end time of the event. func (i ICal) End() time.Time { return i.getTimeField("DTEND") } @@ -90,6 +98,7 @@ func (i *ICal) UnmarshalXML(d *xml.Decoder, start xml.StartElement) error { return nil } +// Builds a string representation of the ICal (as ICS). func (i ICal) String() string { sb := &strings.Builder{} for n, line := range i.data { @@ -101,6 +110,8 @@ func (i ICal) String() string { return sb.String() } +// Parses the given ICal into structure. It identifies the first (and only +// the first) event within the given calendar. func ParseICal(r io.Reader) (*ICal, error) { var data []string scanner := bufio.NewScanner(r) @@ -136,6 +147,9 @@ func ParseICal(r io.Reader) (*ICal, error) { }, err } +// Create a new empty ICal structure with the given parameters. +// Currently only supports time based events; date-only is not +// supported. func CreateICal(uid, summary string, start, end time.Time) *ICal { model := struct { UID string @@ -177,6 +191,12 @@ type CalDAVItem struct { *ICal } +// Retrieve all events from the configured CalDAV server. +// Each item will contain the original URL in relative format +// as well as the ICal data for the given event. +// +// It is expected that each event is a ICal calendar with only +// a single event. func (c *CalDAV) GetEvents() ([]CalDAVItem, error) { req, err := http.NewRequest("PROPFIND", c.URL, strings.NewReader(``)) if err != nil { @@ -207,6 +227,10 @@ func (c *CalDAV) GetEvents() ([]CalDAVItem, error) { return result, nil } +// Put the given item on the configured CalDAV server, either updating +// an existing item or creating a new one. +// The target URL is calculated by the URL in the settings together with +// the HRef of the CalDAV item. func (c CalDAV) UploadItem(item CalDAVItem) error { itemURL, _ := url.Parse(item.HRef) base, _ := url.Parse(c.URL) @@ -231,6 +255,9 @@ func (c CalDAV) UploadItem(item CalDAVItem) error { return nil } +// Removes the given item from the configured CalDAV server using the +// calculated URL from the settings together with the HRef of the given +// CalDAV item. func (c CalDAV) DeleteItem(item CalDAVItem) error { itemURL, _ := url.Parse(item.HRef) base, _ := url.Parse(c.URL) diff --git a/src/calanonsync/ews.go b/src/calanonsync/ews.go index b67c113..74f4941 100644 --- a/src/calanonsync/ews.go +++ b/src/calanonsync/ews.go @@ -29,6 +29,10 @@ type CalendarItem struct { CalendarItemType string } +// Build a hash for the given calendar item by combining the UID and +// the recurrenceId therefore guaranteeing a unique identifier for the +// event, even if it has been a calculated recurrence (which would +// otherwise share the same UID). func (ci CalendarItem) Hash() string { h := md5.New() h.Write([]byte(ci.UID)) @@ -59,6 +63,9 @@ func NewEWSCalendar(url, username, password string) *EWSCalendar { } } +// Internal helper to prepare a request to the EWS server. This is a shortcut +// since all requests share some similarities: they transmit XML and receive +// XML. Also it needs a basic auth header. func (e *EWSCalendar) prepareRequest(body io.Reader) (req *http.Request, err error) { req, err = http.NewRequest(http.MethodPost, e.url, body) req.Header.Set("Content-Type", "text/xml") @@ -67,6 +74,8 @@ func (e *EWSCalendar) prepareRequest(body io.Reader) (req *http.Request, err err return } +// Request the ID of the default calendar folder. This is necessary to do +// the actual event query. func (e *EWSCalendar) getCalendarFolderID() (id *FolderId, err error) { req, err := e.prepareRequest(strings.NewReader(folderIdRequest)) if err != nil { @@ -108,6 +117,7 @@ func (e *EWSCalendar) getCalendarFolderID() (id *FolderId, err error) { } } +// Query events from the given folder in the given timeframe. func (e *EWSCalendar) getCalendarItems(folder *FolderId, start, end time.Time) (items []CalendarItem, err error) { b := &bytes.Buffer{} model := struct {