From 7d41378505183b57180faec7f58584bcbbe52bbf Mon Sep 17 00:00:00 2001 From: Markus Unterwaditzer Date: Thu, 4 Jun 2015 12:18:18 +0200 Subject: [PATCH] dav: Fully move collection management into Discover --- .../storage/dav/servers/radicale/__init__.py | 3 - vdirsyncer/storage/dav.py | 150 +++++++++--------- 2 files changed, 71 insertions(+), 82 deletions(-) diff --git a/tests/storage/dav/servers/radicale/__init__.py b/tests/storage/dav/servers/radicale/__init__.py index dc005c5..d25a189 100644 --- a/tests/storage/dav/servers/radicale/__init__.py +++ b/tests/storage/dav/servers/radicale/__init__.py @@ -116,9 +116,6 @@ class ServerMixin(object): 'collection': collection, 'unsafe_href_chars': ''} if collection is not None: - if storage_backend != 'multifilesystem': - # XXX: https://github.com/Kozea/Radicale/pull/236 - rv = self.storage_class.create_collection(**rv) s = self.storage_class(**rv) s.delete(*s.upload(get_item())) diff --git a/vdirsyncer/storage/dav.py b/vdirsyncer/storage/dav.py index 6af589e..9d60372 100644 --- a/vdirsyncer/storage/dav.py +++ b/vdirsyncer/storage/dav.py @@ -82,6 +82,7 @@ def _get_collection_from_url(url): class Discover(object): + _namespace = None _resourcetype = None _homeset_xml = None _homeset_tag = None @@ -95,8 +96,17 @@ class Discover(object): """ - def __init__(self, session): - self.session = session + def __init__(self, **kwargs): + if kwargs.pop('collection', None) is not None: + raise TypeError('collection argument must not be given.') + + discover_args, _ = utils.split_dict(kwargs, lambda key: key in ( + 'url', 'username', 'password', 'verify', 'auth', 'useragent', + 'verify_fingerprint', 'auth_cert', + )) + + self.session = DavSession(**discover_args) + self.kwargs = kwargs def find_dav(self): try: @@ -113,7 +123,11 @@ class Discover(object): def find_principal(self, url=None): if url is None: - url = self.find_dav() + try: + return self.find_principal('') + except (HTTPError, exceptions.Error): + return self.find_principal(self.find_dav()) + headers = self.session.get_default_headers() headers['Depth'] = 0 body = """ @@ -171,20 +185,60 @@ class Discover(object): yield {'href': href, 'displayname': displayname} def discover(self): - for x in self.find_collections(): - yield x + for c in self.find_collections(): + url = c['href'] + collection = _get_collection_from_url(url) + storage_args = dict(self.kwargs) + storage_args.update({'url': url, 'collection': collection, + 'collection_human': c['displayname']}) + yield storage_args + + def create(self, collection): + if collection is None: + collection = _get_collection_from_url(self.kwargs['url']) + + for c in self.discover(): + if c['collection'] == collection: + return c + + home = self.find_home() + url = '{}/{}'.format(home.rstrip('/'), collection) - # Another one of Radicale's specialties: Discovery is broken (returning - # completely wrong URLs at every stage) as of version 0.9. - # https://github.com/Kozea/Radicale/issues/196 try: - for x in self.find_collections(''): - yield x - except (InvalidXMLResponse, HTTPError, exceptions.Error): - pass + url = self._create_collection_impl(url) + except HTTPError as e: + raise NotImplementedError(e) + else: + rv = dict(self.kwargs) + rv['collection'] = collection + rv['url'] = url + return rv + + def _create_collection_impl(self, url): + data = ''' + + + + + + + + + + + '''.format(self._namespace, self._resourcetype) + + response = self.session.request( + 'MKCOL', + url, + data=data, + headers=self.session.get_default_headers(), + ) + return response.url class CalDiscover(Discover): + _namespace = 'urn:ietf:params:xml:ns:caldav' _resourcetype = 'calendar' _homeset_xml = """ @@ -198,6 +252,7 @@ class CalDiscover(Discover): class CardDiscover(Discover): + _namespace = 'urn:ietf:params:xml:ns:carddav' _resourcetype = 'addressbook' _homeset_xml = """ @@ -294,74 +349,15 @@ class DavStorage(Storage): self.username = username self.url = url - @classmethod - def _get_session(cls, **kwargs): - discover_args, _ = utils.split_dict(kwargs, lambda key: key in ( - 'url', 'username', 'password', 'verify', 'auth', 'useragent', - 'verify_fingerprint', 'auth_cert', - )) - return DavSession(**discover_args) - @classmethod def discover(cls, **kwargs): - if kwargs.pop('collection', None) is not None: - raise TypeError('collection argument must not be given.') - - d = cls.discovery_class(cls._get_session(**kwargs)) - for c in d.discover(): - url = c['href'] - collection = _get_collection_from_url(url) - storage_args = dict(kwargs) - storage_args.update({'url': url, 'collection': collection, - 'collection_human': c['displayname']}) - yield storage_args + d = cls.discovery_class(**kwargs) + return d.discover() @classmethod def create_collection(cls, collection, **kwargs): - if collection is None: - collection = _get_collection_from_url(kwargs['url']) - session = cls._get_session(**kwargs) - d = cls.discovery_class(session) - - for c in cls.discover(**kwargs): - if c['collection'] == collection: - return c - - home = d.find_home() - url = '{}/{}'.format(home.rstrip('/'), collection) - - try: - url = cls._create_collection_impl(d, url, kwargs) - except HTTPError as e: - raise NotImplementedError(e) - else: - rv = dict(kwargs) - rv['collection'] = collection - rv['url'] = url - return rv - - @classmethod - def _create_collection_impl(cls, d, url, kwargs): - data = ''' - - - - - - - - - - - '''.format(cls._dav_namespace, cls._dav_resourcetype) - - response = d.session.request( - 'MKCOL', - url, - data=data, - headers=d.session.get_default_headers(), - ) - return response.url + d = cls.discovery_class(**kwargs) + return d.create(collection) def _normalize_href(self, *args, **kwargs): return _normalize_href(self.session.url, *args, **kwargs) @@ -588,8 +584,6 @@ class CaldavStorage(DavStorage): fileext = '.ics' item_mimetype = 'text/calendar' discovery_class = CalDiscover - _dav_namespace = 'urn:ietf:params:xml:ns:caldav' - _dav_resourcetype = 'calendar' start_date = None end_date = None @@ -723,6 +717,4 @@ class CarddavStorage(DavStorage): {hrefs} ''' - _dav_namespace = 'urn:ietf:params:xml:ns:carddav' - _dav_resourcetype = 'addressbook' get_multi_data_query = '{urn:ietf:params:xml:ns:carddav}address-data'