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 (
2022-05-09 18:54:51 +03:00
"path"
"time"
2022-06-12 18:51:54 +03:00
git_model "code.gitea.io/gitea/models/git"
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"
2021-04-12 17:49:26 +03:00
"code.gitea.io/gitea/modules/httpcache"
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"
2021-08-21 21:22:06 +03:00
"code.gitea.io/gitea/modules/setting"
"code.gitea.io/gitea/modules/storage"
2021-06-09 02:33:54 +03:00
"code.gitea.io/gitea/routers/common"
2014-04-15 20:27:29 +04:00
)
2019-02-12 18:09:43 +03:00
// ServeBlobOrLFS download a git.Blob redirecting to LFS if necessary
2022-05-09 18:54:51 +03:00
func ServeBlobOrLFS ( ctx * context . Context , blob * git . Blob , lastModified time . Time ) error {
if httpcache . HandleGenericETagTimeCache ( ctx . Req , ctx . Resp , ` " ` + blob . ID . String ( ) + ` " ` , lastModified ) {
2021-04-12 17:49:26 +03:00
return nil
}
2019-02-12 18:09:43 +03:00
dataRc , err := blob . DataAsync ( )
if err != nil {
return err
}
2021-05-10 04:27:03 +03:00
closed := false
2019-06-12 22:41:28 +03:00
defer func ( ) {
2021-05-10 04:27:03 +03:00
if closed {
return
}
2019-06-12 22:41:28 +03:00
if err = dataRc . Close ( ) ; err != nil {
log . Error ( "ServeBlobOrLFS: Close: %v" , err )
}
} ( )
2019-02-12 18:09:43 +03:00
2021-04-09 01:25:57 +03:00
pointer , _ := lfs . ReadPointer ( dataRc )
if pointer . IsValid ( ) {
2022-06-12 18:51:54 +03:00
meta , _ := git_model . GetLFSMetaObjectByOid ( ctx . Repo . Repository . ID , pointer . Oid )
2019-02-12 18:09:43 +03:00
if meta == nil {
2021-05-10 04:27:03 +03:00
if err = dataRc . Close ( ) ; err != nil {
log . Error ( "ServeBlobOrLFS: Close: %v" , err )
}
closed = true
2022-05-09 18:54:51 +03:00
return common . ServeBlob ( ctx , blob , lastModified )
2019-02-12 18:09:43 +03:00
}
2021-04-12 17:49:26 +03:00
if httpcache . HandleGenericETagCache ( ctx . Req , ctx . Resp , ` " ` + pointer . Oid + ` " ` ) {
return nil
}
2021-08-21 21:22:06 +03:00
if setting . LFS . ServeDirect {
2022-01-20 20:46:10 +03:00
// If we have a signed url (S3, object storage), redirect to this directly.
2021-08-21 21:22:06 +03:00
u , err := storage . LFS . URL ( pointer . RelativePath ( ) , blob . Name ( ) )
if u != nil && err == nil {
ctx . Redirect ( u . String ( ) )
return nil
}
}
2021-04-09 01:25:57 +03:00
lfsDataRc , err := lfs . ReadMetaObject ( meta . Pointer )
2019-02-12 18:09:43 +03:00
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-06-09 02:33:54 +03:00
return common . ServeData ( ctx , ctx . Repo . TreePath , meta . Size , lfsDataRc )
2019-02-12 18:09:43 +03:00
}
2021-05-10 04:27:03 +03:00
if err = dataRc . Close ( ) ; err != nil {
log . Error ( "ServeBlobOrLFS: Close: %v" , err )
}
closed = true
2019-02-12 18:09:43 +03:00
2022-05-09 18:54:51 +03:00
return common . ServeBlob ( ctx , blob , lastModified )
2019-02-12 18:09:43 +03:00
}
2022-05-09 18:54:51 +03:00
func getBlobForEntry ( ctx * context . Context ) ( blob * git . Blob , lastModified time . Time ) {
entry , err := ctx . Repo . Commit . GetTreeEntryByPath ( 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 ) {
2022-05-09 18:54:51 +03:00
ctx . NotFound ( "GetTreeEntryByPath" , err )
2014-11-17 05:32:26 +03:00
} else {
2022-05-09 18:54:51 +03:00
ctx . ServerError ( "GetTreeEntryByPath" , err )
2014-11-17 05:32:26 +03:00
}
return
}
2022-05-09 18:54:51 +03:00
if entry . IsDir ( ) || entry . IsSubModule ( ) {
ctx . NotFound ( "getBlobForEntry" , nil )
return
}
2022-07-25 18:39:42 +03:00
info , _ , err := git . Entries ( [ ] * git . TreeEntry { entry } ) . GetCommitsInfo ( ctx , ctx . Repo . Commit , path . Dir ( "/" + ctx . Repo . TreePath ) [ 1 : ] )
2022-05-09 18:54:51 +03:00
if err != nil {
ctx . ServerError ( "GetCommitsInfo" , err )
return
}
if len ( info ) == 1 {
// Not Modified
lastModified = info [ 0 ] . Commit . Committer . When
}
blob = entry . Blob ( )
2022-06-20 13:02:49 +03:00
return blob , lastModified
2022-05-09 18:54:51 +03:00
}
// SingleDownload download a file by repos path
func SingleDownload ( ctx * context . Context ) {
blob , lastModified := getBlobForEntry ( ctx )
if blob == nil {
return
}
if err := common . ServeBlob ( ctx , blob , lastModified ) ; 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 ) {
2022-05-09 18:54:51 +03:00
blob , lastModified := getBlobForEntry ( ctx )
if blob == nil {
2019-02-12 18:09:43 +03:00
return
}
2022-05-09 18:54:51 +03:00
if err := ServeBlobOrLFS ( ctx , blob , lastModified ) ; err != nil {
2019-02-12 18:09:43 +03:00
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
}
2022-05-09 18:54:51 +03:00
if err = common . ServeBlob ( ctx , blob , time . Time { } ) ; err != nil {
2018-11-18 21:45:40 +03:00
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
}
2022-05-09 18:54:51 +03:00
if err = ServeBlobOrLFS ( ctx , blob , time . Time { } ) ; err != nil {
2019-02-12 18:09:43 +03:00
ctx . ServerError ( "ServeBlob" , err )
}
}