From f2c0d2376701db752039491697921220dad477fb Mon Sep 17 00:00:00 2001 From: Nicco Kunzmann Date: Mon, 3 Oct 2022 18:51:56 +0100 Subject: [PATCH 1/6] add tests for issue #355 see https://github.com/collective/icalendar/pull/356 see https://github.com/collective/icalendar/issues/355 --- src/icalendar/tests/events/issue_355_url_escaping.ics | 8 ++++++++ src/icalendar/tests/test_issue_355_url_escaping.py | 11 +++++++++++ 2 files changed, 19 insertions(+) create mode 100644 src/icalendar/tests/events/issue_355_url_escaping.ics create mode 100644 src/icalendar/tests/test_issue_355_url_escaping.py diff --git a/src/icalendar/tests/events/issue_355_url_escaping.ics b/src/icalendar/tests/events/issue_355_url_escaping.ics new file mode 100644 index 00000000..53b8ad3c --- /dev/null +++ b/src/icalendar/tests/events/issue_355_url_escaping.ics @@ -0,0 +1,8 @@ +BEGIN:VEVENT +DTSTAMP:20220822T164528Z +UID:1661186704812-62650@ical.marudot.com +DTSTART;TZID=Europe/Berlin:20220822T120000 +DTEND;TZID=Europe/Berlin:20220822T120000 +SUMMARY:test +DESCRIPTION:https://www.facebook.com/events/756119502186737/?acontext=%7B%22source%22%3A5%2C%22action_history%22%3A[%7B%22surface%22%3A%22page%22%2C%22mechanism%22%3A%22main_list%22%2C%22extra_data%22%3A%22%5C%22[]%5C%22%22%7D]%2C%22has_source%22%3Atrue%7D +END:VEVENT \ No newline at end of file diff --git a/src/icalendar/tests/test_issue_355_url_escaping.py b/src/icalendar/tests/test_issue_355_url_escaping.py new file mode 100644 index 00000000..42734ad9 --- /dev/null +++ b/src/icalendar/tests/test_issue_355_url_escaping.py @@ -0,0 +1,11 @@ +"""Tests for Issue 355. + +see https://github.com/collective/icalendar/issues/355 +""" + +def test_facebook_link_is_correctly_parsed(events): + """The facebook link must not be damaged. + + see https://github.com/collective/icalendar/pull/356#issuecomment-1222626128 + """ + events.issue_355_url_escaping["DESCRIPTION"] == "https://www.facebook.com/events/756119502186737/?acontext=%7B%22source%22%3A5%2C%22action_history%22%3A[%7B%22surface%22%3A%22page%22%2C%22mechanism%22%3A%22main_list%22%2C%22extra_data%22%3A%22%5C%22[]%5C%22%22%7D]%2C%22has_source%22%3Atrue%7D" From 334b019eff17ba41cc25b7f62ef5c780cdd79e94 Mon Sep 17 00:00:00 2001 From: Nicco Kunzmann Date: Mon, 3 Oct 2022 20:40:59 +0100 Subject: [PATCH 2/6] add other test case mentioned see https://github.com/collective/icalendar/pull/356#issuecomment-1265872696 --- src/icalendar/tests/events/issue_355_url_escaping_2.ics | 8 ++++++++ src/icalendar/tests/test_issue_355_url_escaping.py | 8 ++++++++ 2 files changed, 16 insertions(+) create mode 100644 src/icalendar/tests/events/issue_355_url_escaping_2.ics diff --git a/src/icalendar/tests/events/issue_355_url_escaping_2.ics b/src/icalendar/tests/events/issue_355_url_escaping_2.ics new file mode 100644 index 00000000..53b8ad3c --- /dev/null +++ b/src/icalendar/tests/events/issue_355_url_escaping_2.ics @@ -0,0 +1,8 @@ +BEGIN:VEVENT +DTSTAMP:20220822T164528Z +UID:1661186704812-62650@ical.marudot.com +DTSTART;TZID=Europe/Berlin:20220822T120000 +DTEND;TZID=Europe/Berlin:20220822T120000 +SUMMARY:test +DESCRIPTION:https://www.facebook.com/events/756119502186737/?acontext=%7B%22source%22%3A5%2C%22action_history%22%3A[%7B%22surface%22%3A%22page%22%2C%22mechanism%22%3A%22main_list%22%2C%22extra_data%22%3A%22%5C%22[]%5C%22%22%7D]%2C%22has_source%22%3Atrue%7D +END:VEVENT \ No newline at end of file diff --git a/src/icalendar/tests/test_issue_355_url_escaping.py b/src/icalendar/tests/test_issue_355_url_escaping.py index 42734ad9..c4fc61b6 100644 --- a/src/icalendar/tests/test_issue_355_url_escaping.py +++ b/src/icalendar/tests/test_issue_355_url_escaping.py @@ -9,3 +9,11 @@ def test_facebook_link_is_correctly_parsed(events): see https://github.com/collective/icalendar/pull/356#issuecomment-1222626128 """ events.issue_355_url_escaping["DESCRIPTION"] == "https://www.facebook.com/events/756119502186737/?acontext=%7B%22source%22%3A5%2C%22action_history%22%3A[%7B%22surface%22%3A%22page%22%2C%22mechanism%22%3A%22main_list%22%2C%22extra_data%22%3A%22%5C%22[]%5C%22%22%7D]%2C%22has_source%22%3Atrue%7D" + +def test_other_facebook_link_is_correctly_parsed(events): + """The facebook link must not be damaged. + + see https://github.com/collective/icalendar/pull/356#issuecomment-1265872696 + """ + expected_result = 'https://www.facebook.com/events/756119502186737/?acontext=%7B%22source%22%3A5%2C%22action_history%22%3A[%7B%22surface%22%3A%22page%22%2C%22mechanism%22%3A%22main_list%22%2C%22extra_data%22%3A%22%5C%22[]%5C%22%22%7D]%2C%22has_source%22%3Atrue%7D' + events.issue_355_url_escaping_2["DESCRIPTION"] == expected_result From 4a634cf48b9991fc5a304960402756e64bfef8ac Mon Sep 17 00:00:00 2001 From: Nicco Kunzmann Date: Mon, 3 Oct 2022 20:46:54 +0100 Subject: [PATCH 3/6] add assert statements see https://github.com/collective/icalendar/pull/426#discussion_r986107808 --- src/icalendar/tests/test_issue_355_url_escaping.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/icalendar/tests/test_issue_355_url_escaping.py b/src/icalendar/tests/test_issue_355_url_escaping.py index c4fc61b6..8c073f14 100644 --- a/src/icalendar/tests/test_issue_355_url_escaping.py +++ b/src/icalendar/tests/test_issue_355_url_escaping.py @@ -8,7 +8,7 @@ def test_facebook_link_is_correctly_parsed(events): see https://github.com/collective/icalendar/pull/356#issuecomment-1222626128 """ - events.issue_355_url_escaping["DESCRIPTION"] == "https://www.facebook.com/events/756119502186737/?acontext=%7B%22source%22%3A5%2C%22action_history%22%3A[%7B%22surface%22%3A%22page%22%2C%22mechanism%22%3A%22main_list%22%2C%22extra_data%22%3A%22%5C%22[]%5C%22%22%7D]%2C%22has_source%22%3Atrue%7D" + assert events.issue_355_url_escaping["DESCRIPTION"] == "https://www.facebook.com/events/756119502186737/?acontext=%7B%22source%22%3A5%2C%22action_history%22%3A[%7B%22surface%22%3A%22page%22%2C%22mechanism%22%3A%22main_list%22%2C%22extra_data%22%3A%22%5C%22[]%5C%22%22%7D]%2C%22has_source%22%3Atrue%7D" def test_other_facebook_link_is_correctly_parsed(events): """The facebook link must not be damaged. @@ -16,4 +16,4 @@ def test_other_facebook_link_is_correctly_parsed(events): see https://github.com/collective/icalendar/pull/356#issuecomment-1265872696 """ expected_result = 'https://www.facebook.com/events/756119502186737/?acontext=%7B%22source%22%3A5%2C%22action_history%22%3A[%7B%22surface%22%3A%22page%22%2C%22mechanism%22%3A%22main_list%22%2C%22extra_data%22%3A%22%5C%22[]%5C%22%22%7D]%2C%22has_source%22%3Atrue%7D' - events.issue_355_url_escaping_2["DESCRIPTION"] == expected_result + assert vents.issue_355_url_escaping_2["DESCRIPTION"] == expected_result From 169eedc4e25895d517c7eaca40c4cbdad221eec7 Mon Sep 17 00:00:00 2001 From: Nicco Kunzmann Date: Mon, 3 Oct 2022 20:48:05 +0100 Subject: [PATCH 4/6] correct misspelling --- src/icalendar/tests/test_issue_355_url_escaping.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/icalendar/tests/test_issue_355_url_escaping.py b/src/icalendar/tests/test_issue_355_url_escaping.py index 8c073f14..b46ac33c 100644 --- a/src/icalendar/tests/test_issue_355_url_escaping.py +++ b/src/icalendar/tests/test_issue_355_url_escaping.py @@ -16,4 +16,4 @@ def test_other_facebook_link_is_correctly_parsed(events): see https://github.com/collective/icalendar/pull/356#issuecomment-1265872696 """ expected_result = 'https://www.facebook.com/events/756119502186737/?acontext=%7B%22source%22%3A5%2C%22action_history%22%3A[%7B%22surface%22%3A%22page%22%2C%22mechanism%22%3A%22main_list%22%2C%22extra_data%22%3A%22%5C%22[]%5C%22%22%7D]%2C%22has_source%22%3Atrue%7D' - assert vents.issue_355_url_escaping_2["DESCRIPTION"] == expected_result + assert events.issue_355_url_escaping_2["DESCRIPTION"] == expected_result From 2e8430ab222eda8517888ab4d8c65b93920edf05 Mon Sep 17 00:00:00 2001 From: NicoHood Date: Wed, 15 Jun 2022 22:29:51 +0200 Subject: [PATCH 5/6] Fix #355 url escaping --- src/icalendar/parser.py | 79 ++++++++++++++++++++++++----------------- 1 file changed, 47 insertions(+), 32 deletions(-) diff --git a/src/icalendar/parser.py b/src/icalendar/parser.py index 6896f9fb..656c3ecf 100644 --- a/src/icalendar/parser.py +++ b/src/icalendar/parser.py @@ -259,25 +259,6 @@ def from_ical(cls, st, strict=False): % (param, exc)) return result - -def escape_string(val): - # '%{:02X}'.format(i) - return val.replace(r'\,', '%2C').replace(r'\:', '%3A')\ - .replace(r'\;', '%3B').replace(r'\\', '%5C') - - -def unescape_string(val): - return val.replace('%2C', ',').replace('%3A', ':')\ - .replace('%3B', ';').replace('%5C', '\\') - - -def unescape_list_or_string(val): - if isinstance(val, list): - return [unescape_string(s) for s in val] - else: - return unescape_string(val) - - ######################################### # parsing and generation of content lines @@ -315,34 +296,68 @@ def from_parts(cls, name, params, values, sorted=True): return cls(f'{name}:{values}') def parts(self): - """Split the content line up into (name, parameters, values) parts. + """ + Split the content line up into (name, parameters, values) parts. + + Example with parameter: + DESCRIPTION;ALTREP="cid:part1.0001@example.org":The Fall'98 Wild + + Example without parameters: + DESCRIPTION:The Fall'98 Wild + + https://icalendar.org/iCalendar-RFC-5545/3-2-property-parameters.html """ try: - st = escape_string(self) + st = self name_split = None value_split = None in_quotes = False + # Any character can be escaped using a backslash, e.g.: "test\:test" + quote_character = False for i, ch in enumerate(st): - if not in_quotes: - if ch in ':;' and not name_split: - name_split = i - if ch == ':' and not value_split: - value_split = i + # We can also quote using quotation marks. This ignores any output, until another quote appears. if ch == '"': in_quotes = not in_quotes - name = unescape_string(st[:name_split]) + continue + + # Ignore input, as we are currently in quotation mark quotes + if in_quotes: + continue + + # Skip quoted character + if quote_character: + quote_character = False + continue + + # The next character should be ignored + if ch == '\\': + quote_character = True + continue + + # The name ends either after the parameter or value delimiter + if ch in ':;' and not name_split: + name_split = i + + # The value starts after the value delimiter + if ch == ':' and not value_split: + value_split = i + + # Get name + name = st[:name_split] if not name: raise ValueError('Key name is required') validate_token(name) + + # Check if parameters are empty if not name_split or name_split + 1 == value_split: raise ValueError('Invalid content line') + + # Get parameters (text between ; and :) params = Parameters.from_ical(st[name_split + 1: value_split], strict=self.strict) - params = Parameters( - (unescape_string(key), unescape_list_or_string(value)) - for key, value in iter(params.items()) - ) - values = unescape_string(st[value_split + 1:]) + + # Get the value after the : + values = st[value_split + 1:] return (name, params, values) except ValueError as exc: raise ValueError( From 832a16639b0df854e269b10b76ab3c2a7f15cccf Mon Sep 17 00:00:00 2001 From: Nicco Kunzmann Date: Mon, 3 Oct 2022 21:18:54 +0100 Subject: [PATCH 6/6] make sure that the edge case of an empty quoted string is considered --- .../tests/events/issue_355_url_escaping_empty_param.ics | 3 +++ src/icalendar/tests/test_issue_355_url_escaping.py | 4 ++++ 2 files changed, 7 insertions(+) create mode 100644 src/icalendar/tests/events/issue_355_url_escaping_empty_param.ics diff --git a/src/icalendar/tests/events/issue_355_url_escaping_empty_param.ics b/src/icalendar/tests/events/issue_355_url_escaping_empty_param.ics new file mode 100644 index 00000000..7d1fb0b1 --- /dev/null +++ b/src/icalendar/tests/events/issue_355_url_escaping_empty_param.ics @@ -0,0 +1,3 @@ +BEGIN:VEVENT +ORGANIZER;CN="":that +END:VEVENT \ No newline at end of file diff --git a/src/icalendar/tests/test_issue_355_url_escaping.py b/src/icalendar/tests/test_issue_355_url_escaping.py index b46ac33c..df60d838 100644 --- a/src/icalendar/tests/test_issue_355_url_escaping.py +++ b/src/icalendar/tests/test_issue_355_url_escaping.py @@ -17,3 +17,7 @@ def test_other_facebook_link_is_correctly_parsed(events): """ expected_result = 'https://www.facebook.com/events/756119502186737/?acontext=%7B%22source%22%3A5%2C%22action_history%22%3A[%7B%22surface%22%3A%22page%22%2C%22mechanism%22%3A%22main_list%22%2C%22extra_data%22%3A%22%5C%22[]%5C%22%22%7D]%2C%22has_source%22%3Atrue%7D' assert events.issue_355_url_escaping_2["DESCRIPTION"] == expected_result + +def test_empty_quotes(events): + """Make sure that empty quoted parameter values are supported.""" + assert events.issue_355_url_escaping_empty_param['ORGANIZER'].params['CN'] == "" \ No newline at end of file