示例#1
0
 def do_PROPPATCH(self, environ, base_prefix, path, user):
     """Manage PROPPATCH request."""
     access = app.Access(self._rights, user, path)
     if not access.check("w"):
         return httputils.NOT_ALLOWED
     try:
         xml_content = self._read_xml_request_body(environ)
     except RuntimeError as e:
         logger.warning(
             "Bad PROPPATCH request on %r: %s", path, e, exc_info=True)
         return httputils.BAD_REQUEST
     except socket.timeout:
         logger.debug("Client timed out", exc_info=True)
         return httputils.REQUEST_TIMEOUT
     with self._storage.acquire_lock("w", user):
         item = next(self._storage.discover(path), None)
         if not item:
             return httputils.NOT_FOUND
         if not access.check("w", item):
             return httputils.NOT_ALLOWED
         if not isinstance(item, storage.BaseCollection):
             return httputils.FORBIDDEN
         headers = {"DAV": httputils.DAV_HEADERS,
                    "Content-Type": "text/xml; charset=%s" % self._encoding}
         try:
             xml_answer = xml_proppatch(base_prefix, path, xml_content,
                                        item)
         except ValueError as e:
             logger.warning(
                 "Bad PROPPATCH request on %r: %s", path, e, exc_info=True)
             return httputils.BAD_REQUEST
         return client.MULTI_STATUS, headers, self._xml_response(xml_answer)
示例#2
0
 def do_PROPFIND(self, environ, base_prefix, path, user):
     """Manage PROPFIND request."""
     access = app.Access(self._rights, user, path)
     if not access.check("r"):
         return httputils.NOT_ALLOWED
     try:
         xml_content = self._read_xml_content(environ)
     except RuntimeError as e:
         logger.warning(
             "Bad PROPFIND request on %r: %s", path, e, exc_info=True)
         return httputils.BAD_REQUEST
     except socket.timeout:
         logger.debug("client timed out", exc_info=True)
         return httputils.REQUEST_TIMEOUT
     with self._storage.acquire_lock("r", user):
         items = self._storage.discover(
             path, environ.get("HTTP_DEPTH", "0"))
         # take root item for rights checking
         item = next(items, None)
         if not item:
             return httputils.NOT_FOUND
         if not access.check("r", item):
             return httputils.NOT_ALLOWED
         # put item back
         items = itertools.chain([item], items)
         allowed_items = self._collect_allowed_items(items, user)
         headers = {"DAV": httputils.DAV_HEADERS,
                    "Content-Type": "text/xml; charset=%s" % self._encoding}
         status, xml_answer = xml_propfind(
             base_prefix, path, xml_content, allowed_items, user,
             self._encoding)
         if status == client.FORBIDDEN and xml_answer is None:
             return httputils.NOT_ALLOWED
         return status, headers, self._write_xml_content(xml_answer)
示例#3
0
 def do_GET(self, environ, base_prefix, path, user):
     """Manage GET request."""
     # Redirect to .web if the root URL is requested
     if not pathutils.strip_path(path):
         web_path = ".web"
         if not environ.get("PATH_INFO"):
             web_path = posixpath.join(posixpath.basename(base_prefix),
                                       web_path)
         return (client.FOUND, {
             "Location": web_path,
             "Content-Type": "text/plain"
         }, "Redirected to %s" % web_path)
     # Dispatch .web URL to web module
     if path == "/.web" or path.startswith("/.web/"):
         return self._web.get(environ, base_prefix, path, user)
     access = app.Access(self._rights, user, path)
     if not access.check("r") and "i" not in access.permissions:
         return httputils.NOT_ALLOWED
     with self._storage.acquire_lock("r", user):
         item = next(self._storage.discover(path), None)
         if not item:
             return httputils.NOT_FOUND
         if access.check("r", item):
             limited_access = False
         elif "i" in access.permissions:
             limited_access = True
         else:
             return httputils.NOT_ALLOWED
         if isinstance(item, storage.BaseCollection):
             tag = item.get_meta("tag")
             if not tag:
                 return (httputils.NOT_ALLOWED
                         if limited_access else httputils.DIRECTORY_LISTING)
             content_type = xmlutils.MIMETYPES[tag]
             content_disposition = self._content_disposition_attachement(
                 propose_filename(item))
         elif limited_access:
             return httputils.NOT_ALLOWED
         else:
             content_type = xmlutils.OBJECT_MIMETYPES[item.name]
             content_disposition = ""
         headers = {
             "Content-Type": content_type,
             "Last-Modified": item.last_modified,
             "ETag": item.etag
         }
         if content_disposition:
             headers["Content-Disposition"] = content_disposition
         answer = item.serialize()
         return client.OK, headers, answer
