From a50910b94372dab7dd9b9cd27a474950400dddc9 Mon Sep 17 00:00:00 2001 From: Winni Neessen Date: Mon, 13 Jun 2022 10:18:35 +0200 Subject: [PATCH 1/4] #21: Add possbility to set dedicated envelope from address - Added `EnvelopeFrom()` and `EnvelopeFromFormat()` methods analogous to the `From()` `FromFormat()` methods - Changed MsgWriter logic for envelope from addresses - Adjusted `Msg.GetSender()` to return the envelope from first and only mail body from if the envelope is not set --- header.go | 6 +++++- msg.go | 24 ++++++++++++++++++++---- msgwriter.go | 17 ++++++++++++++++- 3 files changed, 41 insertions(+), 6 deletions(-) diff --git a/header.go b/header.go index 46cd00b..3d03409 100644 --- a/header.go +++ b/header.go @@ -81,7 +81,7 @@ const ( HeaderXPriority Header = "X-Priority" ) -// List of common generic header field names +// List of common address header field names const ( // HeaderBcc is the "Blind Carbon Copy" header field HeaderBcc AddrHeader = "Bcc" @@ -89,6 +89,10 @@ const ( // HeaderCc is the "Carbon Copy" header field HeaderCc AddrHeader = "Cc" + // HeaderEnvelopeFrom is the envelope FROM header field + // It's not included in the mail body but only used by the Client for the envelope + HeaderEnvelopeFrom AddrHeader = "EnvelopeFrom" + // HeaderFrom is the "From" header field HeaderFrom AddrHeader = "From" diff --git a/msg.go b/msg.go index d27068c..6d27d0e 100644 --- a/msg.go +++ b/msg.go @@ -197,6 +197,18 @@ func (m *Msg) SetAddrHeaderIgnoreInvalid(h AddrHeader, v ...string) { m.addrHeader[h] = al } +// EnvelopeFrom takes and validates a given mail address and sets it as envelope "FROM" +// addrHeader of the Msg +func (m *Msg) EnvelopeFrom(f string) error { + return m.SetAddrHeader(HeaderEnvelopeFrom, f) +} + +// EnvelopeFromFormat takes a name and address, formats them RFC5322 compliant and stores them as +// the envelope FROM address header field +func (m *Msg) EnvelopeFromFormat(n, a string) error { + return m.SetAddrHeader(HeaderEnvelopeFrom, fmt.Sprintf(`"%s" <%s>`, n, a)) +} + // From takes and validates a given mail address and sets it as "From" genHeader of the Msg func (m *Msg) From(f string) error { return m.SetAddrHeader(HeaderFrom, f) @@ -365,12 +377,16 @@ func (m *Msg) SetUserAgent(a string) { m.SetHeader(HeaderXMailer, a) } -// GetSender returns the currently set FROM address. If f is true, it will return the full -// address string including the address name, if set +// GetSender returns the currently set envelope FROM address. If no envelope FROM is set it will use +// the first mail body FROM address. If ff is true, it will return the full address string including +// the address name, if set func (m *Msg) GetSender(ff bool) (string, error) { - f, ok := m.addrHeader[HeaderFrom] + f, ok := m.addrHeader[HeaderEnvelopeFrom] if !ok || len(f) == 0 { - return "", ErrNoFromAddress + f, ok = m.addrHeader[HeaderFrom] + if !ok || len(f) == 0 { + return "", ErrNoFromAddress + } } if ff { return f[0].String(), nil diff --git a/msgwriter.go b/msgwriter.go index bbfdb07..5bbce3d 100644 --- a/msgwriter.go +++ b/msgwriter.go @@ -54,7 +54,22 @@ func (mw *msgWriter) writeMsg(m *Msg) { m.addDefaultHeader() m.checkUserAgent() mw.writeGenHeader(m) - for _, t := range []AddrHeader{HeaderFrom, HeaderTo, HeaderCc} { + + // Set the FROM header (or envelope FROM if FROM is empty) + hf := true + f, ok := m.addrHeader[HeaderFrom] + if !ok || len(f) == 0 { + f, ok = m.addrHeader[HeaderEnvelopeFrom] + if !ok || len(f) == 0 { + hf = false + } + } + if hf { + mw.writeHeader(Header(HeaderFrom), f[0].String()) + } + + // Set the rest of the address headers + for _, t := range []AddrHeader{HeaderTo, HeaderCc} { if al, ok := m.addrHeader[t]; ok { var v []string for _, a := range al { From d0292e863f08c69a95576e82338560bdfa73724a Mon Sep 17 00:00:00 2001 From: Winni Neessen Date: Mon, 13 Jun 2022 10:27:54 +0200 Subject: [PATCH 2/4] #21: Added tests for `Msg.EnvelopeFrom` and `Msg.GetSender` changes --- msg_test.go | 74 +++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 74 insertions(+) diff --git a/msg_test.go b/msg_test.go index 4ed87e5..78cc5fb 100644 --- a/msg_test.go +++ b/msg_test.go @@ -266,6 +266,80 @@ func TestMsg_From(t *testing.T) { } } +// TestMsg_EnvelopeFrom tests the Msg.EnvelopeFrom and Msg.GetSender methods +func TestMsg_EnvelopeFrom(t *testing.T) { + e := "envelope@example.com" + a := "toni@example.com" + n := "Toni Tester" + na := fmt.Sprintf(`"%s" <%s>`, n, a) + ne := fmt.Sprintf(`<%s>`, e) + m := NewMsg() + + _, err := m.GetSender(false) + if err == nil { + t.Errorf("GetSender(false) without a set envelope From address succeeded but was expected to fail") + return + } + + if err := m.EnvelopeFrom(e); err != nil { + t.Errorf("failed to set envelope FROM addresses: %s", err) + return + } + gs, err := m.GetSender(false) + if err != nil { + t.Errorf("GetSender(false) failed: %s", err) + return + } + if gs != e { + t.Errorf("From() failed. Expected: %s, got: %s", e, gs) + return + } + + if err := m.From(na); err != nil { + t.Errorf("failed to set FROM addresses: %s", err) + return + } + gs, err = m.GetSender(false) + if err != nil { + t.Errorf("GetSender(false) failed: %s", err) + return + } + if gs != e { + t.Errorf("From() failed. Expected: %s, got: %s", e, gs) + return + } + + gs, err = m.GetSender(true) + if err != nil { + t.Errorf("GetSender(true) failed: %s", err) + return + } + if gs != ne { + t.Errorf("From() failed. Expected: %s, got: %s", ne, gs) + return + } + m.Reset() + + if err := m.From(na); err != nil { + t.Errorf("failed to set FROM addresses: %s", err) + return + } + gs, err = m.GetSender(false) + if gs != a { + t.Errorf("From() failed. Expected: %s, got: %s", a, gs) + return + } + gs, err = m.GetSender(true) + if err != nil { + t.Errorf("GetSender(true) failed: %s", err) + return + } + if gs != na { + t.Errorf("From() failed. Expected: %s, got: %s", na, gs) + return + } +} + // TestMsg_AddToFormat tests the Msg.AddToFormat method func TestMsg_AddToFormat(t *testing.T) { a := []string{"address1@example.com", "address2@example.com"} From 04ab595f9a83c7d0dafa47e34bb3400c26958ef9 Mon Sep 17 00:00:00 2001 From: Winni Neessen Date: Mon, 13 Jun 2022 10:28:27 +0200 Subject: [PATCH 3/4] Bump `VERSION` for upcoming release --- doc.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/doc.go b/doc.go index 3bfe3a1..45130d1 100644 --- a/doc.go +++ b/doc.go @@ -2,4 +2,4 @@ package mail // VERSION is used in the default user agent string -const VERSION = "0.2.2" +const VERSION = "0.2.4" From e5abcb082d3aa5983aee1369c67a11a97f551250 Mon Sep 17 00:00:00 2001 From: Winni Neessen Date: Mon, 13 Jun 2022 10:33:50 +0200 Subject: [PATCH 4/4] #21: Fix `ineffassign` for GoLinter --- msg_test.go | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/msg_test.go b/msg_test.go index 78cc5fb..3ed7a96 100644 --- a/msg_test.go +++ b/msg_test.go @@ -325,6 +325,10 @@ func TestMsg_EnvelopeFrom(t *testing.T) { return } gs, err = m.GetSender(false) + if err != nil { + t.Errorf("GetSender(true) failed: %s", err) + return + } if gs != a { t.Errorf("From() failed. Expected: %s, got: %s", a, gs) return