Example #1
0
 def calendar_home_set(self, url):
     if isinstance(url, CalendarSet):
         self._calendar_home_set = url
     else:
         if not is_url(url):
             self._calendar_home_set = CalendarSet(self.client, self.client.url.join(URL.objectify(url)))
         else:
             self.client.url = URL.objectify(url)
             self.client.handle = httplib.HTTPSConnection(self.client.url.hostname,
                                               self.client.url.port)
             self._calendar_home_set = CalendarSet(self.client, URL.objectify(url))
Example #2
0
    def __init__(self, client=None, url=None):
        """
        url input is for backward compatibility and should normally be avoided.

        If url is not given, deduct principal path as well as calendar home set path from doing propfinds.
        """
        self.client = client
        self._calendar_home_set = None
        ## backwards compatibility.
        if url is not None:
                self.url = client.url.join(URL.objectify(url))
        else:
            self.url = self.client.url
            cup = self.get_properties([dav.CurrentUserPrincipal()])
            self.url = self.client.url.join(URL.objectify(cup['{DAV:}current-user-principal']))
Example #3
0
    def __init__(self, client=None, url=None, parent=None, name=None, id=None, etag=None, **extra):
        """
        Default constructor.

        Parameters:
         * client: A DAVClient instance
         * url: The url for this object.  May be a full URL or a relative URL.
         * parent: The parent object - used when creating objects
         * name: A displayname
         * id: The resource id (UID for an Event)
        """

        if client is None and parent is not None:
            client = parent.client
        self.client = client
        self.parent = parent
        self.name = name
        self.id = id
        self.etag = etag
        self.extra_init_options = extra
        ## url may be a path relative to the caldav root
        if client and url:
            self.url = client.url.join(url)
        else:
            self.url = URL.objectify(url)
Example #4
0
    def __init__(self,
                 client=None,
                 url=None,
                 parent=None,
                 name=None,
                 id=None,
                 **extra):
        """
        Default constructor.

        Parameters:
         * client: A DAVClient instance
         * url: The url for this object.  May be a full URL or a relative URL.
         * parent: The parent object - used when creating objects
         * name: A displayname
         * id: The resource id (UID for an Event)
        """

        if client is None and parent is not None:
            client = parent.client
        self.client = client
        self.parent = parent
        self.name = name
        self.id = id
        self.extra_init_options = extra
        # url may be a path relative to the caldav root
        if client and url:
            self.url = client.url.join(url)
        else:
            self.url = URL.objectify(url)
Example #5
0
    def __init__(self, client=None, url=None):
        """
        url input is for backward compatibility and should normally be avoided.

        If url is not given, deduct principal path as well as calendar home set path from doing propfinds.
        """
        self.client = client
        self._calendar_home_set = None

        ## backwards compatibility.  
        if url is not None:
            self.url = client.url.join(URL.objectify(url))
        else:
            self.url = self.client.url
            cup = self.get_properties([dav.CurrentUserPrincipal()])
            self.url = self.client.url.join(URL.objectify(cup['{DAV:}current-user-principal']))
Example #6
0
    def children(self, type=None):
        """
        List children, using a propfind (resourcetype) on the parent object,
        at depth = 1.
        """
        c = []

        depth = 1
        properties = {}

        props = [dav.ResourceType(), ]
        response = self._query_properties(props, depth)

        for r in response.tree.findall(dav.Response.tag):
            # We use canonicalized urls to index children
            href = str(self.url.join(URL.objectify(r.find(dav.Href.tag).text)).canonical())
            assert(href)
            properties[href] = {}
            for p in props:
                t = r.find(".//" + p.tag)
                if len(list(t)) > 0:
                    if type is not None:
                        val = t.find(".//" + type)
                    else:
                        val = t.find(".//*")
                    if val is not None:
                        val = val.tag
                    else:
                        val = None
                else:
                    val = t.text
                properties[href][p.tag] = val

        for path in properties.keys():
            resource_type = properties[path][dav.ResourceType.tag]
            if resource_type == type or type is None:
                path = URL.objectify(path)

                ## TODO: investigate the RFCs thoroughly - why does a "get 
                ## members of this collection"-request also return the collection URL itself?  
                ## And why is the strip_trailing_slash-method needed?  The collection URL 
                ## should always end with a slash according to RFC 2518, section 5.2.
                if self.url.strip_trailing_slash() != path.strip_trailing_slash():
                    c.append((path, resource_type))

        return c
Example #7
0
    def request(self, url, method="GET", body="", headers={}):
        """
        Actually sends the request
        """

        # objectify the url
        url = URL.objectify(url)

        proxies = None
        if self.proxy is not None:
            proxies = {url.scheme: self.proxy}
            log.debug("using proxy - %s" % (proxies))

        # ensure that url is a unicode string
        url = str(url)

        combined_headers = dict(self.headers)
        combined_headers.update(headers)
        if body is None or body == "" and "Content-Type" in combined_headers:
            del combined_headers["Content-Type"]

        log.debug(
            "sending request - method={0}, url={1}, headers={2}\nbody:\n{3}"
            .format(method, url, combined_headers, body))
        auth = None
        if self.auth is None and self.username is not None:
            auth = requests.auth.HTTPDigestAuth(self.username, self.password)
        else:
            auth = self.auth

        r = requests.request(method, url, data=to_wire(body),
                             headers=combined_headers, proxies=proxies,
                             auth=auth, verify=self.ssl_verify_cert)
        response = DAVResponse(r)

        # If server supports BasicAuth and not DigestAuth, let's try again:
        if response.status == 401 and self.auth is None and auth is not None:
            auth = requests.auth.HTTPBasicAuth(self.username, self.password)
            r = requests.request(method, url, data=to_wire(body),
                                 headers=combined_headers, proxies=proxies,
                                 auth=auth, verify=self.ssl_verify_cert)
            response = DAVResponse(r)

        # this is an error condition the application wants to know
        if response.status == requests.codes.forbidden or \
                response.status == requests.codes.unauthorized:
            ex = error.AuthorizationError()
            ex.url = url
            ex.reason = response.reason
            raise ex

        # let's save the auth object and remove the user/pass information
        if not self.auth and auth:
            self.auth = auth
            del self.username
            del self.password

        return response
Example #8
0
    def __init__(self,
                 url,
                 proxy=None,
                 username=None,
                 password=None,
                 auth=None,
                 ssl_verify_cert=True,
                 timeout=None):
        """
        Sets up a HTTPConnection object towards the server in the url.
        Parameters:
         * url: A fully qualified url: `scheme://user:pass@hostname:port`
         * proxy: A string defining a proxy server: `hostname:port`
         * username and password should be passed as arguments or in the URL
         * auth and ssl_verify_cert is passed to requests.request.
         ** ssl_verify_cert can be the path of a CA-bundle or False.
        """

        self.session = requests.Session()

        log.debug("url: " + str(url))
        self.url = URL.objectify(url)
        self.timeout = timeout

        # Prepare proxy info
        if proxy is not None:
            self.proxy = proxy
            # requests library expects the proxy url to have a scheme
            if re.match('^.*://', proxy) is None:
                self.proxy = self.url.scheme + '://' + proxy

            # add a port is one is not specified
            # TODO: this will break if using basic auth and embedding
            # username:password in the proxy URL
            p = self.proxy.split(":")
            if len(p) == 2:
                self.proxy += ':8080'
            log.debug("init - proxy: %s" % (self.proxy))

        # Build global headers
        self.headers = {
            "User-Agent": "Mozilla/5.0",
            "Content-Type": "text/xml",
            "Accept": "text/xml, text/calendar"
        }
        if self.url.username is not None:
            username = unquote(self.url.username)
            password = unquote(self.url.password)

        self.username = username
        self.password = password
        self.auth = auth
        # TODO: it's possible to force through a specific auth method here,
        # but no test code for this.
        self.ssl_verify_cert = ssl_verify_cert
        self.url = self.url.unauth()
        log.debug("self.url: " + str(url))
Example #9
0
 def calendar_home_set(self, url):
     if isinstance(url, CalendarSet):
         self._calendar_home_set = url
         return
     sanitized_url = URL.objectify(url)
     if sanitized_url.hostname and sanitized_url.hostname != self.client.url.hostname:
         ## icloud (and others?) having a load balanced system, where each principal resides on one named host
         self.client.url = sanitized_url
     self._calendar_home_set = CalendarSet(self.client, self.client.url.join(sanitized_url))
Example #10
0
 def calendar_home_set(self, url):
     if isinstance(url, CalendarSet):
         self._calendar_home_set = url
         return
     sanitized_url = URL.objectify(url)
     if sanitized_url.hostname and sanitized_url.hostname != self.client.url.hostname:
         ## icloud (and others?) having a load balanced system, where each principal resides on one named host
         self.client.url = sanitized_url
     self._calendar_home_set = CalendarSet(
         self.client, self.client.url.join(sanitized_url))
Example #11
0
    def __init__(self, client=None, url=None):
        """
        Returns a Principal.

        Parameters:
         * client: a DAVClient() oject
         * url: Deprecated - for backwards compatibility purposes only.

        If url is not given, deduct principal path as well as calendar home set path from doing propfinds.
        """
        self.client = client
        self._calendar_home_set = None

        ## backwards compatibility.  
        if url is not None:
            self.url = client.url.join(URL.objectify(url))
        else:
            self.url = self.client.url
            cup = self.get_properties([dav.CurrentUserPrincipal()])
            self.url = self.client.url.join(URL.objectify(cup['{DAV:}current-user-principal']))
Example #12
0
    def __init__(self, client=None, url=None):
        """
        Returns a Principal.

        Parameters:
         * client: a DAVClient() oject
         * url: Deprecated - for backwards compatibility purposes only.

        If url is not given, deduct principal path as well as calendar home set path from doing propfinds.
        """
        self.client = client
        self._calendar_home_set = None

        ## backwards compatibility.
        if url is not None:
            self.url = client.url.join(URL.objectify(url))
        else:
            self.url = self.client.url
            cup = self.get_properties([dav.CurrentUserPrincipal()])
            self.url = self.client.url.join(
                URL.objectify(cup['{DAV:}current-user-principal']))
Example #13
0
    def children(self, type=None):
        """
        List children, using a propfind (resourcetype) on the parent object,
        at depth = 1.
        """
        c = []

        depth = 1
        properties = {}

        props = [dav.ResourceType(), ]
        response = self._query_properties(props, depth)

        for r in response.tree.findall(dav.Response.tag):
            # We use canonicalized urls to index children
            href = str(self.url.join(URL.objectify(r.find(dav.Href.tag).text)).canonical())
            assert(href)
            properties[href] = {}
            for p in props:
                t = r.find(".//" + p.tag)
                if len(list(t)) > 0:
                    if type is not None:
                        val = t.find(".//" + type)
                    else:
                        val = t.find(".//*")
                    if val is not None:
                        val = val.tag
                    else:
                        val = None
                else:
                    val = t.text
                properties[href][p.tag] = val

        for path in properties.keys():
            resource_type = properties[path][dav.ResourceType.tag]
            if resource_type == type or type is None:
                if self.url != path:
                    c.append((URL.objectify(path), resource_type))

        return c
Example #14
0
    def save(self):
        """
        The save method for a calendar is only used to create it, for now.
        We know we have to create it when we don't have a url.

        Returns:
         * self
        """
        if self.url is None:
            self._create(name=self.name, id=self.id, **self.extra_init_options)
            if not self.url.endswith('/'):
                self.url = URL.objectify(str(self.url) + '/')
        return self
Example #15
0
    def save(self):
        """
        The save method for a calendar is only used to create it, for now.
        We know we have to create it when we don't have a url.

        Returns:
         * self
        """
        if self.url is None:
            self._create(self.name, self.id)
            if not self.url.endswith('/'):
                self.url = URL.objectify(str(self.url) + '/')
        return self
Example #16
0
class DAVClientMock:
    """Mock class for DAVClient instance."""

    url = URL.objectify("http://localhost")

    def mkcalendar(self, url):
        return True

    def delete(self, url):
        return True

    def proppatch(self, url, body, dummy=None):
        return Response(200)

    def request(self, url, method="GET", body="", headers={}):
        return Response(200)
Example #17
0
 def calendar_home_set(self, url):
     if isinstance(url, CalendarSet):
         self._calendar_home_set = url
         return
     sanitized_url = URL.objectify(url)
     ## TODO: sanitized_url should never be None, this needs more
     ## research.  added here as it solves real-world issues, ref
     ## https://github.com/python-caldav/caldav/pull/56
     if sanitized_url is not None:
         if (sanitized_url.hostname
                 and sanitized_url.hostname != self.client.url.hostname):
             # icloud (and others?) having a load balanced system,
             # where each principal resides on one named host
             self.client.url = sanitized_url
     self._calendar_home_set = CalendarSet(
         self.client, self.client.url.join(sanitized_url))
Example #18
0
    def __init__(self, url, proxy=None, username=None, password=None, headers=None):
        """
        Sets up a HTTPConnection object towards the server in the url.
        Parameters:
         * url: A fully qualified url: `scheme://user:pass@hostname:port`
         * proxy: A string defining a proxy server: `hostname:port`
        """

        self.url = URL.objectify(url)

        # Prepare proxy info
        if proxy is not None:
            # TODO: this will break if using basic auth and embedding
            # username:password in the proxy URL
            self.proxy = proxy.split(":")
            if len(self.proxy) == 1:
                self.proxy.append(8080)
            else:
                self.proxy[1] = int(self.proxy[1])

        # Build global headers
        self.headers = {"User-Agent": "Mozilla/5.0",
                        "Content-Type": "text/xml",
                        "Accept": "text/xml"}
        if headers:
            self.headers.update(headers)
        if self.url.username is not None:
            username = urllib.unquote(self.url.username)
            password = urllib.unquote(self.url.password)
        if username is not None:
            hash = (("%s:%s" % (username, password))
                    .encode('base64')[:-1])
            self.headers['authorization'] = "Basic %s" % hash

        # Connection proxy
        if self.proxy is not None:
            self.handle = httplib.HTTPConnection(*self.proxy)
        # direct, https
        # TODO: we shouldn't use SSL on http://weird.server.example.com:443/
        elif self.url.port == 443 or self.url.scheme == 'https':
            self.handle = httplib.HTTPSConnection(self.url.hostname,
                                                  self.url.port)
        # direct, http
        else:
            self.handle = httplib.HTTPConnection(self.url.hostname,
                                                 self.url.port)
        self.url = self.url.unauth()
Example #19
0
    def object_by_uid(self, uid, comp_filter=None):
        """
        Get one event from the calendar.

        Parameters:
         * uid: the event uid

        Returns:
         * Event() or None
        """
        data = cdav.CalendarData()
        prop = dav.Prop() + data

        query = cdav.TextMatch(uid)
        query = cdav.PropFilter("UID") + query
        if comp_filter:
            query = comp_filter + query
        vcalendar = cdav.CompFilter("VCALENDAR") + query
        filter = cdav.Filter() + vcalendar

        root = cdav.CalendarQuery() + [prop, filter]

        response = self._query(root, 1, 'report')

        if response.status == 404:
            raise error.NotFoundError("%s not found on server" % uid)
        elif response.status == 400:
            raise error.ReportError(errmsg(response))

        items_found = response.tree.findall(".//" + dav.Response.tag)
        for r in items_found:
            href = unquote(r.find(".//" + dav.Href.tag).text)
            data = unquote(r.find(".//" + cdav.CalendarData.tag).text)
            # Ref Lucas Verney, we've actually done a substring search, if the
            # uid given in the query is short (i.e. just "0") we're likely to
            # get false positives back from the server.
            #
            # Long uids are folded, so splice the lines together here before
            # attempting a match.
            item_uid = re.search(r'\nUID:((.|\n[ \t])*)\n', data)
            if (not item_uid or
                    re.sub(r'\n[ \t]', '', item_uid.group(1)) != uid):
                continue
            return self._calendar_comp_class_by_data(data)(
                self.client, url=URL.objectify(href), data=data, parent=self)
        raise error.NotFoundError("%s not found on server" % uid)
