diff --git a/client.go b/client.go index 6b9baf1..b93e53b 100644 --- a/client.go +++ b/client.go @@ -511,10 +511,15 @@ func (c *Client) DialWithContext(pc context.Context) error { return err } - c.sc, err = smtp.NewClient(c.co, c.host) + sc, err := smtp.NewClient(c.co, c.host) if err != nil { return err } + if sc == nil { + return fmt.Errorf("SMTP client is nil") + } + c.sc = sc + if c.l != nil { c.sc.SetLogger(c.l) } diff --git a/msg.go b/msg.go index e623958..dbce277 100644 --- a/msg.go +++ b/msg.go @@ -287,7 +287,9 @@ func (m *Msg) SetAddrHeader(h AddrHeader, v ...string) error { } switch h { case HeaderFrom: - m.addrHeader[h] = []*mail.Address{al[0]} + if len(al) > 0 { + m.addrHeader[h] = []*mail.Address{al[0]} + } default: m.addrHeader[h] = al } @@ -518,7 +520,9 @@ func (m *Msg) RequestMDNTo(t ...string) error { } tl = append(tl, a.String()) } - m.genHeader[HeaderDispositionNotificationTo] = tl + if _, ok := m.genHeader[HeaderDispositionNotificationTo]; ok { + m.genHeader[HeaderDispositionNotificationTo] = tl + } return nil } @@ -539,7 +543,9 @@ func (m *Msg) RequestMDNAddTo(t string) error { var tl []string tl = append(tl, m.genHeader[HeaderDispositionNotificationTo]...) tl = append(tl, a.String()) - m.genHeader[HeaderDispositionNotificationTo] = tl + if _, ok := m.genHeader[HeaderDispositionNotificationTo]; ok { + m.genHeader[HeaderDispositionNotificationTo] = tl + } return nil } @@ -591,8 +597,8 @@ func (m *Msg) GetAddrHeader(h AddrHeader) []*mail.Address { // GetAddrHeaderString returns the address string of the requested address header of the Msg func (m *Msg) GetAddrHeaderString(h AddrHeader) []string { var al []string - for i := range m.addrHeader[h] { - al = append(al, m.addrHeader[h][i].String()) + for _, mh := range m.addrHeader[h] { + al = append(al, mh.String()) } return al } @@ -999,9 +1005,12 @@ func (m *Msg) WriteToSendmailWithContext(ctx context.Context, sp string, a ...st if err != nil { return fmt.Errorf("failed to set STDIN pipe: %w", err) } + if se == nil || si == nil { + return fmt.Errorf("received nil for STDERR or STDIN pipe") + } // Start the execution and write to STDIN - if err := ec.Start(); err != nil { + if err = ec.Start(); err != nil { return fmt.Errorf("could not start sendmail execution: %w", err) } _, err = m.WriteTo(si) @@ -1012,7 +1021,7 @@ func (m *Msg) WriteToSendmailWithContext(ctx context.Context, sp string, a ...st } // Close STDIN and wait for completion or cancellation of the sendmail executable - if err := si.Close(); err != nil { + if err = si.Close(); err != nil { return fmt.Errorf("failed to close STDIN pipe: %w", err) } @@ -1025,7 +1034,7 @@ func (m *Msg) WriteToSendmailWithContext(ctx context.Context, sp string, a ...st return fmt.Errorf("sendmail command failed: %s", string(serr)) } - if err := ec.Wait(); err != nil { + if err = ec.Wait(); err != nil { return fmt.Errorf("sendmail command execution failed: %w", err) } @@ -1069,7 +1078,7 @@ func (m *Msg) HasSendError() bool { // corresponding error was of temporary nature and should be retried later func (m *Msg) SendErrorIsTemp() bool { var e *SendError - if errors.As(m.sendError, &e) { + if errors.As(m.sendError, &e) && e != nil { return e.isTemp } return false diff --git a/msg_test.go b/msg_test.go index 14e5f18..47d5b18 100644 --- a/msg_test.go +++ b/msg_test.go @@ -216,6 +216,9 @@ func (mw uppercaseMiddleware) Handle(m *Msg) *Msg { if !ok { fmt.Println("can't find the subject header") } + if s == nil || len(s) < 1 { + s = append(s, "") + } m.Subject(strings.ToUpper(s[0])) return m } @@ -231,6 +234,9 @@ func (mw encodeMiddleware) Handle(m *Msg) *Msg { if !ok { fmt.Println("can't find the subject header") } + if s == nil || len(s) < 1 { + s = append(s, "") + } m.Subject(strings.Replace(s[0], "a", "@", -1)) return m } @@ -752,7 +758,9 @@ func TestMsg_SetMessageIDWithValue(t *testing.T) { t.Errorf("SetMessageID() failed. Expected value, got: empty") return } - m.genHeader[HeaderMessageID] = nil + if _, ok := m.genHeader[HeaderMessageID]; ok { + m.genHeader[HeaderMessageID] = nil + } v := "This.is.a.message.id" vf := "" m.SetMessageIDWithValue(v) @@ -773,7 +781,9 @@ func TestMsg_SetMessageIDRandomness(t *testing.T) { m := NewMsg() m.SetMessageID() mid := m.GetGenHeader(HeaderMessageID) - mids = append(mids, mid[0]) + if len(mid) > 0 { + mids = append(mids, mid[0]) + } } c := make(map[string]int) for i := range mids { @@ -1125,9 +1135,11 @@ func TestMsg_RequestMDN(t *testing.T) { if err := m.RequestMDNTo(v); err != nil { t.Errorf("RequestMDNTo with a single valid address failed: %s", err) } - if m.genHeader[HeaderDispositionNotificationTo][0] != fmt.Sprintf("<%s>", v) { - t.Errorf("RequestMDNTo with a single valid address failed. Expected: %s, got: %s", v, - m.genHeader[HeaderDispositionNotificationTo][0]) + if val := m.genHeader[HeaderDispositionNotificationTo]; len(val) > 1 { + if val[0] != fmt.Sprintf("<%s>", v) { + t.Errorf("RequestMDNTo with a single valid address failed. Expected: %s, got: %s", v, + val[0]) + } } m.Reset() @@ -1135,13 +1147,17 @@ func TestMsg_RequestMDN(t *testing.T) { if err := m.RequestMDNTo(vl...); err != nil { t.Errorf("RequestMDNTo with a multiple valid address failed: %s", err) } - if m.genHeader[HeaderDispositionNotificationTo][0] != fmt.Sprintf("<%s>", v) { - t.Errorf("RequestMDNTo with a multiple valid addresses failed. Expected 0: %s, got 0: %s", v, - m.genHeader[HeaderDispositionNotificationTo][0]) + if val := m.genHeader[HeaderDispositionNotificationTo]; len(val) > 0 { + if val[0] != fmt.Sprintf("<%s>", v) { + t.Errorf("RequestMDNTo with a multiple valid addresses failed. Expected 0: %s, got 0: %s", v, + val[0]) + } } - if m.genHeader[HeaderDispositionNotificationTo][1] != fmt.Sprintf("<%s>", v2) { - t.Errorf("RequestMDNTo with a multiple valid addresses failed. Expected 1: %s, got 1: %s", v2, - m.genHeader[HeaderDispositionNotificationTo][1]) + if val := m.genHeader[HeaderDispositionNotificationTo]; len(val) > 1 { + if val[1] != fmt.Sprintf("<%s>", v2) { + t.Errorf("RequestMDNTo with a multiple valid addresses failed. Expected 1: %s, got 1: %s", v2, + val[1]) + } } m.Reset() @@ -1158,9 +1174,11 @@ func TestMsg_RequestMDN(t *testing.T) { if err := m.RequestMDNAddTo(v2); err != nil { t.Errorf("RequestMDNAddTo with a valid address failed: %s", err) } - if m.genHeader[HeaderDispositionNotificationTo][1] != fmt.Sprintf("<%s>", v2) { - t.Errorf("RequestMDNTo with a multiple valid addresses failed. Expected 1: %s, got 1: %s", v2, - m.genHeader[HeaderDispositionNotificationTo][1]) + if val := m.genHeader[HeaderDispositionNotificationTo]; len(val) > 1 { + if val[1] != fmt.Sprintf("<%s>", v2) { + t.Errorf("RequestMDNTo with a multiple valid addresses failed. Expected 1: %s, got 1: %s", v2, + val[1]) + } } m.Reset() @@ -1168,16 +1186,20 @@ func TestMsg_RequestMDN(t *testing.T) { if err := m.RequestMDNToFormat(n, v); err != nil { t.Errorf("RequestMDNToFormat with a single valid address failed: %s", err) } - if m.genHeader[HeaderDispositionNotificationTo][0] != fmt.Sprintf(`"%s" <%s>`, n, v) { - t.Errorf(`RequestMDNToFormat with a single valid address failed. Expected: "%s" <%s>, got: %s`, n, v, - m.genHeader[HeaderDispositionNotificationTo][0]) + if val := m.genHeader[HeaderDispositionNotificationTo]; len(val) > 0 { + if val[0] != fmt.Sprintf(`"%s" <%s>`, n, v) { + t.Errorf(`RequestMDNToFormat with a single valid address failed. Expected: "%s" <%s>, got: %s`, n, v, + val[0]) + } } if err := m.RequestMDNAddToFormat(n2, v2); err != nil { t.Errorf("RequestMDNAddToFormat with a valid address failed: %s", err) } - if m.genHeader[HeaderDispositionNotificationTo][1] != fmt.Sprintf(`"%s" <%s>`, n2, v2) { - t.Errorf(`RequestMDNAddToFormat with a single valid address failed. Expected: "%s" <%s>, got: %s`, n2, v2, - m.genHeader[HeaderDispositionNotificationTo][1]) + if val := m.genHeader[HeaderDispositionNotificationTo]; len(val) > 1 { + if val[1] != fmt.Sprintf(`"%s" <%s>`, n2, v2) { + t.Errorf(`RequestMDNAddToFormat with a single valid address failed. Expected: "%s" <%s>, got: %s`, n2, v2, + val[1]) + } } m.Reset() @@ -2567,6 +2589,10 @@ func TestMsg_WriteToFile(t *testing.T) { if err != nil { t.Errorf("failed to stat output file: %s", err) } + if fi == nil { + t.Errorf("received empty file handle") + return + } if fi.Size() <= 0 { t.Errorf("output file is expected to contain data but its size is zero") } diff --git a/msgwriter.go b/msgwriter.go index 2b29035..cbf0a8f 100644 --- a/msgwriter.go +++ b/msgwriter.go @@ -67,13 +67,13 @@ func (mw *msgWriter) writeMsg(m *Msg) { // Set the FROM header (or envelope FROM if FROM is empty) hf := true f, ok := m.addrHeader[HeaderFrom] - if !ok || len(f) == 0 { + if !ok || (len(f) == 0 || f == nil) { f, ok = m.addrHeader[HeaderEnvelopeFrom] - if !ok || len(f) == 0 { + if !ok || (len(f) == 0 || f == nil) { hf = false } } - if hf { + if hf && (len(f) > 0 && f[0] != nil) { mw.writeHeader(Header(HeaderFrom), f[0].String()) } diff --git a/reader.go b/reader.go index ee74f7d..acde0a2 100644 --- a/reader.go +++ b/reader.go @@ -25,7 +25,7 @@ func (r *Reader) Read(p []byte) (n int, err error) { if r.err != nil { return 0, r.err } - if r.empty() { + if r.empty() || r.buf == nil { r.Reset() if len(p) == 0 { return 0, nil diff --git a/reader_test.go b/reader_test.go index 3f30735..c1c198b 100644 --- a/reader_test.go +++ b/reader_test.go @@ -6,7 +6,9 @@ package mail import ( "bytes" + "errors" "fmt" + "io" "testing" ) @@ -64,9 +66,20 @@ func TestReader_Read_error(t *testing.T) { // TestReader_Read_empty tests the Reader.Read method with an empty buffer func TestReader_Read_empty(t *testing.T) { r := Reader{buf: []byte{}} - var p []byte + p := make([]byte, 1) + p[0] = 'a' _, err := r.Read(p) - if err != nil { + if err != nil && !errors.Is(err, io.EOF) { + t.Errorf("Reader failed: %s", err) + } +} + +// TestReader_Read_nil tests the Reader.Read method with a nil buffer +func TestReader_Read_nil(t *testing.T) { + r := Reader{buf: nil, off: -10} + p := make([]byte, 0) + _, err := r.Read(p) + if err != nil && !errors.Is(err, io.EOF) { t.Errorf("Reader failed: %s", err) } } diff --git a/senderror.go b/senderror.go index b3a8a7b..34037d1 100644 --- a/senderror.go +++ b/senderror.go @@ -98,7 +98,7 @@ func (e *SendError) Error() string { // Is implements the errors.Is functionality and compares the SendErrReason func (e *SendError) Is(et error) bool { var t *SendError - if errors.As(et, &t) { + if errors.As(et, &t) && t != nil { return e.Reason == t.Reason && e.isTemp == t.isTemp } return false @@ -106,6 +106,9 @@ func (e *SendError) Is(et error) bool { // IsTemp returns true if the delivery error is of temporary nature and can be retried func (e *SendError) IsTemp() bool { + if e == nil { + return false + } return e.isTemp }