Merge branch 'master' of github.com:metallerok/Radicale into recurrence_all_day_comparsion

This commit is contained in:
Georgiy
2026-02-06 19:43:53 +03:00
4 changed files with 78 additions and 0 deletions

View File

@@ -4,6 +4,7 @@
* Fix: MOVE failing with URL-encoded destination header
* Improve: add workaround to remove empty lines in item to avoid reject by vobject parser
* Improve: check/enforce RECURRENCE-ID MUST have the same value type as DTSTART in the recurring component (RFC 5545 3.8.4.4)
## 3.6.0

View File

@@ -656,6 +656,23 @@ def _split_overridden_vevents(
vevent_recurrence = vevent
if vevent_recurrence:
# RFC 5545 3.8.4.4: RECURRENCE-ID MUST have the same value type as DTSTART
# in the recurring component.
base_dtstart = vevent_recurrence.dtstart.value
expected_type = datetime.date
if isinstance(base_dtstart, datetime.datetime):
expected_type = datetime.datetime
for ov in vevents_overridden:
rid = ov.recurrence_id.value
if not isinstance(rid, expected_type):
raise ValueError(
f"component with UID {vevent_recurrence.uid} has "
f"incompatible RECURRENCE-ID value type: base DTSTART is "
f"{type(base_dtstart).__name__}, but override RECURRENCE-ID is "
f"{type(rid).__name__} ({ov.recurrence_id.value!r})"
)
return (
vevent_recurrence, sorted(
vevents_overridden,

View File

@@ -0,0 +1,20 @@
BEGIN:VCALENDAR
VERSION:2.0
BEGIN:VEVENT
DTSTART:20060102T120000Z
DURATION:PT1H
RRULE:FREQ=DAILY;COUNT=3
SUMMARY:Recurring event
UID:event_mixed_recurrence_id_dt_type
END:VEVENT
BEGIN:VEVENT
DTSTART:20060103T140000Z
DURATION:PT1H
RECURRENCE-ID;VALUE=DATE:20060103
SUMMARY:Override with wrong type
UID:event_mixed_recurrence_id_dt_type
END:VEVENT
END:VCALENDAR

View File

@@ -699,6 +699,46 @@ permissions: RrWw""")
assert status == 207
assert len(responses) == 0
def test_report_with_incompatible_recurrence_id_dt_types(self, caplog) -> None:
"""Test report with incompatible RECURRENCE-ID value types (RFC 5545 3.8.4.4)"""
import logging
caplog.set_level(logging.WARNING)
event_id = "event_mixed_recurrence_id_dt_type"
start = "20060103T000000Z"
end = "20060105T000000Z"
self.put("/calendar.ics/", get_file_content(f"{event_id}.ics"))
request = f"""<?xml version="1.0" encoding="utf-8" ?>
<C:calendar-query xmlns:D="DAV:" xmlns:C="urn:ietf:params:xml:ns:caldav">
<D:prop>
<C:calendar-data>
<C:expand start="{start}" end="{end}"/>
</C:calendar-data>
</D:prop>
<C:filter>
<C:comp-filter name="VCALENDAR">
<C:comp-filter name="VEVENT">
<C:time-range start="{start}" end="{end}"/>
</C:comp-filter>
</C:comp-filter>
</C:filter>
</C:calendar-query>
"""
status, _, _ = self.request("REPORT", "/calendar.ics/", request)
assert status == 400
logs = caplog.messages
error_logs = [log for log in logs if "incompatible RECURRENCE-ID value type" in log]
assert len(error_logs) > 0
assert event_id in error_logs[0]
assert "base DTSTART is datetime" in error_logs[0]
assert "override RECURRENCE-ID is date" in error_logs[0]
def test_report_with_expand_property_all_day_event_overridden(self) -> None:
self._test_expand(
"event_full_day_rrule_overridden",