Skip to content

Commit

Permalink
Better management sending and creating SMTPClient
Browse files Browse the repository at this point in the history
-NewSMTPServer() change to NewSMTPClient() and return new SMTPClient struct
-New() change to NewMSG()
-Public function Send() only receive SMTPClient struct
-Private function send() receive SMTPClient struct instead SendTimeout, Client and KeepAlive
-Double verification in send, first that SMTPClient struct is not nil and Client is not nil
-Public function Connect() return SMTPClient struct
-Private function checkKeepAlive() only receive SMTPClient struct
-Updated readme

Note: this update is for v2.1.0 and is not compatible with v2.0.0
  • Loading branch information
xhit committed Sep 21, 2019
1 parent 51e7e0b commit 2f094d0
Show file tree
Hide file tree
Showing 2 changed files with 88 additions and 85 deletions.
22 changes: 15 additions & 7 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,9 @@ The best way to send emails in Go with SMTP Keep Alive and Timeout for Connect a

Inspired in joegrasse package github.com/joegrasse/mail Thanks

**IMPORTANT**
This example is for version 2.1.0 and above, for v2.0.0 example go here https://gist.github.com/xhit/54516917473420a8db1b6fff68a21c99

**Download**

```bash
Expand Down Expand Up @@ -33,7 +36,7 @@ func main() {
</body>
</html>`

server := mail.NewSMTPServer()
server := mail.NewSMTPClient()

