1
1
mirror of https://github.com/go-gitea/gitea synced 2025-07-22 18:28:37 +00:00

Use new mail package instead of an unmintained one (#32682)

Resolve #18664
This commit is contained in:
Lunny Xiao
2024-12-04 14:33:43 -08:00
committed by GitHub
parent 4142397b0b
commit 5ab7aa700f
11 changed files with 133 additions and 71 deletions

View File

@@ -0,0 +1,14 @@
// Copyright 2019 The Gitea Authors. All rights reserved.
// SPDX-License-Identifier: MIT
package sender
import (
"testing"
"code.gitea.io/gitea/models/unittest"
)
func TestMain(m *testing.M) {
unittest.MainTest(m)
}

View File

@@ -6,6 +6,7 @@ package sender
import (
"fmt"
"hash/fnv"
"net/mail"
"strings"
"time"
@@ -14,7 +15,7 @@ import (
"code.gitea.io/gitea/modules/setting"
"github.com/jaytaylor/html2text"
"gopkg.in/gomail.v2"
gomail "github.com/wneessen/go-mail"
)
// Message mail body and log info
@@ -31,45 +32,46 @@ type Message struct {
}
// ToMessage converts a Message to gomail.Message
func (m *Message) ToMessage() *gomail.Message {
msg := gomail.NewMessage()
msg.SetAddressHeader("From", m.FromAddress, m.FromDisplayName)
msg.SetHeader("To", m.To)
func (m *Message) ToMessage() *gomail.Msg {
msg := gomail.NewMsg()
addr := mail.Address{Name: m.FromDisplayName, Address: m.FromAddress}
_ = msg.SetAddrHeader("From", addr.String())
_ = msg.SetAddrHeader("To", m.To)
if m.ReplyTo != "" {
msg.SetHeader("Reply-To", m.ReplyTo)
msg.SetGenHeader("Reply-To", m.ReplyTo)
}
for header := range m.Headers {
msg.SetHeader(header, m.Headers[header]...)
msg.SetGenHeader(gomail.Header(header), m.Headers[header]...)
}
if setting.MailService.SubjectPrefix != "" {
msg.SetHeader("Subject", setting.MailService.SubjectPrefix+" "+m.Subject)
msg.SetGenHeader("Subject", setting.MailService.SubjectPrefix+" "+m.Subject)
} else {
msg.SetHeader("Subject", m.Subject)
msg.SetGenHeader("Subject", m.Subject)
}
msg.SetDateHeader("Date", m.Date)
msg.SetHeader("X-Auto-Response-Suppress", "All")
msg.SetDateWithValue(m.Date)
msg.SetGenHeader("X-Auto-Response-Suppress", "All")
plainBody, err := html2text.FromString(m.Body)
if err != nil || setting.MailService.SendAsPlainText {
if strings.Contains(base.TruncateString(m.Body, 100), "<html>") {
log.Warn("Mail contains HTML but configured to send as plain text.")
}
msg.SetBody("text/plain", plainBody)
msg.SetBodyString("text/plain", plainBody)
} else {
msg.SetBody("text/plain", plainBody)
msg.AddAlternative("text/html", m.Body)
msg.SetBodyString("text/plain", plainBody)
msg.AddAlternativeString("text/html", m.Body)
}
if len(msg.GetHeader("Message-ID")) == 0 {
msg.SetHeader("Message-ID", m.generateAutoMessageID())
if len(msg.GetGenHeader("Message-ID")) == 0 {
msg.SetGenHeader("Message-ID", m.generateAutoMessageID())
}
for k, v := range setting.MailService.OverrideHeader {
if len(msg.GetHeader(k)) != 0 {
if len(msg.GetGenHeader(gomail.Header(k))) != 0 {
log.Debug("Mailer override header '%s' as per config", k)
}
msg.SetHeader(k, v...)
msg.SetGenHeader(gomail.Header(k), v...)
}
return msg

View File

@@ -25,25 +25,27 @@ func TestGenerateMessageID(t *testing.T) {
m := NewMessageFrom("", "display-name", "from-address", "subject", "body")
m.Date = date
gm := m.ToMessage()
assert.Equal(t, "<autogen-946782245000-41e8fc54a8ad3a3f@localhost>", gm.GetHeader("Message-ID")[0])
assert.Equal(t, "<autogen-946782245000-41e8fc54a8ad3a3f@localhost>", gm.GetGenHeader("Message-ID")[0])
m = NewMessageFrom("a@b.com", "display-name", "from-address", "subject", "body")
m.Date = date
gm = m.ToMessage()
assert.Equal(t, "<autogen-946782245000-cc88ce3cfe9bd04f@localhost>", gm.GetHeader("Message-ID")[0])
assert.Equal(t, "<autogen-946782245000-cc88ce3cfe9bd04f@localhost>", gm.GetGenHeader("Message-ID")[0])
m = NewMessageFrom("a@b.com", "display-name", "from-address", "subject", "body")
m.SetHeader("Message-ID", "<msg-d@domain.com>")
gm = m.ToMessage()
assert.Equal(t, "<msg-d@domain.com>", gm.GetHeader("Message-ID")[0])
assert.Equal(t, "<msg-d@domain.com>", gm.GetGenHeader("Message-ID")[0])
}
func TestToMessage(t *testing.T) {
oldConf := *setting.MailService
oldConf := setting.MailService
defer func() {
setting.MailService = &oldConf
setting.MailService = oldConf
}()
setting.MailService.From = "test@gitea.com"
setting.MailService = &setting.Mailer{
From: "test@gitea.com",
}
m1 := Message{
Info: "info",
@@ -54,18 +56,24 @@ func TestToMessage(t *testing.T) {
Body: "Some Issue got closed by Y-Man",
}
assertHeaders := func(t *testing.T, expected, header map[string]string) {
for k, v := range expected {
assert.Equal(t, v, header[k], "Header %s should be %s but got %s", k, v, header[k])
}
}
buf := &strings.Builder{}
_, err := m1.ToMessage().WriteTo(buf)
assert.NoError(t, err)
header, _ := extractMailHeaderAndContent(t, buf.String())
assert.EqualValues(t, map[string]string{
assertHeaders(t, map[string]string{
"Content-Type": "multipart/alternative;",
"Date": "Mon, 01 Jan 0001 00:00:00 +0000",
"From": "\"Test Gitea\" <test@gitea.com>",
"Message-ID": "<autogen--6795364578871-69c000786adc60dc@localhost>",
"Mime-Version": "1.0",
"MIME-Version": "1.0",
"Subject": "Issue X Closed",
"To": "a@b.com",
"To": "<a@b.com>",
"X-Auto-Response-Suppress": "All",
}, header)
@@ -78,14 +86,14 @@ func TestToMessage(t *testing.T) {
_, err = m1.ToMessage().WriteTo(buf)
assert.NoError(t, err)
header, _ = extractMailHeaderAndContent(t, buf.String())
assert.EqualValues(t, map[string]string{
assertHeaders(t, map[string]string{
"Content-Type": "multipart/alternative;",
"Date": "Mon, 01 Jan 0001 00:00:00 +0000",
"From": "\"Test Gitea\" <test@gitea.com>",
"Message-ID": "",
"Mime-Version": "1.0",
"MIME-Version": "1.0",
"Subject": "Issue X Closed",
"To": "a@b.com",
"To": "<a@b.com>",
"X-Auto-Response-Suppress": "All",
"Auto-Submitted": "auto-generated",
}, header)

View File

@@ -4,13 +4,15 @@
package sender
import (
"io"
"code.gitea.io/gitea/modules/log"
"code.gitea.io/gitea/modules/setting"
"gopkg.in/gomail.v2"
)
type Sender gomail.Sender
type Sender interface {
Send(from string, to []string, msg io.WriterTo) error
}
var Send = send
@@ -19,9 +21,18 @@ func send(sender Sender, msgs ...*Message) error {
log.Error("Mailer: Send is being invoked but mail service hasn't been initialized")
return nil
}
goMsgs := []*gomail.Message{}
for _, msg := range msgs {
goMsgs = append(goMsgs, msg.ToMessage())
m := msg.ToMessage()
froms := m.GetFrom()
to, err := m.GetRecipients()
if err != nil {
return err
}
// TODO: implement sending from multiple addresses
if err := sender.Send(froms[0].Address, to, m); err != nil {
return err
}
}
return gomail.Send(sender, goMsgs...)
return nil
}

View File

@@ -8,12 +8,13 @@ import (
"fmt"
"io"
"net"
"net/smtp"
"os"
"strings"
"code.gitea.io/gitea/modules/log"
"code.gitea.io/gitea/modules/setting"
"github.com/wneessen/go-mail/smtp"
)
// SMTPSender Sender SMTP mail sender
@@ -106,7 +107,7 @@ func (s *SMTPSender) Send(from string, to []string, msg io.WriterTo) error {
if strings.Contains(options, "CRAM-MD5") {
auth = smtp.CRAMMD5Auth(opts.User, opts.Passwd)
} else if strings.Contains(options, "PLAIN") {
auth = smtp.PlainAuth("", opts.User, opts.Passwd, host)
auth = smtp.PlainAuth("", opts.User, opts.Passwd, host, false)
} else if strings.Contains(options, "LOGIN") {
// Patch for AUTH LOGIN
auth = LoginAuth(opts.User, opts.Passwd)
@@ -146,5 +147,10 @@ func (s *SMTPSender) Send(from string, to []string, msg io.WriterTo) error {
return fmt.Errorf("SMTP close failed: %w", err)
}
return client.Quit()
err = client.Quit()
if err != nil {
log.Error("Quit client failed: %v", err)
}
return nil
}

View File

@@ -5,9 +5,9 @@ package sender
import (
"fmt"
"net/smtp"
"github.com/Azure/go-ntlmssp"
"github.com/wneessen/go-mail/smtp"
)
type loginAuth struct {