示例#4
0
 def do_REPORT(self, environ, base_prefix, path, user):
     """Manage REPORT request."""
     access = app.Access(self._rights, user, path)
     if not access.check("r"):
         return httputils.NOT_ALLOWED
     try:
         xml_content = self._read_xml_request_body(environ)
     except RuntimeError as e:
         logger.warning("Bad REPORT request on %r: %s",
                        path,
                        e,
                        exc_info=True)
         return httputils.BAD_REQUEST
     except socket.timeout:
         logger.debug("Client timed out", exc_info=True)
         return httputils.REQUEST_TIMEOUT
     with contextlib.ExitStack() as lock_stack:
         lock_stack.enter_context(self._storage.acquire_lock("r", user))
         item = next(self._storage.discover(path), None)
         if not item:
             return httputils.NOT_FOUND
         if not access.check("r", item):
             return httputils.NOT_ALLOWED
         if isinstance(item, storage.BaseCollection):
             collection = item
         else:
             collection = item.collection
         headers = {"Content-Type": "text/xml; charset=%s" % self._encoding}
         try:
             status, xml_answer = xml_report(base_prefix, path, xml_content,
                                             collection, self._encoding,
                                             lock_stack.close)
         except ValueError as e:
             logger.warning("Bad REPORT request on %r: %s",
                            path,
                            e,
                            exc_info=True)
             return httputils.BAD_REQUEST
         return status, headers, self._xml_response(xml_answer)
示例#5
0
 def do_DELETE(self, environ, base_prefix, path, user):
     """Manage DELETE request."""
     access = app.Access(self._rights, user, path)
     if not access.check("w"):
         return httputils.NOT_ALLOWED
     with self._storage.acquire_lock("w", user):
         item = next(self._storage.discover(path), None)
         if not item:
             return httputils.NOT_FOUND
         if not access.check("w", item):
             return httputils.NOT_ALLOWED
         if_match = environ.get("HTTP_IF_MATCH", "*")
         if if_match not in ("*", item.etag):
             # ETag precondition not verified, do not delete item
             return httputils.PRECONDITION_FAILED
         if isinstance(item, storage.BaseCollection):
             xml_answer = xml_delete(base_prefix, path, item)
         else:
             xml_answer = xml_delete(base_prefix, path, item.collection,
                                     item.href)
         headers = {"Content-Type": "text/xml; charset=%s" % self._encoding}
         return client.OK, headers, self._xml_response(xml_answer)
示例#6
0
    def do_MOVE(self, environ, base_prefix, path, user, context=None):
        """Manage MOVE request."""
        raw_dest = environ.get("HTTP_DESTINATION", "")
        to_url = urlparse(raw_dest)
        if to_url.netloc != environ["HTTP_HOST"]:
            logger.info("Unsupported destination address: %r", raw_dest)
            # Remote destination server, not supported
            return httputils.REMOTE_DESTINATION
        access = app.Access(self._rights, user, path)
        if not access.check("w"):
            return httputils.NOT_ALLOWED
        to_path = pathutils.sanitize_path(to_url.path)
        if not (to_path + "/").startswith(base_prefix + "/"):
            logger.warning("Destination %r from MOVE request on %r doesn't "
                           "start with base prefix", to_path, path)
            return httputils.NOT_ALLOWED
        to_path = to_path[len(base_prefix):]
        to_access = app.Access(self._rights, user, to_path)
        if not to_access.check("w"):
            return httputils.NOT_ALLOWED

        with self._storage.acquire_lock("w", user):
            item = next(self._storage.discover(path), None)
            if not item:
                return httputils.NOT_FOUND
            if (not access.check("w", item) or
                    not to_access.check("w", item)):
                return httputils.NOT_ALLOWED
            if isinstance(item, storage.BaseCollection):
                # TODO: support moving collections
                return httputils.METHOD_NOT_ALLOWED

            to_item = next(self._storage.discover(to_path), None)
            if isinstance(to_item, storage.BaseCollection):
                return httputils.FORBIDDEN
            to_parent_path = pathutils.unstrip_path(
                posixpath.dirname(pathutils.strip_path(to_path)), True)
            to_collection = next(
                self._storage.discover(to_parent_path), None)
            if not to_collection:
                return httputils.CONFLICT
            tag = item.collection.get_meta("tag")
            if not tag or tag != to_collection.get_meta("tag"):
                return httputils.FORBIDDEN
            if to_item and environ.get("HTTP_OVERWRITE", "F") != "T":
                return httputils.PRECONDITION_FAILED
            if (to_item and item.uid != to_item.uid or
                    not to_item and
                    to_collection.path != item.collection.path and
                    to_collection.has_uid(item.uid)):
                return self._webdav_error_response(
                    client.CONFLICT, "%s:no-uid-conflict" % (
                        "C" if tag == "VCALENDAR" else "CR"))
            to_href = posixpath.basename(pathutils.strip_path(to_path))
            try:
                self._storage.move(item, to_collection, to_href)
            except ValueError as e:
                logger.warning(
                    "Bad MOVE request on %r: %s", path, e, exc_info=True)
                return httputils.BAD_REQUEST
            return client.NO_CONTENT if to_item else client.CREATED, {}, None
