Ejemplo n.º 1
0
def create_calendar(client, parent, name, id=None):
    """
    Create a new calendar with display name `name` in `parent`.
    """
    path = None
    if id is None:
        id = str(uuid.uuid1())

    name = dav.DisplayName(name)
    cal = cdav.CalendarCollection()
    coll = dav.Collection() + cal
    type = dav.ResourceType() + coll

    prop = dav.Prop() + [type, name]
    set = dav.Set() + prop

    mkcol = dav.Mkcol() + set

    q = etree.tostring(mkcol.xmlelement(),
                       encoding="utf-8",
                       xml_declaration=True)
    path = url.join(parent.url.path, id)

    r = client.mkcol(path, q)
    if r.status == 201:
        path = url.make(parent.url, path)
    else:
        raise error.MkcolError(r.raw)

    return (id, path)
Ejemplo n.º 2
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(), dav.DisplayName()]
        response = self._query_properties(props, depth)
        properties = self._handle_prop_response(
            response=response, props=props, type=type, what='tag')

        for path in list(properties.keys()):
            resource_type = properties[path][dav.ResourceType.tag]
            resource_name = properties[path][dav.DisplayName.tag]

            if resource_type == type or type is None:
                # 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() !=
                        self.url.join(path).strip_trailing_slash()):
                    c.append((self.url.join(path), resource_type,
                              resource_name))

        return c
Ejemplo n.º 3
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
Ejemplo n.º 4
0
def children(client, parent, type=None):
    """
    List children, using a propfind (resourcetype) on the parent object,
    at depth = 1.
    TODO: There should be a better way.
    """
    c = []

    response = get_properties(client, parent, [
        dav.ResourceType(),
    ], 1)
    for path in response.keys():
        if path != parent.url.path:
            resource_type = response[path][dav.ResourceType.tag]
            if resource_type == type or type is None:
                c.append((url.make(parent.url, path), resource_type))

    return c
Ejemplo n.º 5
0
    def _create(self, name, id=None, supported_calendar_component_set=None):
        """
        Create a new calendar with display name `name` in `parent`.
        """
        if id is None:
            id = str(uuid.uuid1())
        self.id = id

        path = self.parent.url.join(id)
        self.url = path

        ## TODO: mkcalendar seems to ignore the body on most servers?
        ## at least the name doesn't get set this way.
        ## zimbra gives 500 (!) if body is omitted ...

        cal = cdav.CalendarCollection()
        coll = dav.Collection() + cal
        type = dav.ResourceType() + coll

        prop = dav.Prop() + [
            type,
        ]
        if name:
            display_name = dav.DisplayName(name)
            prop += [
                display_name,
            ]
        if supported_calendar_component_set:
            sccs = cdav.SupportedCalendarComponentSet()
            for scc in supported_calendar_component_set:
                sccs += cdav.Comp(scc)
            prop += sccs
        set = dav.Set() + prop

        mkcol = cdav.Mkcalendar() + set

        r = self._query(root=mkcol,
                        query_method='mkcalendar',
                        url=path,
                        expected_return_value=201)

        if name:
            try:
                self.set_properties([display_name])
            except:
                self.delete()
                raise

        ## Special hack for Zimbra!  The calendar we've made exists at
        ## the specified URL, and we can do operations like ls, even
        ## PUT an event to the calendar.  Zimbra will enforce that the
        ## event uuid matches the event url, and return either 201 or
        ## 302 - but alas, try to do a GET towards the event and we
        ## get 404!  But turn around and replace the calendar ID with
        ## the calendar name in the URL and hey ... it works!

        ## TODO: write test cases for calendars with non-trivial
        ## names and calendars with names already matching existing
        ## calendar urls and ensure they pass.
        zimbra_url = self.parent.url.join(name)
        try:
            ret = self.client.request(zimbra_url)
            if ret.status == 404:
                raise error.NotFoundError
            ## insane server
            self.url = zimbra_url
        except error.NotFoundError:
            ## sane server
            pass
