Beispiel #1
0
    def get_meta(self, key=None):
        logger.debug("get_meta")
        if not key:
            return self._meta_cache

        if self.deep_path >= 2:
            if not self._meta_cache:
                try:
                    collection = DBCollection.objects.get(
                        name=self.attributes[1])

                    self._meta_cache = collection.tags
                    self._last_modified = collection.updated
                except DBCollection.DoesNotExist:
                    self._last_modified = None
                try:
                    check_and_sanitize_props(self._meta_cache)
                except ValueError as e:
                    raise RuntimeError(
                        "Failed to load properties of collection "
                        "%r: %s" % (self.path, e)) from e
        else:
            self._meta_cache = {}

        return self._meta_cache.get(key)
Beispiel #2
0
def proppatch(base_prefix, path, xml_request, collection):
    """Read and answer PROPPATCH requests.

    Read rfc4918-9.2 for info.

    """
    props_to_set = props_from_request(xml_request, actions=("set",))
    props_to_remove = props_from_request(xml_request, actions=("remove",))

    multistatus = ET.Element(_tag("D", "multistatus"))
    response = ET.Element(_tag("D", "response"))
    multistatus.append(response)

    href = ET.Element(_tag("D", "href"))
    href.text = _href(base_prefix, path)
    response.append(href)

    new_props = collection.get_meta()
    for short_name, value in props_to_set.items():
        new_props[short_name] = value
        _add_propstat_to(response, short_name, 200)
    for short_name in props_to_remove:
        try:
            del new_props[short_name]
        except KeyError:
            pass
        _add_propstat_to(response, short_name, 200)
    storage.check_and_sanitize_props(new_props)
    collection.set_meta_all(new_props)

    return multistatus
Beispiel #3
0
 def do_MKCOL(self, environ, base_prefix, path, user):
     """Manage MKCOL request."""
     if not self.Rights.authorized(user, path, "w"):
         return NOT_ALLOWED
     try:
         xml_content = self._read_xml_content(environ)
     except RuntimeError as e:
         self.logger.warning(
             "Bad MKCOL request on %r: %s", path, e, exc_info=True)
         return BAD_REQUEST
     except socket.timeout as e:
         self.logger.debug("client timed out", exc_info=True)
         return REQUEST_TIMEOUT
     with self.Collection.acquire_lock("w", user):
         item = next(self.Collection.discover(path), None)
         if item:
             return WEBDAV_PRECONDITION_FAILED
         props = xmlutils.props_from_request(xml_content)
         try:
             storage.check_and_sanitize_props(props)
             self.Collection.create_collection(path, props=props)
         except ValueError as e:
             self.logger.warning(
                 "Bad MKCOL request on %r: %s", path, e, exc_info=True)
             return BAD_REQUEST
         return client.CREATED, {}, None
Beispiel #4
0
 def do_MKCOL(self, environ, base_prefix, path, user):
     """Manage MKCOL request."""
     if not self.Rights.authorized(user, path, "w"):
         return NOT_ALLOWED
     try:
         xml_content = self._read_xml_content(environ)
     except RuntimeError as e:
         self.logger.warning("Bad MKCOL request on %r: %s",
                             path,
                             e,
                             exc_info=True)
         return BAD_REQUEST
     except socket.timeout as e:
         self.logger.debug("client timed out", exc_info=True)
         return REQUEST_TIMEOUT
     with self.Collection.acquire_lock("w", user):
         item = next(self.Collection.discover(path), None)
         if item:
             return WEBDAV_PRECONDITION_FAILED
         props = xmlutils.props_from_request(xml_content)
         try:
             storage.check_and_sanitize_props(props)
             self.Collection.create_collection(path, props=props)
         except ValueError as e:
             self.logger.warning("Bad MKCOL request on %r: %s",
                                 path,
                                 e,
                                 exc_info=True)
             return BAD_REQUEST
         return client.CREATED, {}, None
