2021-06-09 07:33:54 +08:00
// Copyright 2021 The Gitea Authors. All rights reserved.
2022-11-27 13:20:29 -05:00
// SPDX-License-Identifier: MIT
2021-06-09 07:33:54 +08:00
package common
import (
"io"
"path"
"path/filepath"
"strings"
2022-05-09 16:54:51 +01:00
"time"
2021-06-09 07:33:54 +08:00
2022-07-29 17:26:55 +02:00
charsetModule "code.gitea.io/gitea/modules/charset"
2021-06-09 07:33:54 +08:00
"code.gitea.io/gitea/modules/context"
"code.gitea.io/gitea/modules/git"
"code.gitea.io/gitea/modules/httpcache"
"code.gitea.io/gitea/modules/log"
"code.gitea.io/gitea/modules/setting"
"code.gitea.io/gitea/modules/typesniffer"
2021-10-24 23:12:43 +02:00
"code.gitea.io/gitea/modules/util"
2021-06-09 07:33:54 +08:00
)
// ServeBlob download a git.Blob
2022-05-09 16:54:51 +01:00
func ServeBlob ( ctx * context . Context , blob * git . Blob , lastModified time . Time ) error {
if httpcache . HandleGenericETagTimeCache ( ctx . Req , ctx . Resp , ` " ` + blob . ID . String ( ) + ` " ` , lastModified ) {
2021-06-09 07:33:54 +08:00
return nil
}
dataRc , err := blob . DataAsync ( )
if err != nil {
return err
}
defer func ( ) {
if err = dataRc . Close ( ) ; err != nil {
log . Error ( "ServeBlob: Close: %v" , err )
}
} ( )
return ServeData ( ctx , ctx . Repo . TreePath , blob . Size ( ) , dataRc )
}
// ServeData download file from io.Reader
2022-07-29 17:26:55 +02:00
func ServeData ( ctx * context . Context , filePath string , size int64 , reader io . Reader ) error {
2021-06-09 07:33:54 +08:00
buf := make ( [ ] byte , 1024 )
2021-10-24 23:12:43 +02:00
n , err := util . ReadAtMost ( reader , buf )
if err != nil {
2021-06-09 07:33:54 +08:00
return err
}
if n >= 0 {
buf = buf [ : n ]
}
2022-11-24 15:25:13 +01:00
opts := & context . ServeHeaderOptions {
Filename : path . Base ( filePath ) ,
}
2021-06-09 07:33:54 +08:00
if size >= 0 {
2022-11-24 15:25:13 +01:00
opts . ContentLength = & size
2021-06-09 07:33:54 +08:00
} else {
2022-07-29 17:26:55 +02:00
log . Error ( "ServeData called to serve data: %s with size < 0: %d" , filePath , size )
2021-06-09 07:33:54 +08:00
}
2022-07-29 17:26:55 +02:00
sniffedType := typesniffer . DetectContentType ( buf )
isPlain := sniffedType . IsText ( ) || ctx . FormBool ( "render" )
2021-06-09 07:33:54 +08:00
2021-06-30 23:31:54 +02:00
if setting . MimeTypeMap . Enabled {
2022-11-17 18:55:15 +01:00
fileExtension := strings . ToLower ( filepath . Ext ( filePath ) )
opts . ContentType = setting . MimeTypeMap . Map [ fileExtension ]
2021-06-30 23:31:54 +02:00
}
2022-07-29 17:26:55 +02:00
2022-11-17 18:55:15 +01:00
if opts . ContentType == "" {
2022-07-29 17:26:55 +02:00
if sniffedType . IsBrowsableBinaryType ( ) {
2022-11-17 18:55:15 +01:00
opts . ContentType = sniffedType . GetMimeType ( )
2022-07-29 17:26:55 +02:00
} else if isPlain {
2022-11-17 18:55:15 +01:00
opts . ContentType = "text/plain"
2022-07-29 17:26:55 +02:00
} else {
2022-11-17 18:55:15 +01:00
opts . ContentType = typesniffer . ApplicationOctetStream
2021-06-09 07:33:54 +08:00
}
2022-07-29 17:26:55 +02:00
}
if isPlain {
2022-11-17 18:55:15 +01:00
var charset string
2022-07-29 17:26:55 +02:00
charset , err = charsetModule . DetectEncoding ( buf )
if err != nil {
log . Error ( "Detect raw file %s charset failed: %v, using by default utf-8" , filePath , err )
charset = "utf-8"
2021-06-30 23:31:54 +02:00
}
2022-11-17 18:55:15 +01:00
opts . ContentTypeCharset = strings . ToLower ( charset )
2022-07-29 17:26:55 +02:00
}
isSVG := sniffedType . IsSvgImage ( )
// serve types that can present a security risk with CSP
if isSVG {
ctx . Resp . Header ( ) . Set ( "Content-Security-Policy" , "default-src 'none'; style-src 'unsafe-inline'; sandbox" )
} else if sniffedType . IsPDF ( ) {
// no sandbox attribute for pdf as it breaks rendering in at least safari. this
// should generally be safe as scripts inside PDF can not escape the PDF document
// see https://bugs.chromium.org/p/chromium/issues/detail?id=413851 for more discussion
ctx . Resp . Header ( ) . Set ( "Content-Security-Policy" , "default-src 'none'; style-src 'unsafe-inline'" )
2021-06-09 07:33:54 +08:00
}
2022-11-17 18:55:15 +01:00
opts . Disposition = "inline"
2022-07-29 17:26:55 +02:00
if isSVG && ! setting . UI . SVG . Enabled {
2022-11-17 18:55:15 +01:00
opts . Disposition = "attachment"
2022-07-29 17:26:55 +02:00
}
2022-11-17 18:55:15 +01:00
ctx . SetServeHeaders ( opts )
2022-07-29 17:26:55 +02:00
2021-06-09 07:33:54 +08:00
_ , err = ctx . Resp . Write ( buf )
if err != nil {
return err
}
_ , err = io . Copy ( ctx . Resp , reader )
return err
}