0
0
mirror of https://github.com/go-gitea/gitea.git synced 2025-12-06 22:41:06 +01:00

Use Golang net/smtp instead of gomail's smtp to send email (#36055) (#36083)

Backport #36055 by @lunny

Replace #36032
Fix #36030

This PR use `net/smtp` instead of gomail's smtp. Now
github.com/wneessen/go-mail will be used only for generating email
message body.

Co-authored-by: Lunny Xiao <xiaolunwen@gmail.com>
This commit is contained in:
Giteabot 2025-12-05 02:58:01 +08:00 committed by GitHub
parent 52902d4ece
commit 0ab447005d
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
4 changed files with 64 additions and 14 deletions

View File

@ -33,7 +33,13 @@ func (s *SendmailSender) Send(from string, to []string, msg io.WriterTo) error {
args := []string{"-f", envelopeFrom, "-i"} args := []string{"-f", envelopeFrom, "-i"}
args = append(args, setting.MailService.SendmailArgs...) args = append(args, setting.MailService.SendmailArgs...)
args = append(args, to...) for _, recipient := range to {
smtpTo, err := sanitizeEmailAddress(recipient)
if err != nil {
return fmt.Errorf("invalid recipient address %q: %w", recipient, err)
}
args = append(args, smtpTo)
}
log.Trace("Sending with: %s %v", setting.MailService.SendmailPath, args) log.Trace("Sending with: %s %v", setting.MailService.SendmailPath, args)
desc := fmt.Sprintf("SendMail: %s %v", setting.MailService.SendmailPath, args) desc := fmt.Sprintf("SendMail: %s %v", setting.MailService.SendmailPath, args)

View File

@ -9,13 +9,13 @@ import (
"fmt" "fmt"
"io" "io"
"net" "net"
"net/mail"
"net/smtp"
"os" "os"
"strings" "strings"
"code.gitea.io/gitea/modules/log" "code.gitea.io/gitea/modules/log"
"code.gitea.io/gitea/modules/setting" "code.gitea.io/gitea/modules/setting"
"github.com/wneessen/go-mail/smtp"
) )
// SMTPSender Sender SMTP mail sender // SMTPSender Sender SMTP mail sender
@ -108,7 +108,7 @@ func (s *SMTPSender) Send(from string, to []string, msg io.WriterTo) error {
if strings.Contains(options, "CRAM-MD5") { if strings.Contains(options, "CRAM-MD5") {
auth = smtp.CRAMMD5Auth(opts.User, opts.Passwd) auth = smtp.CRAMMD5Auth(opts.User, opts.Passwd)
} else if strings.Contains(options, "PLAIN") { } else if strings.Contains(options, "PLAIN") {
auth = smtp.PlainAuth("", opts.User, opts.Passwd, host, false) auth = smtp.PlainAuth("", opts.User, opts.Passwd, host)
} else if strings.Contains(options, "LOGIN") { } else if strings.Contains(options, "LOGIN") {
// Patch for AUTH LOGIN // Patch for AUTH LOGIN
auth = LoginAuth(opts.User, opts.Passwd) auth = LoginAuth(opts.User, opts.Passwd)
@ -123,18 +123,24 @@ func (s *SMTPSender) Send(from string, to []string, msg io.WriterTo) error {
} }
} }
if opts.OverrideEnvelopeFrom { fromAddr := from
if err = client.Mail(opts.EnvelopeFrom); err != nil { if opts.OverrideEnvelopeFrom && opts.EnvelopeFrom != "" {
return fmt.Errorf("failed to issue MAIL command: %w", err) fromAddr = opts.EnvelopeFrom
} }
} else { smtpFrom, err := sanitizeEmailAddress(fromAddr)
if err = client.Mail(fmt.Sprintf("<%s>", from)); err != nil { if err != nil {
return fmt.Errorf("failed to issue MAIL command: %w", err) return fmt.Errorf("invalid envelope from address: %w", err)
} }
if err = client.Mail(smtpFrom); err != nil {
return fmt.Errorf("failed to issue MAIL command: %w", err)
} }
for _, rec := range to { for _, rec := range to {
if err = client.Rcpt(rec); err != nil { smtpTo, err := sanitizeEmailAddress(rec)
if err != nil {
return fmt.Errorf("invalid recipient address %q: %w", rec, err)
}
if err = client.Rcpt(smtpTo); err != nil {
return fmt.Errorf("failed to issue RCPT command: %w", err) return fmt.Errorf("failed to issue RCPT command: %w", err)
} }
} }
@ -155,3 +161,11 @@ func (s *SMTPSender) Send(from string, to []string, msg io.WriterTo) error {
return nil return nil
} }
func sanitizeEmailAddress(raw string) (string, error) {
addr, err := mail.ParseAddress(strings.TrimSpace(strings.Trim(raw, "<>")))
if err != nil {
return "", err
}
return addr.Address, nil
}

View File

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

View File

@ -0,0 +1,30 @@
// Copyright 2025 The Gitea Authors. All rights reserved.
// SPDX-License-Identifier: MIT
package sender
import "testing"
func TestSanitizeEmailAddress(t *testing.T) {
tests := []struct {
input string
expected string
hasError bool
}{
{"abc@gitea.com", "abc@gitea.com", false},
{"<abc@gitea.com>", "abc@gitea.com", false},
{"ssss.com", "", true},
{"<invalid-email>", "", true},
}
for _, tt := range tests {
result, err := sanitizeEmailAddress(tt.input)
if (err != nil) != tt.hasError {
t.Errorf("sanitizeEmailAddress(%q) unexpected error status: got %v, want error: %v", tt.input, err != nil, tt.hasError)
continue
}
if result != tt.expected {
t.Errorf("sanitizeEmailAddress(%q) = %q; want %q", tt.input, result, tt.expected)
}
}
}