Used the new quotedprintable package from the standard library

This commit is contained in:
Alexandre Cesaro 2015-06-27 17:20:04 +02:00
parent 11b919ab49
commit a7fe250544
3 changed files with 30 additions and 94 deletions

View File

@ -5,10 +5,9 @@ import (
"encoding/base64" "encoding/base64"
"io" "io"
"mime/multipart" "mime/multipart"
"mime/quotedprintable"
"net/mail" "net/mail"
"time" "time"
"gopkg.in/alexcesaro/quotedprintable.v2"
) )
// Export converts the message into a net/mail.Message. // Export converts the message into a net/mail.Message.
@ -169,8 +168,9 @@ func (w *messageWriter) writeBody(body []byte, enc Encoding) {
} else if enc == Unencoded { } else if enc == Unencoded {
subWriter.Write(body) subWriter.Write(body)
} else { } else {
writer := quotedprintable.NewEncoder(newQpLineWriter(subWriter)) writer := quotedprintable.NewWriter(subWriter)
writer.Write(body) writer.Write(body)
writer.Close()
} }
} }
@ -207,56 +207,3 @@ func (w *base64LineWriter) Write(p []byte) (int, error) {
return n + len(p), nil return n + len(p), nil
} }
// qpLineWriter limits text encoded in quoted-printable to 76 characters per
// line
type qpLineWriter struct {
w io.Writer
lineLen int
}
func newQpLineWriter(w io.Writer) *qpLineWriter {
return &qpLineWriter{w: w}
}
func (w *qpLineWriter) Write(p []byte) (int, error) {
n := 0
for len(p) > 0 {
// If the text is not over the limit, write everything
if len(p) < maxLineLen-w.lineLen {
w.w.Write(p)
w.lineLen += len(p)
return n + len(p), nil
}
i := bytes.IndexAny(p[:maxLineLen-w.lineLen+2], "\n")
// If there is a newline before the limit, write the end of the line
if i != -1 && (i != maxLineLen-w.lineLen+1 || p[i-1] == '\r') {
w.w.Write(p[:i+1])
p = p[i+1:]
n += i + 1
w.lineLen = 0
continue
}
// Quoted-printable text must not be cut between an equal sign and the
// two following characters
var toWrite int
if maxLineLen-w.lineLen-2 >= 0 && p[maxLineLen-w.lineLen-2] == '=' {
toWrite = maxLineLen - w.lineLen - 2
} else if p[maxLineLen-w.lineLen-1] == '=' {
toWrite = maxLineLen - w.lineLen - 1
} else {
toWrite = maxLineLen - w.lineLen
}
// Insert the newline where it is needed
w.w.Write(p[:toWrite])
w.w.Write([]byte("=\r\n"))
p = p[toWrite:]
n += toWrite
w.lineLen = 0
}
return n, nil
}

View File

