@@ -6,11 +6,14 @@ package attachment
66import (
77 "bytes"
88 "context"
9+ "errors"
910 "fmt"
1011 "io"
12+ "net/http"
1113
1214 "code.gitea.io/gitea/models/db"
1315 repo_model "code.gitea.io/gitea/models/repo"
16+ "code.gitea.io/gitea/modules/setting"
1417 "code.gitea.io/gitea/modules/storage"
1518 "code.gitea.io/gitea/modules/util"
1619 "code.gitea.io/gitea/services/context/upload"
@@ -28,27 +31,56 @@ func NewAttachment(ctx context.Context, attach *repo_model.Attachment, file io.R
2831 attach .UUID = uuid .New ().String ()
2932 size , err := storage .Attachments .Save (attach .RelativePath (), file , size )
3033 if err != nil {
31- return fmt .Errorf ("Create : %w" , err )
34+ return fmt .Errorf ("Attachments.Save : %w" , err )
3235 }
3336 attach .Size = size
34-
3537 return db .Insert (ctx , attach )
3638 })
3739
3840 return attach , err
3941}
4042
41- // UploadAttachment upload new attachment into storage and update database
42- func UploadAttachment (ctx context.Context , file io.Reader , allowedTypes string , fileSize int64 , attach * repo_model.Attachment ) (* repo_model.Attachment , error ) {
43+ type UploaderFile struct {
44+ rd io.ReadCloser
45+ size int64
46+ respWriter http.ResponseWriter
47+ }
48+
49+ func NewLimitedUploaderKnownSize (r io.Reader , size int64 ) * UploaderFile {
50+ return & UploaderFile {rd : io .NopCloser (r ), size : size }
51+ }
52+
53+ func NewLimitedUploaderMaxBytesReader (r io.ReadCloser , w http.ResponseWriter ) * UploaderFile {
54+ return & UploaderFile {rd : r , size : - 1 , respWriter : w }
55+ }
56+
57+ func UploadAttachmentGeneralSizeLimit (ctx context.Context , file * UploaderFile , allowedTypes string , attach * repo_model.Attachment ) (* repo_model.Attachment , error ) {
58+ return uploadAttachment (ctx , file , allowedTypes , setting .Attachment .MaxSize << 20 , attach )
59+ }
60+
61+ func uploadAttachment (ctx context.Context , file * UploaderFile , allowedTypes string , maxFileSize int64 , attach * repo_model.Attachment ) (* repo_model.Attachment , error ) {
62+ src := file .rd
63+ if file .size < 0 {
64+ src = http .MaxBytesReader (file .respWriter , src , maxFileSize )
65+ }
4366 buf := make ([]byte , 1024 )
44- n , _ := util .ReadAtMost (file , buf )
67+ n , _ := util .ReadAtMost (src , buf )
4568 buf = buf [:n ]
4669
4770 if err := upload .Verify (buf , attach .Name , allowedTypes ); err != nil {
4871 return nil , err
4972 }
5073
51- return NewAttachment (ctx , attach , io .MultiReader (bytes .NewReader (buf ), file ), fileSize )
74+ if maxFileSize >= 0 && file .size > maxFileSize {
75+ return nil , util .ErrorWrap (util .ErrContentTooLarge , "attachment exceeds limit %d" , maxFileSize )
76+ }
77+
78+ attach , err := NewAttachment (ctx , attach , io .MultiReader (bytes .NewReader (buf ), src ), file .size )
79+ var maxBytesError * http.MaxBytesError
80+ if errors .As (err , & maxBytesError ) {
81+ return nil , util .ErrorWrap (util .ErrContentTooLarge , "attachment exceeds limit %d" , maxFileSize )
82+ }
83+ return attach , err
5284}
5385
5486// UpdateAttachment updates an attachment, verifying that its name is among the allowed types.
0 commit comments