2014-10-15 23:47:07 +08:00
|
|
|
package gomail
|
|
|
|
|
|
|
|
import (
|
|
|
|
"encoding/base64"
|
|
|
|
"net/smtp"
|
|
|
|
"path/filepath"
|
|
|
|
"regexp"
|
|
|
|
"strconv"
|
|
|
|
"strings"
|
|
|
|
"testing"
|
|
|
|
"time"
|
|
|
|
)
|
|
|
|
|
|
|
|
type message struct {
|
|
|
|
from string
|
|
|
|
to []string
|
|
|
|
content string
|
|
|
|
}
|
|
|
|
|
|
|
|
func TestMessage(t *testing.T) {
|
|
|
|
msg := NewMessage()
|
|
|
|
msg.SetAddressHeader("From", "from@example.com", "Señor From")
|
|
|
|
msg.SetHeader("To", msg.FormatAddress("to@example.com", "Señor To"), "tobis@example.com")
|
|
|
|
msg.SetDateHeader("X-Date", stubNow())
|
|
|
|
msg.SetHeader("X-Date-2", msg.FormatDate(stubNow()))
|
|
|
|
msg.SetHeader("Subject", "¡Hola, señor!")
|
|
|
|
msg.SetHeaders(map[string][]string{
|
|
|
|
"X-Headers": {"Test", "Café"},
|
|
|
|
})
|
|
|
|
msg.SetBody("text/plain", "¡Hola, señor!")
|
|
|
|
|
|
|
|
want := message{
|
2014-10-24 00:46:32 +08:00
|
|
|
from: "from@example.com",
|
2014-10-15 23:47:07 +08:00
|
|
|
to: []string{
|
2014-10-24 00:46:32 +08:00
|
|
|
"to@example.com",
|
2014-10-15 23:47:07 +08:00
|
|
|
"tobis@example.com",
|
|
|
|
},
|
|
|
|
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" +
|
2015-02-28 23:46:44 +08:00
|
|
|
"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" +
|
2014-10-15 23:47:07 +08:00
|
|
|
"X-Headers: Test, =?UTF-8?Q?Caf=C3=A9?=\r\n" +
|
|
|
|
"Subject: =?UTF-8?Q?=C2=A1Hola,_se=C3=B1or!?=\r\n" +
|
|
|
|
"Content-Type: text/plain; charset=UTF-8\r\n" +
|
|
|
|
"Content-Transfer-Encoding: quoted-printable\r\n" +
|
|
|
|
"\r\n" +
|
|
|
|
"=C2=A1Hola, se=C3=B1or!",
|
|
|
|
}
|
|
|
|
|
|
|
|
testMessage(t, msg, 0, want)
|
|
|
|
}
|
|
|
|
|
|
|
|
func TestBodyWriter(t *testing.T) {
|
|
|
|
msg := NewMessage()
|
|
|
|
msg.SetHeader("From", "from@example.com")
|
|
|
|
msg.SetHeader("To", "to@example.com")
|
|
|
|
w := msg.GetBodyWriter("text/plain")
|
|
|
|
w.Write([]byte("Test message"))
|
|
|
|
|
|
|
|
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: text/plain; charset=UTF-8\r\n" +
|
|
|
|
"Content-Transfer-Encoding: quoted-printable\r\n" +
|
|
|
|
"\r\n" +
|
|
|
|
"Test message",
|
|
|
|
}
|
|
|
|
|
|
|
|
testMessage(t, msg, 0, want)
|
|
|
|
}
|
|
|
|
|
|
|
|
func TestCustomMessage(t *testing.T) {
|
|
|
|
msg := NewMessage(SetCharset("ISO-8859-1"), SetEncoding(Base64))
|
|
|
|
msg.SetHeaders(map[string][]string{
|
|
|
|
"From": {"from@example.com"},
|
|
|
|
"To": {"to@example.com"},
|
|
|
|
"Subject": {"Café"},
|
|
|
|
})
|
|
|
|
msg.SetBody("text/html", "¡Hola, señor!")
|
|
|
|
|
|
|
|
want := message{
|
|
|
|
from: "from@example.com",
|
|
|
|
to: []string{"to@example.com"},
|
|
|
|
content: "From: from@example.com\r\n" +
|
|
|
|
"To: to@example.com\r\n" +
|
|
|
|
"Subject: =?ISO-8859-1?B?Q2Fmw6k=?=\r\n" +
|
|
|
|
"Content-Type: text/html; charset=ISO-8859-1\r\n" +
|
|
|
|
"Content-Transfer-Encoding: base64\r\n" +
|
|
|
|
"\r\n" +
|
|
|
|
"wqFIb2xhLCBzZcOxb3Ih",
|
|
|
|
}
|
|
|
|
|
|
|
|
testMessage(t, msg, 0, want)
|
|
|
|
}
|
|
|
|
|
2014-10-28 15:35:50 +08:00
|
|
|
func TestUnencodedMessage(t *testing.T) {
|
|
|
|
msg := NewMessage(SetEncoding(Unencoded))
|
|
|
|
msg.SetHeaders(map[string][]string{
|
|
|
|
"From": {"from@example.com"},
|
|
|
|
"To": {"to@example.com"},
|
|
|
|
"Subject": {"Café"},
|
|
|
|
})
|
|
|
|
msg.SetBody("text/html", "¡Hola, señor!")
|
|
|
|
|
|
|
|
want := message{
|
|
|
|
from: "from@example.com",
|
|
|
|
to: []string{"to@example.com"},
|
|
|
|
content: "From: from@example.com\r\n" +
|
|
|
|
"To: to@example.com\r\n" +
|
|
|
|
"Subject: =?UTF-8?Q?Caf=C3=A9?=\r\n" +
|
|
|
|
"Content-Type: text/html; charset=UTF-8\r\n" +
|
|
|
|
"Content-Transfer-Encoding: 8bit\r\n" +
|
|
|
|
"\r\n" +
|
|
|
|
"¡Hola, señor!",
|
|
|
|
}
|
|
|
|
|
|
|
|
testMessage(t, msg, 0, want)
|
|
|
|
}
|
|
|
|
|
2014-10-15 23:47:07 +08:00
|
|
|
func TestRecipients(t *testing.T) {
|
|
|
|
msg := NewMessage()
|
|
|
|
msg.SetHeaders(map[string][]string{
|
|
|
|
"From": {"from@example.com"},
|
|
|
|
"To": {"to@example.com"},
|
|
|
|
"Cc": {"cc@example.com"},
|
|
|
|
"Bcc": {"bcc1@example.com", "bcc2@example.com"},
|
|
|
|
"Subject": {"Hello!"},
|
|
|
|
})
|
|
|
|
msg.SetBody("text/plain", "Test message")
|
|
|
|
|
|
|
|
want := message{
|
|
|
|
from: "from@example.com",
|
|
|
|
to: []string{"to@example.com", "cc@example.com"},
|
|
|
|
content: "From: from@example.com\r\n" +
|
|
|
|
"To: to@example.com\r\n" +
|
|
|
|
"Cc: cc@example.com\r\n" +
|
|
|
|
"Subject: Hello!\r\n" +
|
|
|
|
"Content-Type: text/plain; charset=UTF-8\r\n" +
|
|
|
|
"Content-Transfer-Encoding: quoted-printable\r\n" +
|
|
|
|
"\r\n" +
|
|
|
|
"Test message",
|
|
|
|
}
|
|
|
|
wantBcc1 := message{
|
|
|
|
from: "from@example.com",
|
|
|
|
to: []string{"bcc1@example.com"},
|
|
|
|
content: "From: from@example.com\r\n" +
|
|
|
|
"To: to@example.com\r\n" +
|
|
|
|
"Cc: cc@example.com\r\n" +
|
|
|
|
"Bcc: bcc1@example.com\r\n" +
|
|
|
|
"Subject: Hello!\r\n" +
|
|
|
|
"Content-Type: text/plain; charset=UTF-8\r\n" +
|
|
|
|
"Content-Transfer-Encoding: quoted-printable\r\n" +
|
|
|
|
"\r\n" +
|
|
|
|
"Test message",
|
|
|
|
}
|
|
|
|
wantBcc2 := message{
|
|
|
|
from: "from@example.com",
|
|
|
|
to: []string{"bcc2@example.com"},
|
|
|
|
content: "From: from@example.com\r\n" +
|
|
|
|
"To: to@example.com\r\n" +
|
|
|
|
"Cc: cc@example.com\r\n" +
|
|
|
|
"Bcc: bcc2@example.com\r\n" +
|
|
|
|
"Subject: Hello!\r\n" +
|
|
|
|
"Content-Type: text/plain; charset=UTF-8\r\n" +
|
|
|
|
"Content-Transfer-Encoding: quoted-printable\r\n" +
|
|
|
|
"\r\n" +
|
|
|
|
"Test message",
|
|
|
|
}
|
|
|
|
|
|
|
|
testMessage(t, msg, 0, want, wantBcc1, wantBcc2)
|
|
|
|
}
|
|
|
|
|
|
|
|
func TestAlternative(t *testing.T) {
|
|
|
|
msg := NewMessage()
|
|
|
|
msg.SetHeader("From", "from@example.com")
|
|
|
|
msg.SetHeader("To", "to@example.com")
|
|
|
|
msg.SetBody("text/plain", "¡Hola, señor!")
|
|
|
|
msg.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; charset=UTF-8\r\n" +
|
|
|
|
"Content-Transfer-Encoding: quoted-printable\r\n" +
|
|
|
|
"\r\n" +
|
|
|
|
"=C2=A1Hola, se=C3=B1or!\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, msg, 1, want)
|
|
|
|
}
|
|
|
|
|
|
|
|
func TestAttachmentOnly(t *testing.T) {
|
|
|
|
readFile = func(filename string) ([]byte, error) {
|
|
|
|
return []byte("Content of " + filepath.Base(filename)), nil
|
|
|
|
}
|
|
|
|
|
|
|
|
msg := NewMessage()
|
|
|
|
msg.SetHeader("From", "from@example.com")
|
|
|
|
msg.SetHeader("To", "to@example.com")
|
|
|
|
f, err := OpenFile("/tmp/test.pdf")
|
|
|
|
if err != nil {
|
|
|
|
panic(err)
|
|
|
|
}
|
|
|
|
msg.Attach(f)
|
|
|
|
|
|
|
|
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: application/pdf; name=\"test.pdf\"\r\n" +
|
|
|
|
"Content-Disposition: attachment; filename=\"test.pdf\"\r\n" +
|
|
|
|
"Content-Transfer-Encoding: base64\r\n" +
|
|
|
|
"\r\n" +
|
|
|
|
base64.StdEncoding.EncodeToString([]byte("Content of test.pdf")),
|
|
|
|
}
|
|
|
|
|
|
|
|
testMessage(t, msg, 0, want)
|
|
|
|
}
|
|
|
|
|
|
|
|
func TestAttachment(t *testing.T) {
|
|
|
|
msg := NewMessage()
|
|
|
|
msg.SetHeader("From", "from@example.com")
|
|
|
|
msg.SetHeader("To", "to@example.com")
|
|
|
|
msg.SetBody("text/plain", "Test")
|
|
|
|
msg.Attach(CreateFile("test.pdf", []byte("Content")))
|
|
|
|
|
|
|
|
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/mixed; boundary=_BOUNDARY_1_\r\n" +
|
|
|
|
"\r\n" +
|
|
|
|
"--_BOUNDARY_1_\r\n" +
|
|
|
|
"Content-Type: text/plain; charset=UTF-8\r\n" +
|
|
|
|
"Content-Transfer-Encoding: quoted-printable\r\n" +
|
|
|
|
"\r\n" +
|
|
|
|
"Test\r\n" +
|
|
|
|
"--_BOUNDARY_1_\r\n" +
|
|
|
|
"Content-Type: application/pdf; name=\"test.pdf\"\r\n" +
|
|
|
|
"Content-Disposition: attachment; filename=\"test.pdf\"\r\n" +
|
|
|
|
"Content-Transfer-Encoding: base64\r\n" +
|
|
|
|
"\r\n" +
|
|
|
|
base64.StdEncoding.EncodeToString([]byte("Content")) + "\r\n" +
|
|
|
|
"--_BOUNDARY_1_--\r\n",
|
|
|
|
}
|
|
|
|
|
|
|
|
testMessage(t, msg, 1, want)
|
|
|
|
}
|
|
|
|
|
|
|
|
func TestAttachmentsOnly(t *testing.T) {
|
|
|
|
msg := NewMessage()
|
|
|
|
msg.SetHeader("From", "from@example.com")
|
|
|
|
msg.SetHeader("To", "to@example.com")
|
|
|
|
msg.Attach(CreateFile("test.pdf", []byte("Content 1")))
|
|
|
|
msg.Attach(CreateFile("test.zip", []byte("Content 2")))
|
|
|
|
|
|
|
|
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/mixed; boundary=_BOUNDARY_1_\r\n" +
|
|
|
|
"\r\n" +
|
|
|
|
"--_BOUNDARY_1_\r\n" +
|
|
|
|
"Content-Type: application/pdf; name=\"test.pdf\"\r\n" +
|
|
|
|
"Content-Disposition: attachment; filename=\"test.pdf\"\r\n" +
|
|
|
|
"Content-Transfer-Encoding: base64\r\n" +
|
|
|
|
"\r\n" +
|
|
|
|
base64.StdEncoding.EncodeToString([]byte("Content 1")) + "\r\n" +
|
|
|
|
"--_BOUNDARY_1_\r\n" +
|
|
|
|
"Content-Type: application/zip; name=\"test.zip\"\r\n" +
|
|
|
|
"Content-Disposition: attachment; filename=\"test.zip\"\r\n" +
|
|
|
|
"Content-Transfer-Encoding: base64\r\n" +
|
|
|
|
"\r\n" +
|
|
|
|
base64.StdEncoding.EncodeToString([]byte("Content 2")) + "\r\n" +
|
|
|
|
"--_BOUNDARY_1_--\r\n",
|
|
|
|
}
|
|
|
|
|
|
|
|
testMessage(t, msg, 1, want)
|
|
|
|
}
|
|
|
|
|
|
|
|
func TestAttachments(t *testing.T) {
|
|
|
|
msg := NewMessage()
|
|
|
|
msg.SetHeader("From", "from@example.com")
|
|
|
|
msg.SetHeader("To", "to@example.com")
|
|
|
|
msg.SetBody("text/plain", "Test")
|
|
|
|
msg.Attach(CreateFile("test.pdf", []byte("Content 1")))
|
|
|
|
msg.Attach(CreateFile("test.zip", []byte("Content 2")))
|
|
|
|
|
|
|
|
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/mixed; boundary=_BOUNDARY_1_\r\n" +
|
|
|
|
"\r\n" +
|
|
|
|
"--_BOUNDARY_1_\r\n" +
|
|
|
|
"Content-Type: text/plain; charset=UTF-8\r\n" +
|
|
|
|
"Content-Transfer-Encoding: quoted-printable\r\n" +
|
|
|
|
"\r\n" +
|
|
|
|
"Test\r\n" +
|
|
|
|
"--_BOUNDARY_1_\r\n" +
|
|
|
|
"Content-Type: application/pdf; name=\"test.pdf\"\r\n" +
|
|
|
|
"Content-Disposition: attachment; filename=\"test.pdf\"\r\n" +
|
|
|
|
"Content-Transfer-Encoding: base64\r\n" +
|
|
|
|
"\r\n" +
|
|
|
|
base64.StdEncoding.EncodeToString([]byte("Content 1")) + "\r\n" +
|
|
|
|
"--_BOUNDARY_1_\r\n" +
|
|
|
|
"Content-Type: application/zip; name=\"test.zip\"\r\n" +
|
|
|
|
"Content-Disposition: attachment; filename=\"test.zip\"\r\n" +
|
|
|
|
"Content-Transfer-Encoding: base64\r\n" +
|
|
|
|
"\r\n" +
|
|
|
|
base64.StdEncoding.EncodeToString([]byte("Content 2")) + "\r\n" +
|
|
|
|
"--_BOUNDARY_1_--\r\n",
|
|
|
|
}
|
|
|
|
|
|
|
|
testMessage(t, msg, 1, want)
|
|
|
|
}
|
|
|
|
|
|
|
|
func TestEmbedded(t *testing.T) {
|
|
|
|
msg := NewMessage()
|
|
|
|
msg.SetHeader("From", "from@example.com")
|
|
|
|
msg.SetHeader("To", "to@example.com")
|
2015-02-19 03:46:05 +08:00
|
|
|
f := CreateFile("image1.jpg", []byte("Content 1"))
|
|
|
|
f.ContentID = "test-content-id"
|
|
|
|
msg.Embed(f)
|
2014-12-23 18:23:01 +08:00
|
|
|
msg.Embed(CreateFile("image2.jpg", []byte("Content 2")))
|
2014-10-15 23:47:07 +08:00
|
|
|
msg.SetBody("text/plain", "Test")
|
|
|
|
|
|
|
|
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/related; boundary=_BOUNDARY_1_\r\n" +
|
|
|
|
"\r\n" +
|
|
|
|
"--_BOUNDARY_1_\r\n" +
|
|
|
|
"Content-Type: text/plain; charset=UTF-8\r\n" +
|
|
|
|
"Content-Transfer-Encoding: quoted-printable\r\n" +
|
|
|
|
"\r\n" +
|
|
|
|
"Test\r\n" +
|
|
|
|
"--_BOUNDARY_1_\r\n" +
|
2014-12-23 18:23:01 +08:00
|
|
|
"Content-Type: image/jpeg; name=\"image1.jpg\"\r\n" +
|
|
|
|
"Content-Disposition: inline; filename=\"image1.jpg\"\r\n" +
|
2015-02-19 03:46:05 +08:00
|
|
|
"Content-ID: <test-content-id>\r\n" +
|
2014-10-15 23:47:07 +08:00
|
|
|
"Content-Transfer-Encoding: base64\r\n" +
|
|
|
|
"\r\n" +
|
2014-12-23 18:23:01 +08:00
|
|
|
base64.StdEncoding.EncodeToString([]byte("Content 1")) + "\r\n" +
|
|
|
|
"--_BOUNDARY_1_\r\n" +
|
|
|
|
"Content-Type: image/jpeg; name=\"image2.jpg\"\r\n" +
|
|
|
|
"Content-Disposition: inline; filename=\"image2.jpg\"\r\n" +
|
|
|
|
"Content-ID: <image2.jpg>\r\n" +
|
|
|
|
"Content-Transfer-Encoding: base64\r\n" +
|
|
|
|
"\r\n" +
|
|
|
|
base64.StdEncoding.EncodeToString([]byte("Content 2")) + "\r\n" +
|
2014-10-15 23:47:07 +08:00
|
|
|
"--_BOUNDARY_1_--\r\n",
|
|
|
|
}
|
|
|
|
|
|
|
|
testMessage(t, msg, 1, want)
|
|
|
|
}
|
|
|
|
|
|
|
|
func TestFullMessage(t *testing.T) {
|
|
|
|
msg := NewMessage()
|
|
|
|
msg.SetHeader("From", "from@example.com")
|
|
|
|
msg.SetHeader("To", "to@example.com")
|
|
|
|
msg.SetBody("text/plain", "¡Hola, señor!")
|
|
|
|
msg.AddAlternative("text/html", "¡<b>Hola</b>, <i>señor</i>!</h1>")
|
|
|
|
msg.Attach(CreateFile("test.pdf", []byte("Content 1")))
|
|
|
|
msg.Embed(CreateFile("image.jpg", []byte("Content 2")))
|
|
|
|
|
|
|
|
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/mixed; boundary=_BOUNDARY_1_\r\n" +
|
|
|
|
"\r\n" +
|
|
|
|
"--_BOUNDARY_1_\r\n" +
|
|
|
|
"Content-Type: multipart/related; boundary=_BOUNDARY_2_\r\n" +
|
|
|
|
"\r\n" +
|
|
|
|
"--_BOUNDARY_2_\r\n" +
|
|
|
|
"Content-Type: multipart/alternative; boundary=_BOUNDARY_3_\r\n" +
|
|
|
|
"\r\n" +
|
|
|
|
"--_BOUNDARY_3_\r\n" +
|
|
|
|
"Content-Type: text/plain; charset=UTF-8\r\n" +
|
|
|
|
"Content-Transfer-Encoding: quoted-printable\r\n" +
|
|
|
|
"\r\n" +
|
|
|
|
"=C2=A1Hola, se=C3=B1or!\r\n" +
|
|
|
|
"--_BOUNDARY_3_\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_3_--\r\n" +
|
|
|
|
"\r\n" +
|
|
|
|
"--_BOUNDARY_2_\r\n" +
|
|
|
|
"Content-Type: image/jpeg; name=\"image.jpg\"\r\n" +
|
|
|
|
"Content-Disposition: inline; filename=\"image.jpg\"\r\n" +
|
|
|
|
"Content-ID: <image.jpg>\r\n" +
|
|
|
|
"Content-Transfer-Encoding: base64\r\n" +
|
|
|
|
"\r\n" +
|
|
|
|
base64.StdEncoding.EncodeToString([]byte("Content 2")) + "\r\n" +
|
|
|
|
"--_BOUNDARY_2_--\r\n" +
|
|
|
|
"\r\n" +
|
|
|
|
"--_BOUNDARY_1_\r\n" +
|
|
|
|
"Content-Type: application/pdf; name=\"test.pdf\"\r\n" +
|
|
|
|
"Content-Disposition: attachment; filename=\"test.pdf\"\r\n" +
|
|
|
|
"Content-Transfer-Encoding: base64\r\n" +
|
|
|
|
"\r\n" +
|
|
|
|
base64.StdEncoding.EncodeToString([]byte("Content 1")) + "\r\n" +
|
|
|
|
"--_BOUNDARY_1_--\r\n",
|
|
|
|
}
|
|
|
|
|
|
|
|
testMessage(t, msg, 3, want)
|
|
|
|
}
|
|
|
|
|
|
|
|
func TestQpLineLength(t *testing.T) {
|
|
|
|
msg := NewMessage()
|
|
|
|
msg.SetHeader("From", "from@example.com")
|
|
|
|
msg.SetHeader("To", "to@example.com")
|
|
|
|
msg.SetBody("text/plain",
|
2015-01-20 20:15:52 +08:00
|
|
|
strings.Repeat("0", 77)+"\r\n"+
|
2014-10-15 23:47:07 +08:00
|
|
|
strings.Repeat("0", 76)+"à\r\n"+
|
|
|
|
strings.Repeat("0", 75)+"à\r\n"+
|
2015-01-20 20:15:52 +08:00
|
|
|
strings.Repeat("0", 74)+"à\r\n"+
|
|
|
|
strings.Repeat("0", 73)+"à\r\n"+
|
|
|
|
strings.Repeat("0", 76)+"\r\n"+
|
|
|
|
strings.Repeat("0", 77)+"\n")
|
2014-10-15 23:47:07 +08:00
|
|
|
|
|
|
|
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: text/plain; charset=UTF-8\r\n" +
|
|
|
|
"Content-Transfer-Encoding: quoted-printable\r\n" +
|
|
|
|
"\r\n" +
|
2015-01-20 20:15:52 +08:00
|
|
|
strings.Repeat("0", 76) + "=\r\n0\r\n" +
|
2014-10-15 23:47:07 +08:00
|
|
|
strings.Repeat("0", 76) + "=\r\n=C3=A0\r\n" +
|
2015-01-20 20:15:52 +08:00
|
|
|
strings.Repeat("0", 75) + "=\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", 76) + "\r\n" +
|
|
|
|
strings.Repeat("0", 76) + "=\r\n0\n",
|
2014-10-15 23:47:07 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
testMessage(t, msg, 0, want)
|
|
|
|
}
|
|
|
|
|
|
|
|
func TestBase64LineLength(t *testing.T) {
|
|
|
|
msg := NewMessage(SetCharset("UTF-8"), SetEncoding(Base64))
|
|
|
|
msg.SetHeader("From", "from@example.com")
|
|
|
|
msg.SetHeader("To", "to@example.com")
|
|
|
|
msg.SetBody("text/plain", strings.Repeat("0", 58))
|
|
|
|
|
|
|
|
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: text/plain; charset=UTF-8\r\n" +
|
|
|
|
"Content-Transfer-Encoding: base64\r\n" +
|
|
|
|
"\r\n" +
|
2015-01-20 20:15:52 +08:00
|
|
|
strings.Repeat("MDAw", 19) + "\r\nMA==",
|
2014-10-15 23:47:07 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
testMessage(t, msg, 0, want)
|
|
|
|
}
|
|
|
|
|
|
|
|
func testMessage(t *testing.T, msg *Message, bCount int, emails ...message) {
|
|
|
|
now = stubNow
|
2014-10-22 23:47:24 +08:00
|
|
|
mailer := NewMailer("host", "username", "password", 587, SetSendMail(stubSendMail(t, bCount, emails...)))
|
2014-10-15 23:47:07 +08:00
|
|
|
|
|
|
|
err := mailer.Send(msg)
|
|
|
|
if err != nil {
|
|
|
|
t.Error(err)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
func stubNow() time.Time {
|
|
|
|
return time.Date(2014, 06, 25, 17, 46, 0, 0, time.UTC)
|
|
|
|
}
|
|
|
|
|
|
|
|
func stubSendMail(t *testing.T, bCount int, emails ...message) SendMailFunc {
|
|
|
|
i := 0
|
|
|
|
return func(addr string, a smtp.Auth, from string, to []string, msg []byte) error {
|
|
|
|
if i > len(emails) {
|
|
|
|
t.Fatalf("Only %d mails should be sent", len(emails))
|
|
|
|
}
|
|
|
|
want := emails[i]
|
|
|
|
|
2014-10-22 23:47:24 +08:00
|
|
|
if addr != "host:587" {
|
|
|
|
t.Fatalf("Invalid address, got %q, want host:587", addr)
|
2014-10-15 23:47:07 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
if from != want.from {
|
|
|
|
t.Fatalf("Invalid from, got %q, want %q", from, want.from)
|
|
|
|
}
|
|
|
|
|
|
|
|
if len(to) != len(want.to) {
|
|
|
|
t.Fatalf("Invalid recipient count, \ngot %d: %q\nwant %d: %q",
|
|
|
|
len(to), to,
|
|
|
|
len(want.to), want.to,
|
|
|
|
)
|
|
|
|
}
|
|
|
|
for i := range want.to {
|
|
|
|
if to[i] != want.to[i] {
|
|
|
|
t.Fatalf("Invalid recipient, got %q, want %q",
|
|
|
|
to[i], want.to[i],
|
|
|
|
)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
got := string(msg)
|
|
|
|
wantMsg := string("Mime-Version: 1.0\r\n" +
|
2015-02-28 23:46:44 +08:00
|
|
|
"Date: Wed, 25 Jun 2014 17:46:00 +0000\r\n" +
|
2014-10-15 23:47:07 +08:00
|
|
|
want.content)
|
|
|
|
if bCount > 0 {
|
|
|
|
boundaries := getBoundaries(t, bCount, got)
|
|
|
|
for i, b := range boundaries {
|
|
|
|
wantMsg = strings.Replace(wantMsg, "_BOUNDARY_"+strconv.Itoa(i+1)+"_", b, -1)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
i++
|
|
|
|
|
|
|
|
compareBodies(t, got, wantMsg)
|
|
|
|
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
func compareBodies(t *testing.T, got, want string) {
|
|
|
|
// We cannot do a simple comparison since the ordering of headers' fields
|
|
|
|
// is random.
|
|
|
|
gotLines := strings.Split(got, "\r\n")
|
|
|
|
wantLines := strings.Split(want, "\r\n")
|
|
|
|
|
|
|
|
// We only test for too many lines, missing lines are tested after
|
|
|
|
if len(gotLines) > len(wantLines) {
|
|
|
|
t.Fatalf("Message has too many lines, \ngot %d:\n%s\nwant %d:\n%s", len(gotLines), got, len(wantLines), want)
|
|
|
|
}
|
|
|
|
|
|
|
|
isInHeader := true
|
|
|
|
headerStart := 0
|
|
|
|
for i, line := range wantLines {
|
|
|
|
if line == gotLines[i] {
|
|
|
|
if line == "" {
|
|
|
|
isInHeader = false
|
|
|
|
} else if !isInHeader && len(line) > 2 && line[:2] == "--" {
|
|
|
|
isInHeader = true
|
|
|
|
headerStart = i + 1
|
|
|
|
}
|
|
|
|
continue
|
|
|
|
}
|
|
|
|
|
|
|
|
if !isInHeader {
|
|
|
|
missingLine(t, line, got, want)
|
|
|
|
}
|
|
|
|
|
|
|
|
isMissing := true
|
|
|
|
for j := headerStart; j < len(gotLines); j++ {
|
|
|
|
if gotLines[j] == "" {
|
|
|
|
break
|
|
|
|
}
|
|
|
|
if gotLines[j] == line {
|
|
|
|
isMissing = false
|
|
|
|
break
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if isMissing {
|
|
|
|
missingLine(t, line, got, want)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
func missingLine(t *testing.T, line, got, want string) {
|
|
|
|
t.Fatalf("Missing line %q\ngot:\n%s\nwant:\n%s", line, got, want)
|
|
|
|
}
|
|
|
|
|
|
|
|
func getBoundaries(t *testing.T, count int, msg string) []string {
|
|
|
|
if matches := boundaryRegExp.FindAllStringSubmatch(msg, count); matches != nil {
|
|
|
|
boundaries := make([]string, count)
|
|
|
|
for i, match := range matches {
|
|
|
|
boundaries[i] = match[1]
|
|
|
|
}
|
|
|
|
return boundaries
|
|
|
|
}
|
|
|
|
|
|
|
|
t.Fatal("Boundary not found in body")
|
|
|
|
return []string{""}
|
|
|
|
}
|
|
|
|
|
|
|
|
var boundaryRegExp = regexp.MustCompile("boundary=(\\w+)")
|
|
|
|
|
|
|
|
func BenchmarkFull(b *testing.B) {
|
|
|
|
emptyFunc := func(addr string, a smtp.Auth, from string, to []string, msg []byte) error {
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
|
|
|
for n := 0; n < b.N; n++ {
|
|
|
|
msg := NewMessage()
|
|
|
|
msg.SetAddressHeader("From", "from@example.com", "Señor From")
|
|
|
|
msg.SetHeaders(map[string][]string{
|
|
|
|
"To": {"to@example.com"},
|
|
|
|
"Cc": {"cc@example.com"},
|
|
|
|
"Bcc": {"bcc1@example.com", "bcc2@example.com"},
|
|
|
|
"Subject": {"¡Hola, señor!"},
|
|
|
|
})
|
|
|
|
msg.SetBody("text/plain", "¡Hola, señor!")
|
|
|
|
msg.AddAlternative("text/html", "<p>¡Hola, señor!</p>")
|
|
|
|
msg.Attach(CreateFile("benchmark.txt", []byte("Benchmark")))
|
|
|
|
msg.Embed(CreateFile("benchmark.jpg", []byte("Benchmark")))
|
|
|
|
|
2014-10-22 23:47:24 +08:00
|
|
|
mailer := NewMailer("host", "username", "password", 587, SetSendMail(emptyFunc))
|
2014-10-15 23:47:07 +08:00
|
|
|
if err := mailer.Send(msg); err != nil {
|
|
|
|
panic(err)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|