diff --git a/msg.go b/msg.go index 762dbd2..6e4f8b3 100644 --- a/msg.go +++ b/msg.go @@ -77,6 +77,11 @@ type Msg struct { // genHeader is a slice of strings that the different generic mail Header fields 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 MIMEVersion @@ -96,11 +101,12 @@ type MsgOption func(*Msg) // NewMsg returns a new Msg pointer func NewMsg(o ...MsgOption) *Msg { m := &Msg{ - addrHeader: make(map[AddrHeader][]*mail.Address), - charset: CharsetUTF8, - encoding: EncodingQP, - genHeader: make(map[Header][]string), - mimever: Mime10, + addrHeader: make(map[AddrHeader][]*mail.Address), + charset: CharsetUTF8, + encoding: EncodingQP, + genHeader: make(map[Header][]string), + preformHeader: make(map[Header]string), + mimever: Mime10, } // Override defaults with optionally provided MsgOption functions @@ -194,6 +200,24 @@ func (m *Msg) SetHeader(h Header, v ...string) { 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 func (m *Msg) SetAddrHeader(h AddrHeader, v ...string) error { if m.addrHeader == nil { diff --git a/msg_test.go b/msg_test.go index dc55eca..8cb3b6d 100644 --- a/msg_test.go +++ b/msg_test.go @@ -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) { tests := []struct { 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 func TestMsg_AddTo(t *testing.T) { a := []string{"address1@example.com", "address2@example.com"} diff --git a/msgwriter.go b/msgwriter.go index 8a28ea1..6b5a4b0 100644 --- a/msgwriter.go +++ b/msgwriter.go @@ -62,6 +62,7 @@ func (mw *msgWriter) writeMsg(m *Msg) { m.addDefaultHeader() m.checkUserAgent() mw.writeGenHeader(m) + mw.writePreformattedGenHeader(m) // Set the FROM header (or envelope FROM if FROM is empty) 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 func (mw *msgWriter) startMP(mt MIMEType, b string) { mp := multipart.NewWriter(mw)