Ejemplo n.º 1
0
    def render_GET(self, req):
        """
        Historically, accessing this via "GET /uri?uri=<capabilitiy>"
        was/is a feature -- which simply redirects to the more-common
        "GET /uri/<capability>" with any other query args
        preserved. New code should use "/uri/<cap>"
        """
        uri_arg = req.args.get(b"uri", [None])[0]
        if uri_arg is None:
            raise WebError("GET /uri requires uri=")

        # shennanigans like putting "%2F" or just "/" itself, or ../
        # etc in the <cap> might be a vector for weirdness so we
        # validate that this is a valid capability before proceeding.
        cap = uri.from_string(uri_arg)
        if isinstance(cap, uri.UnknownURI):
            raise WebError("Invalid capability")

        # so, using URL.from_text(req.uri) isn't going to work because
        # it seems Nevow was creating absolute URLs including
        # host/port whereas req.uri is absolute (but lacks host/port)
        redir_uri = URL.from_text(req.prePathURL().decode('utf8'))
        redir_uri = redir_uri.child(urllib.quote(uri_arg).decode('utf8'))
        # add back all the query args that AREN'T "?uri="
        for k, values in req.args.items():
            if k != b"uri":
                for v in values:
                    redir_uri = redir_uri.add(k.decode('utf8'), v.decode('utf8'))
        return redirectTo(redir_uri.to_text().encode('utf8'), req)
Ejemplo n.º 2
0
 def childFactory(self, ctx, name):
     req = IRequest(ctx)
     if should_create_intermediate_directories(req):
         raise WebError("Cannot create directory '%s', because its "
                        "parent is a file, not a directory" % name)
     raise WebError("Files have no children, certainly not named '%s'" %
                    name)
Ejemplo n.º 3
0
    def _POST_rename(self, req):
        charset = get_arg(req, "_charset", "utf-8")
        from_name = get_arg(req, "from_name")
        if from_name is not None:
            from_name = from_name.strip()
            from_name = from_name.decode(charset)
            assert isinstance(from_name, unicode)
        to_name = get_arg(req, "to_name")
        if to_name is not None:
            to_name = to_name.strip()
            to_name = to_name.decode(charset)
            assert isinstance(to_name, unicode)
        if not from_name or not to_name:
            raise WebError("rename requires from_name and to_name")
        if from_name == to_name:
            return defer.succeed("redundant rename")

        # allow from_name to contain slashes, so they can fix names that were
        # accidentally created with them. But disallow them in to_name, to
        # discourage the practice.
        if "/" in to_name:
            raise WebError("to_name= may not contain a slash",
                           http.BAD_REQUEST)

        replace = boolean_of_arg(get_arg(req, "replace", "true"))
        d = self.node.move_child_to(from_name, self.node, to_name, replace)
        d.addCallback(lambda res: "thing renamed")
        return d
Ejemplo n.º 4
0
def authorize(request, get_auth_token):
    if "token" in request.args:
        raise WebError(
            "Do not pass 'token' as URL argument",
            http.BAD_REQUEST,
        )

    t = request.content.tell()
    request.content.seek(0)
    fields = cgi.FieldStorage(
        request.content,
        {k: vs[0]
         for (k, vs)
         in request.requestHeaders.getAllRawHeaders()
        },
        environ={'REQUEST_METHOD': 'POST'},
    )
    request.content.seek(t)

    # not using get_arg() here because we *don't* want the token
    # argument to work if you passed it as a GET-style argument
    token = None
    if fields and 'token' in fields:
        token = fields['token'].value.strip()
    if not token:
        raise WebError("Missing token", http.UNAUTHORIZED)
    if not timing_safe_compare(token, get_auth_token()):
        raise WebError("Invalid token", http.UNAUTHORIZED)
Ejemplo n.º 5
0
 def _POST_rename(self, req):
     # rename is identical to relink, but to_dir is not allowed
     # and to_name is required.
     if get_arg(req, "to_dir") is not None:
         raise WebError("to_dir= is not valid for rename")
     if get_arg(req, "to_name") is None:
         raise WebError("to_name= is required for rename")
     return self._POST_relink(req)
