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:
Philip Couling 2016-12-25 13:55:22 +00:00 committed by Kim "BKC" Carlbäcker
parent 8de8ec027d
commit d4924d45d6
4 changed files with 73 additions and 10 deletions

View file

@ -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"

View file

@ -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

View file

@ -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()
} }

View file

@ -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)