Ejemplo n.º 1
0
    def _POST_upload(self, ctx):
        req = IRequest(ctx)
        charset = get_arg(req, "_charset", "utf-8")
        contents = req.fields["file"]
        assert contents.filename is None or isinstance(contents.filename, str)
        name = get_arg(req, "name")
        name = name or contents.filename
        if name is not None:
            name = name.strip()
        if not name:
            # this prohibts empty, missing, and all-whitespace filenames
            raise WebError("upload requires a name")
        assert isinstance(name, str)
        name = name.decode(charset)
        if "/" in name:
            raise WebError("name= may not contain a slash", http.BAD_REQUEST)
        assert isinstance(name, unicode)

        # since POST /uri/path/file?t=upload is equivalent to
        # POST /uri/path/dir?t=upload&name=foo, just do the same thing that
        # childFactory would do. Things are cleaner if we only do a subset of
        # them, though, so we don't do: d = self.childFactory(ctx, name)

        d = self.node.get(name)

        def _maybe_got_node(node_or_failure):
            if isinstance(node_or_failure, Failure):
                f = node_or_failure
                f.trap(NoSuchChildError)
                # create a placeholder which will see POST t=upload
                return PlaceHolderNodeHandler(self.client, self.node, name)
            else:
                node = node_or_failure
                return make_handler_for(node, self.client, self.node, name)

        d.addBoth(_maybe_got_node)
        # now we have a placeholder or a filenodehandler, and we can just
        # delegate to it. We could return the resource back out of
        # DirectoryNodeHandler.renderHTTP, and nevow would recurse into it,
        # but the addCallback() that handles when_done= would break.
        d.addCallback(lambda child: child.renderHTTP(ctx))
        return d
Ejemplo n.º 2
0
    def render_PUT(self, ctx):
        req = IRequest(ctx)
        t = get_arg(req, "t", "").strip()
        replace = parse_replace_arg(get_arg(req, "replace", "true"))
        offset = parse_offset_arg(get_arg(req, "offset", None))

        if not t:
            if not replace:
                # this is the early trap: if someone else modifies the
                # directory while we're uploading, the add_file(overwrite=)
                # call in replace_me_with_a_child will do the late trap.
                raise ExistingChildError()

            if self.node.is_mutable():
                # Are we a readonly filenode? We shouldn't allow callers
                # to try to replace us if we are.
                if self.node.is_readonly():
                    raise WebError("PUT to a mutable file: replace or update"
                                   " requested with read-only cap")
                if offset is None:
                    return self.replace_my_contents(req)

                if offset >= 0:
                    return self.update_my_contents(req, offset)

                raise WebError("PUT to a mutable file: Invalid offset")

            else:
                if offset is not None:
                    raise WebError("PUT to a file: append operation invoked "
                                   "on an immutable cap")

                assert self.parentnode and self.name
                return self.replace_me_with_a_child(req, self.client, replace)

        if t == "uri":
            if not replace:
                raise ExistingChildError()
            assert self.parentnode and self.name
            return self.replace_me_with_a_childcap(req, self.client, replace)

        raise WebError("PUT to a file: bad t=%s" % t)
Ejemplo n.º 3
0
 def render_GET(self, ctx):
     req = IRequest(ctx)
     uri = get_arg(req, "uri", None)
     if uri is None:
         raise WebError("GET /uri requires uri=")
     there = url.URL.fromContext(ctx)
     there = there.clear("uri")
     # I thought about escaping the childcap that we attach to the URL
     # here, but it seems that nevow does that for us.
     there = there.child(uri)
     return there
Ejemplo n.º 4
0
 def getChild(self, name, req):
     """
     Most requests look like /uri/<cap> so this fetches the capability
     and creates and appropriate handler (depending on the kind of
     capability it was passed).
     """
     try:
         node = self.client.create_node_from_uri(name)
         return directory.make_handler_for(node, self.client)
     except (TypeError, AssertionError):
         raise WebError(
             "'{}' is not a valid file- or directory- cap".format(name))
