@@ -7,9 +7,11 @@ package mailer
7
7
import (
8
8
"bytes"
9
9
"context"
10
+ "encoding/base64"
10
11
"fmt"
11
12
"html/template"
12
13
"mime"
14
+ "net/http"
13
15
"regexp"
14
16
"strconv"
15
17
"strings"
@@ -26,11 +28,13 @@ import (
26
28
"code.gitea.io/gitea/modules/markup"
27
29
"code.gitea.io/gitea/modules/markup/markdown"
28
30
"code.gitea.io/gitea/modules/setting"
31
+ "code.gitea.io/gitea/modules/storage"
29
32
"code.gitea.io/gitea/modules/timeutil"
30
33
"code.gitea.io/gitea/modules/translation"
31
34
incoming_payload "code.gitea.io/gitea/services/mailer/incoming/payload"
32
35
"code.gitea.io/gitea/services/mailer/token"
33
36
37
+ "golang.org/x/net/html"
34
38
"gopkg.in/gomail.v2"
35
39
)
36
40
@@ -232,6 +236,15 @@ func composeIssueCommentMessages(ctx *mailCommentContext, lang string, recipient
232
236
return nil , err
233
237
}
234
238
239
+ if setting .MailService .Base64EmbedImages {
240
+ bodyStr := string (body )
241
+ bodyStr , err = inlineImages (bodyStr , ctx )
242
+ if err != nil {
243
+ return nil , err
244
+ }
245
+ body = template .HTML (bodyStr )
246
+ }
247
+
235
248
actType , actName , tplName := actionToTemplate (ctx .Issue , ctx .ActionType , commentType , reviewType )
236
249
237
250
if actName != "new" {
@@ -363,6 +376,78 @@ func composeIssueCommentMessages(ctx *mailCommentContext, lang string, recipient
363
376
return msgs , nil
364
377
}
365
378
379
+ func inlineImages (body string , ctx * mailCommentContext ) (string , error ) {
380
+ doc , err := html .Parse (strings .NewReader (body ))
381
+ if err != nil {
382
+ log .Error ("Failed to parse HTML body: %v" , err )
383
+ return "" , err
384
+ }
385
+
386
+ var processNode func (* html.Node )
387
+ processNode = func (n * html.Node ) {
388
+ if n .Type == html .ElementNode {
389
+ if n .Data == "img" {
390
+ for i , attr := range n .Attr {
391
+ if attr .Key == "src" {
392
+ attachmentPath := attr .Val
393
+ dataURI , err := attachmentSrcToDataURI (attachmentPath , ctx )
394
+ if err != nil {
395
+ log .Trace ("attachmentSrcToDataURI not possible: %v" , err ) // Not an error, just skip. This is probably an image from outside the gitea instance.
396
+ continue
397
+ }
398
+ log .Trace ("Old value of src attribute: %s, new value (first 100 characters): %s" , attr .Val , dataURI [:100 ])
399
+ n .Attr [i ].Val = dataURI
400
+ }
401
+ }
402
+ }
403
+ }
404
+
405
+ for c := n .FirstChild ; c != nil ; c = c .NextSibling {
406
+ processNode (c )
407
+ }
408
+ }
409
+
410
+ processNode (doc )
411
+
412
+ var buf bytes.Buffer
413
+ err = html .Render (& buf , doc )
414
+ if err != nil {
415
+ log .Error ("Failed to render modified HTML: %v" , err )
416
+ return "" , err
417
+ }
418
+ return buf .String (), nil
419
+ }
420
+
421
+ func attachmentSrcToDataURI (attachmentPath string , ctx * mailCommentContext ) (string , error ) {
422
+ parts := strings .Split (attachmentPath , "/attachments/" )
423
+ if len (parts ) <= 1 {
424
+ return "" , fmt .Errorf ("invalid attachment path: %s" , attachmentPath )
425
+ }
426
+
427
+ attachmentUUID := parts [len (parts )- 1 ]
428
+ attachment , err := repo_model .GetAttachmentByUUID (ctx , attachmentUUID )
429
+ if err != nil {
430
+ return "" , err
431
+ }
432
+
433
+ fr , err := storage .Attachments .Open (attachment .RelativePath ())
434
+ if err != nil {
435
+ return "" , err
436
+ }
437
+ defer fr .Close ()
438
+
439
+ content := make ([]byte , attachment .Size )
440
+ if _ , err := fr .Read (content ); err != nil {
441
+ return "" , err
442
+ }
443
+
444
+ mimeType := http .DetectContentType (content )
445
+ encoded := base64 .StdEncoding .EncodeToString (content )
446
+ dataURI := fmt .Sprintf ("data:%s;base64,%s" , mimeType , encoded )
447
+
448
+ return dataURI , nil
449
+ }
450
+
366
451
func generateMessageIDForIssue (issue * issues_model.Issue , comment * issues_model.Comment , actionType activities_model.ActionType ) string {
367
452
var path string
368
453
if issue .IsPull {
0 commit comments