diff --git a/tests/storage/dav/test_main.py b/tests/storage/dav/test_main.py index dcc94c4..0295005 100644 --- a/tests/storage/dav/test_main.py +++ b/tests/storage/dav/test_main.py @@ -122,14 +122,14 @@ class TestCaldavStorage(DavStorageTests): item = self._create_bogus_item() href, etag = s.upload(item) - old_list = s._list + old_dav_query = s._dav_query calls = [] - def _list(*a, **kw): + def _dav_query(*a, **kw): calls.append(None) - return old_list(*a, **kw) + return old_dav_query(*a, **kw) - monkeypatch.setattr(s, '_list', _list) + monkeypatch.setattr(s, '_dav_query', _dav_query) rv = list(s.list()) if (dav_server != 'radicale' and not s.item_types) \ diff --git a/vdirsyncer/storage/dav.py b/vdirsyncer/storage/dav.py index 8a9a200..09144aa 100644 --- a/vdirsyncer/storage/dav.py +++ b/vdirsyncer/storage/dav.py @@ -395,7 +395,7 @@ class DavStorage(Storage): headers=headers ) - def _list(self, xml): + def _dav_query(self, xml): headers = self.session.get_default_headers() # CardDAV: The request MUST include a Depth header. The scope of the @@ -542,7 +542,7 @@ class CaldavStorage(DavStorage): for caldavfilter in caldavfilters: xml = data.format(caldavfilter=caldavfilter) - for href, etag in self._list(xml): + for href, etag in self._dav_query(xml): assert href not in hrefs hrefs.add(href) yield href, etag @@ -573,13 +573,44 @@ class CarddavStorage(DavStorage): get_multi_data_query = '{urn:ietf:params:xml:ns:carddav}address-data' def list(self): - return self._list(''' - + headers = self.session.get_default_headers() + headers['Depth'] = 1 + + data = ''' + + + - - - - ''') + + ''' + + # We use a PROPFIND request instead of addressbook-query due to issues + # with Zimbra. See https://github.com/untitaker/vdirsyncer/issues/83 + response = self.session.request('PROPFIND', '', data=data, + headers=headers) + + root = etree.XML(response.content) + hrefs = set() + for element in root.iter('{DAV:}response'): + prop = element.find('{DAV:}propstat').find('{DAV:}prop') + + resource_type = prop.find('{DAV:}resourcetype') + if resource_type.find('{DAV:}collection') is not None: + continue + + content_type = prop.find('{DAV:}getcontenttype') + # different servers give different getcontenttypes: + # "text/vcard", "text/x-vcard", "text/x-vcard; charset=utf-8", + # "text/directory;profile=vCard", "text/directory", + # "text/vcard; charset=utf-8" + if 'vcard' not in content_type.text.lower(): + continue + + href = self._normalize_href(element.find('{DAV:}href').text) + etag = prop.find('{DAV:}getetag').text + + if href not in hrefs: + hrefs.add(href) + yield self._normalize_href(href), etag