Add option to convert CRLF to LF line endings for sendmail (#18075)

It appears that several versions of sendmail require that the mail is sent to them with
LF line endings instead of CRLF endings - which of course they will then convert back
to CRLF line endings to comply with the SMTP standard.

This PR adds another setting SENDMAIL_CONVERT_CRLF which will pass the message writer
through a filter. This will filter out and convert CRLFs to LFs before writing them
out to sendmail.

Fix #18024

Signed-off-by: Andrew Thornton <art27@cantab.net>
This commit is contained in:
zeripath 2022-01-06 00:43:45 +00:00 committed by GitHub
parent bf7b083cfe
commit 1514e13bb8
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
4 changed files with 20 additions and 7 deletions

View file

@ -1494,6 +1494,9 @@ PATH =
;; ;;
;; Timeout for Sendmail ;; Timeout for Sendmail
;SENDMAIL_TIMEOUT = 5m ;SENDMAIL_TIMEOUT = 5m
;;
;; convert \r\n to \n for Sendmail
;SENDMAIL_CONVERT_CRLF = true
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

View file

@ -667,6 +667,7 @@ Define allowed algorithms and their minimum key length (use -1 to disable a type
command or full path). command or full path).
- `SENDMAIL_ARGS`: **_empty_**: Specify any extra sendmail arguments. - `SENDMAIL_ARGS`: **_empty_**: Specify any extra sendmail arguments.
- `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.
- `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]`
## Cache (`cache`) ## Cache (`cache`)

View file

@ -37,9 +37,10 @@ type Mailer struct {
IsTLSEnabled bool IsTLSEnabled bool
// Sendmail sender // Sendmail sender
SendmailPath string SendmailPath string
SendmailArgs []string SendmailArgs []string
SendmailTimeout time.Duration SendmailTimeout time.Duration
SendmailConvertCRLF bool
} }
var ( var (
@ -71,8 +72,9 @@ func newMailService() {
IsTLSEnabled: sec.Key("IS_TLS_ENABLED").MustBool(), IsTLSEnabled: sec.Key("IS_TLS_ENABLED").MustBool(),
SubjectPrefix: sec.Key("SUBJECT_PREFIX").MustString(""), 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),
SendmailConvertCRLF: sec.Key("SENDMAIL_CONVERT_CRLF").MustBool(true),
} }
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("")

View file

@ -290,13 +290,20 @@ func (s *sendmailSender) Send(from string, to []string, msg io.WriterTo) error {
return err return err
} }
_, err = msg.WriteTo(pipe) if setting.MailService.SendmailConvertCRLF {
buf := &strings.Builder{}
_, err = msg.WriteTo(buf)
if err == nil {
_, err = strings.NewReplacer("\r\n", "\n").WriteString(pipe, buf.String())
}
} else {
_, err = msg.WriteTo(pipe)
}
// we MUST close the pipe or sendmail will hang waiting for more of the message // we MUST close the pipe or sendmail will hang waiting for more of the message
// Also we should wait on our sendmail command even if something fails // Also we should wait on our sendmail command even if something fails
closeError = pipe.Close() closeError = pipe.Close()
waitError = cmd.Wait() waitError = cmd.Wait()
if err != nil { if err != nil {
return err return err
} else if closeError != nil { } else if closeError != nil {