7378414e11
Use "plain" auth if and only if username and password are provided. Parse port from "host" field if it's provided.
118 lines
2.8 KiB
Go
118 lines
2.8 KiB
Go
package email
|
|
|
|
import (
|
|
"encoding/json"
|
|
"errors"
|
|
"fmt"
|
|
"net"
|
|
"strconv"
|
|
|
|
"gopkg.in/gomail.v2"
|
|
)
|
|
|
|
const (
|
|
SmtpEmailerType = "smtp"
|
|
)
|
|
|
|
func init() {
|
|
RegisterEmailerConfigType(SmtpEmailerType, func() EmailerConfig { return &SmtpEmailerConfig{} })
|
|
}
|
|
|
|
type SmtpEmailerConfig struct {
|
|
Host string `json:"host"`
|
|
Username string `json:"username"`
|
|
Password string `json:"password"`
|
|
FromAddr string `json:"from"`
|
|
|
|
// OPTIONAL: If empty and host is of form "host:port" just use that. For backward
|
|
// compatibility do not change this.
|
|
Port int `json:"port"`
|
|
|
|
// DEPRICATED: If "username" and "password" are provided, use them.
|
|
Auth string `json:"auth"`
|
|
}
|
|
|
|
func (cfg SmtpEmailerConfig) EmailerType() string {
|
|
return SmtpEmailerType
|
|
}
|
|
|
|
func (cfg SmtpEmailerConfig) EmailerID() string {
|
|
return SmtpEmailerType
|
|
}
|
|
|
|
func (cfg SmtpEmailerConfig) Emailer(fromAddr string) (Emailer, error) {
|
|
from := cfg.FromAddr
|
|
if from == "" {
|
|
from = fromAddr
|
|
}
|
|
if from == "" {
|
|
return nil, errors.New(`missing "from" field in email config`)
|
|
}
|
|
|
|
host, port := cfg.Host, cfg.Port
|
|
|
|
// If port hasn't been supplied, check the "host" field.
|
|
if port == 0 {
|
|
hostStr, portStr, err := net.SplitHostPort(cfg.Host)
|
|
if err != nil {
|
|
return nil, fmt.Errorf(`"host" must be in format of "host:port" %v`, err)
|
|
}
|
|
host = hostStr
|
|
if port, err = strconv.Atoi(portStr); err != nil {
|
|
return nil, fmt.Errorf(`failed to parse %q as "host:port" %v`, cfg.Host, err)
|
|
}
|
|
}
|
|
|
|
if (cfg.Username == "") != (cfg.Password == "") {
|
|
return nil, errors.New(`must provide both "username" and "password"`)
|
|
}
|
|
|
|
var dialer *gomail.Dialer
|
|
if cfg.Username == "" {
|
|
// NOTE(ericchiang): Guess SSL using the same logic as gomail. We should
|
|
// eventually allow this to be set explicitly.
|
|
dialer = &gomail.Dialer{Host: host, Port: port, SSL: port == 465}
|
|
} else {
|
|
dialer = gomail.NewPlainDialer(host, port, cfg.Username, cfg.Password)
|
|
}
|
|
|
|
return &smtpEmailer{dialer: dialer, from: from}, nil
|
|
}
|
|
|
|
type smtpEmailerConfig SmtpEmailerConfig
|
|
|
|
func (cfg *SmtpEmailerConfig) UnmarshalJSON(data []byte) error {
|
|
smtpCfg := smtpEmailerConfig{}
|
|
err := json.Unmarshal(data, &smtpCfg)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
if smtpCfg.Host == "" {
|
|
return errors.New("must set SMTP host")
|
|
}
|
|
if smtpCfg.Port == 0 {
|
|
return errors.New("must set SMTP port")
|
|
}
|
|
*cfg = SmtpEmailerConfig(smtpCfg)
|
|
return nil
|
|
}
|
|
|
|
type smtpEmailer struct {
|
|
dialer *gomail.Dialer
|
|
from string
|
|
}
|
|
|
|
func (emailer *smtpEmailer) SendMail(subject, text, html string, to ...string) error {
|
|
msg := gomail.NewMessage()
|
|
msg.SetHeader("From", emailer.from)
|
|
msg.SetHeader("To", to...)
|
|
msg.SetHeader("Subject", subject)
|
|
msg.SetBody("text/plain", text)
|
|
msg.SetBody("text/html", html)
|
|
err := emailer.dialer.DialAndSend(msg)
|
|
if err != nil {
|
|
counterEmailSendErr.Add(1)
|
|
return err
|
|
}
|
|
return nil
|
|
}
|