diff --git a/msg.go b/msg.go index c97fc01..7ca73ce 100644 --- a/msg.go +++ b/msg.go @@ -1141,6 +1141,7 @@ func (m *Msg) hasPGPType() bool { func (m *Msg) newPart(ct ContentType, o ...PartOption) *Part { p := &Part{ ctype: ct, + cset: m.charset, enc: m.encoding, } diff --git a/msgwriter.go b/msgwriter.go index cbf0a8f..b2ff118 100644 --- a/msgwriter.go +++ b/msgwriter.go @@ -245,7 +245,11 @@ func (mw *msgWriter) newPart(h map[string][]string) { // writePart writes the corresponding part to the Msg body func (mw *msgWriter) writePart(p *Part, cs Charset) { - ct := fmt.Sprintf("%s; charset=%s", p.ctype, cs) + pcs := p.cset + if pcs.String() == "" { + pcs = cs + } + ct := fmt.Sprintf("%s; charset=%s", p.ctype, pcs) cte := p.enc.String() if mw.d == 0 { mw.writeHeader(HeaderContentType, ct) diff --git a/msgwriter_test.go b/msgwriter_test.go index d19d334..4f61c6f 100644 --- a/msgwriter_test.go +++ b/msgwriter_test.go @@ -53,6 +53,7 @@ func TestMsgWriter_writeMsg(t *testing.T) { m.SetDateWithValue(now) m.SetMessageIDWithValue("message@id.com") m.SetBodyString(TypeTextPlain, "This is the body") + m.AddAlternativeString(TypeTextHTML, "This is the alternative body") buf := bytes.Buffer{} mw := &msgWriter{w: &buf, c: CharsetUTF8, en: mime.QEncoding} mw.writeMsg(m) @@ -95,6 +96,26 @@ func TestMsgWriter_writeMsg(t *testing.T) { if !strings.Contains(ms, "\r\n\r\nThis is the body") { ea = append(ea, "Message body") } + + pl := m.GetParts() + if len(pl) <= 0 { + t.Errorf("expected multiple parts but got none") + return + } + if len(pl) == 2 { + ap := pl[1] + ap.SetCharset(CharsetISO88591) + } + buf.Reset() + mw.writeMsg(m) + ms = buf.String() + if !strings.Contains(ms, "\r\n\r\nThis is the alternative body") { + ea = append(ea, "Message alternative body") + } + if !strings.Contains(ms, `Content-Type: text/html; charset=ISO-8859-1`) { + ea = append(ea, "alternative body charset") + } + if len(ea) > 0 { em := "writeMsg() failed. The following errors occurred:\n" for e := range ea { diff --git a/part.go b/part.go index 1e4d6cd..78e8aed 100644 --- a/part.go +++ b/part.go @@ -15,6 +15,7 @@ type PartOption func(*Part) // Part is a part of the Msg type Part struct { ctype ContentType + cset Charset desc string enc Encoding del bool @@ -30,6 +31,11 @@ func (p *Part) GetContent() ([]byte, error) { return b.Bytes(), nil } +// GetCharset returns the currently set Charset of the Part +func (p *Part) GetCharset() Charset { + return p.cset +} + // GetContentType returns the currently set ContentType of the Part func (p *Part) GetContentType() ContentType { return p.ctype @@ -61,6 +67,11 @@ func (p *Part) SetContentType(c ContentType) { p.ctype = c } +// SetCharset overrides the Charset of the Part +func (p *Part) SetCharset(c Charset) { + p.cset = c +} + // SetEncoding creates a new mime.WordEncoder based on the encoding setting of the message func (p *Part) SetEncoding(e Encoding) { p.enc = e @@ -82,6 +93,13 @@ func (p *Part) Delete() { p.del = true } +// WithPartCharset overrides the default Part charset +func WithPartCharset(c Charset) PartOption { + return func(p *Part) { + p.cset = c + } +} + // WithPartEncoding overrides the default Part encoding func WithPartEncoding(e Encoding) PartOption { return func(p *Part) { diff --git a/part_test.go b/part_test.go index b0d50f3..276fc69 100644 --- a/part_test.go +++ b/part_test.go @@ -45,6 +45,33 @@ func TestPartEncoding(t *testing.T) { } } +// TestWithPartCharset tests the WithPartCharset method +func TestWithPartCharset(t *testing.T) { + tests := []struct { + name string + cs Charset + want string + }{ + {"Part charset: UTF-8", CharsetUTF8, "UTF-8"}, + {"Part charset: ISO-8859-1", CharsetISO88591, "ISO-8859-1"}, + {"Part charset: empty", "", ""}, + } + for _, tt := range tests { + m := NewMsg() + t.Run(tt.name, func(t *testing.T) { + part := m.newPart(TypeTextPlain, WithPartCharset(tt.cs), nil) + if part == nil { + t.Errorf("newPart() WithPartCharset() failed: no part returned") + return + } + if part.cset.String() != tt.want { + t.Errorf("newPart() WithPartCharset() failed: expected charset: %s, got: %s", + tt.want, part.cset.String()) + } + }) + } +} + // TestPart_WithPartContentDescription tests the WithPartContentDescription method func TestPart_WithPartContentDescription(t *testing.T) { tests := []struct { @@ -320,3 +347,32 @@ func getPartList(m *Msg) ([]*Part, error) { } return pl, nil } + +// TestPart_SetCharset tests Part.SetCharset method +func TestPart_SetCharset(t *testing.T) { + tests := []struct { + name string + cs Charset + want string + }{ + {"Charset: UTF-8", CharsetUTF8, "UTF-8"}, + {"Charset: ISO-8859-1", CharsetISO88591, "ISO-8859-1"}, + {"Charset: empty", "", ""}, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + m := NewMsg() + m.SetBodyString(TypeTextPlain, "This is a test with ümläutß") + pl, err := getPartList(m) + if err != nil { + t.Errorf("failed: %s", err) + return + } + pl[0].SetCharset(tt.cs) + cs := pl[0].GetCharset() + if string(cs) != tt.want { + t.Errorf("SetCharset failed. Got: %s, expected: %s", string(cs), tt.want) + } + }) + } +}