Add SetHeaderPreformatted() method

With the SetHeaderPreformatted() method we have the ability to set headers that are already preformatted by the user and will not be altered in the mail message output
This commit is contained in:
Winni Neessen 2022-10-26 15:33:03 +02:00
parent 4250eaf911
commit 2e60d070a6
Signed by: wneessen
GPG key ID: 5F3AF39B820C119D
3 changed files with 78 additions and 6 deletions

34
msg.go
View file

@ -77,6 +77,11 @@ type Msg struct {
// genHeader is a slice of strings that the different generic mail Header fields // genHeader is a slice of strings that the different generic mail Header fields
genHeader map[Header][]string genHeader map[Header][]string
// preformHeader is a slice of strings that the different generic mail Header fields
// of which content is already preformated and will not be affected by the automatic line
// breaks
preformHeader map[Header]string
// mimever represents the MIME version // mimever represents the MIME version
mimever MIMEVersion mimever MIMEVersion
@ -96,11 +101,12 @@ type MsgOption func(*Msg)
// NewMsg returns a new Msg pointer // NewMsg returns a new Msg pointer
func NewMsg(o ...MsgOption) *Msg { func NewMsg(o ...MsgOption) *Msg {
m := &Msg{ m := &Msg{
addrHeader: make(map[AddrHeader][]*mail.Address), addrHeader: make(map[AddrHeader][]*mail.Address),
charset: CharsetUTF8, charset: CharsetUTF8,
encoding: EncodingQP, encoding: EncodingQP,
genHeader: make(map[Header][]string), genHeader: make(map[Header][]string),
mimever: Mime10, preformHeader: make(map[Header]string),
mimever: Mime10,
} }
// Override defaults with optionally provided MsgOption functions // Override defaults with optionally provided MsgOption functions
@ -194,6 +200,24 @@ func (m *Msg) SetHeader(h Header, v ...string) {
m.genHeader[h] = v m.genHeader[h] = v
} }
// SetHeaderPreformatted sets a generic header field of the Msg which content is
// already preformated.
//
// This method does not take a slice of values but only a single value. This is
// due to the fact, that we do not perform any content alteration and expect the
// user has already done so
//
// **Please note:** This method should be used only as a last resort. Since the
// user is respondible for the formating of the message header, go-mail cannot
// guarantee the fully compliance with the RFC 2822. It is recommended to use
// SetHeader instead.
func (m *Msg) SetHeaderPreformatted(h Header, v string) {
if m.preformHeader == nil {
m.preformHeader = make(map[Header]string)
}
m.preformHeader[h] = v
}
// SetAddrHeader sets an address related header field of the Msg // SetAddrHeader sets an address related header field of the Msg
func (m *Msg) SetAddrHeader(h AddrHeader, v ...string) error { func (m *Msg) SetAddrHeader(h AddrHeader, v ...string) error {
if m.addrHeader == nil { if m.addrHeader == nil {

View file

@ -255,7 +255,7 @@ func TestApplyMiddlewares(t *testing.T) {
} }
} }
// TestMsg_SetHEader tests Msg.SetHeader // TestMsg_SetHeader tests Msg.SetHeader
func TestMsg_SetHeader(t *testing.T) { func TestMsg_SetHeader(t *testing.T) {
tests := []struct { tests := []struct {
name string name string
@ -289,6 +289,46 @@ func TestMsg_SetHeader(t *testing.T) {
} }
} }
// TestMsg_SetHeaderPreformatted tests Msg.SetHeaderPreformatted
func TestMsg_SetHeaderPreformatted(t *testing.T) {
tests := []struct {
name string
header Header
value string
}{
{"set subject", HeaderSubject, "This is Subject"},
{"set content-language", HeaderContentLang, fmt.Sprintf("%s, %s, %s, %s",
"en", "de", "fr", "es")},
{"set subject with newline", HeaderSubject, "This is Subject\r\n with 2nd line"},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
m := &Msg{}
m.SetHeaderPreformatted(tt.header, tt.value)
m = NewMsg()
m.SetHeaderPreformatted(tt.header, tt.value)
if m.preformHeader[tt.header] == "" {
t.Errorf("SetHeaderPreformatted() failed. Tried to set header %s, but it is empty", tt.header)
}
if m.preformHeader[tt.header] != tt.value {
t.Errorf("SetHeaderPreformatted() failed. Expected: %q, got: %q", tt.value,
m.preformHeader[tt.header])
}
buf := bytes.Buffer{}
_, err := m.WriteTo(&buf)
if err != nil {
t.Errorf("failed to write message to memory: %s", err)
return
}
if !strings.Contains(buf.String(), fmt.Sprintf("%s: %s%s", tt.header, tt.value, SingleNewLine)) {
t.Errorf("SetHeaderPreformatted() failed. Unable to find correctly formated header in " +
"mail message output")
}
})
}
}
// TestMsg_AddTo tests the Msg.AddTo method // TestMsg_AddTo tests the Msg.AddTo method
func TestMsg_AddTo(t *testing.T) { func TestMsg_AddTo(t *testing.T) {
a := []string{"address1@example.com", "address2@example.com"} a := []string{"address1@example.com", "address2@example.com"}

View file

@ -62,6 +62,7 @@ func (mw *msgWriter) writeMsg(m *Msg) {
m.addDefaultHeader() m.addDefaultHeader()
m.checkUserAgent() m.checkUserAgent()
mw.writeGenHeader(m) mw.writeGenHeader(m)
mw.writePreformattedGenHeader(m)
// Set the FROM header (or envelope FROM if FROM is empty) // Set the FROM header (or envelope FROM if FROM is empty)
hf := true hf := true
@ -132,6 +133,13 @@ func (mw *msgWriter) writeGenHeader(m *Msg) {
} }
} }
// writePreformatedHeader writes out all preformated generic headers to the msgWriter
func (mw *msgWriter) writePreformattedGenHeader(m *Msg) {
for k, v := range m.preformHeader {
mw.writeString(fmt.Sprintf("%s: %s%s", k, v, SingleNewLine))
}
}
// startMP writes a multipart beginning // startMP writes a multipart beginning
func (mw *msgWriter) startMP(mt MIMEType, b string) { func (mw *msgWriter) startMP(mt MIMEType, b string) {
mp := multipart.NewWriter(mw) mp := multipart.NewWriter(mw)