mirror of
https://github.com/knadh/listmonk.git
synced 2025-01-10 16:28:36 +08:00
68afd61024
This commit removes the Go html2text lib that would automatically convert all HTML messages to plaintext and add them as the alt text body to outgoing e-mails. This lib also had memory leak issues with certain kinds of HTML templates. A new UI field for optionally adding an alt plaintext body to a campaign is added. On enabling, it converts the HTML message in the campaign editor into plaintext (using the textversionjs lib). This introduces breaking changes in the campaigns table schema, model, and template compilation.
159 lines
3.3 KiB
Go
159 lines
3.3 KiB
Go
package email
|
|
|
|
import (
|
|
"crypto/tls"
|
|
"fmt"
|
|
"math/rand"
|
|
"net/smtp"
|
|
"net/textproto"
|
|
|
|
"github.com/knadh/listmonk/internal/messenger"
|
|
"github.com/knadh/smtppool"
|
|
)
|
|
|
|
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"`
|
|
TLSEnabled bool `json:"tls_enabled"`
|
|
TLSSkipVerify bool `json:"tls_skip_verify"`
|
|
EmailHeaders map[string]string `json:"email_headers"`
|
|
|
|
// 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
|
|
}
|
|
|
|
// Emailer is the SMTP e-mail messenger.
|
|
type Emailer struct {
|
|
servers []*Server
|
|
}
|
|
|
|
// New returns an SMTP e-mail Messenger backend with a the given SMTP servers.
|
|
func New(servers ...Server) (*Emailer, error) {
|
|
e := &Emailer{
|
|
servers: make([]*Server, 0, len(servers)),
|
|
}
|
|
|
|
for _, srv := range servers {
|
|
s := srv
|
|
var auth smtp.Auth
|
|
switch s.AuthProtocol {
|
|
case "cram":
|
|
auth = smtp.CRAMMD5Auth(s.Username, s.Password)
|
|
case "plain":
|
|
auth = smtp.PlainAuth("", s.Username, s.Password, s.Host)
|
|
case "login":
|
|
auth = &smtppool.LoginAuth{Username: s.Username, Password: s.Password}
|
|
case "", "none":
|
|
default:
|
|
return nil, fmt.Errorf("unknown SMTP auth type '%s'", s.AuthProtocol)
|
|
}
|
|
s.Opt.Auth = auth
|
|
|
|
// TLS config.
|
|
if s.TLSEnabled {
|
|
s.TLSConfig = &tls.Config{}
|
|
if s.TLSSkipVerify {
|
|
s.TLSConfig.InsecureSkipVerify = s.TLSSkipVerify
|
|
} else {
|
|
s.TLSConfig.ServerName = s.Host
|
|
}
|
|
}
|
|
|
|
pool, err := smtppool.New(s.Opt)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
s.pool = pool
|
|
e.servers = append(e.servers, &s)
|
|
}
|
|
|
|
return e, nil
|
|
}
|
|
|
|
// Name returns the Server's name.
|
|
func (e *Emailer) Name() string {
|
|
return emName
|
|
}
|
|
|
|
// Push pushes a message to the server.
|
|
func (e *Emailer) Push(m messenger.Message) error {
|
|
// 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)]
|
|
} else {
|
|
srv = e.servers[0]
|
|
}
|
|
|
|
// Are there attachments?
|
|
var files []smtppool.Attachment
|
|
if m.Attachments != nil {
|
|
files = make([]smtppool.Attachment, 0, len(m.Attachments))
|
|
for _, f := range m.Attachments {
|
|
a := smtppool.Attachment{
|
|
Filename: f.Name,
|
|
Header: f.Header,
|
|
Content: make([]byte, len(f.Content)),
|
|
}
|
|
copy(a.Content, f.Content)
|
|
files = append(files, a)
|
|
}
|
|
}
|
|
|
|
em := smtppool.Email{
|
|
From: m.From,
|
|
To: m.To,
|
|
Subject: m.Subject,
|
|
Attachments: files,
|
|
}
|
|
|
|
em.Headers = textproto.MIMEHeader{}
|
|
// Attach e-mail level headers.
|
|
if len(m.Headers) > 0 {
|
|
em.Headers = m.Headers
|
|
}
|
|
|
|
// Attach SMTP level headers.
|
|
if len(srv.EmailHeaders) > 0 {
|
|
for k, v := range srv.EmailHeaders {
|
|
em.Headers.Set(k, v)
|
|
}
|
|
}
|
|
|
|
switch m.ContentType {
|
|
case "plain":
|
|
em.Text = []byte(m.Body)
|
|
default:
|
|
em.HTML = m.Body
|
|
if len(m.AltBody) > 0 {
|
|
em.Text = m.AltBody
|
|
}
|
|
}
|
|
|
|
return srv.pool.Send(em)
|
|
}
|
|
|
|
// Flush flushes the message queue to the server.
|
|
func (e *Emailer) Flush() error {
|
|
return nil
|
|
}
|
|
|
|
// Close closes the SMTP pools.
|
|
func (e *Emailer) Close() error {
|
|
for _, s := range e.servers {
|
|
s.pool.Close()
|
|
}
|
|
return nil
|
|
}
|