mirror of
				https://github.com/go-gitea/gitea.git
				synced 2025-11-04 09:44:21 +00:00 
			
		
		
		
	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>
This commit is contained in:
		@@ -414,9 +414,9 @@ var (
 | 
				
			|||||||
			Usage: "SMTP Authentication Type (PLAIN/LOGIN/CRAM-MD5) default PLAIN",
 | 
								Usage: "SMTP Authentication Type (PLAIN/LOGIN/CRAM-MD5) default PLAIN",
 | 
				
			||||||
		},
 | 
							},
 | 
				
			||||||
		cli.StringFlag{
 | 
							cli.StringFlag{
 | 
				
			||||||
			Name:  "host",
 | 
								Name:  "addr",
 | 
				
			||||||
			Value: "",
 | 
								Value: "",
 | 
				
			||||||
			Usage: "SMTP Host",
 | 
								Usage: "SMTP Addr",
 | 
				
			||||||
		},
 | 
							},
 | 
				
			||||||
		cli.IntFlag{
 | 
							cli.IntFlag{
 | 
				
			||||||
			Name:  "port",
 | 
								Name:  "port",
 | 
				
			||||||
@@ -956,8 +956,8 @@ func parseSMTPConfig(c *cli.Context, conf *smtp.Source) error {
 | 
				
			|||||||
		}
 | 
							}
 | 
				
			||||||
		conf.Auth = c.String("auth-type")
 | 
							conf.Auth = c.String("auth-type")
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	if c.IsSet("host") {
 | 
						if c.IsSet("addr") {
 | 
				
			||||||
		conf.Host = c.String("host")
 | 
							conf.Addr = c.String("addr")
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	if c.IsSet("port") {
 | 
						if c.IsSet("port") {
 | 
				
			||||||
		conf.Port = c.Int("port")
 | 
							conf.Port = c.Int("port")
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -1503,30 +1503,42 @@ ROUTER = console
 | 
				
			|||||||
;; Prefix displayed before subject in mail
 | 
					;; Prefix displayed before subject in mail
 | 
				
			||||||
;SUBJECT_PREFIX =
 | 
					;SUBJECT_PREFIX =
 | 
				
			||||||
;;
 | 
					;;
 | 
				
			||||||
;; Mail server
 | 
					;; Mail server protocol. One of "smtp", "smtps", "smtp+startls", "smtp+unix", "sendmail", "dummy".
 | 
				
			||||||
;; Gmail: smtp.gmail.com:587
 | 
					;; - sendmail: use the operating system's `sendmail` command instead of SMTP. This is common on Linux systems.
 | 
				
			||||||
;; QQ: smtp.qq.com:465
 | 
					;; - dummy: send email messages to the log as a testing phase.
 | 
				
			||||||
;; As per RFC 8314 using Implicit TLS/SMTPS on port 465 (if supported) is recommended,
 | 
					;; If your provider does not explicitly say which protocol it uses but does provide a port,
 | 
				
			||||||
;; otherwise STARTTLS on port 587 should be used.
 | 
					;; you can set SMTP_PORT instead and this will be inferred.
 | 
				
			||||||
;HOST =
 | 
					;; (Before 1.18, this was controlled via MAILER_TYPE and IS_TLS_ENABLED.)
 | 
				
			||||||
 | 
					;PROTOCOL =
 | 
				
			||||||
;;
 | 
					;;
 | 
				
			||||||
;; Disable HELO operation when hostnames are different.
 | 
					;; Mail server address, e.g. smtp.gmail.com.
 | 
				
			||||||
;DISABLE_HELO =
 | 
					;; 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 =
 | 
				
			||||||
;;
 | 
					;;
 | 
				
			||||||
;; Custom hostname for HELO operation, if no value is provided, one is retrieved from system.
 | 
					;; 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.
 | 
				
			||||||
 | 
					;; (Before 1.18, this was combined with SMTP_ADDR as HOST.)
 | 
				
			||||||
 | 
					;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 =
 | 
					;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.
 | 
					;; If set to `true`, completely ignores server certificate validation errors.
 | 
				
			||||||
;SKIP_VERIFY = false
 | 
					;; This option is unsafe. Consider adding the certificate to the system trust store instead.
 | 
				
			||||||
 | 
					;FORCE_TRUST_SERVER_CERT = false
 | 
				
			||||||
;;
 | 
					;;
 | 
				
			||||||
;; Use client certificate
 | 
					;; Use client certificate in connection.
 | 
				
			||||||
;USE_CERTIFICATE = false
 | 
					;USE_CLIENT_CERT = false
 | 
				
			||||||
;CERT_FILE = custom/mailer/cert.pem
 | 
					;CLIENT_CERT_FILE = custom/mailer/cert.pem
 | 
				
			||||||
;KEY_FILE = custom/mailer/key.pem
 | 
					;CLIENT_KEY_FILE = custom/mailer/key.pem
 | 
				
			||||||
;;
 | 
					 | 
				
			||||||
;; 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
 | 
					 | 
				
			||||||
;;
 | 
					;;
 | 
				
			||||||
;; Mail from address, RFC 5322. This can be just an email address, or the `"Name" <email@example.com>` format
 | 
					;; Mail from address, RFC 5322. This can be just an email address, or the `"Name" <email@example.com>` format
 | 
				
			||||||
;FROM =
 | 
					;FROM =
 | 
				
			||||||
@@ -1534,19 +1546,15 @@ ROUTER = console
 | 
				
			|||||||
;; 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.
 | 
					;; 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 =
 | 
					;ENVELOPE_FROM =
 | 
				
			||||||
;;
 | 
					;;
 | 
				
			||||||
;; Mailer user name and password
 | 
					;; Mailer user name and password, if required by provider.
 | 
				
			||||||
;; Please Note: Authentication is only supported when the SMTP server communication is encrypted with TLS (this can be via STARTTLS) or `HOST=localhost`.
 | 
					 | 
				
			||||||
;USER =
 | 
					;USER =
 | 
				
			||||||
;;
 | 
					;;
 | 
				
			||||||
;; Use PASSWD = `your password` for quoting if you use special characters in the password.
 | 
					;; Use PASSWD = `your password` for quoting if you use special characters in the password.
 | 
				
			||||||
;PASSWD =
 | 
					;PASSWD =
 | 
				
			||||||
;;
 | 
					;;
 | 
				
			||||||
;; Send mails as plain text
 | 
					;; Send mails only in plain text, without HTML alternative
 | 
				
			||||||
;SEND_AS_PLAIN_TEXT = false
 | 
					;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
 | 
					;; Specify an alternative sendmail binary
 | 
				
			||||||
;SENDMAIL_PATH = sendmail
 | 
					;SENDMAIL_PATH = sendmail
 | 
				
			||||||
;;
 | 
					;;
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -647,41 +647,35 @@ Define allowed algorithms and their minimum key length (use -1 to disable a type
 | 
				
			|||||||
## Mailer (`mailer`)
 | 
					## Mailer (`mailer`)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
- `ENABLED`: **false**: Enable to use a mail service.
 | 
					- `ENABLED`: **false**: Enable to use a mail service.
 | 
				
			||||||
- `DISABLE_HELO`: **\<empty\>**: Disable HELO operation.
 | 
					- `PROTOCOL`: **\<empty\>**: 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`._
 | 
				
			||||||
- `HELO_HOSTNAME`: **\<empty\>**: Custom hostname for HELO operation.
 | 
					  - 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.
 | 
				
			||||||
- `HOST`: **\<empty\>**: SMTP mail host address and port (example: smtp.gitea.io:587).
 | 
					  - **sendmail** Use the operating system's `sendmail` command instead of SMTP. This is common on Linux systems.
 | 
				
			||||||
  - 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.
 | 
					  - **dummy** Send email messages to the log as a testing phase.
 | 
				
			||||||
- `IS_TLS_ENABLED` :  **false** : Forcibly use TLS to connect even if not on a default SMTPS port.
 | 
					  - Note that enabling sendmail will ignore all other `mailer` settings except `ENABLED`, `FROM`, `SUBJECT_PREFIX` and `SENDMAIL_PATH`.
 | 
				
			||||||
  - Note, if the port ends with `465` Implicit TLS/SMTPS/SMTP over TLS will be used despite this setting.
 | 
					  - Enabling dummy will ignore all settings except `ENABLED`, `SUBJECT_PREFIX` and `FROM`.
 | 
				
			||||||
  - 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`.
 | 
					- `SMTP_ADDR`: **\<empty\>**: 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`._
 | 
				
			||||||
- `FROM`: **\<empty\>**: Mail from address, RFC 5322. This can be just an email address, or
 | 
					- `SMTP_PORT`: **\<empty\>**: 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`._
 | 
				
			||||||
   the "Name" \<email@example.com\> format.
 | 
					  - 25:  insecure SMTP
 | 
				
			||||||
- `ENVELOPE_FROM`: **\<empty\>**: Address set as the From address on the SMTP mail envelope. Set to `<>` to send an empty address.
 | 
					  - 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`: **\<empty\>**: Username of mailing user (usually the sender's e-mail address).
 | 
					- `USER`: **\<empty\>**: Username of mailing user (usually the sender's e-mail address).
 | 
				
			||||||
- `PASSWD`: **\<empty\>**: Password of mailing user.  Use \`your password\` for quoting if you use special characters in the password.
 | 
					- `PASSWD`: **\<empty\>**: 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.
 | 
					  - 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.
 | 
				
			||||||
- `SEND_AS_PLAIN_TEXT`: **false**: Send mails as plain text.
 | 
					- `ENABLE_HELO`: **true**: Enable HELO operation.
 | 
				
			||||||
- `SKIP_VERIFY`: **false**: Whether or not to skip verification of certificates; `true` to disable verification.
 | 
					- `HELO_HOSTNAME`: **(retrieved from system)**: HELO hostname.
 | 
				
			||||||
  - **Warning:** This option is unsafe. Consider adding the certificate to the system trust store instead.
 | 
					- `FROM`: **\<empty\>**: Mail from address, RFC 5322. This can be just an email address, or the "Name" \<email@example.com\> format.
 | 
				
			||||||
  - **Note:** Gitea only supports SMTP with STARTTLS.
 | 
					- `ENVELOPE_FROM`: **\<empty\>**: Address set as the From address on the SMTP mail envelope. Set to `<>` to send an empty address.
 | 
				
			||||||
- `USE_CERTIFICATE`: **false**: Use client certificate.
 | 
					 | 
				
			||||||
- `CERT_FILE`: **custom/mailer/cert.pem**
 | 
					 | 
				
			||||||
- `KEY_FILE`: **custom/mailer/key.pem**
 | 
					 | 
				
			||||||
- `SUBJECT_PREFIX`: **\<empty\>**: Prefix to be placed before e-mail subject lines.
 | 
					- `SUBJECT_PREFIX`: **\<empty\>**: Prefix to be placed before e-mail subject lines.
 | 
				
			||||||
- `MAILER_TYPE`: **smtp**: \[smtp, sendmail, dummy\]
 | 
					- `SENDMAIL_PATH`: **sendmail**: The location of sendmail on the operating system (can be command or full path).
 | 
				
			||||||
  - **smtp** Use SMTP to send mail
 | 
					- `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** 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_TIMEOUT`: **5m**: default timeout for sending email through sendmail
 | 
					- `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.
 | 
					- `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_BUFFER_LEN`: **100**: Buffer length of mailing queue. **DEPRECATED** use `LENGTH` in `[queue.mailer]`
 | 
				
			||||||
 | 
					- `SEND_AS_PLAIN_TEXT`: **false**: Send mails only in plain text, without HTML alternative.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
## Cache (`cache`)
 | 
					## Cache (`cache`)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -5,7 +5,9 @@
 | 
				
			|||||||
package setting
 | 
					package setting
 | 
				
			||||||
 | 
					
 | 
				
			||||||
import (
 | 
					import (
 | 
				
			||||||
 | 
						"net"
 | 
				
			||||||
	"net/mail"
 | 
						"net/mail"
 | 
				
			||||||
 | 
						"strings"
 | 
				
			||||||
	"time"
 | 
						"time"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	"code.gitea.io/gitea/modules/log"
 | 
						"code.gitea.io/gitea/modules/log"
 | 
				
			||||||
@@ -23,18 +25,19 @@ type Mailer struct {
 | 
				
			|||||||
	FromName             string
 | 
						FromName             string
 | 
				
			||||||
	FromEmail            string
 | 
						FromEmail            string
 | 
				
			||||||
	SendAsPlainText      bool
 | 
						SendAsPlainText      bool
 | 
				
			||||||
	MailerType           string
 | 
					 | 
				
			||||||
	SubjectPrefix        string
 | 
						SubjectPrefix        string
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	// SMTP sender
 | 
						// SMTP sender
 | 
				
			||||||
	Host              string
 | 
						Protocol             string
 | 
				
			||||||
	User, Passwd      string
 | 
						SMTPAddr             string
 | 
				
			||||||
	DisableHelo       bool
 | 
						SMTPPort             string
 | 
				
			||||||
	HeloHostname      string
 | 
						User, Passwd         string
 | 
				
			||||||
	SkipVerify        bool
 | 
						EnableHelo           bool
 | 
				
			||||||
	UseCertificate    bool
 | 
						HeloHostname         string
 | 
				
			||||||
	CertFile, KeyFile string
 | 
						ForceTrustServerCert bool
 | 
				
			||||||
	IsTLSEnabled      bool
 | 
						UseClientCert        bool
 | 
				
			||||||
 | 
						ClientCertFile       string
 | 
				
			||||||
 | 
						ClientKeyFile        string
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	// Sendmail sender
 | 
						// Sendmail sender
 | 
				
			||||||
	SendmailPath        string
 | 
						SendmailPath        string
 | 
				
			||||||
@@ -56,19 +59,19 @@ func newMailService() {
 | 
				
			|||||||
	MailService = &Mailer{
 | 
						MailService = &Mailer{
 | 
				
			||||||
		Name:            sec.Key("NAME").MustString(AppName),
 | 
							Name:            sec.Key("NAME").MustString(AppName),
 | 
				
			||||||
		SendAsPlainText: sec.Key("SEND_AS_PLAIN_TEXT").MustBool(false),
 | 
							SendAsPlainText: sec.Key("SEND_AS_PLAIN_TEXT").MustBool(false),
 | 
				
			||||||
		MailerType:      sec.Key("MAILER_TYPE").In("", []string{"smtp", "sendmail", "dummy"}),
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
		Host:           sec.Key("HOST").String(),
 | 
							Protocol:             sec.Key("PROTOCOL").In("", []string{"smtp", "smtps", "smtp+startls", "smtp+unix", "sendmail", "dummy"}),
 | 
				
			||||||
		User:           sec.Key("USER").String(),
 | 
							SMTPAddr:             sec.Key("SMTP_ADDR").String(),
 | 
				
			||||||
		Passwd:         sec.Key("PASSWD").String(),
 | 
							SMTPPort:             sec.Key("SMTP_PORT").String(),
 | 
				
			||||||
		DisableHelo:    sec.Key("DISABLE_HELO").MustBool(),
 | 
							User:                 sec.Key("USER").String(),
 | 
				
			||||||
		HeloHostname:   sec.Key("HELO_HOSTNAME").String(),
 | 
							Passwd:               sec.Key("PASSWD").String(),
 | 
				
			||||||
		SkipVerify:     sec.Key("SKIP_VERIFY").MustBool(),
 | 
							EnableHelo:           sec.Key("ENABLE_HELO").MustBool(true),
 | 
				
			||||||
		UseCertificate: sec.Key("USE_CERTIFICATE").MustBool(),
 | 
							HeloHostname:         sec.Key("HELO_HOSTNAME").String(),
 | 
				
			||||||
		CertFile:       sec.Key("CERT_FILE").String(),
 | 
							ForceTrustServerCert: sec.Key("FORCE_TRUST_SERVER_CERT").MustBool(false),
 | 
				
			||||||
		KeyFile:        sec.Key("KEY_FILE").String(),
 | 
							UseClientCert:        sec.Key("USE_CLIENT_CERT").MustBool(false),
 | 
				
			||||||
		IsTLSEnabled:   sec.Key("IS_TLS_ENABLED").MustBool(),
 | 
							ClientCertFile:       sec.Key("CLIENT_CERT_FILE").String(),
 | 
				
			||||||
		SubjectPrefix:  sec.Key("SUBJECT_PREFIX").MustString(""),
 | 
							ClientKeyFile:        sec.Key("CLIENT_KEY_FILE").String(),
 | 
				
			||||||
 | 
							SubjectPrefix:        sec.Key("SUBJECT_PREFIX").MustString(""),
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		SendmailPath:        sec.Key("SENDMAIL_PATH").MustString("sendmail"),
 | 
							SendmailPath:        sec.Key("SENDMAIL_PATH").MustString("sendmail"),
 | 
				
			||||||
		SendmailTimeout:     sec.Key("SENDMAIL_TIMEOUT").MustDuration(5 * time.Minute),
 | 
							SendmailTimeout:     sec.Key("SENDMAIL_TIMEOUT").MustDuration(5 * time.Minute),
 | 
				
			||||||
@@ -77,26 +80,123 @@ func newMailService() {
 | 
				
			|||||||
	MailService.From = sec.Key("FROM").MustString(MailService.User)
 | 
						MailService.From = sec.Key("FROM").MustString(MailService.User)
 | 
				
			||||||
	MailService.EnvelopeFrom = sec.Key("ENVELOPE_FROM").MustString("")
 | 
						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", "ENABLE_HTML_ALTERNATIVE", "mailer", "SEND_AS_PLAIN_TEXT")
 | 
						deprecatedSetting("mailer", "MAILER_TYPE", "mailer", "PROTOCOL")
 | 
				
			||||||
	if sec.HasKey("ENABLE_HTML_ALTERNATIVE") {
 | 
						if sec.HasKey("MAILER_TYPE") && !sec.HasKey("PROTOCOL") {
 | 
				
			||||||
		MailService.SendAsPlainText = !sec.Key("ENABLE_HTML_ALTERNATIVE").MustBool(false)
 | 
							if sec.Key("MAILER_TYPE").String() == "sendmail" {
 | 
				
			||||||
	}
 | 
								MailService.Protocol = "sendmail"
 | 
				
			||||||
 | 
					 | 
				
			||||||
	// FIXME: DEPRECATED to be removed in v1.18.0
 | 
					 | 
				
			||||||
	deprecatedSetting("mailer", "USE_SENDMAIL", "mailer", "MAILER_TYPE")
 | 
					 | 
				
			||||||
	if sec.HasKey("USE_SENDMAIL") {
 | 
					 | 
				
			||||||
		if MailService.MailerType == "" && sec.Key("USE_SENDMAIL").MustBool(false) {
 | 
					 | 
				
			||||||
			MailService.MailerType = "sendmail"
 | 
					 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	parsed, err := mail.ParseAddress(MailService.From)
 | 
						// FIXME: DEPRECATED to be removed in v1.19.0
 | 
				
			||||||
	if err != nil {
 | 
						deprecatedSetting("mailer", "HOST", "mailer", "SMTP_ADDR")
 | 
				
			||||||
		log.Fatal("Invalid mailer.FROM (%s): %v", MailService.From, err)
 | 
						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.")
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	MailService.FromName = parsed.Name
 | 
					 | 
				
			||||||
	MailService.FromEmail = parsed.Address
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
	switch MailService.EnvelopeFrom {
 | 
						switch MailService.EnvelopeFrom {
 | 
				
			||||||
	case "":
 | 
						case "":
 | 
				
			||||||
@@ -105,7 +205,7 @@ func newMailService() {
 | 
				
			|||||||
		MailService.EnvelopeFrom = ""
 | 
							MailService.EnvelopeFrom = ""
 | 
				
			||||||
		MailService.OverrideEnvelopeFrom = true
 | 
							MailService.OverrideEnvelopeFrom = true
 | 
				
			||||||
	default:
 | 
						default:
 | 
				
			||||||
		parsed, err = mail.ParseAddress(MailService.EnvelopeFrom)
 | 
							parsed, err := mail.ParseAddress(MailService.EnvelopeFrom)
 | 
				
			||||||
		if err != nil {
 | 
							if err != nil {
 | 
				
			||||||
			log.Fatal("Invalid mailer.ENVELOPE_FROM (%s): %v", MailService.EnvelopeFrom, err)
 | 
								log.Fatal("Invalid mailer.ENVELOPE_FROM (%s): %v", MailService.EnvelopeFrom, err)
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
@@ -113,11 +213,8 @@ func newMailService() {
 | 
				
			|||||||
		MailService.EnvelopeFrom = parsed.Address
 | 
							MailService.EnvelopeFrom = parsed.Address
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	if MailService.MailerType == "" {
 | 
						if MailService.Protocol == "sendmail" {
 | 
				
			||||||
		MailService.MailerType = "smtp"
 | 
							var err error
 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	if MailService.MailerType == "sendmail" {
 | 
					 | 
				
			||||||
		MailService.SendmailArgs, err = shellquote.Split(sec.Key("SENDMAIL_ARGS").String())
 | 
							MailService.SendmailArgs, err = shellquote.Split(sec.Key("SENDMAIL_ARGS").String())
 | 
				
			||||||
		if err != nil {
 | 
							if err != nil {
 | 
				
			||||||
			log.Error("Failed to parse Sendmail args: %s with error %v", CustomConf, err)
 | 
								log.Error("Failed to parse Sendmail args: %s with error %v", CustomConf, err)
 | 
				
			||||||
@@ -148,3 +245,21 @@ func newNotifyMailService() {
 | 
				
			|||||||
	Service.EnableNotifyMail = true
 | 
						Service.EnableNotifyMail = true
 | 
				
			||||||
	log.Info("Notify Mail Service Enabled")
 | 
						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
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -179,7 +179,8 @@ log_root_path_helper = Log files will be written to this directory.
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
optional_title = Optional Settings
 | 
					optional_title = Optional Settings
 | 
				
			||||||
email_title = Email Settings
 | 
					email_title = Email Settings
 | 
				
			||||||
smtp_host = SMTP Host
 | 
					smtp_addr = SMTP Host
 | 
				
			||||||
 | 
					smtp_port = SMTP Port
 | 
				
			||||||
smtp_from = Send Email As
 | 
					smtp_from = Send Email As
 | 
				
			||||||
smtp_from_helper = Email address Gitea will use. Enter a plain email address or use the "Name" <email@example.com> format.
 | 
					smtp_from_helper = Email address Gitea will use. Enter a plain email address or use the "Name" <email@example.com> format.
 | 
				
			||||||
mailer_user = SMTP Username
 | 
					mailer_user = SMTP Username
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -133,7 +133,8 @@ func Install(ctx *context.Context) {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
	// E-mail service settings
 | 
						// E-mail service settings
 | 
				
			||||||
	if setting.MailService != nil {
 | 
						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.SMTPFrom = setting.MailService.From
 | 
				
			||||||
		form.SMTPUser = setting.MailService.User
 | 
							form.SMTPUser = setting.MailService.User
 | 
				
			||||||
		form.SMTPPasswd = setting.MailService.Passwd
 | 
							form.SMTPPasswd = setting.MailService.Passwd
 | 
				
			||||||
@@ -421,9 +422,10 @@ func SubmitInstall(ctx *context.Context) {
 | 
				
			|||||||
		cfg.Section("server").Key("LFS_START_SERVER").SetValue("false")
 | 
							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("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("FROM").SetValue(form.SMTPFrom)
 | 
				
			||||||
		cfg.Section("mailer").Key("USER").SetValue(form.SMTPUser)
 | 
							cfg.Section("mailer").Key("USER").SetValue(form.SMTPUser)
 | 
				
			||||||
		cfg.Section("mailer").Key("PASSWD").SetValue(form.SMTPPasswd)
 | 
							cfg.Section("mailer").Key("PASSWD").SetValue(form.SMTPPasswd)
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -159,7 +159,7 @@ func parseLDAPConfig(form forms.AuthenticationForm) *ldap.Source {
 | 
				
			|||||||
func parseSMTPConfig(form forms.AuthenticationForm) *smtp.Source {
 | 
					func parseSMTPConfig(form forms.AuthenticationForm) *smtp.Source {
 | 
				
			||||||
	return &smtp.Source{
 | 
						return &smtp.Source{
 | 
				
			||||||
		Auth:           form.SMTPAuth,
 | 
							Auth:           form.SMTPAuth,
 | 
				
			||||||
		Host:           form.SMTPHost,
 | 
							Addr:           form.SMTPAddr,
 | 
				
			||||||
		Port:           form.SMTPPort,
 | 
							Port:           form.SMTPPort,
 | 
				
			||||||
		AllowedDomains: form.AllowedDomains,
 | 
							AllowedDomains: form.AllowedDomains,
 | 
				
			||||||
		ForceSMTPS:     form.ForceSMTPS,
 | 
							ForceSMTPS:     form.ForceSMTPS,
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -58,10 +58,10 @@ var ErrUnsupportedLoginType = errors.New("Login source is unknown")
 | 
				
			|||||||
func Authenticate(a smtp.Auth, source *Source) error {
 | 
					func Authenticate(a smtp.Auth, source *Source) error {
 | 
				
			||||||
	tlsConfig := &tls.Config{
 | 
						tlsConfig := &tls.Config{
 | 
				
			||||||
		InsecureSkipVerify: source.SkipVerify,
 | 
							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 {
 | 
						if err != nil {
 | 
				
			||||||
		return err
 | 
							return err
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
@@ -71,7 +71,7 @@ func Authenticate(a smtp.Auth, source *Source) error {
 | 
				
			|||||||
		conn = tls.Client(conn, tlsConfig)
 | 
							conn = tls.Client(conn, tlsConfig)
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	client, err := smtp.NewClient(conn, source.Host)
 | 
						client, err := smtp.NewClient(conn, source.Addr)
 | 
				
			||||||
	if err != nil {
 | 
						if err != nil {
 | 
				
			||||||
		return fmt.Errorf("failed to create NewClient: %w", err)
 | 
							return fmt.Errorf("failed to create NewClient: %w", err)
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -19,7 +19,7 @@ import (
 | 
				
			|||||||
// Source holds configuration for the SMTP login source.
 | 
					// Source holds configuration for the SMTP login source.
 | 
				
			||||||
type Source struct {
 | 
					type Source struct {
 | 
				
			||||||
	Auth           string
 | 
						Auth           string
 | 
				
			||||||
	Host           string
 | 
						Addr           string
 | 
				
			||||||
	Port           int
 | 
						Port           int
 | 
				
			||||||
	AllowedDomains string `xorm:"TEXT"`
 | 
						AllowedDomains string `xorm:"TEXT"`
 | 
				
			||||||
	ForceSMTPS     bool
 | 
						ForceSMTPS     bool
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -32,7 +32,7 @@ func (source *Source) Authenticate(user *user_model.User, userName, password str
 | 
				
			|||||||
	var auth smtp.Auth
 | 
						var auth smtp.Auth
 | 
				
			||||||
	switch source.Auth {
 | 
						switch source.Auth {
 | 
				
			||||||
	case PlainAuthentication:
 | 
						case PlainAuthentication:
 | 
				
			||||||
		auth = smtp.PlainAuth("", userName, password, source.Host)
 | 
							auth = smtp.PlainAuth("", userName, password, source.Addr)
 | 
				
			||||||
	case LoginAuthentication:
 | 
						case LoginAuthentication:
 | 
				
			||||||
		auth = &loginAuthenticator{userName, password}
 | 
							auth = &loginAuthenticator{userName, password}
 | 
				
			||||||
	case CRAMMD5Authentication:
 | 
						case CRAMMD5Authentication:
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -45,7 +45,7 @@ type AuthenticationForm struct {
 | 
				
			|||||||
	IsActive                      bool
 | 
						IsActive                      bool
 | 
				
			||||||
	IsSyncEnabled                 bool
 | 
						IsSyncEnabled                 bool
 | 
				
			||||||
	SMTPAuth                      string
 | 
						SMTPAuth                      string
 | 
				
			||||||
	SMTPHost                      string
 | 
						SMTPAddr                      string
 | 
				
			||||||
	SMTPPort                      int
 | 
						SMTPPort                      int
 | 
				
			||||||
	AllowedDomains                string
 | 
						AllowedDomains                string
 | 
				
			||||||
	SecurityProtocol              int `binding:"Range(0,2)"`
 | 
						SecurityProtocol              int `binding:"Range(0,2)"`
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -40,7 +40,8 @@ type InstallForm struct {
 | 
				
			|||||||
	AppURL       string `binding:"Required"`
 | 
						AppURL       string `binding:"Required"`
 | 
				
			||||||
	LogRootPath  string `binding:"Required"`
 | 
						LogRootPath  string `binding:"Required"`
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	SMTPHost        string
 | 
						SMTPAddr        string
 | 
				
			||||||
 | 
						SMTPPort        string
 | 
				
			||||||
	SMTPFrom        string
 | 
						SMTPFrom        string
 | 
				
			||||||
	SMTPUser        string `binding:"OmitEmpty;MaxSize(254)" locale:"install.mailer_user"`
 | 
						SMTPUser        string `binding:"OmitEmpty;MaxSize(254)" locale:"install.mailer_user"`
 | 
				
			||||||
	SMTPPasswd      string
 | 
						SMTPPasswd      string
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -147,65 +147,82 @@ type smtpSender struct{}
 | 
				
			|||||||
func (s *smtpSender) Send(from string, to []string, msg io.WriterTo) error {
 | 
					func (s *smtpSender) Send(from string, to []string, msg io.WriterTo) error {
 | 
				
			||||||
	opts := setting.MailService
 | 
						opts := setting.MailService
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	host, port, err := net.SplitHostPort(opts.Host)
 | 
						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)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						conn, err := net.Dial(network, address)
 | 
				
			||||||
	if err != nil {
 | 
						if err != nil {
 | 
				
			||||||
		return err
 | 
							return fmt.Errorf("failed to establish network connection to SMTP server: %v", err)
 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	tlsconfig := &tls.Config{
 | 
					 | 
				
			||||||
		InsecureSkipVerify: opts.SkipVerify,
 | 
					 | 
				
			||||||
		ServerName:         host,
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	if opts.UseCertificate {
 | 
					 | 
				
			||||||
		cert, err := tls.LoadX509KeyPair(opts.CertFile, opts.KeyFile)
 | 
					 | 
				
			||||||
		if err != nil {
 | 
					 | 
				
			||||||
			return err
 | 
					 | 
				
			||||||
		}
 | 
					 | 
				
			||||||
		tlsconfig.Certificates = []tls.Certificate{cert}
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	conn, err := net.Dial("tcp", net.JoinHostPort(host, port))
 | 
					 | 
				
			||||||
	if err != nil {
 | 
					 | 
				
			||||||
		return err
 | 
					 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	defer conn.Close()
 | 
						defer conn.Close()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	isSecureConn := opts.IsTLSEnabled || (strings.HasSuffix(port, "465"))
 | 
						var tlsconfig *tls.Config
 | 
				
			||||||
	// Start TLS directly if the port ends with 465 (SMTPS protocol)
 | 
						if opts.Protocol == "smtps" || opts.Protocol == "smtp+startls" {
 | 
				
			||||||
	if isSecureConn {
 | 
							tlsconfig = &tls.Config{
 | 
				
			||||||
 | 
								InsecureSkipVerify: opts.ForceTrustServerCert,
 | 
				
			||||||
 | 
								ServerName:         opts.SMTPAddr,
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							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}
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if opts.Protocol == "smtps" {
 | 
				
			||||||
		conn = tls.Client(conn, tlsconfig)
 | 
							conn = tls.Client(conn, tlsconfig)
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						host := "localhost"
 | 
				
			||||||
 | 
						if opts.Protocol == "smtp+unix" {
 | 
				
			||||||
 | 
							host = opts.SMTPAddr
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
	client, err := smtp.NewClient(conn, host)
 | 
						client, err := smtp.NewClient(conn, host)
 | 
				
			||||||
	if err != nil {
 | 
						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
 | 
							hostname := opts.HeloHostname
 | 
				
			||||||
		if len(hostname) == 0 {
 | 
							if len(hostname) == 0 {
 | 
				
			||||||
			hostname, err = os.Hostname()
 | 
								hostname, err = os.Hostname()
 | 
				
			||||||
			if err != nil {
 | 
								if err != nil {
 | 
				
			||||||
				return err
 | 
									return fmt.Errorf("could not retrieve system hostname: %v", err)
 | 
				
			||||||
			}
 | 
								}
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		if err = client.Hello(hostname); err != nil {
 | 
							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
 | 
						if opts.Protocol == "smtp+startls" {
 | 
				
			||||||
	hasStartTLS, _ := client.Extension("STARTTLS")
 | 
							hasStartTLS, _ := client.Extension("STARTTLS")
 | 
				
			||||||
	if !isSecureConn && hasStartTLS {
 | 
							if hasStartTLS {
 | 
				
			||||||
		if err = client.StartTLS(tlsconfig); err != nil {
 | 
								if err = client.StartTLS(tlsconfig); err != nil {
 | 
				
			||||||
			return fmt.Errorf("StartTLS: %v", err)
 | 
									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")
 | 
						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
 | 
							var auth smtp.Auth
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		if strings.Contains(options, "CRAM-MD5") {
 | 
							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 auth != nil {
 | 
				
			||||||
			if err = client.Auth(auth); err != 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 opts.OverrideEnvelopeFrom {
 | 
				
			||||||
		if err = client.Mail(opts.EnvelopeFrom); err != nil {
 | 
							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 {
 | 
						} else {
 | 
				
			||||||
		if err = client.Mail(from); err != nil {
 | 
							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 {
 | 
						for _, rec := range to {
 | 
				
			||||||
		if err = client.Rcpt(rec); err != nil {
 | 
							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()
 | 
						w, err := client.Data()
 | 
				
			||||||
	if err != nil {
 | 
						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 {
 | 
						} 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 {
 | 
						} else if err = w.Close(); err != nil {
 | 
				
			||||||
		return fmt.Errorf("Close: %v", err)
 | 
							return fmt.Errorf("SMTP close failed: %v", err)
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	return client.Quit()
 | 
						return client.Quit()
 | 
				
			||||||
@@ -338,13 +355,13 @@ func NewContext() {
 | 
				
			|||||||
		return
 | 
							return
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	switch setting.MailService.MailerType {
 | 
						switch setting.MailService.Protocol {
 | 
				
			||||||
	case "smtp":
 | 
					 | 
				
			||||||
		Sender = &smtpSender{}
 | 
					 | 
				
			||||||
	case "sendmail":
 | 
						case "sendmail":
 | 
				
			||||||
		Sender = &sendmailSender{}
 | 
							Sender = &sendmailSender{}
 | 
				
			||||||
	case "dummy":
 | 
						case "dummy":
 | 
				
			||||||
		Sender = &dummySender{}
 | 
							Sender = &dummySender{}
 | 
				
			||||||
 | 
						default:
 | 
				
			||||||
 | 
							Sender = &smtpSender{}
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	mailQueue = queue.CreateQueue("mail", func(data ...queue.Data) []queue.Data {
 | 
						mailQueue = queue.CreateQueue("mail", func(data ...queue.Data) []queue.Data {
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -173,8 +173,12 @@
 | 
				
			|||||||
							{{.locale.Tr "install.email_title"}}
 | 
												{{.locale.Tr "install.email_title"}}
 | 
				
			||||||
						</summary>
 | 
											</summary>
 | 
				
			||||||
						<div class="inline field">
 | 
											<div class="inline field">
 | 
				
			||||||
							<label for="smtp_host">{{.locale.Tr "install.smtp_host"}}</label>
 | 
												<label for="smtp_addr">{{.locale.Tr "install.smtp_addr"}}</label>
 | 
				
			||||||
							<input id="smtp_host" name="smtp_host" value="{{.smtp_host}}">
 | 
												<input id="smtp_addr" name="smtp_addr" value="{{.smtp_addr}}">
 | 
				
			||||||
 | 
											</div>
 | 
				
			||||||
 | 
											<div class="inline field">
 | 
				
			||||||
 | 
												<label for="smtp_port">{{.locale.Tr "install.smtp_port"}}</label>
 | 
				
			||||||
 | 
												<input id="smtp_port" name="smtp_port" value="{{.smtp_port}}">
 | 
				
			||||||
						</div>
 | 
											</div>
 | 
				
			||||||
						<div class="inline field {{if .Err_SMTPFrom}}error{{end}}">
 | 
											<div class="inline field {{if .Err_SMTPFrom}}error{{end}}">
 | 
				
			||||||
							<label for="smtp_from">{{.locale.Tr "install.smtp_from"}}</label>
 | 
												<label for="smtp_from">{{.locale.Tr "install.smtp_from"}}</label>
 | 
				
			||||||
 
 | 
				
			|||||||
		Reference in New Issue
	
	Block a user