@ -11,8 +11,6 @@ import (
"path/filepath" "path/filepath"
"sync" "sync"
"time" "time"
"gopkg.in/alexcesaro/quotedprintable.v2"
) )
// Message represents an email. // Message represents an email.
@ -23,7 +21,7 @@ type Message struct {
embedded []*File embedded []*File
charset string charset string
encoding Encoding encoding Encoding
hEncoder *quotedprintable.HeaderEncoder hEncoder mime.WordEncoder
} }
type header map[string][]string type header map[string][]string
@ -44,13 +42,11 @@ func NewMessage(settings ...MessageSetting) *Message {
msg.applySettings(settings) msg.applySettings(settings)
var e quotedprintable.Encoding
if msg.encoding == Base64 { if msg.encoding == Base64 {
e = quotedprintable.B msg.hEncoder = mime.BEncoding
} else { } else {
e = quotedprintable.Q msg.hEncoder = mime.QEncoding
} }
msg.hEncoder = e.NewHeaderEncoder(msg.charset)
return msg return msg
} }
@ -104,7 +100,7 @@ const (
// SetHeader sets a value to the given header field. // SetHeader sets a value to the given header field.
func (msg *Message) SetHeader(field string, value ...string) { func (msg *Message) SetHeader(field string, value ...string) {
for i := range value { for i := range value {
value[i] = encodeHeader(msg.hEncoder, value[i]) value[i] = msg.encodeHeader(value[i])
} }
msg.header[field] = value msg.header[field] = value
} }
@ -134,16 +130,13 @@ func (msg *Message) FormatAddress(address, name string) string {
buf := getBuffer() buf := getBuffer()
defer putBuffer(buf) defer putBuffer(buf)
if !quotedprintable.NeedsEncoding(name) { enc := msg.encodeHeader(name)
if enc == name {
quote(buf, name) quote(buf, name)
} else if hasSpecials(name) {
buf.WriteString(mime.BEncoding.Encode(msg.charset, name))
} else { } else {
var n string buf.WriteString(enc)
if hasSpecials(name) {
n = encodeHeader(quotedprintable.B.NewHeaderEncoder(msg.charset), name)
} else {
n = encodeHeader(msg.hEncoder, name)
}
buf.WriteString(n)
} }
buf.WriteString(" <") buf.WriteString(" <")
buf.WriteString(address) buf.WriteString(address)
@ -307,12 +300,8 @@ func hasSpecials(text string) bool {
return false return false
} }
func encodeHeader(enc *quotedprintable.HeaderEncoder, value string) string { func (msg *Message) encodeHeader(value string) string {
if !quotedprintable.NeedsEncoding(value) { return msg.hEncoder.Encode(msg.charset, value)
return value
}
return enc.Encode(value)
} }
var bufPool = sync.Pool{ var bufPool = sync.Pool{

View File

@ -38,14 +38,14 @@ func TestMessage(t *testing.T) {
"tobis@example.com", "tobis@example.com",
"cc@example.com", "cc@example.com",
}, },
content: "From: =?UTF-8?Q?Se=C3=B1or_From?= <from@example.com>\r\n" + content: "From: =?UTF-8?q?Se=C3=B1or_From?= <from@example.com>\r\n" +
"To: =?UTF-8?Q?Se=C3=B1or_To?= <to@example.com>, tobis@example.com\r\n" + "To: =?UTF-8?q?Se=C3=B1or_To?= <to@example.com>, tobis@example.com\r\n" +
"Cc: \"A, B\" <cc@example.com>\r\n" + "Cc: \"A, B\" <cc@example.com>\r\n" +
"X-To: =?UTF-8?B?w6AsIGI=?= <ccbis@example.com>\r\n" + "X-To: =?UTF-8?b?w6AsIGI=?= <ccbis@example.com>\r\n" +
"X-Date: Wed, 25 Jun 2014 17:46:00 +0000\r\n" + "X-Date: Wed, 25 Jun 2014 17:46:00 +0000\r\n" +
"X-Date-2: Wed, 25 Jun 2014 17:46:00 +0000\r\n" + "X-Date-2: Wed, 25 Jun 2014 17:46:00 +0000\r\n" +
"X-Headers: Test, =?UTF-8?Q?Caf=C3=A9?=\r\n" + "X-Headers: Test, =?UTF-8?q?Caf=C3=A9?=\r\n" +
"Subject: =?UTF-8?Q?=C2=A1Hola,_se=C3=B1or!?=\r\n" + "Subject: =?UTF-8?q?=C2=A1Hola,_se=C3=B1or!?=\r\n" +
"Content-Type: text/plain; charset=UTF-8\r\n" + "Content-Type: text/plain; charset=UTF-8\r\n" +
"Content-Transfer-Encoding: quoted-printable\r\n" + "Content-Transfer-Encoding: quoted-printable\r\n" +
"\r\n" + "\r\n" +
@ -90,7 +90,7 @@ func TestCustomMessage(t *testing.T) {
to: []string{"to@example.com"}, to: []string{"to@example.com"},
content: "From: from@example.com\r\n" + content: "From: from@example.com\r\n" +
"To: to@example.com\r\n" + "To: to@example.com\r\n" +
"Subject: =?ISO-8859-1?B?Q2Fmw6k=?=\r\n" + "Subject: =?ISO-8859-1?b?Q2Fmw6k=?=\r\n" +
"Content-Type: text/html; charset=ISO-8859-1\r\n" + "Content-Type: text/html; charset=ISO-8859-1\r\n" +
"Content-Transfer-Encoding: base64\r\n" + "Content-Transfer-Encoding: base64\r\n" +
"\r\n" + "\r\n" +
@ -114,7 +114,7 @@ func TestUnencodedMessage(t *testing.T) {
to: []string{"to@example.com"}, to: []string{"to@example.com"},
content: "From: from@example.com\r\n" + content: "From: from@example.com\r\n" +
"To: to@example.com\r\n" + "To: to@example.com\r\n" +
"Subject: =?UTF-8?Q?Caf=C3=A9?=\r\n" + "Subject: =?UTF-8?q?Caf=C3=A9?=\r\n" +
"Content-Type: text/html; charset=UTF-8\r\n" + "Content-Type: text/html; charset=UTF-8\r\n" +
"Content-Transfer-Encoding: 8bit\r\n" + "Content-Transfer-Encoding: 8bit\r\n" +
"\r\n" + "\r\n" +
@ -439,13 +439,13 @@ func TestQpLineLength(t *testing.T) {
msg.SetHeader("From", "from@example.com") msg.SetHeader("From", "from@example.com")
msg.SetHeader("To", "to@example.com") msg.SetHeader("To", "to@example.com")
msg.SetBody("text/plain", msg.SetBody("text/plain",
strings.Repeat("0", 77)+"\r\n"+ strings.Repeat("0", 76)+"\r\n"+
strings.Repeat("0", 76)+"à\r\n"+
strings.Repeat("0", 75)+"à\r\n"+ strings.Repeat("0", 75)+"à\r\n"+
strings.Repeat("0", 74)+"à\r\n"+ strings.Repeat("0", 74)+"à\r\n"+
strings.Repeat("0", 73)+"à\r\n"+ strings.Repeat("0", 73)+"à\r\n"+
strings.Repeat("0", 76)+"\r\n"+ strings.Repeat("0", 72)+"à\r\n"+
strings.Repeat("0", 77)+"\n") strings.Repeat("0", 75)+"\r\n"+
strings.Repeat("0", 76)+"\n")
want := message{ want := message{
from: "from@example.com", from: "from@example.com",
@ -455,13 +455,13 @@ func TestQpLineLength(t *testing.T) {
"Content-Type: text/plain; charset=UTF-8\r\n" + "Content-Type: text/plain; charset=UTF-8\r\n" +
"Content-Transfer-Encoding: quoted-printable\r\n" + "Content-Transfer-Encoding: quoted-printable\r\n" +
"\r\n" + "\r\n" +
strings.Repeat("0", 76) + "=\r\n0\r\n" + strings.Repeat("0", 75) + "=\r\n0\r\n" +
strings.Repeat("0", 76) + "=\r\n=C3=A0\r\n" +
strings.Repeat("0", 75) + "=\r\n=C3=A0\r\n" + strings.Repeat("0", 75) + "=\r\n=C3=A0\r\n" +
strings.Repeat("0", 74) + "=\r\n=C3=A0\r\n" + strings.Repeat("0", 74) + "=\r\n=C3=A0\r\n" +
strings.Repeat("0", 73) + "=C3=\r\n=A0\r\n" + strings.Repeat("0", 73) + "=\r\n=C3=A0\r\n" +
strings.Repeat("0", 76) + "\r\n" + strings.Repeat("0", 72) + "=C3=\r\n=A0\r\n" +
strings.Repeat("0", 76) + "=\r\n0\n", strings.Repeat("0", 75) + "\r\n" +
strings.Repeat("0", 75) + "=\r\n0\r\n",
} }
testMessage(t, msg, 0, want) testMessage(t, msg, 0, want)