diff --git a/go.mod b/go.mod index 5ef0567f7e..31beb08bee 100644 --- a/go.mod +++ b/go.mod @@ -64,7 +64,7 @@ require ( github.com/open-policy-agent/opa v1.15.2 github.com/opencloud-eu/icap-client v0.0.0-20250930132611-28a2afe62d89 github.com/opencloud-eu/libre-graph-api-go v1.0.8-0.20260310090739-853d972b282d - github.com/opencloud-eu/reva/v2 v2.46.4-0.20260612072211-aa5e96aa80e7 + github.com/opencloud-eu/reva/v2 v2.46.4-0.20260615073558-209c2cd3b52b github.com/opensearch-project/opensearch-go/v4 v4.6.0 github.com/orcaman/concurrent-map v1.0.0 github.com/pkg/errors v0.9.1 diff --git a/go.sum b/go.sum index 97bdb032a8..45cb574aa0 100644 --- a/go.sum +++ b/go.sum @@ -948,12 +948,8 @@ github.com/opencloud-eu/icap-client v0.0.0-20250930132611-28a2afe62d89 h1:W1ms+l github.com/opencloud-eu/icap-client v0.0.0-20250930132611-28a2afe62d89/go.mod h1:vigJkNss1N2QEceCuNw/ullDehncuJNFB6mEnzfq9UI= github.com/opencloud-eu/libre-graph-api-go v1.0.8-0.20260310090739-853d972b282d h1:JcqGDiyrcaQwVyV861TUyQgO7uEmsjkhfm7aQd84dOw= github.com/opencloud-eu/libre-graph-api-go v1.0.8-0.20260310090739-853d972b282d/go.mod h1:pzatilMEHZFT3qV7C/X3MqOa3NlRQuYhlRhZTL+hN6Q= -github.com/opencloud-eu/reva/v2 v2.46.3-0.20260611095012-6617969b3720 h1:UHJDrOoU9hoVFg0hgKmNIMp0hFEb/reiDYthVHlX5g8= -github.com/opencloud-eu/reva/v2 v2.46.3-0.20260611095012-6617969b3720/go.mod h1:RoFQt+u7edxwzHr1IZ2Y6VaDinMiRPQupAvMBy3WVmE= -github.com/opencloud-eu/reva/v2 v2.46.3 h1:7td4rXcku0goMyGYbWcJ3KGFZe5Ls+nDlBWWZRQfkJY= -github.com/opencloud-eu/reva/v2 v2.46.3/go.mod h1:RoFQt+u7edxwzHr1IZ2Y6VaDinMiRPQupAvMBy3WVmE= -github.com/opencloud-eu/reva/v2 v2.46.4-0.20260612072211-aa5e96aa80e7 h1:H9RbSBCN/oMw21Q2kYYdEOf4pyxoZchfTjPCCDsR+80= -github.com/opencloud-eu/reva/v2 v2.46.4-0.20260612072211-aa5e96aa80e7/go.mod h1:RoFQt+u7edxwzHr1IZ2Y6VaDinMiRPQupAvMBy3WVmE= +github.com/opencloud-eu/reva/v2 v2.46.4-0.20260615073558-209c2cd3b52b h1:ejmGkLVlWauUkcnXYbSr7dY1a59dUdkKHOvzeGpwwgE= +github.com/opencloud-eu/reva/v2 v2.46.4-0.20260615073558-209c2cd3b52b/go.mod h1:RoFQt+u7edxwzHr1IZ2Y6VaDinMiRPQupAvMBy3WVmE= github.com/opencloud-eu/secure v0.0.0-20260312082735-b6f5cb2244e4 h1:l2oB/RctH+t8r7QBj5p8thfEHCM/jF35aAY3WQ3hADI= github.com/opencloud-eu/secure v0.0.0-20260312082735-b6f5cb2244e4/go.mod h1:BmF5hyM6tXczk3MpQkFf1hpKSRqCyhqcbiQtiAF7+40= github.com/opencontainers/go-digest v1.0.0 h1:apOUWs51W5PlhuyGyz9FCeeBIOUDA/6nW8Oi/yOhh5U= diff --git a/vendor/github.com/opencloud-eu/reva/v2/internal/http/services/archiver/handler.go b/vendor/github.com/opencloud-eu/reva/v2/internal/http/services/archiver/handler.go index 49aa40e8d8..d665de7da1 100644 --- a/vendor/github.com/opencloud-eu/reva/v2/internal/http/services/archiver/handler.go +++ b/vendor/github.com/opencloud-eu/reva/v2/internal/http/services/archiver/handler.go @@ -23,6 +23,8 @@ import ( "errors" "fmt" "net/http" + "path" + "strings" "time" "regexp" @@ -34,6 +36,7 @@ import ( "github.com/gdexlab/go-render/render" "github.com/go-viper/mapstructure/v2" "github.com/opencloud-eu/reva/v2/internal/http/services/archiver/manager" + "github.com/opencloud-eu/reva/v2/internal/http/services/owncloud/ocdav/net" "github.com/opencloud-eu/reva/v2/pkg/errtypes" "github.com/opencloud-eu/reva/v2/pkg/rgrpc/todo/pool" "github.com/opencloud-eu/reva/v2/pkg/rhttp" @@ -200,6 +203,56 @@ func (s *svc) allAllowed(paths []string) error { } */ +// resourceName resolves the name of a single resource so the archive can be named after it instead +// of the generic "download". It returns an empty string on any failure, so the caller keeps the +// default name. The name is sanitized via sanitizeArchiveName. +func (s *svc) resourceName(ctx context.Context, id *provider.ResourceId) (string, error) { + gatewayClient, err := s.gatewaySelector.Next() + if err != nil { + s.log.Debug().Err(err).Msg("archiver: could not select gateway to resolve the archive name, using the default") + return "", err + } + + res, err := gatewayClient.Stat(ctx, &provider.StatRequest{ + Ref: &provider.Reference{ResourceId: id}, + }) + if err != nil { + s.log.Debug().Err(err).Msg("archiver: stat failed while resolving the archive name, using the default") + return "", err + } + if code := res.GetStatus().GetCode(); code != rpc.Code_CODE_OK { + s.log.Debug().Str("code", code.String()).Msg("archiver: stat returned non-OK while resolving the archive name, using the default") + return "", fmt.Errorf("stat returned non-OK code %s", code.String()) + } + + name := res.GetInfo().GetName() + if name == "" { + name = path.Base(res.GetInfo().GetPath()) + } + return sanitizeArchiveName(name), nil +} + +// sanitizeArchiveName removes characters that would break the Content-Disposition header (CR, LF, +// double quote) or let the name act as a path (slash, backslash), plus all control characters +// (C0, DEL and C1). It returns an empty string if nothing usable is left. +func sanitizeArchiveName(name string) string { + name = strings.Map(func(r rune) rune { + switch { + case r == '"', r == '\\', r == '/': + return -1 + case r < 0x20 || (r >= 0x7f && r <= 0x9f): + return -1 + default: + return r + } + }, name) + name = strings.TrimSpace(name) + if name == "." || name == ".." { + return "" + } + return name +} + func (s *svc) writeHTTPError(rw http.ResponseWriter, err error) { s.log.Error().Msg(err.Error()) @@ -251,7 +304,20 @@ func (s *svc) Handler() http.Handler { return } + // Name the archive after the resource when a single one was requested, instead of the + // generic "download". The name must be resolved here, before the body is streamed: the + // Content-Disposition header below is written before CreateZip/CreateTar run, so the name + // the walker resolves while building the archive would come too late. + // See https://github.com/opencloud-eu/reva/issues/308 archName := s.config.Name + if len(resources) == 1 { + if name, err := s.resourceName(ctx, resources[0]); name != "" && err == nil { + archName = name + } else { + s.log.Debug().Err(err).Msg("could not resolve the archive name, using the default") + archName = "download" + } + } if format == "tar" { archName += ".tar" } else { @@ -260,7 +326,7 @@ func (s *svc) Handler() http.Handler { s.log.Debug().Msg("Requested the following resources to archive: " + render.Render(resources)) - rw.Header().Set("Content-Disposition", fmt.Sprintf("attachment; filename=\"%s\"", archName)) + rw.Header().Set(net.HeaderContentDisposistion, net.ContentDispositionAttachment(archName)) rw.Header().Set("Content-Transfer-Encoding", "binary") // create the archive diff --git a/vendor/modules.txt b/vendor/modules.txt index 19e72f589d..df6ad146c1 100644 --- a/vendor/modules.txt +++ b/vendor/modules.txt @@ -1360,7 +1360,7 @@ github.com/opencloud-eu/icap-client # github.com/opencloud-eu/libre-graph-api-go v1.0.8-0.20260310090739-853d972b282d ## explicit; go 1.18 github.com/opencloud-eu/libre-graph-api-go -# github.com/opencloud-eu/reva/v2 v2.46.4-0.20260612072211-aa5e96aa80e7 +# github.com/opencloud-eu/reva/v2 v2.46.4-0.20260615073558-209c2cd3b52b ## explicit; go 1.25.0 github.com/opencloud-eu/reva/v2/cmd/revad/internal/grace github.com/opencloud-eu/reva/v2/cmd/revad/runtime