mirror of
https://codeberg.org/forgejo/forgejo
synced 2024-11-25 11:16:11 +01:00
036dd8a788
* `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>
266 lines
8.1 KiB
Go
266 lines
8.1 KiB
Go
// Copyright 2019 The Gitea Authors. All rights reserved.
|
|
// Use of this source code is governed by a MIT-style
|
|
// license that can be found in the LICENSE file.
|
|
|
|
package setting
|
|
|
|
import (
|
|
"net"
|
|
"net/mail"
|
|
"strings"
|
|
"time"
|
|
|
|
"code.gitea.io/gitea/modules/log"
|
|
|
|
shellquote "github.com/kballard/go-shellquote"
|
|
)
|
|
|
|
// Mailer represents mail service.
|
|
type Mailer struct {
|
|
// Mailer
|
|
Name string
|
|
From string
|
|
EnvelopeFrom string
|
|
OverrideEnvelopeFrom bool `ini:"-"`
|
|
FromName string
|
|
FromEmail string
|
|
SendAsPlainText bool
|
|
SubjectPrefix string
|
|
|
|
// SMTP sender
|
|
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
|
|
SendmailArgs []string
|
|
SendmailTimeout time.Duration
|
|
SendmailConvertCRLF bool
|
|
}
|
|
|
|
// MailService the global mailer
|
|
var MailService *Mailer
|
|
|
|
func newMailService() {
|
|
sec := Cfg.Section("mailer")
|
|
// Check mailer setting.
|
|
if !sec.Key("ENABLED").MustBool() {
|
|
return
|
|
}
|
|
|
|
MailService = &Mailer{
|
|
Name: sec.Key("NAME").MustString(AppName),
|
|
SendAsPlainText: sec.Key("SEND_AS_PLAIN_TEXT").MustBool(false),
|
|
|
|
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(),
|
|
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),
|
|
SendmailConvertCRLF: sec.Key("SENDMAIL_CONVERT_CRLF").MustBool(true),
|
|
}
|
|
MailService.From = sec.Key("FROM").MustString(MailService.User)
|
|
MailService.EnvelopeFrom = sec.Key("ENVELOPE_FROM").MustString("")
|
|
|
|
// FIXME: DEPRECATED to be removed in v1.19.0
|
|
deprecatedSetting("mailer", "MAILER_TYPE", "mailer", "PROTOCOL")
|
|
if sec.HasKey("MAILER_TYPE") && !sec.HasKey("PROTOCOL") {
|
|
if sec.Key("MAILER_TYPE").String() == "sendmail" {
|
|
MailService.Protocol = "sendmail"
|
|
}
|
|
}
|
|
|
|
// FIXME: DEPRECATED to be removed in v1.19.0
|
|
deprecatedSetting("mailer", "HOST", "mailer", "SMTP_ADDR")
|
|
if sec.HasKey("HOST") && !sec.HasKey("SMTP_ADDR") {
|
|
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.19.0
|
|
deprecatedSetting("mailer", "IS_TLS_ENABLED", "mailer", "PROTOCOL")
|
|
if sec.HasKey("IS_TLS_ENABLED") && !sec.HasKey("PROTOCOL") {
|
|
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.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.Error("unable to infer unspecified mailer.PROTOCOL from mailer.SMTP_PORT = %q, assume using smtps", MailService.SMTPPort)
|
|
MailService.Protocol = "smtps"
|
|
}
|
|
}
|
|
}
|
|
|
|
// 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" {
|
|
for _, ip := range ips {
|
|
if !ip.IsLoopback() {
|
|
log.Warn("connecting over insecure SMTP protocol to non-local address is not recommended")
|
|
break
|
|
}
|
|
}
|
|
}
|
|
|
|
// FIXME: DEPRECATED to be removed in v1.19.0
|
|
deprecatedSetting("mailer", "DISABLE_HELO", "mailer", "ENABLE_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") && !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") && !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") && !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") && !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") && !sec.HasKey("SEND_AS_PLAIN_TEXT") {
|
|
MailService.SendAsPlainText = !sec.Key("ENABLE_HTML_ALTERNATIVE").MustBool(false)
|
|
}
|
|
|
|
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.")
|
|
}
|
|
|
|
switch MailService.EnvelopeFrom {
|
|
case "":
|
|
MailService.OverrideEnvelopeFrom = false
|
|
case "<>":
|
|
MailService.EnvelopeFrom = ""
|
|
MailService.OverrideEnvelopeFrom = true
|
|
default:
|
|
parsed, err := mail.ParseAddress(MailService.EnvelopeFrom)
|
|
if err != nil {
|
|
log.Fatal("Invalid mailer.ENVELOPE_FROM (%s): %v", MailService.EnvelopeFrom, err)
|
|
}
|
|
MailService.OverrideEnvelopeFrom = true
|
|
MailService.EnvelopeFrom = parsed.Address
|
|
}
|
|
|
|
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)
|
|
}
|
|
}
|
|
|
|
log.Info("Mail Service Enabled")
|
|
}
|
|
|
|
func newRegisterMailService() {
|
|
if !Cfg.Section("service").Key("REGISTER_EMAIL_CONFIRM").MustBool() {
|
|
return
|
|
} else if MailService == nil {
|
|
log.Warn("Register Mail Service: Mail Service is not enabled")
|
|
return
|
|
}
|
|
Service.RegisterEmailConfirm = true
|
|
log.Info("Register Mail Service Enabled")
|
|
}
|
|
|
|
func newNotifyMailService() {
|
|
if !Cfg.Section("service").Key("ENABLE_NOTIFY_MAIL").MustBool() {
|
|
return
|
|
} else if MailService == nil {
|
|
log.Warn("Notify Mail Service: Mail Service is not enabled")
|
|
return
|
|
}
|
|
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
|
|
}
|