Fix 500 on REPORT for recurring events with Windows timezone IDs

DAVx5 reported a 500 Internal Server Error on a time-range REPORT
against a calendar containing a recurring event whose DTSTART/DTEND used
a Windows-style TZID (e.g. "W. Europe Standard Time", as commonly written
by Outlook/Exchange and some Thunderbird/Lightning setups) instead of an
IANA zone name.

go-ical resolves TZID via a plain time.LoadLocation call, which only
understands IANA names. A simple decode of such an event succeeds (RRULE
dates aren't parsed eagerly), but expanding its recurrence - which
go-webdav's caldav.Filter does for every time-range REPORT - calls
Component.RecurrenceSet, which does call time.LoadLocation(tzid) and
fails with "ical: error parsing start time: unknown time zone ...".

Add internal/icalfix, a small shared helper that rewrites recognized
Windows timezone identifiers (TZID parameters and VTIMEZONE TZID: lines)
to their IANA equivalent in raw ICS bytes before decoding. Wire it into
every ical.NewDecoder call site: internal/caldav/backend.go's
decodeObject (fixes the reported bug), internal/web/calendar.go's event
rendering/ICS import, and internal/icssub's remote feed fetching, so a
subscribed feed with the same issue doesn't hit it either.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
This commit is contained in:
2026-08-20 22:23:11 +02:00
co-authored by Copilot
parent 1707ab4060
commit 213b22f821
5 changed files with 295 additions and 5 deletions
+8 -1
View File
@@ -20,6 +20,7 @@ import (
"github.com/yourusername/caldav-server/internal/auth"
"github.com/yourusername/caldav-server/internal/config"
"github.com/yourusername/caldav-server/internal/db"
"github.com/yourusername/caldav-server/internal/icalfix"
"github.com/yourusername/caldav-server/internal/icssub"
"github.com/yourusername/caldav-server/internal/store"
)
@@ -383,7 +384,13 @@ func (b *Backend) calendarMeta(owner, realName, localName string) caldav.Calenda
}
func (b *Backend) decodeObject(objPath string, data []byte) (*caldav.CalendarObject, error) {
cal, err := ical.NewDecoder(strings.NewReader(string(data))).Decode()
// Some clients (Outlook/Exchange, some Thunderbird/Lightning setups)
// write Windows timezone names into TZID instead of IANA ones, which
// go-ical can't resolve. A recurring event with such a TZID decodes
// fine here but fails later, as a 500, the moment a CalDAV client
// issues a time-range query that expands its recurrence — so fix it
// up before decoding rather than only in QueryCalendarObjects.
cal, err := ical.NewDecoder(bytes.NewReader(icalfix.NormalizeTimeZones(data))).Decode()
if err != nil {
return nil, fmt.Errorf("decoding ical: %w", err)
}