Ejemplo n.º 5
0
 def childFactory(self, ctx, name):
     if not name:
         return self
     # /operation/$OPHANDLE/$STORAGEINDEX provides detailed information
     # about a specific file or directory that was checked
     si = base32.a2b(name)
     s = self.monitor.get_status()
     try:
         results = s.get_results_for_storage_index(si)
         return CheckAndRepairResultsRenderer(self.client, results)
     except KeyError:
         raise WebError("No detailed results for SI %s" % html.escape(name),
                        http.NOT_FOUND)
Ejemplo n.º 6
0
 def render_HEAD(self, ctx):
     req = IRequest(ctx)
     t = get_arg(req, "t", "").strip()
     if t:
         raise WebError("GET file: bad t=%s" % t)
     filename = get_arg(req, "filename", self.name) or "unknown"
     if self.node.is_mutable():
         # some day: d = self.node.get_best_version()
         d = makeMutableDownloadable(self.node)
     else:
         d = defer.succeed(self.node)
     d.addCallback(lambda dn: FileDownloader(dn, filename))
     return d
Ejemplo n.º 7
0
def PUTUnlinkedCreateDirectory(req, client):
    # "PUT /uri?t=mkdir", to create an unlinked directory.
    file_format = get_format(req, None)
    if file_format == "CHK":
        raise WebError("format=CHK not accepted for PUT /uri?t=mkdir",
                       http.BAD_REQUEST)
    mt = None
    if file_format:
        mt = get_mutable_type(file_format)
    d = client.create_dirnode(version=mt)
    d.addCallback(lambda dirnode: dirnode.get_uri())
    # XXX add redirect_to_result
    return d
Ejemplo n.º 8
0
def POSTUnlinkedCreateDirectory(req, client):
    # "POST /uri?t=mkdir", to create an unlinked directory.
    ct = req.getHeader("content-type") or ""
    if not ct.startswith("multipart/form-data"):
        # guard against accidental attempts to call t=mkdir as if it were
        # t=mkdir-with-children, but make sure we tolerate the usual HTML
        # create-directory form (in which the t=mkdir and redirect_to_result=
        # and other arguments can be passed encoded as multipath/form-data,
        # in the request body).
        req.content.seek(0)
        kids_json = req.content.read()
        if kids_json:
            raise WebError(
                "t=mkdir does not accept children=, "
                "try t=mkdir-with-children instead", http.BAD_REQUEST)
    file_format = get_format(req, None)
    if file_format == "CHK":
        raise WebError(
            "format=CHK not currently accepted for POST /uri?t=mkdir",
            http.BAD_REQUEST)
    mt = None
    if file_format:
        mt = get_mutable_type(file_format)
    d = client.create_dirnode(version=mt)
    redirect = get_arg(req, "redirect_to_result", "false")
    if boolean_of_arg(redirect):

        def _then_redir(res):
            new_url = "uri/" + urllib.quote(res.get_uri())
            req.setResponseCode(http.SEE_OTHER)  # 303
            req.setHeader('location', new_url)
            req.finish()
            return ''

        d.addCallback(_then_redir)
    else:
        d.addCallback(lambda dirnode: dirnode.get_uri())
    return d
Ejemplo n.º 9
0
 def getChild(self, name, req):
     if not name:
         return self
     # /operation/$OPHANDLE/$STORAGEINDEX provides detailed information
     # about a specific file or directory that was checked
     si = base32.a2b(name)
     r = self.monitor.get_status()
     try:
         return CheckResultsRenderer(self._client,
                                     r.get_results_for_storage_index(si))
     except KeyError:
         raise WebError(
             "No detailed results for SI %s" %
             html.escape(str(name, "utf-8")), http.NOT_FOUND)
Ejemplo n.º 10
0
 def render_PUT(self, ctx):
     req = IRequest(ctx)
     # either "PUT /uri" to create an unlinked file, or
     # "PUT /uri?t=mkdir" to create an unlinked directory
     t = get_arg(req, "t", "").strip()
     if t == "":
         mutable = boolean_of_arg(get_arg(req, "mutable", "false").strip())
         if mutable:
             return unlinked.PUTUnlinkedSSK(req, self.client)
         else:
             return unlinked.PUTUnlinkedCHK(req, self.client)
     if t == "mkdir":
         return unlinked.PUTUnlinkedCreateDirectory(req, self.client)
     errmsg = ("/uri accepts only PUT, PUT?t=mkdir, POST?t=upload, "
               "and POST?t=mkdir")
     raise WebError(errmsg, http.BAD_REQUEST)
