mirror of
https://github.com/go-gitea/gitea
synced 2025-07-22 18:28:37 +00:00
Email option to embed images as base64 instead of link (#32061)
ref: #15081 ref: #14037 Documentation: https://gitea.com/gitea/docs/pulls/69 # Example Content:  Result in Email:  Result with source code: (first image is external image, 2nd is now embedded)  --------- Co-authored-by: wxiaoguang <wxiaoguang@gmail.com>
This commit is contained in:
@@ -102,25 +102,77 @@ func MakeAbsoluteURL(ctx context.Context, link string) string {
|
||||
return GuessCurrentHostURL(ctx) + "/" + strings.TrimPrefix(link, "/")
|
||||
}
|
||||
|
||||
func IsCurrentGiteaSiteURL(ctx context.Context, s string) bool {
|
||||
type urlType int
|
||||
|
||||
const (
|
||||
urlTypeGiteaAbsolute urlType = iota + 1 // "http://gitea/subpath"
|
||||
urlTypeGiteaPageRelative // "/subpath"
|
||||
urlTypeGiteaSiteRelative // "?key=val"
|
||||
urlTypeUnknown // "http://other"
|
||||
)
|
||||
|
||||
func detectURLRoutePath(ctx context.Context, s string) (routePath string, ut urlType) {
|
||||
u, err := url.Parse(s)
|
||||
if err != nil {
|
||||
return false
|
||||
return "", urlTypeUnknown
|
||||
}
|
||||
cleanedPath := ""
|
||||
if u.Path != "" {
|
||||
cleanedPath := util.PathJoinRelX(u.Path)
|
||||
if cleanedPath == "" || cleanedPath == "." {
|
||||
u.Path = "/"
|
||||
} else {
|
||||
u.Path = "/" + cleanedPath + "/"
|
||||
}
|
||||
cleanedPath = util.PathJoinRelX(u.Path)
|
||||
cleanedPath = util.Iif(cleanedPath == ".", "", "/"+cleanedPath)
|
||||
}
|
||||
if urlIsRelative(s, u) {
|
||||
return u.Path == "" || strings.HasPrefix(strings.ToLower(u.Path), strings.ToLower(setting.AppSubURL+"/"))
|
||||
}
|
||||
if u.Path == "" {
|
||||
u.Path = "/"
|
||||
if u.Path == "" {
|
||||
return "", urlTypeGiteaPageRelative
|
||||
}
|
||||
if strings.HasPrefix(strings.ToLower(cleanedPath+"/"), strings.ToLower(setting.AppSubURL+"/")) {
|
||||
return cleanedPath[len(setting.AppSubURL):], urlTypeGiteaSiteRelative
|
||||
}
|
||||
return "", urlTypeUnknown
|
||||
}
|
||||
u.Path = cleanedPath + "/"
|
||||
urlLower := strings.ToLower(u.String())
|
||||
return strings.HasPrefix(urlLower, strings.ToLower(setting.AppURL)) || strings.HasPrefix(urlLower, strings.ToLower(GuessCurrentAppURL(ctx)))
|
||||
if strings.HasPrefix(urlLower, strings.ToLower(setting.AppURL)) {
|
||||
return cleanedPath[len(setting.AppSubURL):], urlTypeGiteaAbsolute
|
||||
}
|
||||
guessedCurURL := GuessCurrentAppURL(ctx)
|
||||
if strings.HasPrefix(urlLower, strings.ToLower(guessedCurURL)) {
|
||||
return cleanedPath[len(setting.AppSubURL):], urlTypeGiteaAbsolute
|
||||
}
|
||||
return "", urlTypeUnknown
|
||||
}
|
||||
|
||||
func IsCurrentGiteaSiteURL(ctx context.Context, s string) bool {
|
||||
_, ut := detectURLRoutePath(ctx, s)
|
||||
return ut != urlTypeUnknown
|
||||
}
|
||||
|
||||
type GiteaSiteURL struct {
|
||||
RoutePath string
|
||||
OwnerName string
|
||||
RepoName string
|
||||
RepoSubPath string
|
||||
}
|
||||
|
||||
func ParseGiteaSiteURL(ctx context.Context, s string) *GiteaSiteURL {
|
||||
routePath, ut := detectURLRoutePath(ctx, s)
|
||||
if ut == urlTypeUnknown || ut == urlTypeGiteaPageRelative {
|
||||
return nil
|
||||
}
|
||||
ret := &GiteaSiteURL{RoutePath: routePath}
|
||||
fields := strings.SplitN(strings.TrimPrefix(ret.RoutePath, "/"), "/", 3)
|
||||
|
||||
// TODO: now it only does a quick check for some known reserved paths, should do more strict checks in the future
|
||||
if fields[0] == "attachments" {
|
||||
return ret
|
||||
}
|
||||
if len(fields) < 2 {
|
||||
return ret
|
||||
}
|
||||
ret.OwnerName = fields[0]
|
||||
ret.RepoName = fields[1]
|
||||
if len(fields) == 3 {
|
||||
ret.RepoSubPath = "/" + fields[2]
|
||||
}
|
||||
return ret
|
||||
}
|
||||
|
@@ -122,3 +122,26 @@ func TestIsCurrentGiteaSiteURL(t *testing.T) {
|
||||
assert.True(t, IsCurrentGiteaSiteURL(ctx, "https://user-host"))
|
||||
assert.False(t, IsCurrentGiteaSiteURL(ctx, "https://forwarded-host"))
|
||||
}
|
||||
|
||||
func TestParseGiteaSiteURL(t *testing.T) {
|
||||
defer test.MockVariableValue(&setting.AppURL, "http://localhost:3000/sub/")()
|
||||
defer test.MockVariableValue(&setting.AppSubURL, "/sub")()
|
||||
ctx := t.Context()
|
||||
tests := []struct {
|
||||
url string
|
||||
exp *GiteaSiteURL
|
||||
}{
|
||||
{"http://localhost:3000/sub?k=v", &GiteaSiteURL{RoutePath: ""}},
|
||||
{"http://localhost:3000/sub/", &GiteaSiteURL{RoutePath: ""}},
|
||||
{"http://localhost:3000/sub/foo", &GiteaSiteURL{RoutePath: "/foo"}},
|
||||
{"http://localhost:3000/sub/foo/bar", &GiteaSiteURL{RoutePath: "/foo/bar", OwnerName: "foo", RepoName: "bar"}},
|
||||
{"http://localhost:3000/sub/foo/bar/", &GiteaSiteURL{RoutePath: "/foo/bar", OwnerName: "foo", RepoName: "bar"}},
|
||||
{"http://localhost:3000/sub/attachments/bar", &GiteaSiteURL{RoutePath: "/attachments/bar"}},
|
||||
{"http://localhost:3000/other", nil},
|
||||
{"http://other/", nil},
|
||||
}
|
||||
for _, test := range tests {
|
||||
su := ParseGiteaSiteURL(ctx, test.url)
|
||||
assert.Equal(t, test.exp, su, "URL = %s", test.url)
|
||||
}
|
||||
}
|
||||
|
@@ -13,7 +13,7 @@ import (
|
||||
|
||||
"code.gitea.io/gitea/modules/log"
|
||||
|
||||
shellquote "github.com/kballard/go-shellquote"
|
||||
"github.com/kballard/go-shellquote"
|
||||
)
|
||||
|
||||
// Mailer represents mail service.
|
||||
@@ -29,6 +29,9 @@ type Mailer struct {
|
||||
SubjectPrefix string `ini:"SUBJECT_PREFIX"`
|
||||
OverrideHeader map[string][]string `ini:"-"`
|
||||
|
||||
// Embed attachment images as inline base64 img src attribute
|
||||
EmbedAttachmentImages bool
|
||||
|
||||
// SMTP sender
|
||||
Protocol string `ini:"PROTOCOL"`
|
||||
SMTPAddr string `ini:"SMTP_ADDR"`
|
||||
|
Reference in New Issue
Block a user