Implement sendmail (#355)
* Implemented sendmail. This piggybacks on existing configuration to keep the change simple * Changed privicy of new sendSMTP and sendSendmail functions * Fixed Lint errors * Seperated SMTP and sendmail into their own senders * Making new structs private as they should not be used externally now * Added sendmail setting to ini file * Minor code cleanup
This commit is contained in:
parent
8de8ec027d
commit
d4924d45d6
4 changed files with 73 additions and 10 deletions
|
@ -226,6 +226,10 @@ USER =
|
||||||
PASSWD =
|
PASSWD =
|
||||||
; Use text/html as alternative format of content
|
; Use text/html as alternative format of content
|
||||||
ENABLE_HTML_ALTERNATIVE = false
|
ENABLE_HTML_ALTERNATIVE = false
|
||||||
|
; Enable sendmail (override SMTP)
|
||||||
|
USE_SENDMAIL = false
|
||||||
|
; Specifiy an alternative sendmail binary
|
||||||
|
SENDMAIL_PATH = sendmail
|
||||||
|
|
||||||
[cache]
|
[cache]
|
||||||
; Either "memory", "redis", or "memcache", default is "memory"
|
; Either "memory", "redis", or "memcache", default is "memory"
|
||||||
|
|
|
@ -40,7 +40,7 @@ func InitMailRender(tmpls *template.Template) {
|
||||||
|
|
||||||
// SendTestMail sends a test mail
|
// SendTestMail sends a test mail
|
||||||
func SendTestMail(email string) error {
|
func SendTestMail(email string) error {
|
||||||
return gomail.Send(&mailer.Sender{}, mailer.NewMessage([]string{email}, "Gitea Test Email!", "Gitea Test Email!").Message)
|
return gomail.Send(mailer.Sender, mailer.NewMessage([]string{email}, "Gitea Test Email!", "Gitea Test Email!").Message)
|
||||||
}
|
}
|
||||||
|
|
||||||
// SendUserMail sends a mail to the user
|
// SendUserMail sends a mail to the user
|
||||||
|
|
|
@ -11,6 +11,7 @@ import (
|
||||||
"net"
|
"net"
|
||||||
"net/smtp"
|
"net/smtp"
|
||||||
"os"
|
"os"
|
||||||
|
"os/exec"
|
||||||
"strings"
|
"strings"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
|
@ -87,12 +88,12 @@ func (a *loginAuth) Next(fromServer []byte, more bool) ([]byte, error) {
|
||||||
return nil, nil
|
return nil, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// Sender mail sender
|
// Sender SMTP mail sender
|
||||||
type Sender struct {
|
type smtpSender struct {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Send send email
|
// Send send email
|
||||||
func (s *Sender) 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)
|
host, port, err := net.SplitHostPort(opts.Host)
|
||||||
|
@ -195,14 +196,51 @@ func (s *Sender) Send(from string, to []string, msg io.WriterTo) error {
|
||||||
return client.Quit()
|
return client.Quit()
|
||||||
}
|
}
|
||||||
|
|
||||||
func processMailQueue() {
|
// Sender sendmail mail sender
|
||||||
sender := &Sender{}
|
type sendmailSender struct {
|
||||||
|
}
|
||||||
|
|
||||||
|
// Send send email
|
||||||
|
func (s *sendmailSender) Send(from string, to []string, msg io.WriterTo) error {
|
||||||
|
var err error
|
||||||
|
var closeError error
|
||||||
|
var waitError error
|
||||||
|
|
||||||
|
args := []string{"-F", from, "-i"}
|
||||||
|
args = append(args, to...)
|
||||||
|
log.Trace("Sending with: %s %v", setting.MailService.SendmailPath, args)
|
||||||
|
cmd := exec.Command(setting.MailService.SendmailPath, args...)
|
||||||
|
pipe, err := cmd.StdinPipe()
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
if err = cmd.Start(); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
_,err = msg.WriteTo(pipe)
|
||||||
|
|
||||||
|
// 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
|
||||||
|
closeError = pipe.Close()
|
||||||
|
waitError = cmd.Wait()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
} else if closeError != nil {
|
||||||
|
return closeError
|
||||||
|
} else {
|
||||||
|
return waitError
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func processMailQueue() {
|
||||||
for {
|
for {
|
||||||
select {
|
select {
|
||||||
case msg := <-mailQueue:
|
case msg := <-mailQueue:
|
||||||
log.Trace("New e-mail sending request %s: %s", msg.GetHeader("To"), msg.Info)
|
log.Trace("New e-mail sending request %s: %s", msg.GetHeader("To"), msg.Info)
|
||||||
if err := gomail.Send(sender, msg.Message); err != nil {
|
if err := gomail.Send(Sender, msg.Message); err != nil {
|
||||||
log.Error(3, "Fail to send emails %s: %s - %v", msg.GetHeader("To"), msg.Info, err)
|
log.Error(3, "Fail to send emails %s: %s - %v", msg.GetHeader("To"), msg.Info, err)
|
||||||
} else {
|
} else {
|
||||||
log.Trace("E-mails sent %s: %s", msg.GetHeader("To"), msg.Info)
|
log.Trace("E-mails sent %s: %s", msg.GetHeader("To"), msg.Info)
|
||||||
|
@ -213,6 +251,9 @@ func processMailQueue() {
|
||||||
|
|
||||||
var mailQueue chan *Message
|
var mailQueue chan *Message
|
||||||
|
|
||||||
|
// Sender sender for sending mail synchronously
|
||||||
|
var Sender gomail.Sender
|
||||||
|
|
||||||
// NewContext start mail queue service
|
// NewContext start mail queue service
|
||||||
func NewContext() {
|
func NewContext() {
|
||||||
// Need to check if mailQueue is nil because in during reinstall (user had installed
|
// Need to check if mailQueue is nil because in during reinstall (user had installed
|
||||||
|
@ -222,6 +263,13 @@ func NewContext() {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
if setting.MailService.UseSendmail {
|
||||||
|
Sender = &sendmailSender{}
|
||||||
|
} else {
|
||||||
|
Sender = &smtpSender{}
|
||||||
|
}
|
||||||
|
|
||||||
mailQueue = make(chan *Message, setting.MailService.QueueLength)
|
mailQueue = make(chan *Message, setting.MailService.QueueLength)
|
||||||
go processMailQueue()
|
go processMailQueue()
|
||||||
}
|
}
|
||||||
|
|
|
@ -858,18 +858,25 @@ func newSessionService() {
|
||||||
|
|
||||||
// Mailer represents mail service.
|
// Mailer represents mail service.
|
||||||
type Mailer struct {
|
type Mailer struct {
|
||||||
|
// Mailer
|
||||||
QueueLength int
|
QueueLength int
|
||||||
Name string
|
Name string
|
||||||
Host string
|
|
||||||
From string
|
From string
|
||||||
FromEmail string
|
FromEmail string
|
||||||
|
EnableHTMLAlternative bool
|
||||||
|
|
||||||
|
// SMTP sender
|
||||||
|
Host string
|
||||||
User, Passwd string
|
User, Passwd string
|
||||||
DisableHelo bool
|
DisableHelo bool
|
||||||
HeloHostname string
|
HeloHostname string
|
||||||
SkipVerify bool
|
SkipVerify bool
|
||||||
UseCertificate bool
|
UseCertificate bool
|
||||||
CertFile, KeyFile string
|
CertFile, KeyFile string
|
||||||
EnableHTMLAlternative bool
|
|
||||||
|
// Sendmail sender
|
||||||
|
UseSendmail bool
|
||||||
|
SendmailPath string
|
||||||
}
|
}
|
||||||
|
|
||||||
var (
|
var (
|
||||||
|
@ -887,6 +894,8 @@ func newMailService() {
|
||||||
MailService = &Mailer{
|
MailService = &Mailer{
|
||||||
QueueLength: sec.Key("SEND_BUFFER_LEN").MustInt(100),
|
QueueLength: sec.Key("SEND_BUFFER_LEN").MustInt(100),
|
||||||
Name: sec.Key("NAME").MustString(AppName),
|
Name: sec.Key("NAME").MustString(AppName),
|
||||||
|
EnableHTMLAlternative: sec.Key("ENABLE_HTML_ALTERNATIVE").MustBool(),
|
||||||
|
|
||||||
Host: sec.Key("HOST").String(),
|
Host: sec.Key("HOST").String(),
|
||||||
User: sec.Key("USER").String(),
|
User: sec.Key("USER").String(),
|
||||||
Passwd: sec.Key("PASSWD").String(),
|
Passwd: sec.Key("PASSWD").String(),
|
||||||
|
@ -896,7 +905,9 @@ func newMailService() {
|
||||||
UseCertificate: sec.Key("USE_CERTIFICATE").MustBool(),
|
UseCertificate: sec.Key("USE_CERTIFICATE").MustBool(),
|
||||||
CertFile: sec.Key("CERT_FILE").String(),
|
CertFile: sec.Key("CERT_FILE").String(),
|
||||||
KeyFile: sec.Key("KEY_FILE").String(),
|
KeyFile: sec.Key("KEY_FILE").String(),
|
||||||
EnableHTMLAlternative: sec.Key("ENABLE_HTML_ALTERNATIVE").MustBool(),
|
|
||||||
|
UseSendmail: sec.Key("USE_SENDMAIL").MustBool(),
|
||||||
|
SendmailPath: sec.Key("SENDMAIL_PATH").MustString("sendmail"),
|
||||||
}
|
}
|
||||||
MailService.From = sec.Key("FROM").MustString(MailService.User)
|
MailService.From = sec.Key("FROM").MustString(MailService.User)
|
||||||
|
|
||||||
|
|
Reference in a new issue