Example #20
0
    def _create(self, data, id=None, path=None):
        if id is None and path is not None and str(path).endswith('.ics'):
            id = re.search('(/|^)([^/]*).ics',str(path)).group(2)
        elif id is None:
            id = self.instance.vevent.uid.value
        if path is None:
            path = id + ".ics"
        path = self.parent.url.join(path)
        r = self.client.put(path, data,
                            {"Content-Type": 'text/calendar; charset="utf-8"'})

        if r.status == 302:
            path = [x[1] for x in r.headers if x[0]=='location'][0]
        elif not (r.status in (204, 201)):
            raise error.PutError(r.raw)

        self.url = URL.objectify(path)
        self.id = id
Example #21
0
    def _create(self, data, id=None, path=None):
        if id is None and path is not None and str(path).endswith('.ics'):
            id = re.search('(/|^)([^/]*).ics',str(path)).group(2)
        elif id is None:
            id = self.instance.vevent.uid.value
        if path is None:
            path = id + ".ics"
        path = self.parent.url.join(path)
        r = self.client.put(path, data,
                            {"Content-Type": 'text/calendar; charset="utf-8"'})

        if r.status == 302:
            path = [x[1] for x in r.headers if x[0]=='location'][0]
        elif not (r.status in (204, 201)):
            raise error.PutError(r.raw)

        self.url = URL.objectify(path)
        self.id = id
Example #22
0
    def object_by_uid(self, uid, comp_filter=None):
        """
        Get one event from the calendar.

        Parameters:
         * uid: the event uid

        Returns:
         * Event() or None
        """
        data = cdav.CalendarData()
        prop = dav.Prop() + data

        query = cdav.TextMatch(uid)
        query = cdav.PropFilter("UID") + query
        if comp_filter:
            query = comp_filter + query
        vcalendar = cdav.CompFilter("VCALENDAR") + query
        filter = cdav.Filter() + vcalendar

        root = cdav.CalendarQuery() + [prop, filter]

        response = self._query(root, 1, 'report')

        if response.status == 404:
            raise error.NotFoundError(errmsg(response))
        elif response.status == 400:
            raise error.ReportError(errmsg(response))

        items_found = response.tree.findall(".//" + dav.Response.tag)
        for r in items_found:
            href = unquote(r.find(".//" + dav.Href.tag).text)
            data = unquote(r.find(".//" + cdav.CalendarData.tag).text)
            # Ref Lucas Verney, we've actually done a substring search, if the
            # uid given in the query is short (i.e. just "0") we're likely to
            # get false positives back from the server.
            if not "\nUID:%s\n" % uid in data:
                # TODO: optimistic assumption, uid line is not folded.  We
                # need to unfold the content to be 100% sure that we won't
                # filter away true positives here.
                continue
            return self._calendar_comp_class_by_data(data)(
                self.client, url=URL.objectify(href), data=data, parent=self)
        raise error.NotFoundError(errmsg(response))
Example #23
0
    def _create(self, data, id=None, path=None):
        if id is None and path is not None and str(path).endswith('.ics'):
            id = re.search('(/|^)([^/]*).ics', str(path)).group(2)
        elif id is None:
            for obj_type in ('vevent', 'vtodo', 'vjournal', 'vfreebusy'):
                obj = None
                if hasattr(self.vobject_instance, obj_type):
                    obj = getattr(self.vobject_instance, obj_type)
                elif self.vobject_instance.name.lower() == obj_type:
                    obj = self.vobject_instance
                if obj is not None:
                    try:
                        id = obj.uid.value
                    except AttributeError:
                        id = str(uuid.uuid1())
                        obj.add('uid')
                        obj.uid.value = id
                    break
        else:
            for obj_type in ('vevent', 'vtodo', 'vjournal', 'vfreebusy'):
                obj = None
                if hasattr(self.vobject_instance, obj_type):
                    obj = getattr(self.vobject_instance, obj_type)
                elif self.vobject_instance.name.lower() == obj_type:
                    obj = self.vobject_instance
                if obj is not None:
                    if not hasattr(obj, 'uid'):
                        obj.add('uid')
                    obj.uid.value = id
                    break
        if path is None:
            path = quote(id) + ".ics"
        path = self.parent.url.join(path)
        r = self.client.put(path, data,
                            {"Content-Type": 'text/calendar; charset="utf-8"'})

        if r.status == 302:
            path = [x[1] for x in r.headers if x[0] == 'location'][0]
        elif not (r.status in (204, 201)):
            raise error.PutError(errmsg(r))

        self.url = URL.objectify(path)
        self.id = id
Example #24
0
    def __init__(self, client=None, url=None, parent=None, name=None, id=None, etag=None):
        """
        Default constructor.

        Parameters:
         * client: A DAVClient instance
         * url: The url for this object
         * parent: The parent object - used when creating objects
         * name: A displayname
         * id: The resource id (UID for an Event)
        """

        if client is None and parent is not None:
            client = parent.client
        self.client = client
        self.parent = parent
        self.name = name
        self.id = id
        self.url = URL.objectify(url)