Ejemplo n.º 6
0
 def childFactory(self, ctx, name):
     req = IRequest(ctx)
     if isinstance(self.node, ProhibitedNode):
         raise FileProhibited(self.node.reason)
     if should_create_intermediate_directories(req):
         raise WebError("Cannot create directory %s, because its "
                        "parent is a file, not a directory" % quote_output(name, encoding='utf-8'))
     raise WebError("Files have no children, certainly not named %s"
                    % quote_output(name, encoding='utf-8'))
Ejemplo n.º 7
0
 def getChild(self, name, req):
     if req.method not in (b"GET", b"HEAD"):
         raise WebError("/file can only be used with GET or HEAD")
     # 'name' must be a file URI
     try:
         node = self.client.create_node_from_uri(name)
     except (TypeError, AssertionError):
         # I think this can no longer be reached
         raise WebError("%r is not a valid file- or directory- cap" % name)
     if not IFileNode.providedBy(node):
         raise WebError("%r is not a file-cap" % name)
     return filenode.FileNodeDownloadHandler(self.client, node)
Ejemplo n.º 8
0
    def _POST_relink(self, req):
        charset = get_arg(req, "_charset", "utf-8")
        replace = parse_replace_arg(get_arg(req, "replace", "true"))

        from_name = get_arg(req, "from_name")
        if from_name is not None:
            from_name = from_name.strip()
            from_name = from_name.decode(charset)
            assert isinstance(from_name, unicode)
        else:
            raise WebError("from_name= is required")

        to_name = get_arg(req, "to_name")
        if to_name is not None:
            to_name = to_name.strip()
            to_name = to_name.decode(charset)
            assert isinstance(to_name, unicode)
        else:
            to_name = from_name

        # Disallow slashes in both from_name and to_name, that would only
        # cause confusion.
        if "/" in from_name:
            raise WebError("from_name= may not contain a slash",
                           http.BAD_REQUEST)
        if "/" in to_name:
            raise WebError("to_name= may not contain a slash",
                           http.BAD_REQUEST)

        to_dir = get_arg(req, "to_dir")
        if to_dir is not None and to_dir != self.node.get_write_uri():
            to_dir = to_dir.strip()
            to_dir = to_dir.decode(charset)
            assert isinstance(to_dir, unicode)
            to_path = to_dir.split(u"/")
            to_root = self.client.nodemaker.create_from_cap(to_str(to_path[0]))
            if not IDirectoryNode.providedBy(to_root):
                raise WebError("to_dir is not a directory", http.BAD_REQUEST)
            d = to_root.get_child_at_path(to_path[1:])
        else:
            d = defer.succeed(self.node)

        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)

        d.addCallback(_got_new_parent)
        d.addCallback(lambda res: "thing moved")
        return d
Ejemplo n.º 9
0
    def render_PUT(self, req):
        t = get_arg(req, "t", "").strip()
        replace = parse_replace_arg(get_arg(req, "replace", "true"))

        assert self.parentnode and self.name
        if req.getHeader("content-range"):
            raise WebError("Content-Range in PUT not yet supported",
                           http.NOT_IMPLEMENTED)
        if not t:
            return self.replace_me_with_a_child(req, self.client, replace)
        if t == "uri":
            return self.replace_me_with_a_childcap(req, self.client, replace)

        raise WebError("PUT to a file: bad t=%s" % t)
Ejemplo n.º 10
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)
    d = client.create_dirnode()
    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.º 11
0
    def render_POST(self, ctx):
        req = IRequest(ctx)
        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)

        when_done = get_arg(req, "when_done", None)
        if when_done:
            d.addCallback(lambda res: url.URL.fromString(when_done))
        return d
Ejemplo n.º 12
0
 def render_POST(self, req):
     """
     "POST /uri?t=upload&file=newfile" to upload an
     unlinked file or "POST /uri?t=mkdir" to create a
     new directory
     """
     t = get_arg(req, "t", "").strip()
     if t in ("", "upload"):
         file_format = get_format(req)
         mutable_type = get_mutable_type(file_format)
         if mutable_type is not None:
             return unlinked.POSTUnlinkedSSK(req, self.client, mutable_type)
         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.º 13
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()

        # t=info contains variable ophandles, t=rename-form contains the name
        # of the child being renamed. Neither is allowed an ETag.
        FIXED_OUTPUT_TYPES =  ["", "json", "uri", "readonly-uri"]
        if not self.node.is_mutable() and t in FIXED_OUTPUT_TYPES:
            si = self.node.get_storage_index()
            if si and req.setETag('DIR:%s-%s' % (base32.b2a(si), t or "")):
                return ""

        if not t:
            # render the directory as HTML, using the docFactory and Nevow's
            # whole templating thing.
            return DirectoryAsHTML(self.node,
                                   self.client.mutable_file_default)

        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.º 14
