Add additional error handling to email message sending. (#1639)

* Adding additional error handling to email message sending.

* Adding additional error handling to email message sending.

* Fixing issue with err being lost in the defer.

* Moving defers to be queued up earlier to ensure they get called.

* Fixed closing encoder to use defer pattern that matches the others.
This commit is contained in:
Simon Guindon 2019-05-13 11:14:45 -04:00 committed by GitHub
parent 1d1040accd
commit 7ce9a60c85
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
2 changed files with 29 additions and 9 deletions

View File

@ -12,6 +12,8 @@ import (
"mime/quotedprintable"
"net/textproto"
"time"
"github.com/zeebo/errs"
)
// Message is RFC compliant email message
@ -35,8 +37,11 @@ type Part struct {
Content string
}
// Error is the default message errs class
var Error = errs.Class("Email message error")
// Bytes builds message and returns result as bytes
func (msg *Message) Bytes() []byte {
func (msg *Message) Bytes() (data []byte, err error) {
// always returns nil error on read and write, so most of the errors can be ignored
var body bytes.Buffer
@ -62,6 +67,7 @@ func (msg *Message) Bytes() []byte {
// multipart upload
case len(msg.Parts) > 0:
wr := multipart.NewWriter(&body)
defer func() { err = errs.Combine(err, wr.Close()) }()
fmt.Fprintf(&body, "Content-Type: multipart/alternative;")
fmt.Fprintf(&body, "\tboundary=\"%v\"\r\n", wr.Boundary())
@ -70,14 +76,21 @@ func (msg *Message) Bytes() []byte {
var sub io.Writer
if len(msg.PlainText) > 0 {
sub, _ = wr.CreatePart(textproto.MIMEHeader{
sub, err := wr.CreatePart(textproto.MIMEHeader{
"Content-Type": []string{"text/plain; charset=UTF-8; format=flowed"},
"Content-Transfer-Encoding": []string{"quoted-printable"},
})
if err != nil {
return nil, Error.Wrap(err)
}
enc := quotedprintable.NewWriter(sub)
_, _ = enc.Write([]byte(msg.PlainText))
_ = enc.Close()
defer func() { err = errs.Combine(err, enc.Close()) }()
_, err = enc.Write([]byte(msg.PlainText))
if err != nil {
return nil, Error.Wrap(err)
}
}
for _, part := range msg.Parts {
@ -93,18 +106,20 @@ func (msg *Message) Bytes() []byte {
fmt.Fprint(sub, part.Content)
}
_ = wr.Close()
// fallback if there are no parts, write PlainText with appropriate Content-Type
default:
fmt.Fprintf(&body, "Content-Type: text/plain; charset=UTF-8; format=flowed\r\n")
fmt.Fprintf(&body, "Content-Transfer-Encoding: quoted-printable\r\n\r\n")
enc := quotedprintable.NewWriter(&body)
_, _ = enc.Write([]byte(msg.PlainText))
_ = enc.Close()
defer func() { err = errs.Combine(err, enc.Close()) }()
if _, err := enc.Write([]byte(msg.PlainText)); err != nil {
return nil, Error.Wrap(err)
}
}
return tocrlf(body.Bytes())
return tocrlf(body.Bytes()), nil
}
func tocrlf(data []byte) []byte {

View File

@ -76,7 +76,12 @@ func (sender *SMTPSender) SendEmail(msg *Message) error {
err = errs.Combine(err, data.Close())
}()
_, err = data.Write(msg.Bytes())
mess, err := msg.Bytes()
if err != nil {
return err
}
_, err = data.Write(mess)
if err != nil {
return err
}