From d1679a53b1d4983d59cbe3ce8552187543e03d3d Mon Sep 17 00:00:00 2001 From: Peter Bieringer Date: Wed, 24 Sep 2025 21:15:20 +0200 Subject: [PATCH 1/5] new test items --- .../tests/static/event_issue1812_getetag.ics | 30 ++ radicale/tests/static/event_issue1880_1.ics | 29 ++ radicale/tests/static/event_issue1880_2.ics | 396 ++++++++++++++++++ 3 files changed, 455 insertions(+) create mode 100644 radicale/tests/static/event_issue1812_getetag.ics create mode 100644 radicale/tests/static/event_issue1880_1.ics create mode 100644 radicale/tests/static/event_issue1880_2.ics diff --git a/radicale/tests/static/event_issue1812_getetag.ics b/radicale/tests/static/event_issue1812_getetag.ics new file mode 100644 index 00000000..8b9936eb --- /dev/null +++ b/radicale/tests/static/event_issue1812_getetag.ics @@ -0,0 +1,30 @@ +BEGIN:VCALENDAR +VERSION:2.0 +PRODID:-//algoo.fr//NONSGML Open Calendar v0.9//EN +BEGIN:VTIMEZONE +TZID:Europe/Paris +LAST-MODIFIED:20250523T094234Z +BEGIN:STANDARD +DTSTART:19701025T030000Z +RRULE:BYDAY=-1SU;BYMONTH=10;FREQ=YEARLY +TZNAME:CET +TZOFFSETFROM:+0200 +TZOFFSETTO:+0100 +END:STANDARD +BEGIN:DAYLIGHT +DTSTART:19700329T020000Z +RRULE:BYDAY=-1SU;BYMONTH=3;FREQ=YEARLY +TZNAME:CEST +TZOFFSETFROM:+0100 +TZOFFSETTO:+0200 +END:DAYLIGHT +END:VTIMEZONE +BEGIN:VEVENT +UID:070a3478-4411-4364-844d-26f3542fc364 +DTSTART;TZID=Europe/Paris;VALUE=DATE:20250716 +DTEND;TZID=Europe/Paris;VALUE=DATE:20250717 +DTSTAMP;VALUE=DATE-TIME:20250723T080354Z +SEQUENCE:1 +SUMMARY:Filtered event +END:VEVENT +END:VCALENDAR diff --git a/radicale/tests/static/event_issue1880_1.ics b/radicale/tests/static/event_issue1880_1.ics new file mode 100644 index 00000000..74484364 --- /dev/null +++ b/radicale/tests/static/event_issue1880_1.ics @@ -0,0 +1,29 @@ +BEGIN:VCALENDAR +VERSION:2.0 +PRODID:-//algoo.fr//NONSGML Open Calendar v0.9//EN +BEGIN:VTIMEZONE +TZID:Europe/Paris +LAST-MODIFIED:20250523T094234Z +BEGIN:STANDARD +DTSTART:19701025T030000Z +RRULE:BYDAY=-1SU;BYMONTH=10;FREQ=YEARLY +TZNAME:CET +TZOFFSETFROM:+0200 +TZOFFSETTO:+0100 +END:STANDARD +BEGIN:DAYLIGHT +DTSTART:19700329T020000Z +RRULE:BYDAY=-1SU;BYMONTH=3;FREQ=YEARLY +TZNAME:CEST +TZOFFSETFROM:+0100 +TZOFFSETTO:+0200 +END:DAYLIGHT +END:VTIMEZONE +BEGIN:VEVENT +UID:f5b69821-addc-4010-9ab8-891df1c33c01 +DTSTART;TZID=Europe/Paris;VALUE=DATE-TIME:20250925T093000 +DTEND;TZID=Europe/Paris;VALUE=DATE-TIME:20250925T140000 +DTSTAMP:20250923T114003Z +SUMMARY:event from opencalendar +END:VEVENT +END:VCALENDAR diff --git a/radicale/tests/static/event_issue1880_2.ics b/radicale/tests/static/event_issue1880_2.ics new file mode 100644 index 00000000..791f3d2e --- /dev/null +++ b/radicale/tests/static/event_issue1880_2.ics @@ -0,0 +1,396 @@ +BEGIN:VCALENDAR +VERSION:2.0 +PRODID:-//Mozilla.org/NONSGML Mozilla Calendar V1.1//EN +BEGIN:VTIMEZONE +TZID:Europe/Paris +BEGIN:STANDARD +DTSTART:19110311T000000 +RDATE:19110311T000000 +TZNAME:Europe/Paris(STD) +TZOFFSETFROM:+000921 +TZOFFSETTO:+000000 +END:STANDARD +BEGIN:STANDARD +DTSTART:19161002T000000 +RDATE:19161002T000000 +TZNAME:Europe/Paris(STD) +TZOFFSETFROM:+010000 +TZOFFSETTO:+000000 +END:STANDARD +BEGIN:STANDARD +DTSTART:19171008T000000 +RDATE:19171008T000000 +TZNAME:Europe/Paris(STD) +TZOFFSETFROM:+010000 +TZOFFSETTO:+000000 +END:STANDARD +BEGIN:STANDARD +DTSTART:19181007T000000 +RRULE:FREQ=YEARLY;BYMONTH=10;BYDAY=1MO;UNTIL=19191006T000000 +TZNAME:Europe/Paris(STD) +TZOFFSETFROM:+010000 +TZOFFSETTO:+000000 +END:STANDARD +BEGIN:STANDARD +DTSTART:19201024T000000 +RDATE:19201024T000000 +TZNAME:Europe/Paris(STD) +TZOFFSETFROM:+010000 +TZOFFSETTO:+000000 +END:STANDARD +BEGIN:STANDARD +DTSTART:19211026T000000 +RDATE:19211026T000000 +TZNAME:Europe/Paris(STD) +TZOFFSETFROM:+010000 +TZOFFSETTO:+000000 +END:STANDARD +BEGIN:STANDARD +DTSTART:19221008T000000 +RDATE:19221008T000000 +TZNAME:Europe/Paris(STD) +TZOFFSETFROM:+010000 +TZOFFSETTO:+000000 +END:STANDARD +BEGIN:STANDARD +DTSTART:19231007T000000 +RRULE:FREQ=YEARLY;BYMONTH=10;BYDAY=1SU;UNTIL=19321002T000000 +TZNAME:Europe/Paris(STD) +TZOFFSETFROM:+010000 +TZOFFSETTO:+000000 +END:STANDARD +BEGIN:STANDARD +DTSTART:19331008T000000 +RDATE:19331008T000000 +TZNAME:Europe/Paris(STD) +TZOFFSETFROM:+010000 +TZOFFSETTO:+000000 +END:STANDARD +BEGIN:STANDARD +DTSTART:19341007T000000 +RRULE:FREQ=YEARLY;BYMONTH=10;BYDAY=1SU;UNTIL=19381002T000000 +TZNAME:Europe/Paris(STD) +TZOFFSETFROM:+010000 +TZOFFSETTO:+000000 +END:STANDARD +BEGIN:STANDARD +DTSTART:19391119T000000 +RDATE:19391119T000000 +TZNAME:Europe/Paris(STD) +TZOFFSETFROM:+010000 +TZOFFSETTO:+000000 +END:STANDARD +BEGIN:STANDARD +DTSTART:19421102T030000 +RDATE:19421102T030000 +TZNAME:Europe/Paris(STD) +TZOFFSETFROM:+020000 +TZOFFSETTO:+010000 +END:STANDARD +BEGIN:STANDARD +DTSTART:19431004T030000 +RDATE:19431004T030000 +TZNAME:Europe/Paris(STD) +TZOFFSETFROM:+020000 +TZOFFSETTO:+010000 +END:STANDARD +BEGIN:STANDARD +DTSTART:19450916T030000 +RDATE:19450916T030000 +TZNAME:Europe/Paris(STD) +TZOFFSETFROM:+020000 +TZOFFSETTO:+010000 +END:STANDARD +BEGIN:STANDARD +DTSTART:19760926T010000 +RDATE:19760926T010000 +TZNAME:Europe/Paris(STD) +TZOFFSETFROM:+020000 +TZOFFSETTO:+010000 +END:STANDARD +BEGIN:STANDARD +DTSTART:19770925T030000 +RDATE:19770925T030000 +TZNAME:Europe/Paris(STD) +TZOFFSETFROM:+020000 +TZOFFSETTO:+010000 +END:STANDARD +BEGIN:STANDARD +DTSTART:19781001T030000 +RDATE:19781001T030000 +TZNAME:Europe/Paris(STD) +TZOFFSETFROM:+020000 +TZOFFSETTO:+010000 +END:STANDARD +BEGIN:STANDARD +DTSTART:19790930T030000 +RRULE:FREQ=YEARLY;BYMONTH=9;BYDAY=-1SU;UNTIL=19950924T030000 +TZNAME:Europe/Paris(STD) +TZOFFSETFROM:+020000 +TZOFFSETTO:+010000 +END:STANDARD +BEGIN:STANDARD +DTSTART:19961027T030000 +RDATE:19961027T030000 +TZNAME:Europe/Paris(STD) +TZOFFSETFROM:+020000 +TZOFFSETTO:+010000 +END:STANDARD +BEGIN:STANDARD +DTSTART:19971026T030000 +RRULE:FREQ=YEARLY;BYMONTH=10;BYDAY=-1SU +TZNAME:(STD) +TZOFFSETFROM:+020000 +TZOFFSETTO:+010000 +END:STANDARD +BEGIN:DAYLIGHT +DTSTART:19160614T230000 +RDATE:19160614T230000 +TZNAME:Europe/Paris(DST) +TZOFFSETFROM:+000000 +TZOFFSETTO:+010000 +END:DAYLIGHT +BEGIN:DAYLIGHT +DTSTART:19170324T230000 +RDATE:19170324T230000 +TZNAME:Europe/Paris(DST) +TZOFFSETFROM:+000000 +TZOFFSETTO:+010000 +END:DAYLIGHT +BEGIN:DAYLIGHT +DTSTART:19180309T230000 +RDATE:19180309T230000 +TZNAME:Europe/Paris(DST) +TZOFFSETFROM:+000000 +TZOFFSETTO:+010000 +END:DAYLIGHT +BEGIN:DAYLIGHT +DTSTART:19190301T230000 +RDATE:19190301T230000 +TZNAME:Europe/Paris(DST) +TZOFFSETFROM:+000000 +TZOFFSETTO:+010000 +END:DAYLIGHT +BEGIN:DAYLIGHT +DTSTART:19200214T230000 +RDATE:19200214T230000 +TZNAME:Europe/Paris(DST) +TZOFFSETFROM:+000000 +TZOFFSETTO:+010000 +END:DAYLIGHT +BEGIN:DAYLIGHT +DTSTART:19210314T230000 +RDATE:19210314T230000 +TZNAME:Europe/Paris(DST) +TZOFFSETFROM:+000000 +TZOFFSETTO:+010000 +END:DAYLIGHT +BEGIN:DAYLIGHT +DTSTART:19220325T230000 +RDATE:19220325T230000 +TZNAME:Europe/Paris(DST) +TZOFFSETFROM:+000000 +TZOFFSETTO:+010000 +END:DAYLIGHT +BEGIN:DAYLIGHT +DTSTART:19230526T230000 +RDATE:19230526T230000 +TZNAME:Europe/Paris(DST) +TZOFFSETFROM:+000000 +TZOFFSETTO:+010000 +END:DAYLIGHT +BEGIN:DAYLIGHT +DTSTART:19240329T230000 +RDATE:19240329T230000 +TZNAME:Europe/Paris(DST) +TZOFFSETFROM:+000000 +TZOFFSETTO:+010000 +END:DAYLIGHT +BEGIN:DAYLIGHT +DTSTART:19250404T230000 +RDATE:19250404T230000 +TZNAME:Europe/Paris(DST) +TZOFFSETFROM:+000000 +TZOFFSETTO:+010000 +END:DAYLIGHT +BEGIN:DAYLIGHT +DTSTART:19260417T230000 +RDATE:19260417T230000 +TZNAME:Europe/Paris(DST) +TZOFFSETFROM:+000000 +TZOFFSETTO:+010000 +END:DAYLIGHT +BEGIN:DAYLIGHT +DTSTART:19270409T230000 +RRULE:FREQ=YEARLY;BYMONTH=4;BYDAY=2SA;UNTIL=19280414T230000 +TZNAME:Europe/Paris(DST) +TZOFFSETFROM:+000000 +TZOFFSETTO:+010000 +END:DAYLIGHT +BEGIN:DAYLIGHT +DTSTART:19290420T230000 +RDATE:19290420T230000 +TZNAME:Europe/Paris(DST) +TZOFFSETFROM:+000000 +TZOFFSETTO:+010000 +END:DAYLIGHT +BEGIN:DAYLIGHT +DTSTART:19300412T230000 +RDATE:19300412T230000 +TZNAME:Europe/Paris(DST) +TZOFFSETFROM:+000000 +TZOFFSETTO:+010000 +END:DAYLIGHT +BEGIN:DAYLIGHT +DTSTART:19310418T230000 +RDATE:19310418T230000 +TZNAME:Europe/Paris(DST) +TZOFFSETFROM:+000000 +TZOFFSETTO:+010000 +END:DAYLIGHT +BEGIN:DAYLIGHT +DTSTART:19320402T230000 +RDATE:19320402T230000 +TZNAME:Europe/Paris(DST) +TZOFFSETFROM:+000000 +TZOFFSETTO:+010000 +END:DAYLIGHT +BEGIN:DAYLIGHT +DTSTART:19330325T230000 +RDATE:19330325T230000 +TZNAME:Europe/Paris(DST) +TZOFFSETFROM:+000000 +TZOFFSETTO:+010000 +END:DAYLIGHT +BEGIN:DAYLIGHT +DTSTART:19340407T230000 +RDATE:19340407T230000 +TZNAME:Europe/Paris(DST) +TZOFFSETFROM:+000000 +TZOFFSETTO:+010000 +END:DAYLIGHT +BEGIN:DAYLIGHT +DTSTART:19350330T230000 +RDATE:19350330T230000 +TZNAME:Europe/Paris(DST) +TZOFFSETFROM:+000000 +TZOFFSETTO:+010000 +END:DAYLIGHT +BEGIN:DAYLIGHT +DTSTART:19360418T230000 +RDATE:19360418T230000 +TZNAME:Europe/Paris(DST) +TZOFFSETFROM:+000000 +TZOFFSETTO:+010000 +END:DAYLIGHT +BEGIN:DAYLIGHT +DTSTART:19370403T230000 +RDATE:19370403T230000 +TZNAME:Europe/Paris(DST) +TZOFFSETFROM:+000000 +TZOFFSETTO:+010000 +END:DAYLIGHT +BEGIN:DAYLIGHT +DTSTART:19380326T230000 +RDATE:19380326T230000 +TZNAME:Europe/Paris(DST) +TZOFFSETFROM:+000000 +TZOFFSETTO:+010000 +END:DAYLIGHT +BEGIN:DAYLIGHT +DTSTART:19390415T230000 +RDATE:19390415T230000 +TZNAME:Europe/Paris(DST) +TZOFFSETFROM:+000000 +TZOFFSETTO:+010000 +END:DAYLIGHT +BEGIN:DAYLIGHT +DTSTART:19400225T020000 +RDATE:19400225T020000 +TZNAME:Europe/Paris(DST) +TZOFFSETFROM:+000000 +TZOFFSETTO:+010000 +END:DAYLIGHT +BEGIN:DAYLIGHT +DTSTART:19400614T230000 +RDATE:19400614T230000 +TZNAME:Europe/Paris(DST) +TZOFFSETFROM:+010000 +TZOFFSETTO:+020000 +END:DAYLIGHT +BEGIN:DAYLIGHT +DTSTART:19430329T020000 +RDATE:19430329T020000 +TZNAME:Europe/Paris(DST) +TZOFFSETFROM:+010000 +TZOFFSETTO:+020000 +END:DAYLIGHT +BEGIN:DAYLIGHT +DTSTART:19440403T020000 +RDATE:19440403T020000 +TZNAME:Europe/Paris(DST) +TZOFFSETFROM:+010000 +TZOFFSETTO:+020000 +END:DAYLIGHT +BEGIN:DAYLIGHT +DTSTART:19440825T000000 +RDATE:19440825T000000 +TZNAME:Europe/Paris(DST) +TZOFFSETFROM:+020000 +TZOFFSETTO:+020000 +END:DAYLIGHT +BEGIN:DAYLIGHT +DTSTART:19441008T010000 +RDATE:19441008T010000 +TZNAME:Europe/Paris(DST) +TZOFFSETFROM:+020000 +TZOFFSETTO:+010000 +END:DAYLIGHT +BEGIN:DAYLIGHT +DTSTART:19450402T020000 +RDATE:19450402T020000 +TZNAME:Europe/Paris(DST) +TZOFFSETFROM:+010000 +TZOFFSETTO:+020000 +END:DAYLIGHT +BEGIN:DAYLIGHT +DTSTART:19760328T010000 +RDATE:19760328T010000 +TZNAME:Europe/Paris(DST) +TZOFFSETFROM:+010000 +TZOFFSETTO:+020000 +END:DAYLIGHT +BEGIN:DAYLIGHT +DTSTART:19770403T020000 +RRULE:FREQ=YEARLY;BYMONTH=4;BYDAY=1SU;UNTIL=19800406T020000 +TZNAME:Europe/Paris(DST) +TZOFFSETFROM:+010000 +TZOFFSETTO:+020000 +END:DAYLIGHT +BEGIN:DAYLIGHT +DTSTART:19810329T020000 +RRULE:FREQ=YEARLY;BYMONTH=3;BYDAY=-1SU;UNTIL=19960331T020000 +TZNAME:Europe/Paris(DST) +TZOFFSETFROM:+010000 +TZOFFSETTO:+020000 +END:DAYLIGHT +BEGIN:DAYLIGHT +DTSTART:19970330T020000 +RRULE:FREQ=YEARLY;BYMONTH=3;BYDAY=-1SU +TZNAME:(DST) +TZOFFSETFROM:+010000 +TZOFFSETTO:+020000 +END:DAYLIGHT +X-TZINFO:Europe/Paris[2024a] +END:VTIMEZONE +BEGIN:VEVENT +UID:50c08af4-295c-4bea-9ea4-7402b8e82143 +DTSTART;TZID=Europe/Paris:20250924T133000 +DTEND;TZID=Europe/Paris:20250924T143000 +CREATED:20250923T113902Z +DTSTAMP:20250923T113912Z +LAST-MODIFIED:20250923T113912Z +SUMMARY:event from thunderbird +TRANSP:OPAQUE +END:VEVENT +END:VCALENDAR From ec9ef124ff880a3939c6e935fb068949e3b55652 Mon Sep 17 00:00:00 2001 From: Peter Bieringer Date: Wed, 24 Sep 2025 21:17:37 +0200 Subject: [PATCH 2/5] add new test cases for #1880 and #1812 --- radicale/tests/test_expand.py | 182 ++++++++++++++++++++++++++++++++++ 1 file changed, 182 insertions(+) diff --git a/radicale/tests/test_expand.py b/radicale/tests/test_expand.py index 2cc4a49f..8eab1ced 100644 --- a/radicale/tests/test_expand.py +++ b/radicale/tests/test_expand.py @@ -512,3 +512,185 @@ permissions: RrWw""") status, event2_calendar_data = responses["/test/event2.ics"]["C:calendar-data"] assert event2_calendar_data.text assert "UID:c6be8b2c-3d72-453c-b698-4f25cdf1569e" in event2_calendar_data.text + + def test_report_getetag_expand_filter(self) -> None: + """Test getetag with time-range filter and expand (example from #1880).""" + self.mkcalendar("/test/") + self.put("/test/event_issue1880_1.ics", get_file_content("event_issue1880_1.ics")) + self.put("/test/event_issue1880_2.ics", get_file_content("event_issue1880_2.ics")) + + request = """ + + + + + + + + + + + + + + + """ + status, responses = self.report("/test", request) + assert status == 207 + assert len(responses) == 2 + assert "D:getetag" in responses["/test/event_issue1880_1.ics"] + assert "D:getetag" in responses["/test/event_issue1880_2.ics"] + + def test_report_getetag_expand_filter_positive1(self) -> None: + """Test getetag with time-range filter and expand (not applicable), should return as matching filter range (example from #1812).""" + self.mkcalendar("/test/") + self.put("/test/event_issue1812_getetag.ics", get_file_content("event_issue1812_getetag.ics")) + + request = """ + + + + + + + + + + + + + + + """ + status, responses = self.report("/test", request) + assert status == 207 + assert len(responses) == 1 + assert "D:getetag" in responses["/test/event_issue1812_getetag.ics"] + + def test_report_getetag_expand_filter_positive2(self) -> None: + """Test getetag with time-range filter and expand, should return as matching filter range (example from #1812).""" + self.mkcalendar("/test/") + self.put("/test/event_issue1812.ics", get_file_content("event_issue1812.ics")) + + request = """ + + + + + + + + + + + + + + + """ + status, responses = self.report("/test", request) + assert status == 207 + assert len(responses) == 1 + assert "D:getetag" in responses["/test/event_issue1812.ics"] + + def test_report_getetag_expand_filter_negative1(self) -> None: + """Test getetag with time-range filter and expand, should not return anything (example from #1812).""" + self.mkcalendar("/test/") + self.put("/test/event_issue1812_getetag.ics", get_file_content("event_issue1812_getetag.ics")) + + request = """ + + + + + + + + + + + + + + + """ + status, responses = self.report("/test", request) + assert status == 207 + assert len(responses) == 0 + + def test_report_getetag_expand_filter_negative2(self) -> None: + """Test getetag with time-range filter and expand, should not return anything (example from #1812).""" + self.mkcalendar("/test/") + self.put("/test/event_issue1812_getetag.ics", get_file_content("event_issue1812_getetag.ics")) + + request = """ + + + + + + + + + + + + + + + + """ + status, responses = self.report("/test", request) + assert status == 207 + assert len(responses) == 0 + + + def test_report_getetag_expand_filter_negative3(self) -> None: + """Test getetag with time-range filter and expand, should not return anything (example from #1812).""" + self.mkcalendar("/test/") + self.put("/test/event_issue1812_getetag.ics", get_file_content("event_issue1812_getetag.ics")) + + request = """ + + + + + + + + + + + + + + + """ + status, responses = self.report("/test", request) + assert status == 207 + assert len(responses) == 0 + + def test_report_getetag_expand_filter_negative4(self) -> None: + """Test getetag with time-range filter and expand, nothing returned as filter is not matching (example from #1812).""" + self.mkcalendar("/test/") + self.put("/test/event_issue1812.ics", get_file_content("event_issue1812.ics")) + + request = """ + + + + + + + + + + + + + + + """ + status, responses = self.report("/test", request) + assert status == 207 + assert len(responses) == 0 From 77e7745f9319b6b00a80f1d89c502d59c7ad0f0f Mon Sep 17 00:00:00 2001 From: Peter Bieringer Date: Wed, 24 Sep 2025 21:30:06 +0200 Subject: [PATCH 3/5] make mypy happy --- radicale/tests/test_expand.py | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/radicale/tests/test_expand.py b/radicale/tests/test_expand.py index 8eab1ced..28d650cf 100644 --- a/radicale/tests/test_expand.py +++ b/radicale/tests/test_expand.py @@ -538,6 +538,8 @@ permissions: RrWw""") status, responses = self.report("/test", request) assert status == 207 assert len(responses) == 2 + assert isinstance(responses["/test/event_issue1880_1.ics"], dict) + assert isinstance(responses["/test/event_issue1880_2.ics"], dict) assert "D:getetag" in responses["/test/event_issue1880_1.ics"] assert "D:getetag" in responses["/test/event_issue1880_2.ics"] @@ -565,6 +567,7 @@ permissions: RrWw""") status, responses = self.report("/test", request) assert status == 207 assert len(responses) == 1 + assert isinstance(responses["/test/event_issue1812_getetag.ics"], dict) assert "D:getetag" in responses["/test/event_issue1812_getetag.ics"] def test_report_getetag_expand_filter_positive2(self) -> None: @@ -591,6 +594,7 @@ permissions: RrWw""") status, responses = self.report("/test", request) assert status == 207 assert len(responses) == 1 + assert isinstance(responses["/test/event_issue1812.ics"], dict) assert "D:getetag" in responses["/test/event_issue1812.ics"] def test_report_getetag_expand_filter_negative1(self) -> None: From 2899c677c1e62a892f8978da4ca9a7614dc694ff Mon Sep 17 00:00:00 2001 From: Peter Bieringer Date: Wed, 24 Sep 2025 21:31:27 +0200 Subject: [PATCH 4/5] revert improper PR#1839, finally fix #1812 and #1880 --- radicale/app/report.py | 42 ++++++++++++++++++++++++------------------ 1 file changed, 24 insertions(+), 18 deletions(-) diff --git a/radicale/app/report.py b/radicale/app/report.py index 555154c3..b63681f7 100644 --- a/radicale/app/report.py +++ b/radicale/app/report.py @@ -156,6 +156,7 @@ def xml_report(base_prefix: str, path: str, xml_request: Optional[ET.Element], Read rfc3253-3.6 for info. """ + logger.debug("TRACE/REPORT/xml_report: base_prefix=%r path=%r", base_prefix, path) multistatus = ET.Element(xmlutils.make_clark("D:multistatus")) if xml_request is None: return client.MULTI_STATUS, multistatus @@ -239,6 +240,7 @@ def xml_report(base_prefix: str, path: str, xml_request: Optional[ET.Element], filter_copy = copy.deepcopy(filter_) if expand is not None: + logger.debug("TRACE/REPORT/xml_report: expand") for comp_filter in filter_copy.findall(".//" + xmlutils.make_clark("C:comp-filter")): if comp_filter.get("name", "").upper() == "VCALENDAR": continue @@ -275,21 +277,15 @@ def xml_report(base_prefix: str, path: str, xml_request: Optional[ET.Element], found_props = [] not_found_props = [] - item_etag: str = "" for prop in props: element = ET.Element(prop.tag) - if prop.tag == xmlutils.make_clark("D:getetag"): - if expand is not None: - item_etag = item.etag - else: - element.text = item.etag - found_props.append(element) - elif prop.tag == xmlutils.make_clark("D:getcontenttype"): + if prop.tag == xmlutils.make_clark("D:getcontenttype"): element.text = xmlutils.get_content_type(item, encoding) found_props.append(element) elif prop.tag in ( xmlutils.make_clark("C:calendar-data"), + xmlutils.make_clark("D:getetag"), xmlutils.make_clark("CR:address-data")): element.text = item.serialize() @@ -326,11 +322,24 @@ def xml_report(base_prefix: str, path: str, xml_request: Optional[ET.Element], continue n_vevents += n_vev - found_props.append(expanded_element) + if prop.tag == xmlutils.make_clark("D:getetag"): + if n_vev > 0: + logger.debug("TRACE/REPORT/xml_report: getetag/expanded element") + element.text = item.etag + found_props.append(element) + else: + logger.debug("TRACE/REPORT/xml_report: getetag/no expanded element") + else: + logger.debug("TRACE/REPORT/xml_report: default") + found_props.append(expanded_element) else: - found_props.append(element) - if hasattr(item.vobject_item, "vevent_list"): - n_vevents += len(item.vobject_item.vevent_list) + if prop.tag == xmlutils.make_clark("D:getetag"): + element.text = item.etag + found_props.append(element) + else: + found_props.append(element) + if hasattr(item.vobject_item, "vevent_list"): + n_vevents += len(item.vobject_item.vevent_list) # Avoid DoS with too many events if max_occurrence and n_vevents > max_occurrence: raise ValueError("REPORT occurrences limit of {} hit" @@ -345,7 +354,7 @@ def xml_report(base_prefix: str, path: str, xml_request: Optional[ET.Element], if found_props or not_found_props: multistatus.append(xml_item_response( base_prefix, uri, found_props=found_props, - not_found_props=not_found_props, found_item=True, item_etag=item_etag)) + not_found_props=not_found_props, found_item=True)) return client.MULTI_STATUS, multistatus @@ -664,7 +673,7 @@ def _find_overridden( def xml_item_response(base_prefix: str, href: str, found_props: Sequence[ET.Element] = (), not_found_props: Sequence[ET.Element] = (), - found_item: bool = True, item_etag: str = "") -> ET.Element: + found_item: bool = True) -> ET.Element: response = ET.Element(xmlutils.make_clark("D:response")) href_element = ET.Element(xmlutils.make_clark("D:href")) @@ -678,10 +687,6 @@ def xml_item_response(base_prefix: str, href: str, status = ET.Element(xmlutils.make_clark("D:status")) status.text = xmlutils.make_response(code) prop_element = ET.Element(xmlutils.make_clark("D:prop")) - if (item_etag != "") and (code == 200): - prop_etag = ET.Element(xmlutils.make_clark("D:getetag")) - prop_etag.text = item_etag - prop_element.append(prop_etag) for prop in props: prop_element.append(prop) propstat.append(prop_element) @@ -735,6 +740,7 @@ def retrieve_items( else: yield item, False if collection_requested: + logger.debug("TRACE/REPORT/retrieve_items: get_filtered") yield from collection.get_filtered(filters) From 7604d447011da01f2c9e94208abc14112e5c6384 Mon Sep 17 00:00:00 2001 From: Peter Bieringer Date: Wed, 24 Sep 2025 21:36:33 +0200 Subject: [PATCH 5/5] make flake8 happy --- radicale/tests/test_expand.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/radicale/tests/test_expand.py b/radicale/tests/test_expand.py index 28d650cf..d70af4b5 100644 --- a/radicale/tests/test_expand.py +++ b/radicale/tests/test_expand.py @@ -523,7 +523,7 @@ permissions: RrWw""") - + @@ -648,7 +648,6 @@ permissions: RrWw""") assert status == 207 assert len(responses) == 0 - def test_report_getetag_expand_filter_negative3(self) -> None: """Test getetag with time-range filter and expand, should not return anything (example from #1812).""" self.mkcalendar("/test/")