Merge pull request #22 from wneessen/21-envfrom

Add possibility to set dedicated envelope FROM address
This commit is contained in:
Winni Neessen 2022-06-13 10:37:58 +02:00 committed by GitHub
commit d9491eef0b
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
5 changed files with 120 additions and 7 deletions

2
doc.go
View file

@ -2,4 +2,4 @@
package mail package mail
// VERSION is used in the default user agent string // VERSION is used in the default user agent string
const VERSION = "0.2.2" const VERSION = "0.2.4"

View file

@ -81,7 +81,7 @@ const (
HeaderXPriority Header = "X-Priority" HeaderXPriority Header = "X-Priority"
) )
// List of common generic header field names // List of common address header field names
const ( const (
// HeaderBcc is the "Blind Carbon Copy" header field // HeaderBcc is the "Blind Carbon Copy" header field
HeaderBcc AddrHeader = "Bcc" HeaderBcc AddrHeader = "Bcc"
@ -89,6 +89,10 @@ const (
// HeaderCc is the "Carbon Copy" header field // HeaderCc is the "Carbon Copy" header field
HeaderCc AddrHeader = "Cc" 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 is the "From" header field
HeaderFrom AddrHeader = "From" HeaderFrom AddrHeader = "From"

24
msg.go
View file

@ -197,6 +197,18 @@ func (m *Msg) SetAddrHeaderIgnoreInvalid(h AddrHeader, v ...string) {
m.addrHeader[h] = al 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 // From takes and validates a given mail address and sets it as "From" genHeader of the Msg
func (m *Msg) From(f string) error { func (m *Msg) From(f string) error {
return m.SetAddrHeader(HeaderFrom, f) return m.SetAddrHeader(HeaderFrom, f)
@ -365,12 +377,16 @@ func (m *Msg) SetUserAgent(a string) {
m.SetHeader(HeaderXMailer, a) m.SetHeader(HeaderXMailer, a)
} }
// GetSender returns the currently set FROM address. If f is true, it will return the full // GetSender returns the currently set envelope FROM address. If no envelope FROM is set it will use
// address string including the address name, if set // 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) { func (m *Msg) GetSender(ff bool) (string, error) {
f, ok := m.addrHeader[HeaderFrom] f, ok := m.addrHeader[HeaderEnvelopeFrom]
if !ok || len(f) == 0 { if !ok || len(f) == 0 {
return "", ErrNoFromAddress f, ok = m.addrHeader[HeaderFrom]
if !ok || len(f) == 0 {
return "", ErrNoFromAddress
}
} }
if ff { if ff {
return f[0].String(), nil return f[0].String(), nil

View file

@ -266,6 +266,84 @@ 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 err != nil {
t.Errorf("GetSender(true) failed: %s", err)
return
}
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 // TestMsg_AddToFormat tests the Msg.AddToFormat method
func TestMsg_AddToFormat(t *testing.T) { func TestMsg_AddToFormat(t *testing.T) {
a := []string{"address1@example.com", "address2@example.com"} a := []string{"address1@example.com", "address2@example.com"}

View file

@ -54,7 +54,22 @@ func (mw *msgWriter) writeMsg(m *Msg) {
m.addDefaultHeader() m.addDefaultHeader()
m.checkUserAgent() m.checkUserAgent()
mw.writeGenHeader(m) 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 { if al, ok := m.addrHeader[t]; ok {
var v []string var v []string
for _, a := range al { for _, a := range al {