@@ -7,9 +7,11 @@ package mailer
77import  (
88	"bytes" 
99	"context" 
10+ 	"encoding/base64" 
1011	"fmt" 
1112	"html/template" 
1213	"mime" 
14+ 	"net/http" 
1315	"regexp" 
1416	"strconv" 
1517	"strings" 
@@ -26,11 +28,13 @@ import (
2628	"code.gitea.io/gitea/modules/markup" 
2729	"code.gitea.io/gitea/modules/markup/markdown" 
2830	"code.gitea.io/gitea/modules/setting" 
31+ 	"code.gitea.io/gitea/modules/storage" 
2932	"code.gitea.io/gitea/modules/timeutil" 
3033	"code.gitea.io/gitea/modules/translation" 
3134	incoming_payload "code.gitea.io/gitea/services/mailer/incoming/payload" 
3235	"code.gitea.io/gitea/services/mailer/token" 
3336
37+ 	"golang.org/x/net/html" 
3438	"gopkg.in/gomail.v2" 
3539)
3640
@@ -232,6 +236,15 @@ func composeIssueCommentMessages(ctx *mailCommentContext, lang string, recipient
232236		return  nil , err 
233237	}
234238
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+ 
235248	actType , actName , tplName  :=  actionToTemplate (ctx .Issue , ctx .ActionType , commentType , reviewType )
236249
237250	if  actName  !=  "new"  {
@@ -363,6 +376,85 @@ func composeIssueCommentMessages(ctx *mailCommentContext, lang string, recipient
363376	return  msgs , nil 
364377}
365378
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+ 				// Process <img> tags 
391+ 				for  i , attr  :=  range  n .Attr  {
392+ 					if  attr .Key  ==  "src"  {
393+ 						attachmentPath  :=  attr .Val 
394+ 						// Read the attachment file and encode it as base64 
395+ 						dataURI , err  :=  attachmentSrcToDataURI (attachmentPath , ctx )
396+ 						if  err  !=  nil  {
397+ 							log .Error ("attachmentSrcToDataURI failed: %v" , err )
398+ 							continue 
399+ 						}
400+ 						log .Trace ("Old value of src attribute: %s, new value (first 100 characters): %s" , attr .Val , dataURI [:100 ])
401+ 						n .Attr [i ].Val  =  dataURI 
402+ 					}
403+ 				}
404+ 			}
405+ 		}
406+ 
407+ 		if  n .FirstChild  !=  nil  {
408+ 			log .Trace ("Processing child nodes of <%s>" , n .Data )
409+ 		}
410+ 		for  c  :=  n .FirstChild ; c  !=  nil ; c  =  c .NextSibling  {
411+ 			processNode (c )
412+ 		}
413+ 	}
414+ 
415+ 	processNode (doc )
416+ 
417+ 	var  buf  bytes.Buffer 
418+ 	err  =  html .Render (& buf , doc )
419+ 	if  err  !=  nil  {
420+ 		log .Error ("Failed to render modified HTML: %v" , err )
421+ 		return  "" , err 
422+ 	}
423+ 	return  buf .String (), nil 
424+ }
425+ 
426+ // Helper function to convert attachment source to data URI 
427+ 
428+ func  attachmentSrcToDataURI (attachmentPath  string , ctx  * mailCommentContext ) (string , error ) {
429+ 	parts  :=  strings .Split (attachmentPath , "/attachments/" )
430+ 	if  len (parts ) <=  1  {
431+ 		return  "" , fmt .Errorf ("Invalid attachment path: %s" , attachmentPath )
432+ 	}
433+ 
434+ 	attachmentUUID  :=  parts [len (parts )- 1 ]
435+ 	attachment , err  :=  repo_model .GetAttachmentByUUID (ctx , attachmentUUID )
436+ 	if  err  !=  nil  {
437+ 		return  "" , err 
438+ 	}
439+ 
440+ 	fr , err  :=  storage .Attachments .Open (attachment .RelativePath ())
441+ 	if  err  !=  nil  {
442+ 		return  "" , err 
443+ 	}
444+ 	defer  fr .Close ()
445+ 
446+ 	content  :=  make ([]byte , attachment .Size )
447+ 	if  _ , err  :=  fr .Read (content ); err  !=  nil  {
448+ 		return  "" , err 
449+ 	}
450+ 
451+ 	mimeType  :=  http .DetectContentType (content )
452+ 	encoded  :=  base64 .StdEncoding .EncodeToString (content )
453+ 	dataURI  :=  fmt .Sprintf ("data:%s;base64,%s" , mimeType , encoded )
454+ 
455+ 	return  dataURI , nil 
456+ }
457+ 
366458func  generateMessageIDForIssue (issue  * issues_model.Issue , comment  * issues_model.Comment , actionType  activities_model.ActionType ) string  {
367459	var  path  string 
368460	if  issue .IsPull  {
0 commit comments