Skip to content

Commit ee6e371

Browse files
lunnyGiteaBot
andauthored
Use Golang net/smtp instead of gomail's smtp to send email (#36055)
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: Giteabot <teabot@gitea.io>
1 parent e30a130 commit ee6e371

File tree

4 files changed

+64
-14
lines changed

4 files changed

+64
-14
lines changed

services/mailer/sender/sendmail.go

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,13 @@ func (s *SendmailSender) Send(from string, to []string, msg io.WriterTo) error {
3333

3434
args := []string{"-f", envelopeFrom, "-i"}
3535
args = append(args, setting.MailService.SendmailArgs...)
36-
args = append(args, to...)
36+
for _, recipient := range to {
37+
smtpTo, err := sanitizeEmailAddress(recipient)
38+
if err != nil {
39+
return fmt.Errorf("invalid recipient address %q: %w", recipient, err)
40+
}
41+
args = append(args, smtpTo)
42+
}
3743
log.Trace("Sending with: %s %v", setting.MailService.SendmailPath, args)
3844

3945
desc := fmt.Sprintf("SendMail: %s %v", setting.MailService.SendmailPath, args)

services/mailer/sender/smtp.go

Lines changed: 26 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -9,13 +9,13 @@ import (
99
"fmt"
1010
"io"
1111
"net"
12+
"net/mail"
13+
"net/smtp"
1214
"os"
1315
"strings"
1416

1517
"code.gitea.io/gitea/modules/log"
1618
"code.gitea.io/gitea/modules/setting"
17-
18-
"github.com/wneessen/go-mail/smtp"
1919
)
2020

2121
// SMTPSender Sender SMTP mail sender
@@ -108,7 +108,7 @@ func (s *SMTPSender) Send(from string, to []string, msg io.WriterTo) error {
108108
if strings.Contains(options, "CRAM-MD5") {
109109
auth = smtp.CRAMMD5Auth(opts.User, opts.Passwd)
110110
} else if strings.Contains(options, "PLAIN") {
111-
auth = smtp.PlainAuth("", opts.User, opts.Passwd, host, false)
111+
auth = smtp.PlainAuth("", opts.User, opts.Passwd, host)
112112
} else if strings.Contains(options, "LOGIN") {
113113
// Patch for AUTH LOGIN
114114
auth = LoginAuth(opts.User, opts.Passwd)
@@ -123,18 +123,24 @@ func (s *SMTPSender) Send(from string, to []string, msg io.WriterTo) error {
123123
}
124124
}
125125

126-
if opts.OverrideEnvelopeFrom {
127-
if err = client.Mail(opts.EnvelopeFrom); err != nil {
128-
return fmt.Errorf("failed to issue MAIL command: %w", err)
129-
}
130-
} else {
131-
if err = client.Mail(fmt.Sprintf("<%s>", from)); err != nil {
132-
return fmt.Errorf("failed to issue MAIL command: %w", err)
133-
}
126+
fromAddr := from
127+
if opts.OverrideEnvelopeFrom && opts.EnvelopeFrom != "" {
128+
fromAddr = opts.EnvelopeFrom
129+
}
130+
smtpFrom, err := sanitizeEmailAddress(fromAddr)
131+
if err != nil {
132+
return fmt.Errorf("invalid envelope from address: %w", err)
133+
}
134+
if err = client.Mail(smtpFrom); err != nil {
135+
return fmt.Errorf("failed to issue MAIL command: %w", err)
134136
}
135137

136138
for _, rec := range to {
137-
if err = client.Rcpt(rec); err != nil {
139+
smtpTo, err := sanitizeEmailAddress(rec)
140+
if err != nil {
141+
return fmt.Errorf("invalid recipient address %q: %w", rec, err)
142+
}
143+
if err = client.Rcpt(smtpTo); err != nil {
138144
return fmt.Errorf("failed to issue RCPT command: %w", err)
139145
}
140146
}
@@ -155,3 +161,11 @@ func (s *SMTPSender) Send(from string, to []string, msg io.WriterTo) error {
155161

156162
return nil
157163
}
164+
165+
func sanitizeEmailAddress(raw string) (string, error) {
166+
addr, err := mail.ParseAddress(strings.TrimSpace(strings.Trim(raw, "<>")))
167+
if err != nil {
168+
return "", err
169+
}
170+
return addr.Address, nil
171+
}

services/mailer/sender/smtp_auth.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,9 +6,9 @@ package sender
66
import (
77
"errors"
88
"fmt"
9+
"net/smtp"
910

1011
"github.com/Azure/go-ntlmssp"
11-
"github.com/wneessen/go-mail/smtp"
1212
)
1313

1414
type loginAuth struct {
Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
// Copyright 2025 The Gitea Authors. All rights reserved.
2+
// SPDX-License-Identifier: MIT
3+
4+
package sender
5+
6+
import "testing"
7+
8+
func TestSanitizeEmailAddress(t *testing.T) {
9+
tests := []struct {
10+
input string
11+
expected string
12+
hasError bool
13+
}{
14+
{"abc@gitea.com", "abc@gitea.com", false},
15+
{"<abc@gitea.com>", "abc@gitea.com", false},
16+
{"ssss.com", "", true},
17+
{"<invalid-email>", "", true},
18+
}
19+
20+
for _, tt := range tests {
21+
result, err := sanitizeEmailAddress(tt.input)
22+
if (err != nil) != tt.hasError {
23+
t.Errorf("sanitizeEmailAddress(%q) unexpected error status: got %v, want error: %v", tt.input, err != nil, tt.hasError)
24+
continue
25+
}
26+
if result != tt.expected {
27+
t.Errorf("sanitizeEmailAddress(%q) = %q; want %q", tt.input, result, tt.expected)
28+
}
29+
}
30+
}

0 commit comments

Comments
 (0)