<commit_before># -*- coding: utf-8 -*-
'''
    vdirsyncer.storage.dav
    ~~~~~~~~~~~~~~~~~~~~~~

    :copyright: (c) 2014 <NAME> & contributors
    :license: MIT, see LICENSE for more details.
'''
import datetime
import itertools

from lxml import etree

from requests import session as requests_session

from .base import Item, Storage
from .http import USERAGENT, prepare_auth, prepare_verify
from .. import exceptions, log, utils


dav_logger = log.get(__name__)

CALDAV_DT_FORMAT = '%Y%m%dT%H%M%SZ'


def _normalize_href(base, href, decoding_rounds=1):
    '''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


class Discover(object):
    _resourcetype = None
    _homeset_xml = None
    _homeset_tag = None
    _collection_xml = """
    <d:propfind xmlns:d="DAV:">
        <d:prop>
            <d:resourcetype />
            <d:displayname />
        </d:prop>
    </d:propfind>
    """

    def __init__(self, session):
        self.session = session

    def _find_principal(self):
        """tries to find the principal URL of the user
        :returns: iterable (but should be only of element) of urls
        :rtype: iterable(unicode)

        """
        headers = self.session.get_default_headers()
        headers['Depth'] = 0
        body = """
        <d:propfind xmlns:d="DAV:">
            <d:prop>
                <d:current-user-principal />
            </d:prop>
        </d:propfind>
        """
        response = self.session.request('PROPFIND', '', headers=headers,
                                        data=body)
        root = etree.XML(response.content)

        for element in root.iter('{*}current-user-principal'):
            for principal in element.iter():  # should be only one
                if principal.tag.endswith('href'):
                    yield principal.text

    def discover(self):
        """discover all the user's CalDAV or CardDAV collections on the server
        :returns: a list of the user's collections (as urls)
        :rtype: list(unicode)
        """
        # 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
        done = set()
        for principal in itertools.chain(self._find_principal(), ['']):
            for home in itertools.chain(self._find_home(principal), ['']):
                for collection in self._find_collections(home):
                    href = utils.urlparse.urljoin(self.session.url,
                                                  collection['href'])
                    if href not in done:
                        done.add(href)
                        yield collection
<commit_msg>Fix a bug in DAV discovery<commit_after># -*- coding: utf-8 -*-
'''
    vdirsyncer.storage.dav
    ~~~~~~~~~~~~~~~~~~~~~~

    :copyright: (c) 2014 <NAME> & contributors
    :license: MIT, see LICENSE for more details.
'''
import datetime
import itertools

from lxml import etree

from requests import session as requests_session

from .base import Item, Storage
from .http import USERAGENT, prepare_auth, prepare_verify
from .. import exceptions, log, utils


dav_logger = log.get(__name__)

CALDAV_DT_FORMAT = '%Y%m%dT%H%M%SZ'


def _normalize_href(base, href, decoding_rounds=1):
    '''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


class Discover(object):
    _resourcetype = None
    _homeset_xml = None
    _homeset_tag = None
    _collection_xml = """
    <d:propfind xmlns:d="DAV:">
        <d:prop>
            <d:resourcetype />
            <d:displayname />
        </d:prop>
    </d:propfind>
    """

    def __init__(self, session):
        self.session = session

    def _find_principal(self):
        """tries to find the principal URL of the user
        :returns: iterable (but should be only of element) of urls
        :rtype: iterable(unicode)

        """
        headers = self.session.get_default_headers()
        headers['Depth'] = 0
        body = """
        <d:propfind xmlns:d="DAV:">
            <d:prop>
                <d:current-user-principal />
            </d:prop>
        </d:propfind>
        """
        response = self.session.request('PROPFIND', '', headers=headers,
                                        data=body)
        root = etree.XML(response.content)

        for element in root.iter('{*}current-user-principal'):
            for principal in element.iter():  # should be only one
                if principal.tag.endswith('href'):
                    yield principal.text

    def discover(self):
        """discover all the user's CalDAV or CardDAV collections on the server
        :returns: a list of the user's collections (as urls)
        :rtype: list(unicode)
        """
        # 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
        done = set()
        for principal in itertools.chain(self._find_principal(), ['']):
            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

{'fraction_non_alphanumeric': 0.1168724279835391, 'fraction_numerical': 0.003950617283950617, 'mean_word_length': 2.0456140350877194, 'pattern_counts': {'":': 0, '<': 27, '<?xml version=': 0, '>': 27, 'https://': 2, 'lorem ipsum': 0, 'www.': 0, 'xml': 10}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 6, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '1521203', 'n_tokens_mistral': 1812, 'n_tokens_neox': 1734, 'n_words': 472}