Example #25
0
    def __init__(self, url, proxy=None, username=None, password=None, auth=None, ssl_verify_cert=True):
        """
        Sets up a HTTPConnection object towards the server in the url.
        Parameters:
         * url: A fully qualified url: `scheme://user:pass@hostname:port`
         * proxy: A string defining a proxy server: `hostname:port`
         * username and password should be passed as arguments or in the URL
         * auth and ssl_verify_cert is passed to requests.request.  
         ** ssl_verify_cert can be the path of a CA-bundle or False.
        """

        log.debug("url: " + str(url))
        self.url = URL.objectify(url)

        # Prepare proxy info
        if proxy is not None:
            self.proxy = proxy
            if re.match('^.*://', proxy) is None:  # requests library expects the proxy url to have a scheme
                self.proxy = self.url.scheme + '://' + proxy

            # add a port is one is not specified
            # TODO: this will break if using basic auth and embedding 
            # username:password in the proxy URL
            p = self.proxy.split(":")
            if len(p) == 2:
                self.proxy += ':8080'
            log.debug("init - proxy: %s" % (self.proxy))

        # Build global headers
        self.headers = {"User-Agent": "Mac+OS+X/10.11.2 (15C50) accountsd/113",
                        "Content-Type": "text/xml",
                        "Accept": "text/xml"}
        if self.url.username is not None:
            username = unquote(self.url.username)
            password = unquote(self.url.password)

        self.username = username
        self.password = password
        self.auth = auth ## TODO: it's possible to force through a specific auth method here, but no test code for this.
        self.ssl_verify_cert = ssl_verify_cert
        self.url = self.url.unauth()
        log.debug("self.url: " + str(url))
Example #26
0
    def event_by_uid(self, uid):
        """
        Get one event from the calendar.

        Parameters:
         * uid: the event uid

        Returns:
         * Event() or None
        """
        e = None
        data = cdav.CalendarData()
        etag = dav.ETag()
        prop = dav.Prop() + etag + data

        match = cdav.TextMatch(uid)
        propf = cdav.PropFilter("UID") + match
        vevent = cdav.CompFilter("VEVENT") + propf
        vcalendar = cdav.CompFilter("VCALENDAR") + vevent
        filter = cdav.Filter() + vcalendar

        root = cdav.CalendarQuery() + [prop, filter]

        q = etree.tostring(root.xmlelement(), encoding="utf-8",
                           xml_declaration=True)
        response = self.client.report(self.url.path, q, 1)

        if response.status == 404:
            raise error.NotFoundError(response.raw)
        elif response.status == 400:
            raise error.ReportError(response.raw)

        r = response.tree.find(".//" + dav.Response.tag)
        if r is not None:
            href = URL.objectify(r.find(".//" + dav.Href.tag).text)
            data = r.find(".//" + cdav.CalendarData.tag).text
            etag = r.find(".//" + dav.ETag.tag).text
            e = Event(self.client, url=href, data=data, etag=etag, parent=self)
        else:
            raise error.NotFoundError(response.raw)

        return e
Example #27
0
    def event_by_uid(self, uid):
        """
        Get one event from the calendar.

        Parameters:
         * uid: the event uid

        Returns:
         * Event() or None
        """
        e = None

        data = cdav.CalendarData()
        prop = dav.Prop() + data

        match = cdav.TextMatch(uid)
        propf = cdav.PropFilter("UID") + match
        vevent = cdav.CompFilter("VEVENT") + propf
        vcalendar = cdav.CompFilter("VCALENDAR") + vevent
        filter = cdav.Filter() + vcalendar

        root = cdav.CalendarQuery() + [prop, filter]

        q = etree.tostring(root.xmlelement(), encoding="utf-8",
                           xml_declaration=True)
        response = self.client.report(self.url, q, 1)

        if response.status == 404:
            raise error.NotFoundError(response.raw)
        elif response.status == 400:
            raise error.ReportError(response.raw)
            
        r = response.tree.find(".//" + dav.Response.tag)
        if r is not None:
            href = URL.objectify(r.find(".//" + dav.Href.tag).text)
            data = r.find(".//" + cdav.CalendarData.tag).text
            e = Event(self.client, url=href, data=data, parent=self)
        else:
            raise error.NotFoundError(response.raw)

        return e
Example #28
0
    def date_search(self, start, end=None):
        """
        Search events by date in the calendar. Recurring events are expanded
        if they have an occurence during the specified time frame.

        Parameters:
         * start = datetime.today().
         * end = same as above.

        Returns:
         * [Event(), ...]
        """
        matches = []

        # build the request
        expand = cdav.Expand(start, end)
        data = cdav.CalendarData() + expand
        prop = dav.Prop() + data

        range = cdav.TimeRange(start, end)
        vevent = cdav.CompFilter("VEVENT") + range
        vcalendar = cdav.CompFilter("VCALENDAR") + vevent
        filter = cdav.Filter() + vcalendar

        root = cdav.CalendarQuery() + [prop, filter]

        q = etree.tostring(root.xmlelement(), encoding="utf-8",
                           xml_declaration=True)
        response = self.client.report(self.url, q, 1)
        for r in response.tree.findall(".//" + dav.Response.tag):
            status = r.find(".//" + dav.Status.tag)
            if status.text.endswith("200 OK"):
                href = URL.objectify(r.find(dav.Href.tag).text)
                href = self.url.join(href)
                data = r.find(".//" + cdav.CalendarData.tag).text
                e = Event(self.client, url=href, data=data, parent=self)
                matches.append(e)
            else:
                raise error.ReportError(response.raw)

        return matches
