Skip to content

Commit

Permalink
Implement sendmail (go-gitea#355)
Browse files Browse the repository at this point in the history
* 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
  • Loading branch information
couling authored and bkcsoft committed Dec 25, 2016
1 parent 8de8ec0 commit d4924d4
Show file tree
Hide file tree
Showing 4 changed files with 73 additions and 10 deletions.
4 changes: 4 additions & 0 deletions conf/app.ini
Original file line number Diff line number Diff line change
Expand Up @@ -226,6 +226,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"
Expand Down
2 changes: 1 addition & 1 deletion models/mail.go
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
60 changes: 54 additions & 6 deletions modules/mailer/mailer.go
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ import (
"net"
"net/smtp"
"os"
"os/exec"
"strings"
"time"

Expand Down Expand Up @@ -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)
Expand Down Expand Up @@ -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)
Expand All @@ -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
Expand All @@ -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()
}
Expand Down
17 changes: 14 additions & 3 deletions modules/setting/setting.go
Original file line number Diff line number Diff line change
Expand Up @@ -858,18 +858,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 (
Expand All @@ -887,6 +894,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(),
Expand All @@ -896,7 +905,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)

Expand Down

0 comments on commit d4924d4

Please sign in to comment.