diff --git a/CHANGELOG.md b/CHANGELOG.md
index ec301b7..333d120 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -4,9 +4,8 @@ This project adheres to [Semantic Versioning](http://semver.org/).
## [2.0.0] - unreleased
- Mailer has been removed. It has been replaced by Dialer and Sender.
-- `CreateFile` has been removed and `OpenFile` has been renamed into `NewFile`.
-- `File` fields changed. The `File.Header` field replaces `File.MimeType`
-and `File.ContentID`.
+- `File` type and the `CreateFile` and `OpenFile` functions have been removed.
+- `Message.Attach` and `Message.Embed` have a new signature.
- `Message.GetBodyWriter` has been removed. Use `Message.AddAlternativeWriter`
instead.
- `Message.Export` has been removed. `Message.WriteTo` can be used instead.
diff --git a/example_test.go b/example_test.go
index cb4b8ee..a09ca73 100644
--- a/example_test.go
+++ b/example_test.go
@@ -15,7 +15,7 @@ func Example() {
m.SetAddressHeader("Cc", "dan@example.com", "Dan")
m.SetHeader("Subject", "Hello!")
m.SetBody("text/html", "Hello Bob and Cora!")
- m.Attach(gomail.NewFile("/home/Alex/lolcat.jpg"))
+ m.Attach("/home/Alex/lolcat.jpg")
d := gomail.NewPlainDialer("smtp.example.com", "user", "123456", 587)
diff --git a/message.go b/message.go
index c05b49f..2a16c56 100644
--- a/message.go
+++ b/message.go
@@ -12,8 +12,8 @@ import (
type Message struct {
header header
parts []part
- attachments []*File
- embedded []*File
+ attachments []*file
+ embedded []*file
charset string
encoding Encoding
hEncoder mimeEncoder
@@ -106,12 +106,20 @@ const (
// SetHeader sets a value to the given header field.
func (m *Message) SetHeader(field string, value ...string) {
- for i := range value {
- value[i] = m.encodeHeader(value[i])
- }
+ m.encodeHeader(value)
m.header[field] = value
}
+func (m *Message) encodeHeader(values []string) {
+ for i := range values {
+ values[i] = m.encodeString(values[i])
+ }
+}
+
+func (m *Message) encodeString(value string) string {
+ return m.hEncoder.Encode(m.charset, value)
+}
+
// SetHeaders sets the message headers.
//
// Example:
@@ -134,7 +142,7 @@ func (m *Message) SetAddressHeader(field, address, name string) {
// FormatAddress formats an address and a name as a valid RFC 5322 address.
func (m *Message) FormatAddress(address, name string) string {
- enc := m.encodeHeader(name)
+ enc := m.encodeString(name)
if enc == name {
m.buf.WriteByte('"')
for i := 0; i < len(name); i++ {
@@ -170,10 +178,6 @@ func hasSpecials(text string) bool {
return false
}
-func (m *Message) encodeHeader(value string) string {
- return m.hEncoder.Encode(m.charset, value)
-}
-
// SetDateHeader sets a date to the given header field.
func (m *Message) SetDateHeader(field string, date time.Time) {
m.header[field] = []string{m.FormatDate(date)}
@@ -254,27 +258,61 @@ func (m *Message) getPartHeader(contentType string) header {
}
}
-// A File represents a file that can be attached or embedded in an email.
-type File struct {
- // Name represents the base name of the file. If the file is attached to the
- // message it is the name of the attachment.
- Name string
- // Header represents the MIME header of the message part that contains the
- // file content. It is empty by default. Mandatory headers are automatically
- // added if they are not sent when sending the email.
- Header map[string][]string
- // Copier is a function run when the message is sent. It should copy the
- // content of the file to w.
- Copier func(w io.Writer) error
+type file struct {
+ Name string
+ Header map[string][]string
+ CopyFunc func(w io.Writer) error
}
-// NewFile creates a File from the given filename.
-func NewFile(filename string) *File {
- return &File{
- Name: filepath.Base(filename),
+func (f *file) setHeader(field, value string) {
+ f.Header[field] = []string{value}
+}
+
+// A FileSetting can be used as an argument in Message.Attach or Message.Embed.
+type FileSetting func(*file)
+
+// SetHeader is a file setting to set the MIME header of the message part that
+// contains the file content.
+//
+// Mandatory headers are automatically added if they are not set when sending
+// the email.
+//
+// Example:
+//
+// h := map[string][]string{"Content-ID": {""}}
+// m.Attach("foo.jpg", gomail.SetHeader(h))
+func SetHeader(h map[string][]string) FileSetting {
+ return func(f *file) {
+ for k, v := range h {
+ f.Header[k] = v
+ }
+ }
+}
+
+// SetCopyFunc is a file setting to replace the function that runs when the
+// message is sent.
+// It should copy the content of the file to the io.Writer.
+// The default copy function opens the file with the given filename, and copy
+// its content to the io.Writer.
+//
+// Example:
+//
+// m.Attach("foo.txt", gomail.SetCopyFunc(func(w io.Writer) error {
+// _, err := w.Write([]byte("Content of foo.txt"))
+// return err
+// }))
+func SetCopyFunc(f func(io.Writer) error) FileSetting {
+ return func(fi *file) {
+ fi.CopyFunc = f
+ }
+}
+
+func (m *Message) appendFile(list []*file, name string, settings []FileSetting) []*file {
+ f := &file{
+ Name: filepath.Base(name),
Header: make(map[string][]string),
- Copier: func(w io.Writer) error {
- h, err := os.Open(filename)
+ CopyFunc: func(w io.Writer) error {
+ h, err := os.Open(name)
if err != nil {
return err
}
@@ -285,35 +323,33 @@ func NewFile(filename string) *File {
return h.Close()
},
}
-}
-func (f *File) setHeader(field string, value ...string) {
- f.Header[field] = value
+ for _, s := range settings {
+ s(f)
+ }
+
+ if list == nil {
+ return []*file{f}
+ }
+
+ return append(list, f)
}
// Attach attaches the files to the email.
//
// Example:
//
-// m.Attach(gomail.NewFile("/tmp/image.jpg"))
-func (m *Message) Attach(f ...*File) {
- if m.attachments == nil {
- m.attachments = f
- } else {
- m.attachments = append(m.attachments, f...)
- }
+// m.Attach("/tmp/image.jpg")
+func (m *Message) Attach(filename string, settings ...FileSetting) {
+ m.attachments = m.appendFile(m.attachments, filename, settings)
}
// Embed embeds the images to the email.
//
// Example:
//
-// m.Embed(gomail.NewFile("/tmp/image.jpg"))
+// m.Embed("/tmp/image.jpg")
// m.SetBody("text/html", ``)
-func (m *Message) Embed(image ...*File) {
- if m.embedded == nil {
- m.embedded = image
- } else {
- m.embedded = append(m.embedded, image...)
- }
+func (m *Message) Embed(filename string, settings ...FileSetting) {
+ m.embedded = m.appendFile(m.embedded, filename, settings)
}
diff --git a/message_test.go b/message_test.go
index 0553382..fdd9ff9 100644
--- a/message_test.go
+++ b/message_test.go
@@ -195,7 +195,7 @@ func TestAttachmentOnly(t *testing.T) {
m := NewMessage()
m.SetHeader("From", "from@example.com")
m.SetHeader("To", "to@example.com")
- m.Attach(testFile("/tmp/test.pdf"))
+ m.Attach(mockCopyFile("/tmp/test.pdf"))
want := &message{
from: "from@example.com",
@@ -217,7 +217,7 @@ func TestAttachment(t *testing.T) {
m.SetHeader("From", "from@example.com")
m.SetHeader("To", "to@example.com")
m.SetBody("text/plain", "Test")
- m.Attach(testFile("/tmp/test.pdf"))
+ m.Attach(mockCopyFile("/tmp/test.pdf"))
want := &message{
from: "from@example.com",
@@ -247,8 +247,8 @@ func TestAttachmentsOnly(t *testing.T) {
m := NewMessage()
m.SetHeader("From", "from@example.com")
m.SetHeader("To", "to@example.com")
- m.Attach(testFile("/tmp/test.pdf"))
- m.Attach(testFile("/tmp/test.zip"))
+ m.Attach(mockCopyFile("/tmp/test.pdf"))
+ m.Attach(mockCopyFile("/tmp/test.zip"))
want := &message{
from: "from@example.com",
@@ -280,8 +280,8 @@ func TestAttachments(t *testing.T) {
m.SetHeader("From", "from@example.com")
m.SetHeader("To", "to@example.com")
m.SetBody("text/plain", "Test")
- m.Attach(testFile("/tmp/test.pdf"))
- m.Attach(testFile("/tmp/test.zip"))
+ m.Attach(mockCopyFile("/tmp/test.pdf"))
+ m.Attach(mockCopyFile("/tmp/test.zip"))
want := &message{
from: "from@example.com",
@@ -317,10 +317,8 @@ func TestEmbedded(t *testing.T) {
m := NewMessage()
m.SetHeader("From", "from@example.com")
m.SetHeader("To", "to@example.com")
- f := testFile("image1.jpg")
- f.Header["Content-ID"] = []string{""}
- m.Embed(f)
- m.Embed(testFile("image2.jpg"))
+ m.Embed(mockCopyFileWithHeader(m, "image1.jpg", map[string][]string{"Content-ID": {""}}))
+ m.Embed(mockCopyFile("image2.jpg"))
m.SetBody("text/plain", "Test")
want := &message{
@@ -361,8 +359,8 @@ func TestFullMessage(t *testing.T) {
m.SetHeader("To", "to@example.com")
m.SetBody("text/plain", "¡Hola, señor!")
m.AddAlternative("text/html", "¡Hola, señor!")
- m.Attach(testFile("test.pdf"))
- m.Embed(testFile("image.jpg"))
+ m.Attach(mockCopyFile("test.pdf"))
+ m.Embed(mockCopyFile("image.jpg"))
want := &message{
from: "from@example.com",
@@ -591,13 +589,16 @@ func getBoundaries(t *testing.T, count int, m string) []string {
var boundaryRegExp = regexp.MustCompile("boundary=(\\w+)")
-func testFile(name string) *File {
- f := NewFile(name)
- f.Copier = func(w io.Writer) error {
- _, err := w.Write([]byte("Content of " + filepath.Base(f.Name)))
+func mockCopyFile(name string) (string, FileSetting) {
+ return name, SetCopyFunc(func(w io.Writer) error {
+ _, err := w.Write([]byte("Content of " + filepath.Base(name)))
return err
- }
- return f
+ })
+}
+
+func mockCopyFileWithHeader(m *Message, name string, h map[string][]string) (string, FileSetting, FileSetting) {
+ name, f := mockCopyFile(name)
+ return name, f, SetHeader(h)
}
func BenchmarkFull(b *testing.B) {
@@ -618,8 +619,8 @@ func BenchmarkFull(b *testing.B) {
})
m.SetBody("text/plain", "¡Hola, señor!")
m.AddAlternative("text/html", "¡Hola, señor!
")
- m.Attach(testFile("benchmark.txt"))
- m.Embed(testFile("benchmark.jpg"))
+ m.Attach(mockCopyFile("benchmark.txt"))
+ m.Embed(mockCopyFile("benchmark.jpg"))
if err := Send(discardFunc, m); err != nil {
panic(err)
diff --git a/writeto.go b/writeto.go
index 625d63e..57a1dd7 100644
--- a/writeto.go
+++ b/writeto.go
@@ -104,7 +104,7 @@ func (w *messageWriter) closeMultipart() {
}
}
-func (w *messageWriter) addFiles(files []*File, isAttachment bool) {
+func (w *messageWriter) addFiles(files []*file, isAttachment bool) {
for _, f := range files {
if _, ok := f.Header["Content-Type"]; !ok {
mediaType := mime.TypeByExtension(filepath.Ext(f.Name))
@@ -134,7 +134,7 @@ func (w *messageWriter) addFiles(files []*File, isAttachment bool) {
}
}
w.writeHeaders(f.Header)
- w.writeBody(f.Copier, Base64)
+ w.writeBody(f.CopyFunc, Base64)
}
}