Ejemplo n.º 6
0
    def _create(self, name, id=None):
        """
        Create a new calendar with display name `name` in `parent`.
        """
        if id is None:
            id = str(uuid.uuid1())
        self.id = id
            
        path = self.parent.url.join(id)
        self.url = path

        ## TODO: mkcalendar seems to ignore the body on most servers?  
        ## at least the name doesn't get set this way.
        ## zimbra gives 500 (!) if body is omitted ...

        if name:
            display_name = dav.DisplayName(name)
        cal = cdav.CalendarCollection()
        coll = dav.Collection() + cal
        type = dav.ResourceType() + coll

        prop = dav.Prop() + [type,]
        if name:
            prop += [display_name,]
        set = dav.Set() + prop

        mkcol = cdav.Mkcalendar() + set

        q = etree.tostring(mkcol.xmlelement(), encoding="utf-8",
                           xml_declaration=True)

        r = self.client.mkcalendar(path, q)

        if r.status != 201:
            raise error.MkcalendarError(r.raw)

        if name:
            try:
                self.set_properties([display_name])
            except:
                self.delete()
                raise

        ## Special hack for Zimbra!  The calendar we've made exists at
        ## the specified URL, and we can do operations like ls, even
        ## PUT an event to the calendar.  Zimbra will enforce that the
        ## event uuid matches the event url, and return either 201 or
        ## 302 - but alas, try to do a GET towards the event and we
        ## get 404!  But turn around and replace the calendar ID with
        ## the calendar name in the URL and hey ... it works!  

        ## TODO: write test cases for calendars with non-trivial
        ## names and calendars with names already matching existing
        ## calendar urls and ensure they pass.
        zimbra_url = self.parent.url.join(name)
        try:
            ret = self.client.request(zimbra_url)
            if ret.status == 404:
                raise error.NotFoundError
            ## insane server
            self.url = zimbra_url
        except error.NotFoundError:
            ## sane server
            pass
