Merge pull request #2153 from pbiering/issue-2151
Improve: sanitize item add timezone to EXDATE or RDATE if missing but DTSTART has
This commit is contained in:
@@ -7,6 +7,7 @@
|
||||
* Add: [sharing] conversion_bday_categories (customize)
|
||||
* Add: [sharing] conversion_bday_age_max (limit in case of "age" placeholder is used which blocks using RRULE)
|
||||
* Extension: [sharing/bday conversion]: add STATUS + CLASS fields
|
||||
* Improve: sanitize item align timezone (add/remove) of EXDATE or RDATE with DTSTART
|
||||
|
||||
## 3.7.4
|
||||
* Fix: sharing: PROPFIND returns now empty owner element in case of a mapped share as clients try PROPFIND on this not accessable href
|
||||
|
||||
@@ -174,12 +174,26 @@ def check_and_sanitize_items(
|
||||
# EXDATE has value DATE even if DTSTART/DTEND is DATE-TIME.
|
||||
# The RFC is vaguely formulated on the issue.
|
||||
# To resolve the issue convert EXDATE and RDATE to
|
||||
# the same type as DTDSTART
|
||||
# the same type as DTSTART
|
||||
if hasattr(component, "dtstart"):
|
||||
ref_date = component.dtstart.value
|
||||
ref_value_param = component.dtstart.params.get("VALUE")
|
||||
for dates in chain(component.contents.get("exdate", []),
|
||||
component.contents.get("rdate", [])):
|
||||
for i, date in enumerate(dates.value):
|
||||
if type(ref_date) is datetime.datetime and type(date) is datetime.datetime:
|
||||
if hasattr(ref_date, 'tzinfo') and ref_date.tzinfo is not None:
|
||||
logger.trace("ITEM/check_and_sanitize_item: dtstart has tzinfo: '%s'", ref_date)
|
||||
if hasattr(date, 'tzinfo') and date.tzinfo is None:
|
||||
# Ensure that datetime.datetime object has timezone set if dtstart has
|
||||
dates.value[i] = dates.value[i].replace(tzinfo=ref_date.tzinfo)
|
||||
logger.trace("ITEM/check_and_sanitize_item: overtake missing tzinfo from dtstart: '%s' -> '%s'", date, dates.value[i])
|
||||
elif (hasattr(ref_date, 'tzinfo') and ref_date.tzinfo is None) or not hasattr(ref_date, 'tzinfo'):
|
||||
logger.trace("ITEM/check_and_sanitize_item: dtstart has no tzinfo: '%s'", ref_date)
|
||||
if hasattr(date, 'tzinfo') and date.tzinfo is not None:
|
||||
# Ensure that datetime.datetime object has no timezone set if dtstart has none
|
||||
dates.value[i] = dates.value[i].replace(tzinfo=None)
|
||||
logger.trace("ITEM/check_and_sanitize_item: remove existing tzinfo (dtstart has none): '%s' -> '%s'", date, dates.value[i])
|
||||
if all(type(d) is type(ref_date) for d in dates.value):
|
||||
continue
|
||||
for i, date in enumerate(dates.value):
|
||||
|
||||
116
radicale/tests/static/event_issue2151.ics
Normal file
116
radicale/tests/static/event_issue2151.ics
Normal file
@@ -0,0 +1,116 @@
|
||||
BEGIN:VCALENDAR
|
||||
PRODID:-//Mozilla.org/NONSGML Mozilla Calendar V1.1//EN
|
||||
VERSION:2.0
|
||||
BEGIN:VTIMEZONE
|
||||
TZID:W. Europe Standard Time
|
||||
BEGIN:STANDARD
|
||||
DTSTART:16010101T030000
|
||||
RRULE:FREQ=YEARLY;INTERVAL=1;BYDAY=-1SU;BYMONTH=10
|
||||
TZOFFSETFROM:+0200
|
||||
TZOFFSETTO:+0100
|
||||
END:STANDARD
|
||||
BEGIN:DAYLIGHT
|
||||
DTSTART:16010101T020000
|
||||
RRULE:FREQ=YEARLY;INTERVAL=1;BYDAY=-1SU;BYMONTH=3
|
||||
TZOFFSETFROM:+0100
|
||||
TZOFFSETTO:+0200
|
||||
END:DAYLIGHT
|
||||
END:VTIMEZONE
|
||||
BEGIN:VEVENT
|
||||
CREATED:20260605T085814Z
|
||||
LAST-MODIFIED:20260605T100722Z
|
||||
DTSTAMP:20260605T100722Z
|
||||
UID:XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
|
||||
SUMMARY:Billings-task-setting follow-up series
|
||||
PRIORITY:5
|
||||
STATUS:CONFIRMED
|
||||
ORGANIZER;CN=XXXXXXXX XXXXX:mailto:XXXXXXX@XXXXXXXXXXXXXX.XXX
|
||||
ATTENDEE;RSVP=TRUE;CN=XX;PARTSTAT=ACCEPTED;ROLE=REQ-PARTICIPANT:mailto:XX@XXXXXXX.XXX
|
||||
ATTENDEE;RSVP=TRUE;CN=XXXXXXXX XXXXXXX;PARTSTAT=NEEDS-ACTION;ROLE=REQ-PARTICIPANT:mailto:XXXXXXXXX@XXXXXXXXXXXXXX.XXX
|
||||
ATTENDEE;RSVP=TRUE;CN=XXXXXX XXXXXXXX;PARTSTAT=NEEDS-ACTION;ROLE=REQ-PARTICIPANT:mailto:XXXXXXXXXX@XXXXXXXXXXXXXX.XXX
|
||||
EXDATE:20260403T110000
|
||||
RRULE:FREQ=WEEKLY;BYDAY=FR;UNTIL=20260731T090000Z
|
||||
X-MOZ-LASTACK:20260605T100722Z
|
||||
DTSTART;TZID=W. Europe Standard Time:20260123T110000
|
||||
DTEND;TZID=W. Europe Standard Time:20260123T120000
|
||||
CLASS:PUBLIC
|
||||
DESCRIPTION;LANGUAGE=de-DE:Serie war letzte Woche ausgelaufen\n\nMicrosoft Teams meeting details redacted.\n
|
||||
LOCATION;LANGUAGE=de-DE:Microsoft Teams-Besprechung
|
||||
SEQUENCE:0
|
||||
TRANSP:OPAQUE
|
||||
X-MICROSOFT-CDO-ALLDAYEVENT:FALSE
|
||||
X-MICROSOFT-CDO-APPT-SEQUENCE:0
|
||||
X-MICROSOFT-CDO-BUSYSTATUS:TENTATIVE
|
||||
X-MICROSOFT-CDO-IMPORTANCE:1
|
||||
X-MICROSOFT-CDO-INSTTYPE:1
|
||||
X-MICROSOFT-CDO-INTENDEDSTATUS:BUSY
|
||||
X-MICROSOFT-CDO-OWNERAPPTID:XXXXXXXXXX
|
||||
X-MICROSOFT-DISALLOW-COUNTER:FALSE
|
||||
X-MICROSOFT-DONOTFORWARDMEETING:FALSE
|
||||
X-MICROSOFT-ISRESPONSEREQUESTED:TRUE
|
||||
X-MICROSOFT-LOCATIONDISPLAYNAME:Microsoft Teams-Besprechung
|
||||
X-MICROSOFT-LOCATIONS:[{"DisplayName":"Microsoft Teams-Besprechung"\,"LocationAnnotation":""\,"LocationUri":""\,"LocationStreet":""\,"LocationCity":""\,"LocationState":""\,"LocationCountry":""\,"LocationPostalCode":""\,"LocationFullAddress":""}]
|
||||
X-MICROSOFT-LOCATIONSOURCE:None
|
||||
X-MICROSOFT-ONLINEMEETINGINFORMATION:{"OnlineMeetingChannelId":null\,"OnlineMeetingProvider":3}
|
||||
X-MICROSOFT-REQUESTEDATTENDANCEMODE:DEFAULT
|
||||
X-MICROSOFT-SCHEDULINGSERVICEUPDATEURL:REDACTED
|
||||
X-MICROSOFT-SKYPETEAMSMEETINGURL:REDACTED
|
||||
X-MICROSOFT-SKYPETEAMSPROPERTIES:REDACTED
|
||||
X-MOZ-GENERATION:17
|
||||
X-MOZ-INVITED-ATTENDEE:mailto:XX@XXXXXXX.XXX
|
||||
X-MOZ-RECEIVED-DTSTAMP:20260123T100333Z
|
||||
X-MOZ-RECEIVED-SEQUENCE:0
|
||||
BEGIN:VALARM
|
||||
ACTION:DISPLAY
|
||||
TRIGGER:-PT5M
|
||||
DESCRIPTION:Mozilla Standardbeschreibung
|
||||
END:VALARM
|
||||
END:VEVENT
|
||||
BEGIN:VEVENT
|
||||
CREATED:20260605T085814Z
|
||||
LAST-MODIFIED:20260130T093740Z
|
||||
DTSTAMP:20260130T093740Z
|
||||
UID:XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
|
||||
SUMMARY:Billings-task-setting follow-up series
|
||||
PRIORITY:5
|
||||
STATUS:CONFIRMED
|
||||
RECURRENCE-ID;TZID=W. Europe Standard Time:20260130T110000
|
||||
ORGANIZER;CN=XXXXXXXX XXXXX:mailto:XXXXXXX@XXXXXXXXXXXXXX.XXX
|
||||
ATTENDEE;RSVP=TRUE;CN=XXXXXXXX XXXXXXX;PARTSTAT=NEEDS-ACTION;ROLE=REQ-PARTICIPANT:mailto:XXXXXXXXX@XXXXXXXXXXXXXX.XXX
|
||||
ATTENDEE;RSVP=TRUE;CN=XXXXXX XXXXXXXX;PARTSTAT=NEEDS-ACTION;ROLE=REQ-PARTICIPANT:mailto:XXXXXXXXXX@XXXXXXXXXXXXXX.XXX
|
||||
ATTENDEE;RSVP=TRUE;CN=XX;PARTSTAT=ACCEPTED;ROLE=REQ-PARTICIPANT:mailto:XX@XXXXXXX.XXX
|
||||
DTSTART;TZID=W. Europe Standard Time:20260130T103000
|
||||
DTEND;TZID=W. Europe Standard Time:20260130T113000
|
||||
CLASS:PUBLIC
|
||||
DESCRIPTION:Serie war letzte Woche ausgelaufen\n\nMicrosoft Teams meeting details redacted.\n
|
||||
LOCATION:Microsoft Teams-Besprechung
|
||||
SEQUENCE:1
|
||||
TRANSP:OPAQUE
|
||||
X-MICROSOFT-CDO-ALLDAYEVENT:FALSE
|
||||
X-MICROSOFT-CDO-APPT-SEQUENCE:1
|
||||
X-MICROSOFT-CDO-BUSYSTATUS:TENTATIVE
|
||||
X-MICROSOFT-CDO-IMPORTANCE:1
|
||||
X-MICROSOFT-CDO-INSTTYPE:3
|
||||
X-MICROSOFT-CDO-INTENDEDSTATUS:BUSY
|
||||
X-MICROSOFT-CDO-OWNERAPPTID:XXXXXXXXXX
|
||||
X-MICROSOFT-DISALLOW-COUNTER:FALSE
|
||||
X-MICROSOFT-DONOTFORWARDMEETING:FALSE
|
||||
X-MICROSOFT-ISRESPONSEREQUESTED:TRUE
|
||||
X-MICROSOFT-LOCATIONDISPLAYNAME:Microsoft Teams-Besprechung
|
||||
X-MICROSOFT-LOCATIONS:[{"DisplayName":"Microsoft Teams-Besprechung"\,"LocationAnnotation":""\,"LocationUri":""\,"LocationStreet":""\,"LocationCity":""\,"LocationState":""\,"LocationCountry":""\,"LocationPostalCode":""\,"LocationFullAddress":""}]
|
||||
X-MICROSOFT-LOCATIONSOURCE:None
|
||||
X-MICROSOFT-ONLINEMEETINGINFORMATION:{"OnlineMeetingChannelId":null\,"OnlineMeetingProvider":3}
|
||||
X-MICROSOFT-REQUESTEDATTENDANCEMODE:DEFAULT
|
||||
X-MICROSOFT-SCHEDULINGSERVICEUPDATEURL:REDACTED
|
||||
X-MICROSOFT-SKYPETEAMSMEETINGURL:REDACTED
|
||||
X-MICROSOFT-SKYPETEAMSPROPERTIES:REDACTED
|
||||
X-MOZ-GENERATION:0
|
||||
X-MOZ-RECEIVED-DTSTAMP:20260130T093205Z
|
||||
X-MOZ-RECEIVED-SEQUENCE:1
|
||||
BEGIN:VALARM
|
||||
ACTION:DISPLAY
|
||||
TRIGGER:-PT5M
|
||||
DESCRIPTION:Mozilla Standardbeschreibung
|
||||
END:VALARM
|
||||
END:VEVENT
|
||||
END:VCALENDAR
|
||||
@@ -0,0 +1,33 @@
|
||||
BEGIN:VCALENDAR
|
||||
PRODID:-//Mozilla.org/NONSGML Mozilla Calendar V1.1//EN
|
||||
VERSION:2.0
|
||||
BEGIN:VTIMEZONE
|
||||
TZID:Europe/Paris
|
||||
X-LIC-LOCATION:Europe/Paris
|
||||
BEGIN:DAYLIGHT
|
||||
TZOFFSETFROM:+0100
|
||||
TZOFFSETTO:+0200
|
||||
TZNAME:CEST
|
||||
DTSTART:19700329T020000
|
||||
RRULE:FREQ=YEARLY;BYDAY=-1SU;BYMONTH=3
|
||||
END:DAYLIGHT
|
||||
BEGIN:STANDARD
|
||||
TZOFFSETFROM:+0200
|
||||
TZOFFSETTO:+0100
|
||||
TZNAME:CET
|
||||
DTSTART:19701025T030000
|
||||
RRULE:FREQ=YEARLY;BYDAY=-1SU;BYMONTH=10
|
||||
END:STANDARD
|
||||
END:VTIMEZONE
|
||||
BEGIN:VEVENT
|
||||
CREATED:20130902T150157Z
|
||||
LAST-MODIFIED:20130902T150158Z
|
||||
DTSTAMP:20130902T150158Z
|
||||
UID:event_mixed_datetime_and_date_exdate
|
||||
SUMMARY:Event
|
||||
DTSTART:20130901T180000
|
||||
DTEND:20130901T190000
|
||||
RRULE:FREQ=DAILY;COUNT=3
|
||||
EXDATE:20130902T193000Z
|
||||
END:VEVENT
|
||||
END:VCALENDAR
|
||||
@@ -273,6 +273,18 @@ permissions: RrWw""")
|
||||
event = get_file_content("event_exdate_without_rrule.ics")
|
||||
self.put("/calendar.ics/event.ics", event)
|
||||
|
||||
def test_add_event_exdate_no_tz(self) -> None:
|
||||
"""Test event where EXDATE has no tzinfo."""
|
||||
self.mkcalendar("/calendar.ics/")
|
||||
event = get_file_content("event_issue2151.ics")
|
||||
self.put("/calendar.ics/event_issue2151.ics", event)
|
||||
|
||||
def test_add_event_dtstart_no_tz_exdate_tz(self) -> None:
|
||||
"""Test event where DTSTART has no tzinfo but EXDATE."""
|
||||
self.mkcalendar("/calendar.ics/")
|
||||
event = get_file_content("event_mixed_datetime_and_date_exdate.ics")
|
||||
self.put("/calendar.ics/event_mixed_datetime_and_date_exdate.ics", event)
|
||||
|
||||
def test_add_todo(self) -> None:
|
||||
"""Add a todo."""
|
||||
self.mkcalendar("/calendar.ics/")
|
||||
|
||||
Reference in New Issue
Block a user