Example #29
0
    def request(self, url, method="GET", body="", headers={}):
        """
        Actually sends the request
        """
        url = URL.objectify(url)
        if self.proxy is not None:
            url = "%s://%s:%s%s" % (self.url.scheme, self.url.hostname,
                                    self.url.port, url.path)

        combined_headers = self.headers
        combined_headers.update(headers)
        if body is None or body == "" and "Content-Type" in combined_headers:
            del combined_headers["Content-Type"]

        if isinstance(body, unicode):
            body = body.encode('utf-8')

        try:
            logging.debug("########## SENDING REQUEST ###########")
            logging.debug("\nMETHOD=%s, \nURL=%s, \nHEADERS=%s\nBODY:\n%s" % (method, url, combined_headers, body))
            self.handle.request(method, url, body, combined_headers)
            response = DAVResponse(self.handle.getresponse())
        except httplib.BadStatusLine:
            # Try to reconnect
            self.handle.close()
            self.handle.connect()

            ## TODO: we're missing test code on this.  (will need to
            ## mock up a server to test this)
            self.handle.request(method, url, body, combined_headers)
            response = DAVResponse(self.handle.getresponse(n))

        # this is an error condition the application wants to know
        if response.status == httplib.FORBIDDEN or \
                response.status == httplib.UNAUTHORIZED:
            ex = error.AuthorizationError()
            ex.url = url
            ex.reason = response.reason
            raise ex

        return response
Example #30
0
    def date_search(self, start, end=None):
        """
        Search events by date in the calendar. Recurring events are expanded
        if they have an occurence during the specified time frame.

        Parameters:
         * start = datetime.today().
         * end = same as above.

        Returns:
         * [Event(), ...]
        """
        matches = []

        # build the request
        expand = cdav.Expand(start, end)
        data = cdav.CalendarData() + expand
        prop = dav.Prop() + data

        range = cdav.TimeRange(start, end)
        vevent = cdav.CompFilter("VEVENT") + range
        vcalendar = cdav.CompFilter("VCALENDAR") + vevent
        filter = cdav.Filter() + vcalendar

        root = cdav.CalendarQuery() + [prop, filter]

        q = etree.tostring(root.xmlelement(), encoding="utf-8",
                           xml_declaration=True)
        response = self.client.report(self.url.path, q, 1)
        for r in response.tree.findall(".//" + dav.Response.tag):
            status = r.find(".//" + dav.Status.tag)
            if status.text.endswith("200 OK"):
                href = URL.objectify(r.find(dav.Href.tag).text)
                href = self.url.join(href)
                data = r.find(".//" + cdav.CalendarData.tag).text
                e = Event(self.client, url=href, data=data, parent=self)
                matches.append(e)
            else:
                raise error.ReportError(response.raw)

        return matches
Example #31
0
    def _create(self, data, id=None, path=None):
        if id is None and path is not None and str(path).endswith('.ics'):
            id = re.search('(/|^)([^/]*).ics', str(path)).group(2)
        elif id is None:
            for obj_type in ('vevent', 'vtodo', 'vjournal', 'vfreebusy'):
                obj = None
                for subcomp in self.instance.subcomponents:
                    if subcomp.name.lower() == obj_type:
                        obj = subcomp
                        break
                if obj is None and self.instance.name.lower == obj_type:
                    obj = self.instance
                if obj is not None:
                    id = obj["uid"]
                    break
        else:
            for obj_type in ('vevent', 'vtodo', 'vjournal', 'vfreebusy'):
                obj = None
                for subcomp in self.instance.subcomponents:
                    if subcomp.name.lower() == obj_type:
                        obj = subcomp
                        break
                if obj is None and self.instance.name.lower == obj_type:
                    obj = self.instance
                if obj is not None:
                    obj.add("uid", id)
                    break
        if path is None:
            path = id + ".ics"
        path = self.parent.url.join(path)
        r = self.client.put(path, data,
                            {"Content-Type": 'text/calendar; charset="utf-8"'})

        if r.status == 302:
            path = [x[1] for x in r.headers if x[0] == 'location'][0]
        elif not (r.status in (204, 201)):
            raise error.PutError(errmsg(r))

        self.url = URL.objectify(path)
        self.id = id
Example #32
0
    def object_by_uid(self, uid, comp_filter=None):
        """
        Get one event from the calendar.

        Parameters:
         * uid: the event uid

        Returns:
         * Event() or None
        """
        data = cdav.CalendarData()
        prop = dav.Prop() + data

        query = cdav.TextMatch(uid)
        query = cdav.PropFilter("UID") + query
        if comp_filter:
            query = comp_filter + query
        else:
            raise Exception("Need a comp_filter")
        vcalendar = cdav.CompFilter("VCALENDAR") + query
        filter = cdav.Filter() + vcalendar

        root = cdav.CalendarQuery() + [prop, filter]

        response = self._query(root, 1, 'report')

        if response.status == 404:
            raise error.NotFoundError(response.raw)
        elif response.status == 400:
            raise error.ReportError(response.raw)

        r = response.tree.find(".//" + dav.Response.tag)
        if r is not None:
            href = r.find(".//" + dav.Href.tag).text
            data = r.find(".//" + cdav.CalendarData.tag).text
            return self._calendar_comp_class_by_data(data)(
                self.client, url=URL.objectify(href), data=data, parent=self)
        else:
            raise error.NotFoundError(response.raw)
