mirror of
https://github.com/wneessen/go-mail.git
synced 2024-11-22 13:50:49 +01:00
Added support for requesting MDNs as described in RFC 8098
This commit is contained in:
parent
da266bcac4
commit
46001dc691
4 changed files with 138 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 attachments and inline embeds (from file system, `io.Reader` or `embed.FS`)
|
||||||
* [X] Support for different encodings
|
* [X] Support for different encodings
|
||||||
* [X] Support sending mails via a local sendmail command
|
* [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] 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] 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
|
* [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
|
// See: https://www.rfc-editor.org/rfc/rfc822#section-5.1
|
||||||
HeaderDate Header = "Date"
|
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 represents the "Importance" field
|
||||||
HeaderImportance Header = "Importance"
|
HeaderImportance Header = "Importance"
|
||||||
|
|
||||||
|
|
50
msg.go
50
msg.go
|
@ -37,6 +37,9 @@ const (
|
||||||
|
|
||||||
// errTplPointerNil is issued when a template pointer is expected but it is nil
|
// errTplPointerNil is issued when a template pointer is expected but it is nil
|
||||||
errTplPointerNil = "template pointer 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
|
// Msg is the mail message struct
|
||||||
|
@ -175,7 +178,7 @@ func (m *Msg) SetAddrHeader(h AddrHeader, v ...string) error {
|
||||||
for _, av := range v {
|
for _, av := range v {
|
||||||
a, err := mail.ParseAddress(av)
|
a, err := mail.ParseAddress(av)
|
||||||
if err != nil {
|
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)
|
al = append(al, a)
|
||||||
}
|
}
|
||||||
|
@ -382,6 +385,51 @@ func (m *Msg) SetUserAgent(a string) {
|
||||||
m.SetHeader(HeaderXMailer, a)
|
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
|
||||||
|
for _, cl := range m.genHeader[HeaderDispositionNotificationTo] {
|
||||||
|
tl = append(tl, cl)
|
||||||
|
}
|
||||||
|
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
|
// 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 first mail body FROM address. If ff is true, it will return the full address string including
|
||||||
// the address name, if set
|
// 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
|
// TestMsg_SetBodyString tests the Msg.SetBodyString method
|
||||||
func TestMsg_SetBodyString(t *testing.T) {
|
func TestMsg_SetBodyString(t *testing.T) {
|
||||||
tests := []struct {
|
tests := []struct {
|
||||||
|
|
Loading…
Reference in a new issue