Ejemplo n.º 11
0
 def render_GET(self, ctx):
     req = IRequest(ctx)
     t = get_arg(req, "t", "").strip()
     if t == "info":
         return MoreInfo(self.node)
     if t == "json":
         is_parent_known_immutable = self.parentnode and not self.parentnode.is_mutable()
         if self.parentnode and self.name:
             d = self.parentnode.get_metadata_for(self.name)
         else:
             d = defer.succeed(None)
         d.addCallback(lambda md: UnknownJSONMetadata(ctx, self.node, md, is_parent_known_immutable))
         return d
     raise WebError("GET unknown URI type: can only do t=info and t=json, not t=%s.\n"
                    "Using a webapi server that supports a later version of Tahoe "
                    "may help." % t)
Ejemplo n.º 12
0
 def render_GET(self, ctx):
     req = IRequest(ctx)
     t = get_arg(req, "t", "").strip()
     if not t:
         # just get the contents
         # the filename arrives as part of the URL or in a form input
         # element, and will be sent back in a Content-Disposition header.
         # Different browsers use various character sets for this name,
         # sometimes depending upon how language environment is
         # configured. Firefox sends the equivalent of
         # urllib.quote(name.encode("utf-8")), while IE7 sometimes does
         # latin-1. Browsers cannot agree on how to interpret the name
         # they see in the Content-Disposition header either, despite some
         # 11-year old standards (RFC2231) that explain how to do it
         # properly. So we assume that at least the browser will agree
         # with itself, and echo back the same bytes that we were given.
         filename = get_arg(req, "filename", self.name) or "unknown"
         d = self.node.get_best_readable_version()
         d.addCallback(lambda dn: FileDownloader(dn, filename))
         return d
     if t == "json":
         # We do this to make sure that fields like size and
         # mutable-type (which depend on the file on the grid and not
         # just on the cap) are filled in. The latter gets used in
         # tests, in particular.
         #
         # TODO: Make it so that the servermap knows how to update in
         # a mode specifically designed to fill in these fields, and
         # then update it in that mode.
         if self.node.is_mutable():
             d = self.node.get_servermap(MODE_READ)
         else:
             d = defer.succeed(None)
         if self.parentnode and self.name:
             d.addCallback(lambda ignored: self.parentnode.get_metadata_for(
                 self.name))
         else:
             d.addCallback(lambda ignored: None)
         d.addCallback(lambda md: FileJSONMetadata(ctx, self.node, md))
         return d
     if t == "info":
         return MoreInfo(self.node)
     if t == "uri":
         return FileURI(ctx, self.node)
     if t == "readonly-uri":
         return FileReadOnlyURI(ctx, self.node)
     raise WebError("GET file: bad t=%s" % t)
Ejemplo n.º 13
0
    def render_POST(self, req):
        t = get_arg(req, "t", "").strip()
        replace = boolean_of_arg(get_arg(req, "replace", "true"))
        if t == "upload":
            # like PUT, but get the file data from an HTML form's input field.
            # We could get here from POST /uri/mutablefilecap?t=upload,
            # or POST /uri/path/file?t=upload, or
            # POST /uri/path/dir?t=upload&name=foo . All have the same
            # behavior, we just ignore any name= argument
            d = self.replace_me_with_a_formpost(req, self.client, replace)
        else:
            # t=mkdir is handled in DirectoryNodeHandler._POST_mkdir, so
            # there are no other t= values left to be handled by the
            # placeholder.
            raise WebError("POST to a file: bad t=%s" % t)

        return handle_when_done(req, d)
Ejemplo n.º 14
0
 def render_PUT(self, ctx):
     req = IRequest(ctx)
     # either "PUT /uri" to create an unlinked file, or
     # "PUT /uri?t=mkdir" to create an unlinked directory
     t = get_arg(req, "t", "").strip()
     if t == "":
         file_format = get_format(req, "CHK")
         mutable_type = get_mutable_type(file_format)
         if mutable_type is not None:
             return unlinked.PUTUnlinkedSSK(req, self.client, mutable_type)
         else:
             return unlinked.PUTUnlinkedCHK(req, self.client)
     if t == "mkdir":
         return unlinked.PUTUnlinkedCreateDirectory(req, self.client)
     errmsg = ("/uri accepts only PUT, PUT?t=mkdir, POST?t=upload, "
               "and POST?t=mkdir")
     raise WebError(errmsg, http.BAD_REQUEST)