//SMTP Server
server.Host = "smtp.example.com"
Expand All @@ -52,14 +55,14 @@ func main() {
server.SendTimeout = 10

//SMTP client
smtpServer,err :=server.Connect()
smtpClient,err :=server.Connect()

if err != nil{
log.Fatal(err)
}

//New email simple html with inline and CC
email := mail.New()
email := mail.NewMSG()

email.SetFrom("From Example <[email protected]>").
AddTo("[email protected]").
Expand All @@ -70,7 +73,8 @@ func main() {

email.AddInline("/path/to/image.png", "Gopher.png")

err = email.Send(smtpServer,server)
//Call Send and pass the client
err = email.Send(smtpClient)

if err != nil {
log.Println(err)
Expand All @@ -80,10 +84,10 @@ func main() {


//Other email with same connection and attachments
email = mail.New()
email = mail.NewMSG()

email.SetFrom("HELLO <[email protected]>").
AddTo("[email protected]").
AddTo("[email protected]").
SetSubject("dfgdfgdf")

email.SetBody("text/plain", "Hello Gophers!")
Expand All @@ -92,7 +96,11 @@ func main() {
email.AddAttachment("path/to/file","filename test")
email.AddAttachment("path/to/file2")

err = email.Send(smtpServer,server)
// also you can attach a base64 instead a file path
email.AddAttachmentBase64("SGVsbG8gZ29waGVycyE=", "hello.txt")

//Call Send and pass the client
err = email.Send(smtpClient)

if err != nil {
log.Println(err)
Expand Down
151 changes: 73 additions & 78 deletions email.go
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,13 @@ type SMTPServer struct {
KeepAlive bool
}

//SMTPClient represents a SMTP Client for send email
type SMTPClient struct {
Client *Client
KeepAlive bool
SendTimeout int
}

// part represents the different content parts of an email body.
type part struct {
contentType string
Expand Down Expand Up @@ -92,8 +99,8 @@ func (encoding encoding) String() string {
return encodingTypes[encoding]
}

// New creates a new email. It uses UTF-8 by default.
func New() *Email {
// NewMSG creates a new email. It uses UTF-8 by default.
func NewMSG() *Email {
email := &Email{
headers: make(textproto.MIMEHeader),
Charset: "UTF-8",
Expand All @@ -105,8 +112,8 @@ func New() *Email {
return email
}

//NewSMTPServer returns the client for send email
func NewSMTPServer() *SMTPServer {
//NewSMTPClient returns the client for send email
func NewSMTPClient() *SMTPServer {
server := &SMTPServer{}
return server
}
Expand Down Expand Up @@ -542,14 +549,10 @@ func (email *Email) attach(f string, inline bool, name ...string) error {
return nil
}

// attachB64 does the low level attaching of the files
// attachB64 does the low level attaching of the files but decoding base64 instad have a filepath
func (email *Email) attachB64(b64File string, name string) error {
// Get the file data
// data, err := ioutil.ReadFile(f)
// if err != nil {
// return errors.New("Mail Error: Failed to add file with following error: " + err.Error())
// }

// decode the string
dec, err := base64.StdEncoding.DecodeString(b64File)
if err != nil {
return errors.New("Mail Error: Failed to decode base64 attachment with following error: " + err.Error())
Expand All @@ -561,27 +564,11 @@ func (email *Email) attachB64(b64File string, name string) error {
mimeType = "application/octet-stream"
}

// get the filename
// _, filename := filepath.Split(f)

// if an alternative filename was provided, use that instead
// if len(name) == 1 {
// filename = name[0]
// }

// if inline {
// email.inlines = append(email.inlines, &file{
// filename: filename,
// mimeType: mimeType,
// data: data,
// })
// } else {
email.attachments = append(email.attachments, &file{
filename: name,
mimeType: mimeType,
data: dec,
})
// }

return nil
}
Expand Down Expand Up @@ -652,7 +639,7 @@ func (email *Email) GetMessage() string {
}

// Send sends the composed email
func (email *Email) Send(c *Client, server *SMTPServer) error {
func (email *Email) Send(smtpClient *SMTPClient) error {

if email.Error != nil {
return email.Error
Expand All @@ -664,7 +651,7 @@ func (email *Email) Send(c *Client, server *SMTPServer) error {

msg := email.GetMessage()

return send(email.from, email.recipients, msg, server.SendTimeout, c, server.KeepAlive)
return send(email.from, email.recipients, msg, smtpClient)

}

Expand Down Expand Up @@ -740,7 +727,7 @@ func smtpConnect(host string, port string, auth Auth, encryption encryption, con
}

//Connect returns the smtp client
func (server *SMTPServer) Connect() (*Client, error) {
func (server *SMTPServer) Connect() (*SMTPClient, error) {

var auth Auth

Expand Down Expand Up @@ -780,79 +767,87 @@ func (server *SMTPServer) Connect() (*Client, error) {
}
}

return c, nil
return &SMTPClient{
Client: c,
KeepAlive: server.KeepAlive,
SendTimeout: server.SendTimeout,
}, nil
}

// send does the low level sending of the email
func send(from string, to []string, msg string, sendTimeout int, c *Client, keepAlive bool) error {
func send(from string, to []string, msg string, smtpClient *SMTPClient) error {

//Check if client is not nil
if c != nil {
var smtpSendChannel chan error
//Check if client struct is not nil
if smtpClient != nil {

// set the timeout value
timeout := time.Duration(sendTimeout) * time.Second
//Check if client is not nil
if smtpClient.Client != nil {
var smtpSendChannel chan error

smtpSendChannel = make(chan error, 1)
// set the timeout value
timeout := time.Duration(smtpClient.SendTimeout) * time.Second

go func(c *Client) {
// Set the sender
if err := c.Mail(from); err != nil {
smtpSendChannel <- err
return
}
smtpSendChannel = make(chan error, 1)

// Set the recipients
for _, address := range to {
if err := c.Rcpt(address); err != nil {
go func(c *Client) {
// Set the sender
if err := c.Mail(from); err != nil {
smtpSendChannel <- err
return
}
}

// Send the data command
w, err := c.Data()
if err != nil {
smtpSendChannel <- err
return
}
// Set the recipients
for _, address := range to {
if err := c.Rcpt(address); err != nil {
smtpSendChannel <- err
return
}
}

// write the message
_, err = fmt.Fprint(w, msg)
if err != nil {
smtpSendChannel <- err
return
}
// Send the data command
w, err := c.Data()
if err != nil {
smtpSendChannel <- err
return
}

err = w.Close()
if err != nil {
smtpSendChannel <- err
return
}
// write the message
_, err = fmt.Fprint(w, msg)
if err != nil {
smtpSendChannel <- err
return
}

smtpSendChannel <- err
err = w.Close()
if err != nil {
smtpSendChannel <- err
return
}

}(c)
smtpSendChannel <- err

select {
case sendError := <-smtpSendChannel:
checkKeepAlive(keepAlive, c)
return sendError
case <-time.After(timeout):
checkKeepAlive(keepAlive, c)
return errors.New("Mail Error: SMTP Send timed out")
}(smtpClient.Client)

select {
case sendError := <-smtpSendChannel:
checkKeepAlive(smtpClient)
return sendError
case <-time.After(timeout):
checkKeepAlive(smtpClient)
return errors.New("Mail Error: SMTP Send timed out")
}
}
}

return errors.New("Mail Error: No SMTP Client Provided")
}

//check if keepAlive for close or reset
func checkKeepAlive(keepAlive bool, c *Client) {
if keepAlive {
c.Reset()
func checkKeepAlive(smtpClient *SMTPClient) {
if smtpClient.KeepAlive {
smtpClient.Client.Reset()
} else {
c.Quit()
c.Close()
smtpClient.Client.Quit()
smtpClient.Client.Close()
}
}

0 comments on commit 2f094d0

Please sign in to comment.