Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Eleminate potential null pointer exceptions #157

Merged
merged 16 commits into from
Nov 30, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 6 additions & 1 deletion client.go
Original file line number Diff line number Diff line change
Expand Up @@ -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)
}
Expand Down
27 changes: 18 additions & 9 deletions msg.go
Original file line number Diff line number Diff line change
Expand Up @@ -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
}
Expand Down Expand Up @@ -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
}

Expand All @@ -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
}

Expand Down Expand Up @@ -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
}
Expand Down Expand Up @@ -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)
Expand All @@ -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)
}

Expand All @@ -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)
}

Expand Down Expand Up @@ -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
Expand Down
66 changes: 46 additions & 20 deletions msg_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -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
}
Expand All @@ -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
}
Expand Down Expand Up @@ -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 := "<This.is.a.message.id>"
m.SetMessageIDWithValue(v)
Expand All @@ -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 {
Expand Down Expand Up @@ -1125,23 +1135,29 @@ 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()

// Multiples valid addresses
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()

Expand All @@ -1158,26 +1174,32 @@ 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()

// Single valid address formated + AddToFromat
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()

Expand Down Expand Up @@ -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")
}
Expand Down
6 changes: 3 additions & 3 deletions msgwriter.go
Original file line number Diff line number Diff line change
Expand Up @@ -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())
}

Expand Down
2 changes: 1 addition & 1 deletion reader.go
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
17 changes: 15 additions & 2 deletions reader_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,9 @@ package mail

import (
"bytes"
"errors"
"fmt"
"io"
"testing"
)

Expand Down Expand Up @@ -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)
}
}
5 changes: 4 additions & 1 deletion senderror.go
Original file line number Diff line number Diff line change
Expand Up @@ -98,14 +98,17 @@ 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
}

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

Expand Down
Loading