Beispiel #5
0
def proppatch(base_prefix, path, xml_request, collection):
    """Read and answer PROPPATCH requests.

    Read rfc4918-9.2 for info.

    """
    props_to_set = props_from_request(xml_request, actions=("set",))
    props_to_remove = props_from_request(xml_request, actions=("remove",))

    multistatus = ET.Element(_tag("D", "multistatus"))
    response = ET.Element(_tag("D", "response"))
    multistatus.append(response)

    href = ET.Element(_tag("D", "href"))
    href.text = _href(base_prefix, path)
    response.append(href)

    new_props = collection.get_meta()
    for short_name, value in props_to_set.items():
        new_props[short_name] = value
        _add_propstat_to(response, short_name, 200)
    for short_name in props_to_remove:
        try:
            del new_props[short_name]
        except KeyError:
            pass
        _add_propstat_to(response, short_name, 200)
    storage.check_and_sanitize_props(new_props)
    collection.set_meta_all(new_props)

    return multistatus
Beispiel #6
0
 def do_MKCOL(self, environ, base_prefix, path, user):
     """Manage MKCOL request."""
     permissions = self.Rights.authorized(user, path, "Ww")
     if not permissions:
         return NOT_ALLOWED
     try:
         xml_content = self._read_xml_content(environ)
     except RuntimeError as e:
         logger.warning("Bad MKCOL request on %r: %s",
                        path,
                        e,
                        exc_info=True)
         return BAD_REQUEST
     except socket.timeout as e:
         logger.debug("client timed out", exc_info=True)
         return REQUEST_TIMEOUT
     # Prepare before locking
     props = xmlutils.props_from_request(xml_content)
     try:
         storage.check_and_sanitize_props(props)
     except ValueError as e:
         logger.warning("Bad MKCOL request on %r: %s",
                        path,
                        e,
                        exc_info=True)
         return BAD_REQUEST
     if (props.get("tag") and "w" not in permissions
             or not props.get("tag") and "W" not in permissions):
         return NOT_ALLOWED
     with self.Collection.acquire_lock("w", user):
         item = next(self.Collection.discover(path), None)
         if item:
             return METHOD_NOT_ALLOWED
         parent_path = storage.sanitize_path(
             "/%s/" % posixpath.dirname(path.strip("/")))
         parent_item = next(self.Collection.discover(parent_path), None)
         if not parent_item:
             return CONFLICT
         if (not isinstance(parent_item, storage.BaseCollection)
                 or parent_item.get_meta("tag")):
             return FORBIDDEN
         try:
             self.Collection.create_collection(path, props=props)
         except ValueError as e:
             logger.warning("Bad MKCOL request on %r: %s",
                            path,
                            e,
                            exc_info=True)
             return BAD_REQUEST
         return client.CREATED, {}, None
Beispiel #7
0
 def do_MKCALENDAR(self, environ, base_prefix, path, user):
     """Manage MKCALENDAR request."""
     if not self.Rights.authorized(user, path, "w"):
         return NOT_ALLOWED
     try:
         xml_content = self._read_xml_content(environ)
     except RuntimeError as e:
         logger.warning("Bad MKCALENDAR request on %r: %s",
                        path,
                        e,
                        exc_info=True)
         return BAD_REQUEST
     except socket.timeout as e:
         logger.debug("client timed out", exc_info=True)
         return REQUEST_TIMEOUT
     # Prepare before locking
     props = xmlutils.props_from_request(xml_content)
     props["tag"] = "VCALENDAR"
     # TODO: use this?
     # timezone = props.get("C:calendar-timezone")
     try:
         storage.check_and_sanitize_props(props)
     except ValueError as e:
         logger.warning("Bad MKCALENDAR request on %r: %s",
                        path,
                        e,
                        exc_info=True)
     with self.Collection.acquire_lock("w", user):
         item = next(self.Collection.discover(path), None)
         if item:
             return self._webdav_error_response("D",
                                                "resource-must-be-null")
         parent_path = storage.sanitize_path(
             "/%s/" % posixpath.dirname(path.strip("/")))
         parent_item = next(self.Collection.discover(parent_path), None)
         if not parent_item:
             return CONFLICT
         if (not isinstance(parent_item, storage.BaseCollection)
                 or parent_item.get_meta("tag")):
             return FORBIDDEN
         try:
             self.Collection.create_collection(path, props=props)
         except ValueError as e:
             logger.warning("Bad MKCALENDAR request on %r: %s",
                            path,
                            e,
                            exc_info=True)
             return BAD_REQUEST
         return client.CREATED, {}, None
