Skip to content

Commit

Permalink
Integrate new SMTP pool lib
Browse files Browse the repository at this point in the history
  • Loading branch information
knadh committed May 16, 2020
1 parent e58b2fa commit 9d3ca35
Show file tree
Hide file tree
Showing 7 changed files with 69 additions and 57 deletions.
35 changes: 26 additions & 9 deletions config.toml.sample
Original file line number Diff line number Diff line change
Expand Up @@ -91,21 +91,27 @@ max_idle = 10
username = "xxxxx"
password = ""

# Optional. Inform listmonk on which email format to use
# {html|plain|both} default: both
# Format to send e-mails in: html|plain|both.
email_format = "both"

# Optional. Some SMTP servers require a FQDN in the hostname.
# By default, HELLOs go with "localhost". Set this if a custom
# hostname should be used.
hello_hostname = ""

# Maximum time (milliseconds) to wait per e-mail push.
send_timeout = 5000

# Maximum concurrent connections to the SMTP server.
max_conns = 10

# Time to wait for new activity on a connection before closing
# it and removing it from the pool.
idle_timeout = "15s"

# Message send / wait timeout.
wait_timeout = "5s"

# The number of times a message should be retried if sending fails.
max_msg_retries = 2

[smtp.postal]
enabled = false
host = "my.smtp.server2"
Expand All @@ -116,16 +122,27 @@ max_idle = 10
username = "xxxxx"
password = ""

# Optional. Inform listmonk on which email format to use
# {html|plain|both} default: both
# Format to send e-mails in: html|plain|both.
email_format = "both"

# Maximum time (milliseconds) to wait per e-mail push.
send_timeout = 5000
# Optional. Some SMTP servers require a FQDN in the hostname.
# By default, HELLOs go with "localhost". Set this if a custom
# hostname should be used.
hello_hostname = ""

# Maximum concurrent connections to the SMTP server.
max_conns = 10

# Time to wait for new activity on a connection before closing
# it and removing it from the pool.
idle_timeout = "15s"

# Message send / wait timeout.
wait_timeout = "5s"

# The number of times a message should be retried if sending fails.
max_msg_retries = 2


[upload]
# File storage backend. "filesystem" or "s3".
Expand Down
6 changes: 2 additions & 4 deletions go.mod
Original file line number Diff line number Diff line change
@@ -1,13 +1,14 @@
module github.com/knadh/listmonk
go 1.13