Example #33
0
    def children(self, type=None):
        """
        List children, using a propfind (resourcetype) on the parent object,
        at depth = 1.
        """
        c = []

        depth = 1
        properties = {}

        props = [dav.ResourceType(), ]
        if type:
            props.append(dav.DisplayName())
        response = self._query_properties(props, depth)

        for r in response.tree.findall(dav.Response.tag):
            # We use canonicalized urls to index children
            try:
                href = str(self.url.join(URL.objectify(r.find(dav.Href.tag).text)).canonical())
                assert(href)
                properties[href] = {}
                for p in props:
                    t = r.find(".//" + p.tag)
                    if len(list(t)) > 0:
                        if type is not None:
                            val = t.find(".//" + type)
                        else:
                            val = t.find(".//*")
                        if val is not None:
                            val = val.tag
                        else:
                            val = None
                    else:
                        val = t.text
                    properties[href][p.tag] = val
            except Exception , e:
                    logging.info("error %s" % e)
Example #34
0
    def object_by_uid(self, uid, comp_filter=None):
        """
        Get one event from the calendar.

        Parameters:
         * uid: the event uid

        Returns:
         * Event() or None
        """
        data = cdav.CalendarData()
        prop = dav.Prop() + data

        query = cdav.TextMatch(uid)
        query = cdav.PropFilter("UID") + query
        if comp_filter:
            query = comp_filter + query
        vcalendar = cdav.CompFilter("VCALENDAR") + query
        filter = cdav.Filter() + vcalendar

        root = cdav.CalendarQuery() + [prop, filter]

        response = self._query(root, 1, 'report')

        if response.status == 404:
            raise error.NotFoundError(response.raw)
        elif response.status == 400:
            raise error.ReportError(response.raw)
            
        r = response.tree.find(".//" + dav.Response.tag)
        if r is not None:
            href = r.find(".//" + dav.Href.tag).text
            data = r.find(".//" + cdav.CalendarData.tag).text
            return self._calendar_comp_class_by_data(data)(self.client, url=URL.objectify(href), data=data, parent=self)
        else:
            raise error.NotFoundError(response.raw)
Example #35
0
    def request(self, url, method="GET", body="", headers={}):
        """
        Actually sends the request
        """

        # objectify the url
        url = URL.objectify(url)

        proxies = None
        if self.proxy is not None:
            proxies = {url.scheme: self.proxy}
            log.debug("using proxy - %s" % (proxies))

        # ensure that url is a unicode string
        url = str(url)

        combined_headers = self.headers
        combined_headers.update(headers)
        if body is None or body == "" and "Content-Type" in combined_headers:
            del combined_headers["Content-Type"]

        log.debug("sending request - method={0}, url={1}, headers={2}\nbody:\n{3}".format(method, url, combined_headers, body))
        auth = None
        if self.auth is None and self.username is not None:
            auth = requests.auth.HTTPDigestAuth(self.username, self.password)
        else:
            auth = self.auth

        r = requests.request(method, url, data=to_wire(body), headers=combined_headers, proxies=proxies, auth=auth, verify=self.ssl_verify_cert)

        response = DAVResponse(r)

        ## If server supports BasicAuth and not DigestAuth, let's try again:
        if response.status == 401 and self.auth is None and auth is not None:
            # if there were redirects, we need to continue with them
            if r.history:
                url = r.url

            auth = requests.auth.HTTPBasicAuth(self.username, self.password)

            r = requests.request(method, url, data=to_wire(body), headers=combined_headers, proxies=proxies, auth=auth,
                                 verify=self.ssl_verify_cert)

            response = DAVResponse(r)

            if r.history:
                # requests do not redirect with body
                r = requests.request(method, r.url, data=to_wire(body), headers=combined_headers, proxies=proxies,
                                     auth=auth, verify=self.ssl_verify_cert)
                response = DAVResponse(r)

        self._last_response = r

        # this is an error condition the application wants to know
        if response.status == requests.codes.forbidden or \
                response.status == requests.codes.unauthorized:
            ex = error.AuthorizationError()
            ex.url = url
            ex.reason = response.reason
            raise ex

        ## let's save the auth object and remove the user/pass information
        if not self.auth and auth:
            self.auth = auth
            del self.username
            del self.password

        return response
Example #36
0
 def calendar_home_set(self, url):
     if isinstance(url, CalendarSet):
         self._calendar_home_set = url
     else:
         self._calendar_home_set = CalendarSet(self.client, self.client.url.join(URL.objectify(url)))