Beispiel #8
0
    def do_PUT(self, environ, base_prefix, path, user):
        """Manage PUT request."""
        if not self._access(user, path, "w"):
            return NOT_ALLOWED
        try:
            content = self._read_content(environ)
        except RuntimeError as e:
            self.logger.warning("Bad PUT request on %r: %s",
                                path,
                                e,
                                exc_info=True)
            return BAD_REQUEST
        except socket.timeout:
            self.logger.debug("client timed out", exc_info=True)
            return REQUEST_TIMEOUT
        parent_path = storage.sanitize_path("/%s/" %
                                            posixpath.dirname(path.strip("/")))
        item = next(self.Collection.discover(path), None)
        parent_item = next(self.Collection.discover(parent_path), None)
        if not parent_item:
            return CONFLICT

        write_whole_collection = isinstance(
            item, storage.BaseCollection) or not parent_item.get_meta("tag")
        if write_whole_collection:
            if not self.Rights.authorized(user, path, "w"):
                return NOT_ALLOWED
        elif not self.Rights.authorized_item(user, path, "w"):
            return NOT_ALLOWED

        etag = environ.get("HTTP_IF_MATCH", "")
        if not item and etag:
            # Etag asked but no item found: item has been removed
            return PRECONDITION_FAILED
        if item and etag and item.etag != etag:
            # Etag asked but item not matching: item has changed
            return PRECONDITION_FAILED

        match = environ.get("HTTP_IF_NONE_MATCH", "") == "*"
        if item and match:
            # Creation asked but item found: item can't be replaced
            return PRECONDITION_FAILED
        try:
            items = tuple(vobject.readComponents(content or ""))
            if write_whole_collection:
                content_type = environ.get("CONTENT_TYPE", "").split(";")[0]
                tags = {
                    value: key
                    for key, value in xmlutils.MIMETYPES.items()
                }
                tag = tags.get(content_type)
                if items and items[0].name == "VCALENDAR":
                    tag = "VCALENDAR"
                elif items and items[0].name in ("VCARD", "VLIST"):
                    tag = "VADDRESSBOOK"
            else:
                tag = parent_item.get_meta("tag")
            if tag == "VCALENDAR" and len(items) > 1:
                raise RuntimeError("VCALENDAR collection contains %d "
                                   "components" % len(items))
            for i in items:
                storage.check_and_sanitize_item(
                    i,
                    is_collection=write_whole_collection,
                    uid=item.uid
                    if not write_whole_collection and item else None,
                    tag=tag,
                )
        except Exception as e:
            self.logger.warning("Bad PUT request on %r: %s",
                                path,
                                e,
                                exc_info=True)
            return BAD_REQUEST

        if write_whole_collection:
            props = {}
            if tag:
                props["tag"] = tag
            if tag == "VCALENDAR" and items:
                if hasattr(items[0], "x_wr_calname"):
                    calname = items[0].x_wr_calname.value
                    if calname:
                        props["D:displayname"] = calname
                if hasattr(items[0], "x_wr_caldesc"):
                    caldesc = items[0].x_wr_caldesc.value
                    if caldesc:
                        props["C:calendar-description"] = caldesc
            try:
                storage.check_and_sanitize_props(props)
                new_item = self.Collection.create_collection(
                    path, items, props)
            except ValueError as e:
                self.logger.warning("Bad PUT request on %r: %s",
                                    path,
                                    e,
                                    exc_info=True)
                return BAD_REQUEST
        else:
            href = posixpath.basename(path.strip("/"))
            try:
                if tag and not parent_item.get_meta("tag"):
                    new_props = parent_item.get_meta()
                    new_props["tag"] = tag
                    storage.check_and_sanitize_props(new_props)
                    parent_item.set_meta_all(new_props)
                new_item = parent_item.upload(href, items[0])
            except ValueError as e:
                self.logger.warning("Bad PUT request on %r: %s",
                                    path,
                                    e,
                                    exc_info=True)
                return BAD_REQUEST
        headers = {"ETag": new_item.etag}
        return client.CREATED, headers, None