0
    def childFactory(self, ctx, name):
        ophandle = name
        if ophandle not in self.handles:
            raise WebError("unknown/expired handle '%s'" % escape(ophandle),
                           NOT_FOUND)
        (monitor, renderer, when_added) = self.handles[ophandle]

        request = IRequest(ctx)
        t = get_arg(ctx, "t", "status")
        if t == "cancel" and request.method == "POST":
            monitor.cancel()
            # return the status anyways, but release the handle
            self._release_ophandle(ophandle)

        else:
            retain_for = get_arg(ctx, "retain-for", None)
            if retain_for is not None:
                self._set_timer(ophandle, int(retain_for))

            if monitor.is_finished():
                if boolean_of_arg(
                        get_arg(ctx, "release-after-complete", "false")):
                    self._release_ophandle(ophandle)
                if retain_for is None:
                    # this GET is collecting the ophandle, so change its timer
                    self._set_timer(ophandle, self.COLLECTED_HANDLE_LIFETIME)

        status = monitor.get_status()
        if isinstance(status, Failure):
            return defer.fail(status)

        return renderer
Ejemplo n.º 15
0
 def render_HEAD(self, req):
     t = get_arg(req, "t", "").strip()
     if t:
         raise WebError("HEAD file: bad t=%s" % t)
     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
Ejemplo n.º 16
0
 def childFactory(self, ctx, name):
     # 'name' is expected to be a URI
     try:
         node = self.client.create_node_from_uri(name)
         return directory.make_handler_for(node, self.client)
     except (TypeError, AssertionError):
         raise WebError("'%s' is not a valid file- or directory- cap" %
                        name)
Ejemplo n.º 17
0
 def _POST_mkdir_p(self, req):
     path = get_arg(req, "path")
     if not path:
         raise WebError("mkdir-p requires a path")
     path_ = tuple([seg.decode("utf-8") for seg in path.split('/') if seg])
     # TODO: replace
     d = self._get_or_create_directories(self.node, path_)
     d.addCallback(lambda node: node.get_uri())
     return d
Ejemplo n.º 18
0
    def _POST_uri(self, req):
        childcap = get_arg(req, "uri")
        if not childcap:
            raise WebError("set-uri requires a uri")
        name = get_arg(req, "name")
        if not name:
            raise WebError("set-uri requires a name")
        charset = get_arg(req, "_charset", "utf-8")
        name = name.decode(charset)
        replace = parse_replace_arg(get_arg(req, "replace", "true"))

        # We mustn't pass childcap for the readcap argument because we don't
        # know whether it is a read cap. Passing a read cap as the writecap
        # argument will work (it ends up calling NodeMaker.create_from_cap,
        # which derives a readcap if necessary and possible).
        d = self.node.set_uri(name, childcap, None, overwrite=replace)
        d.addCallback(lambda res: childcap)
        return d
Ejemplo n.º 19
0
    def render(self, req):
        if req.method != "POST":
            raise WebError("/report_incident can only be used with POST")

        log.msg(format="User reports incident through web page: %(details)s",
                details=get_arg(req, "details", ""),
                level=log.WEIRD, umid="LkD9Pw")
        req.setHeader("content-type", "text/plain; charset=UTF-8")
        return b"An incident report has been saved to logs/incidents/ in the node directory."
Ejemplo n.º 20
0
    def render_GET(self, ctx):
        req = IRequest(ctx)
        t = get_arg(req, "t", "").strip()

        # t=info contains variable ophandles, so is not allowed an ETag.
        FIXED_OUTPUT_TYPES = ["", "json", "uri", "readonly-uri"]
        if not self.node.is_mutable() and t in FIXED_OUTPUT_TYPES:
            # if the client already has the ETag then we can
            # short-circuit the whole process.
            si = self.node.get_storage_index()
            if si and req.setETag('%s-%s' % (base32.b2a(si), t or "")):
                return ""

        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.º 21
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.º 22
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.º 23
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.º 24
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.º 25
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.º 26
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.º 27
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.º 28
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.º 29
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.º 30
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)