Ejemplo n.º 15
0
    def render_POST(self, ctx):
        req = IRequest(ctx)
        t = get_arg(req, "t", "").strip()

        if t == "mkdir":
            d = self._POST_mkdir(req)
        elif t == "mkdir-with-children":
            d = self._POST_mkdir_with_children(req)
        elif t == "mkdir-immutable":
            d = self._POST_mkdir_immutable(req)
        elif t == "mkdir-p":
            # TODO: docs, tests
            d = self._POST_mkdir_p(req)
        elif t == "upload":
            d = self._POST_upload(ctx)  # this one needs the context
        elif t == "uri":
            d = self._POST_uri(req)
        elif t == "delete":
            d = self._POST_delete(req)
        elif t == "rename":
            d = self._POST_rename(req)
        elif t == "check":
            d = self._POST_check(req)
        elif t == "start-deep-check":
            d = self._POST_start_deep_check(ctx)
        elif t == "stream-deep-check":
            d = self._POST_stream_deep_check(ctx)
        elif t == "start-manifest":
            d = self._POST_start_manifest(ctx)
        elif t == "start-deep-size":
            d = self._POST_start_deep_size(ctx)
        elif t == "start-deep-stats":
            d = self._POST_start_deep_stats(ctx)
        elif t == "stream-manifest":
            d = self._POST_stream_manifest(ctx)
        elif t == "set_children" or t == "set-children":
            d = self._POST_set_children(req)
        else:
            raise WebError("POST to a directory with bad t=%s" % t)

        when_done = get_arg(req, "when_done", None)
        if when_done:
            d.addCallback(lambda res: url.URL.fromString(when_done))
        return d
Ejemplo n.º 16
0
    def render_PUT(self, ctx):
        req = IRequest(ctx)
        t = get_arg(req, "t", "").strip()
        replace = parse_replace_arg(get_arg(req, "replace", "true"))

        if t == "mkdir":
            # our job was done by the traversal/create-intermediate-directory
            # process that got us here.
            return text_plain(self.node.get_uri(), ctx) # TODO: urlencode
        if t == "uri":
            if not replace:
                # they're trying to set_uri and that name is already occupied
                # (by us).
                raise ExistingChildError()
            d = self.replace_me_with_a_childcap(req, self.client, replace)
            # TODO: results
            return d

        raise WebError("PUT to a directory")
Ejemplo n.º 17
0
    def render_GET(self, ctx):
        req = IRequest(ctx)
        # This is where all of the directory-related ?t=* code goes.
        t = get_arg(req, "t", "").strip()
        if not t:
            # render the directory as HTML, using the docFactory and Nevow's
            # whole templating thing.
            return DirectoryAsHTML(self.node)

        if t == "json":
            return DirectoryJSONMetadata(ctx, self.node)
        if t == "info":
            return MoreInfo(self.node)
        if t == "uri":
            return DirectoryURI(ctx, self.node)
        if t == "readonly-uri":
            return DirectoryReadonlyURI(ctx, self.node)
        if t == 'rename-form':
            return RenameForm(self.node)

        raise WebError("GET directory: bad t=%s" % t)
Ejemplo n.º 18
0
 def getChild(self, name, req):
     """
     Most requests look like /uri/<cap> so this fetches the capability
     and creates and appropriate handler (depending on the kind of
     capability it was passed).
     """
     # this is in case a URI like "/uri/?cap=<valid capability>" is
     # passed -- we re-direct to the non-trailing-slash version so
     # that there is just one valid URI for "uri" resource.
     if not name:
         u = DecodedURL.from_text(req.uri.decode('utf8'))
         u = u.replace(
             path=(s for s in u.path if s),  # remove empty segments
         )
         return redirectTo(u.to_uri().to_text().encode('utf8'), req)
     try:
         node = self.client.create_node_from_uri(name)
         return directory.make_handler_for(node, self.client)
     except (TypeError, AssertionError):
         raise WebError(
             "'{}' is not a valid file- or directory- cap".format(name))
