mirror of
https://github.com/samsonjs/vdirsyncer.git
synced 2026-04-27 14:57:41 +00:00
Improve DAV discovery
- Fix #156, .well-known URIs are now supported. - More resilience against error responses, which are not surprising given that we send partially invalid requests in order to brute-force the home-set URL.
This commit is contained in:
parent
839f53e872
commit
2a80dfae30
2 changed files with 48 additions and 14 deletions
|
|
@ -26,6 +26,8 @@ Version 0.4.0
|
||||||
``VDIRSYNCER_CONFIG`` environment variable isn't set and the
|
``VDIRSYNCER_CONFIG`` environment variable isn't set and the
|
||||||
``~/.vdirsyncer/config`` file doesn't exist, it will look for the
|
``~/.vdirsyncer/config`` file doesn't exist, it will look for the
|
||||||
configuration file at ``$XDG_CONFIG_HOME/vdirsyncer/config``.
|
configuration file at ``$XDG_CONFIG_HOME/vdirsyncer/config``.
|
||||||
|
- Some improvements to CardDAV and CalDAV discovery, based on problems found
|
||||||
|
with FastMail. Support for ``.well-known``-URIs has been added.
|
||||||
|
|
||||||
Version 0.3.4
|
Version 0.3.4
|
||||||
=============
|
=============
|
||||||
|
|
|
||||||
|
|
@ -7,6 +7,7 @@
|
||||||
:license: MIT, see LICENSE for more details.
|
:license: MIT, see LICENSE for more details.
|
||||||
'''
|
'''
|
||||||
import datetime
|
import datetime
|
||||||
|
import functools
|
||||||
import itertools
|
import itertools
|
||||||
|
|
||||||
from lxml import etree
|
from lxml import etree
|
||||||
|
|
@ -63,10 +64,27 @@ def _fuzzy_matches_mimetype(strict, weak):
|
||||||
return False
|
return False
|
||||||
|
|
||||||
|
|
||||||
|
def _catch_generator_exceptions(rv=None):
|
||||||
|
def wrapper(f):
|
||||||
|
@functools.wraps(f)
|
||||||
|
def inner(*args, **kwargs):
|
||||||
|
try:
|
||||||
|
for x in f(*args, **kwargs):
|
||||||
|
yield x
|
||||||
|
except Exception:
|
||||||
|
import traceback
|
||||||
|
dav_logger.debug(traceback.format_exc())
|
||||||
|
for x in rv:
|
||||||
|
yield x
|
||||||
|
return inner
|
||||||
|
return wrapper
|
||||||
|
|
||||||
|
|
||||||
class Discover(object):
|
class Discover(object):
|
||||||
_resourcetype = None
|
_resourcetype = None
|
||||||
_homeset_xml = None
|
_homeset_xml = None
|
||||||
_homeset_tag = None
|
_homeset_tag = None
|
||||||
|
_well_known_uri = None
|
||||||
_collection_xml = """
|
_collection_xml = """
|
||||||
<d:propfind xmlns:d="DAV:">
|
<d:propfind xmlns:d="DAV:">
|
||||||
<d:prop>
|
<d:prop>
|
||||||
|
|
@ -79,7 +97,8 @@ class Discover(object):
|
||||||
def __init__(self, session):
|
def __init__(self, session):
|
||||||
self.session = session
|
self.session = session
|
||||||
|
|
||||||
def _find_principal(self):
|
@_catch_generator_exceptions(rv=())
|
||||||
|
def _find_principal(self, url):
|
||||||
"""tries to find the principal URL of the user
|
"""tries to find the principal URL of the user
|
||||||
:returns: iterable (but should be only of element) of urls
|
:returns: iterable (but should be only of element) of urls
|
||||||
:rtype: iterable(unicode)
|
:rtype: iterable(unicode)
|
||||||
|
|
@ -94,8 +113,8 @@ class Discover(object):
|
||||||
</d:prop>
|
</d:prop>
|
||||||
</d:propfind>
|
</d:propfind>
|
||||||
"""
|
"""
|
||||||
response = self.session.request('PROPFIND', '', headers=headers,
|
response = self.session.request('PROPFIND', url, headers=headers,
|
||||||
data=body)
|
data=body, is_subpath=False)
|
||||||
root = _parse_xml(response.content)
|
root = _parse_xml(response.content)
|
||||||
|
|
||||||
for element in root.iter('{*}current-user-principal'):
|
for element in root.iter('{*}current-user-principal'):
|
||||||
|
|
@ -103,6 +122,11 @@ class Discover(object):
|
||||||
if principal.tag.endswith('href'):
|
if principal.tag.endswith('href'):
|
||||||
yield principal.text
|
yield principal.text
|
||||||
|
|
||||||
|
@_catch_generator_exceptions(rv=())
|
||||||
|
def _find_dav(self):
|
||||||
|
response = self.session.request('GET', self._well_known_uri)
|
||||||
|
yield response.url
|
||||||
|
|
||||||
def discover(self):
|
def discover(self):
|
||||||
"""discover all the user's CalDAV or CardDAV collections on the server
|
"""discover all the user's CalDAV or CardDAV collections on the server
|
||||||
:returns: a list of the user's collections (as urls)
|
:returns: a list of the user's collections (as urls)
|
||||||
|
|
@ -111,18 +135,23 @@ class Discover(object):
|
||||||
# Another one of Radicale's specialties: Discovery is broken (returning
|
# Another one of Radicale's specialties: Discovery is broken (returning
|
||||||
# completely wrong URLs at every stage) as of version 0.9.
|
# completely wrong URLs at every stage) as of version 0.9.
|
||||||
# https://github.com/Kozea/Radicale/issues/196
|
# https://github.com/Kozea/Radicale/issues/196
|
||||||
done = set()
|
#
|
||||||
for principal in itertools.chain(self._find_principal(), ['']):
|
# So we just brute-force a lot of paths here.
|
||||||
for home in itertools.chain(self._find_home(principal), ['']):
|
|
||||||
for collection in self._find_collections(home):
|
|
||||||
collection['href'] = href = \
|
|
||||||
utils.urlparse.urljoin(self.session.url,
|
|
||||||
collection['href'])
|
|
||||||
if href not in done:
|
|
||||||
done.add(href)
|
|
||||||
yield collection
|
|
||||||
|
|
||||||
def _find_home(self, principal):
|
done = set()
|
||||||
|
for dav in list(self._find_dav()) or ['']:
|
||||||
|
for principal in list(self._find_principal(dav)) or ['']:
|
||||||
|
for home in itertools.chain(self._find_homes(principal), ['']):
|
||||||
|
for collection in self._find_collections(home):
|
||||||
|
collection['href'] = href = \
|
||||||
|
utils.urlparse.urljoin(self.session.url,
|
||||||
|
collection['href'])
|
||||||
|
if href not in done:
|
||||||
|
done.add(href)
|
||||||
|
yield collection
|
||||||
|
|
||||||
|
@_catch_generator_exceptions(rv=())
|
||||||
|
def _find_homes(self, principal):
|
||||||
headers = self.session.get_default_headers()
|
headers = self.session.get_default_headers()
|
||||||
headers['Depth'] = 0
|
headers['Depth'] = 0
|
||||||
response = self.session.request('PROPFIND', principal, headers=headers,
|
response = self.session.request('PROPFIND', principal, headers=headers,
|
||||||
|
|
@ -135,6 +164,7 @@ class Discover(object):
|
||||||
if homeset.tag.endswith('href'):
|
if homeset.tag.endswith('href'):
|
||||||
yield homeset.text
|
yield homeset.text
|
||||||
|
|
||||||
|
@_catch_generator_exceptions(rv=())
|
||||||
def _find_collections(self, home):
|
def _find_collections(self, home):
|
||||||
"""find all CalDAV collections under `home`"""
|
"""find all CalDAV collections under `home`"""
|
||||||
|
|
||||||
|
|
@ -168,6 +198,7 @@ class CalDiscover(Discover):
|
||||||
</d:propfind>
|
</d:propfind>
|
||||||
"""
|
"""
|
||||||
_homeset_tag = '{*}calendar-home-set'
|
_homeset_tag = '{*}calendar-home-set'
|
||||||
|
_well_known_uri = '/.well-known/caldav/'
|
||||||
|
|
||||||
|
|
||||||
class CardDiscover(Discover):
|
class CardDiscover(Discover):
|
||||||
|
|
@ -180,6 +211,7 @@ class CardDiscover(Discover):
|
||||||
</d:propfind>
|
</d:propfind>
|
||||||
"""
|
"""
|
||||||
_homeset_tag = '{*}addressbook-home-set'
|
_homeset_tag = '{*}addressbook-home-set'
|
||||||
|
_well_known_uri = '/.well-known/carddav/'
|
||||||
|
|
||||||
|
|
||||||
class DavSession(object):
|
class DavSession(object):
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue