@ -7,12 +7,13 @@ package common 
				
			 
			
		
	
		
		
			
				
					
					 
					 
					 
					import  (  
					 
					 
					 
					import  (  
				
			 
			
		
	
		
		
			
				
					
					 
					 
					 
						"fmt"   
					 
					 
					 
						"fmt"   
				
			 
			
		
	
		
		
			
				
					
					 
					 
					 
						"io"   
					 
					 
					 
						"io"   
				
			 
			
		
	
		
		
			
				
					
					 
					 
					 
					 
					 
					 
					 
						"net/url"   
				
			 
			
		
	
		
		
			
				
					
					 
					 
					 
						"path"   
					 
					 
					 
						"path"   
				
			 
			
		
	
		
		
			
				
					
					 
					 
					 
						"path/filepath"   
					 
					 
					 
						"path/filepath"   
				
			 
			
		
	
		
		
			
				
					
					 
					 
					 
						"strings"   
					 
					 
					 
						"strings"   
				
			 
			
		
	
		
		
			
				
					
					 
					 
					 
						"time"   
					 
					 
					 
						"time"   
				
			 
			
		
	
		
		
			
				
					
					 
					 
					 
					
 
					 
					 
					 
					
 
				
			 
			
		
	
		
		
			
				
					
					 
					 
					 
						"code.gitea.io/gitea/modules/charset"   
					 
					 
					 
						charsetModule  "code.gitea.io/gitea/modules/charset"   
				
			 
			
				
				
			
		
	
		
		
	
		
		
			
				
					
					 
					 
					 
						"code.gitea.io/gitea/modules/context"   
					 
					 
					 
						"code.gitea.io/gitea/modules/context"   
				
			 
			
		
	
		
		
			
				
					
					 
					 
					 
						"code.gitea.io/gitea/modules/git"   
					 
					 
					 
						"code.gitea.io/gitea/modules/git"   
				
			 
			
		
	
		
		
			
				
					
					 
					 
					 
						"code.gitea.io/gitea/modules/httpcache"   
					 
					 
					 
						"code.gitea.io/gitea/modules/httpcache"   
				
			 
			
		
	
	
		
		
			
				
					
						
							
								 
							 
						
						
							
								 
							 
						
						
					 
					 
					@ -42,7 +43,7 @@ func ServeBlob(ctx *context.Context, blob *git.Blob, lastModified time.Time) err 
				
			 
			
		
	
		
		
			
				
					
					 
					 
					 
					}  
					 
					 
					 
					}  
				
			 
			
		
	
		
		
			
				
					
					 
					 
					 
					
 
					 
					 
					 
					
 
				
			 
			
		
	
		
		
			
				
					
					 
					 
					 
					// ServeData download file from io.Reader
  
					 
					 
					 
					// ServeData download file from io.Reader
  
				
			 
			
		
	
		
		
			
				
					
					 
					 
					 
					func  ServeData ( ctx  * context . Context ,  name   string ,  size  int64 ,  reader  io . Reader )  error  {  
					 
					 
					 
					func  ServeData ( ctx  * context . Context ,  filePath   string ,  size  int64 ,  reader  io . Reader )  error  {  
				
			 
			
				
				
			
		
	
		
		
	
		
		
			
				
					
					 
					 
					 
						buf  :=  make ( [ ] byte ,  1024 )   
					 
					 
					 
						buf  :=  make ( [ ] byte ,  1024 )   
				
			 
			
		
	
		
		
			
				
					
					 
					 
					 
						n ,  err  :=  util . ReadAtMost ( reader ,  buf )   
					 
					 
					 
						n ,  err  :=  util . ReadAtMost ( reader ,  buf )   
				
			 
			
		
	
		
		
			
				
					
					 
					 
					 
						if  err  !=  nil  {   
					 
					 
					 
						if  err  !=  nil  {   
				
			 
			
		
	
	
		
		
			
				
					
						
						
						
							
								 
							 
						
					 
					 
					@ -52,56 +53,73 @@ func ServeData(ctx *context.Context, name string, size int64, reader io.Reader) 
				
			 
			
		
	
		
		
			
				
					
					 
					 
					 
							buf  =  buf [ : n ]   
					 
					 
					 
							buf  =  buf [ : n ]   
				
			 
			
		
	
		
		
			
				
					
					 
					 
					 
						}   
					 
					 
					 
						}   
				
			 
			
		
	
		
		
			
				
					
					 
					 
					 
					
 
					 
					 
					 
					
 
				
			 
			
		
	
		
		
			
				
					
					 
					 
					 
						ctx . Resp . Header ( ) . Set ( "Cache-Control" ,  "public,max-age=86400" )   
					 
					 
					 
						httpcache . AddCacheControlToHeader ( ctx . Resp . Header ( ) ,  5 * time . Minute )   
				
			 
			
				
				
			
		
	
		
		
	
		
		
			
				
					
					 
					 
					 
					
 
					 
					 
					 
					
 
				
			 
			
		
	
		
		
			
				
					
					 
					 
					 
						if  size  >=  0  {   
					 
					 
					 
						if  size  >=  0  {   
				
			 
			
		
	
		
		
			
				
					
					 
					 
					 
							ctx . Resp . Header ( ) . Set ( "Content-Length" ,  fmt . Sprintf ( "%d" ,  size ) )   
					 
					 
					 
							ctx . Resp . Header ( ) . Set ( "Content-Length" ,  fmt . Sprintf ( "%d" ,  size ) )   
				
			 
			
		
	
		
		
			
				
					
					 
					 
					 
						}  else  {   
					 
					 
					 
						}  else  {   
				
			 
			
		
	
		
		
			
				
					
					 
					 
					 
							log . Error ( "ServeData called to serve data: %s with size < 0: %d" ,  name ,  size )   
					 
					 
					 
							log . Error ( "ServeData called to serve data: %s with size < 0: %d" ,  filePath ,  size )   
				
			 
			
				
				
			
		
	
		
		
	
		
		
			
				
					
					 
					 
					 
						}   
					 
					 
					 
						}   
				
			 
			
		
	
		
		
			
				
					
					 
					 
					 
						name  =  path . Base ( name )   
					 
					 
					 
					 
				
			 
			
		
	
		
		
			
				
					
					 
					 
					 
					
 
					 
					 
					 
					
 
				
			 
			
		
	
		
		
			
				
					
					 
					 
					 
						// Google Chrome dislike commas in filenames, so let's change it to a space
   
					 
					 
					 
						fileName  :=  path . Base ( filePath )   
				
			 
			
				
				
			
		
	
		
		
			
				
					
					 
					 
					 
						name  =  strings . ReplaceAll ( name ,  "," ,  " " )   
					 
					 
					 
						sniffedType  :=  typesniffer . DetectContentType ( buf )   
				
			 
			
				
				
			
		
	
		
		
	
		
		
	
		
		
			
				
					
					 
					 
					 
					 
					 
					 
					 
						isPlain  :=  sniffedType . IsText ( )  ||  ctx . FormBool ( "render" )   
				
			 
			
		
	
		
		
			
				
					
					 
					 
					 
					 
					 
					 
					 
						mimeType  :=  ""   
				
			 
			
		
	
		
		
			
				
					
					 
					 
					 
					 
					 
					 
					 
						charset  :=  ""   
				
			 
			
		
	
		
		
			
				
					
					 
					 
					 
					
 
					 
					 
					 
					
 
				
			 
			
		
	
		
		
			
				
					
					 
					 
					 
						st  :=  typesniffer . DetectContentType ( buf )   
					 
					 
					 
					 
				
			 
			
		
	
		
		
			
				
					
					 
					 
					 
					
 
					 
					 
					 
					 
				
			 
			
		
	
		
		
			
				
					
					 
					 
					 
						mappedMimeType  :=  ""   
					 
					 
					 
					 
				
			 
			
		
	
		
		
			
				
					
					 
					 
					 
						if  setting . MimeTypeMap . Enabled  {   
					 
					 
					 
						if  setting . MimeTypeMap . Enabled  {   
				
			 
			
		
	
		
		
			
				
					
					 
					 
					 
							fileExtension  :=  strings . ToLower ( filepath . Ext ( n ame) )   
					 
					 
					 
							fileExtension  :=  strings . ToLower ( filepath . Ext ( fileName ) )   
				
			 
			
				
				
			
		
	
		
		
			
				
					
					 
					 
					 
							mappedM imeType  =  setting . MimeTypeMap . Map [ fileExtension ]   
					 
					 
					 
							mimeType  =  setting . MimeTypeMap . Map [ fileExtension ]   
				
			 
			
				
				
			
		
	
		
		
	
		
		
	
		
		
			
				
					
					 
					 
					 
						}   
					 
					 
					 
						}   
				
			 
			
		
	
		
		
			
				
					
					 
					 
					 
						if  st . IsText ( )  ||  ctx . FormBool ( "render" )  {   
					 
					 
					 
					
 
				
			 
			
				
				
			
		
	
		
		
			
				
					
					 
					 
					 
							cs ,  err  :=  charset . DetectEncoding ( buf )   
					 
					 
					 
						if  mimeType  ==  ""  {   
				
			 
			
				
				
			
		
	
		
		
			
				
					
					 
					 
					 
							if  err  !=  nil  {   
					 
					 
					 
							if  sniffedType . IsBrowsableBinaryType ( )  {   
				
			 
			
				
				
			
		
	
		
		
			
				
					
					 
					 
					 
								log . Error ( "Detect raw file %s charset failed: %v, using by default utf-8" ,  name ,  err )   
					 
					 
					 
								mimeType  =  sniffedType . GetMimeType ( )   
				
			 
			
				
				
			
		
	
		
		
			
				
					
					 
					 
					 
								cs  =  "utf-8"   
					 
					 
					 
							}  else  if  isPlain  {   
				
			 
			
				
				
			
		
	
		
		
	
		
		
	
		
		
	
		
		
	
		
		
	
		
		
			
				
					
					 
					 
					 
					 
					 
					 
					 
								mimeType  =  "text/plain"   
				
			 
			
		
	
		
		
			
				
					
					 
					 
					 
					 
					 
					 
					 
							}  else  {   
				
			 
			
		
	
		
		
			
				
					
					 
					 
					 
					 
					 
					 
					 
								mimeType  =  typesniffer . ApplicationOctetStream   
				
			 
			
		
	
		
		
			
				
					
					 
					 
					 
							}   
					 
					 
					 
							}   
				
			 
			
		
	
		
		
			
				
					
					 
					 
					 
							if  mappedMimeType  ==  ""  {   
					 
					 
					 
						}   
				
			 
			
				
				
			
		
	
		
		
			
				
					
					 
					 
					 
								mappedMimeType  =  "text/plain"   
					 
					 
					 
					
 
				
			 
			
				
				
			
		
	
		
		
	
		
		
	
		
		
			
				
					
					 
					 
					 
					 
					 
					 
					 
						if  isPlain  {   
				
			 
			
		
	
		
		
			
				
					
					 
					 
					 
					 
					 
					 
					 
							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"   
				
			 
			
		
	
		
		
			
				
					
					 
					 
					 
							}   
					 
					 
					 
							}   
				
			 
			
		
	
		
		
			
				
					
					 
					 
					 
							ctx . Resp . Header ( ) . Set ( "Content-Type" ,  mappedMimeType + "; charset=" + strings . ToLower ( cs ) )   
					 
					 
					 
						}   
				
			 
			
				
				
			
		
	
		
		
	
		
		
			
				
					
					 
					 
					 
					 
					 
					 
					 
					
 
				
			 
			
		
	
		
		
			
				
					
					 
					 
					 
					 
					 
					 
					 
						if  charset  !=  ""  {   
				
			 
			
		
	
		
		
			
				
					
					 
					 
					 
					 
					 
					 
					 
							ctx . Resp . Header ( ) . Set ( "Content-Type" ,  mimeType + "; charset=" + strings . ToLower ( charset ) )   
				
			 
			
		
	
		
		
			
				
					
					 
					 
					 
						}  else  {   
					 
					 
					 
						}  else  {   
				
			 
			
		
	
		
		
			
				
					
					 
					 
					 
							ctx . Resp . Header ( ) . Set ( "Access-Control-Expose-Headers" ,  "Content-Disposition" )   
					 
					 
					 
							ctx . Resp . Header ( ) . Set ( "Content-Type" ,  mimeType )   
				
			 
			
				
				
			
		
	
		
		
			
				
					
					 
					 
					 
							if  mappedMimeType  !=  ""  {   
					 
					 
					 
						}   
				
			 
			
				
				
			
		
	
		
		
			
				
					
					 
					 
					 
								ctx . Resp . Header ( ) . Set ( "Content-Type" ,  mappedMimeType )   
					 
					 
					 
						ctx . Resp . Header ( ) . Set ( "X-Content-Type-Options" ,  "nosniff" )   
				
			 
			
				
				
			
		
	
		
		
			
				
					
					 
					 
					 
							}   
					 
					 
					 
					
 
				
			 
			
				
				
			
		
	
		
		
			
				
					
					 
					 
					 
							if  ( st . IsImage ( )  ||  st . IsPDF ( ) )  &&  ( setting . UI . SVG . Enabled  ||  ! st . IsSvgImage ( ) )  {   
					 
					 
					 
						isSVG  :=  sniffedType . IsSvgImage ( )   
				
			 
			
				
				
			
		
	
		
		
			
				
					
					 
					 
					 
								ctx . Resp . Header ( ) . Set ( "Content-Disposition" ,  fmt . Sprintf ( ` inline; filename="%s" ` ,  name ) )   
					 
					 
					 
					
 
				
			 
			
				
				
			
		
	
		
		
			
				
					
					 
					 
					 
								if  st . IsSvgImage ( )  ||  st . IsPDF ( )  {   
					 
					 
					 
						// serve types that can present a security risk with CSP
   
				
			 
			
				
				
			
		
	
		
		
			
				
					
					 
					 
					 
									ctx . Resp . Header ( ) . Set ( "Content-Security-Policy" ,  "default-src 'none'; style-src 'unsafe-inline'; sandbox" )   
					 
					 
					 
						if  isSVG  {   
				
			 
			
				
				
			
		
	
		
		
			
				
					
					 
					 
					 
									ctx . Resp . Header ( ) . Set ( "X-Content-Type-Options" ,  "nosniff" )   
					 
					 
					 
							ctx . Resp . Header ( ) . Set ( "Content-Security-Policy" ,  "default-src 'none'; style-src 'unsafe-inline'; sandbox" )   
				
			 
			
				
				
			
		
	
		
		
			
				
					
					 
					 
					 
									if  st . IsSvgImage ( )  {   
					 
					 
					 
						}  else  if  sniffedType . IsPDF ( )  {   
				
			 
			
				
				
			
		
	
		
		
			
				
					
					 
					 
					 
										ctx . Resp . Header ( ) . Set ( "Content-Type" ,  typesniffer . SvgMimeType )   
					 
					 
					 
							// no sandbox attribute for pdf as it breaks rendering in at least safari. this
   
				
			 
			
				
				
			
		
	
		
		
			
				
					
					 
					 
					 
									}  else  {   
					 
					 
					 
							// should generally be safe as scripts inside PDF can not escape the PDF document
   
				
			 
			
				
				
			
		
	
		
		
			
				
					
					 
					 
					 
										ctx . Resp . Header ( ) . Set ( "Content-Type" ,  typesniffer . ApplicationOctetStream )   
					 
					 
					 
							// 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'" )   
				
			 
			
				
				
			
		
	
		
		
			
				
					
					 
					 
					 
								}   
					 
					 
					 
					 
				
			 
			
		
	
		
		
			
				
					
					 
					 
					 
							}  else  {   
					 
					 
					 
					 
				
			 
			
		
	
		
		
			
				
					
					 
					 
					 
								ctx . Resp . Header ( ) . Set ( "Content-Disposition" ,  fmt . Sprintf ( ` attachment; filename="%s" ` ,  name ) )   
					 
					 
					 
					 
				
			 
			
		
	
		
		
			
				
					
					 
					 
					 
							}   
					 
					 
					 
					 
				
			 
			
		
	
		
		
	
		
		
	
		
		
	
		
		
	
		
		
	
		
		
	
		
		
	
		
		
	
		
		
	
		
		
	
		
		
	
		
		
	
		
		
	
		
		
	
		
		
			
				
					
					 
					 
					 
						}   
					 
					 
					 
						}   
				
			 
			
		
	
		
		
			
				
					
					 
					 
					 
					
 
					 
					 
					 
					
 
				
			 
			
		
	
		
		
			
				
					
					 
					 
					 
					 
					 
					 
					 
						disposition  :=  "inline"   
				
			 
			
		
	
		
		
			
				
					
					 
					 
					 
					 
					 
					 
					 
						if  isSVG  &&  ! setting . UI . SVG . Enabled  {   
				
			 
			
		
	
		
		
			
				
					
					 
					 
					 
					 
					 
					 
					 
							disposition  =  "attachment"   
				
			 
			
		
	
		
		
			
				
					
					 
					 
					 
					 
					 
					 
					 
						}   
				
			 
			
		
	
		
		
			
				
					
					 
					 
					 
					 
					 
					 
					 
					
 
				
			 
			
		
	
		
		
			
				
					
					 
					 
					 
					 
					 
					 
					 
						// encode filename per https://datatracker.ietf.org/doc/html/rfc5987
   
				
			 
			
		
	
		
		
			
				
					
					 
					 
					 
					 
					 
					 
					 
						encodedFileName  :=  ` filename*=UTF-8'' `  +  url . PathEscape ( fileName )   
				
			 
			
		
	
		
		
			
				
					
					 
					 
					 
					 
					 
					 
					 
					
 
				
			 
			
		
	
		
		
			
				
					
					 
					 
					 
					 
					 
					 
					 
						ctx . Resp . Header ( ) . Set ( "Content-Disposition" ,  disposition + "; " + encodedFileName )   
				
			 
			
		
	
		
		
			
				
					
					 
					 
					 
					 
					 
					 
					 
						ctx . Resp . Header ( ) . Set ( "Access-Control-Expose-Headers" ,  "Content-Disposition" )   
				
			 
			
		
	
		
		
			
				
					
					 
					 
					 
					 
					 
					 
					 
					
 
				
			 
			
		
	
		
		
			
				
					
					 
					 
					 
						_ ,  err  =  ctx . Resp . Write ( buf )   
					 
					 
					 
						_ ,  err  =  ctx . Resp . Write ( buf )   
				
			 
			
		
	
		
		
			
				
					
					 
					 
					 
						if  err  !=  nil  {   
					 
					 
					 
						if  err  !=  nil  {   
				
			 
			
		
	
		
		
			
				
					
					 
					 
					 
							return  err   
					 
					 
					 
							return  err