Ejemplo n.º 19
0
    def render_POST(self, request):
        authorize(request, self.get_auth_token)

        request.setHeader("content-type", "application/json")
        nick = request.args.get("name", ["default"])[0]

        try:
            magic_folder = self.get_magic_folder(nick)
        except KeyError:
            raise WebError(
                "No such magic-folder '{}'".format(nick),
                404,
            )

        data = []
        for item in magic_folder.uploader.get_status():
            d = dict(
                path=item.relpath_u,
                status=item.status_history()[-1][0],
                kind='upload',
            )
            for (status, ts) in item.status_history():
                d[status + '_at'] = ts
            d['percent_done'] = item.progress.progress
            d['size'] = item.size
            data.append(d)

        for item in magic_folder.downloader.get_status():
            d = dict(
                path=item.relpath_u,
                status=item.status_history()[-1][0],
                kind='download',
            )
            for (status, ts) in item.status_history():
                d[status + '_at'] = ts
            d['percent_done'] = item.progress.progress
            d['size'] = item.size
            data.append(d)

        return json.dumps(data)
Ejemplo n.º 20
0
    def render_PUT(self, ctx):
        req = IRequest(ctx)
        t = get_arg(req, "t", "").strip()
        replace = parse_replace_arg(get_arg(req, "replace", "true"))

        if not t:
            if self.node.is_mutable():
                return self.replace_my_contents(req)
            if not replace:
                # this is the early trap: if someone else modifies the
                # directory while we're uploading, the add_file(overwrite=)
                # call in replace_me_with_a_child will do the late trap.
                raise ExistingChildError()
            assert self.parentnode and self.name
            return self.replace_me_with_a_child(req, self.client, replace)
        if t == "uri":
            if not replace:
                raise ExistingChildError()
            assert self.parentnode and self.name
            return self.replace_me_with_a_childcap(req, self.client, replace)

        raise WebError("PUT to a file: bad t=%s" % t)
Ejemplo n.º 21
0
    def render_POST(self, req):
        t = get_arg(req, "t", "").strip()
        replace = boolean_of_arg(get_arg(req, "replace", "true"))
        if t == "check":
            d = self._POST_check(req)
        elif t == "upload":
            # like PUT, but get the file data from an HTML form's input field
            # We could get here from POST /uri/mutablefilecap?t=upload,
            # or POST /uri/path/file?t=upload, or
            # POST /uri/path/dir?t=upload&name=foo . All have the same
            # behavior, we just ignore any name= argument
            if self.node.is_mutable():
                d = self.replace_my_contents_with_a_formpost(req)
            else:
                if not replace:
                    raise ExistingChildError()
                assert self.parentnode and self.name
                d = self.replace_me_with_a_formpost(req, self.client, replace)
        else:
            raise WebError("POST to file: bad t=%s" % t)

        return handle_when_done(req, d)
Ejemplo n.º 22
0
    def post_json(self, req):
        req.setHeader("content-type", "application/json")
        nick = get_arg(req, 'name', 'default')

        try:
            magic_folder = self.client._magic_folders[nick]
        except KeyError:
            raise WebError(
                "No such magic-folder '{}'".format(nick),
                404,
            )

        data = []
        for item in magic_folder.uploader.get_status():
            d = dict(
                path=item.relpath_u,
                status=item.status_history()[-1][0],
                kind='upload',
            )
            for (status, ts) in item.status_history():
                d[status + '_at'] = ts
            d['percent_done'] = item.progress.progress
            d['size'] = item.size
            data.append(d)

        for item in magic_folder.downloader.get_status():
            d = dict(
                path=item.relpath_u,
                status=item.status_history()[-1][0],
                kind='download',
            )
            for (status, ts) in item.status_history():
                d[status + '_at'] = ts
            d['percent_done'] = item.progress.progress
            d['size'] = item.size
            data.append(d)

        return json.dumps(data)
