2016-06-01 14:29:38 +02:00
|
|
|
package mail
|
|
|
|
|
|
|
|
import (
|
2016-06-06 00:37:32 +02:00
|
|
|
"encoding/base64"
|
2016-06-01 14:29:38 +02:00
|
|
|
"fmt"
|
2016-06-06 00:37:32 +02:00
|
|
|
"net/mail"
|
2016-06-01 14:29:38 +02:00
|
|
|
"net/smtp"
|
|
|
|
"strings"
|
2016-06-14 07:45:40 +02:00
|
|
|
"sync"
|
2016-06-01 14:29:38 +02:00
|
|
|
|
|
|
|
"github.com/kataras/iris/config"
|
|
|
|
"github.com/kataras/iris/utils"
|
|
|
|
)
|
|
|
|
|
|
|
|
var buf = utils.NewBufferPool(64)
|
2016-06-14 07:45:40 +02:00
|
|
|
var once sync.Once
|
2016-06-01 14:29:38 +02:00
|
|
|
|
|
|
|
type (
|
|
|
|
// Service is the interface which mail sender should implement
|
|
|
|
Service interface {
|
|
|
|
// Send sends a mail to recipients
|
|
|
|
// the body can be html also
|
2016-06-14 07:45:40 +02:00
|
|
|
Send(string, string, ...string) error
|
|
|
|
UpdateConfig(config.Mail)
|
2016-06-01 14:29:38 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
mailer struct {
|
2016-06-14 07:45:40 +02:00
|
|
|
config *config.Mail
|
2016-06-06 00:37:32 +02:00
|
|
|
fromAddr mail.Address
|
2016-06-01 14:29:38 +02:00
|
|
|
auth smtp.Auth
|
|
|
|
authenticated bool
|
|
|
|
}
|
|
|
|
)
|
|
|
|
|
|
|
|
// New creates and returns a new Service
|
|
|
|
func New(cfg config.Mail) Service {
|
2016-06-14 07:45:40 +02:00
|
|
|
m := &mailer{config: &cfg}
|
2016-06-06 12:25:09 +02:00
|
|
|
if cfg.FromAlias == "" {
|
|
|
|
if !cfg.UseCommand && cfg.Username != "" && strings.Contains(cfg.Username, "@") {
|
2016-06-14 07:45:40 +02:00
|
|
|
m.fromAddr = mail.Address{Name: cfg.Username[0:strings.IndexByte(cfg.Username, '@')], Address: cfg.Username}
|
2016-06-06 12:25:09 +02:00
|
|
|
}
|
|
|
|
} else {
|
2016-06-14 07:45:40 +02:00
|
|
|
m.fromAddr = mail.Address{Name: cfg.FromAlias, Address: cfg.Username}
|
2016-06-06 00:37:32 +02:00
|
|
|
}
|
|
|
|
return m
|
2016-06-01 14:29:38 +02:00
|
|
|
}
|
|
|
|
|
2016-06-14 07:45:40 +02:00
|
|
|
func (m *mailer) UpdateConfig(cfg config.Mail) {
|
|
|
|
m.config = &cfg
|
|
|
|
}
|
|
|
|
|
2016-06-01 14:29:38 +02:00
|
|
|
// Send sends a mail to recipients
|
|
|
|
// the body can be html also
|
2016-06-14 07:45:40 +02:00
|
|
|
func (m *mailer) Send(subject string, body string, to ...string) error {
|
2016-06-02 22:22:48 +02:00
|
|
|
if m.config.UseCommand {
|
2016-06-14 07:45:40 +02:00
|
|
|
return m.sendCmd(subject, body, to)
|
2016-06-02 22:22:48 +02:00
|
|
|
}
|
|
|
|
|
2016-06-14 07:45:40 +02:00
|
|
|
return m.sendSMTP(subject, body, to)
|
2016-06-02 22:22:48 +02:00
|
|
|
}
|
|
|
|
|
2016-06-14 07:45:40 +02:00
|
|
|
func (m *mailer) sendSMTP(subject string, body string, to []string) error {
|
2016-06-01 14:29:38 +02:00
|
|
|
buffer := buf.Get()
|
|
|
|
defer buf.Put(buffer)
|
|
|
|
|
|
|
|
if !m.authenticated {
|
2016-06-14 07:45:40 +02:00
|
|
|
cfg := m.config
|
|
|
|
if cfg.Username == "" || cfg.Password == "" || cfg.Host == "" || cfg.Port <= 0 {
|
2016-06-02 22:29:29 +02:00
|
|
|
return fmt.Errorf("Username, Password, Host & Port cannot be empty when using SMTP!")
|
2016-06-01 14:29:38 +02:00
|
|
|
}
|
2016-06-14 07:45:40 +02:00
|
|
|
m.auth = smtp.PlainAuth("", cfg.Username, cfg.Password, cfg.Host)
|
2016-06-02 22:22:48 +02:00
|
|
|
m.authenticated = true
|
2016-06-01 14:29:38 +02:00
|
|
|
}
|
|
|
|
|
2016-06-06 00:37:32 +02:00
|
|
|
fullhost := fmt.Sprintf("%s:%d", m.config.Host, m.config.Port)
|
|
|
|
|
|
|
|
header := make(map[string]string)
|
|
|
|
header["From"] = m.fromAddr.String()
|
|
|
|
header["To"] = strings.Join(to, ",")
|
|
|
|
header["Subject"] = subject
|
|
|
|
header["MIME-Version"] = "1.0"
|
|
|
|
header["Content-Type"] = "text/html; charset=\"utf-8\""
|
|
|
|
header["Content-Transfer-Encoding"] = "base64"
|
2016-06-01 14:29:38 +02:00
|
|
|
|
2016-06-06 00:37:32 +02:00
|
|
|
message := ""
|
|
|
|
for k, v := range header {
|
|
|
|
message += fmt.Sprintf("%s: %s\r\n", k, v)
|
2016-06-01 14:29:38 +02:00
|
|
|
}
|
2016-06-06 00:37:32 +02:00
|
|
|
message += "\r\n" + base64.StdEncoding.EncodeToString([]byte(body))
|
|
|
|
|
2016-06-01 14:29:38 +02:00
|
|
|
return smtp.SendMail(
|
2016-06-06 00:37:32 +02:00
|
|
|
fmt.Sprintf(fullhost),
|
2016-06-01 14:29:38 +02:00
|
|
|
m.auth,
|
|
|
|
m.config.Username,
|
|
|
|
to,
|
2016-06-06 00:37:32 +02:00
|
|
|
[]byte(message),
|
2016-06-01 14:29:38 +02:00
|
|
|
)
|
|
|
|
}
|
2016-06-02 22:22:48 +02:00
|
|
|
|
2016-06-14 07:45:40 +02:00
|
|
|
func (m *mailer) sendCmd(subject string, body string, to []string) error {
|
2016-06-02 22:22:48 +02:00
|
|
|
buffer := buf.Get()
|
|
|
|
defer buf.Put(buffer)
|
2016-06-02 22:29:29 +02:00
|
|
|
|
2016-06-14 07:45:40 +02:00
|
|
|
header := make(map[string]string)
|
|
|
|
header["To"] = strings.Join(to, ",")
|
|
|
|
header["Subject"] = subject
|
|
|
|
header["MIME-Version"] = "1.0"
|
|
|
|
header["Content-Type"] = "text/html; charset=\"utf-8\""
|
|
|
|
header["Content-Transfer-Encoding"] = "base64"
|
|
|
|
|
|
|
|
message := ""
|
|
|
|
for k, v := range header {
|
|
|
|
message += fmt.Sprintf("%s: %s\r\n", k, v)
|
|
|
|
}
|
|
|
|
message += "\r\n" + base64.StdEncoding.EncodeToString([]byte(body))
|
|
|
|
buffer.WriteString(message)
|
|
|
|
// fix by @qskousen
|
|
|
|
cmd := utils.CommandBuilder("sendmail", "-F", m.fromAddr.Name, "-f", m.fromAddr.Address, "-t")
|
2016-06-02 22:22:48 +02:00
|
|
|
|
|
|
|
cmd.Stdin = buffer
|
2016-06-14 07:45:40 +02:00
|
|
|
_, err := cmd.CombinedOutput()
|
2016-06-02 22:22:48 +02:00
|
|
|
return err
|
|
|
|
}
|