From d60c43869420f5fc43ad19b454c9ae50dad65964 Mon Sep 17 00:00:00 2001 From: ltdk Date: Wed, 2 Mar 2022 15:37:37 -0500 Subject: [PATCH 01/15] Rework mailer settings (fixes #18901) --- cmd/admin.go | 8 +- custom/conf/app.example.ini | 52 +++--- modules/setting/mailer.go | 156 ++++++++++++++---- routers/install/install.go | 8 +- routers/web/admin/auths.go | 2 +- services/auth/source/smtp/auth.go | 6 +- services/auth/source/smtp/source.go | 2 +- .../auth/source/smtp/source_authenticate.go | 2 +- services/forms/auth_form.go | 2 +- services/forms/user_form.go | 3 +- services/mailer/mailer.go | 89 ++++++---- 11 files changed, 227 insertions(+), 103 deletions(-) diff --git a/cmd/admin.go b/cmd/admin.go index e4a254c613909..988eecadffbb5 100644 --- a/cmd/admin.go +++ b/cmd/admin.go @@ -404,9 +404,9 @@ var ( Usage: "SMTP Authentication Type (PLAIN/LOGIN/CRAM-MD5) default PLAIN", }, cli.StringFlag{ - Name: "host", + Name: "addr", Value: "", - Usage: "SMTP Host", + Usage: "SMTP Addr", }, cli.IntFlag{ Name: "port", @@ -936,8 +936,8 @@ func parseSMTPConfig(c *cli.Context, conf *smtp.Source) error { } conf.Auth = c.String("auth-type") } - if c.IsSet("host") { - conf.Host = c.String("host") + if c.IsSet("addr") { + conf.Addr = c.String("addr") } if c.IsSet("port") { conf.Port = c.Int("port") diff --git a/custom/conf/app.example.ini b/custom/conf/app.example.ini index cbfde9c8d8cc3..101fa33c15d41 100644 --- a/custom/conf/app.example.ini +++ b/custom/conf/app.example.ini @@ -1503,30 +1503,37 @@ PATH = ;; Prefix displayed before subject in mail ;SUBJECT_PREFIX = ;; -;; Mail server -;; Gmail: smtp.gmail.com:587 -;; QQ: smtp.qq.com:465 -;; As per RFC 8314 using Implicit TLS/SMTPS on port 465 (if supported) is recommended, -;; otherwise STARTTLS on port 587 should be used. -;HOST = -;; -;; Disable HELO operation when hostnames are different. -;DISABLE_HELO = -;; -;; Custom hostname for HELO operation, if no value is provided, one is retrieved from system. +;; Mail server protocol. One of "smtp", "smtps", "smtp+startls", "smtp+unix". +;; If your provider does not explicitly say which protocol it uses but does provide a port, +;; you can set SMTP_PORT instead and this will be inferred. +;PROTOCOL = +; +;; Mail server address, e.g. smtp.gmail.com. +;; For smtp+unix, this should be a path to a unix socket instead. +;SMTP_ADDR = +;; +;; Mail server port. Common ports are: +;; 25: insecure SMTP +;; 465: SMTP Secure +;; 587: StartTLS +;; If no protocol is specified, it will be inferred by this setting. +;SMTP_PORT = +;; +;; Enable HELO operation. Defaults to true. +;ENABLE_HELO = true +;; +;; Custom hostname for HELO operation. +;; If no value is provided, one is retrieved from system. ;HELO_HOSTNAME = ;; -;; Whether or not to skip verification of certificates; `true` to disable verification. This option is unsafe. Consider adding the certificate to the system trust store instead. -;SKIP_VERIFY = false -;; -;; Use client certificate -;USE_CERTIFICATE = false -;CERT_FILE = custom/mailer/cert.pem -;KEY_FILE = custom/mailer/key.pem +;; If set to `true`, completely ignores server certificate validation errors. +;; This option is unsafe. Consider adding the certificate to the system trust store instead. +;FORCE_TRUST_SERVER_CERT = false ;; -;; Should SMTP connect with TLS, (if port ends with 465 TLS will always be used.) -;; If this is false but STARTTLS is supported the connection will be upgraded to TLS opportunistically. -;IS_TLS_ENABLED = false +;; Use client certificate in connection. +;USE_CLIENT_CERT = false +;CLIENT_CERT_FILE = custom/mailer/cert.pem +;CLIENT_KEY_FILE = custom/mailer/key.pem ;; ;; Mail from address, RFC 5322. This can be just an email address, or the `"Name" ` format ;FROM = @@ -1534,8 +1541,7 @@ PATH = ;; Sometimes it is helpful to use a different address on the envelope. Set this to use ENVELOPE_FROM as the from on the envelope. Set to `<>` to send an empty address. ;ENVELOPE_FROM = ;; -;; Mailer user name and password -;; Please Note: Authentication is only supported when the SMTP server communication is encrypted with TLS (this can be via STARTTLS) or `HOST=localhost`. +;; Mailer user name and password, if required by provider. ;USER = ;; ;; Use PASSWD = `your password` for quoting if you use special characters in the password. diff --git a/modules/setting/mailer.go b/modules/setting/mailer.go index 8a26f8b0c49f8..2887e9ca083d1 100644 --- a/modules/setting/mailer.go +++ b/modules/setting/mailer.go @@ -5,7 +5,9 @@ package setting import ( + "net" "net/mail" + "strings" "time" "code.gitea.io/gitea/modules/log" @@ -27,14 +29,16 @@ type Mailer struct { SubjectPrefix string // SMTP sender - Host string - User, Passwd string - DisableHelo bool - HeloHostname string - SkipVerify bool - UseCertificate bool - CertFile, KeyFile string - IsTLSEnabled bool + Protocol string + SMTPAddr string + SMTPPort string + User, Passwd string + EnableHelo bool + HeloHostname string + ForceTrustServerCert bool + UseClientCert bool + ClientCertFile string + ClientKeyFile string // Sendmail sender SendmailPath string @@ -58,17 +62,18 @@ func newMailService() { SendAsPlainText: sec.Key("SEND_AS_PLAIN_TEXT").MustBool(false), MailerType: sec.Key("MAILER_TYPE").In("", []string{"smtp", "sendmail", "dummy"}), - Host: sec.Key("HOST").String(), - User: sec.Key("USER").String(), - Passwd: sec.Key("PASSWD").String(), - DisableHelo: sec.Key("DISABLE_HELO").MustBool(), - HeloHostname: sec.Key("HELO_HOSTNAME").String(), - SkipVerify: sec.Key("SKIP_VERIFY").MustBool(), - UseCertificate: sec.Key("USE_CERTIFICATE").MustBool(), - CertFile: sec.Key("CERT_FILE").String(), - KeyFile: sec.Key("KEY_FILE").String(), - IsTLSEnabled: sec.Key("IS_TLS_ENABLED").MustBool(), - SubjectPrefix: sec.Key("SUBJECT_PREFIX").MustString(""), + Protocol: sec.Key("PROTOCOL").In("", []string{"smtp", "smtps", "smtp+startls", "smtp+unix"}), + SMTPAddr: sec.Key("SMTP_ADDR").String(), + SMTPPort: sec.Key("SMTP_PORT").String(), + User: sec.Key("USER").String(), + Passwd: sec.Key("PASSWD").String(), + EnableHelo: sec.Key("ENABLE_HELO").MustBool(true), + HeloHostname: sec.Key("HELO_HOSTNAME").String(), + ForceTrustServerCert: sec.Key("FORCE_TRUST_SERVER_CERT").MustBool(false), + UseClientCert: sec.Key("USE_CLIENT_CERT").MustBool(false), + ClientCertFile: sec.Key("CLIENT_CERT_FILE").String(), + ClientKeyFile: sec.Key("CLIENT_KEY_FILE").String(), + SubjectPrefix: sec.Key("SUBJECT_PREFIX").MustString(""), SendmailPath: sec.Key("SENDMAIL_PATH").MustString("sendmail"), SendmailTimeout: sec.Key("SENDMAIL_TIMEOUT").MustDuration(5 * time.Minute), @@ -77,12 +82,6 @@ func newMailService() { MailService.From = sec.Key("FROM").MustString(MailService.User) MailService.EnvelopeFrom = sec.Key("ENVELOPE_FROM").MustString("") - // FIXME: DEPRECATED to be removed in v1.18.0 - deprecatedSetting("mailer", "ENABLE_HTML_ALTERNATIVE", "mailer", "SEND_AS_PLAIN_TEXT") - if sec.HasKey("ENABLE_HTML_ALTERNATIVE") { - MailService.SendAsPlainText = !sec.Key("ENABLE_HTML_ALTERNATIVE").MustBool(false) - } - // FIXME: DEPRECATED to be removed in v1.18.0 deprecatedSetting("mailer", "USE_SENDMAIL", "mailer", "MAILER_TYPE") if sec.HasKey("USE_SENDMAIL") { @@ -91,6 +90,109 @@ func newMailService() { } } + if MailService.MailerType == "" { + MailService.MailerType = "smtp" + } + + // FIXME: DEPRECATED to be removed in v1.18.0 + deprecatedSetting("mailer", "HOST", "mailer", "SMTP_ADDR") + if sec.HasKey("HOST") { + givenHost := sec.Key("HOST").String() + addr, port, err := net.SplitHostPort(givenHost) + if err != nil { + log.Fatal("Invalid mailer.HOST (%s): %v", givenHost, err) + } + MailService.SMTPAddr = addr + MailService.SMTPPort = port + } + + // FIXME: DEPRECATED to be removed in v1.18.0 + deprecatedSetting("mailer", "IS_TLS_ENABLED", "mailer", "PROTOCOL") + if sec.HasKey("IS_TLS_ENABLED") { + if sec.Key("IS_TLS_ENABLED").MustBool() { + MailService.Protocol = "smtps" + } else { + MailService.Protocol = "smtp+startls" + } + } + + if MailService.SMTPPort == "" { + switch MailService.Protocol { + case "smtp": + MailService.SMTPPort = "25" + case "smtps": + MailService.SMTPPort = "465" + case "smtp+startls": + MailService.SMTPPort = "587" + } + } + + if MailService.MailerType == "smtp" && MailService.Protocol == "" { + if strings.ContainsAny(MailService.SMTPAddr, "/\\") { + MailService.Protocol = "smtp+unix" + } else { + switch MailService.SMTPPort { + case "25": + MailService.Protocol = "smtp" + case "465": + MailService.Protocol = "smtps" + case "587": + MailService.Protocol = "smtp+startls" + default: + log.Fatal("unable to infer unspecified mailer.PROTOCOL from mailer.SMTP_PORT = \"%s\"", MailService.SMTPPort) + } + } + } + + if MailService.Protocol == "smtp" { + switch MailService.SMTPAddr { + case "localhost": + case "127.0.0.1": + case "::1": + case "[::1]": + // this is a local address, so, we're fine + break + default: + log.Warn("connect via insecure SMTP to non-local address") + } + } + + // FIXME: DEPRECATED to be removed in v1.18.0 + deprecatedSetting("mailer", "DISABLE_HELO", "mailer", "ENABLE_HELO") + if sec.HasKey("DISABLE_HELO") { + MailService.EnableHelo = !sec.Key("DISABLE_HELO").MustBool() + } + + // FIXME: DEPRECATED to be removed in v1.18.0 + deprecatedSetting("mailer", "SKIP_VERIFY", "mailer", "FORCE_TRUST_SERVER_CERT") + if sec.HasKey("SKIP_VERIFY") { + MailService.ForceTrustServerCert = sec.Key("SKIP_VERIFY").MustBool() + } + + // FIXME: DEPRECATED to be removed in v1.18.0 + deprecatedSetting("mailer", "USE_CERTIFICATE", "mailer", "USE_CLIENT_CERT") + if sec.HasKey("USE_CERTIFICATE") { + MailService.UseClientCert = sec.Key("USE_CLIENT_CERT").MustBool() + } + + // FIXME: DEPRECATED to be removed in v1.18.0 + deprecatedSetting("mailer", "CERT_FILE", "mailer", "CLIENT_CERT_FILE") + if sec.HasKey("CERT_FILE") { + MailService.ClientCertFile = sec.Key("CERT_FILE").String() + } + + // FIXME: DEPRECATED to be removed in v1.18.0 + deprecatedSetting("mailer", "KEY_FILE", "mailer", "CLIENT_KEY_FILE") + if sec.HasKey("KEY_FILE") { + MailService.ClientKeyFile = sec.Key("KEY_FILE").String() + } + + // FIXME: DEPRECATED to be removed in v1.18.0 + deprecatedSetting("mailer", "ENABLE_HTML_ALTERNATIVE", "mailer", "SEND_AS_PLAIN_TEXT") + if sec.HasKey("ENABLE_HTML_ALTERNATIVE") { + MailService.SendAsPlainText = !sec.Key("ENABLE_HTML_ALTERNATIVE").MustBool(false) + } + parsed, err := mail.ParseAddress(MailService.From) if err != nil { log.Fatal("Invalid mailer.FROM (%s): %v", MailService.From, err) @@ -113,10 +215,6 @@ func newMailService() { MailService.EnvelopeFrom = parsed.Address } - if MailService.MailerType == "" { - MailService.MailerType = "smtp" - } - if MailService.MailerType == "sendmail" { MailService.SendmailArgs, err = shellquote.Split(sec.Key("SENDMAIL_ARGS").String()) if err != nil { diff --git a/routers/install/install.go b/routers/install/install.go index ec1719439f53a..f05664b9162d9 100644 --- a/routers/install/install.go +++ b/routers/install/install.go @@ -131,7 +131,8 @@ func Install(ctx *context.Context) { // E-mail service settings if setting.MailService != nil { - form.SMTPHost = setting.MailService.Host + form.SMTPAddr = setting.MailService.SMTPAddr + form.SMTPPort = setting.MailService.SMTPPort form.SMTPFrom = setting.MailService.From form.SMTPUser = setting.MailService.User } @@ -418,9 +419,10 @@ func SubmitInstall(ctx *context.Context) { cfg.Section("server").Key("LFS_START_SERVER").SetValue("false") } - if len(strings.TrimSpace(form.SMTPHost)) > 0 { + if len(strings.TrimSpace(form.SMTPAddr)) > 0 { cfg.Section("mailer").Key("ENABLED").SetValue("true") - cfg.Section("mailer").Key("HOST").SetValue(form.SMTPHost) + cfg.Section("mailer").Key("SMTP_ADDR").SetValue(form.SMTPAddr) + cfg.Section("mailer").Key("SMTP_PORT").SetValue(form.SMTPPort) cfg.Section("mailer").Key("FROM").SetValue(form.SMTPFrom) cfg.Section("mailer").Key("USER").SetValue(form.SMTPUser) cfg.Section("mailer").Key("PASSWD").SetValue(form.SMTPPasswd) diff --git a/routers/web/admin/auths.go b/routers/web/admin/auths.go index 1d72a88aa1bd9..815269d3a5a9d 100644 --- a/routers/web/admin/auths.go +++ b/routers/web/admin/auths.go @@ -159,7 +159,7 @@ func parseLDAPConfig(form forms.AuthenticationForm) *ldap.Source { func parseSMTPConfig(form forms.AuthenticationForm) *smtp.Source { return &smtp.Source{ Auth: form.SMTPAuth, - Host: form.SMTPHost, + Addr: form.SMTPAddr, Port: form.SMTPPort, AllowedDomains: form.AllowedDomains, ForceSMTPS: form.ForceSMTPS, diff --git a/services/auth/source/smtp/auth.go b/services/auth/source/smtp/auth.go index 8d0cbb11cdc9a..a9e4b0e5f4454 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 5e69f912da35b..b2286d42a0ff7 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 3be2f1128de2f..6e3c5037877aa 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 7e7c75675299b..9064be2cca38e 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 405b4a9a490f2..c8f2b02d8c809 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 3ca9b50fc6147..11f4386972a5c 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() From bc7b4549989ab0379ed0a3f9391f6b69b94085ae Mon Sep 17 00:00:00 2001 From: ltdk Date: Tue, 3 May 2022 16:29:50 -0400 Subject: [PATCH 02/15] Do proper check if SMTP_ADDR is local IP --- modules/setting/mailer.go | 35 ++++++++++++++++++++++++++--------- 1 file changed, 26 insertions(+), 9 deletions(-) diff --git a/modules/setting/mailer.go b/modules/setting/mailer.go index 2887e9ca083d1..a186ec0b76d92 100644 --- a/modules/setting/mailer.go +++ b/modules/setting/mailer.go @@ -144,16 +144,15 @@ func newMailService() { } } + // we want to warn if users use SMTP on a non-local IP; + // we might as well take the opportunity to check that it has an IP at all + ips := tryResolveAddr(MailService.SMTPAddr) if MailService.Protocol == "smtp" { - switch MailService.SMTPAddr { - case "localhost": - case "127.0.0.1": - case "::1": - case "[::1]": - // this is a local address, so, we're fine - break - default: - log.Warn("connect via insecure SMTP to non-local address") + for _, ip := range ips { + if !ip.IsLoopback() { + log.Warn("connect via insecure SMTP to non-local address") + break + } } } @@ -246,3 +245,21 @@ func newNotifyMailService() { Service.EnableNotifyMail = true log.Info("Notify Mail Service Enabled") } + +func tryResolveAddr(addr string) []net.IP { + if strings.HasPrefix(addr, "[") && strings.HasSuffix(addr, "]") { + addr = addr[1 : len(addr)-1] + } + ip := net.ParseIP(addr) + if ip != nil { + ips := make([]net.IP, 1) + ips[0] = ip + return ips + } + ips, err := net.LookupIP(addr) + if err != nil { + log.Warn("could not look up mailer.SMTP_ADDR: %v", err) + return make([]net.IP, 0) + } + return ips +} From a4736f8a0af0ed0ff76f7d64a517d882f912344f Mon Sep 17 00:00:00 2001 From: ltdk Date: Sun, 8 May 2022 11:53:45 -0400 Subject: [PATCH 03/15] Reword insecure SMTP warning --- modules/setting/mailer.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/setting/mailer.go b/modules/setting/mailer.go index a186ec0b76d92..6327e09f6e33e 100644 --- a/modules/setting/mailer.go +++ b/modules/setting/mailer.go @@ -150,7 +150,7 @@ func newMailService() { if MailService.Protocol == "smtp" { for _, ip := range ips { if !ip.IsLoopback() { - log.Warn("connect via insecure SMTP to non-local address") + log.Warn("connecting over insecure SMTP protocol to non-local address is not recommended") break } } From 5f1d299bee9917f59ae8e16402bdd3e12fdfb0d4 Mon Sep 17 00:00:00 2001 From: wxiaoguang Date: Thu, 28 Jul 2022 18:44:20 +0800 Subject: [PATCH 04/15] fix comments to "be removed in v1.19.0" --- modules/setting/mailer.go | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/modules/setting/mailer.go b/modules/setting/mailer.go index 6327e09f6e33e..c5acd5c0a4fd6 100644 --- a/modules/setting/mailer.go +++ b/modules/setting/mailer.go @@ -82,7 +82,7 @@ func newMailService() { MailService.From = sec.Key("FROM").MustString(MailService.User) MailService.EnvelopeFrom = sec.Key("ENVELOPE_FROM").MustString("") - // FIXME: DEPRECATED to be removed in v1.18.0 + // FIXME: DEPRECATED to be removed in v1.19.0 deprecatedSetting("mailer", "USE_SENDMAIL", "mailer", "MAILER_TYPE") if sec.HasKey("USE_SENDMAIL") { if MailService.MailerType == "" && sec.Key("USE_SENDMAIL").MustBool(false) { @@ -94,7 +94,7 @@ func newMailService() { MailService.MailerType = "smtp" } - // FIXME: DEPRECATED to be removed in v1.18.0 + // FIXME: DEPRECATED to be removed in v1.19.0 deprecatedSetting("mailer", "HOST", "mailer", "SMTP_ADDR") if sec.HasKey("HOST") { givenHost := sec.Key("HOST").String() @@ -106,7 +106,7 @@ func newMailService() { MailService.SMTPPort = port } - // FIXME: DEPRECATED to be removed in v1.18.0 + // FIXME: DEPRECATED to be removed in v1.19.0 deprecatedSetting("mailer", "IS_TLS_ENABLED", "mailer", "PROTOCOL") if sec.HasKey("IS_TLS_ENABLED") { if sec.Key("IS_TLS_ENABLED").MustBool() { @@ -156,37 +156,37 @@ func newMailService() { } } - // FIXME: DEPRECATED to be removed in v1.18.0 + // FIXME: DEPRECATED to be removed in v1.19.0 deprecatedSetting("mailer", "DISABLE_HELO", "mailer", "ENABLE_HELO") if sec.HasKey("DISABLE_HELO") { MailService.EnableHelo = !sec.Key("DISABLE_HELO").MustBool() } - // FIXME: DEPRECATED to be removed in v1.18.0 + // FIXME: DEPRECATED to be removed in v1.19.0 deprecatedSetting("mailer", "SKIP_VERIFY", "mailer", "FORCE_TRUST_SERVER_CERT") if sec.HasKey("SKIP_VERIFY") { MailService.ForceTrustServerCert = sec.Key("SKIP_VERIFY").MustBool() } - // FIXME: DEPRECATED to be removed in v1.18.0 + // FIXME: DEPRECATED to be removed in v1.19.0 deprecatedSetting("mailer", "USE_CERTIFICATE", "mailer", "USE_CLIENT_CERT") if sec.HasKey("USE_CERTIFICATE") { MailService.UseClientCert = sec.Key("USE_CLIENT_CERT").MustBool() } - // FIXME: DEPRECATED to be removed in v1.18.0 + // FIXME: DEPRECATED to be removed in v1.19.0 deprecatedSetting("mailer", "CERT_FILE", "mailer", "CLIENT_CERT_FILE") if sec.HasKey("CERT_FILE") { MailService.ClientCertFile = sec.Key("CERT_FILE").String() } - // FIXME: DEPRECATED to be removed in v1.18.0 + // FIXME: DEPRECATED to be removed in v1.19.0 deprecatedSetting("mailer", "KEY_FILE", "mailer", "CLIENT_KEY_FILE") if sec.HasKey("KEY_FILE") { MailService.ClientKeyFile = sec.Key("KEY_FILE").String() } - // FIXME: DEPRECATED to be removed in v1.18.0 + // FIXME: DEPRECATED to be removed in v1.19.0 deprecatedSetting("mailer", "ENABLE_HTML_ALTERNATIVE", "mailer", "SEND_AS_PLAIN_TEXT") if sec.HasKey("ENABLE_HTML_ALTERNATIVE") { MailService.SendAsPlainText = !sec.Key("ENABLE_HTML_ALTERNATIVE").MustBool(false) From dad722ee2b78c94dbc833fd1c9aecb3f30f9c363 Mon Sep 17 00:00:00 2001 From: wxiaoguang Date: Thu, 28 Jul 2022 18:50:43 +0800 Subject: [PATCH 05/15] only use deprecated key if the new key doesn't exist --- modules/setting/mailer.go | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/modules/setting/mailer.go b/modules/setting/mailer.go index c5acd5c0a4fd6..7ab785d423cec 100644 --- a/modules/setting/mailer.go +++ b/modules/setting/mailer.go @@ -84,7 +84,7 @@ func newMailService() { // FIXME: DEPRECATED to be removed in v1.19.0 deprecatedSetting("mailer", "USE_SENDMAIL", "mailer", "MAILER_TYPE") - if sec.HasKey("USE_SENDMAIL") { + if sec.HasKey("USE_SENDMAIL") && !sec.HasKey("MAILER_TYPE") { if MailService.MailerType == "" && sec.Key("USE_SENDMAIL").MustBool(false) { MailService.MailerType = "sendmail" } @@ -96,7 +96,7 @@ func newMailService() { // FIXME: DEPRECATED to be removed in v1.19.0 deprecatedSetting("mailer", "HOST", "mailer", "SMTP_ADDR") - if sec.HasKey("HOST") { + if sec.HasKey("HOST") && !sec.HasKey("SMTP_ADDR") { givenHost := sec.Key("HOST").String() addr, port, err := net.SplitHostPort(givenHost) if err != nil { @@ -108,7 +108,7 @@ func newMailService() { // FIXME: DEPRECATED to be removed in v1.19.0 deprecatedSetting("mailer", "IS_TLS_ENABLED", "mailer", "PROTOCOL") - if sec.HasKey("IS_TLS_ENABLED") { + if sec.HasKey("IS_TLS_ENABLED") && !sec.HasKey("PROTOCOL") { if sec.Key("IS_TLS_ENABLED").MustBool() { MailService.Protocol = "smtps" } else { @@ -158,37 +158,37 @@ func newMailService() { // FIXME: DEPRECATED to be removed in v1.19.0 deprecatedSetting("mailer", "DISABLE_HELO", "mailer", "ENABLE_HELO") - if sec.HasKey("DISABLE_HELO") { + if sec.HasKey("DISABLE_HELO") && !sec.HasKey("ENABLE_HELO") { MailService.EnableHelo = !sec.Key("DISABLE_HELO").MustBool() } // FIXME: DEPRECATED to be removed in v1.19.0 deprecatedSetting("mailer", "SKIP_VERIFY", "mailer", "FORCE_TRUST_SERVER_CERT") - if sec.HasKey("SKIP_VERIFY") { + if sec.HasKey("SKIP_VERIFY") && !sec.HasKey("FORCE_TRUST_SERVER_CERT") { MailService.ForceTrustServerCert = sec.Key("SKIP_VERIFY").MustBool() } // FIXME: DEPRECATED to be removed in v1.19.0 deprecatedSetting("mailer", "USE_CERTIFICATE", "mailer", "USE_CLIENT_CERT") - if sec.HasKey("USE_CERTIFICATE") { + if sec.HasKey("USE_CERTIFICATE") && !sec.HasKey("USE_CLIENT_CERT") { MailService.UseClientCert = sec.Key("USE_CLIENT_CERT").MustBool() } // FIXME: DEPRECATED to be removed in v1.19.0 deprecatedSetting("mailer", "CERT_FILE", "mailer", "CLIENT_CERT_FILE") - if sec.HasKey("CERT_FILE") { + if sec.HasKey("CERT_FILE") && !sec.HasKey("CLIENT_CERT_FILE") { MailService.ClientCertFile = sec.Key("CERT_FILE").String() } // FIXME: DEPRECATED to be removed in v1.19.0 deprecatedSetting("mailer", "KEY_FILE", "mailer", "CLIENT_KEY_FILE") - if sec.HasKey("KEY_FILE") { + if sec.HasKey("KEY_FILE") && !sec.HasKey("CLIENT_KEY_FILE") { MailService.ClientKeyFile = sec.Key("KEY_FILE").String() } // FIXME: DEPRECATED to be removed in v1.19.0 deprecatedSetting("mailer", "ENABLE_HTML_ALTERNATIVE", "mailer", "SEND_AS_PLAIN_TEXT") - if sec.HasKey("ENABLE_HTML_ALTERNATIVE") { + if sec.HasKey("ENABLE_HTML_ALTERNATIVE") && !sec.HasKey("SEND_AS_PLAIN_TEXT") { MailService.SendAsPlainText = !sec.Key("ENABLE_HTML_ALTERNATIVE").MustBool(false) } From ae30251c0c83cea952ed733a251bf6672d0409f9 Mon Sep 17 00:00:00 2001 From: wxiaoguang Date: Thu, 28 Jul 2022 19:16:43 +0800 Subject: [PATCH 06/15] sync document and options --- custom/conf/app.example.ini | 12 ++-- .../doc/advanced/config-cheat-sheet.en-us.md | 55 +++++++++---------- modules/setting/mailer.go | 20 +++---- services/mailer/mailer.go | 6 +- 4 files changed, 43 insertions(+), 50 deletions(-) diff --git a/custom/conf/app.example.ini b/custom/conf/app.example.ini index 01d4ce089c213..01711cc3df8f3 100644 --- a/custom/conf/app.example.ini +++ b/custom/conf/app.example.ini @@ -1499,11 +1499,16 @@ ROUTER = console ;; Prefix displayed before subject in mail ;SUBJECT_PREFIX = ;; -;; Mail server protocol. One of "smtp", "smtps", "smtp+startls", "smtp+unix". +;; NOTICE: many options are new in 1.18. For old Gitea, please refer to old app.example.ini +;; eg: https://github.com/go-gitea/gitea/blob/release/v1.17/custom/conf/app.example.ini +;; +;; Mail server protocol. One of "smtp", "smtps", "smtp+startls", "smtp+unix", "sendmail", "dummy". +;; - sendmail: use the operating system's `sendmail` command instead of SMTP. This is common on Linux systems. +;; - dummy: send email messages to the log as a testing phase. ;; If your provider does not explicitly say which protocol it uses but does provide a port, ;; you can set SMTP_PORT instead and this will be inferred. ;PROTOCOL = -; +;; ;; Mail server address, e.g. smtp.gmail.com. ;; For smtp+unix, this should be a path to a unix socket instead. ;SMTP_ADDR = @@ -1546,9 +1551,6 @@ ROUTER = console ;; Send mails as plain text ;SEND_AS_PLAIN_TEXT = false ;; -;; Set Mailer Type (either SMTP, sendmail or dummy to just send to the log) -;MAILER_TYPE = smtp -;; ;; Specify an alternative sendmail binary ;SENDMAIL_PATH = sendmail ;; diff --git a/docs/content/doc/advanced/config-cheat-sheet.en-us.md b/docs/content/doc/advanced/config-cheat-sheet.en-us.md index 31a294e1c9334..26838a045d7ab 100644 --- a/docs/content/doc/advanced/config-cheat-sheet.en-us.md +++ b/docs/content/doc/advanced/config-cheat-sheet.en-us.md @@ -644,42 +644,39 @@ Define allowed algorithms and their minimum key length (use -1 to disable a type ## Mailer (`mailer`) +*NOTICE: many options are new in 1.18. For old Gitea, please refer to old app.example.ini. +eg: https://github.com/go-gitea/gitea/blob/release/v1.17/custom/conf/app.example.ini* + - `ENABLED`: **false**: Enable to use a mail service. -- `DISABLE_HELO`: **\**: Disable HELO operation. -- `HELO_HOSTNAME`: **\**: Custom hostname for HELO operation. -- `HOST`: **\**: SMTP mail host address and port (example: smtp.gitea.io:587). - - As per RFC 8314, if supported, Implicit TLS/SMTPS on port 465 is recommended, otherwise opportunistic TLS via STARTTLS on port 587 should be used. -- `IS_TLS_ENABLED` : **false** : Forcibly use TLS to connect even if not on a default SMTPS port. - - Note, if the port ends with `465` Implicit TLS/SMTPS/SMTP over TLS will be used despite this setting. - - Otherwise if `IS_TLS_ENABLED=false` and the server supports `STARTTLS` this will be used. Thus if `STARTTLS` is preferred you should set `IS_TLS_ENABLED=false`. -- `FROM`: **\**: Mail from address, RFC 5322. This can be just an email address, or - the "Name" \ format. -- `ENVELOPE_FROM`: **\**: Address set as the From address on the SMTP mail envelope. Set to `<>` to send an empty address. +- `PROTOCOL`: **\**: Mail server protocol. One of "smtp", "smtps", "smtp+startls", "smtp+unix", "sendmail", "dummy". + - SMTP family, if your provider does not explicitly say which protocol it uses but does provide a port, you can set SMTP_PORT instead and this will be inferred. + - **sendmail** Use the operating system's `sendmail` command instead of SMTP. This is common on Linux systems. + - **dummy** Send email messages to the log as a testing phase. + - Note that enabling sendmail will ignore all other `mailer` settings except `ENABLED`, `FROM`, `SUBJECT_PREFIX` and `SENDMAIL_PATH`. + - Enabling dummy will ignore all settings except `ENABLED`, `SUBJECT_PREFIX` and `FROM`. +- `SMTP_ADDR`: **\**: Mail server address. e.g. smtp.gmail.com. For smtp+unix, this should be a path to a unix socket instead. +- `SMTP_PORT`: **\**: Mail server port. If no protocol is specified, it will be inferred by this setting. Common ports are: + - 25: insecure SMTP + - 465: SMTP Secure + - 587: StartTLS +- `USE_CLIENT_CERT`: **false**: Use client certificate for TLS/SSL. +- `CLIENT_CERT_FILE`: **custom/mailer/cert.pem**: Client certificate file. +- `CLIENT_KEY_FILE`: **custom/mailer/key.pem**: Client key file. +- `FORCE_TRUST_SERVER_CERT`: **false**: If set to `true`, completely ignores server certificate validation errors. This option is unsafe. Consider adding the certificate to the system trust store instead. - `USER`: **\**: Username of mailing user (usually the sender's e-mail address). - `PASSWD`: **\**: Password of mailing user. Use \`your password\` for quoting if you use special characters in the password. - - Please note: authentication is only supported when the SMTP server communication is encrypted with TLS (this can be via `STARTTLS`) or `HOST=localhost`. See [Email Setup]({{< relref "doc/usage/email-setup.en-us.md" >}}) for more information. -- `SEND_AS_PLAIN_TEXT`: **false**: Send mails as plain text. -- `SKIP_VERIFY`: **false**: Whether or not to skip verification of certificates; `true` to disable verification. - - **Warning:** This option is unsafe. Consider adding the certificate to the system trust store instead. - - **Note:** Gitea only supports SMTP with STARTTLS. -- `USE_CERTIFICATE`: **false**: Use client certificate. -- `CERT_FILE`: **custom/mailer/cert.pem** -- `KEY_FILE`: **custom/mailer/key.pem** + - Please note: authentication is only supported when the SMTP server communication is encrypted with TLS (this can be via `STARTTLS`) or SMTP host is localhost. See [Email Setup]({{< relref "doc/usage/email-setup.en-us.md" >}}) for more information. +- `ENABLE_HELO`: **true**: Enable HELO operation. +- `HELO_HOSTNAME`: **(retrieved from system)**: HELO hostname. +- `FROM`: **\**: Mail from address, RFC 5322. This can be just an email address, or the "Name" \ format. +- `ENVELOPE_FROM`: **\**: Address set as the From address on the SMTP mail envelope. Set to `<>` to send an empty address. - `SUBJECT_PREFIX`: **\**: Prefix to be placed before e-mail subject lines. -- `MAILER_TYPE`: **smtp**: \[smtp, sendmail, dummy\] - - **smtp** Use SMTP to send mail - - **sendmail** Use the operating system's `sendmail` command instead of SMTP. - This is common on Linux systems. - - **dummy** Send email messages to the log as a testing phase. - - Note that enabling sendmail will ignore all other `mailer` settings except `ENABLED`, - `FROM`, `SUBJECT_PREFIX` and `SENDMAIL_PATH`. - - Enabling dummy will ignore all settings except `ENABLED`, `SUBJECT_PREFIX` and `FROM`. -- `SENDMAIL_PATH`: **sendmail**: The location of sendmail on the operating system (can be - command or full path). -- `SENDMAIL_ARGS`: **_empty_**: Specify any extra sendmail arguments. (NOTE: you should be aware that email addresses can look like options - if your `sendmail` command takes options you must set the option terminator `--`) +- `SENDMAIL_PATH`: **sendmail**: The location of sendmail on the operating system (can be command or full path). +- `SENDMAIL_ARGS`: **\**: Specify any extra sendmail arguments. (NOTE: you should be aware that email addresses can look like options - if your `sendmail` command takes options you must set the option terminator `--`) - `SENDMAIL_TIMEOUT`: **5m**: default timeout for sending email through sendmail - `SENDMAIL_CONVERT_CRLF`: **true**: Most versions of sendmail prefer LF line endings rather than CRLF line endings. Set this to false if your version of sendmail requires CRLF line endings. - `SEND_BUFFER_LEN`: **100**: Buffer length of mailing queue. **DEPRECATED** use `LENGTH` in `[queue.mailer]` +- `SEND_AS_PLAIN_TEXT`: **false**: Send e-mail as plain text. ## Cache (`cache`) diff --git a/modules/setting/mailer.go b/modules/setting/mailer.go index 7ab785d423cec..7e8dafe482cd5 100644 --- a/modules/setting/mailer.go +++ b/modules/setting/mailer.go @@ -25,7 +25,6 @@ type Mailer struct { FromName string FromEmail string SendAsPlainText bool - MailerType string SubjectPrefix string // SMTP sender @@ -60,9 +59,8 @@ func newMailService() { MailService = &Mailer{ Name: sec.Key("NAME").MustString(AppName), SendAsPlainText: sec.Key("SEND_AS_PLAIN_TEXT").MustBool(false), - MailerType: sec.Key("MAILER_TYPE").In("", []string{"smtp", "sendmail", "dummy"}), - Protocol: sec.Key("PROTOCOL").In("", []string{"smtp", "smtps", "smtp+startls", "smtp+unix"}), + Protocol: sec.Key("PROTOCOL").In("", []string{"smtp", "smtps", "smtp+startls", "smtp+unix", "sendmail", "dummy"}), SMTPAddr: sec.Key("SMTP_ADDR").String(), SMTPPort: sec.Key("SMTP_PORT").String(), User: sec.Key("USER").String(), @@ -83,17 +81,13 @@ func newMailService() { MailService.EnvelopeFrom = sec.Key("ENVELOPE_FROM").MustString("") // FIXME: DEPRECATED to be removed in v1.19.0 - deprecatedSetting("mailer", "USE_SENDMAIL", "mailer", "MAILER_TYPE") - if sec.HasKey("USE_SENDMAIL") && !sec.HasKey("MAILER_TYPE") { - if MailService.MailerType == "" && sec.Key("USE_SENDMAIL").MustBool(false) { - MailService.MailerType = "sendmail" + deprecatedSetting("mailer", "MAILER_TYPE", "mailer", "PROTOCOL") + if sec.HasKey("MAILER_TYPE") && !sec.HasKey("PROTOCOL") { + if sec.Key("MAILER_TYPE").String() == "sendmail" { + MailService.Protocol = "sendmail" } } - if MailService.MailerType == "" { - MailService.MailerType = "smtp" - } - // FIXME: DEPRECATED to be removed in v1.19.0 deprecatedSetting("mailer", "HOST", "mailer", "SMTP_ADDR") if sec.HasKey("HOST") && !sec.HasKey("SMTP_ADDR") { @@ -127,7 +121,7 @@ func newMailService() { } } - if MailService.MailerType == "smtp" && MailService.Protocol == "" { + if MailService.Protocol == "" { if strings.ContainsAny(MailService.SMTPAddr, "/\\") { MailService.Protocol = "smtp+unix" } else { @@ -214,7 +208,7 @@ func newMailService() { MailService.EnvelopeFrom = parsed.Address } - if MailService.MailerType == "sendmail" { + if MailService.Protocol == "sendmail" { MailService.SendmailArgs, err = shellquote.Split(sec.Key("SENDMAIL_ARGS").String()) if err != nil { log.Error("Failed to parse Sendmail args: %s with error %v", CustomConf, err) diff --git a/services/mailer/mailer.go b/services/mailer/mailer.go index d81308fd0a30e..c86c54c748c59 100644 --- a/services/mailer/mailer.go +++ b/services/mailer/mailer.go @@ -355,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 { From 8d207029e91379927058c040b59bc53ffa34f518 Mon Sep 17 00:00:00 2001 From: wxiaoguang Date: Thu, 28 Jul 2022 19:31:37 +0800 Subject: [PATCH 07/15] fix smtp fields in installation page --- modules/setting/mailer.go | 20 +++++++++++++------- options/locale/locale_en-US.ini | 1 + templates/install.tmpl | 8 ++++++-- 3 files changed, 20 insertions(+), 9 deletions(-) diff --git a/modules/setting/mailer.go b/modules/setting/mailer.go index 7e8dafe482cd5..d6f1dae0f7156 100644 --- a/modules/setting/mailer.go +++ b/modules/setting/mailer.go @@ -133,7 +133,8 @@ func newMailService() { case "587": MailService.Protocol = "smtp+startls" default: - log.Fatal("unable to infer unspecified mailer.PROTOCOL from mailer.SMTP_PORT = \"%s\"", MailService.SMTPPort) + log.Error("unable to infer unspecified mailer.PROTOCOL from mailer.SMTP_PORT = %q, assume using smtps", MailService.SMTPPort) + MailService.Protocol = "smtps" } } } @@ -186,12 +187,16 @@ func newMailService() { MailService.SendAsPlainText = !sec.Key("ENABLE_HTML_ALTERNATIVE").MustBool(false) } - parsed, err := mail.ParseAddress(MailService.From) - if err != nil { - log.Fatal("Invalid mailer.FROM (%s): %v", MailService.From, err) + if MailService.From != "" { + parsed, err := mail.ParseAddress(MailService.From) + if err != nil { + log.Fatal("Invalid mailer.FROM (%s): %v", MailService.From, err) + } + MailService.FromName = parsed.Name + MailService.FromEmail = parsed.Address + } else { + log.Error("no mailer.FROM provided, email system may not work.") } - MailService.FromName = parsed.Name - MailService.FromEmail = parsed.Address switch MailService.EnvelopeFrom { case "": @@ -200,7 +205,7 @@ func newMailService() { MailService.EnvelopeFrom = "" MailService.OverrideEnvelopeFrom = true default: - parsed, err = mail.ParseAddress(MailService.EnvelopeFrom) + parsed, err := mail.ParseAddress(MailService.EnvelopeFrom) if err != nil { log.Fatal("Invalid mailer.ENVELOPE_FROM (%s): %v", MailService.EnvelopeFrom, err) } @@ -209,6 +214,7 @@ func newMailService() { } if MailService.Protocol == "sendmail" { + var err error MailService.SendmailArgs, err = shellquote.Split(sec.Key("SENDMAIL_ARGS").String()) if err != nil { log.Error("Failed to parse Sendmail args: %s with error %v", CustomConf, err) diff --git a/options/locale/locale_en-US.ini b/options/locale/locale_en-US.ini index 257bae80d8c0e..728cbd4005268 100644 --- a/options/locale/locale_en-US.ini +++ b/options/locale/locale_en-US.ini @@ -180,6 +180,7 @@ log_root_path_helper = Log files will be written to this directory. optional_title = Optional Settings email_title = Email Settings smtp_host = SMTP Host +smtp_addr = SMTP Port smtp_from = Send Email As smtp_from_helper = Email address Gitea will use. Enter a plain email address or use the "Name" format. mailer_user = SMTP Username diff --git a/templates/install.tmpl b/templates/install.tmpl index 8f87a9e0d64c9..36f58218d4638 100644 --- a/templates/install.tmpl +++ b/templates/install.tmpl @@ -173,8 +173,12 @@ {{.locale.Tr "install.email_title"}}
- - + + +
+
+ +
From 360e32e7d978b923d663f2e8320a251177301b21 Mon Sep 17 00:00:00 2001 From: wxiaoguang Date: Thu, 28 Jul 2022 19:53:10 +0800 Subject: [PATCH 08/15] fix markdown lint --- .../doc/advanced/config-cheat-sheet.en-us.md | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/docs/content/doc/advanced/config-cheat-sheet.en-us.md b/docs/content/doc/advanced/config-cheat-sheet.en-us.md index 26838a045d7ab..2007b766c9333 100644 --- a/docs/content/doc/advanced/config-cheat-sheet.en-us.md +++ b/docs/content/doc/advanced/config-cheat-sheet.en-us.md @@ -644,16 +644,16 @@ Define allowed algorithms and their minimum key length (use -1 to disable a type ## Mailer (`mailer`) -*NOTICE: many options are new in 1.18. For old Gitea, please refer to old app.example.ini. -eg: https://github.com/go-gitea/gitea/blob/release/v1.17/custom/conf/app.example.ini* +_NOTICE: **many options are new in 1.18**. For old Gitea, please refer to old app.example.ini. +eg: https://github.com/go-gitea/gitea/blob/release/v1.17/custom/conf/app.example.ini_ - `ENABLED`: **false**: Enable to use a mail service. - `PROTOCOL`: **\**: Mail server protocol. One of "smtp", "smtps", "smtp+startls", "smtp+unix", "sendmail", "dummy". - - SMTP family, if your provider does not explicitly say which protocol it uses but does provide a port, you can set SMTP_PORT instead and this will be inferred. - - **sendmail** Use the operating system's `sendmail` command instead of SMTP. This is common on Linux systems. - - **dummy** Send email messages to the log as a testing phase. - - Note that enabling sendmail will ignore all other `mailer` settings except `ENABLED`, `FROM`, `SUBJECT_PREFIX` and `SENDMAIL_PATH`. - - Enabling dummy will ignore all settings except `ENABLED`, `SUBJECT_PREFIX` and `FROM`. + - SMTP family, if your provider does not explicitly say which protocol it uses but does provide a port, you can set SMTP_PORT instead and this will be inferred. + - **sendmail** Use the operating system's `sendmail` command instead of SMTP. This is common on Linux systems. + - **dummy** Send email messages to the log as a testing phase. + - Note that enabling sendmail will ignore all other `mailer` settings except `ENABLED`, `FROM`, `SUBJECT_PREFIX` and `SENDMAIL_PATH`. + - Enabling dummy will ignore all settings except `ENABLED`, `SUBJECT_PREFIX` and `FROM`. - `SMTP_ADDR`: **\**: Mail server address. e.g. smtp.gmail.com. For smtp+unix, this should be a path to a unix socket instead. - `SMTP_PORT`: **\**: Mail server port. If no protocol is specified, it will be inferred by this setting. Common ports are: - 25: insecure SMTP From 12c00fde3d0b14f57221feff3d22f1d783d37a99 Mon Sep 17 00:00:00 2001 From: wxiaoguang Date: Thu, 28 Jul 2022 19:53:10 +0800 Subject: [PATCH 09/15] fix i18n --- options/locale/locale_en-US.ini | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/options/locale/locale_en-US.ini b/options/locale/locale_en-US.ini index 728cbd4005268..6af42d0bf9b8a 100644 --- a/options/locale/locale_en-US.ini +++ b/options/locale/locale_en-US.ini @@ -179,8 +179,8 @@ log_root_path_helper = Log files will be written to this directory. optional_title = Optional Settings email_title = Email Settings -smtp_host = SMTP Host -smtp_addr = SMTP Port +smtp_addr = SMTP Host +smtp_port = SMTP Port smtp_from = Send Email As smtp_from_helper = Email address Gitea will use. Enter a plain email address or use the "Name" format. mailer_user = SMTP Username From 4ecb4e8794a6f04f3ac337390e93e374d9e3f7ba Mon Sep 17 00:00:00 2001 From: wxiaoguang Date: Thu, 28 Jul 2022 22:08:45 +0800 Subject: [PATCH 10/15] Update docs/content/doc/advanced/config-cheat-sheet.en-us.md --- docs/content/doc/advanced/config-cheat-sheet.en-us.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/content/doc/advanced/config-cheat-sheet.en-us.md b/docs/content/doc/advanced/config-cheat-sheet.en-us.md index 2007b766c9333..143070ab3f902 100644 --- a/docs/content/doc/advanced/config-cheat-sheet.en-us.md +++ b/docs/content/doc/advanced/config-cheat-sheet.en-us.md @@ -648,7 +648,7 @@ _NOTICE: **many options are new in 1.18**. For old Gitea, please refer to old ap eg: https://github.com/go-gitea/gitea/blob/release/v1.17/custom/conf/app.example.ini_ - `ENABLED`: **false**: Enable to use a mail service. -- `PROTOCOL`: **\**: Mail server protocol. One of "smtp", "smtps", "smtp+startls", "smtp+unix", "sendmail", "dummy". +- `PROTOCOL`: **\**: Since 1.18. Mail server protocol. One of "smtp", "smtps", "smtp+startls", "smtp+unix", "sendmail", "dummy". - SMTP family, if your provider does not explicitly say which protocol it uses but does provide a port, you can set SMTP_PORT instead and this will be inferred. - **sendmail** Use the operating system's `sendmail` command instead of SMTP. This is common on Linux systems. - **dummy** Send email messages to the log as a testing phase. From 466d6ba3f576ab8b04aa33f6eee488c1131de9f5 Mon Sep 17 00:00:00 2001 From: wxiaoguang Date: Thu, 28 Jul 2022 22:12:08 +0800 Subject: [PATCH 11/15] Update custom/conf/app.example.ini --- custom/conf/app.example.ini | 1 + 1 file changed, 1 insertion(+) diff --git a/custom/conf/app.example.ini b/custom/conf/app.example.ini index 01711cc3df8f3..aef49c2abb8f3 100644 --- a/custom/conf/app.example.ini +++ b/custom/conf/app.example.ini @@ -1507,6 +1507,7 @@ ROUTER = console ;; - dummy: send email messages to the log as a testing phase. ;; If your provider does not explicitly say which protocol it uses but does provide a port, ;; you can set SMTP_PORT instead and this will be inferred. +;; Since 1.18 ;PROTOCOL = ;; ;; Mail server address, e.g. smtp.gmail.com. From 4fbdc262c3d271e7c63c4c4dfaed630cdce3d164 Mon Sep 17 00:00:00 2001 From: Clar Fon <15850505+clarfonthey@users.noreply.github.com> Date: Fri, 29 Jul 2022 16:56:43 -0400 Subject: [PATCH 12/15] Update config-cheat-sheet.en-us.md --- docs/content/doc/advanced/config-cheat-sheet.en-us.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/content/doc/advanced/config-cheat-sheet.en-us.md b/docs/content/doc/advanced/config-cheat-sheet.en-us.md index 143070ab3f902..2007b766c9333 100644 --- a/docs/content/doc/advanced/config-cheat-sheet.en-us.md +++ b/docs/content/doc/advanced/config-cheat-sheet.en-us.md @@ -648,7 +648,7 @@ _NOTICE: **many options are new in 1.18**. For old Gitea, please refer to old ap eg: https://github.com/go-gitea/gitea/blob/release/v1.17/custom/conf/app.example.ini_ - `ENABLED`: **false**: Enable to use a mail service. -- `PROTOCOL`: **\**: Since 1.18. Mail server protocol. One of "smtp", "smtps", "smtp+startls", "smtp+unix", "sendmail", "dummy". +- `PROTOCOL`: **\**: Mail server protocol. One of "smtp", "smtps", "smtp+startls", "smtp+unix", "sendmail", "dummy". - SMTP family, if your provider does not explicitly say which protocol it uses but does provide a port, you can set SMTP_PORT instead and this will be inferred. - **sendmail** Use the operating system's `sendmail` command instead of SMTP. This is common on Linux systems. - **dummy** Send email messages to the log as a testing phase. From 45df0a6bb139ba4fbdb8e1e5f42bff39a75a2006 Mon Sep 17 00:00:00 2001 From: Clar Fon <15850505+clarfonthey@users.noreply.github.com> Date: Fri, 29 Jul 2022 16:57:04 -0400 Subject: [PATCH 13/15] Update app.example.ini --- custom/conf/app.example.ini | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/custom/conf/app.example.ini b/custom/conf/app.example.ini index aef49c2abb8f3..be75d7269d69f 100644 --- a/custom/conf/app.example.ini +++ b/custom/conf/app.example.ini @@ -1507,7 +1507,7 @@ ROUTER = console ;; - dummy: send email messages to the log as a testing phase. ;; If your provider does not explicitly say which protocol it uses but does provide a port, ;; you can set SMTP_PORT instead and this will be inferred. -;; Since 1.18 +;; ;PROTOCOL = ;; ;; Mail server address, e.g. smtp.gmail.com. From be36a99031d8b3760e9e822f3a58f0514d3c445e Mon Sep 17 00:00:00 2001 From: ltdk Date: Fri, 29 Jul 2022 17:11:48 -0400 Subject: [PATCH 14/15] Update docs to make conversion between settings clearer --- custom/conf/app.example.ini | 9 ++++----- docs/content/doc/advanced/config-cheat-sheet.en-us.md | 11 ++++------- 2 files changed, 8 insertions(+), 12 deletions(-) diff --git a/custom/conf/app.example.ini b/custom/conf/app.example.ini index be75d7269d69f..372fc10ada9b5 100644 --- a/custom/conf/app.example.ini +++ b/custom/conf/app.example.ini @@ -1499,19 +1499,17 @@ ROUTER = console ;; Prefix displayed before subject in mail ;SUBJECT_PREFIX = ;; -;; NOTICE: many options are new in 1.18. For old Gitea, please refer to old app.example.ini -;; eg: https://github.com/go-gitea/gitea/blob/release/v1.17/custom/conf/app.example.ini -;; ;; Mail server protocol. One of "smtp", "smtps", "smtp+startls", "smtp+unix", "sendmail", "dummy". ;; - sendmail: use the operating system's `sendmail` command instead of SMTP. This is common on Linux systems. ;; - dummy: send email messages to the log as a testing phase. ;; If your provider does not explicitly say which protocol it uses but does provide a port, ;; you can set SMTP_PORT instead and this will be inferred. -;; +;; (Before 1.18, this was controlled via MAILER_TYPE and IS_TLS_ENABLED.) ;PROTOCOL = ;; ;; Mail server address, e.g. smtp.gmail.com. ;; For smtp+unix, this should be a path to a unix socket instead. +;; (Before 1.18, this was combined with SMTP_PORT as HOST.) ;SMTP_ADDR = ;; ;; Mail server port. Common ports are: @@ -1519,6 +1517,7 @@ ROUTER = console ;; 465: SMTP Secure ;; 587: StartTLS ;; If no protocol is specified, it will be inferred by this setting. +;; (Before 1.18, this was combined with SMTP_ADDR as HOST.) ;SMTP_PORT = ;; ;; Enable HELO operation. Defaults to true. @@ -1549,7 +1548,7 @@ ROUTER = console ;; Use PASSWD = `your password` for quoting if you use special characters in the password. ;PASSWD = ;; -;; Send mails as plain text +;; Send mails only in plain text, without HTML alternative ;SEND_AS_PLAIN_TEXT = false ;; ;; Specify an alternative sendmail binary diff --git a/docs/content/doc/advanced/config-cheat-sheet.en-us.md b/docs/content/doc/advanced/config-cheat-sheet.en-us.md index 2007b766c9333..91cb5ac451519 100644 --- a/docs/content/doc/advanced/config-cheat-sheet.en-us.md +++ b/docs/content/doc/advanced/config-cheat-sheet.en-us.md @@ -644,18 +644,15 @@ Define allowed algorithms and their minimum key length (use -1 to disable a type ## Mailer (`mailer`) -_NOTICE: **many options are new in 1.18**. For old Gitea, please refer to old app.example.ini. -eg: https://github.com/go-gitea/gitea/blob/release/v1.17/custom/conf/app.example.ini_ - - `ENABLED`: **false**: Enable to use a mail service. -- `PROTOCOL`: **\**: Mail server protocol. One of "smtp", "smtps", "smtp+startls", "smtp+unix", "sendmail", "dummy". +- `PROTOCOL`: **\**: Mail server protocol. One of "smtp", "smtps", "smtp+startls", "smtp+unix", "sendmail", "dummy". *Before 1.18, this was inferred from a combination of `MAILER_TYPE` and `IS_TLS_ENABLED`.* - SMTP family, if your provider does not explicitly say which protocol it uses but does provide a port, you can set SMTP_PORT instead and this will be inferred. - **sendmail** Use the operating system's `sendmail` command instead of SMTP. This is common on Linux systems. - **dummy** Send email messages to the log as a testing phase. - Note that enabling sendmail will ignore all other `mailer` settings except `ENABLED`, `FROM`, `SUBJECT_PREFIX` and `SENDMAIL_PATH`. - Enabling dummy will ignore all settings except `ENABLED`, `SUBJECT_PREFIX` and `FROM`. -- `SMTP_ADDR`: **\**: Mail server address. e.g. smtp.gmail.com. For smtp+unix, this should be a path to a unix socket instead. -- `SMTP_PORT`: **\**: Mail server port. If no protocol is specified, it will be inferred by this setting. Common ports are: +- `SMTP_ADDR`: **\**: Mail server address. e.g. smtp.gmail.com. For smtp+unix, this should be a path to a unix socket instead. *Before 1.18, this was combined with `SMTP_PORT` under the name `HOST`.* +- `SMTP_PORT`: **\**: Mail server port. If no protocol is specified, it will be inferred by this setting. Common ports are listed below. *Before 1.18, this was combined with `SMTP_ADDR` under the name `HOST`.* - 25: insecure SMTP - 465: SMTP Secure - 587: StartTLS @@ -676,7 +673,7 @@ eg: https://github.com/go-gitea/gitea/blob/release/v1.17/custom/conf/app.example - `SENDMAIL_TIMEOUT`: **5m**: default timeout for sending email through sendmail - `SENDMAIL_CONVERT_CRLF`: **true**: Most versions of sendmail prefer LF line endings rather than CRLF line endings. Set this to false if your version of sendmail requires CRLF line endings. - `SEND_BUFFER_LEN`: **100**: Buffer length of mailing queue. **DEPRECATED** use `LENGTH` in `[queue.mailer]` -- `SEND_AS_PLAIN_TEXT`: **false**: Send e-mail as plain text. +- `SEND_AS_PLAIN_TEXT`: **false**: Send mails only in plain text, without HTML alternative. ## Cache (`cache`) From c14ec6c0d3a4ff83d5629a8be53c214259636f02 Mon Sep 17 00:00:00 2001 From: wxiaoguang Date: Sat, 30 Jul 2022 10:03:36 +0800 Subject: [PATCH 15/15] fix lint errors --- docs/content/doc/advanced/config-cheat-sheet.en-us.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/docs/content/doc/advanced/config-cheat-sheet.en-us.md b/docs/content/doc/advanced/config-cheat-sheet.en-us.md index 91cb5ac451519..22bd3bed9ebbf 100644 --- a/docs/content/doc/advanced/config-cheat-sheet.en-us.md +++ b/docs/content/doc/advanced/config-cheat-sheet.en-us.md @@ -645,14 +645,14 @@ Define allowed algorithms and their minimum key length (use -1 to disable a type ## Mailer (`mailer`) - `ENABLED`: **false**: Enable to use a mail service. -- `PROTOCOL`: **\**: Mail server protocol. One of "smtp", "smtps", "smtp+startls", "smtp+unix", "sendmail", "dummy". *Before 1.18, this was inferred from a combination of `MAILER_TYPE` and `IS_TLS_ENABLED`.* +- `PROTOCOL`: **\**: Mail server protocol. One of "smtp", "smtps", "smtp+startls", "smtp+unix", "sendmail", "dummy". _Before 1.18, this was inferred from a combination of `MAILER_TYPE` and `IS_TLS_ENABLED`._ - SMTP family, if your provider does not explicitly say which protocol it uses but does provide a port, you can set SMTP_PORT instead and this will be inferred. - **sendmail** Use the operating system's `sendmail` command instead of SMTP. This is common on Linux systems. - **dummy** Send email messages to the log as a testing phase. - Note that enabling sendmail will ignore all other `mailer` settings except `ENABLED`, `FROM`, `SUBJECT_PREFIX` and `SENDMAIL_PATH`. - Enabling dummy will ignore all settings except `ENABLED`, `SUBJECT_PREFIX` and `FROM`. -- `SMTP_ADDR`: **\**: Mail server address. e.g. smtp.gmail.com. For smtp+unix, this should be a path to a unix socket instead. *Before 1.18, this was combined with `SMTP_PORT` under the name `HOST`.* -- `SMTP_PORT`: **\**: Mail server port. If no protocol is specified, it will be inferred by this setting. Common ports are listed below. *Before 1.18, this was combined with `SMTP_ADDR` under the name `HOST`.* +- `SMTP_ADDR`: **\**: Mail server address. e.g. smtp.gmail.com. For smtp+unix, this should be a path to a unix socket instead. _Before 1.18, this was combined with `SMTP_PORT` under the name `HOST`._ +- `SMTP_PORT`: **\**: Mail server port. If no protocol is specified, it will be inferred by this setting. Common ports are listed below. _Before 1.18, this was combined with `SMTP_ADDR` under the name `HOST`._ - 25: insecure SMTP - 465: SMTP Secure - 587: StartTLS