Repository: jhillyerd/enmime
Pull request #187 (2021-04-01 08:43:22)

Validate To/BCC/CC Ref #186
References  #186

Only append To/BCC/CC if they have an address with a length. Added some tests.

The tests are slightly verbose as I couldn't think of a nice way of tabulating them, plus having the error message hard coded instead of as a referenceable error value feels strange.


Patch:
diff --git a/builder.go b/builder.go
index da29fd0..69b521d 100644
--- a/builder.go
+++ b/builder.go
@@ -62,7 +62,9 @@ func (p MailBuilder) Subject(subject string) MailBuilder {
 // To returns a copy of MailBuilder with this name & address appended to the To header.  name may be
 // empty.
 func (p MailBuilder) To(name, addr string) MailBuilder {
-	p.to = append(p.to, mail.Address{Name: name, Address: addr})
+	if len(addr) > 0 {
+		p.to = append(p.to, mail.Address{Name: name, Address: addr})
+	}
 	return p
 }
 
@@ -75,7 +77,9 @@ func (p MailBuilder) ToAddrs(to []mail.Address) MailBuilder {
 // CC returns a copy of MailBuilder with this name & address appended to the CC header.  name may be
 // empty.
 func (p MailBuilder) CC(name, addr string) MailBuilder {
-	p.cc = append(p.cc, mail.Address{Name: name, Address: addr})
+	if len(addr) > 0 {
+		p.cc = append(p.cc, mail.Address{Name: name, Address: addr})
+	}
 	return p
 }
 
@@ -89,7 +93,9 @@ func (p MailBuilder) CCAddrs(cc []mail.Address) MailBuilder {
 // empty.  This method only has an effect if the Send method is used to transmit the message, there
 // is no effect on the parts returned by Build().
 func (p MailBuilder) BCC(name, addr string) MailBuilder {
-	p.bcc = append(p.bcc, mail.Address{Name: name, Address: addr})
+	if len(addr) > 0 {
+		p.bcc = append(p.bcc, mail.Address{Name: name, Address: addr})
+	}
 	return p
 }
 
@@ -219,7 +225,7 @@ func (p MailBuilder) Build() (*Part, error) {
 		return nil, errors.New("subject not set")
 	}
 	if len(p.to)+len(p.cc)+len(p.bcc) == 0 {
-		return nil, errors.New("no recipients (to, cc, bcc) set")
+		return nil, errors.New(ErrorMissingRecipient)
 	}
 	// Fully loaded structure; the presence of text, html, inlines, and attachments will determine
 	// how much is necessary:
diff --git a/error.go b/error.go
index 81b2148..26a366a 100644
--- a/error.go
+++ b/error.go
@@ -21,6 +21,8 @@ const (
 	ErrorPlainTextFromHTML = "Plain Text from HTML"
 	// ErrorCharsetDeclaration name.
 	ErrorCharsetDeclaration = "Character Set Declaration Mismatch"
+	// ErrorMissingRecipient name.
+	ErrorMissingRecipient = "no recipients (to, cc, bcc) set"
 )
 
 // Error describes an error encountered while parsing.


Test patch:
diff --git a/builder_test.go b/builder_test.go
index 66d8486..a7219d2 100644
--- a/builder_test.go
+++ b/builder_test.go
@@ -1003,3 +1003,63 @@ func TestSendWithReversePath(t *testing.T) {
 		t.Errorf("msg bytes did not contain text body %q", text)
 	}
 }
+
+func TestEmptyTo(t *testing.T) {
+	from := "from@example.com"
+	text := []byte("test text body")
+	rcpt := "rcpt name"
+	a := enmime.Builder().
+		Text(text).
+		From("name", from).
+		Subject("foo").
+		To(rcpt, "")
+
+	_, err := a.Build()
+	if err == nil {
+		t.Fatal("Expected error, got nil")
+	}
+
+	if err.Error() != enmime.ErrorMissingRecipient {
+		t.Fatalf("Unexpected error, wanted %q got %s", enmime.ErrorMissingRecipient, err)
+	}
+}
+
+func TestEmptyBCC(t *testing.T) {
+	from := "from@example.com"
+	text := []byte("test text body")
+	rcpt := "rcpt name"
+	a := enmime.Builder().
+		Text(text).
+		From("name", from).
+		Subject("foo").
+		BCC(rcpt, "")
+
+	_, err := a.Build()
+	if err == nil {
+		t.Fatal("Expected error, got nil")
+	}
+
+	if err.Error() != enmime.ErrorMissingRecipient {
+		t.Fatalf("Unexpected error, wanted %q got %s", enmime.ErrorMissingRecipient, err)
+	}
+}
+
+func TestEmptyCC(t *testing.T) {
+	from := "from@example.com"
+	text := []byte("test text body")
+	rcpt := "rcpt name"
+	a := enmime.Builder().
+		Text(text).
+		From("name", from).
+		Subject("foo").
+		CC(rcpt, "")
+
+	_, err := a.Build()
+	if err == nil {
+		t.Fatal("Expected error, got nil")
+	}
+
+	if err.Error() != enmime.ErrorMissingRecipient {
+		t.Fatalf("Unexpected error, wanted %q got %s", enmime.ErrorMissingRecipient, err)
+	}
+}