Ejemplo n.º 23
0
 def render_GET(self, ctx):
     req = IRequest(ctx)
     t = get_arg(req, "t", "").strip()
     if not t:
         # just get the contents
         # the filename arrives as part of the URL or in a form input
         # element, and will be sent back in a Content-Disposition header.
         # Different browsers use various character sets for this name,
         # sometimes depending upon how language environment is
         # configured. Firefox sends the equivalent of
         # urllib.quote(name.encode("utf-8")), while IE7 sometimes does
         # latin-1. Browsers cannot agree on how to interpret the name
         # they see in the Content-Disposition header either, despite some
         # 11-year old standards (RFC2231) that explain how to do it
         # properly. So we assume that at least the browser will agree
         # with itself, and echo back the same bytes that we were given.
         filename = get_arg(req, "filename", self.name) or "unknown"
         if self.node.is_mutable():
             # some day: d = self.node.get_best_version()
             d = makeMutableDownloadable(self.node)
         else:
             d = defer.succeed(self.node)
         d.addCallback(lambda dn: FileDownloader(dn, filename))
         return d
     if t == "json":
         if self.parentnode and self.name:
             d = self.parentnode.get_metadata_for(self.name)
         else:
             d = defer.succeed(None)
         d.addCallback(lambda md: FileJSONMetadata(ctx, self.node, md))
         return d
     if t == "info":
         return MoreInfo(self.node)
     if t == "uri":
         return FileURI(ctx, self.node)
     if t == "readonly-uri":
         return FileReadOnlyURI(ctx, self.node)
     raise WebError("GET file: bad t=%s" % t)
Ejemplo n.º 24
0
 def render_POST(self, ctx):
     # "POST /uri?t=upload&file=newfile" to upload an
     # unlinked file or "POST /uri?t=mkdir" to create a
     # new directory
     req = IRequest(ctx)
     t = get_arg(req, "t", "").strip()
     if t in ("", "upload"):
         mutable = bool(get_arg(req, "mutable", "").strip())
         if mutable:
             return unlinked.POSTUnlinkedSSK(req, self.client)
         else:
             return unlinked.POSTUnlinkedCHK(req, self.client)
     if t == "mkdir":
         return unlinked.POSTUnlinkedCreateDirectory(req, self.client)
     elif t == "mkdir-with-children":
         return unlinked.POSTUnlinkedCreateDirectoryWithChildren(req,
                                                                 self.client)
     elif t == "mkdir-immutable":
         return unlinked.POSTUnlinkedCreateImmutableDirectory(req,
                                                              self.client)
     errmsg = ("/uri accepts only PUT, PUT?t=mkdir, POST?t=upload, "
               "and POST?t=mkdir")
     raise WebError(errmsg, http.BAD_REQUEST)
Ejemplo n.º 25
0
    def got_child(self, node_or_failure, ctx, name):
        DEBUG = False
        if DEBUG: print "GOT_CHILD", name, node_or_failure
        req = IRequest(ctx)
        method = req.method
        nonterminal = len(req.postpath) > 1
        t = get_arg(req, "t", "").strip()
        if isinstance(node_or_failure, Failure):
            f = node_or_failure
            f.trap(NoSuchChildError)
            # No child by this name. What should we do about it?
            if DEBUG: print "no child", name
            if DEBUG: print "postpath", req.postpath
            if nonterminal:
                if DEBUG: print " intermediate"
                if should_create_intermediate_directories(req):
                    # create intermediate directories
                    if DEBUG: print " making intermediate directory"
                    d = self.node.create_subdirectory(name)
                    d.addCallback(make_handler_for,
                                  self.client, self.node, name)
                    return d
            else:
                if DEBUG: print " terminal"
                # terminal node
                if (method,t) in [ ("POST","mkdir"), ("PUT","mkdir"),
                                   ("POST", "mkdir-with-children"),
                                   ("POST", "mkdir-immutable") ]:
                    if DEBUG: print " making final directory"
                    # final directory
                    kids = {}
                    if t in ("mkdir-with-children", "mkdir-immutable"):
                        req.content.seek(0)
                        kids_json = req.content.read()
                        kids = convert_children_json(self.client.nodemaker,
                                                     kids_json)
                    file_format = get_format(req, None)
                    mutable = True
                    mt = get_mutable_type(file_format)
                    if t == "mkdir-immutable":
                        mutable = False

                    d = self.node.create_subdirectory(name, kids,
                                                      mutable=mutable,
                                                      mutable_version=mt)
                    d.addCallback(make_handler_for,
                                  self.client, self.node, name)
                    return d
                if (method,t) in ( ("PUT",""), ("PUT","uri"), ):
                    if DEBUG: print " PUT, making leaf placeholder"
                    # we were trying to find the leaf filenode (to put a new
                    # file in its place), and it didn't exist. That's ok,
                    # since that's the leaf node that we're about to create.
                    # We make a dummy one, which will respond to the PUT
                    # request by replacing itself.
                    return PlaceHolderNodeHandler(self.client, self.node, name)
            if DEBUG: print " 404"
            # otherwise, we just return a no-such-child error
            return f

        node = node_or_failure
        if nonterminal and should_create_intermediate_directories(req):
            if not IDirectoryNode.providedBy(node):
                # we would have put a new directory here, but there was a
                # file in the way.
                if DEBUG: print "blocking"
                raise WebError("Unable to create directory '%s': "
                               "a file was in the way" % name,
                               http.CONFLICT)
        if DEBUG: print "good child"
        return make_handler_for(node, self.client, self.node, name)
