瀏覽代碼

Merge pull request #1971 from pbiering/fix-issue-1970

Support issue 1970
Peter Bieringer 3 周之前
父節點
當前提交
5b5fc8dd39

+ 1 - 0
CHANGELOG.md

@@ -3,6 +3,7 @@
 ## 3.6.1.dev
 
 * Fix: MOVE failing with URL-encoded destination header
+* Improve: add workaround to remove empty lines in item to avoid reject by vobject parser
 
 ## 3.6.0
 

+ 3 - 1
radicale/item/__init__.py

@@ -4,7 +4,7 @@
 # Copyright © 2014 Jean-Marc Martins
 # Copyright © 2008-2017 Guillaume Ayoub
 # Copyright © 2017-2022 Unrud <unrud@outlook.com>
-# Copyright © 2024-2025 Peter Bieringer <pb@bieringer.de>
+# Copyright © 2024-2026 Peter Bieringer <pb@bieringer.de>
 #
 # This library is free software: you can redistribute it and/or modify
 # it under the terms of the GNU General Public License as published by
@@ -56,6 +56,8 @@ def read_components(s: str) -> List[vobject.base.Component]:
     #  * 0x0A Line Feed
     #  * 0x0D Carriage Return
     s = re.sub(r'[\x00-\x08\x0B\x0C\x0E-\x1F]', '', s)
+    # Workaround delete all empty lines to avoid vobject parsing errors
+    s = re.sub(r'(?m)^[ \t]*\r?\n', '', s)
     return list(vobject.readComponents(s, allowQP=True))
 
 

+ 38 - 0
radicale/tests/static/event_issue1970_ok.ics

@@ -0,0 +1,38 @@
+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:event1
+SUMMARY:Event
+CATEGORIES:some_category1,another_category2
+DESCRIPTION:Line1
+ Line2
+ Line3
+ORGANIZER:mailto:unclesam@example.com
+ATTENDEE;ROLE=REQ-PARTICIPANT;PARTSTAT=TENTATIVE;CN=Jane Doe:MAILTO:janedoe@example.com
+ATTENDEE;ROLE=REQ-PARTICIPANT;DELEGATED-FROM="MAILTO:bob@host.com";PARTSTAT=ACCEPTED;CN=John Doe:MAILTO:johndoe@example.com
+DTSTART;TZID=Europe/Paris:20130901T180000
+DTEND;TZID=Europe/Paris:20130901T190000
+END:VEVENT
+END:VCALENDAR

+ 39 - 0
radicale/tests/static/event_issue1970_problem.ics

@@ -0,0 +1,39 @@
+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:event1
+SUMMARY:Event having description with empty line
+CATEGORIES:some_category1,another_category2
+DESCRIPTION:Line1
+ Line2
+ 
+ Line4
+ORGANIZER:mailto:unclesam@example.com
+ATTENDEE;ROLE=REQ-PARTICIPANT;PARTSTAT=TENTATIVE;CN=Jane Doe:MAILTO:janedoe@example.com
+ATTENDEE;ROLE=REQ-PARTICIPANT;DELEGATED-FROM="MAILTO:bob@host.com";PARTSTAT=ACCEPTED;CN=John Doe:MAILTO:johndoe@example.com
+DTSTART;TZID=Europe/Paris:20130901T180000
+DTEND;TZID=Europe/Paris:20130901T190000
+END:VEVENT
+END:VCALENDAR

+ 28 - 0
radicale/tests/test_base.py

@@ -144,6 +144,34 @@ permissions: RrWw""")
         assert "Event" in answer
         assert "UID:event" in answer
 
+    def test_add_event_with_desc_ok(self) -> None:
+        """Add an event."""
+        self.mkcalendar("/calendar.ics/")
+        event = get_file_content("event_issue1970_ok.ics")
+        path = "/calendar.ics/event_issue1970_ok.ics"
+        self.put(path, event)
+        _, headers, answer = self.request("GET", path, check=200)
+        assert "ETag" in headers
+        assert headers["Content-Type"] == "text/calendar; charset=utf-8"
+        assert "DESCRIPTION" in answer
+        assert "VEVENT" in answer
+        assert "Event" in answer
+        assert "UID:event" in answer
+
+    def test_add_event_with_desc_problem(self) -> None:
+        """Add an event."""
+        self.mkcalendar("/calendar.ics/")
+        event = get_file_content("event_issue1970_problem.ics")
+        path = "/calendar.ics/event_issue1970_problem.ics"
+        self.put(path, event)
+        _, headers, answer = self.request("GET", path, check=200)
+        assert "ETag" in headers
+        assert headers["Content-Type"] == "text/calendar; charset=utf-8"
+        assert "DESCRIPTION" in answer
+        assert "VEVENT" in answer
+        assert "Event" in answer
+        assert "UID:event" in answer
+
     def test_add_event_exceed_size(self) -> None:
         """Add an event which is exceeding max-resource-size."""
         self.configure({"server": {"max_resource_size": 20}})