+ var attachment bool
+ credentialsOK := h.Config.TrustAllContent
+
+ if r.Host != "" && r.Host == h.Config.AttachmentOnlyHost {
+ credentialsOK = true
+ attachment = true
+ } else if r.FormValue("disposition") == "attachment" {
+ attachment = true
+ }
+
+ if targetID = parseCollectionIDFromDNSName(r.Host); targetID != "" {
+ // http://ID.collections.example/PATH...
+ credentialsOK = true
+ targetPath = pathParts
+ } else if len(pathParts) >= 2 && strings.HasPrefix(pathParts[0], "c=") {
+ // /c=ID/PATH...
+ targetID = parseCollectionIDFromURL(pathParts[0][2:])
+ targetPath = pathParts[1:]
+ } else if len(pathParts) >= 3 && pathParts[0] == "collections" {
+ if len(pathParts) >= 5 && pathParts[1] == "download" {
+ // /collections/download/ID/TOKEN/PATH...
+ targetID = parseCollectionIDFromURL(pathParts[2])
+ tokens = []string{pathParts[3]}
+ targetPath = pathParts[4:]
+ pathToken = true
+ } else {
+ // /collections/ID/PATH...
+ targetID = parseCollectionIDFromURL(pathParts[1])
+ tokens = h.Config.AnonymousTokens
+ targetPath = pathParts[2:]
+ }
+ }
+
+ if targetID == "" {
+ statusCode = http.StatusNotFound
+ return
+ }
+
+ formToken := r.FormValue("api_token")
+ if formToken != "" && r.Header.Get("Origin") != "" && attachment && r.URL.Query().Get("api_token") == "" {
+ // The client provided an explicit token in the POST
+ // body. The Origin header indicates this *might* be
+ // an AJAX request, in which case redirect-with-cookie
+ // won't work: we should just serve the content in the
+ // POST response. This is safe because:
+ //
+ // * We're supplying an attachment, not inline
+ // content, so we don't need to convert the POST to
+ // a GET and avoid the "really resubmit form?"
+ // problem.
+ //
+ // * The token isn't embedded in the URL, so we don't
+ // need to worry about bookmarks and copy/paste.
+ tokens = append(tokens, formToken)
+ } else if formToken != "" {
+ // The client provided an explicit token in the query
+ // string, or a form in POST body. We must put the
+ // token in an HttpOnly cookie, and redirect to the
+ // same URL with the query param redacted and method =
+ // GET.
+
+ if !credentialsOK {
+ // It is not safe to copy the provided token
+ // into a cookie unless the current vhost
+ // (origin) serves only a single collection or
+ // we are in TrustAllContent mode.
+ statusCode = http.StatusBadRequest
+ return
+ }
+
+ // The HttpOnly flag is necessary to prevent
+ // JavaScript code (included in, or loaded by, a page
+ // in the collection being served) from employing the
+ // user's token beyond reading other files in the same
+ // domain, i.e., same collection.
+ //
+ // The 303 redirect is necessary in the case of a GET
+ // request to avoid exposing the token in the Location
+ // bar, and in the case of a POST request to avoid
+ // raising warnings when the user refreshes the
+ // resulting page.
+
+ http.SetCookie(w, &http.Cookie{
+ Name: "arvados_api_token",
+ Value: auth.EncodeTokenCookie([]byte(formToken)),
+ Path: "/",
+ HttpOnly: true,
+ })
+
+ // Propagate query parameters (except api_token) from
+ // the original request.
+ redirQuery := r.URL.Query()
+ redirQuery.Del("api_token")
+
+ redir := (&url.URL{
+ Host: r.Host,
+ Path: r.URL.Path,
+ RawQuery: redirQuery.Encode(),
+ }).String()
+
+ w.Header().Add("Location", redir)
+ statusCode, statusText = http.StatusSeeOther, redir
+ w.WriteHeader(statusCode)
+ io.WriteString(w, `<A href="`)
+ io.WriteString(w, html.EscapeString(redir))
+ io.WriteString(w, `">Continue</A>`)
+ return
+ }
+
+ if tokens == nil && strings.HasPrefix(targetPath[0], "t=") {
+ // http://ID.example/t=TOKEN/PATH...
+ // /c=ID/t=TOKEN/PATH...
+ //
+ // This form must only be used to pass scoped tokens
+ // that give permission for a single collection. See
+ // FormValue case above.
+ tokens = []string{targetPath[0][2:]}