Ejemplo n.º 26
0
        def _got_new_parent(new_parent):
            if not IDirectoryNode.providedBy(new_parent):
                raise WebError("to_dir is not a directory", http.BAD_REQUEST)

            return self.node.move_child_to(from_name, new_parent,
                                           to_name, replace)
Ejemplo n.º 27
0
 def render_GET(self, req):
     raise WebError("/file must be followed by a file-cap and a name",
                    http.NOT_FOUND)
Ejemplo n.º 28
0
    def renderHTTP(self, ctx):
        req = IRequest(ctx)
        gte = static.getTypeAndEncoding
        ctype, encoding = gte(self.filename,
                              static.File.contentTypes,
                              static.File.contentEncodings,
                              defaultType="text/plain")
        req.setHeader("content-type", ctype)
        if encoding:
            req.setHeader("content-encoding", encoding)

        if boolean_of_arg(get_arg(req, "save", "False")):
            # tell the browser to save the file rather display it we don't
            # try to encode the filename, instead we echo back the exact same
            # bytes we were given in the URL. See the comment in
            # FileNodeHandler.render_GET for the sad details.
            req.setHeader("content-disposition",
                          'attachment; filename="%s"' % self.filename)

        filesize = self.filenode.get_size()
        assert isinstance(filesize, (int, long)), filesize
        first, size = 0, None
        contentsize = filesize
        req.setHeader("accept-ranges", "bytes")
        if not self.filenode.is_mutable():
            # TODO: look more closely at Request.setETag and how it interacts
            # with a conditional "if-etag-equals" request, I think this may
            # need to occur after the setResponseCode below
            si = self.filenode.get_storage_index()
            if si:
                req.setETag(base32.b2a(si))
        # TODO: for mutable files, use the roothash. For LIT, hash the data.
        # or maybe just use the URI for CHK and LIT.
        rangeheader = req.getHeader('range')
        if rangeheader:
            ranges = self.parse_range_header(rangeheader)

            # ranges = None means the header didn't parse, so ignore
            # the header as if it didn't exist.  If is more than one
            # range, then just return the first for now, until we can
            # generate multipart/byteranges.
            if ranges is not None:
                first, last = ranges[0]

                if first >= filesize:
                    raise WebError('First beyond end of file',
                                   http.REQUESTED_RANGE_NOT_SATISFIABLE)
                else:
                    first = max(0, first)
                    last = min(filesize - 1, last)

                    req.setResponseCode(http.PARTIAL_CONTENT)
                    req.setHeader(
                        'content-range', "bytes %s-%s/%s" %
                        (str(first), str(last), str(filesize)))
                    contentsize = last - first + 1
                    size = contentsize

        req.setHeader("content-length", str(contentsize))
        if req.method == "HEAD":
            return ""

        # Twisted >=9.0 throws an error if we call req.finish() on a closed
        # HTTP connection. It also has req.notifyFinish() to help avoid it.
        finished = []

        def _request_finished(ign):
            finished.append(True)

        if hasattr(req, "notifyFinish"):
            req.notifyFinish().addBoth(_request_finished)

        d = self.filenode.read(req, first, size)

        def _finished(ign):
            if not finished:
                req.finish()

        def _error(f):
            lp = log.msg("error during GET",
                         facility="tahoe.webish",
                         failure=f,
                         level=log.UNUSUAL,
                         umid="xSiF3w")
            if finished:
                log.msg("but it's too late to tell them",
                        parent=lp,
                        level=log.UNUSUAL,
                        umid="j1xIbw")
                return
            req._tahoe_request_had_error = f  # for HTTP-style logging
            if req.startedWriting:
                # The content-type is already set, and the response code has
                # already been sent, so we can't provide a clean error
                # indication. We can emit text (which a browser might
                # interpret as something else), and if we sent a Size header,
                # they might notice that we've truncated the data. Keep the
                # error message small to improve the chances of having our
                # error response be shorter than the intended results.
                #
                # We don't have a lot of options, unfortunately.
                req.write("problem during download\n")
                req.finish()
            else:
                # We haven't written anything yet, so we can provide a
                # sensible error message.
                eh = MyExceptionHandler()
                eh.renderHTTP_exception(ctx, f)

        d.addCallbacks(_finished, _error)
        return req.deferred
