Skip to content

Commit

Permalink
Fix collective#355 url escaping
Browse files Browse the repository at this point in the history
  • Loading branch information
NicoHood committed Jun 15, 2022
1 parent 90c0593 commit 757cea3
Showing 1 changed file with 48 additions and 33 deletions.
81 changes: 48 additions & 33 deletions src/icalendar/parser.py
Original file line number Diff line number Diff line change
Expand Up @@ -263,25 +263,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

Expand Down Expand Up @@ -319,34 +300,68 @@ def from_parts(cls, name, params, values, sorted=True):
return cls('%s:%s' % (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 compat.iteritems(params)
)
values = unescape_string(st[value_split + 1:])
strict=self.strict)

# Get the value after the :
values = st[value_split + 1:]
return (name, params, values)
except ValueError as exc:
raise ValueError(
Expand Down

0 comments on commit 757cea3

Please sign in to comment.