diff --git a/tests/storage/dav/test_main.py b/tests/storage/dav/test_main.py index 701512c..27ff141 100644 --- a/tests/storage/dav/test_main.py +++ b/tests/storage/dav/test_main.py @@ -20,8 +20,7 @@ from tests import EVENT_TEMPLATE, TASK_TEMPLATE, VCARD_TEMPLATE import vdirsyncer.exceptions as exceptions from vdirsyncer.storage.base import Item -from vdirsyncer.storage.dav import CaldavStorage, CarddavStorage, \ - _normalize_href +from vdirsyncer.storage.dav import CaldavStorage, CarddavStorage from .. import StorageTests, format_item @@ -183,26 +182,3 @@ class TestCarddavStorage(DavStorageTests): @pytest.fixture def item_template(self, request): return VCARD_TEMPLATE - - -@pytest.mark.parametrize('base,path', [ - ('http://example.com/', ''), - ('http://example.com/L%C3%98/', '/L%C3%98'), - ('http://example.com/LØ/', '/L%C3%98'), -]) -def test_normalize_href(base, path): - assert _normalize_href(base, 'asdf') == path + '/asdf' - - assert _normalize_href(base, 'hahah') == path + '/hahah' - - assert _normalize_href(base, 'whoops@vdirsyncer.vcf') == \ - path + '/whoops@vdirsyncer.vcf' - - assert _normalize_href(base, 'whoops%40vdirsyncer.vcf') == \ - path + '/whoops@vdirsyncer.vcf' - - assert _normalize_href(base, 'wh%C3%98ops@vdirsyncer.vcf') == \ - path + '/wh%C3%98ops@vdirsyncer.vcf' - - assert _normalize_href(base, 'whØops@vdirsyncer.vcf') == \ - path + '/wh%C3%98ops@vdirsyncer.vcf' diff --git a/vdirsyncer/storage/dav.py b/vdirsyncer/storage/dav.py index 2f18362..528ed8f 100644 --- a/vdirsyncer/storage/dav.py +++ b/vdirsyncer/storage/dav.py @@ -23,21 +23,24 @@ dav_logger = log.get(__name__) CALDAV_DT_FORMAT = '%Y%m%dT%H%M%SZ' -def _normalize_href(base, href, decoding_rounds=1): +def _normalize_href(base, href): '''Normalize the href to be a path only relative to hostname and schema.''' if not href: raise ValueError(href) x = utils.urlparse.urljoin(base, href) x = utils.urlparse.urlsplit(x).path - - for i in range(decoding_rounds): - x = utils.compat.urlunquote(x) - - x = utils.compat.urlquote(x, '/@') return x +def _encode_href(x): + return utils.compat.urlquote(x, '/@') + + +def _decode_href(x): + return utils.compat.urlunquote(x) + + def _parse_xml(content): try: return etree.XML(content) @@ -293,7 +296,7 @@ class DavStorage(Storage): for href in hrefs: if href != self._normalize_href(href): raise exceptions.NotFoundError(href) - href_xml.append('{}'.format(href)) + href_xml.append('{}'.format(_encode_href(href))) if not href_xml: return () @@ -345,7 +348,7 @@ class DavStorage(Storage): response = self.session.request( 'PUT', - href, + _encode_href(href), data=item.raw.encode('utf-8'), headers=headers ) @@ -375,7 +378,7 @@ class DavStorage(Storage): self.session.request( 'DELETE', - href, + _encode_href(href), headers=headers ) @@ -388,7 +391,10 @@ class DavStorage(Storage): dav_logger.error('Skipping response, href is missing.') continue - href = self._normalize_href(href, decoding_rounds=decoding_rounds) + href = self._normalize_href(href) + for i in range(decoding_rounds): + href = _decode_href(href) + if href in hrefs: # Servers that send duplicate hrefs: # - Zimbra @@ -618,7 +624,6 @@ class CarddavStorage(DavStorage): # with Zimbra. See https://github.com/untitaker/vdirsyncer/issues/83 response = self.session.request('PROPFIND', '', data=data, headers=headers) - root = _parse_xml(response.content) # Decode twice because ownCloud encodes twice.