Added support for SMTPS

Gomail now automatically uses SMTPS on port 465
This commit is contained in:
alexcesaro 2014-10-22 17:55:36 +02:00
parent a0730d2c47
commit 062b8e4ef3
4 changed files with 101 additions and 14 deletions

View File

@ -15,6 +15,7 @@ It requires Go 1.2 or newer.
* Supports HTML and text templates * Supports HTML and text templates
* Attachments * Attachments
* Embedded images * Embedded images
* SSL/TLS support
* Automatic encoding of special characters * Automatic encoding of special characters
* Well-documented * Well-documented
* High test coverage * High test coverage

View File

@ -67,7 +67,7 @@ func NewMailer(host string, username string, password string, port int, settings
// gomail.NewCustomMailer("host:587", smtp.CRAMMD5Auth("username", "secret")) // gomail.NewCustomMailer("host:587", smtp.CRAMMD5Auth("username", "secret"))
func NewCustomMailer(addr string, auth smtp.Auth, settings ...MailerSetting) *Mailer { func NewCustomMailer(addr string, auth smtp.Auth, settings ...MailerSetting) *Mailer {
// Error is not handled here to preserve backward compatibility // Error is not handled here to preserve backward compatibility
host, _, _ := net.SplitHostPort(addr) host, port, _ := net.SplitHostPort(addr)
m := &Mailer{ m := &Mailer{
addr: addr, addr: addr,
@ -83,7 +83,7 @@ func NewCustomMailer(addr string, auth smtp.Auth, settings ...MailerSetting) *Ma
m.config = &tls.Config{ServerName: host} m.config = &tls.Config{ServerName: host}
} }
if m.send == nil { if m.send == nil {
m.send = m.getSendMailFunc() m.send = m.getSendMailFunc(port == "465")
} }
return m return m

44
send.go
View File

@ -3,18 +3,22 @@ package gomail
import ( import (
"crypto/tls" "crypto/tls"
"io" "io"
"net"
"net/smtp" "net/smtp"
) )
func (m *Mailer) getSendMailFunc() SendMailFunc { func (m *Mailer) getSendMailFunc(ssl bool) SendMailFunc {
return func(addr string, a smtp.Auth, from string, to []string, msg []byte) error { return func(addr string, a smtp.Auth, from string, to []string, msg []byte) error {
c, err := initSMTP(addr) var c smtpClient
var err error
if ssl {
c, err = sslDial(addr, m.host, m.config)
} else {
c, err = starttlsDial(addr, m.config)
}
if err != nil { if err != nil {
return err return err
} }
if ok, _ := c.Extension("STARTTLS"); ok {
return c.StartTLS(m.config)
}
defer c.Close() defer c.Close()
if a != nil { if a != nil {
@ -52,10 +56,40 @@ func (m *Mailer) getSendMailFunc() SendMailFunc {
} }
} }
func sslDial(addr, host string, config *tls.Config) (smtpClient, error) {
conn, err := initTLS("tcp", addr, config)
if err != nil {
return nil, err
}
return newClient(conn, host)
}
func starttlsDial(addr string, config *tls.Config) (smtpClient, error) {
c, err := initSMTP(addr)
if err != nil {
return c, err
}
if ok, _ := c.Extension("STARTTLS"); ok {
return c, c.StartTLS(config)
}
return c, nil
}
var initSMTP = func(addr string) (smtpClient, error) { var initSMTP = func(addr string) (smtpClient, error) {
return smtp.Dial(addr) return smtp.Dial(addr)
} }
var initTLS = func(network, addr string, config *tls.Config) (*tls.Conn, error) {
return tls.Dial(network, addr, config)
}
var newClient = func(conn net.Conn, host string) (smtpClient, error) {
return smtp.NewClient(conn, host)
}
type smtpClient interface { type smtpClient interface {
Extension(string) (bool, string) Extension(string) (bool, string)
StartTLS(*tls.Config) error StartTLS(*tls.Config) error

View File

@ -3,18 +3,21 @@ package gomail
import ( import (
"crypto/tls" "crypto/tls"
"io" "io"
"net"
"net/smtp" "net/smtp"
"testing" "testing"
) )
var ( var (
testAddr = "smtp.example.com:587" testAddr = "smtp.example.com:587"
testConfig = &tls.Config{InsecureSkipVerify: true} testSSLAddr = "smtp.example.com:465"
testHost = "smtp.example.com" testTLSConn = &tls.Conn{}
testAuth = smtp.PlainAuth("", "user", "pwd", "smtp.example.com") testConfig = &tls.Config{InsecureSkipVerify: true}
testFrom = "from@example.com" testHost = "smtp.example.com"
testTo = []string{"to1@example.com", "to2@example.com"} testAuth = smtp.PlainAuth("", "user", "pwd", "smtp.example.com")
testBody = "Test message" testFrom = "from@example.com"
testTo = []string{"to1@example.com", "to2@example.com"}
testBody = "Test message"
) )
const wantMsg = "To: to1@example.com, to2@example.com\r\n" + const wantMsg = "To: to1@example.com, to2@example.com\r\n" +
@ -43,6 +46,21 @@ func TestDefaultSendMail(t *testing.T) {
}) })
} }
func TestSSLSendMail(t *testing.T) {
testSendMail(t, testSSLAddr, nil, []string{
"Extension AUTH",
"Auth",
"Mail " + testFrom,
"Rcpt " + testTo[0],
"Rcpt " + testTo[1],
"Data",
"Write message",
"Close writer",
"Quit",
"Close",
})
}
func TestTLSConfigSendMail(t *testing.T) { func TestTLSConfigSendMail(t *testing.T) {
testSendMail(t, testAddr, testConfig, []string{ testSendMail(t, testAddr, testConfig, []string{
"Extension STARTTLS", "Extension STARTTLS",
@ -60,6 +78,21 @@ func TestTLSConfigSendMail(t *testing.T) {
}) })
} }
func TestTLSConfigSSLSendMail(t *testing.T) {
testSendMail(t, testSSLAddr, testConfig, []string{
"Extension AUTH",
"Auth",
"Mail " + testFrom,
"Rcpt " + testTo[0],
"Rcpt " + testTo[1],
"Data",
"Write message",
"Close writer",
"Quit",
"Close",
})
}
type mockClient struct { type mockClient struct {
t *testing.T t *testing.T
i int i int
@ -152,6 +185,25 @@ func testSendMail(t *testing.T, addr string, config *tls.Config, want []string)
return testClient, nil return testClient, nil
} }
initTLS = func(network, addr string, config *tls.Config) (*tls.Conn, error) {
if network != "tcp" {
t.Errorf("Invalid network, got %q, want tcp", network)
}
assertAddr(t, addr, testClient.addr)
assertConfig(t, config, testClient.config)
return testTLSConn, nil
}
newClient = func(conn net.Conn, host string) (smtpClient, error) {
if conn != testTLSConn {
t.Error("Invalid TLS connection used")
}
if host != testHost {
t.Errorf("Invalid host, got %q, want %q", host, testHost)
}
return testClient, nil
}
msg := NewMessage() msg := NewMessage()
msg.SetHeader("From", testFrom) msg.SetHeader("From", testFrom)
msg.SetHeader("To", testTo...) msg.SetHeader("To", testTo...)