Skip to content

Commit

Permalink
Unify attachment api
Browse files Browse the repository at this point in the history
WIP to merge all attachment to only one Email method called Attach and use the struct File to set the attachment information.

For #33
  • Loading branch information
xhit committed Apr 26, 2021
1 parent e5b7a7c commit cb2f478
Show file tree
Hide file tree
Showing 2 changed files with 83 additions and 107 deletions.
178 changes: 77 additions & 101 deletions email.go
Original file line number Diff line number Diff line change
Expand Up @@ -25,8 +25,8 @@ type Email struct {
recipients []string
headers textproto.MIMEHeader
parts []part
attachments []*file
inlines []*file
attachments []*File
inlines []*File
Charset string
Encoding encoding
Error error
Expand Down Expand Up @@ -64,11 +64,14 @@ type part struct {
body *bytes.Buffer
}

// file represents the files that can be added to the email message.
type file struct {
filename string
mimeType string
data []byte
// File represents the file that can be added to the email message.
type File struct {
Filepath string
Name string
MimeType string
B64File string
Data []byte
Inline bool
}

// Encryption type to enum encryption types (None, SSL/TLS, STARTTLS)
Expand Down Expand Up @@ -515,117 +518,104 @@ func (email *Email) AddAlternative(contentType contentType, body string) *Email
return email
}

// AddAttachment allows you to add an attachment to the email message.
// You can optionally provide a different name for the file.
func (email *Email) AddAttachment(file string, name ...string) *Email {
if email.Error != nil {
return email
}
type attachType int

if len(name) > 1 {
email.Error = errors.New("Mail Error: Attach can only have a file and an optional name")
return email
}

email.Error = email.attach(file, false, name[0], "")
const (
attachData attachType = iota
attachB64
attachFile
)

return email
}
func getAttachmentType(file *File) (attachType, error) {
// 1- data
// 2- base64
// 3- file

// AddAttachmentData allows you to add an in-memory attachment to the email message.
func (email *Email) AddAttachmentData(data []byte, filename, mimeType string) *Email {
if email.Error != nil {
return email
// first check if Data
if len(file.Data) > 0 {
// data requires a name
if len(file.Name) == 0 {
return 0, errors.New("Mail Error: Attach from bytes requires a name")
}
return attachData, nil
}

email.attachData(data, false, filename, mimeType)

return email
}

// AddAttachmentBase64 allows you to add an attachment in base64 to the email message.
// You need provide a name for the file.
func (email *Email) AddAttachmentBase64(b64File, name string) *Email {
if email.Error != nil {
return email
// check if base64
if len(file.B64File) > 0 {
// b64file requires a name
if len(file.Name) == 0 {
return 0, errors.New("Mail Error: Attach from base64 string requires a name")
}
return attachB64, nil
}

if len(name) < 1 || len(b64File) < 1 {
email.Error = errors.New("Mail Error: Attach Base64 need have a base64 string and name")
return email
// check if file
if len(file.Filepath) > 0 {
return attachFile, nil
}

email.Error = email.attachB64(b64File, false, name, "")

return email
return 0, errors.New("Mail Error: Empty attachment")
}

// AddInline allows you to add an inline attachment to the email message.
// Attach allows you to add an attachment to the email message.
// You can optionally provide a different name for the file.
func (email *Email) AddInline(file string, name ...string) *Email {
func (email *Email) Attach(file File) *Email {
if email.Error != nil {
return email
}

if len(name) > 1 {
email.Error = errors.New("Mail Error: Inline can only have a file and an optional name")
return email
}
var name = file.Name

email.Error = email.attach(file, true, name[0], "")

return email
}
// if no alternative name was provided, get the filename
if len(name) == 0 {
_, name = filepath.Split(file.Filepath)
}

// AddInlineData allows you to add an inline in-memory attachment to the email message.
func (email *Email) AddInlineData(data []byte, filename, mimeType string) *Email {
if email.Error != nil {
attachTy, err := getAttachmentType(&file)
if err != nil {
email.Error = errors.New("Mail Error: Failed to add attachment with following error: " + err.Error())
return email
}

email.attachData(data, true, filename, mimeType)
switch attachTy {
case attachData:
email.attachData(file.Data, name, file.MimeType, file.Inline)
case attachB64:
email.Error = email.attachB64(file.B64File, name, file.MimeType, file.Inline)
case attachFile:
email.Error = email.attachFile(file.Filepath, name, file.MimeType, file.Inline)
}

return email
return nil
}

// AddInlineBase64 allows you to add an inline in-memory base64 encoded attachment to the email message.
// You need provide a name for the file. If mimeType is an empty string, attachment mime type will be deduced
// from the file name extension and defaults to application/octet-stream.
func (email *Email) AddInlineBase64(b64File, name, mimeType string) *Email {
if email.Error != nil {
return email
}
// attachB64 does the low level attaching of the files but decoding base64
func (email *Email) attachB64(b64File, name, mimeType string, inline bool) error {

if len(name) < 1 || len(b64File) < 1 {
email.Error = errors.New("Mail Error: Attach Base64 need have a base64 string and name")
return email
// decode the string
dec, err := base64.StdEncoding.DecodeString(b64File)
if err != nil {
return errors.New("Mail Error: Failed to decode base64 attachment with following error: " + err.Error())
}

email.Error = email.attachB64(b64File, true, name, mimeType)
email.attachData(dec, name, mimeType, inline)

return email
return nil
}

// attach does the low level attaching of the files
func (email *Email) attach(f string, inline bool, name, mimeType string) error {
// Get the file data
data, err := ioutil.ReadFile(f)
func (email *Email) attachFile(filepath, name, mimeType string, inline bool) error {
data, err := ioutil.ReadFile(filepath)
if err != nil {
return errors.New("Mail Error: Failed to add file with following error: " + err.Error())
}

// if no alternative name was provided, get the filename
if len(name) == 0 {
_, name = filepath.Split(f)
}

email.attachData(data, inline, name, mimeType)
email.attachData(data, name, mimeType, inline)

return nil
}

// attachData does the low level attaching of the in-memory data
func (email *Email) attachData(data []byte, inline bool, name, mimeType string) {
func (email *Email) attachData(data []byte, name, mimeType string, inline bool) {
if mimeType == "" {
mimeType = mime.TypeByExtension(filepath.Ext(name))
if mimeType == "" {
Expand All @@ -634,34 +624,20 @@ func (email *Email) attachData(data []byte, inline bool, name, mimeType string)
}

if inline {
email.inlines = append(email.inlines, &file{
filename: name,
mimeType: mimeType,
data: data,
email.inlines = append(email.inlines, &File{
Name: name,
MimeType: mimeType,
Data: data,
})
} else {
email.attachments = append(email.attachments, &file{
filename: name,
mimeType: mimeType,
data: data,
email.attachments = append(email.attachments, &File{
Name: name,
MimeType: mimeType,
Data: data,
})
}
}

// attachB64 does the low level attaching of the files but decoding base64 instead have a filepath
func (email *Email) attachB64(b64File string, inline bool, name, mimeType string) error {

// decode the string
dec, err := base64.StdEncoding.DecodeString(b64File)
if err != nil {
return errors.New("Mail Error: Failed to decode base64 attachment with following error: " + err.Error())
}

email.attachData(dec, inline, name, mimeType)

return nil
}

// GetFrom returns the sender of the email, if any
func (email *Email) GetFrom() string {
from := email.returnPath
Expand Down
12 changes: 6 additions & 6 deletions message.go
Original file line number Diff line number Diff line change
Expand Up @@ -230,19 +230,19 @@ func escapeQuotes(s string) string {
return quoteEscaper.Replace(s)
}

func (msg *message) addFiles(files []*file, inline bool) {
func (msg *message) addFiles(files []*File, inline bool) {
encoding := EncodingBase64
for _, file := range files {
header := make(textproto.MIMEHeader)
header.Set("Content-Type", file.mimeType+";\n \tname=\""+encodeHeader(escapeQuotes(file.filename), msg.charset, 6)+`"`)
header.Set("Content-Type", file.MimeType+";\n \tname=\""+encodeHeader(escapeQuotes(file.Name), msg.charset, 6)+`"`)
header.Set("Content-Transfer-Encoding", encoding.string())
if inline {
header.Set("Content-Disposition", "inline;\n \tfilename=\""+encodeHeader(escapeQuotes(file.filename), msg.charset, 10)+`"`)
header.Set("Content-ID", "<"+msg.getCID(file.filename)+">")
header.Set("Content-Disposition", "inline;\n \tfilename=\""+encodeHeader(escapeQuotes(file.Name), msg.charset, 10)+`"`)
header.Set("Content-ID", "<"+msg.getCID(file.Name)+">")
} else {
header.Set("Content-Disposition", "attachment;\n \tfilename=\""+encodeHeader(escapeQuotes(file.filename), msg.charset, 10)+`"`)
header.Set("Content-Disposition", "attachment;\n \tfilename=\""+encodeHeader(escapeQuotes(file.Name), msg.charset, 10)+`"`)
}

msg.write(header, file.data, encoding)
msg.write(header, file.Data, encoding)
}
}

0 comments on commit cb2f478

Please sign in to comment.