示例#7
0
    def do_PUT(self, environ, base_prefix, path, user):
        """Manage PUT request."""
        access = app.Access(self._rights, user, path)
        if not access.check("w"):
            return httputils.NOT_ALLOWED
        try:
            content = self._read_content(environ)
        except RuntimeError as e:
            logger.warning("Bad PUT request on %r: %s", path, e, exc_info=True)
            return httputils.BAD_REQUEST
        except socket.timeout:
            logger.debug("client timed out", exc_info=True)
            return httputils.REQUEST_TIMEOUT
        # Prepare before locking
        content_type = environ.get("CONTENT_TYPE", "").split(";")[0]
        try:
            vobject_items = tuple(vobject.readComponents(content or ""))
        except Exception as e:
            logger.warning(
                "Bad PUT request on %r: %s", path, e, exc_info=True)
            return httputils.BAD_REQUEST
        (prepared_items, prepared_tag, prepared_write_whole_collection,
         prepared_props, prepared_exc_info) = prepare(
             vobject_items, path, content_type,
             bool(rights.intersect(access.permissions, "Ww")),
             bool(rights.intersect(access.parent_permissions, "w")))

        with self._storage.acquire_lock("w", user):
            item = next(self._storage.discover(path), None)
            parent_item = next(
                self._storage.discover(access.parent_path), None)
            if not parent_item:
                return httputils.CONFLICT

            write_whole_collection = (
                isinstance(item, storage.BaseCollection) or
                not parent_item.get_meta("tag"))

            if write_whole_collection:
                tag = prepared_tag
            else:
                tag = parent_item.get_meta("tag")

            if write_whole_collection:
                if ("w" if tag else "W") not in access.permissions:
                    return httputils.NOT_ALLOWED
            elif "w" not in access.parent_permissions:
                return httputils.NOT_ALLOWED

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

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

            if (tag != prepared_tag or
                    prepared_write_whole_collection != write_whole_collection):
                (prepared_items, prepared_tag, prepared_write_whole_collection,
                 prepared_props, prepared_exc_info) = prepare(
                     vobject_items, path, content_type,
                     bool(rights.intersect(access.permissions, "Ww")),
                     bool(rights.intersect(access.parent_permissions, "w")),
                     tag, write_whole_collection)
            props = prepared_props
            if prepared_exc_info:
                logger.warning(
                    "Bad PUT request on %r: %s", path, prepared_exc_info[1],
                    exc_info=prepared_exc_info)
                return httputils.BAD_REQUEST

            if write_whole_collection:
                try:
                    etag = self._storage.create_collection(
                        path, prepared_items, props).etag
                except ValueError as e:
                    logger.warning(
                        "Bad PUT request on %r: %s", path, e, exc_info=True)
                    return httputils.BAD_REQUEST
            else:
                prepared_item, = prepared_items
                if (item and item.uid != prepared_item.uid or
                        not item and parent_item.has_uid(prepared_item.uid)):
                    return self._webdav_error_response("%s:no-uid-conflict" % (
                        "C" if tag == "VCALENDAR" else "CR"))

                href = posixpath.basename(pathutils.strip_path(path))
                try:
                    etag = parent_item.upload(href, prepared_item).etag
                except ValueError as e:
                    logger.warning(
                        "Bad PUT request on %r: %s", path, e, exc_info=True)
                    return httputils.BAD_REQUEST

            headers = {"ETag": etag}
            return client.CREATED, headers, None