Merge pull request #172 from wneessen/feature/171_implement-per-part-charsets

Add Charset support for message parts
This commit is contained in:
Winni Neessen 2024-02-05 12:59:43 +01:00 committed by GitHub
commit 64a07399c4
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
5 changed files with 101 additions and 1 deletions

1
msg.go
View file

@ -1141,6 +1141,7 @@ func (m *Msg) hasPGPType() bool {
func (m *Msg) newPart(ct ContentType, o ...PartOption) *Part { func (m *Msg) newPart(ct ContentType, o ...PartOption) *Part {
p := &Part{ p := &Part{
ctype: ct, ctype: ct,
cset: m.charset,
enc: m.encoding, enc: m.encoding,
} }

View file

@ -245,7 +245,11 @@ func (mw *msgWriter) newPart(h map[string][]string) {
// writePart writes the corresponding part to the Msg body // writePart writes the corresponding part to the Msg body
func (mw *msgWriter) writePart(p *Part, cs Charset) { 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() cte := p.enc.String()
if mw.d == 0 { if mw.d == 0 {
mw.writeHeader(HeaderContentType, ct) mw.writeHeader(HeaderContentType, ct)

View file

@ -53,6 +53,7 @@ func TestMsgWriter_writeMsg(t *testing.T) {
m.SetDateWithValue(now) m.SetDateWithValue(now)
m.SetMessageIDWithValue("message@id.com") m.SetMessageIDWithValue("message@id.com")
m.SetBodyString(TypeTextPlain, "This is the body") m.SetBodyString(TypeTextPlain, "This is the body")
m.AddAlternativeString(TypeTextHTML, "This is the alternative body")
buf := bytes.Buffer{} buf := bytes.Buffer{}
mw := &msgWriter{w: &buf, c: CharsetUTF8, en: mime.QEncoding} mw := &msgWriter{w: &buf, c: CharsetUTF8, en: mime.QEncoding}
mw.writeMsg(m) mw.writeMsg(m)
@ -95,6 +96,26 @@ func TestMsgWriter_writeMsg(t *testing.T) {
if !strings.Contains(ms, "\r\n\r\nThis is the body") { if !strings.Contains(ms, "\r\n\r\nThis is the body") {
ea = append(ea, "Message 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 { if len(ea) > 0 {
em := "writeMsg() failed. The following errors occurred:\n" em := "writeMsg() failed. The following errors occurred:\n"
for e := range ea { for e := range ea {

18
part.go
View file

@ -15,6 +15,7 @@ type PartOption func(*Part)
// Part is a part of the Msg // Part is a part of the Msg
type Part struct { type Part struct {
ctype ContentType ctype ContentType
cset Charset
desc string desc string
enc Encoding enc Encoding
del bool del bool
@ -30,6 +31,11 @@ func (p *Part) GetContent() ([]byte, error) {
return b.Bytes(), nil 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 // GetContentType returns the currently set ContentType of the Part
func (p *Part) GetContentType() ContentType { func (p *Part) GetContentType() ContentType {
return p.ctype return p.ctype
@ -61,6 +67,11 @@ func (p *Part) SetContentType(c ContentType) {
p.ctype = c 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 // SetEncoding creates a new mime.WordEncoder based on the encoding setting of the message
func (p *Part) SetEncoding(e Encoding) { func (p *Part) SetEncoding(e Encoding) {
p.enc = e p.enc = e
@ -82,6 +93,13 @@ func (p *Part) Delete() {
p.del = true 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 // WithPartEncoding overrides the default Part encoding
func WithPartEncoding(e Encoding) PartOption { func WithPartEncoding(e Encoding) PartOption {
return func(p *Part) { return func(p *Part) {

View file

@ -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 // TestPart_WithPartContentDescription tests the WithPartContentDescription method
func TestPart_WithPartContentDescription(t *testing.T) { func TestPart_WithPartContentDescription(t *testing.T) {
tests := []struct { tests := []struct {
@ -320,3 +347,32 @@ func getPartList(m *Msg) ([]*Part, error) {
} }
return pl, nil 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)
}
})
}
}