From 168995a14cfaa4f1f705eeb5995d74f8e6034858 Mon Sep 17 00:00:00 2001 From: Gianmaria Del Monte Date: Thu, 17 Nov 2022 18:16:08 +0100 Subject: [PATCH 01/15] add download endpoint --- .../http/services/owncloud/ocdav/download.go | 177 ++++++++++++++++++ .../http/services/owncloud/ocdav/ocdav.go | 12 ++ 2 files changed, 189 insertions(+) create mode 100644 internal/http/services/owncloud/ocdav/download.go diff --git a/internal/http/services/owncloud/ocdav/download.go b/internal/http/services/owncloud/ocdav/download.go new file mode 100644 index 0000000000..b06d6edc18 --- /dev/null +++ b/internal/http/services/owncloud/ocdav/download.go @@ -0,0 +1,177 @@ +package ocdav + +import ( + "context" + "io" + "net/http" + "net/url" + "path" + + rpc "github.com/cs3org/go-cs3apis/cs3/rpc/v1beta1" + link "github.com/cs3org/go-cs3apis/cs3/sharing/link/v1beta1" + provider "github.com/cs3org/go-cs3apis/cs3/storage/provider/v1beta1" + "github.com/cs3org/reva/pkg/errtypes" + "github.com/cs3org/reva/pkg/rhttp" + "github.com/cs3org/reva/pkg/storage/utils/downloader" +) + +// index.php/s/jIKrtrkXCIXwg1y/download?path=%2FHugo&files=Intrinsico +func (s *svc) handleLegacyPublicLinkDownload(w http.ResponseWriter, r *http.Request) { + token := r.URL.Path + files := getFilesFromRequest(r) + s.downloadFiles(r.Context(), w, token, files) +} + +func getFilesFromRequest(r *http.Request) []string { + q := r.URL.Query() + dir := q.Get("path") + files := []string{} + + if q.Get("files") != "" { + files = append(files, path.Join(dir, q.Get("files"))) + } else { + for _, f := range q["files[]"] { + files = append(files, path.Join(dir, f)) + } + } + return files +} + +func (s *svc) downloadFiles(ctx context.Context, w http.ResponseWriter, token string, files []string) { + isSingleFileShare, res, err := s.isSingleFileShare(ctx, token, files) + if err != nil { + // TODO + return + } + if isSingleFileShare { + s.downloadFile(ctx, w, res) + } else { + s.downloadArchive(ctx, w, token, files) + } +} + +func (s *svc) isSingleFileShare(ctx context.Context, token string, files []string) (bool, *provider.ResourceInfo, error) { + switch len(files) { + case 0: + return s.resourceIsFileInPublicLink(ctx, token, "") + case 1: + return s.resourceIsFileInPublicLink(ctx, token, files[0]) + default: + // FIXME (gdelmont): even if the list contains more than one file + // these (or part of them), could not exist + // in this case, filtering the existing ones, we could + // end up having 0 or 1 files + return false, nil, nil + } +} + +func (s *svc) resourceIsFileInPublicLink(ctx context.Context, token, file string) (bool, *provider.ResourceInfo, error) { + res, err := s.getResourceFromPublicLinkToken(ctx, token, file) + if err != nil { + return false, nil, err + } + return res.Type == provider.ResourceType_RESOURCE_TYPE_FILE, res, nil +} + +func (s *svc) getResourceFromPublicLinkToken(ctx context.Context, token, file string) (*provider.ResourceInfo, error) { + c, err := s.getClient() + if err != nil { + return nil, err + } + res, err := c.GetPublicShareByToken(ctx, &link.GetPublicShareByTokenRequest{ + Token: token, + }) + if err != nil { + return nil, err + } + + if res.Status.Code != rpc.Code_CODE_OK { + if res.Status.Code == rpc.Code_CODE_NOT_FOUND { + return nil, errtypes.NotFound(token) + } + return nil, errtypes.InternalError(res.Status.Message) + } + + statRes, err := c.Stat(ctx, &provider.StatRequest{Ref: &provider.Reference{ResourceId: res.Share.ResourceId, Path: file}}) + if err != nil { + return nil, err + } + + if res.Status.Code != rpc.Code_CODE_OK { + if res.Status.Code == rpc.Code_CODE_NOT_FOUND { + return nil, errtypes.NotFound(token) + } + return nil, errtypes.InternalError(res.Status.Message) + } + return statRes.Info, nil +} + +func (s *svc) downloadFile(ctx context.Context, w http.ResponseWriter, res *provider.ResourceInfo) { + c, err := s.getClient() + if err != nil { + // TODO + return + } + d := downloader.NewDownloader(c) + r, err := d.Download(ctx, res.Path) + if err != nil { + // TODO + return + } + defer r.Close() + + w.WriteHeader(http.StatusOK) + + _, err = io.Copy(w, r) + if err != nil { + http.Error(w, "", http.StatusInternalServerError) + return + } +} + +func getPublicLinkResources(rootFolder, token string, files []string) []string { + r := make([]string, 0, len(files)) + for _, f := range files { + r = append(r, path.Join(rootFolder, token, f)) + } + return r +} + +func prepareArchiverURL(endpoint string, files []string) string { + q := url.Values{} + for _, f := range files { + q.Add("file", f) + } + u := url.URL{ + RawPath: endpoint, + RawQuery: q.Encode(), + } + return u.String() +} + +func (s *svc) downloadArchive(ctx context.Context, w http.ResponseWriter, token string, files []string) { + resources := getPublicLinkResources(s.c.PublicLinkDownload.PublicFolder, token, files) + url := prepareArchiverURL(s.c.PublicLinkDownload.ArchiverEndpoint, resources) + + req, err := rhttp.NewRequest(ctx, http.MethodGet, url, nil) + if err != nil { + http.Error(w, "", http.StatusInternalServerError) + return + } + + res, err := s.client.Do(req) + if err != nil { + // TODO + http.Error(w, "", http.StatusInternalServerError) + return + } + defer res.Body.Close() + + w.WriteHeader(http.StatusOK) + + _, err = io.Copy(w, res.Body) + if err != nil { + http.Error(w, "", http.StatusInternalServerError) + return + } +} diff --git a/internal/http/services/owncloud/ocdav/ocdav.go b/internal/http/services/owncloud/ocdav/ocdav.go index 33020ddde2..e42283ca50 100644 --- a/internal/http/services/owncloud/ocdav/ocdav.go +++ b/internal/http/services/owncloud/ocdav/ocdav.go @@ -83,6 +83,11 @@ func init() { global.Register("ocdav", New) } +type ConfigPublicLinkDownload struct { + ArchiverEndpoint string `mapstructure:"archiver_endpoint"` + PublicFolder string `mapstructure:"public_folder"` +} + // Config holds the config options that need to be passed down to all ocdav handlers type Config struct { Prefix string `mapstructure:"prefix"` @@ -104,6 +109,7 @@ type Config struct { PublicURL string `mapstructure:"public_url"` FavoriteStorageDriver string `mapstructure:"favorite_storage_driver"` FavoriteStorageDrivers map[string]map[string]interface{} `mapstructure:"favorite_storage_drivers"` + PublicLinkDownload ConfigPublicLinkDownload `mapstructure:"publiclink_download"` } func (c *Config) init() { @@ -216,6 +222,12 @@ func (s *svc) Handler() http.Handler { case "index.php": head, r.URL.Path = router.ShiftPath(r.URL.Path) if head == "s" { + if strings.HasSuffix(r.URL.Path, "/download") { + r.URL.Path = strings.TrimSuffix(r.URL.Path, "/download") + s.handleLegacyPublicLinkDownload(w, r) + return + } + token := r.URL.Path rURL := s.c.PublicURL + path.Join(head, token) From ad371fce8412186eac1a9c223e936401d236c7f2 Mon Sep 17 00:00:00 2001 From: Gianmaria Del Monte Date: Thu, 17 Nov 2022 18:26:37 +0100 Subject: [PATCH 02/15] fix token from url --- internal/http/services/owncloud/ocdav/download.go | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/internal/http/services/owncloud/ocdav/download.go b/internal/http/services/owncloud/ocdav/download.go index b06d6edc18..90b4ce6830 100644 --- a/internal/http/services/owncloud/ocdav/download.go +++ b/internal/http/services/owncloud/ocdav/download.go @@ -6,6 +6,7 @@ import ( "net/http" "net/url" "path" + "strings" rpc "github.com/cs3org/go-cs3apis/cs3/rpc/v1beta1" link "github.com/cs3org/go-cs3apis/cs3/sharing/link/v1beta1" @@ -17,7 +18,7 @@ import ( // index.php/s/jIKrtrkXCIXwg1y/download?path=%2FHugo&files=Intrinsico func (s *svc) handleLegacyPublicLinkDownload(w http.ResponseWriter, r *http.Request) { - token := r.URL.Path + token := strings.TrimPrefix(r.URL.Path, "/") files := getFilesFromRequest(r) s.downloadFiles(r.Context(), w, token, files) } From 1f36320f63d182e77f55f3ebcbf5442d99b992df Mon Sep 17 00:00:00 2001 From: Gianmaria Del Monte Date: Fri, 18 Nov 2022 10:45:20 +0100 Subject: [PATCH 03/15] authenticate public link --- .../http/services/owncloud/ocdav/download.go | 35 +++++++++++++++++++ 1 file changed, 35 insertions(+) diff --git a/internal/http/services/owncloud/ocdav/download.go b/internal/http/services/owncloud/ocdav/download.go index 90b4ce6830..6c44aae67a 100644 --- a/internal/http/services/owncloud/ocdav/download.go +++ b/internal/http/services/owncloud/ocdav/download.go @@ -8,12 +8,16 @@ import ( "path" "strings" + gateway "github.com/cs3org/go-cs3apis/cs3/gateway/v1beta1" rpc "github.com/cs3org/go-cs3apis/cs3/rpc/v1beta1" link "github.com/cs3org/go-cs3apis/cs3/sharing/link/v1beta1" provider "github.com/cs3org/go-cs3apis/cs3/storage/provider/v1beta1" + ctxpkg "github.com/cs3org/reva/pkg/ctx" "github.com/cs3org/reva/pkg/errtypes" "github.com/cs3org/reva/pkg/rhttp" "github.com/cs3org/reva/pkg/storage/utils/downloader" + "github.com/pkg/errors" + "google.golang.org/grpc/metadata" ) // index.php/s/jIKrtrkXCIXwg1y/download?path=%2FHugo&files=Intrinsico @@ -38,7 +42,38 @@ func getFilesFromRequest(r *http.Request) []string { return files } +func (s *svc) authenticate(ctx context.Context, token string) (context.Context, error) { + // TODO (gdelmont): support password protected public links + c, err := s.getClient() + if err != nil { + return nil, err + } + res, err := c.Authenticate(ctx, &gateway.AuthenticateRequest{ + Type: "publicshares", + ClientId: token, + ClientSecret: "password|", + }) + if err != nil { + return nil, err + } + + // TODO: status check + if res.Status.Code != rpc.Code_CODE_OK { + return nil, errors.New(res.Status.Message) + } + + ctx = metadata.AppendToOutgoingContext(ctx, ctxpkg.TokenHeader, res.Token) + ctx = ctxpkg.ContextSetToken(ctx, res.Token) + + return ctx, nil +} + func (s *svc) downloadFiles(ctx context.Context, w http.ResponseWriter, token string, files []string) { + ctx, err := s.authenticate(ctx, token) + if err != nil { + // TODO + return + } isSingleFileShare, res, err := s.isSingleFileShare(ctx, token, files) if err != nil { // TODO From 2013f104f1a16ba7286927426cce95a76b96a60b Mon Sep 17 00:00:00 2001 From: Gianmaria Del Monte Date: Fri, 18 Nov 2022 10:56:00 +0100 Subject: [PATCH 04/15] use new format for public links --- internal/http/services/owncloud/ocdav/ocdav.go | 16 +++++++++------- 1 file changed, 9 insertions(+), 7 deletions(-) diff --git a/internal/http/services/owncloud/ocdav/ocdav.go b/internal/http/services/owncloud/ocdav/ocdav.go index e42283ca50..63c3b83065 100644 --- a/internal/http/services/owncloud/ocdav/ocdav.go +++ b/internal/http/services/owncloud/ocdav/ocdav.go @@ -179,7 +179,7 @@ func (s *svc) Close() error { } func (s *svc) Unprotected() []string { - return []string{"/status.php", "/remote.php/dav/public-files/", "/apps/files/", "/index.php/f/", "/index.php/s/"} + return []string{"/status.php", "/remote.php/dav/public-files/", "/apps/files/", "/index.php/f/", "/index.php/s/", "/s/"} } func (s *svc) Handler() http.Handler { @@ -204,6 +204,14 @@ func (s *svc) Handler() http.Handler { head, r.URL.Path = router.ShiftPath(r.URL.Path) log.Debug().Str("head", head).Str("tail", r.URL.Path).Msg("http routing") switch head { + case "s": + if strings.HasSuffix(r.URL.Path, "/download") { + r.URL.Path = strings.TrimSuffix(r.URL.Path, "/download") + s.handleLegacyPublicLinkDownload(w, r) + return + } + http.Error(w, "Not Yet Implemented", http.StatusNotImplemented) + return case "status.php": s.doStatus(w, r) return @@ -222,12 +230,6 @@ func (s *svc) Handler() http.Handler { case "index.php": head, r.URL.Path = router.ShiftPath(r.URL.Path) if head == "s" { - if strings.HasSuffix(r.URL.Path, "/download") { - r.URL.Path = strings.TrimSuffix(r.URL.Path, "/download") - s.handleLegacyPublicLinkDownload(w, r) - return - } - token := r.URL.Path rURL := s.c.PublicURL + path.Join(head, token) From 11ba2dfbed36f19ed356dbbe35c64efc91fd6a23 Mon Sep 17 00:00:00 2001 From: Gianmaria Del Monte Date: Fri, 18 Nov 2022 10:59:51 +0100 Subject: [PATCH 05/15] add redirection log --- internal/http/services/owncloud/ocdav/ocdav.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/internal/http/services/owncloud/ocdav/ocdav.go b/internal/http/services/owncloud/ocdav/ocdav.go index 63c3b83065..ca202463d5 100644 --- a/internal/http/services/owncloud/ocdav/ocdav.go +++ b/internal/http/services/owncloud/ocdav/ocdav.go @@ -232,7 +232,7 @@ func (s *svc) Handler() http.Handler { if head == "s" { token := r.URL.Path rURL := s.c.PublicURL + path.Join(head, token) - + log.Debug().Msgf("%s redirected to %s", path.Join(head, r.URL.Path), rURL) http.Redirect(w, r, rURL, http.StatusMovedPermanently) return } From f36b50df44bde31c29a0af7af5ca5f3183bb156d Mon Sep 17 00:00:00 2001 From: Gianmaria Del Monte Date: Fri, 18 Nov 2022 14:16:20 +0100 Subject: [PATCH 06/15] reset oldpath before redirection --- internal/http/services/owncloud/ocdav/ocdav.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/internal/http/services/owncloud/ocdav/ocdav.go b/internal/http/services/owncloud/ocdav/ocdav.go index ca202463d5..0d7055fd9e 100644 --- a/internal/http/services/owncloud/ocdav/ocdav.go +++ b/internal/http/services/owncloud/ocdav/ocdav.go @@ -232,7 +232,7 @@ func (s *svc) Handler() http.Handler { if head == "s" { token := r.URL.Path rURL := s.c.PublicURL + path.Join(head, token) - log.Debug().Msgf("%s redirected to %s", path.Join(head, r.URL.Path), rURL) + r.URL.Path = "/" // reset old path for redirection http.Redirect(w, r, rURL, http.StatusMovedPermanently) return } From f646505371c4b74038e3c11244f5edaf58008b15 Mon Sep 17 00:00:00 2001 From: Gianmaria Del Monte Date: Fri, 18 Nov 2022 14:28:19 +0100 Subject: [PATCH 07/15] better error handling --- .../http/services/owncloud/ocdav/download.go | 34 ++++++++++++++----- 1 file changed, 25 insertions(+), 9 deletions(-) diff --git a/internal/http/services/owncloud/ocdav/download.go b/internal/http/services/owncloud/ocdav/download.go index 6c44aae67a..cbc54ac929 100644 --- a/internal/http/services/owncloud/ocdav/download.go +++ b/internal/http/services/owncloud/ocdav/download.go @@ -12,11 +12,13 @@ import ( rpc "github.com/cs3org/go-cs3apis/cs3/rpc/v1beta1" link "github.com/cs3org/go-cs3apis/cs3/sharing/link/v1beta1" provider "github.com/cs3org/go-cs3apis/cs3/storage/provider/v1beta1" + "github.com/cs3org/reva/pkg/appctx" ctxpkg "github.com/cs3org/reva/pkg/ctx" "github.com/cs3org/reva/pkg/errtypes" "github.com/cs3org/reva/pkg/rhttp" "github.com/cs3org/reva/pkg/storage/utils/downloader" "github.com/pkg/errors" + "github.com/rs/zerolog" "google.golang.org/grpc/metadata" ) @@ -57,8 +59,10 @@ func (s *svc) authenticate(ctx context.Context, token string) (context.Context, return nil, err } - // TODO: status check if res.Status.Code != rpc.Code_CODE_OK { + if res.Status.Code == rpc.Code_CODE_NOT_FOUND { + return nil, errtypes.NotFound(token) + } return nil, errors.New(res.Status.Message) } @@ -68,15 +72,26 @@ func (s *svc) authenticate(ctx context.Context, token string) (context.Context, return ctx, nil } +func (s *svc) handleHttpError(w http.ResponseWriter, err error, log *zerolog.Logger) { + log.Err(err).Msg("ocdav: got error") + switch err.(type) { + case errtypes.NotFound: + http.Error(w, "Resource not found", http.StatusNotFound) + default: + http.Error(w, err.Error(), http.StatusInternalServerError) + } +} + func (s *svc) downloadFiles(ctx context.Context, w http.ResponseWriter, token string, files []string) { + log := appctx.GetLogger(ctx) ctx, err := s.authenticate(ctx, token) if err != nil { - // TODO + s.handleHttpError(w, err, log) return } isSingleFileShare, res, err := s.isSingleFileShare(ctx, token, files) if err != nil { - // TODO + s.handleHttpError(w, err, log) return } if isSingleFileShare { @@ -143,15 +158,16 @@ func (s *svc) getResourceFromPublicLinkToken(ctx context.Context, token, file st } func (s *svc) downloadFile(ctx context.Context, w http.ResponseWriter, res *provider.ResourceInfo) { + log := appctx.GetLogger(ctx) c, err := s.getClient() if err != nil { - // TODO + s.handleHttpError(w, err, log) return } d := downloader.NewDownloader(c) r, err := d.Download(ctx, res.Path) if err != nil { - // TODO + s.handleHttpError(w, err, log) return } defer r.Close() @@ -160,7 +176,7 @@ func (s *svc) downloadFile(ctx context.Context, w http.ResponseWriter, res *prov _, err = io.Copy(w, r) if err != nil { - http.Error(w, "", http.StatusInternalServerError) + s.handleHttpError(w, err, log) return } } @@ -186,6 +202,7 @@ func prepareArchiverURL(endpoint string, files []string) string { } func (s *svc) downloadArchive(ctx context.Context, w http.ResponseWriter, token string, files []string) { + log := appctx.GetLogger(ctx) resources := getPublicLinkResources(s.c.PublicLinkDownload.PublicFolder, token, files) url := prepareArchiverURL(s.c.PublicLinkDownload.ArchiverEndpoint, resources) @@ -197,8 +214,7 @@ func (s *svc) downloadArchive(ctx context.Context, w http.ResponseWriter, token res, err := s.client.Do(req) if err != nil { - // TODO - http.Error(w, "", http.StatusInternalServerError) + s.handleHttpError(w, err, log) return } defer res.Body.Close() @@ -207,7 +223,7 @@ func (s *svc) downloadArchive(ctx context.Context, w http.ResponseWriter, token _, err = io.Copy(w, res.Body) if err != nil { - http.Error(w, "", http.StatusInternalServerError) + s.handleHttpError(w, err, log) return } } From b2c79ba4425c6d1b3ab933e187ca21ac032cbe90 Mon Sep 17 00:00:00 2001 From: Gianmaria Del Monte Date: Fri, 18 Nov 2022 14:36:55 +0100 Subject: [PATCH 08/15] fix config --- internal/http/services/owncloud/ocdav/ocdav.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/internal/http/services/owncloud/ocdav/ocdav.go b/internal/http/services/owncloud/ocdav/ocdav.go index 0d7055fd9e..6dc5bc6312 100644 --- a/internal/http/services/owncloud/ocdav/ocdav.go +++ b/internal/http/services/owncloud/ocdav/ocdav.go @@ -109,7 +109,7 @@ type Config struct { PublicURL string `mapstructure:"public_url"` FavoriteStorageDriver string `mapstructure:"favorite_storage_driver"` FavoriteStorageDrivers map[string]map[string]interface{} `mapstructure:"favorite_storage_drivers"` - PublicLinkDownload ConfigPublicLinkDownload `mapstructure:"publiclink_download"` + PublicLinkDownload *ConfigPublicLinkDownload `mapstructure:"publiclink_download"` } func (c *Config) init() { From c56c5bddce63343e83546672d9b71a95b313641e Mon Sep 17 00:00:00 2001 From: Gianmaria Del Monte Date: Fri, 18 Nov 2022 14:44:48 +0100 Subject: [PATCH 09/15] fix archiver url creation --- internal/http/services/owncloud/ocdav/download.go | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/internal/http/services/owncloud/ocdav/download.go b/internal/http/services/owncloud/ocdav/download.go index cbc54ac929..8e4ae6ac70 100644 --- a/internal/http/services/owncloud/ocdav/download.go +++ b/internal/http/services/owncloud/ocdav/download.go @@ -194,10 +194,8 @@ func prepareArchiverURL(endpoint string, files []string) string { for _, f := range files { q.Add("file", f) } - u := url.URL{ - RawPath: endpoint, - RawQuery: q.Encode(), - } + u, _ := url.Parse(endpoint) + u.RawQuery = q.Encode() return u.String() } @@ -208,7 +206,7 @@ func (s *svc) downloadArchive(ctx context.Context, w http.ResponseWriter, token req, err := rhttp.NewRequest(ctx, http.MethodGet, url, nil) if err != nil { - http.Error(w, "", http.StatusInternalServerError) + s.handleHttpError(w, err, log) return } From 349dd69506c62eba11c85e71ccc91fed20a25c9b Mon Sep 17 00:00:00 2001 From: Gianmaria Del Monte Date: Fri, 18 Nov 2022 14:49:41 +0100 Subject: [PATCH 10/15] fix folders --- internal/http/services/owncloud/ocdav/download.go | 3 +++ 1 file changed, 3 insertions(+) diff --git a/internal/http/services/owncloud/ocdav/download.go b/internal/http/services/owncloud/ocdav/download.go index 8e4ae6ac70..7b00231baa 100644 --- a/internal/http/services/owncloud/ocdav/download.go +++ b/internal/http/services/owncloud/ocdav/download.go @@ -186,6 +186,9 @@ func getPublicLinkResources(rootFolder, token string, files []string) []string { for _, f := range files { r = append(r, path.Join(rootFolder, token, f)) } + if len(r) == 0 { + r = []string{path.Join(rootFolder, token)} + } return r } From 13cd246f37ffab6e8a3748f27bdeafeeeaf9b4d6 Mon Sep 17 00:00:00 2001 From: Gianmaria Del Monte Date: Fri, 18 Nov 2022 14:58:44 +0100 Subject: [PATCH 11/15] handle archive error --- internal/http/services/owncloud/ocdav/download.go | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/internal/http/services/owncloud/ocdav/download.go b/internal/http/services/owncloud/ocdav/download.go index 7b00231baa..a486702cb3 100644 --- a/internal/http/services/owncloud/ocdav/download.go +++ b/internal/http/services/owncloud/ocdav/download.go @@ -73,7 +73,7 @@ func (s *svc) authenticate(ctx context.Context, token string) (context.Context, } func (s *svc) handleHttpError(w http.ResponseWriter, err error, log *zerolog.Logger) { - log.Err(err).Msg("ocdav: got error") + log.Error().Err(err).Msg("ocdav: got error") switch err.(type) { case errtypes.NotFound: http.Error(w, "Resource not found", http.StatusNotFound) @@ -220,6 +220,11 @@ func (s *svc) downloadArchive(ctx context.Context, w http.ResponseWriter, token } defer res.Body.Close() + if res.StatusCode != http.StatusOK { + s.handleArchiveError(w, res.StatusCode, log) + return + } + w.WriteHeader(http.StatusOK) _, err = io.Copy(w, res.Body) @@ -228,3 +233,8 @@ func (s *svc) downloadArchive(ctx context.Context, w http.ResponseWriter, token return } } + +func (s *svc) handleArchiveError(w http.ResponseWriter, code int, log *zerolog.Logger) { + log.Error().Int("code", code).Msg("ocdav: got error code from archiver") + w.WriteHeader(code) +} From 3986502c2340764ed7657c202c7ddd86c5bce581 Mon Sep 17 00:00:00 2001 From: Gianmaria Del Monte Date: Fri, 18 Nov 2022 15:10:10 +0100 Subject: [PATCH 12/15] set token for archiver service --- internal/http/services/owncloud/ocdav/download.go | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/internal/http/services/owncloud/ocdav/download.go b/internal/http/services/owncloud/ocdav/download.go index a486702cb3..c23a78100e 100644 --- a/internal/http/services/owncloud/ocdav/download.go +++ b/internal/http/services/owncloud/ocdav/download.go @@ -213,6 +213,11 @@ func (s *svc) downloadArchive(ctx context.Context, w http.ResponseWriter, token return } + // FIXME: this only works for CERNBox config, + // as the bearer authentication is used to set the reva token + authtkn := ctxpkg.ContextMustGetToken(ctx) + req.Header.Add("Authorization", "Bearer "+authtkn) + res, err := s.client.Do(req) if err != nil { s.handleHttpError(w, err, log) From c41f679eabcae5cc39cde0d8dfab7c1f4a8e14c1 Mon Sep 17 00:00:00 2001 From: Gianmaria Del Monte Date: Fri, 18 Nov 2022 15:23:51 +0100 Subject: [PATCH 13/15] fix archive call --- internal/http/services/owncloud/ocdav/download.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/internal/http/services/owncloud/ocdav/download.go b/internal/http/services/owncloud/ocdav/download.go index c23a78100e..00be88a0d9 100644 --- a/internal/http/services/owncloud/ocdav/download.go +++ b/internal/http/services/owncloud/ocdav/download.go @@ -195,7 +195,7 @@ func getPublicLinkResources(rootFolder, token string, files []string) []string { func prepareArchiverURL(endpoint string, files []string) string { q := url.Values{} for _, f := range files { - q.Add("file", f) + q.Add("path", f) } u, _ := url.Parse(endpoint) u.RawQuery = q.Encode() From 8678c14e4b0b1a1ab11afde4aba47ff20053f7b6 Mon Sep 17 00:00:00 2001 From: Gianmaria Del Monte Date: Fri, 18 Nov 2022 17:26:32 +0100 Subject: [PATCH 14/15] use archiver obj instead of endpoint --- .../http/services/owncloud/ocdav/download.go | 41 +++++-------------- .../http/services/owncloud/ocdav/ocdav.go | 5 ++- 2 files changed, 13 insertions(+), 33 deletions(-) diff --git a/internal/http/services/owncloud/ocdav/download.go b/internal/http/services/owncloud/ocdav/download.go index 00be88a0d9..f1c3b5a7b9 100644 --- a/internal/http/services/owncloud/ocdav/download.go +++ b/internal/http/services/owncloud/ocdav/download.go @@ -4,7 +4,6 @@ import ( "context" "io" "net/http" - "net/url" "path" "strings" @@ -12,11 +11,13 @@ import ( rpc "github.com/cs3org/go-cs3apis/cs3/rpc/v1beta1" link "github.com/cs3org/go-cs3apis/cs3/sharing/link/v1beta1" provider "github.com/cs3org/go-cs3apis/cs3/storage/provider/v1beta1" + "github.com/cs3org/reva/internal/http/services/archiver/manager" "github.com/cs3org/reva/pkg/appctx" ctxpkg "github.com/cs3org/reva/pkg/ctx" "github.com/cs3org/reva/pkg/errtypes" "github.com/cs3org/reva/pkg/rhttp" "github.com/cs3org/reva/pkg/storage/utils/downloader" + "github.com/cs3org/reva/pkg/storage/utils/walker" "github.com/pkg/errors" "github.com/rs/zerolog" "google.golang.org/grpc/metadata" @@ -192,54 +193,32 @@ func getPublicLinkResources(rootFolder, token string, files []string) []string { return r } -func prepareArchiverURL(endpoint string, files []string) string { - q := url.Values{} - for _, f := range files { - q.Add("path", f) - } - u, _ := url.Parse(endpoint) - u.RawQuery = q.Encode() - return u.String() -} - func (s *svc) downloadArchive(ctx context.Context, w http.ResponseWriter, token string, files []string) { log := appctx.GetLogger(ctx) resources := getPublicLinkResources(s.c.PublicLinkDownload.PublicFolder, token, files) - url := prepareArchiverURL(s.c.PublicLinkDownload.ArchiverEndpoint, resources) - req, err := rhttp.NewRequest(ctx, http.MethodGet, url, nil) + gtw, err := s.getClient() if err != nil { s.handleHttpError(w, err, log) return } - // FIXME: this only works for CERNBox config, - // as the bearer authentication is used to set the reva token - authtkn := ctxpkg.ContextMustGetToken(ctx) - req.Header.Add("Authorization", "Bearer "+authtkn) + downloader := downloader.NewDownloader(gtw, rhttp.Context(ctx)) + walker := walker.NewWalker(gtw) - res, err := s.client.Do(req) + archiver, err := manager.NewArchiver(resources, walker, downloader, manager.Config{ + MaxNumFiles: s.c.PublicLinkDownload.MaxNumFiles, + MaxSize: s.c.PublicLinkDownload.MaxSize, + }) if err != nil { s.handleHttpError(w, err, log) return } - defer res.Body.Close() - - if res.StatusCode != http.StatusOK { - s.handleArchiveError(w, res.StatusCode, log) - return - } w.WriteHeader(http.StatusOK) - _, err = io.Copy(w, res.Body) - if err != nil { + if err := archiver.CreateTar(ctx, w); err != nil { s.handleHttpError(w, err, log) return } } - -func (s *svc) handleArchiveError(w http.ResponseWriter, code int, log *zerolog.Logger) { - log.Error().Int("code", code).Msg("ocdav: got error code from archiver") - w.WriteHeader(code) -} diff --git a/internal/http/services/owncloud/ocdav/ocdav.go b/internal/http/services/owncloud/ocdav/ocdav.go index 6dc5bc6312..7594a7f22d 100644 --- a/internal/http/services/owncloud/ocdav/ocdav.go +++ b/internal/http/services/owncloud/ocdav/ocdav.go @@ -84,8 +84,9 @@ func init() { } type ConfigPublicLinkDownload struct { - ArchiverEndpoint string `mapstructure:"archiver_endpoint"` - PublicFolder string `mapstructure:"public_folder"` + MaxNumFiles int64 `mapstructure:"max_num_files"` + MaxSize int64 `mapstructure:"max_size"` + PublicFolder string `mapstructure:"public_folder"` } // Config holds the config options that need to be passed down to all ocdav handlers From 2f2cdc82d6e955e8534e89caff632364c5786180 Mon Sep 17 00:00:00 2001 From: Gianmaria Del Monte Date: Fri, 18 Nov 2022 17:35:12 +0100 Subject: [PATCH 15/15] better error handling --- internal/http/services/owncloud/ocdav/download.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/internal/http/services/owncloud/ocdav/download.go b/internal/http/services/owncloud/ocdav/download.go index f1c3b5a7b9..cbce3c79df 100644 --- a/internal/http/services/owncloud/ocdav/download.go +++ b/internal/http/services/owncloud/ocdav/download.go @@ -78,6 +78,8 @@ func (s *svc) handleHttpError(w http.ResponseWriter, err error, log *zerolog.Log switch err.(type) { case errtypes.NotFound: http.Error(w, "Resource not found", http.StatusNotFound) + case manager.ErrMaxSize, manager.ErrMaxFileCount: + http.Error(w, err.Error(), http.StatusRequestEntityTooLarge) default: http.Error(w, err.Error(), http.StatusInternalServerError) } @@ -215,8 +217,6 @@ func (s *svc) downloadArchive(ctx context.Context, w http.ResponseWriter, token return } - w.WriteHeader(http.StatusOK) - if err := archiver.CreateTar(ctx, w); err != nil { s.handleHttpError(w, err, log) return