2014-04-15 20:27:29 +04:00
// Copyright 2014 The Gogs Authors. All rights reserved.
2018-11-18 21:45:40 +03:00
// Copyright 2018 The Gitea Authors. All rights reserved.
2014-04-15 20:27:29 +04:00
// Use of this source code is governed by a MIT-style
// license that can be found in the LICENSE file.
package repo
import (
2016-11-26 15:08:31 +03:00
"fmt"
2014-07-26 10:28:04 +04:00
"io"
2017-05-05 09:03:54 +03:00
"path"
2016-11-26 16:26:03 +03:00
"strings"
2014-04-15 20:27:29 +04:00
2016-11-10 19:24:48 +03:00
"code.gitea.io/gitea/modules/base"
2020-01-17 15:16:05 +03:00
"code.gitea.io/gitea/modules/charset"
2016-11-10 19:24:48 +03:00
"code.gitea.io/gitea/modules/context"
2019-03-27 12:33:00 +03:00
"code.gitea.io/gitea/modules/git"
2019-02-12 18:09:43 +03:00
"code.gitea.io/gitea/modules/lfs"
2019-06-12 22:41:28 +03:00
"code.gitea.io/gitea/modules/log"
2014-04-15 20:27:29 +04:00
)
2016-11-24 10:04:31 +03:00
// ServeData download file from io.Reader
2021-02-05 23:10:40 +03:00
func ServeData ( ctx * context . Context , name string , size int64 , reader io . Reader ) error {
2014-07-26 10:28:04 +04:00
buf := make ( [ ] byte , 1024 )
2020-08-18 07:23:45 +03:00
n , err := reader . Read ( buf )
if err != nil && err != io . EOF {
return err
}
2017-04-20 05:38:56 +03:00
if n >= 0 {
2014-07-26 10:28:04 +04:00
buf = buf [ : n ]
}
2016-11-26 15:08:31 +03:00
ctx . Resp . Header ( ) . Set ( "Cache-Control" , "public,max-age=86400" )
2021-02-05 23:10:40 +03:00
if size >= 0 {
ctx . Resp . Header ( ) . Set ( "Content-Length" , fmt . Sprintf ( "%d" , size ) )
} else {
log . Error ( "ServeData called to serve data: %s with size < 0: %d" , name , size )
}
2017-05-05 09:03:54 +03:00
name = path . Base ( name )
2016-11-26 15:08:31 +03:00
2016-11-26 16:26:03 +03:00
// Google Chrome dislike commas in filenames, so let's change it to a space
2020-10-11 23:27:20 +03:00
name = strings . ReplaceAll ( name , "," , " " )
2016-11-26 16:26:03 +03:00
2016-11-26 15:08:31 +03:00
if base . IsTextFile ( buf ) || ctx . QueryBool ( "render" ) {
2020-01-17 15:16:05 +03:00
cs , err := charset . DetectEncoding ( buf )
if err != nil {
log . Error ( "Detect raw file %s charset failed: %v, using by default utf-8" , name , err )
cs = "utf-8"
}
ctx . Resp . Header ( ) . Set ( "Content-Type" , "text/plain; charset=" + strings . ToLower ( cs ) )
2016-11-26 15:08:31 +03:00
} else if base . IsImageFile ( buf ) || base . IsPDFFile ( buf ) {
ctx . Resp . Header ( ) . Set ( "Content-Disposition" , fmt . Sprintf ( ` inline; filename="%s" ` , name ) )
2020-08-13 20:18:18 +03:00
ctx . Resp . Header ( ) . Set ( "Access-Control-Expose-Headers" , "Content-Disposition" )
2021-01-13 06:45:19 +03:00
if base . IsSVGImageFile ( buf ) {
ctx . Resp . Header ( ) . Set ( "Content-Security-Policy" , "default-src 'none'; style-src 'unsafe-inline'; sandbox" )
ctx . Resp . Header ( ) . Set ( "X-Content-Type-Options" , "nosniff" )
ctx . Resp . Header ( ) . Set ( "Content-Type" , base . SVGMimeType )
}
2016-11-26 15:08:31 +03:00
} else {
ctx . Resp . Header ( ) . Set ( "Content-Disposition" , fmt . Sprintf ( ` attachment; filename="%s" ` , name ) )
2020-08-13 20:18:18 +03:00
ctx . Resp . Header ( ) . Set ( "Access-Control-Expose-Headers" , "Content-Disposition" )
2014-07-26 10:28:04 +04:00
}
2016-11-26 15:08:31 +03:00
2020-08-18 07:23:45 +03:00
_ , err = ctx . Resp . Write ( buf )
2019-06-12 22:41:28 +03:00
if err != nil {
return err
}
_ , err = io . Copy ( ctx . Resp , reader )
2015-01-31 23:27:57 +03:00
return err
2014-11-17 05:32:26 +03:00
}
2016-11-24 10:04:31 +03:00
// ServeBlob download a git.Blob
2016-03-11 19:56:52 +03:00
func ServeBlob ( ctx * context . Context , blob * git . Blob ) error {
2017-11-29 04:50:39 +03:00
dataRc , err := blob . DataAsync ( )
2015-08-11 23:49:51 +03:00
if err != nil {
return err
}
2019-06-12 22:41:28 +03:00
defer func ( ) {
if err = dataRc . Close ( ) ; err != nil {
log . Error ( "ServeBlob: Close: %v" , err )
}
} ( )
2015-08-11 23:49:51 +03:00
2021-02-05 23:10:40 +03:00
return ServeData ( ctx , ctx . Repo . TreePath , blob . Size ( ) , dataRc )
2015-08-11 23:49:51 +03:00
}
2019-02-12 18:09:43 +03:00
// ServeBlobOrLFS download a git.Blob redirecting to LFS if necessary
func ServeBlobOrLFS ( ctx * context . Context , blob * git . Blob ) error {
dataRc , err := blob . DataAsync ( )
if err != nil {
return err
}
2019-06-12 22:41:28 +03:00
defer func ( ) {
if err = dataRc . Close ( ) ; err != nil {
log . Error ( "ServeBlobOrLFS: Close: %v" , err )
}
} ( )
2019-02-12 18:09:43 +03:00
if meta , _ := lfs . ReadPointerFile ( dataRc ) ; meta != nil {
meta , _ = ctx . Repo . Repository . GetLFSMetaObjectByOid ( meta . Oid )
if meta == nil {
return ServeBlob ( ctx , blob )
}
lfsDataRc , err := lfs . ReadMetaObject ( meta )
if err != nil {
return err
}
2019-10-16 08:55:31 +03:00
defer func ( ) {
if err = lfsDataRc . Close ( ) ; err != nil {
log . Error ( "ServeBlobOrLFS: Close: %v" , err )
}
} ( )
2021-02-05 23:10:40 +03:00
return ServeData ( ctx , ctx . Repo . TreePath , meta . Size , lfsDataRc )
2019-02-12 18:09:43 +03:00
}
return ServeBlob ( ctx , blob )
}
2016-11-24 10:04:31 +03:00
// SingleDownload download a file by repos path
2016-03-11 19:56:52 +03:00
func SingleDownload ( ctx * context . Context ) {
2016-08-25 07:35:03 +03:00
blob , err := ctx . Repo . Commit . GetBlobByPath ( ctx . Repo . TreePath )
2014-11-17 05:32:26 +03:00
if err != nil {
2015-12-10 04:46:05 +03:00
if git . IsErrNotExist ( err ) {
2018-01-11 00:34:17 +03:00
ctx . NotFound ( "GetBlobByPath" , nil )
2014-11-17 05:32:26 +03:00
} else {
2018-01-11 00:34:17 +03:00
ctx . ServerError ( "GetBlobByPath" , err )
2014-11-17 05:32:26 +03:00
}
return
}
if err = ServeBlob ( ctx , blob ) ; err != nil {
2018-01-11 00:34:17 +03:00
ctx . ServerError ( "ServeBlob" , err )
2014-11-17 05:32:26 +03:00
}
2014-04-15 20:27:29 +04:00
}
2018-11-18 21:45:40 +03:00
2019-02-12 18:09:43 +03:00
// SingleDownloadOrLFS download a file by repos path redirecting to LFS if necessary
func SingleDownloadOrLFS ( ctx * context . Context ) {
blob , err := ctx . Repo . Commit . GetBlobByPath ( ctx . Repo . TreePath )
if err != nil {
if git . IsErrNotExist ( err ) {
ctx . NotFound ( "GetBlobByPath" , nil )
} else {
ctx . ServerError ( "GetBlobByPath" , err )
}
return
}
if err = ServeBlobOrLFS ( ctx , blob ) ; err != nil {
ctx . ServerError ( "ServeBlobOrLFS" , err )
}
}
2018-11-18 21:45:40 +03:00
// DownloadByID download a file by sha1 ID
func DownloadByID ( ctx * context . Context ) {
blob , err := ctx . Repo . GitRepo . GetBlob ( ctx . Params ( "sha" ) )
if err != nil {
if git . IsErrNotExist ( err ) {
ctx . NotFound ( "GetBlob" , nil )
} else {
ctx . ServerError ( "GetBlob" , err )
}
return
}
if err = ServeBlob ( ctx , blob ) ; err != nil {
ctx . ServerError ( "ServeBlob" , err )
}
}
2019-02-12 18:09:43 +03:00
// DownloadByIDOrLFS download a file by sha1 ID taking account of LFS
func DownloadByIDOrLFS ( ctx * context . Context ) {
blob , err := ctx . Repo . GitRepo . GetBlob ( ctx . Params ( "sha" ) )
if err != nil {
if git . IsErrNotExist ( err ) {
ctx . NotFound ( "GetBlob" , nil )
} else {
ctx . ServerError ( "GetBlob" , err )
}
return
}
if err = ServeBlobOrLFS ( ctx , blob ) ; err != nil {
ctx . ServerError ( "ServeBlob" , err )
}
}