summaryrefslogtreecommitdiffstats
path: root/services
diff options
context:
space:
mode:
authorClar Fon <15850505+clarfonthey@users.noreply.github.com>2022-08-02 01:24:18 -0400
committerGitHub <noreply@github.com>2022-08-02 13:24:18 +0800
commit036dd8a788468e7730b29982747cc3cf8829ce86 (patch)
tree27d7bdd0a616a6f67a4e2140136cfb9c9b80296d /services
parentae3b88bef36179fb43ddcf2a14b46ca0969d8aad (diff)
downloadgitea-036dd8a788468e7730b29982747cc3cf8829ce86.tar.gz
gitea-036dd8a788468e7730b29982747cc3cf8829ce86.zip
Rework mailer settings (#18982)
* `PROTOCOL`: can be smtp, smtps, smtp+startls, smtp+unix, sendmail, dummy * `SMTP_ADDR`: domain for SMTP, or path to unix socket * `SMTP_PORT`: port for SMTP; defaults to 25 for `smtp`, 465 for `smtps`, and 587 for `smtp+startls` * `ENABLE_HELO`, `HELO_HOSTNAME`: reverse `DISABLE_HELO` to `ENABLE_HELO`; default to false + system hostname * `FORCE_TRUST_SERVER_CERT`: replace the unclear `SKIP_VERIFY` * `CLIENT_CERT_FILE`, `CLIENT_KEY_FILE`, `USE_CLIENT_CERT`: clarify client certificates here Co-authored-by: wxiaoguang <wxiaoguang@gmail.com> Co-authored-by: Lunny Xiao <xiaolunwen@gmail.com>
Diffstat (limited to 'services')
-rw-r--r--services/auth/source/smtp/auth.go6
-rw-r--r--services/auth/source/smtp/source.go2
-rw-r--r--services/auth/source/smtp/source_authenticate.go2
-rw-r--r--services/forms/auth_form.go2
-rw-r--r--services/forms/user_form.go3
-rw-r--r--services/mailer/mailer.go95
6 files changed, 64 insertions, 46 deletions
diff --git a/services/auth/source/smtp/auth.go b/services/auth/source/smtp/auth.go
index 8d0cbb11cd..a9e4b0e5f4 100644
--- a/services/auth/source/smtp/auth.go
+++ b/services/auth/source/smtp/auth.go
@@ -58,10 +58,10 @@ var ErrUnsupportedLoginType = errors.New("Login source is unknown")
func Authenticate(a smtp.Auth, source *Source) error {
tlsConfig := &tls.Config{
InsecureSkipVerify: source.SkipVerify,
- ServerName: source.Host,
+ ServerName: source.Addr,
}
- conn, err := net.Dial("tcp", net.JoinHostPort(source.Host, strconv.Itoa(source.Port)))
+ conn, err := net.Dial("tcp", net.JoinHostPort(source.Addr, strconv.Itoa(source.Port)))
if err != nil {
return err
}
@@ -71,7 +71,7 @@ func Authenticate(a smtp.Auth, source *Source) error {
conn = tls.Client(conn, tlsConfig)
}
- client, err := smtp.NewClient(conn, source.Host)
+ client, err := smtp.NewClient(conn, source.Addr)
if err != nil {
return fmt.Errorf("failed to create NewClient: %w", err)
}
diff --git a/services/auth/source/smtp/source.go b/services/auth/source/smtp/source.go
index 5e69f912da..b2286d42a0 100644
--- a/services/auth/source/smtp/source.go
+++ b/services/auth/source/smtp/source.go
@@ -19,7 +19,7 @@ import (
// Source holds configuration for the SMTP login source.
type Source struct {
Auth string
- Host string
+ Addr string
Port int
AllowedDomains string `xorm:"TEXT"`
ForceSMTPS bool
diff --git a/services/auth/source/smtp/source_authenticate.go b/services/auth/source/smtp/source_authenticate.go
index dff24d494e..63fd3e5511 100644
--- a/services/auth/source/smtp/source_authenticate.go
+++ b/services/auth/source/smtp/source_authenticate.go
@@ -32,7 +32,7 @@ func (source *Source) Authenticate(user *user_model.User, userName, password str
var auth smtp.Auth
switch source.Auth {
case PlainAuthentication:
- auth = smtp.PlainAuth("", userName, password, source.Host)
+ auth = smtp.PlainAuth("", userName, password, source.Addr)
case LoginAuthentication:
auth = &loginAuthenticator{userName, password}
case CRAMMD5Authentication:
diff --git a/services/forms/auth_form.go b/services/forms/auth_form.go
index 7e7c756752..9064be2cca 100644
--- a/services/forms/auth_form.go
+++ b/services/forms/auth_form.go
@@ -45,7 +45,7 @@ type AuthenticationForm struct {
IsActive bool
IsSyncEnabled bool
SMTPAuth string
- SMTPHost string
+ SMTPAddr string
SMTPPort int
AllowedDomains string
SecurityProtocol int `binding:"Range(0,2)"`
diff --git a/services/forms/user_form.go b/services/forms/user_form.go
index 405b4a9a49..c8f2b02d8c 100644
--- a/services/forms/user_form.go
+++ b/services/forms/user_form.go
@@ -40,7 +40,8 @@ type InstallForm struct {
AppURL string `binding:"Required"`
LogRootPath string `binding:"Required"`
- SMTPHost string
+ SMTPAddr string
+ SMTPPort string
SMTPFrom string
SMTPUser string `binding:"OmitEmpty;MaxSize(254)" locale:"install.mailer_user"`
SMTPPasswd string
diff --git a/services/mailer/mailer.go b/services/mailer/mailer.go
index f4bc2ddc63..c86c54c748 100644
--- a/services/mailer/mailer.go
+++ b/services/mailer/mailer.go
@@ -147,65 +147,82 @@ type smtpSender struct{}
func (s *smtpSender) Send(from string, to []string, msg io.WriterTo) error {
opts := setting.MailService
- host, port, err := net.SplitHostPort(opts.Host)
- if err != nil {
- return err
+ var network string
+ var address string
+ if opts.Protocol == "smtp+unix" {
+ network = "unix"
+ address = opts.SMTPAddr
+ } else {
+ network = "tcp"
+ address = net.JoinHostPort(opts.SMTPAddr, opts.SMTPPort)
}
- tlsconfig := &tls.Config{
- InsecureSkipVerify: opts.SkipVerify,
- ServerName: host,
+ conn, err := net.Dial(network, address)
+ if err != nil {
+ return fmt.Errorf("failed to establish network connection to SMTP server: %v", err)
}
+ defer conn.Close()
- if opts.UseCertificate {
- cert, err := tls.LoadX509KeyPair(opts.CertFile, opts.KeyFile)
- if err != nil {
- return err
+ var tlsconfig *tls.Config
+ if opts.Protocol == "smtps" || opts.Protocol == "smtp+startls" {
+ tlsconfig = &tls.Config{
+ InsecureSkipVerify: opts.ForceTrustServerCert,
+ ServerName: opts.SMTPAddr,
}
- tlsconfig.Certificates = []tls.Certificate{cert}
- }
- conn, err := net.Dial("tcp", net.JoinHostPort(host, port))
- if err != nil {
- return err
+ if opts.UseClientCert {
+ cert, err := tls.LoadX509KeyPair(opts.ClientCertFile, opts.ClientKeyFile)
+ if err != nil {
+ return fmt.Errorf("could not load SMTP client certificate: %v", err)
+ }
+ tlsconfig.Certificates = []tls.Certificate{cert}
+ }
}
- defer conn.Close()
- isSecureConn := opts.IsTLSEnabled || (strings.HasSuffix(port, "465"))
- // Start TLS directly if the port ends with 465 (SMTPS protocol)
- if isSecureConn {
+ if opts.Protocol == "smtps" {
conn = tls.Client(conn, tlsconfig)
}
+ host := "localhost"
+ if opts.Protocol == "smtp+unix" {
+ host = opts.SMTPAddr
+ }
client, err := smtp.NewClient(conn, host)
if err != nil {
- return fmt.Errorf("NewClient: %v", err)
+ return fmt.Errorf("could not initiate SMTP session: %v", err)
}
- if !opts.DisableHelo {
+ if opts.EnableHelo {
hostname := opts.HeloHostname
if len(hostname) == 0 {
hostname, err = os.Hostname()
if err != nil {
- return err
+ return fmt.Errorf("could not retrieve system hostname: %v", err)
}
}
if err = client.Hello(hostname); err != nil {
- return fmt.Errorf("Hello: %v", err)
+ return fmt.Errorf("failed to issue HELO command: %v", err)
}
}
- // If not using SMTPS, always use STARTTLS if available
- hasStartTLS, _ := client.Extension("STARTTLS")
- if !isSecureConn && hasStartTLS {
- if err = client.StartTLS(tlsconfig); err != nil {
- return fmt.Errorf("StartTLS: %v", err)
+ if opts.Protocol == "smtp+startls" {
+ hasStartTLS, _ := client.Extension("STARTTLS")
+ if hasStartTLS {
+ if err = client.StartTLS(tlsconfig); err != nil {
+ return fmt.Errorf("failed to start TLS connection: %v", err)
+ }
+ } else {
+ log.Warn("StartTLS requested, but SMTP server does not support it; falling back to regular SMTP")
}
}
canAuth, options := client.Extension("AUTH")
- if canAuth && len(opts.User) > 0 {
+ if len(opts.User) > 0 {
+ if !canAuth {
+ return fmt.Errorf("SMTP server does not support AUTH, but credentials provided")
+ }
+
var auth smtp.Auth
if strings.Contains(options, "CRAM-MD5") {
@@ -219,34 +236,34 @@ func (s *smtpSender) Send(from string, to []string, msg io.WriterTo) error {
if auth != nil {
if err = client.Auth(auth); err != nil {
- return fmt.Errorf("Auth: %v", err)
+ return fmt.Errorf("failed to authenticate SMTP: %v", err)
}
}
}
if opts.OverrideEnvelopeFrom {
if err = client.Mail(opts.EnvelopeFrom); err != nil {
- return fmt.Errorf("Mail: %v", err)
+ return fmt.Errorf("failed to issue MAIL command: %v", err)
}
} else {
if err = client.Mail(from); err != nil {
- return fmt.Errorf("Mail: %v", err)
+ return fmt.Errorf("failed to issue MAIL command: %v", err)
}
}
for _, rec := range to {
if err = client.Rcpt(rec); err != nil {
- return fmt.Errorf("Rcpt: %v", err)
+ return fmt.Errorf("failed to issue RCPT command: %v", err)
}
}
w, err := client.Data()
if err != nil {
- return fmt.Errorf("Data: %v", err)
+ return fmt.Errorf("failed to issue DATA command: %v", err)
} else if _, err = msg.WriteTo(w); err != nil {
- return fmt.Errorf("WriteTo: %v", err)
+ return fmt.Errorf("SMTP write failed: %v", err)
} else if err = w.Close(); err != nil {
- return fmt.Errorf("Close: %v", err)
+ return fmt.Errorf("SMTP close failed: %v", err)
}
return client.Quit()
@@ -338,13 +355,13 @@ func NewContext() {
return
}
- switch setting.MailService.MailerType {
- case "smtp":
- Sender = &smtpSender{}
+ switch setting.MailService.Protocol {
case "sendmail":
Sender = &sendmailSender{}
case "dummy":
Sender = &dummySender{}
+ default:
+ Sender = &smtpSender{}
}
mailQueue = queue.CreateQueue("mail", func(data ...queue.Data) []queue.Data {