Ejemplo n.º 29
0
    def render(self, req):
        gte = static.getTypeAndEncoding
        ctype, encoding = gte(self.filename,
                              static.File.contentTypes,
                              static.File.contentEncodings,
                              defaultType="text/plain")
        req.setHeader("content-type", ctype)
        if encoding:
            req.setHeader("content-encoding", encoding)

        if boolean_of_arg(get_arg(req, "save", "False")):
            # tell the browser to save the file rather display it we don't
            # try to encode the filename, instead we echo back the exact same
            # bytes we were given in the URL. See the comment in
            # FileNodeHandler.render_GET for the sad details.
            req.setHeader("content-disposition",
                          'attachment; filename="%s"' % self.filename)

        filesize = self.filenode.get_size()
        assert isinstance(filesize, (int, long)), filesize
        first, size = 0, None
        contentsize = filesize
        req.setHeader("accept-ranges", "bytes")

        # TODO: for mutable files, use the roothash. For LIT, hash the data.
        # or maybe just use the URI for CHK and LIT.
        rangeheader = req.getHeader('range')
        if rangeheader:
            ranges = self.parse_range_header(rangeheader)

            # ranges = None means the header didn't parse, so ignore
            # the header as if it didn't exist.  If is more than one
            # range, then just return the first for now, until we can
            # generate multipart/byteranges.
            if ranges is not None:
                first, last = ranges[0]

                if first >= filesize:
                    raise WebError('First beyond end of file',
                                   http.REQUESTED_RANGE_NOT_SATISFIABLE)
                else:
                    first = max(0, first)
                    last = min(filesize - 1, last)

                    req.setResponseCode(http.PARTIAL_CONTENT)
                    req.setHeader(
                        'content-range', "bytes %s-%s/%s" %
                        (str(first), str(last), str(filesize)))
                    contentsize = last - first + 1
                    size = contentsize

        req.setHeader("content-length", b"%d" % contentsize)
        if req.method == "HEAD":
            return ""

        d = self.filenode.read(req, first, size)

        def _error(f):
            if f.check(defer.CancelledError):
                # The HTTP connection was lost and we no longer have anywhere
                # to send our result.  Let this pass through.
                return f
            if req.startedWriting:
                # The content-type is already set, and the response code has
                # already been sent, so we can't provide a clean error
                # indication. We can emit text (which a browser might
                # interpret as something else), and if we sent a Size header,
                # they might notice that we've truncated the data. Keep the
                # error message small to improve the chances of having our
                # error response be shorter than the intended results.
                #
                # We don't have a lot of options, unfortunately.
                return b"problem during download\n"
            else:
                # We haven't written anything yet, so we can provide a
                # sensible error message.
                return f

        d.addCallbacks(
            lambda ignored: None,
            _error,
        )
        return d
Ejemplo n.º 30
0
 def renderHTTP(self, ctx):
     raise WebError("/file must be followed by a file-cap and a name",
                    http.NOT_FOUND)