Provided a way to specify the encoding of a message part
This commit introduces a minor backward-compatibility break: Message.SetBody, Message.AddAlternative and Message.AddAlternativeWriter now accept a new variadic argument. Since this argument is optional, in almost all cases there is nothing to change. That is why Gomail version number wasn't bumped. Fixes #47
This commit is contained in:
parent
19acfc29a0
commit
db70192787
|
@ -213,3 +213,7 @@ func ExampleSetCharset() {
|
||||||
func ExampleSetEncoding() {
|
func ExampleSetEncoding() {
|
||||||
m = gomail.NewMessage(gomail.SetEncoding(gomail.Base64))
|
m = gomail.NewMessage(gomail.SetEncoding(gomail.Base64))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func ExampleWithEncoding() {
|
||||||
|
m.SetBody("text/plain", "Hello!", gomail.SetPartEncoding(gomail.Unencoded))
|
||||||
|
}
|
||||||
|
|
67
message.go
67
message.go
|
@ -11,7 +11,7 @@ import (
|
||||||
// Message represents an email.
|
// Message represents an email.
|
||||||
type Message struct {
|
type Message struct {
|
||||||
header header
|
header header
|
||||||
parts []part
|
parts []*part
|
||||||
attachments []*file
|
attachments []*file
|
||||||
embedded []*file
|
embedded []*file
|
||||||
charset string
|
charset string
|
||||||
|
@ -23,8 +23,9 @@ type Message struct {
|
||||||
type header map[string][]string
|
type header map[string][]string
|
||||||
|
|
||||||
type part struct {
|
type part struct {
|
||||||
header header
|
contentType string
|
||||||
copier func(io.Writer) error
|
copier func(io.Writer) error
|
||||||
|
encoding Encoding
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewMessage creates a new message. It uses UTF-8 and quoted-printable encoding
|
// NewMessage creates a new message. It uses UTF-8 and quoted-printable encoding
|
||||||
|
@ -179,16 +180,8 @@ func (m *Message) GetHeader(field string) []string {
|
||||||
|
|
||||||
// SetBody sets the body of the message. It replaces any content previously set
|
// SetBody sets the body of the message. It replaces any content previously set
|
||||||
// by SetBody, AddAlternative or AddAlternativeWriter.
|
// by SetBody, AddAlternative or AddAlternativeWriter.
|
||||||
func (m *Message) SetBody(contentType, body string) {
|
func (m *Message) SetBody(contentType, body string, settings ...PartSetting) {
|
||||||
m.parts = []part{
|
m.parts = []*part{m.newPart(contentType, newCopier(body), settings)}
|
||||||
{
|
|
||||||
header: m.getPartHeader(contentType),
|
|
||||||
copier: func(w io.Writer) error {
|
|
||||||
_, err := io.WriteString(w, body)
|
|
||||||
return err
|
|
||||||
},
|
|
||||||
},
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// AddAlternative adds an alternative part to the message.
|
// AddAlternative adds an alternative part to the message.
|
||||||
|
@ -197,30 +190,48 @@ func (m *Message) SetBody(contentType, body string) {
|
||||||
// version for backward compatibility. AddAlternative appends the new part to
|
// version for backward compatibility. AddAlternative appends the new part to
|
||||||
// the end of the message. So the plain text part should be added before the
|
// the end of the message. So the plain text part should be added before the
|
||||||
// HTML part. See http://en.wikipedia.org/wiki/MIME#Alternative
|
// HTML part. See http://en.wikipedia.org/wiki/MIME#Alternative
|
||||||
func (m *Message) AddAlternative(contentType, body string) {
|
func (m *Message) AddAlternative(contentType, body string, settings ...PartSetting) {
|
||||||
m.parts = append(m.parts, part{
|
m.AddAlternativeWriter(contentType, newCopier(body), settings...)
|
||||||
header: m.getPartHeader(contentType),
|
}
|
||||||
copier: func(w io.Writer) error {
|
|
||||||
_, err := io.WriteString(w, body)
|
func newCopier(s string) func(io.Writer) error {
|
||||||
|
return func(w io.Writer) error {
|
||||||
|
_, err := io.WriteString(w, s)
|
||||||
return err
|
return err
|
||||||
},
|
}
|
||||||
})
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// AddAlternativeWriter adds an alternative part to the message. It can be
|
// AddAlternativeWriter adds an alternative part to the message. It can be
|
||||||
// useful with the text/template or html/template packages.
|
// useful with the text/template or html/template packages.
|
||||||
func (m *Message) AddAlternativeWriter(contentType string, f func(io.Writer) error) {
|
func (m *Message) AddAlternativeWriter(contentType string, f func(io.Writer) error, settings ...PartSetting) {
|
||||||
m.parts = append(m.parts, part{
|
m.parts = append(m.parts, m.newPart(contentType, f, settings))
|
||||||
header: m.getPartHeader(contentType),
|
|
||||||
copier: f,
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (m *Message) getPartHeader(contentType string) header {
|
func (m *Message) newPart(contentType string, f func(io.Writer) error, settings []PartSetting) *part {
|
||||||
return map[string][]string{
|
p := &part{
|
||||||
"Content-Type": {contentType + "; charset=" + m.charset},
|
contentType: contentType,
|
||||||
"Content-Transfer-Encoding": {string(m.encoding)},
|
copier: f,
|
||||||
|
encoding: m.encoding,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
for _, s := range settings {
|
||||||
|
s(p)
|
||||||
|
}
|
||||||
|
|
||||||
|
return p
|
||||||
|
}
|
||||||
|
|
||||||
|
// A PartSetting can be used as an argument in Message.SetBody,
|
||||||
|
// Message.AddAlternative or Message.AddAlternativeWriter to configure the part
|
||||||
|
// added to a message.
|
||||||
|
type PartSetting func(*part)
|
||||||
|
|
||||||
|
// SetPartEncoding sets the encoding of the part added to the message. By
|
||||||
|
// default, parts use the same encoding than the message.
|
||||||
|
func SetPartEncoding(e Encoding) PartSetting {
|
||||||
|
return PartSetting(func(p *part) {
|
||||||
|
p.encoding = e
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
type file struct {
|
type file struct {
|
||||||
|
|
|
@ -168,6 +168,36 @@ func TestAlternative(t *testing.T) {
|
||||||
testMessage(t, m, 1, want)
|
testMessage(t, m, 1, want)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestPartSetting(t *testing.T) {
|
||||||
|
m := NewMessage()
|
||||||
|
m.SetHeader("From", "from@example.com")
|
||||||
|
m.SetHeader("To", "to@example.com")
|
||||||
|
m.SetBody("text/plain; format=flowed", "¡Hola, señor!", SetPartEncoding(Unencoded))
|
||||||
|
m.AddAlternative("text/html", "¡<b>Hola</b>, <i>señor</i>!</h1>")
|
||||||
|
|
||||||
|
want := &message{
|
||||||
|
from: "from@example.com",
|
||||||
|
to: []string{"to@example.com"},
|
||||||
|
content: "From: from@example.com\r\n" +
|
||||||
|
"To: to@example.com\r\n" +
|
||||||
|
"Content-Type: multipart/alternative; boundary=_BOUNDARY_1_\r\n" +
|
||||||
|
"\r\n" +
|
||||||
|
"--_BOUNDARY_1_\r\n" +
|
||||||
|
"Content-Type: text/plain; format=flowed; charset=UTF-8\r\n" +
|
||||||
|
"Content-Transfer-Encoding: 8bit\r\n" +
|
||||||
|
"\r\n" +
|
||||||
|
"¡Hola, señor!\r\n" +
|
||||||
|
"--_BOUNDARY_1_\r\n" +
|
||||||
|
"Content-Type: text/html; charset=UTF-8\r\n" +
|
||||||
|
"Content-Transfer-Encoding: quoted-printable\r\n" +
|
||||||
|
"\r\n" +
|
||||||
|
"=C2=A1<b>Hola</b>, <i>se=C3=B1or</i>!</h1>\r\n" +
|
||||||
|
"--_BOUNDARY_1_--\r\n",
|
||||||
|
}
|
||||||
|
|
||||||
|
testMessage(t, m, 1, want)
|
||||||
|
}
|
||||||
|
|
||||||
func TestBodyWriter(t *testing.T) {
|
func TestBodyWriter(t *testing.T) {
|
||||||
m := NewMessage()
|
m := NewMessage()
|
||||||
m.SetHeader("From", "from@example.com")
|
m.SetHeader("From", "from@example.com")
|
||||||
|
|
11
writeto.go
11
writeto.go
|
@ -38,8 +38,7 @@ func (w *messageWriter) writeMessage(m *Message) {
|
||||||
w.openMultipart("alternative")
|
w.openMultipart("alternative")
|
||||||
}
|
}
|
||||||
for _, part := range m.parts {
|
for _, part := range m.parts {
|
||||||
w.writeHeaders(part.header)
|
w.writePart(part, m.charset)
|
||||||
w.writeBody(part.copier, m.encoding)
|
|
||||||
}
|
}
|
||||||
if m.hasAlternativePart() {
|
if m.hasAlternativePart() {
|
||||||
w.closeMultipart()
|
w.closeMultipart()
|
||||||
|
@ -104,6 +103,14 @@ func (w *messageWriter) closeMultipart() {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (w *messageWriter) writePart(p *part, charset string) {
|
||||||
|
w.writeHeaders(map[string][]string{
|
||||||
|
"Content-Type": {p.contentType + "; charset=" + charset},
|
||||||
|
"Content-Transfer-Encoding": {string(p.encoding)},
|
||||||
|
})
|
||||||
|
w.writeBody(p.copier, p.encoding)
|
||||||
|
}
|
||||||
|
|
||||||
func (w *messageWriter) addFiles(files []*file, isAttachment bool) {
|
func (w *messageWriter) addFiles(files []*file, isAttachment bool) {
|
||||||
for _, f := range files {
|
for _, f := range files {
|
||||||
if _, ok := f.Header["Content-Type"]; !ok {
|
if _, ok := f.Header["Content-Type"]; !ok {
|
||||||
|
|
Loading…
Reference in New Issue