mirror of
https://github.com/samsonjs/vdirsyncer.git
synced 2026-04-25 14:37:47 +00:00
Merge branch 'encodings'
Conflicts: vdirsyncer/storage/dav.py
This commit is contained in:
commit
291edd7be0
2 changed files with 17 additions and 36 deletions
|
|
@ -20,8 +20,7 @@ from tests import EVENT_TEMPLATE, TASK_TEMPLATE, VCARD_TEMPLATE
|
||||||
|
|
||||||
import vdirsyncer.exceptions as exceptions
|
import vdirsyncer.exceptions as exceptions
|
||||||
from vdirsyncer.storage.base import Item
|
from vdirsyncer.storage.base import Item
|
||||||
from vdirsyncer.storage.dav import CaldavStorage, CarddavStorage, \
|
from vdirsyncer.storage.dav import CaldavStorage, CarddavStorage
|
||||||
_normalize_href
|
|
||||||
|
|
||||||
from .. import StorageTests, format_item
|
from .. import StorageTests, format_item
|
||||||
|
|
||||||
|
|
@ -183,26 +182,3 @@ class TestCarddavStorage(DavStorageTests):
|
||||||
@pytest.fixture
|
@pytest.fixture
|
||||||
def item_template(self, request):
|
def item_template(self, request):
|
||||||
return VCARD_TEMPLATE
|
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'
|
|
||||||
|
|
|
||||||
|
|
@ -23,21 +23,24 @@ dav_logger = log.get(__name__)
|
||||||
CALDAV_DT_FORMAT = '%Y%m%dT%H%M%SZ'
|
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
|
'''Normalize the href to be a path only relative to hostname and
|
||||||
schema.'''
|
schema.'''
|
||||||
if not href:
|
if not href:
|
||||||
raise ValueError(href)
|
raise ValueError(href)
|
||||||
x = utils.urlparse.urljoin(base, href)
|
x = utils.urlparse.urljoin(base, href)
|
||||||
x = utils.urlparse.urlsplit(x).path
|
x = utils.urlparse.urlsplit(x).path
|
||||||
|
|
||||||
for i in range(decoding_rounds):
|
|
||||||
x = utils.compat.urlunquote(x)
|
|
||||||
|
|
||||||
x = utils.compat.urlquote(x, '/@')
|
|
||||||
return 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):
|
def _parse_xml(content):
|
||||||
try:
|
try:
|
||||||
return etree.XML(content)
|
return etree.XML(content)
|
||||||
|
|
@ -293,7 +296,7 @@ class DavStorage(Storage):
|
||||||
for href in hrefs:
|
for href in hrefs:
|
||||||
if href != self._normalize_href(href):
|
if href != self._normalize_href(href):
|
||||||
raise exceptions.NotFoundError(href)
|
raise exceptions.NotFoundError(href)
|
||||||
href_xml.append('<D:href>{}</D:href>'.format(href))
|
href_xml.append('<D:href>{}</D:href>'.format(_encode_href(href)))
|
||||||
if not href_xml:
|
if not href_xml:
|
||||||
return ()
|
return ()
|
||||||
|
|
||||||
|
|
@ -345,7 +348,7 @@ class DavStorage(Storage):
|
||||||
|
|
||||||
response = self.session.request(
|
response = self.session.request(
|
||||||
'PUT',
|
'PUT',
|
||||||
href,
|
_encode_href(href),
|
||||||
data=item.raw.encode('utf-8'),
|
data=item.raw.encode('utf-8'),
|
||||||
headers=headers
|
headers=headers
|
||||||
)
|
)
|
||||||
|
|
@ -375,7 +378,7 @@ class DavStorage(Storage):
|
||||||
|
|
||||||
self.session.request(
|
self.session.request(
|
||||||
'DELETE',
|
'DELETE',
|
||||||
href,
|
_encode_href(href),
|
||||||
headers=headers
|
headers=headers
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
@ -388,7 +391,10 @@ class DavStorage(Storage):
|
||||||
dav_logger.error('Skipping response, href is missing.')
|
dav_logger.error('Skipping response, href is missing.')
|
||||||
continue
|
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:
|
if href in hrefs:
|
||||||
# Servers that send duplicate hrefs:
|
# Servers that send duplicate hrefs:
|
||||||
# - Zimbra
|
# - Zimbra
|
||||||
|
|
@ -618,7 +624,6 @@ class CarddavStorage(DavStorage):
|
||||||
# with Zimbra. See https://github.com/untitaker/vdirsyncer/issues/83
|
# with Zimbra. See https://github.com/untitaker/vdirsyncer/issues/83
|
||||||
response = self.session.request('PROPFIND', '', data=data,
|
response = self.session.request('PROPFIND', '', data=data,
|
||||||
headers=headers)
|
headers=headers)
|
||||||
|
|
||||||
root = _parse_xml(response.content)
|
root = _parse_xml(response.content)
|
||||||
|
|
||||||
# Decode twice because ownCloud encodes twice.
|
# Decode twice because ownCloud encodes twice.
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue