mirror of
https://github.com/wneessen/go-mail.git
synced 2024-12-23 11:00:39 +01:00
Merge pull request #46 from wneessen/MDN
Added support for requesting MDNs as described in RFC 8098
This commit is contained in:
commit
5bd3ceca02
4 changed files with 136 additions and 1 deletions
|
@ -43,6 +43,7 @@ Some of the features of this library:
|
|||
* [X] Support for attachments and inline embeds (from file system, `io.Reader` or `embed.FS`)
|
||||
* [X] Support for different encodings
|
||||
* [X] Support sending mails via a local sendmail command
|
||||
* [X] Support for requestng MDNs
|
||||
* [X] Message object satisfies `io.WriteTo` and `io.Reader` interfaces
|
||||
* [X] Support for Go's `html/template` and `text/template` (as message body, alternative part or attachment/emebed)
|
||||
* [X] Output to file support which allows storing mail messages as e. g. `.eml` files to disk to open them in a MUA
|
||||
|
|
|
@ -37,6 +37,10 @@ const (
|
|||
// See: https://www.rfc-editor.org/rfc/rfc822#section-5.1
|
||||
HeaderDate Header = "Date"
|
||||
|
||||
// HeaderDispositionNotificationTo is the MDN header as described in RFC8098
|
||||
// See: https://www.rfc-editor.org/rfc/rfc8098.html#section-2.1
|
||||
HeaderDispositionNotificationTo Header = "Disposition-Notification-To"
|
||||
|
||||
// HeaderImportance represents the "Importance" field
|
||||
HeaderImportance Header = "Importance"
|
||||
|
||||
|
|
48
msg.go
48
msg.go
|
@ -37,6 +37,9 @@ const (
|
|||
|
||||
// errTplPointerNil is issued when a template pointer is expected but it is nil
|
||||
errTplPointerNil = "template pointer is nil"
|
||||
|
||||
// errParseMailAddr is used when a mail address could not be validated
|
||||
errParseMailAddr = "failed to parse mail address %q: %w"
|
||||
)
|
||||
|
||||
// Msg is the mail message struct
|
||||
|
@ -175,7 +178,7 @@ func (m *Msg) SetAddrHeader(h AddrHeader, v ...string) error {
|
|||
for _, av := range v {
|
||||
a, err := mail.ParseAddress(av)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to parse mail address header %q: %w", av, err)
|
||||
return fmt.Errorf(errParseMailAddr, av, err)
|
||||
}
|
||||
al = append(al, a)
|
||||
}
|
||||
|
@ -382,6 +385,49 @@ func (m *Msg) SetUserAgent(a string) {
|
|||
m.SetHeader(HeaderXMailer, a)
|
||||
}
|
||||
|
||||
// RequestMDNTo adds the Disposition-Notification-To header to request a MDN from the receiving end
|
||||
// as described in RFC8098. It allows to provide a list recipient addresses.
|
||||
// Address validation is performed
|
||||
// See: https://www.rfc-editor.org/rfc/rfc8098.html
|
||||
func (m *Msg) RequestMDNTo(t ...string) error {
|
||||
var tl []string
|
||||
for _, at := range t {
|
||||
a, err := mail.ParseAddress(at)
|
||||
if err != nil {
|
||||
return fmt.Errorf(errParseMailAddr, at, err)
|
||||
}
|
||||
tl = append(tl, a.String())
|
||||
}
|
||||
m.genHeader[HeaderDispositionNotificationTo] = tl
|
||||
return nil
|
||||
}
|
||||
|
||||
// RequestMDNToFormat adds the Disposition-Notification-To header to request a MDN from the receiving end
|
||||
// as described in RFC8098. It allows to provide a recipient address with name and address and will format
|
||||
// accordingly. Address validation is performed
|
||||
// See: https://www.rfc-editor.org/rfc/rfc8098.html
|
||||
func (m *Msg) RequestMDNToFormat(n, a string) error {
|
||||
return m.RequestMDNTo(fmt.Sprintf(`%s <%s>`, n, a))
|
||||
}
|
||||
|
||||
// RequestMDNAddTo adds an additional recipient to the recipient list of the MDN
|
||||
func (m *Msg) RequestMDNAddTo(t string) error {
|
||||
a, err := mail.ParseAddress(t)
|
||||
if err != nil {
|
||||
return fmt.Errorf(errParseMailAddr, t, err)
|
||||
}
|
||||
var tl []string
|
||||
tl = append(tl, m.genHeader[HeaderDispositionNotificationTo]...)
|
||||
tl = append(tl, a.String())
|
||||
m.genHeader[HeaderDispositionNotificationTo] = tl
|
||||
return nil
|
||||
}
|
||||
|
||||
// RequestMDNAddToFormat adds an additional formated recipient to the recipient list of the MDN
|
||||
func (m *Msg) RequestMDNAddToFormat(n, a string) error {
|
||||
return m.RequestMDNAddTo(fmt.Sprintf(`"%s" <%s>`, n, a))
|
||||
}
|
||||
|
||||
// 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
|
||||
|
|
84
msg_test.go
84
msg_test.go
|
@ -927,6 +927,90 @@ func TestMsg_SetUserAgent(t *testing.T) {
|
|||
}
|
||||
}
|
||||
|
||||
// TestMsg_RequestMDN tests the different RequestMDN* related methods of Msg
|
||||
func TestMsg_RequestMDN(t *testing.T) {
|
||||
n := "Toni Tester"
|
||||
n2 := "Melanie Tester"
|
||||
v := "toni.tester@example.com"
|
||||
v2 := "melanie.tester@example.com"
|
||||
iv := "testertest.tld"
|
||||
vl := []string{v, v2}
|
||||
m := NewMsg()
|
||||
|
||||
// Single valid address
|
||||
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])
|
||||
}
|
||||
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 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])
|
||||
}
|
||||
m.Reset()
|
||||
|
||||
// Invalid address
|
||||
if err := m.RequestMDNTo(iv); err == nil {
|
||||
t.Errorf("RequestMDNTo with an invalid address was supposed to failed, but didn't")
|
||||
}
|
||||
m.Reset()
|
||||
|
||||
// Single valid addresses + AddTo
|
||||
if err := m.RequestMDNTo(v); err != nil {
|
||||
t.Errorf("RequestMDNTo with a single valid address failed: %s", err)
|
||||
}
|
||||
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])
|
||||
}
|
||||
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 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])
|
||||
}
|
||||
m.Reset()
|
||||
|
||||
// Invalid formated address
|
||||
if err := m.RequestMDNToFormat(n, iv); err == nil {
|
||||
t.Errorf("RequestMDNToFormat with an invalid address was supposed to failed, but didn't")
|
||||
}
|
||||
|
||||
// Invalid address AddTo + AddToFormat
|
||||
if err := m.RequestMDNAddTo(iv); err == nil {
|
||||
t.Errorf("RequestMDNAddTo with an invalid address was supposed to failed, but didn't")
|
||||
}
|
||||
if err := m.RequestMDNAddToFormat(n, iv); err == nil {
|
||||
t.Errorf("RequestMDNAddToFormat with an invalid address was supposed to failed, but didn't")
|
||||
}
|
||||
}
|
||||
|
||||
// TestMsg_SetBodyString tests the Msg.SetBodyString method
|
||||
func TestMsg_SetBodyString(t *testing.T) {
|
||||
tests := []struct {
|
||||
|
|
Loading…
Reference in a new issue