feat: vendor gitea 1.16.2
This commit is contained in:
@@ -9,7 +9,6 @@ import (
|
||||
"errors"
|
||||
"fmt"
|
||||
net_mail "net/mail"
|
||||
"regexp"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
@@ -21,34 +20,13 @@ import (
|
||||
"github.com/dimiro1/reply"
|
||||
"github.com/emersion/go-imap"
|
||||
"github.com/emersion/go-imap/client"
|
||||
"github.com/jhillyerd/enmime"
|
||||
)
|
||||
|
||||
var (
|
||||
addressTokenRegex *regexp.Regexp
|
||||
referenceTokenRegex *regexp.Regexp
|
||||
"github.com/jhillyerd/enmime/v2"
|
||||
)
|
||||
|
||||
func Init(ctx context.Context) error {
|
||||
if !setting.IncomingEmail.Enabled {
|
||||
return nil
|
||||
}
|
||||
|
||||
var err error
|
||||
addressTokenRegex, err = regexp.Compile(
|
||||
fmt.Sprintf(
|
||||
`\A%s\z`,
|
||||
strings.Replace(regexp.QuoteMeta(setting.IncomingEmail.ReplyToAddress), regexp.QuoteMeta(setting.IncomingEmail.TokenPlaceholder), "(.+)", 1),
|
||||
),
|
||||
)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
referenceTokenRegex, err = regexp.Compile(fmt.Sprintf(`\Areply-(.+)@%s\z`, regexp.QuoteMeta(setting.Domain)))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
go func() {
|
||||
ctx, _, finished := process.GetManager().AddTypedContext(ctx, "Incoming Email", process.SystemProcessType, true)
|
||||
defer finished()
|
||||
@@ -241,7 +219,7 @@ loop:
|
||||
return nil
|
||||
}
|
||||
|
||||
handlerType, user, payload, err := token.ExtractToken(ctx, t)
|
||||
handlerType, user, payload, err := token.DecodeToken(ctx, t)
|
||||
if err != nil {
|
||||
if _, ok := err.(*token.ErrToken); ok {
|
||||
log.Info("Invalid incoming email token: %v", err)
|
||||
@@ -292,22 +270,31 @@ func isAutomaticReply(env *enmime.Envelope) bool {
|
||||
return autoRespond != ""
|
||||
}
|
||||
|
||||
func extractToken(s, tokenPrefix, tokenSuffix string) string {
|
||||
if len(s) <= len(tokenPrefix)+len(tokenSuffix) {
|
||||
return ""
|
||||
}
|
||||
prefix, suffix := s[0:len(tokenPrefix)], s[len(s)-len(tokenSuffix):]
|
||||
if strings.EqualFold(prefix, tokenPrefix) && strings.EqualFold(suffix, tokenSuffix) {
|
||||
return s[len(tokenPrefix) : len(s)-len(tokenSuffix)]
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
// searchTokenInHeaders looks for the token in To, Delivered-To and References
|
||||
func searchTokenInHeaders(env *enmime.Envelope) string {
|
||||
if addressTokenRegex != nil {
|
||||
to, _ := env.AddressList("To")
|
||||
to, _ := env.AddressList("To")
|
||||
|
||||
token := searchTokenInAddresses(to)
|
||||
if token != "" {
|
||||
return token
|
||||
}
|
||||
token := searchTokenInAddresses(to)
|
||||
if token != "" {
|
||||
return token
|
||||
}
|
||||
|
||||
deliveredTo, _ := env.AddressList("Delivered-To")
|
||||
deliveredTo, _ := env.AddressList("Delivered-To")
|
||||
|
||||
token = searchTokenInAddresses(deliveredTo)
|
||||
if token != "" {
|
||||
return token
|
||||
}
|
||||
token = searchTokenInAddresses(deliveredTo)
|
||||
if token != "" {
|
||||
return token
|
||||
}
|
||||
|
||||
references := env.GetHeader("References")
|
||||
@@ -322,10 +309,9 @@ func searchTokenInHeaders(env *enmime.Envelope) string {
|
||||
if end == -1 || begin > end {
|
||||
break
|
||||
}
|
||||
|
||||
match := referenceTokenRegex.FindStringSubmatch(references[begin:end])
|
||||
if len(match) == 2 {
|
||||
return match[1]
|
||||
t := extractToken(references[begin:end], "reply-", "@"+setting.Domain)
|
||||
if t != "" {
|
||||
return t
|
||||
}
|
||||
|
||||
references = references[end+1:]
|
||||
@@ -336,15 +322,15 @@ func searchTokenInHeaders(env *enmime.Envelope) string {
|
||||
|
||||
// searchTokenInAddresses looks for the token in an address
|
||||
func searchTokenInAddresses(addresses []*net_mail.Address) string {
|
||||
for _, address := range addresses {
|
||||
match := addressTokenRegex.FindStringSubmatch(address.Address)
|
||||
if len(match) != 2 {
|
||||
continue
|
||||
}
|
||||
|
||||
return match[1]
|
||||
tokenPrefix, tokenSuffix, _ := strings.Cut(setting.IncomingEmail.ReplyToAddress, setting.IncomingEmailTokenPlaceholder)
|
||||
if tokenSuffix == "" {
|
||||
return ""
|
||||
}
|
||||
for _, address := range addresses {
|
||||
if t := extractToken(address.Address, tokenPrefix, tokenSuffix); t != "" {
|
||||
return t
|
||||
}
|
||||
}
|
||||
|
||||
return ""
|
||||
}
|
||||
|
||||
|
||||
@@ -67,7 +67,7 @@ func (h *ReplyHandler) Handle(ctx context.Context, content *MailContent, doer *u
|
||||
return err
|
||||
}
|
||||
|
||||
perm, err := access_model.GetUserRepoPermission(ctx, issue.Repo, doer)
|
||||
perm, err := access_model.GetDoerRepoPermission(ctx, issue.Repo, doer)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -88,7 +88,7 @@ func (h *ReplyHandler) Handle(ctx context.Context, content *MailContent, doer *u
|
||||
for _, attachment := range content.Attachments {
|
||||
attachmentBuf := bytes.NewReader(attachment.Content)
|
||||
uploaderFile := attachment_service.NewLimitedUploaderKnownSize(attachmentBuf, attachmentBuf.Size())
|
||||
a, err := attachment_service.UploadAttachmentGeneralSizeLimit(ctx, uploaderFile, setting.Attachment.AllowedTypes, &repo_model.Attachment{
|
||||
a, err := attachment_service.UploadAttachmentForIssue(ctx, uploaderFile, &repo_model.Attachment{
|
||||
Name: attachment.Name,
|
||||
UploaderID: doer.ID,
|
||||
RepoID: issue.Repo.ID,
|
||||
@@ -171,7 +171,7 @@ func (h *UnsubscribeHandler) Handle(ctx context.Context, _ *MailContent, doer *u
|
||||
return err
|
||||
}
|
||||
|
||||
perm, err := access_model.GetUserRepoPermission(ctx, issue.Repo, doer)
|
||||
perm, err := access_model.GetDoerRepoPermission(ctx, issue.Repo, doer)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
@@ -7,7 +7,9 @@ import (
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
"github.com/jhillyerd/enmime"
|
||||
"code.gitea.io/gitea/modules/setting"
|
||||
|
||||
"github.com/jhillyerd/enmime/v2"
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
@@ -68,6 +70,18 @@ func TestIsAutomaticReply(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
func TestSearchTokenInHeadersCaseInsensitive(t *testing.T) {
|
||||
setting.IncomingEmail.ReplyToAddress = "InComing+%{token}@ExAmPle.com"
|
||||
setting.Domain = "DoMain.com"
|
||||
mkEnv := func(s string) *enmime.Envelope {
|
||||
env, _ := enmime.ReadEnvelope(strings.NewReader(s + "\r\n\r\n"))
|
||||
return env
|
||||
}
|
||||
assert.Equal(t, "abc", searchTokenInHeaders(mkEnv("To: incoming+abc@EXAMPLE.COM")))
|
||||
assert.Equal(t, "abc", searchTokenInHeaders(mkEnv("Delivered-To: INCOMING+abc@example.com")))
|
||||
assert.Equal(t, "abc", searchTokenInHeaders(mkEnv("References: <ReplY-abc@DomaiN.COM>")))
|
||||
}
|
||||
|
||||
func TestGetContentFromMailReader(t *testing.T) {
|
||||
mailString := "Content-Type: multipart/mixed; boundary=message-boundary\r\n" +
|
||||
"\r\n" +
|
||||
|
||||
@@ -15,7 +15,6 @@ import (
|
||||
"mime"
|
||||
"regexp"
|
||||
"strings"
|
||||
"sync/atomic"
|
||||
|
||||
repo_model "code.gitea.io/gitea/models/repo"
|
||||
user_model "code.gitea.io/gitea/models/user"
|
||||
@@ -32,12 +31,10 @@ import (
|
||||
|
||||
const mailMaxSubjectRunes = 256 // There's no actual limit for subject in RFC 5322
|
||||
|
||||
var loadedTemplates atomic.Pointer[templates.MailTemplates]
|
||||
|
||||
var subjectRemoveSpaces = regexp.MustCompile(`[\s]+`)
|
||||
|
||||
func LoadedTemplates() *templates.MailTemplates {
|
||||
return loadedTemplates.Load()
|
||||
func LoadedTemplates() *templates.MailRender {
|
||||
return templates.MailRenderer()
|
||||
}
|
||||
|
||||
// SendTestMail sends a test mail
|
||||
@@ -161,18 +158,23 @@ func (b64embedder *mailAttachmentBase64Embedder) AttachmentSrcToBase64DataURI(ct
|
||||
|
||||
func fromDisplayName(u *user_model.User) string {
|
||||
if setting.MailService.FromDisplayNameFormatTemplate != nil {
|
||||
var ctx bytes.Buffer
|
||||
err := setting.MailService.FromDisplayNameFormatTemplate.Execute(&ctx, map[string]any{
|
||||
var buf bytes.Buffer
|
||||
err := setting.MailService.FromDisplayNameFormatTemplate.Execute(&buf, map[string]any{
|
||||
"DisplayName": u.DisplayName(),
|
||||
"AppName": setting.AppName,
|
||||
"Domain": setting.Domain,
|
||||
})
|
||||
if err == nil {
|
||||
return mime.QEncoding.Encode("utf-8", ctx.String())
|
||||
return mime.QEncoding.Encode("utf-8", buf.String())
|
||||
}
|
||||
log.Error("fromDisplayName: %w", err)
|
||||
}
|
||||
return u.GetCompleteName()
|
||||
def := u.Name
|
||||
if fullName := strings.TrimSpace(u.FullName); fullName != "" {
|
||||
// use "Full Name (username)" for email's sender name if Full Name is not empty
|
||||
def = fullName + " (" + u.Name + ")"
|
||||
}
|
||||
return def
|
||||
}
|
||||
|
||||
func generateMetadataHeaders(repo *repo_model.Repository) map[string]string {
|
||||
|
||||
@@ -21,6 +21,7 @@ import (
|
||||
"code.gitea.io/gitea/modules/markup/markdown"
|
||||
"code.gitea.io/gitea/modules/setting"
|
||||
"code.gitea.io/gitea/modules/translation"
|
||||
"code.gitea.io/gitea/modules/util"
|
||||
incoming_payload "code.gitea.io/gitea/services/mailer/incoming/payload"
|
||||
sender_service "code.gitea.io/gitea/services/mailer/sender"
|
||||
"code.gitea.io/gitea/services/mailer/token"
|
||||
@@ -122,9 +123,7 @@ func composeIssueCommentMessages(ctx context.Context, comment *mailComment, lang
|
||||
var mailSubject bytes.Buffer
|
||||
if err := LoadedTemplates().SubjectTemplates.ExecuteTemplate(&mailSubject, tplName, mailMeta); err == nil {
|
||||
subject = sanitizeSubject(mailSubject.String())
|
||||
if subject == "" {
|
||||
subject = fallback
|
||||
}
|
||||
subject = util.IfZero(subject, fallback)
|
||||
} else {
|
||||
log.Error("ExecuteTemplate [%s]: %v", tplName+"/subject", err)
|
||||
}
|
||||
@@ -183,7 +182,7 @@ func composeIssueCommentMessages(ctx context.Context, comment *mailComment, lang
|
||||
if err != nil {
|
||||
log.Error("CreateToken failed: %v", err)
|
||||
} else {
|
||||
replyAddress := strings.Replace(setting.IncomingEmail.ReplyToAddress, setting.IncomingEmail.TokenPlaceholder, token, 1)
|
||||
replyAddress := strings.Replace(setting.IncomingEmail.ReplyToAddress, setting.IncomingEmailTokenPlaceholder, token, 1)
|
||||
msg.ReplyTo = replyAddress
|
||||
msg.SetHeader("List-Post", fmt.Sprintf("<mailto:%s>", replyAddress))
|
||||
|
||||
@@ -195,7 +194,7 @@ func composeIssueCommentMessages(ctx context.Context, comment *mailComment, lang
|
||||
if err != nil {
|
||||
log.Error("CreateToken failed: %v", err)
|
||||
} else {
|
||||
unsubAddress := strings.Replace(setting.IncomingEmail.ReplyToAddress, setting.IncomingEmail.TokenPlaceholder, token, 1)
|
||||
unsubAddress := strings.Replace(setting.IncomingEmail.ReplyToAddress, setting.IncomingEmailTokenPlaceholder, token, 1)
|
||||
listUnsubscribe = append(listUnsubscribe, "<mailto:"+unsubAddress+">")
|
||||
}
|
||||
}
|
||||
@@ -203,7 +202,7 @@ func composeIssueCommentMessages(ctx context.Context, comment *mailComment, lang
|
||||
msg.SetHeader("References", references...)
|
||||
msg.SetHeader("List-Unsubscribe", listUnsubscribe...)
|
||||
|
||||
for key, value := range generateAdditionalHeadersForIssue(comment, actType, recipient) {
|
||||
for key, value := range generateAdditionalHeadersForIssue(ctx, comment, actType, recipient) {
|
||||
msg.SetHeader(key, value)
|
||||
}
|
||||
|
||||
@@ -261,14 +260,14 @@ func actionToTemplate(issue *issues_model.Issue, actionType activities_model.Act
|
||||
}
|
||||
|
||||
template = "repo/" + typeName + "/" + name
|
||||
ok := LoadedTemplates().BodyTemplates.Lookup(template) != nil
|
||||
ok := LoadedTemplates().BodyTemplates.HasTemplate(template)
|
||||
if !ok && typeName != "issue" {
|
||||
template = "repo/issue/" + name
|
||||
ok = LoadedTemplates().BodyTemplates.Lookup(template) != nil
|
||||
ok = LoadedTemplates().BodyTemplates.HasTemplate(template)
|
||||
}
|
||||
if !ok {
|
||||
template = "repo/" + typeName + "/default"
|
||||
ok = LoadedTemplates().BodyTemplates.Lookup(template) != nil
|
||||
ok = LoadedTemplates().BodyTemplates.HasTemplate(template)
|
||||
}
|
||||
if !ok {
|
||||
template = "repo/issue/default"
|
||||
@@ -303,17 +302,17 @@ func generateMessageIDForIssue(issue *issues_model.Issue, comment *issues_model.
|
||||
return fmt.Sprintf("<%s/%s/%d%s@%s>", issue.Repo.FullName(), path, issue.Index, extra, setting.Domain)
|
||||
}
|
||||
|
||||
func generateAdditionalHeadersForIssue(ctx *mailComment, reason string, recipient *user_model.User) map[string]string {
|
||||
repo := ctx.Issue.Repo
|
||||
func generateAdditionalHeadersForIssue(ctx context.Context, comment *mailComment, reason string, recipient *user_model.User) map[string]string {
|
||||
repo := comment.Issue.Repo
|
||||
|
||||
issueID := strconv.FormatInt(ctx.Issue.Index, 10)
|
||||
issueID := strconv.FormatInt(comment.Issue.Index, 10)
|
||||
headers := generateMetadataHeaders(repo)
|
||||
|
||||
maps.Copy(headers, generateSenderRecipientHeaders(ctx.Doer, recipient))
|
||||
maps.Copy(headers, generateSenderRecipientHeaders(comment.Doer, recipient))
|
||||
maps.Copy(headers, generateReasonHeaders(reason))
|
||||
|
||||
headers["X-Gitea-Issue-ID"] = issueID
|
||||
headers["X-Gitea-Issue-Link"] = ctx.Issue.HTMLURL(context.TODO()) // FIXME: use proper context
|
||||
headers["X-Gitea-Issue-Link"] = comment.Issue.HTMLURL(ctx)
|
||||
headers["X-GitLab-Issue-IID"] = issueID
|
||||
|
||||
return headers
|
||||
|
||||
@@ -10,6 +10,7 @@ import (
|
||||
"code.gitea.io/gitea/models/unittest"
|
||||
user_model "code.gitea.io/gitea/models/user"
|
||||
"code.gitea.io/gitea/modules/setting"
|
||||
"code.gitea.io/gitea/modules/test"
|
||||
sender_service "code.gitea.io/gitea/services/mailer/sender"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
@@ -19,18 +20,10 @@ import (
|
||||
func TestMailNewReleaseFiltersUnauthorizedWatchers(t *testing.T) {
|
||||
assert.NoError(t, unittest.PrepareTestDatabase())
|
||||
|
||||
origMailService := setting.MailService
|
||||
origDomain := setting.Domain
|
||||
origAppName := setting.AppName
|
||||
origAppURL := setting.AppURL
|
||||
origTemplates := LoadedTemplates()
|
||||
defer func() {
|
||||
setting.MailService = origMailService
|
||||
setting.Domain = origDomain
|
||||
setting.AppName = origAppName
|
||||
setting.AppURL = origAppURL
|
||||
loadedTemplates.Store(origTemplates)
|
||||
}()
|
||||
defer test.MockVariableValue(&setting.MailService)()
|
||||
defer test.MockVariableValue(&setting.Domain)()
|
||||
defer test.MockVariableValue(&setting.AppName)()
|
||||
defer test.MockVariableValue(&setting.AppURL)()
|
||||
|
||||
setting.MailService = &setting.Mailer{
|
||||
From: "Gitea",
|
||||
@@ -39,7 +32,7 @@ func TestMailNewReleaseFiltersUnauthorizedWatchers(t *testing.T) {
|
||||
setting.Domain = "example.com"
|
||||
setting.AppName = "Gitea"
|
||||
setting.AppURL = "https://example.com/"
|
||||
prepareMailTemplates(string(tplNewReleaseMail), "{{.Subject}}", "<p>{{.Release.TagName}}</p>")
|
||||
defer mockMailTemplates(string(tplNewReleaseMail), "{{.Subject}}", "<p>{{.Release.TagName}}</p>")()
|
||||
|
||||
repo := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ID: 2})
|
||||
require.True(t, repo.IsPrivate)
|
||||
|
||||
@@ -96,11 +96,8 @@ func prepareMailerBase64Test(t *testing.T) (doer *user_model.User, repo *repo_mo
|
||||
return user, repo, issue, att1, att2
|
||||
}
|
||||
|
||||
func prepareMailTemplates(name, subjectTmpl, bodyTmpl string) {
|
||||
loadedTemplates.Store(&templates.MailTemplates{
|
||||
SubjectTemplates: texttmpl.Must(texttmpl.New(name).Parse(subjectTmpl)),
|
||||
BodyTemplates: template.Must(template.New(name).Parse(bodyTmpl)),
|
||||
})
|
||||
func mockMailTemplates(name, subjectTmpl, bodyTmpl string) func() {
|
||||
return templates.MailRenderer().MockTemplate(name, subjectTmpl, bodyTmpl)
|
||||
}
|
||||
|
||||
func TestComposeIssueComment(t *testing.T) {
|
||||
@@ -112,10 +109,8 @@ func TestComposeIssueComment(t *testing.T) {
|
||||
},
|
||||
})
|
||||
|
||||
setting.IncomingEmail.Enabled = true
|
||||
defer func() { setting.IncomingEmail.Enabled = false }()
|
||||
|
||||
prepareMailTemplates("repo/issue/comment", subjectTpl, bodyTpl)
|
||||
defer test.MockVariableValue(&setting.IncomingEmail.Enabled, true)()
|
||||
defer mockMailTemplates("repo/issue/comment", subjectTpl, bodyTpl)()
|
||||
|
||||
recipients := []*user_model.User{{Name: "Test", Email: "test@gitea.com"}, {Name: "Test2", Email: "test2@gitea.com"}}
|
||||
msgs, err := composeIssueCommentMessages(t.Context(), &mailComment{
|
||||
@@ -160,7 +155,7 @@ func TestComposeIssueComment(t *testing.T) {
|
||||
func TestMailMentionsComment(t *testing.T) {
|
||||
doer, _, issue, comment := prepareMailerTest(t)
|
||||
comment.Poster = doer
|
||||
prepareMailTemplates("repo/issue/comment", subjectTpl, bodyTpl)
|
||||
defer mockMailTemplates("repo/issue/comment", subjectTpl, bodyTpl)()
|
||||
mails := 0
|
||||
|
||||
defer test.MockVariableValue(&SendAsync, func(msgs ...*sender_service.Message) {
|
||||
@@ -175,7 +170,7 @@ func TestMailMentionsComment(t *testing.T) {
|
||||
func TestComposeIssueMessage(t *testing.T) {
|
||||
doer, _, issue, _ := prepareMailerTest(t)
|
||||
|
||||
prepareMailTemplates("repo/issue/new", subjectTpl, bodyTpl)
|
||||
defer mockMailTemplates("repo/issue/new", subjectTpl, bodyTpl)()
|
||||
recipients := []*user_model.User{{Name: "Test", Email: "test@gitea.com"}, {Name: "Test2", Email: "test2@gitea.com"}}
|
||||
msgs, err := composeIssueCommentMessages(t.Context(), &mailComment{
|
||||
Issue: issue, Doer: doer, ActionType: activities_model.ActionCreateIssue,
|
||||
@@ -204,14 +199,10 @@ func TestTemplateSelection(t *testing.T) {
|
||||
doer, repo, issue, comment := prepareMailerTest(t)
|
||||
recipients := []*user_model.User{{Name: "Test", Email: "test@gitea.com"}}
|
||||
|
||||
prepareMailTemplates("repo/issue/default", "repo/issue/default/subject", "repo/issue/default/body")
|
||||
|
||||
texttmpl.Must(LoadedTemplates().SubjectTemplates.New("repo/issue/new").Parse("repo/issue/new/subject"))
|
||||
texttmpl.Must(LoadedTemplates().SubjectTemplates.New("repo/pull/comment").Parse("repo/pull/comment/subject"))
|
||||
texttmpl.Must(LoadedTemplates().SubjectTemplates.New("repo/issue/close").Parse("")) // Must default to a fallback subject
|
||||
template.Must(LoadedTemplates().BodyTemplates.New("repo/issue/new").Parse("repo/issue/new/body"))
|
||||
template.Must(LoadedTemplates().BodyTemplates.New("repo/pull/comment").Parse("repo/pull/comment/body"))
|
||||
template.Must(LoadedTemplates().BodyTemplates.New("repo/issue/close").Parse("repo/issue/close/body"))
|
||||
defer mockMailTemplates("repo/issue/default", "repo/issue/default/subject", "repo/issue/default/body")()
|
||||
defer mockMailTemplates("repo/issue/new", "repo/issue/new/subject", "repo/issue/new/body")()
|
||||
defer mockMailTemplates("repo/pull/comment", "repo/pull/comment/subject", "repo/pull/comment/body")()
|
||||
defer mockMailTemplates("repo/issue/close", "", "repo/issue/close/body")() // Must default to a fallback subject
|
||||
|
||||
expect := func(t *testing.T, msg *sender_service.Message, expSubject, expBody string) {
|
||||
subject := msg.ToMessage().GetGenHeader("Subject")
|
||||
@@ -256,7 +247,7 @@ func TestTemplateServices(t *testing.T) {
|
||||
expect := func(t *testing.T, issue *issues_model.Issue, comment *issues_model.Comment, doer *user_model.User,
|
||||
actionType activities_model.ActionType, fromMention bool, tplSubject, tplBody, expSubject, expBody string,
|
||||
) {
|
||||
prepareMailTemplates("repo/issue/default", tplSubject, tplBody)
|
||||
defer mockMailTemplates("repo/issue/default", tplSubject, tplBody)()
|
||||
recipients := []*user_model.User{{Name: "Test", Email: "test@gitea.com"}}
|
||||
msg := testComposeIssueCommentMessage(t, &mailComment{
|
||||
Issue: issue, Doer: doer, ActionType: actionType,
|
||||
@@ -304,7 +295,7 @@ func TestGenerateAdditionalHeadersForIssue(t *testing.T) {
|
||||
comment := &mailComment{Issue: issue, Doer: doer}
|
||||
recipient := &user_model.User{Name: "test", Email: "test@gitea.com"}
|
||||
|
||||
headers := generateAdditionalHeadersForIssue(comment, "dummy-reason", recipient)
|
||||
headers := generateAdditionalHeadersForIssue(t.Context(), comment, "dummy-reason", recipient)
|
||||
|
||||
expected := map[string]string{
|
||||
"List-ID": "user2/repo1 <repo1.user2.localhost>",
|
||||
@@ -523,7 +514,7 @@ func TestEmbedBase64Images(t *testing.T) {
|
||||
att2ImgBase64 := fmt.Sprintf(`<img src="%s"/>`, att2Base64)
|
||||
|
||||
t.Run("ComposeMessage", func(t *testing.T) {
|
||||
prepareMailTemplates("repo/issue/new", subjectTpl, bodyTpl)
|
||||
defer mockMailTemplates("repo/issue/new", subjectTpl, bodyTpl)()
|
||||
|
||||
issue.Content = fmt.Sprintf(`MSG-BEFORE <image src="attachments/%s"> MSG-AFTER`, att1.UUID)
|
||||
require.NoError(t, issues_model.UpdateIssueCols(t.Context(), issue, "content"))
|
||||
|
||||
@@ -149,30 +149,31 @@ func composeAndSendActionsWorkflowRunStatusEmail(ctx context.Context, repo *repo
|
||||
return nil
|
||||
}
|
||||
|
||||
func MailActionsTrigger(ctx context.Context, sender *user_model.User, repo *repo_model.Repository, run *actions_model.ActionRun) error {
|
||||
func MailActionsTrigger(ctx context.Context, recipient *user_model.User, repo *repo_model.Repository, run *actions_model.ActionRun) error {
|
||||
if setting.MailService == nil {
|
||||
return nil
|
||||
}
|
||||
if !run.Status.IsDone() || run.Status.IsSkipped() {
|
||||
return nil
|
||||
}
|
||||
|
||||
recipients := make([]*user_model.User, 0)
|
||||
|
||||
if !sender.IsGiteaActions() && !sender.IsGhost() && sender.IsMailable() {
|
||||
notifyPref, err := user_model.GetUserSetting(ctx, sender.ID,
|
||||
user_model.SettingsKeyEmailNotificationGiteaActions, user_model.SettingEmailNotificationGiteaActionsFailureOnly)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if notifyPref == user_model.SettingEmailNotificationGiteaActionsAll || !run.Status.IsSuccess() && notifyPref != user_model.SettingEmailNotificationGiteaActionsDisabled {
|
||||
recipients = append(recipients, sender)
|
||||
}
|
||||
if !recipient.IsMailable() {
|
||||
return nil
|
||||
}
|
||||
|
||||
if len(recipients) > 0 {
|
||||
log.Debug("MailActionsTrigger: Initiate email composition")
|
||||
return composeAndSendActionsWorkflowRunStatusEmail(ctx, repo, run, sender, recipients)
|
||||
notifyPref, err := user_model.GetUserSetting(ctx, recipient.ID,
|
||||
user_model.SettingsKeyEmailNotificationGiteaActions, user_model.SettingEmailNotificationGiteaActionsFailureOnly)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
// "disabled" never sends
|
||||
if notifyPref == user_model.SettingEmailNotificationGiteaActionsDisabled {
|
||||
return nil
|
||||
}
|
||||
// "failure-only" skips non-failure runs
|
||||
if notifyPref != user_model.SettingEmailNotificationGiteaActionsAll && !run.Status.IsFailure() {
|
||||
return nil
|
||||
}
|
||||
|
||||
log.Debug("MailActionsTrigger: Initiate email composition")
|
||||
return composeAndSendActionsWorkflowRunStatusEmail(ctx, repo, run, recipient, []*user_model.User{recipient})
|
||||
}
|
||||
|
||||
@@ -43,7 +43,7 @@ func NewContext(ctx context.Context) {
|
||||
sender = &sender_service.SMTPSender{}
|
||||
}
|
||||
|
||||
templates.LoadMailTemplates(ctx, &loadedTemplates)
|
||||
_ = templates.MailRenderer()
|
||||
|
||||
mailQueue = queue.CreateSimpleQueue(graceful.GetManager().ShutdownContext(), "mail", func(items ...*sender_service.Message) []*sender_service.Message {
|
||||
for _, msg := range items {
|
||||
|
||||
@@ -41,19 +41,17 @@ func (a *loginAuth) Next(fromServer []byte, more bool) ([]byte, error) {
|
||||
}
|
||||
|
||||
type ntlmAuth struct {
|
||||
username, password, domain string
|
||||
domainNeeded bool
|
||||
username, password string
|
||||
}
|
||||
|
||||
// NtlmAuth SMTP AUTH NTLM Auth Handler
|
||||
func NtlmAuth(username, password string) smtp.Auth {
|
||||
user, domain, domainNeeded := ntlmssp.GetDomain(username)
|
||||
return &ntlmAuth{user, password, domain, domainNeeded}
|
||||
return &ntlmAuth{username, password}
|
||||
}
|
||||
|
||||
// Start starts SMTP NTLM Auth
|
||||
func (a *ntlmAuth) Start(server *smtp.ServerInfo) (string, []byte, error) {
|
||||
negotiateMessage, err := ntlmssp.NewNegotiateMessage(a.domain, "")
|
||||
negotiateMessage, err := ntlmssp.NewNegotiateMessage("", "")
|
||||
return "NTLM", negotiateMessage, err
|
||||
}
|
||||
|
||||
@@ -63,7 +61,7 @@ func (a *ntlmAuth) Next(fromServer []byte, more bool) ([]byte, error) {
|
||||
if len(fromServer) == 0 {
|
||||
return nil, errors.New("ntlm ChallengeMessage is empty")
|
||||
}
|
||||
authenticateMessage, err := ntlmssp.ProcessChallenge(fromServer, a.username, a.password, a.domainNeeded)
|
||||
authenticateMessage, err := ntlmssp.NewAuthenticateMessage(fromServer, a.username, a.password, nil)
|
||||
return authenticateMessage, err
|
||||
}
|
||||
return nil, nil
|
||||
|
||||
@@ -9,6 +9,7 @@ import (
|
||||
"crypto/sha256"
|
||||
"encoding/base32"
|
||||
"fmt"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
user_model "code.gitea.io/gitea/models/user"
|
||||
@@ -73,9 +74,11 @@ func CreateToken(ht HandlerType, user *user_model.User, data []byte) (string, er
|
||||
return encodingWithoutPadding.EncodeToString(append([]byte{tokenVersion1}, packagedData...)), nil
|
||||
}
|
||||
|
||||
// ExtractToken extracts the action/user tuple from the token and verifies the content
|
||||
func ExtractToken(ctx context.Context, token string) (HandlerType, *user_model.User, []byte, error) {
|
||||
data, err := encodingWithoutPadding.DecodeString(token)
|
||||
// DecodeToken decodes the handler, user and payload from the token and verifies the content
|
||||
func DecodeToken(ctx context.Context, token string) (HandlerType, *user_model.User, []byte, error) {
|
||||
// MTAs are permitted to alter the case of the local-part (RFC 5321 §2.4), so normalize
|
||||
// to the base32 alphabet before decoding to survive a lowercased reply-to address.
|
||||
data, err := encodingWithoutPadding.DecodeString(strings.ToUpper(token))
|
||||
if err != nil {
|
||||
return UnknownHandlerType, nil, nil, err
|
||||
}
|
||||
@@ -118,11 +121,11 @@ func ExtractToken(ctx context.Context, token string) (HandlerType, *user_model.U
|
||||
return handlerType, user, innerPayload, nil
|
||||
}
|
||||
|
||||
// generateHmac creates a trunkated HMAC for the given payload
|
||||
// generateHmac creates a truncated HMAC for the given payload
|
||||
func generateHmac(secret, payload []byte) []byte {
|
||||
mac := crypto_hmac.New(sha256.New, secret)
|
||||
mac.Write(payload)
|
||||
hmac := mac.Sum(nil)
|
||||
|
||||
return hmac[:10] // RFC2104 recommends not using less then 80 bits
|
||||
return hmac[:10] // RFC2104 recommends not using less than 80 bits
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user