Example #37
0
    def testURL(self):
        """Excersising the URL class"""

        ## 1) URL.objectify should return a valid URL object almost no matter what's thrown in
        url0 = URL.objectify(None)
        url0b = URL.objectify("")
        url1 = URL.objectify(
            "http://*****:*****@www.example.com:8080/caldav.php/?foo=bar")
        url2 = URL.objectify(url1)
        url3 = URL.objectify("/bar")
        url4 = URL.objectify(urlparse(str(url1)))
        url5 = URL.objectify(urlparse("/bar"))

        ## 2) __eq__ works well
        assert_equal(url1, url2)
        assert_equal(url1, url4)
        assert_equal(url3, url5)

        ## 3) str will always return the URL
        assert_equal(
            str(url1),
            "http://*****:*****@www.example.com:8080/caldav.php/?foo=bar")
        assert_equal(str(url3), "/bar")
        assert_equal(
            str(url4),
            "http://*****:*****@www.example.com:8080/caldav.php/?foo=bar")
        assert_equal(str(url5), "/bar")

        ## 4) join method
        url6 = url1.join(url2)
        url7 = url1.join(url3)
        url8 = url1.join(url4)
        url9 = url1.join(url5)
        urlA = url1.join("someuser/calendar")
        urlB = url5.join(url1)
        assert_equal(url6, url1)
        assert_equal(url7, "http://*****:*****@www.example.com:8080/bar")
        assert_equal(url8, url1)
        assert_equal(url9, url7)
        assert_equal(
            urlA,
            "http://*****:*****@www.example.com:8080/caldav.php/someuser/calendar")
        assert_equal(urlB, url1)
        assert_raises(ValueError, url1.join, "http://www.google.com")

        ## 4b) join method, with URL as input parameter
        url6 = url1.join(URL.objectify(url2))
        url7 = url1.join(URL.objectify(url3))
        url8 = url1.join(URL.objectify(url4))
        url9 = url1.join(URL.objectify(url5))
        urlA = url1.join(URL.objectify("someuser/calendar"))
        urlB = url5.join(URL.objectify(url1))
        url6b = url6.join(url0)
        url6c = url6.join(url0b)
        url6d = url6.join(None)
        for url6alt in (url6b, url6c, url6d):
            assert_equal(url6, url6alt)
        assert_equal(url6, url1)
        assert_equal(url7, "http://*****:*****@www.example.com:8080/bar")
        assert_equal(url8, url1)
        assert_equal(url9, url7)
        assert_equal(
            urlA,
            "http://*****:*****@www.example.com:8080/caldav.php/someuser/calendar")
        assert_equal(urlB, url1)
        assert_raises(ValueError, url1.join, "http://www.google.com")

        ## 5) all urlparse methods will work.  always.
        assert_equal(url1.scheme, 'http')
        assert_equal(url2.path, '/caldav.php/')
        assert_equal(url7.username, 'foo')
        assert_equal(url5.path, '/bar')
        urlC = URL.objectify("https://www.example.com:443/foo")
        assert_equal(urlC.port, 443)

        ## 6) is_auth returns True if the URL contains a username.
        assert_equal(urlC.is_auth(), False)
        assert_equal(url7.is_auth(), True)

        ## 7) unauth() strips username/password
        assert_equal(url7.unauth(), 'http://www.example.com:8080/bar')
Example #38
0
    def testURL(self):
        """Excersising the URL class"""

        ## 1) URL.objectify should return a valid URL object almost no matter what's thrown in
        url0 = URL.objectify(None)
        url0b= URL.objectify("")
        url1 = URL.objectify("http://*****:*****@www.example.com:8080/caldav.php/?foo=bar")
        url2 = URL.objectify(url1)
        url3 = URL.objectify("/bar")
        url4 = URL.objectify(urlparse.urlparse(str(url1)))
        url5 = URL.objectify(urlparse.urlparse("/bar"))
    
        ## 2) __eq__ works well
        assert_equal(url1, url2)
        assert_equal(url1, url4)
        assert_equal(url3, url5)

        ## 3) str will always return the URL
        assert_equal(str(url1), "http://*****:*****@www.example.com:8080/caldav.php/?foo=bar")
        assert_equal(str(url3), "/bar")
        assert_equal(str(url4), "http://*****:*****@www.example.com:8080/caldav.php/?foo=bar")
        assert_equal(str(url5), "/bar")

        ## 4) join method
        url6 = url1.join(url2)
        url7 = url1.join(url3)
        url8 = url1.join(url4)
        url9 = url1.join(url5)
        urlA = url1.join("someuser/calendar")
        urlB = url5.join(url1)
        assert_equal(url6, url1)
        assert_equal(url7, "http://*****:*****@www.example.com:8080/bar")
        assert_equal(url8, url1)
        assert_equal(url9, url7)
        assert_equal(urlA, "http://*****:*****@www.example.com:8080/caldav.php/someuser/calendar")
        assert_equal(urlB, url1)
        assert_raises(ValueError, url1.join, "http://www.google.com")

        ## 4b) join method, with URL as input parameter
        url6 = url1.join(URL.objectify(url2))
        url7 = url1.join(URL.objectify(url3))
        url8 = url1.join(URL.objectify(url4))
        url9 = url1.join(URL.objectify(url5))
        urlA = url1.join(URL.objectify("someuser/calendar"))
        urlB = url5.join(URL.objectify(url1))
        url6b= url6.join(url0)
        url6c= url6.join(url0b)
        url6d= url6.join(None)
        for url6alt in (url6b, url6c, url6d):
            assert_equal(url6, url6alt)
        assert_equal(url6, url1)
        assert_equal(url7, "http://*****:*****@www.example.com:8080/bar")
        assert_equal(url8, url1)
        assert_equal(url9, url7)
        assert_equal(urlA, "http://*****:*****@www.example.com:8080/caldav.php/someuser/calendar")
        assert_equal(urlB, url1)
        assert_raises(ValueError, url1.join, "http://www.google.com")

        ## 5) all urlparse methods will work.  always.
        assert_equal(url1.scheme, 'http')
        assert_equal(url2.path, '/caldav.php/')
        assert_equal(url7.username, 'foo')
        assert_equal(url5.path, '/bar')
        urlC = URL.objectify("https://www.example.com:443/foo")
        assert_equal(urlC.port, 443)

        ## 6) is_auth returns True if the URL contains a username.  
        assert_equal(urlC.is_auth(), False)
        assert_equal(url7.is_auth(), True)

        ## 7) unauth() strips username/password
        assert_equal(url7.unauth(), 'http://www.example.com:8080/bar')
Example #39
0
 def calendar_home_set(self, url):
     if isinstance(url, CalendarSet):
         self._calendar_home_set = url
     else:
         self._calendar_home_set = CalendarSet(self.client, self.client.url.join(URL.objectify(url)))