From 26d0cd400a6ae9d30f39f46c982630aff8b873d4 Mon Sep 17 00:00:00 2001 From: Andreas Schneider Date: Mon, 2 Apr 2018 11:51:57 +0200 Subject: [PATCH] Implemented ICal update --- src/calanonsync/caldav.go | 26 ++++++++++++++ src/calanonsync/caldav_test.go | 62 ++++++++++++++++++++++++++++++++++ 2 files changed, 88 insertions(+) diff --git a/src/calanonsync/caldav.go b/src/calanonsync/caldav.go index 05a835a..43554a3 100644 --- a/src/calanonsync/caldav.go +++ b/src/calanonsync/caldav.go @@ -10,6 +10,7 @@ import ( "log" "net/http" "net/url" + "regexp" "strings" "text/template" "time" @@ -73,6 +74,31 @@ func (i ICal) getTimeField(f string) time.Time { return time.Time{} } +var fieldMatch = regexp.MustCompile(`^(\w+)[;:]`) + +// Update the ical structure with a new start/end time (and the +// timestamp of the last modification). +func (ical *ICal) Update(newStart, newEnd time.Time) { + for i, line := range ical.eventData { + match := fieldMatch.FindStringSubmatch(line) + if match == nil { + continue + } + + // For last modification + now := time.Now().UTC() + + switch match[1] { + case "DTSTART": + ical.eventData[i] = "DTSTART:" + newStart.UTC().Format(ICAL_TIME) + "Z" + case "DTEND": + ical.eventData[i] = "DTEND:" + newEnd.UTC().Format(ICAL_TIME) + "Z" + case "DTSTAMP", "LAST-MODIFIED": + ical.eventData[i] = match[1] + ":" + now.Format(ICAL_TIME) + "Z" + } + } +} + // Retrieve the start time of the event. func (i ICal) Start() time.Time { return i.getTimeField("DTSTART") diff --git a/src/calanonsync/caldav_test.go b/src/calanonsync/caldav_test.go index 4c81197..3ea4be8 100644 --- a/src/calanonsync/caldav_test.go +++ b/src/calanonsync/caldav_test.go @@ -73,6 +73,68 @@ func TestParseICal(t *testing.T) { }) } +func TestICal_Update(t *testing.T) { + newStart := time.Date(2019, 10, 05, 10, 25, 0, 0, time.UTC) + newEnd := time.Date(2019, 10, 05, 10, 55, 30, 0, time.UTC) + + t.Run("Values updated", func(t *testing.T) { + ical, _ := ParseICal(strings.NewReader(icsSimple)) + ical.Update(newStart, newEnd) + + if ical.Start() != newStart { + t.Error("Start not updated correctly") + } + if ical.End() != newEnd { + t.Error("End not updated correctly") + } + + if ical.getTimeField("DTSTAMP").Sub(time.Now()) > 5*time.Second { + t.Error("Timestamp doesn't seem to be updated") + } + }) + t.Run("Other values untouched", func(t *testing.T) { + // Use a ICal structure with dummy/unknown fields but also without + // DTSTAMP or LAST-MODIFIED, so we can do a string comparison against + // known values. + ical, _ := ParseICal(strings.NewReader(`BEGIN:VCALENDAR +PRODID:-//some//thing//EN +VERSION:2.0 +UNKNOWN:FIELD +SOME;MORE:STUFF +BEGIN:VEVENT +UID:WHATEVERID +SUMMARY:Summary +CLASS:PUBLIC +DTSTART:20180308T113000Z +DTEND:20180308T130000Z +CREATED:20180401T182238Z +END:VEVENT +EVEN;MORE:STUFF +END:VCALENDAR`)) + + ical.Update(newStart, newEnd) + + if ical.String() != `BEGIN:VCALENDAR +PRODID:-//some//thing//EN +VERSION:2.0 +UNKNOWN:FIELD +SOME;MORE:STUFF +BEGIN:VEVENT +UID:WHATEVERID +SUMMARY:Summary +CLASS:PUBLIC +DTSTART:20191005T102500Z +DTEND:20191005T105530Z +CREATED:20180401T182238Z +END:VEVENT +EVEN;MORE:STUFF +END:VCALENDAR` { + + t.Error("The resulting ICS seems wrong.") + } + }) +} + const icsWithTZ = `BEGIN:VCALENDAR PRODID:-//Ximian//NONSGML Evolution Calendar//EN VERSION:2.0