Ejemplo n.º 7
0
    def test_xml_parsing(self):
        """
        DAVResponse has quite some code to parse the XML received from the
        server.  This test contains real XML received from various
        caldav servers, and the expected result from the parse
        methods.
        """
        xml = """
<multistatus xmlns="DAV:">
  <response xmlns="DAV:">
    <href>/</href>
    <propstat>
      <prop>
        <current-user-principal xmlns="DAV:">
          <href xmlns="DAV:">/17149682/principal/</href>
        </current-user-principal>
      </prop>
      <status>HTTP/1.1 200 OK</status>
    </propstat>
  </response>
</multistatus>
"""
        expected_result = {
            '/': {
                '{DAV:}current-user-principal': '/17149682/principal/'
            }
        }

        assert_equal(
            MockedDAVResponse(xml).expand_simple_props(
                props=[dav.CurrentUserPrincipal()]), expected_result)

        xml = """
<multistatus xmlns="DAV:">
  <response xmlns="DAV:">
    <href>/17149682/principal/</href>
    <propstat>
      <prop>
        <calendar-home-set xmlns="urn:ietf:params:xml:ns:caldav">
          <href xmlns="DAV:">https://p62-caldav.icloud.com:443/17149682/calendars/</href>
        </calendar-home-set>
      </prop>
      <status>HTTP/1.1 200 OK</status>
    </propstat>
  </response>
</multistatus>"""
        expected_result = {
            '/17149682/principal/': {
                '{urn:ietf:params:xml:ns:caldav}calendar-home-set':
                'https://p62-caldav.icloud.com:443/17149682/calendars/'
            }
        }
        assert_equal(
            MockedDAVResponse(xml).expand_simple_props(
                props=[cdav.CalendarHomeSet()]), expected_result)

        xml = """
<multistatus xmlns="DAV:">
  <response xmlns="DAV:">
    <href>/</href>
    <propstat>
      <prop>
        <current-user-principal xmlns="DAV:">
          <href xmlns="DAV:">/17149682/principal/</href>
        </current-user-principal>
      </prop>
      <status>HTTP/1.1 200 OK</status>
    </propstat>
  </response>
</multistatus>"""
        expected_result = {
            '/': {
                '{DAV:}current-user-principal': '/17149682/principal/'
            }
        }
        assert_equal(
            MockedDAVResponse(xml).expand_simple_props(
                props=[dav.CurrentUserPrincipal()]), expected_result)

        xml = """
<multistatus xmlns="DAV:">
  <response>
    <href>/17149682/calendars/testcalendar-84439d0b-ce46-4416-b978-7b4009122c64/</href>
    <propstat>
      <prop>
                </prop>
      <status>HTTP/1.1 200 OK</status>
    </propstat>
    <propstat>
      <prop>
        <calendar-data xmlns="urn:ietf:params:xml:ns:caldav"/>
      </prop>
      <status>HTTP/1.1 404 Not Found</status>
    </propstat>
  </response>
  <response>
    <href>/17149682/calendars/testcalendar-84439d0b-ce46-4416-b978-7b4009122c64/20010712T182145Z-123401%40example.com.ics</href>
    <propstat>
      <prop>
        <calendar-data xmlns="urn:ietf:params:xml:ns:caldav">BEGIN:VCALENDAR
VERSION:2.0
PRODID:-//Example Corp.//CalDAV Client//EN
BEGIN:VEVENT
UID:[email protected]
DTSTAMP:20060712T182145Z
DTSTART:20060714T170000Z
DTEND:20060715T040000Z
SUMMARY:Bastille Day Party
END:VEVENT
END:VCALENDAR
</calendar-data>
      </prop>
      <status>HTTP/1.1 200 OK</status>
    </propstat>
  </response>
</multistatus>
"""
        expected_result = {
            '/17149682/calendars/testcalendar-84439d0b-ce46-4416-b978-7b4009122c64/':
            {
                '{urn:ietf:params:xml:ns:caldav}calendar-data': None
            },
            '/17149682/calendars/testcalendar-84439d0b-ce46-4416-b978-7b4009122c64/[email protected]':
            {
                '{urn:ietf:params:xml:ns:caldav}calendar-data':
                'BEGIN:VCALENDAR\nVERSION:2.0\nPRODID:-//Example Corp.//CalDAV Client//EN\nBEGIN:VEVENT\nUID:[email protected]\nDTSTAMP:20060712T182145Z\nDTSTART:20060714T170000Z\nDTEND:20060715T040000Z\nSUMMARY:Bastille Day Party\nEND:VEVENT\nEND:VCALENDAR\n'
            }
        }
        assert_equal(
            MockedDAVResponse(xml).expand_simple_props(
                props=[cdav.CalendarData()]), expected_result)

        xml = """
<multistatus xmlns="DAV:">
  <response xmlns="DAV:">
    <href>/17149682/calendars/</href>
    <propstat>
      <prop>
        <resourcetype xmlns="DAV:">
          <collection/>
        </resourcetype>
        <displayname xmlns="DAV:">Ny Test</displayname>
      </prop>
      <status>HTTP/1.1 200 OK</status>
    </propstat>
  </response>
  <response xmlns="DAV:">
    <href>/17149682/calendars/06888b87-397f-11eb-943b-3af9d3928d42/</href>
    <propstat>
      <prop>
        <resourcetype xmlns="DAV:">
          <collection/>
          <calendar xmlns="urn:ietf:params:xml:ns:caldav"/>
        </resourcetype>
        <displayname xmlns="DAV:">calfoo3</displayname>
      </prop>
      <status>HTTP/1.1 200 OK</status>
    </propstat>
  </response>
  <response xmlns="DAV:">
    <href>/17149682/calendars/inbox/</href>
    <propstat>
      <prop>
        <resourcetype xmlns="DAV:">
          <collection/>
          <schedule-inbox xmlns="urn:ietf:params:xml:ns:caldav"/>
        </resourcetype>
      </prop>
      <status>HTTP/1.1 200 OK</status>
    </propstat>
    <propstat>
      <prop>
        <displayname xmlns="DAV:"/>
      </prop>
      <status>HTTP/1.1 404 Not Found</status>
    </propstat>
  </response>
  <response xmlns="DAV:">
    <href>/17149682/calendars/testcalendar-e2910e0a-feab-4b51-b3a8-55828acaa912/</href>
    <propstat>
      <prop>
        <resourcetype xmlns="DAV:">
          <collection/>
          <calendar xmlns="urn:ietf:params:xml:ns:caldav"/>
        </resourcetype>
        <displayname xmlns="DAV:">Yep</displayname>
      </prop>
      <status>HTTP/1.1 200 OK</status>
    </propstat>
  </response>
</multistatus>
"""
        expected_result = {
            '/17149682/calendars/': {
                '{DAV:}resourcetype': ['{DAV:}collection'],
                '{DAV:}displayname': 'Ny Test'
            },
            '/17149682/calendars/06888b87-397f-11eb-943b-3af9d3928d42/': {
                '{DAV:}resourcetype': [
                    '{DAV:}collection',
                    '{urn:ietf:params:xml:ns:caldav}calendar'
                ],
                '{DAV:}displayname':
                'calfoo3'
            },
            '/17149682/calendars/inbox/': {
                '{DAV:}resourcetype': [
                    '{DAV:}collection',
                    '{urn:ietf:params:xml:ns:caldav}schedule-inbox'
                ],
                '{DAV:}displayname':
                None
            },
            '/17149682/calendars/testcalendar-e2910e0a-feab-4b51-b3a8-55828acaa912/':
            {
                '{DAV:}resourcetype': [
                    '{DAV:}collection',
                    '{urn:ietf:params:xml:ns:caldav}calendar'
                ],
                '{DAV:}displayname':
                'Yep'
            }
        }
        assert_equal(
            MockedDAVResponse(xml).expand_simple_props(
                props=[dav.DisplayName()],
                multi_value_props=[dav.ResourceType()]), expected_result)

        xml = """
<multistatus xmlns="DAV:">
  <response xmlns="DAV:">
    <href>/17149682/calendars/testcalendar-f96b3bf0-09e1-4f3d-b891-3a25c99a2894/</href>
    <propstat>
      <prop>
        <getetag xmlns="DAV:">"kkkgopik"</getetag>
      </prop>
      <status>HTTP/1.1 200 OK</status>
    </propstat>
  </response>
  <response xmlns="DAV:">
    <href>/17149682/calendars/testcalendar-f96b3bf0-09e1-4f3d-b891-3a25c99a2894/1761bf8c-6363-11eb-8fe4-74e5f9bfd8c1.ics</href>
    <propstat>
      <prop>
        <getetag xmlns="DAV:">"kkkgorwx"</getetag>
      </prop>
      <status>HTTP/1.1 200 OK</status>
    </propstat>
  </response>
  <response xmlns="DAV:">
    <href>/17149682/calendars/testcalendar-f96b3bf0-09e1-4f3d-b891-3a25c99a2894/20010712T182145Z-123401%40example.com.ics</href>
    <propstat>
      <prop>
        <getetag xmlns="DAV:">"kkkgoqqu"</getetag>
      </prop>
      <status>HTTP/1.1 200 OK</status>
    </propstat>
  </response>
  <sync-token>HwoQEgwAAAh4yw8ntwAAAAAYAhgAIhUIopml463FieB4EKq9+NSn04DrkQEoAA==</sync-token>
</multistatus>
"""
        expected_results = {
            '/17149682/calendars/testcalendar-f96b3bf0-09e1-4f3d-b891-3a25c99a2894/':
            {
                '{DAV:}getetag': '"kkkgopik"',
                '{urn:ietf:params:xml:ns:caldav}calendar-data': None
            },
            '/17149682/calendars/testcalendar-f96b3bf0-09e1-4f3d-b891-3a25c99a2894/1761bf8c-6363-11eb-8fe4-74e5f9bfd8c1.ics':
            {
                '{DAV:}getetag': '"kkkgorwx"',
                '{urn:ietf:params:xml:ns:caldav}calendar-data': None
            },
            '/17149682/calendars/testcalendar-f96b3bf0-09e1-4f3d-b891-3a25c99a2894/[email protected]':
            {
                '{DAV:}getetag': '"kkkgoqqu"',
                '{urn:ietf:params:xml:ns:caldav}calendar-data': None
            }
        }