require (
github.com/disintegration/imaging v1.6.2
github.com/gofrs/uuid v3.2.0+incompatible
github.com/jaytaylor/html2text v0.0.0-20200220170450-61d9dc4d7195
github.com/jmoiron/sqlx v1.2.0
github.com/jordan-wright/email v0.0.0-20200307200233-de844847de93
github.com/knadh/goyesql/v2 v2.1.1
github.com/knadh/koanf v0.8.1
github.com/knadh/smtppool v0.2.0
github.com/knadh/stuffbin v1.1.0
github.com/labstack/echo v3.3.10+incompatible
github.com/labstack/gommon v0.3.0 // indirect
Expand All @@ -22,6 +23,3 @@ require (
jaytaylor.com/html2text v0.0.0-20200220170450-61d9dc4d7195
)

replace github.com/jordan-wright/email => github.com/knadh/email v0.0.0-20200206100304-6d2c7064c2e8

go 1.13
4 changes: 4 additions & 0 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,10 @@ github.com/knadh/goyesql/v2 v2.1.1 h1:Orp5ldaxPM4ozKHfu1m7p6iolJFXDGOpF3/jyOgO6l
github.com/knadh/goyesql/v2 v2.1.1/go.mod h1:pMzCA130/ZhEIoMmSmbEFXor3A2dxl5L+JllAc/l64s=
github.com/knadh/koanf v0.8.1 h1:4VLACWqrkWRQIup3ooq6lOnaSbOJSNO+YVXnJn/NPZ8=
github.com/knadh/koanf v0.8.1/go.mod h1:kVvmDbXnBtW49Czi4c1M+nnOWF0YSNZ8BaKvE/bCO1w=
github.com/knadh/smtppool v0.1.1 h1:pSi1Gc5TXOaN/Z/YiqfZbk/vd9dqzXzAfQiss0QSGQU=
github.com/knadh/smtppool v0.1.1/go.mod h1:3DJHouXAgPDBz0kC50HukOsdapYSwIEfJGwuip46oCA=
github.com/knadh/smtppool v0.2.0 h1:+llTWRljNIVg05MMu9TiefELTNwblexjsd1ALAPXZUs=
github.com/knadh/smtppool v0.2.0/go.mod h1:3DJHouXAgPDBz0kC50HukOsdapYSwIEfJGwuip46oCA=
github.com/knadh/stuffbin v1.0.0 h1:NQon6PTpLXies4bRFhS3VpLCf6y+jn6YVXU3i2wPQ+M=
github.com/knadh/stuffbin v1.0.0/go.mod h1:yVCFaWaKPubSNibBsTAJ939q2ABHudJQxRWZWV5yh+4=
github.com/knadh/stuffbin v1.1.0 h1:f5S5BHzZALjuJEgTIOMC9NidEnBJM7Ze6Lu1GHR/lwU=
Expand Down
15 changes: 7 additions & 8 deletions init.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,11 +7,11 @@ import (
"os"
"path/filepath"
"strings"
"time"

"github.com/jmoiron/sqlx"
"github.com/knadh/goyesql/v2"
goyesqlx "github.com/knadh/goyesql/v2/sqlx"
"github.com/knadh/koanf"
"github.com/knadh/koanf/maps"
"github.com/knadh/listmonk/internal/manager"
"github.com/knadh/listmonk/internal/media"
Expand Down Expand Up @@ -214,28 +214,27 @@ func initImporter(q *Queries, db *sqlx.DB, app *App) *subimporter.Importer {
})
}

