kopia lustrzana https://github.com/collective/icalendar
Allow `=` in parameter values.
Some parameter values (e.g., BASE64 encoded binary data often ends with one or two equal signs) may contain an equal sign (`=`). The current implementation splits key-value pairs at all equal signs, which leads to errors. Especially icalendar files generated by Apple's software often feature BASE64 encoded binary data in parameter values. This patch introduces a new parameter `maxsplit` to icalendar.parser.q_split() which works similar as python's string.split(sep, maxsplit) which we then use to split parameter key-value pairs only at the first equal sign. This patch fixes #197.pull/207/head
rodzic
6c86898399
commit
d0fd108ec7
|
|
@ -14,7 +14,8 @@ New features:
|
||||||
|
|
||||||
Bug fixes:
|
Bug fixes:
|
||||||
|
|
||||||
- *add item here*
|
- Don't break on parameter values which contain equal signs, e.g. base64 encoded
|
||||||
|
binary data [geier]
|
||||||
|
|
||||||
|
|
||||||
3.11.3 (2017-02-15)
|
3.11.3 (2017-02-15)
|
||||||
|
|
|
||||||
|
|
@ -149,13 +149,14 @@ def dquote(val):
|
||||||
|
|
||||||
|
|
||||||
# parsing helper
|
# parsing helper
|
||||||
def q_split(st, sep=','):
|
def q_split(st, sep=',', maxsplit=-1):
|
||||||
"""Splits a string on char, taking double (q)uotes into considderation.
|
"""Splits a string on char, taking double (q)uotes into considderation.
|
||||||
"""
|
"""
|
||||||
result = []
|
result = []
|
||||||
cursor = 0
|
cursor = 0
|
||||||
length = len(st)
|
length = len(st)
|
||||||
inquote = 0
|
inquote = 0
|
||||||
|
splits = 0
|
||||||
for i in range(length):
|
for i in range(length):
|
||||||
ch = st[i]
|
ch = st[i]
|
||||||
if ch == '"':
|
if ch == '"':
|
||||||
|
|
@ -163,8 +164,10 @@ def q_split(st, sep=','):
|
||||||
if not inquote and ch == sep:
|
if not inquote and ch == sep:
|
||||||
result.append(st[cursor:i])
|
result.append(st[cursor:i])
|
||||||
cursor = i + 1
|
cursor = i + 1
|
||||||
if i + 1 == length:
|
splits += 1
|
||||||
|
if i + 1 == length or splits == maxsplit:
|
||||||
result.append(st[cursor:])
|
result.append(st[cursor:])
|
||||||
|
break
|
||||||
return result
|
return result
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -227,7 +230,7 @@ class Parameters(CaselessDict):
|
||||||
result = cls()
|
result = cls()
|
||||||
for param in q_split(st, ';'):
|
for param in q_split(st, ';'):
|
||||||
try:
|
try:
|
||||||
key, val = q_split(param, '=')
|
key, val = q_split(param, '=', maxsplit=1)
|
||||||
validate_token(key)
|
validate_token(key)
|
||||||
# Property parameter values that are not in quoted
|
# Property parameter values that are not in quoted
|
||||||
# strings are case insensitive.
|
# strings are case insensitive.
|
||||||
|
|
|
||||||
|
|
@ -10,17 +10,11 @@ class TestEncoding(unittest.TestCase):
|
||||||
|
|
||||||
def test_apple_xlocation(self):
|
def test_apple_xlocation(self):
|
||||||
"""
|
"""
|
||||||
Test if error messages are encode properly.
|
Test if we support base64 encoded binary data in parameter values.
|
||||||
"""
|
"""
|
||||||
try:
|
|
||||||
directory = os.path.dirname(__file__)
|
directory = os.path.dirname(__file__)
|
||||||
with open(os.path.join(directory, 'x_location.ics'), 'rb') as fp:
|
with open(os.path.join(directory, 'x_location.ics'), 'rb') as fp:
|
||||||
data = fp.read()
|
data = fp.read()
|
||||||
cal = icalendar.Calendar.from_ical(data)
|
cal = icalendar.Calendar.from_ical(data)
|
||||||
for event in cal.walk('vevent'):
|
for event in cal.walk('vevent'):
|
||||||
self.assertEqual(len(event.errors), 1, 'Got too many errors')
|
self.assertEqual(len(event.errors), 0, 'Got too many errors')
|
||||||
error = event.errors[0][1]
|
|
||||||
self.assertTrue(error.startswith(u'Content line could not be parsed into parts'))
|
|
||||||
|
|
||||||
except UnicodeEncodeError as e:
|
|
||||||
self.fail("There is something wrong with encoding in the collected error messages")
|
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,6 @@
|
||||||
# -*- coding: utf-8 -*-
|
# -*- coding: utf-8 -*-
|
||||||
|
import textwrap
|
||||||
|
|
||||||
from icalendar.tests import unittest
|
from icalendar.tests import unittest
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -207,6 +209,30 @@ class IcalendarTestCase (unittest.TestCase):
|
||||||
('key', Parameters({'PARAM': 'pValue'}), 'value')
|
('key', Parameters({'PARAM': 'pValue'}), 'value')
|
||||||
)
|
)
|
||||||
|
|
||||||
|
contains_base64 = (
|
||||||
|
'X-APPLE-STRUCTURED-LOCATION;'
|
||||||
|
'VALUE=URI;X-ADDRESS="Kaiserliche Hofburg, 1010 Wien";'
|
||||||
|
'X-APPLE-MAPKIT-HANDLE=CAESxQEZgr3QZXJyZWljaA==;'
|
||||||
|
'X-APPLE-RADIUS=328.7978217977285;X-APPLE-REFERENCEFRAME=1;'
|
||||||
|
'X-TITLE=Heldenplatz:geo:48.206686,16.363235'
|
||||||
|
).encode('utf-8')
|
||||||
|
|
||||||
|
self.assertEqual(
|
||||||
|
Contentline(contains_base64, strict=True).parts(),
|
||||||
|
('X-APPLE-STRUCTURED-LOCATION',
|
||||||
|
Parameters({
|
||||||
|
'X-APPLE-RADIUS': '328.7978217977285',
|
||||||
|
'X-ADDRESS': 'Kaiserliche Hofburg, 1010 Wien',
|
||||||
|
'X-APPLE-REFERENCEFRAME': '1',
|
||||||
|
'X-TITLE': u'HELDENPLATZ',
|
||||||
|
'X-APPLE-MAPKIT-HANDLE':
|
||||||
|
'CAESXQEZGR3QZXJYZWLJAA==',
|
||||||
|
'VALUE': 'URI',
|
||||||
|
}),
|
||||||
|
'geo:48.206686,16.363235'
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
def test_fold_line(self):
|
def test_fold_line(self):
|
||||||
from ..parser import foldline
|
from ..parser import foldline
|
||||||
|
|
||||||
|
|
@ -247,6 +273,10 @@ class IcalendarTestCase (unittest.TestCase):
|
||||||
self.assertEqual(q_split('Max,Moller,"Rasmussen, Max"'),
|
self.assertEqual(q_split('Max,Moller,"Rasmussen, Max"'),
|
||||||
['Max', 'Moller', '"Rasmussen, Max"'])
|
['Max', 'Moller', '"Rasmussen, Max"'])
|
||||||
|
|
||||||
|
def test_q_split_bin(self):
|
||||||
|
from ..parser import q_split
|
||||||
|
self.assertEqual(q_split('X-SOMETHING=ABCDE==', '=', maxsplit=1), ['X-SOMETHING', 'ABCDE=='])
|
||||||
|
|
||||||
def test_q_join(self):
|
def test_q_join(self):
|
||||||
from ..parser import q_join
|
from ..parser import q_join
|
||||||
self.assertEqual(q_join(['Max', 'Moller', 'Rasmussen, Max']),
|
self.assertEqual(q_join(['Max', 'Moller', 'Rasmussen, Max']),
|
||||||
|
|
|
||||||
Ładowanie…
Reference in New Issue