<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 .. import exceptions, log, utils
from .base import Item, Storage
from .http import USERAGENT, prepare_auth, prepare_verify


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

    def _find_home(self, principal):
        headers = self.session.get_default_headers()
        headers['Depth'] = 0
        response = self.session.request('PROPFIND', principal, headers=headers,
                                        data=self._homeset_xml,
                                        is_subpath=False)

        root = etree.fromstring(response.content)
        for element in root.iter(self._homeset_tag):
            for homeset in element.iter():
                if homeset.tag.endswith('href'):
                    yield homeset.text

    def _find_collections(self, home):
        """find all CalDAV collections under `home`"""

        headers = self.session.get_default_headers()
        headers['Depth'] = 1
        response = self.session.request('PROPFIND', home, headers=headers,
                                        data=self._collection_xml,
                                        is_subpath=False)
        root = etree.XML(response.content)
        for response in root.iter('{*}response'):
            prop = response.find('{*}propstat/{*}prop')
            if prop.find('{*}resourcetype/{*}' + self._resourcetype) is None:
                continue

            displayname = prop.find('{*}displayname')
            collection = {
                'href': response.find('{*}href').text,
                'displayname': '' if displayname is None else displayname.text
            }

            yield collection


class CalDiscover(Discover):
    _resourcetype = 'calendar'
    _homeset_xml = """
    <d:propfind xmlns:d="DAV:" xmlns:c="urn:ietf:params:xml:ns:caldav">
        <d:prop>
            <c:calendar-home-set />
        </d:prop>
    </d:propfind>
    """
    _homeset_tag = '{*}calendar-home-set'


class CardDiscover(Discover):
    _resourcetype = 'addressbook'
    _homeset_xml = """
    <d:propfind xmlns:d="DAV:" xmlns:c="urn:ietf:params:xml:ns:carddav">
        <d:prop>
            <c:addressbook-home-set />
        </d:prop>
    </d:propfind>
    """
    _homeset_tag = '{*}addressbook-home-set'


class DavSession(object):
    '''
    A helper class to connect to DAV servers.
    '''

    def __init__(self, url, username='', password='', verify=True, auth=None,
                 useragent=USERAGENT, verify_fingerprint=None, dav_header=None):
        if username and not password:
            password = utils.get_password(username, url)

        self._settings = {
            'verify': prepare_verify(verify),
            'auth': prepare_auth(auth, username, password),
            'verify_fingerprint': verify_fingerprint,
        }
        self.useragent = useragent
        self.url = url.rstrip('/') + '/'
        self.parsed_url = utils.urlparse.urlparse(self.url)
        self.dav_header = dav_header
        self._session = None

    def request(self, method, path, data=None, headers=None,<commit_msg>Fix style errors<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 .. import exceptions, log, utils
from .base import Item, Storage
from .http import USERAGENT, prepare_auth, prepare_verify


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

    def _find_home(self, principal):
        headers = self.session.get_default_headers()
        headers['Depth'] = 0
        response = self.session.request('PROPFIND', principal, headers=headers,
                                        data=self._homeset_xml,
                                        is_subpath=False)

        root = etree.fromstring(response.content)
        for element in root.iter(self._homeset_tag):
            for homeset in element.iter():
                if homeset.tag.endswith('href'):
                    yield homeset.text

    def _find_collections(self, home):
        """find all CalDAV collections under `home`"""

        headers = self.session.get_default_headers()
        headers['Depth'] = 1
        response = self.session.request('PROPFIND', home, headers=headers,
                                        data=self._collection_xml,
                                        is_subpath=False)
        root = etree.XML(response.content)
        for response in root.iter('{*}response'):
            prop = response.find('{*}propstat/{*}prop')
            if prop.find('{*}resourcetype/{*}' + self._resourcetype) is None:
                continue

            displayname = prop.find('{*}displayname')
            collection = {
                'href': response.find('{*}href').text,
                'displayname': '' if displayname is None else displayname.text
            }

            yield collection


class CalDiscover(Discover):
    _resourcetype = 'calendar'
    _homeset_xml = """
    <d:propfind xmlns:d="DAV:" xmlns:c="urn:ietf:params:xml:ns:caldav">
        <d:prop>
            <c:calendar-home-set />
        </d:prop>
    </d:propfind>
    """
    _homeset_tag = '{*}calendar-home-set'


class CardDiscover(Discover):
    _resourcetype = 'addressbook'
    _homeset_xml = """
    <d:propfind xmlns:d="DAV:" xmlns:c="urn:ietf:params:xml:ns:carddav">
        <d:prop>
            <c:addressbook-home-set />
        </d:prop>
    </d:propfind>
    """
    _homeset_tag = '{*}addressbook-home-set'


class DavSession(object):
    '''
    A helper class to connect to DAV servers.
    '''

    def __init__(self, url, username='', password='', verify=True, auth=None,
                 useragent=USERAGENT, verify_fingerprint=None,
                 dav_header=None):
        if username and not password:
            password = <PASSWORD>password(username, url)

        self._settings = {
            'verify': prepare_verify(verify),
            'auth': prepare_auth(auth, username, password),
            'verify_fingerprint': verify_fingerprint,
        }
        self.useragent = useragent
        self.url = url.rstrip('/') + '/'
        self.parsed_url = utils.urlparse.urlparse(self.url)
        self.dav_header = dav_header
        self._session = None

    def request(self, method, path, data=None, headers=None,
{'fraction_non_alphanumeric': 0.12195744680851064, 'fraction_numerical': 0.0023829787234042553, 'mean_word_length': 2.0356497029191423, 'pattern_counts': {'":': 0, '<': 48, '<?xml version=': 0, '>': 48, 'https://': 2, 'lorem ipsum': 0, 'www.': 0, 'xml': 30}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 14, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '3680225', 'n_tokens_mistral': 3485, 'n_tokens_neox': 3351, 'n_words': 777}