// initMessengers initializes various messaging backends.
// initMessengers initializes various messenger backends.
func initMessengers(m *manager.Manager) messenger.Messenger {
// Load SMTP configurations for the default e-mail Messenger.
var (
mapKeys = ko.MapKeys("smtp")
srv = make([]messenger.Server, 0, len(mapKeys))
)

// Load the default SMTP messengers.
for _, name := range mapKeys {
if !ko.Bool(fmt.Sprintf("smtp.%s.enabled", name)) {
lo.Printf("skipped SMTP: %s", name)
continue
}

var s messenger.Server
if err := ko.Unmarshal("smtp."+name, &s); err != nil {
// Read the SMTP config.
s := messenger.Server{Name: name}
if err := ko.UnmarshalWithConf("smtp."+name, &s, koanf.UnmarshalConf{Tag: "json"}); err != nil {
lo.Fatalf("error loading SMTP: %v", err)
}
s.Name = name
s.SendTimeout *= time.Millisecond
srv = append(srv, s)

srv = append(srv, s)
lo.Printf("loaded SMTP: %s (%s@%s)", s.Name, s.Username, s.Host)
}

Expand Down
59 changes: 27 additions & 32 deletions internal/messenger/emailer.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,10 +5,9 @@ import (
"fmt"
"math/rand"
"net/smtp"
"time"

"github.com/jaytaylor/html2text"
"github.com/jordan-wright/email"
"github.com/knadh/smtppool"
)

const emName = "email"
Expand All @@ -21,30 +20,30 @@ type loginAuth struct {

// Server represents an SMTP server's credentials.
type Server struct {
Name string
Host string `koanf:"host"`
Port int `koanf:"port"`
AuthProtocol string `koanf:"auth_protocol"`
Username string `koanf:"username"`
Password string `koanf:"password"`
EmailFormat string `koanf:"email_format"`
HelloHostname string `koanf:"hello_hostname"`
SendTimeout time.Duration `koanf:"send_timeout"`
MaxConns int `koanf:"max_conns"`

mailer *email.Pool
Name string
Username string `json:"username"`
Password string `json:"password"`
AuthProtocol string `json:"auth_protocol"`
EmailFormat string `json:"email_format"`

// 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
}

type emailer struct {
// Emailer is the SMTP e-mail messenger.
type Emailer struct {
servers map[string]*Server
serverNames []string
numServers int
}

// NewEmailer creates and returns an e-mail Messenger backend.
// It takes multiple SMTP configurations.
func NewEmailer(srv ...Server) (Messenger, error) {
e := &emailer{
func NewEmailer(srv ...Server) (*Emailer, error) {
e := &Emailer{
servers: make(map[string]*Server),
}

Expand All @@ -62,18 +61,14 @@ func NewEmailer(srv ...Server) (Messenger, error) {
default:
return nil, fmt.Errorf("unknown SMTP auth type '%s'", s.AuthProtocol)
}
s.Opt.Auth = auth

pool, err := email.NewPool(fmt.Sprintf("%s:%d", s.Host, s.Port), s.MaxConns, auth)
pool, err := smtppool.New(s.Opt)
if err != nil {
return nil, err
}

// Optional SMTP HELLO hostname.
if server.HelloHostname != "" {
pool.SetHelloHostname(server.HelloHostname)
}

s.mailer = pool
s.pool = pool
e.servers[s.Name] = &s
e.serverNames = append(e.serverNames, s.Name)
}
Expand All @@ -83,12 +78,12 @@ func NewEmailer(srv ...Server) (Messenger, error) {
}

// Name returns the Server's name.
func (e *emailer) Name() string {
func (e *Emailer) Name() string {
return emName
}

// Push pushes a message to the server.
func (e *emailer) Push(fromAddr string, toAddr []string, subject string, m []byte, atts []*Attachment) error {
func (e *Emailer) Push(fromAddr string, toAddr []string, subject string, m []byte, atts []Attachment) error {
var key string

// If there are more than one SMTP servers, send to a random
Expand All @@ -100,11 +95,11 @@ func (e *emailer) Push(fromAddr string, toAddr []string, subject string, m []byt
}

// Are there attachments?
var files []*email.Attachment
var files []smtppool.Attachment
if atts != nil {
files = make([]*email.Attachment, 0, len(atts))
files = make([]smtppool.Attachment, 0, len(atts))
for _, f := range atts {
a := &email.Attachment{
a := smtppool.Attachment{
Filename: f.Name,
Header: f.Header,
Content: make([]byte, len(f.Content)),
Expand All @@ -120,7 +115,7 @@ func (e *emailer) Push(fromAddr string, toAddr []string, subject string, m []byt
}

srv := e.servers[key]
em := &email.Email{
em := smtppool.Email{
From: fromAddr,
To: toAddr,
Subject: subject,
Expand All @@ -137,11 +132,11 @@ func (e *emailer) Push(fromAddr string, toAddr []string, subject string, m []byt
em.Text = []byte(mtext)
}

return srv.mailer.Send(em, srv.SendTimeout)
return srv.pool.Send(em)
}

// Flush flushes the message queue to the server.
func (e *emailer) Flush() error {
func (e *Emailer) Flush() error {
return nil
}

Expand Down
3 changes: 1 addition & 2 deletions internal/messenger/messenger.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,7 @@ import "net/textproto"
// for instance, e-mail, SMS etc.
type Messenger interface {
Name() string

Push(fromAddr string, toAddr []string, subject string, message []byte, atts []*Attachment) error
Push(fromAddr string, toAddr []string, subject string, message []byte, atts []Attachment) error
Flush() error
}

Expand Down
4 changes: 2 additions & 2 deletions public.go
Original file line number Diff line number Diff line change
Expand Up @@ -360,8 +360,8 @@ func handleSelfExportSubscriberData(c echo.Context) error {
[]string{data.Email},
"Your profile data",
msg.Bytes(),
[]*messenger.Attachment{
&messenger.Attachment{
[]messenger.Attachment{
{
Name: fname,
Content: b,
Header: messenger.MakeAttachmentHeader(fname, "base64"),
Expand Down

0 comments on commit 9d3ca35

Please sign in to comment.