@@ -6,6 +6,7 @@ package mailer
66import (
77 "bytes"
88 "context"
9+ "encoding/base64"
910 "fmt"
1011 "html/template"
1112 "io"
@@ -23,6 +24,7 @@ import (
2324 user_model "code.gitea.io/gitea/models/user"
2425 "code.gitea.io/gitea/modules/markup"
2526 "code.gitea.io/gitea/modules/setting"
27+ "code.gitea.io/gitea/modules/storage"
2628 "code.gitea.io/gitea/services/attachment"
2729 sender_service "code.gitea.io/gitea/services/mailer/sender"
2830
@@ -67,21 +69,29 @@ func prepareMailerTest(t *testing.T) (doer *user_model.User, repo *repo_model.Re
6769 return doer , repo , issue , comment
6870}
6971
70- func prepareMailerBase64Test (t * testing.T ) (doer * user_model.User , repo * repo_model.Repository , issue * issues_model.Issue , att * repo_model.Attachment ) {
72+ func prepareMailerBase64Test (t * testing.T ) (doer * user_model.User , repo * repo_model.Repository , issue * issues_model.Issue , att1 , att2 * repo_model.Attachment ) {
7173 user , repo , issue , comment := prepareMailerTest (t )
72- setting .MailService .Base64EmbedImages = true
73- setting . MailService . Base64EmbedImagesMaxSizePerEmail = 10 * 1024 * 1024
74- att , err := attachment .NewAttachment (t .Context (), & repo_model.Attachment {
74+ setting .MailService .EmbedAttachmentImages = true
75+
76+ att1 , err := attachment .NewAttachment (t .Context (), & repo_model.Attachment {
7577 RepoID : repo .ID ,
7678 IssueID : issue .ID ,
7779 UploaderID : user .ID ,
7880 CommentID : comment .ID ,
7981 Name : "test.png" ,
8082 }, bytes .NewReader ([]byte ("\x89 \x50 \x4e \x47 \x0d \x0a \x1a \x0a " )), 8 )
8183 require .NoError (t , err )
82- issue .Content = fmt .Sprintf (`MSG-BEFORE <image src="attachments/%s"> MSG-AFTER` , att .UUID )
83- require .NoError (t , issues_model .UpdateIssueCols (t .Context (), issue , "content" ))
84- return user , repo , issue , att
84+
85+ att2 , err = attachment .NewAttachment (t .Context (), & repo_model.Attachment {
86+ RepoID : repo .ID ,
87+ IssueID : issue .ID ,
88+ UploaderID : user .ID ,
89+ CommentID : comment .ID ,
90+ Name : "test.png" ,
91+ }, bytes .NewReader ([]byte ("\x89 \x50 \x4e \x47 \x0d \x0a \x1a \x0a " + strings .Repeat ("\x00 " , 1024 ))), 8 + 1024 )
92+ require .NoError (t , err )
93+
94+ return user , repo , issue , att1 , att2
8595}
8696
8797func TestComposeIssueComment (t * testing.T ) {
@@ -468,90 +478,75 @@ func TestFromDisplayName(t *testing.T) {
468478 })
469479}
470480
471- func TestEmbedBase64ImagesInEmail (t * testing.T ) {
472- doer , _ , issue , _ := prepareMailerBase64Test (t )
473- subjectTemplates = texttmpl .Must (texttmpl .New ("issue/new" ).Parse (subjectTpl ))
474- bodyTemplates = template .Must (template .New ("issue/new" ).Parse (bodyTpl ))
475-
476- recipients := []
* user_model.
User {{
Name :
"Test" ,
Email :
"[email protected] " }}
477- msgs , err := composeIssueCommentMessages (& mailCommentContext {
478- Context : t .Context (),
479- Issue : issue ,
480- Doer : doer ,
481- ActionType : activities_model .ActionCreateIssue ,
482- Content : strings .ReplaceAll (issue .Content , `src="` , `src="` + setting .AppURL ),
483- }, "en-US" , recipients , false , "issue create" )
484- require .NoError (t , err )
485-
486- mailBody := msgs [0 ].Body
487- re := regexp .MustCompile (`MSG-BEFORE.*MSG-AFTER` )
488- matches := re .FindStringSubmatch (mailBody )
489- require .NotEmpty (t , matches )
490- mailBody = matches [0 ]
491- assert .Equal (t , `MSG-BEFORE <img src="data:image/png;base64,iVBORw0KGgo="/> MSG-AFTER` , mailBody )
492- }
493-
494481func TestEmbedBase64Images (t * testing.T ) {
495- user , repo , issue , att := prepareMailerBase64Test (t )
482+ user , repo , issue , att1 , att2 := prepareMailerBase64Test (t )
496483 ctx := & mailCommentContext {Context : t .Context (), Issue : issue , Doer : user }
497484
498- img1ExternalURL := "https://via.placeholder.com/10"
499- img1ExternalImg := "<img src=\" " + img1ExternalURL + "\" />"
500-
501- img2InternalURL := setting .AppURL + repo .Owner .Name + "/" + repo .Name + "/attachments/" + att .UUID
502- img2InternalImg := "<img src=\" " + img2InternalURL + "\" />"
503- img2InternalBase64 := "data:image/png;base64,iVBORw0KGgo="
504- img2InternalBase64Img := "<img src=\" " + img2InternalBase64 + "\" />"
485+ imgExternalURL := "https://via.placeholder.com/10"
486+ imgExternalImg := fmt .Sprintf (`<img src="%s"/>` , imgExternalURL )
505487
506- // 1st Test: convert internal image to base64
507- t .Run ("replaceSpecifiedBase64ImagesInternal" , func (t * testing.T ) {
508- totalEmbeddedImagesSize := int64 (0 )
488+ att1URL := setting .AppURL + repo .Owner .Name + "/" + repo .Name + "/attachments/" + att1 .UUID
489+ att1Img := fmt .Sprintf (`<img src="%s"/>` , att1URL )
490+ att1Base64 := "data:image/png;base64,iVBORw0KGgo="
491+ att1ImgBase64 := fmt .Sprintf (`<img src="%s"/>` , att1Base64 )
509492
510- resultImg1Internal , err := AttachmentSrcToBase64DataURI (img2InternalURL , ctx , & totalEmbeddedImagesSize )
511- assert .NoError (t , err )
512- assert .Equal (t , img2InternalBase64 , resultImg1Internal ) // replace cause internal image
513- })
514-
515- // 2nd Test: convert external image to base64 -> abort cause external image
516- t .Run ("replaceSpecifiedBase64ImagesExternal" , func (t * testing.T ) {
517- totalEmbeddedImagesSize := int64 (0 )
493+ att2URL := setting .AppURL + repo .Owner .Name + "/" + repo .Name + "/attachments/" + att2 .UUID
494+ att2Img := fmt .Sprintf (`<img src="%s"/>` , att2URL )
495+ att2File , err := storage .Attachments .Open (att2 .RelativePath ())
496+ require .NoError (t , err )
497+ defer att2File .Close ()
498+ att2Bytes , err := io .ReadAll (att2File )
499+ require .NoError (t , err )
500+ require .Greater (t , len (att2Bytes ), 1024 )
501+ att2Base64 := "data:image/png;base64," + base64 .StdEncoding .EncodeToString (att2Bytes )
502+ att2ImgBase64 := fmt .Sprintf (`<img src="%s"/>` , att2Base64 )
518503
519- resultImg1External , err := AttachmentSrcToBase64DataURI (img1ExternalURL , ctx , & totalEmbeddedImagesSize )
520- assert .Error (t , err )
521- assert .Equal (t , "" , resultImg1External ) // don't replace cause external image
522- })
504+ t .Run ("ComposeMessage" , func (t * testing.T ) {
505+ issue .Content = fmt .Sprintf (`MSG-BEFORE <image src="attachments/%s"> MSG-AFTER` , att1 .UUID )
506+ require .NoError (t , issues_model .UpdateIssueCols (t .Context (), issue , "content" ))
523507
524- // 3rd Test: generate email body with 1 internal and 1 external image, expect the result to have the internal image replaced with base64 data and the external not replaced
525- t .Run ("generateEmailBody" , func (t * testing.T ) {
526- mailBody := "<html><head></head><body><p>Test1</p>" + img1ExternalImg + "<p>Test2</p>" + img2InternalImg + "<p>Test3</p></body></html>"
527- expectedMailBody := "<html><head></head><body><p>Test1</p>" + img1ExternalImg + "<p>Test2</p>" + img2InternalBase64Img + "<p>Test3</p></body></html>"
528- resultMailBody , err := Base64InlineImages (mailBody , ctx )
508+ subjectTemplates = texttmpl .Must (texttmpl .New ("issue/new" ).Parse (subjectTpl ))
509+ bodyTemplates = template .Must (template .New ("issue/new" ).Parse (bodyTpl ))
529510
530- assert .NoError (t , err )
531- assert .Equal (t , expectedMailBody , resultMailBody )
511+ recipients := []
* user_model.
User {{
Name :
"Test" ,
Email :
"[email protected] " }}
512+ msgs , err := composeIssueCommentMessages (& mailCommentContext {
513+ Context : t .Context (),
514+ Issue : issue ,
515+ Doer : user ,
516+ ActionType : activities_model .ActionCreateIssue ,
517+ Content : issue .Content , // strings.ReplaceAll(issue.Content, `src="`, `src="`+setting.AppURL),
518+ }, "en-US" , recipients , false , "issue create" )
519+ require .NoError (t , err )
520+
521+ mailBody := msgs [0 ].Body
522+ re := regexp .MustCompile (`MSG-BEFORE.*MSG-AFTER` )
523+ matches := re .FindStringSubmatch (mailBody )
524+ require .NotEmpty (t , matches )
525+ mailBody = matches [0 ]
526+ assert .Regexp (t , `MSG-BEFORE <a[^>]+><img src="data:image/png;base64,iVBORw0KGgo="/></a> MSG-AFTER` , mailBody )
532527 })
533528
534- // 4th Test, generate email body with 2 internal images, but set Mailer.Base64EmbedImagesMaxSizePerEmail to the size of the first image (+1), expect the first image to be replaced and the second not
535- t .Run ("generateEmailBodyWithMaxSize" , func (t * testing.T ) {
536- setting .MailService .Base64EmbedImagesMaxSizePerEmail = 10
537-
538- mailBody := "<html><head></head><body><p>Test1</p>" + img2InternalImg + "<p>Test2</p>" + img2InternalImg + "<p>Test3</p></body></html>"
539- expectedMailBody := "<html><head></head><body><p>Test1</p>" + img2InternalBase64Img + "<p>Test2</p>" + img2InternalImg + "<p>Test3</p></body></html>"
540- resultMailBody , err := Base64InlineImages (mailBody , ctx )
541-
542- assert .NoError (t , err )
543- assert .Equal (t , expectedMailBody , resultMailBody )
529+ t .Run ("EmbedInstanceImageSkipExternalImage" , func (t * testing.T ) {
530+ mailBody := "<html><head></head><body><p>Test1</p>" + imgExternalImg + "<p>Test2</p>" + att1Img + "<p>Test3</p></body></html>"
531+ expectedMailBody := "<html><head></head><body><p>Test1</p>" + imgExternalImg + "<p>Test2</p>" + att1ImgBase64 + "<p>Test3</p></body></html>"
532+ b64embedder := newMailAttachmentBase64Embedder (user , repo , 1024 )
533+ resultMailBody , err := b64embedder .Base64InlineImages (ctx , template .HTML (mailBody ))
534+ require .NoError (t , err )
535+ assert .Equal (t , expectedMailBody , string (resultMailBody ))
544536 })
545537
546- // 5th Test, generate email body with 3 internal images, but set Mailer.Base64EmbedImagesMaxSizePerEmail to the size of all 3 images (+1), expect all images to be replaced
547- t .Run ("generateEmailBodyWith3Images" , func (t * testing.T ) {
548- setting .MailService .Base64EmbedImagesMaxSizePerEmail = int64 (len (img2InternalBase64 )* 3 + 1 )
549-
550- mailBody := "<html><head></head><body><p>Test1</p>" + img2InternalImg + "<p>Test2</p>" + img2InternalImg + "<p>Test3</p>" + img2InternalImg + "</body></html>"
551- expectedMailBody := "<html><head></head><body><p>Test1</p>" + img2InternalBase64Img + "<p>Test2</p>" + img2InternalBase64Img + "<p>Test3</p>" + img2InternalBase64Img + "</body></html>"
552- resultMailBody , err := Base64InlineImages (mailBody , ctx )
553-
554- assert .NoError (t , err )
555- assert .Equal (t , expectedMailBody , resultMailBody )
538+ t .Run ("LimitedEmailBodySize" , func (t * testing.T ) {
539+ mailBody := fmt .Sprintf ("<html><head></head><body>%s%s</body></html>" , att1Img , att2Img )
540+ b64embedder := newMailAttachmentBase64Embedder (user , repo , 1024 )
541+ resultMailBody , err := b64embedder .Base64InlineImages (ctx , template .HTML (mailBody ))
542+ require .NoError (t , err )
543+ expected := fmt .Sprintf ("<html><head></head><body>%s%s</body></html>" , att1ImgBase64 , att2Img )
544+ assert .Equal (t , expected , string (resultMailBody ))
545+
546+ b64embedder = newMailAttachmentBase64Embedder (user , repo , 4096 )
547+ resultMailBody , err = b64embedder .Base64InlineImages (ctx , template .HTML (mailBody ))
548+ require .NoError (t , err )
549+ expected = fmt .Sprintf ("<html><head></head><body>%s%s</body></html>" , att1ImgBase64 , att2ImgBase64 )
550+ assert .Equal (t , expected , string (resultMailBody ))
556551 })
557552}
0 commit comments