Beispiel #9
0
    def do_PUT(self, environ, base_prefix, path, user):
        """Manage PUT request."""
        if not self._access(user, path, "w"):
            return NOT_ALLOWED
        try:
            content = self._read_content(environ)
        except RuntimeError as e:
            self.logger.warning(
                "Bad PUT request on %r: %s", path, e, exc_info=True)
            return BAD_REQUEST
        except socket.timeout as e:
            self.logger.debug("client timed out", exc_info=True)
            return REQUEST_TIMEOUT
        with self.Collection.acquire_lock("w", user):
            parent_path = storage.sanitize_path(
                "/%s/" % posixpath.dirname(path.strip("/")))
            item = next(self.Collection.discover(path), None)
            parent_item = next(self.Collection.discover(parent_path), None)

            write_whole_collection = (
                isinstance(item, storage.BaseCollection) or
                not parent_item or (
                    not next(parent_item.list(), None) and
                    parent_item.get_meta("tag") not in (
                        "VADDRESSBOOK", "VCALENDAR")))
            if write_whole_collection:
                if not self.Rights.authorized(user, path, "w"):
                    return NOT_ALLOWED
            elif not self.Rights.authorized_item(user, path, "w"):
                return NOT_ALLOWED

            etag = environ.get("HTTP_IF_MATCH", "")
            if not item and etag:
                # Etag asked but no item found: item has been removed
                return PRECONDITION_FAILED
            if item and etag and item.etag != etag:
                # Etag asked but item not matching: item has changed
                return PRECONDITION_FAILED

            match = environ.get("HTTP_IF_NONE_MATCH", "") == "*"
            if item and match:
                # Creation asked but item found: item can't be replaced
                return PRECONDITION_FAILED

            try:
                items = tuple(vobject.readComponents(content or ""))
                if not write_whole_collection and len(items) != 1:
                    raise RuntimeError(
                        "Item contains %d components" % len(items))
                if write_whole_collection or not parent_item.get_meta("tag"):
                    content_type = environ.get("CONTENT_TYPE",
                                               "").split(";")[0]
                    tags = {value: key
                            for key, value in xmlutils.MIMETYPES.items()}
                    tag = tags.get(content_type)
                    if items and items[0].name == "VCALENDAR":
                        tag = "VCALENDAR"
                    elif items and items[0].name in ("VCARD", "VLIST"):
                        tag = "VADDRESSBOOK"
                else:
                    tag = parent_item.get_meta("tag")
                if tag == "VCALENDAR" and len(items) > 1:
                    raise RuntimeError("VCALENDAR collection contains %d "
                                       "components" % len(items))
                for i in items:
                    storage.check_and_sanitize_item(
                        i, is_collection=write_whole_collection, uid=item.uid
                        if not write_whole_collection and item else None,
                        tag=tag)
            except Exception as e:
                self.logger.warning(
                    "Bad PUT request on %r: %s", path, e, exc_info=True)
                return BAD_REQUEST

            if write_whole_collection:
                props = {}
                if tag:
                    props["tag"] = tag
                if tag == "VCALENDAR" and items:
                    if hasattr(items[0], "x_wr_calname"):
                        calname = items[0].x_wr_calname.value
                        if calname:
                            props["D:displayname"] = calname
                    if hasattr(items[0], "x_wr_caldesc"):
                        caldesc = items[0].x_wr_caldesc.value
                        if caldesc:
                            props["C:calendar-description"] = caldesc
                try:
                    storage.check_and_sanitize_props(props)
                    new_item = self.Collection.create_collection(
                        path, items, props)
                except ValueError as e:
                    self.logger.warning(
                        "Bad PUT request on %r: %s", path, e, exc_info=True)
                    return BAD_REQUEST
            else:
                href = posixpath.basename(path.strip("/"))
                try:
                    if tag and not parent_item.get_meta("tag"):
                        new_props = parent_item.get_meta()
                        new_props["tag"] = tag
                        storage.check_and_sanitize_props(new_props)
                        parent_item.set_meta_all(new_props)
                    new_item = parent_item.upload(href, items[0])
                except ValueError as e:
                    self.logger.warning(
                        "Bad PUT request on %r: %s", path, e, exc_info=True)
                    return BAD_REQUEST
            headers = {"ETag": new_item.etag}
            return client.CREATED, headers, None
