email.go 3.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100
  1. //Email is designed to be a package providing an "email interface for humans."
  2. //Designed to be robust and flexible, the email package aims to make sending email easy without getting in the way.
  3. package email
  4. import (
  5. "bytes"
  6. "fmt"
  7. "io/ioutil"
  8. "log"
  9. "mime"
  10. "net/mail"
  11. "net/smtp"
  12. "net/textproto"
  13. "os"
  14. "path/filepath"
  15. "strings"
  16. )
  17. //Email is the type used for email messages
  18. type Email struct {
  19. From string
  20. To []string
  21. Bcc []string
  22. Cc []string
  23. Subject string
  24. Text string //Plaintext message (optional)
  25. Html string //Html message (optional)
  26. Headers textproto.MIMEHeader
  27. Attachments map[string]*Attachment
  28. }
  29. //NewEmail creates an Email, and returns the pointer to it.
  30. func NewEmail() *Email {
  31. return &Email{Attachments: make(map[string]*Attachment), Headers: make(textproto.MIMEHeader)}
  32. }
  33. //Attach is used to attach a file to the email.
  34. //It attempts to open the file reference by filename and, if successful, creates an Attachment.
  35. //This Attachment is then appended to the slice of Email.Attachments.
  36. //The function will then return the Attachment for reference, as well as nil for the error if successful.
  37. func (e *Email) Attach(filename string) (a *Attachment, err error) {
  38. //Check if the file exists, return any error
  39. if _, err := os.Stat(filename); os.IsNotExist(err) {
  40. log.Fatal("%s does not exist", filename)
  41. return nil, err
  42. }
  43. //Read the file, and set the appropriate headers
  44. buffer, _ := ioutil.ReadFile(filename)
  45. e.Attachments[filename] = &Attachment{
  46. Filename: filename,
  47. Header: make(textproto.MIMEHeader),
  48. Content: buffer}
  49. at := e.Attachments[filename]
  50. //Get the Content-Type to be used in the MIMEHeader
  51. ct := mime.TypeByExtension(filepath.Ext(filename))
  52. if ct != "" {
  53. at.Header.Set("Content-Type", ct)
  54. } else {
  55. //If the Content-Type is blank, set the Content-Type to "application/octet-stream"
  56. at.Header.Set("Content-Type", "application/octet-stream")
  57. }
  58. at.Header.Set("Content-Disposition", fmt.Sprintf("attachment;\r\n filename=%s", filename))
  59. return e.Attachments[filename], nil
  60. }
  61. //Bytes converts the Email object to a []byte representation of it, including all needed MIMEHeaders, boundaries, etc.
  62. func (e *Email) Bytes() []byte {
  63. buff := bytes.Buffer{}
  64. //w := multipart.NewWriter(&buff)
  65. //Set the appropriate headers (overwriting any conflicts)
  66. //Leave out Bcc (only included in envelope headers)
  67. //TODO: Support wrapping on 76 characters (ref: MIME RFC)
  68. e.Headers.Set("To", strings.Join(e.To, ","))
  69. e.Headers.Set("Cc", strings.Join(e.Cc, ","))
  70. e.Headers.Set("From", e.From)
  71. e.Headers.Set("Subject", e.Subject)
  72. //Write the envelope headers (including any custom headers)
  73. return buff.Bytes()
  74. }
  75. //Send an email using the given host and SMTP auth (optional), returns any error thrown by smtp.SendMail
  76. //This function merges the To, Cc, and Bcc fields and calls the smtp.SendMail function using the Email.Bytes() output as the message
  77. func (e *Email) Send(addr string, a smtp.Auth) error {
  78. // Merge the To, Cc, and Bcc fields
  79. to := append(append(e.To, e.Cc...), e.Bcc...)
  80. from, err := mail.ParseAddress(e.From)
  81. if err != nil {
  82. return err
  83. }
  84. return smtp.SendMail(addr, a, from.Address, to, e.Bytes())
  85. }
  86. //Attachment is a struct representing an email attachment.
  87. //Based on the mime/multipart.FileHeader struct, Attachment contains the name, MIMEHeader, and content of the attachment in question
  88. type Attachment struct {
  89. Filename string
  90. Header textproto.MIMEHeader
  91. Content []byte
  92. }