listmonk/internal/messenger/emailer.go

162 lines
3.5 KiB
Go
Raw Normal View History

2018-10-25 21:51:47 +08:00
package messenger
import (
2020-05-17 23:37:48 +08:00
"crypto/tls"
2018-10-25 21:51:47 +08:00
"fmt"
"math/rand"
"net/smtp"
"net/textproto"
2018-10-25 21:51:47 +08:00
"github.com/jaytaylor/html2text"
2020-05-17 01:11:30 +08:00
"github.com/knadh/smtppool"
2018-10-25 21:51:47 +08:00
)
const emName = "email"
// Server represents an SMTP server's credentials.
type Server struct {
Username string `json:"username"`
Password string `json:"password"`
AuthProtocol string `json:"auth_protocol"`
EmailFormat string `json:"email_format"`
TLSEnabled bool `json:"tls_enabled"`
TLSSkipVerify bool `json:"tls_skip_verify"`
EmailHeaders map[string]string `json:"email_headers"`
2020-05-17 01:11:30 +08:00
// Rest of the options are embedded directly from the smtppool lib.
// The JSON tag is for config unmarshal to work.
smtppool.Opt `json:",squash"`
pool *smtppool.Pool
2018-10-25 21:51:47 +08:00
}
2020-05-17 01:11:30 +08:00
// Emailer is the SMTP e-mail messenger.
type Emailer struct {
servers []*Server
2018-10-25 21:51:47 +08:00
}
// NewEmailer creates and returns an e-mail Messenger backend.
// It takes multiple SMTP configurations.
2020-05-17 23:37:48 +08:00
func NewEmailer(servers ...Server) (*Emailer, error) {
2020-05-17 01:11:30 +08:00
e := &Emailer{
servers: make([]*Server, 0, len(servers)),
2018-10-25 21:51:47 +08:00
}
2020-05-17 23:37:48 +08:00
for _, srv := range servers {
s := srv
2018-10-25 21:51:47 +08:00
var auth smtp.Auth
2020-04-01 22:26:40 +08:00
switch s.AuthProtocol {
case "cram":
2018-10-25 21:51:47 +08:00
auth = smtp.CRAMMD5Auth(s.Username, s.Password)
2020-04-01 22:26:40 +08:00
case "plain":
2018-10-25 21:51:47 +08:00
auth = smtp.PlainAuth("", s.Username, s.Password, s.Host)
2020-04-01 22:26:40 +08:00
case "login":
auth = &smtppool.LoginAuth{Username: s.Username, Password: s.Password}
2020-08-01 19:15:29 +08:00
case "", "none":
2020-04-01 22:26:40 +08:00
default:
2020-05-11 23:30:06 +08:00
return nil, fmt.Errorf("unknown SMTP auth type '%s'", s.AuthProtocol)
2018-10-25 21:51:47 +08:00
}
2020-05-17 01:11:30 +08:00
s.Opt.Auth = auth
2018-10-25 21:51:47 +08:00
2020-05-17 23:37:48 +08:00
// TLS config.
if s.TLSEnabled {
s.TLSConfig = &tls.Config{}
if s.TLSSkipVerify {
s.TLSConfig.InsecureSkipVerify = s.TLSSkipVerify
} else {
s.TLSConfig.ServerName = s.Host
}
}
2020-05-17 01:11:30 +08:00
pool, err := smtppool.New(s.Opt)
2018-10-25 21:51:47 +08:00
if err != nil {
return nil, err
}
2020-05-17 01:11:30 +08:00
s.pool = pool
e.servers = append(e.servers, &s)
2018-10-25 21:51:47 +08:00
}
return e, nil
}
// Name returns the Server's name.
2020-05-17 01:11:30 +08:00
func (e *Emailer) Name() string {
2018-10-25 21:51:47 +08:00
return emName
}
// Push pushes a message to the server.
2020-05-17 01:11:30 +08:00
func (e *Emailer) Push(fromAddr string, toAddr []string, subject string, m []byte, atts []Attachment) error {
2018-10-25 21:51:47 +08:00
// If there are more than one SMTP servers, send to a random
// one from the list.
var (
ln = len(e.servers)
srv *Server
)
if ln > 1 {
srv = e.servers[rand.Intn(ln)]
2018-10-25 21:51:47 +08:00
} else {
srv = e.servers[0]
2018-10-25 21:51:47 +08:00
}
// Are there attachments?
2020-05-17 01:11:30 +08:00
var files []smtppool.Attachment
if atts != nil {
2020-05-17 01:11:30 +08:00
files = make([]smtppool.Attachment, 0, len(atts))
for _, f := range atts {
2020-05-17 01:11:30 +08:00
a := smtppool.Attachment{
Filename: f.Name,
Header: f.Header,
Content: make([]byte, len(f.Content)),
}
copy(a.Content, f.Content)
files = append(files, a)
}
}
mtext, err := html2text.FromString(string(m), html2text.Options{PrettyTables: true})
if err != nil {
return err
}
2020-05-17 01:11:30 +08:00
em := smtppool.Email{
From: fromAddr,
To: toAddr,
Subject: subject,
Attachments: files,
}
// If there are custom e-mail headers, attach them.
if len(srv.EmailHeaders) > 0 {
em.Headers = textproto.MIMEHeader{}
for k, v := range srv.EmailHeaders {
em.Headers.Set(k, v)
}
}
switch srv.EmailFormat {
case "html":
em.HTML = m
case "plain":
em.Text = []byte(mtext)
default:
em.HTML = m
em.Text = []byte(mtext)
}
2018-10-25 21:51:47 +08:00
2020-05-17 01:11:30 +08:00
return srv.pool.Send(em)
2018-10-25 21:51:47 +08:00
}
// Flush flushes the message queue to the server.
2020-05-17 01:11:30 +08:00
func (e *Emailer) Flush() error {
2018-10-25 21:51:47 +08:00
return nil
}
// Close closes the SMTP pools.
func (e *Emailer) Close() error {
for _, s := range e.servers {
s.pool.Close()
}
return nil
}