Beispiel #10
0
        def prepare(vobject_items, tag=None, write_whole_collection=None):
            if (write_whole_collection
                    or permissions and not parent_permissions):
                write_whole_collection = True
                content_type = environ.get("CONTENT_TYPE", "").split(";")[0]
                tags = {
                    value: key
                    for key, value in xmlutils.MIMETYPES.items()
                }
                tag = storage.predict_tag_of_whole_collection(
                    vobject_items, tags.get(content_type))
                if not tag:
                    raise ValueError("Can't determine collection tag")
                collection_path = storage.sanitize_path(path).strip("/")
            elif (write_whole_collection is not None
                  and not write_whole_collection
                  or not permissions and parent_permissions):
                write_whole_collection = False
                if tag is None:
                    tag = storage.predict_tag_of_parent_collection(
                        vobject_items)
                collection_path = posixpath.dirname(
                    storage.sanitize_path(path).strip("/"))
            props = None
            stored_exc_info = None
            items = []
            try:
                if tag:
                    storage.check_and_sanitize_items(
                        vobject_items,
                        is_collection=write_whole_collection,
                        tag=tag)
                    if write_whole_collection and tag == "VCALENDAR":
                        vobject_components = []
                        vobject_item, = vobject_items
                        for content in ("vevent", "vtodo", "vjournal"):
                            vobject_components.extend(
                                getattr(vobject_item, "%s_list" % content, []))
                        vobject_components_by_uid = itertools.groupby(
                            sorted(vobject_components, key=storage.get_uid),
                            storage.get_uid)
                        for uid, components in vobject_components_by_uid:
                            vobject_collection = vobject.iCalendar()
                            for component in components:
                                vobject_collection.add(component)
                            item = storage.Item(
                                collection_path=collection_path,
                                vobject_item=vobject_collection)
                            item.prepare()
                            items.append(item)
                    elif write_whole_collection and tag == "VADDRESSBOOK":
                        for vobject_item in vobject_items:
                            item = storage.Item(
                                collection_path=collection_path,
                                vobject_item=vobject_item)
                            item.prepare()
                            items.append(item)
                    elif not write_whole_collection:
                        vobject_item, = vobject_items
                        item = storage.Item(collection_path=collection_path,
                                            vobject_item=vobject_item)
                        item.prepare()
                        items.append(item)

                if write_whole_collection:
                    props = {}
                    if tag:
                        props["tag"] = tag
                    if tag == "VCALENDAR" and vobject_items:
                        if hasattr(vobject_items[0], "x_wr_calname"):
                            calname = vobject_items[0].x_wr_calname.value
                            if calname:
                                props["D:displayname"] = calname
                        if hasattr(vobject_items[0], "x_wr_caldesc"):
                            caldesc = vobject_items[0].x_wr_caldesc.value
                            if caldesc:
                                props["C:calendar-description"] = caldesc
                    storage.check_and_sanitize_props(props)
            except Exception:
                stored_exc_info = sys.exc_info()

            # Use generator for items and delete references to free memory
            # early
            def items_generator():
                while items:
                    yield items.pop(0)

            return (items_generator(), tag, write_whole_collection, props,
                    stored_exc_info)