diff --git a/conf/app.ini b/conf/app.ini index 18ad9c3d215d2..1309e73e8dfa0 100644 --- a/conf/app.ini +++ b/conf/app.ini @@ -225,6 +225,10 @@ USER = PASSWD = ; Use text/html as alternative format of content ENABLE_HTML_ALTERNATIVE = false +; Enable sendmail (override SMTP) +USE_SENDMAIL = false +; Specifiy an alternative sendmail binary +SENDMAIL_PATH = sendmail [cache] ; Either "memory", "redis", or "memcache", default is "memory" diff --git a/models/mail.go b/models/mail.go index 8f54d6651fe6a..e66e316ba0e78 100644 --- a/models/mail.go +++ b/models/mail.go @@ -40,7 +40,7 @@ func InitMailRender(tmpls *template.Template) { // SendTestMail sends a test mail 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 diff --git a/modules/mailer/mailer.go b/modules/mailer/mailer.go index 3dc2c4e53168f..3201075f3fa75 100644 --- a/modules/mailer/mailer.go +++ b/modules/mailer/mailer.go @@ -11,6 +11,7 @@ import ( "net" "net/smtp" "os" + "os/exec" "strings" "time" @@ -87,12 +88,12 @@ func (a *loginAuth) Next(fromServer []byte, more bool) ([]byte, error) { return nil, nil } -// Sender mail sender -type Sender struct { +// Sender SMTP mail sender +type smtpSender struct { } // 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 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() } -func processMailQueue() { - sender := &Sender{} +// Sender sendmail mail 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 { select { case msg := <-mailQueue: 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) } else { log.Trace("E-mails sent %s: %s", msg.GetHeader("To"), msg.Info) @@ -213,6 +251,9 @@ func processMailQueue() { var mailQueue chan *Message +// Sender sender for sending mail synchronously +var Sender gomail.Sender + // NewContext start mail queue service func NewContext() { // Need to check if mailQueue is nil because in during reinstall (user had installed @@ -222,6 +263,13 @@ func NewContext() { return } + + if setting.MailService.UseSendmail { + Sender = &sendmailSender{} + } else { + Sender = &smtpSender{} + } + mailQueue = make(chan *Message, setting.MailService.QueueLength) go processMailQueue() } diff --git a/modules/setting/setting.go b/modules/setting/setting.go index d78b22c484897..5ac2b66d6a098 100644 --- a/modules/setting/setting.go +++ b/modules/setting/setting.go @@ -731,18 +731,25 @@ func newSessionService() { // Mailer represents mail service. type Mailer struct { + // Mailer QueueLength int Name string - Host string From string FromEmail string + EnableHTMLAlternative bool + + // SMTP sender + Host string User, Passwd string DisableHelo bool HeloHostname string SkipVerify bool UseCertificate bool CertFile, KeyFile string - EnableHTMLAlternative bool + + // Sendmail sender + UseSendmail bool + SendmailPath string } var ( @@ -760,6 +767,8 @@ func newMailService() { MailService = &Mailer{ QueueLength: sec.Key("SEND_BUFFER_LEN").MustInt(100), Name: sec.Key("NAME").MustString(AppName), + EnableHTMLAlternative: sec.Key("ENABLE_HTML_ALTERNATIVE").MustBool(), + Host: sec.Key("HOST").String(), User: sec.Key("USER").String(), Passwd: sec.Key("PASSWD").String(), @@ -769,7 +778,9 @@ func newMailService() { UseCertificate: sec.Key("USE_CERTIFICATE").MustBool(), CertFile: sec.Key("CERT_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)