192 lines
4.9 KiB
Go
192 lines
4.9 KiB
Go
package emailsender
|
||
|
||
import (
|
||
"net/mail"
|
||
"regexp"
|
||
"strings"
|
||
"time"
|
||
|
||
"qoobing.com/gomod/email"
|
||
"qoobing.com/gomod/email/smtp"
|
||
"qoobing.com/gomod/log"
|
||
)
|
||
|
||
// ///////////////////////////////////////
|
||
// 发送器接口
|
||
// ///////////////////////////////////////
|
||
type Sender interface {
|
||
Send(to string, name, body string) error
|
||
SenderType() string
|
||
SenderSupportAddressType() string
|
||
}
|
||
|
||
type Config struct {
|
||
Id string `toml:"id"` //id
|
||
Type string `toml:"type"` //必选,发送器类型
|
||
Smtp string `toml:"smtp" ` //必选,SMTP服务器地址(含端口)
|
||
From string `toml:"from"` //必选,发件人
|
||
DryRun bool `toml:"dryrun"` //可选,只执行发送流程,但不真实发送
|
||
SmtpUser string `toml:"smtpuser"` //可选,SMTP授权人地址,默认时从from中取
|
||
SmtpPass string `toml:"smtppass"` //可选,SMTP授权人密码,默认时从from中取
|
||
SmtpProxy string `toml:"smtpproxy"` //可选,SMTP代理地址,默认时为空
|
||
WhiteList []string `toml:"whitelist"` //可选,发送白名单,默认为空
|
||
BlackList []string `toml:"blacklist"` //可选,发送黑名单,默认为空
|
||
}
|
||
|
||
func (cfg Config) GetId() string {
|
||
return cfg.Id
|
||
}
|
||
|
||
func (cfg Config) NewSender() (Sender, error) {
|
||
return New(cfg), nil
|
||
}
|
||
|
||
type EmailPlainAuthSender struct {
|
||
cfg Config
|
||
send func(to string, name, body string) error
|
||
}
|
||
|
||
func New(cfg Config) *EmailPlainAuthSender {
|
||
var (
|
||
arrFrom = strings.Split(cfg.From, "/")
|
||
fromName = arrFrom[0]
|
||
fromAddr = arrFrom[1]
|
||
fromPass = arrFrom[2]
|
||
arrSmtp = strings.Split(cfg.Smtp, ":")
|
||
smtpAddr = arrSmtp[0]
|
||
smtpPort = arrSmtp[1]
|
||
smtpUser = cfg.SmtpUser
|
||
smtpPass = cfg.SmtpPass
|
||
)
|
||
if smtpUser == "" {
|
||
smtpUser = fromAddr
|
||
}
|
||
if smtpPass == "" {
|
||
smtpPass = fromPass
|
||
}
|
||
|
||
type emailItem struct {
|
||
to string
|
||
name string
|
||
body string
|
||
}
|
||
|
||
var whiteListRegs = []*regexp.Regexp{}
|
||
for _, r := range cfg.WhiteList {
|
||
whiteListRegs = append(whiteListRegs, regexp.MustCompile(r))
|
||
}
|
||
var blackListRegs = []*regexp.Regexp{}
|
||
for _, r := range cfg.BlackList {
|
||
blackListRegs = append(blackListRegs, regexp.MustCompile(r))
|
||
}
|
||
var addressInWhiteList = func(address string) bool {
|
||
for _, reg := range blackListRegs {
|
||
if reg.Match([]byte(address)) {
|
||
return false
|
||
}
|
||
}
|
||
if len(whiteListRegs) == 0 {
|
||
return true
|
||
}
|
||
for _, reg := range whiteListRegs {
|
||
if reg.Match([]byte(address)) {
|
||
return true
|
||
}
|
||
}
|
||
return false
|
||
}
|
||
|
||
var emailQueue = make(chan emailItem, 1024)
|
||
var emailSendFunc = func(to string, name, body string) (err error) {
|
||
eml := emailItem{to, name, body}
|
||
emailQueue <- eml
|
||
return nil
|
||
}
|
||
var emailSendFuncInner = func() {
|
||
defer func() {
|
||
if re := recover(); re != nil {
|
||
log.Errorf("email inner sender exit with recover: %v", re)
|
||
}
|
||
}()
|
||
for {
|
||
eml, ok := <-emailQueue
|
||
if !ok {
|
||
log.Infof("emailQueue closed")
|
||
return
|
||
}
|
||
log.Infof("start send email to [%s]", eml.to)
|
||
if !addressInWhiteList(eml.to) {
|
||
// white-blck-list
|
||
log.Infof("send email to [%s] ignored for not in white list.", eml.to)
|
||
continue
|
||
} else if cfg.DryRun {
|
||
// dryrun mode
|
||
log.Infof("end send email to [%s]: ignored for dryrun mode.", eml.to)
|
||
continue
|
||
}
|
||
// compose the message
|
||
m := email.NewHTMLMessage(eml.name, eml.body)
|
||
m.To = []string{eml.to}
|
||
m.From = mail.Address{Name: fromName, Address: fromAddr}
|
||
// send it to smtp server
|
||
auth := smtp.PlainAuth("", smtpUser, smtpPass, smtpAddr)
|
||
addr := smtpAddr + ":" + smtpPort
|
||
if cfg.SmtpProxy != "" {
|
||
addr += "|" + cfg.SmtpProxy
|
||
}
|
||
if err := email.Send(addr, auth, m); err != nil {
|
||
log.Errorf("failed send email to [%s]", err.Error())
|
||
log.Infof("to[%v],from[%s]", m.To, m.From)
|
||
log.Infof("smtpUser[%s],smtpPass[%s],smtpAddr[%s:%s],smtpProxy[%s]",
|
||
smtpUser, secLogPass(smtpPass), smtpAddr, smtpPort, secLogProxy(cfg.SmtpProxy))
|
||
continue
|
||
}
|
||
log.Infof("success send email to [%s]", eml.to)
|
||
}
|
||
}
|
||
go func() {
|
||
for {
|
||
emailSendFuncInner()
|
||
log.Infof("email inner sender exit abnormal, restart it 5s later...")
|
||
time.Sleep(5 * time.Second)
|
||
}
|
||
}()
|
||
var sender = &EmailPlainAuthSender{
|
||
cfg: cfg,
|
||
send: emailSendFunc,
|
||
}
|
||
return sender
|
||
}
|
||
|
||
func (sender *EmailPlainAuthSender) Send(to string, name, body string) error {
|
||
return sender.send(to, name, body)
|
||
}
|
||
|
||
func (sender *EmailPlainAuthSender) SenderType() string {
|
||
return "EMAIL" // SENDER_TYPE_EMAIL
|
||
}
|
||
|
||
func (sender *EmailPlainAuthSender) SenderSupportAddressType() string {
|
||
return "EMAIL_ADDRESS" // SENDER_SUPPORT_ADDRESS_TYPE_EMAIL_ADDRESS
|
||
}
|
||
|
||
func secLogPass(password string) string {
|
||
n := len(password)
|
||
if n < 3 {
|
||
return "*****"
|
||
} else if n < 8 {
|
||
return "***" + password[n-2:]
|
||
}
|
||
return "" + password[:1] + "**" + password[n-2:]
|
||
}
|
||
|
||
func secLogProxy(proxy string) string {
|
||
if strings.HasPrefix(proxy, "socks5://") && strings.Contains(proxy, "@") {
|
||
arr := strings.Split(proxy, "@")
|
||
if len(arr) == 2 {
|
||
return arr[1]
|
||
}
|
||
}
|
||
return proxy
|
||
}
|