email.go 3.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174
  1. package email
  2. import (
  3. "bytes"
  4. "crypto/tls"
  5. "html/template"
  6. "net"
  7. "net/mail"
  8. "net/smtp"
  9. "time"
  10. "github.com/labstack/gommon/random"
  11. )
  12. type (
  13. Email struct {
  14. Auth smtp.Auth
  15. Header map[string]string
  16. Template *template.Template
  17. smtpAddress string
  18. }
  19. Message struct {
  20. ID string `json:"id"`
  21. From string `json:"from"`
  22. To string `json:"to"`
  23. CC string `json:"cc"`
  24. Subject string `json:"subject"`
  25. BodyText string `json:"body_text"`
  26. BodyHTML string `json:"body_html"`
  27. Inlines []*File `json:"inlines"`
  28. Attachments []*File `json:"attachments"`
  29. buffer *bytes.Buffer
  30. boundary string
  31. }
  32. File struct {
  33. Name string
  34. Type string
  35. Content string
  36. }
  37. )
  38. func New(smtpAddress string) *Email {
  39. return &Email{
  40. smtpAddress: smtpAddress,
  41. Header: map[string]string{},
  42. }
  43. }
  44. func (m *Message) writeHeader(key, value string) {
  45. m.buffer.WriteString(key)
  46. m.buffer.WriteString(": ")
  47. m.buffer.WriteString(value)
  48. m.buffer.WriteString("\r\n")
  49. }
  50. func (m *Message) writeBoundary() {
  51. m.buffer.WriteString("--")
  52. m.buffer.WriteString(m.boundary)
  53. m.buffer.WriteString("\r\n")
  54. }
  55. func (m *Message) writeText(content string, contentType string) {
  56. m.writeBoundary()
  57. m.writeHeader("Content-Type", contentType+"; charset=UTF-8")
  58. m.buffer.WriteString("\r\n")
  59. m.buffer.WriteString(content)
  60. m.buffer.WriteString("\r\n")
  61. m.buffer.WriteString("\r\n")
  62. }
  63. func (m *Message) writeFile(f *File, disposition string) {
  64. m.writeBoundary()
  65. m.writeHeader("Content-Type", f.Type+`; name="`+f.Name+`"`)
  66. m.writeHeader("Content-Disposition", disposition+`; filename="`+f.Name+`"`)
  67. m.writeHeader("Content-Transfer-Encoding", "base64")
  68. m.buffer.WriteString("\r\n")
  69. m.buffer.WriteString(f.Content)
  70. m.buffer.WriteString("\r\n")
  71. m.buffer.WriteString("\r\n")
  72. }
  73. func (e *Email) Send(m *Message) (err error) {
  74. // Message header
  75. m.buffer = bytes.NewBuffer(make([]byte, 256))
  76. m.buffer.Reset()
  77. m.boundary = random.String(16)
  78. m.writeHeader("MIME-Version", "1.0")
  79. m.writeHeader("Message-ID", m.ID)
  80. m.writeHeader("Date", time.Now().Format(time.RFC1123Z))
  81. m.writeHeader("From", m.From)
  82. m.writeHeader("To", m.To)
  83. if m.CC != "" {
  84. m.writeHeader("CC", m.CC)
  85. }
  86. if m.Subject != "" {
  87. m.writeHeader("Subject", m.Subject)
  88. }
  89. // Extra
  90. for k, v := range e.Header {
  91. m.writeHeader(k, v)
  92. }
  93. m.writeHeader("Content-Type", "multipart/mixed; boundary="+m.boundary)
  94. m.buffer.WriteString("\r\n")
  95. // Message body
  96. if m.BodyText != "" {
  97. m.writeText(m.BodyText, "text/plain")
  98. } else if m.BodyHTML != "" {
  99. m.writeText(m.BodyHTML, "text/html")
  100. } else {
  101. m.writeBoundary()
  102. }
  103. // Inlines/attachments
  104. for _, f := range m.Inlines {
  105. m.writeFile(f, "inline")
  106. }
  107. for _, f := range m.Attachments {
  108. m.writeFile(f, "attachment")
  109. }
  110. m.buffer.WriteString("--")
  111. m.buffer.WriteString(m.boundary)
  112. m.buffer.WriteString("--")
  113. // Dial
  114. c, err := smtp.Dial(e.smtpAddress)
  115. if err != nil {
  116. return
  117. }
  118. defer c.Quit()
  119. // Check if TLS is required
  120. if ok, _ := c.Extension("STARTTLS"); ok {
  121. host, _, _ := net.SplitHostPort(e.smtpAddress)
  122. config := &tls.Config{ServerName: host}
  123. if err = c.StartTLS(config); err != nil {
  124. return err
  125. }
  126. }
  127. // Authenticate
  128. if e.Auth != nil {
  129. if err = c.Auth(e.Auth); err != nil {
  130. return
  131. }
  132. }
  133. // Send message
  134. from, err := mail.ParseAddress(m.From)
  135. if err != nil {
  136. return
  137. }
  138. if err = c.Mail(from.Address); err != nil {
  139. return
  140. }
  141. to, err := mail.ParseAddressList(m.To)
  142. if err != nil {
  143. return
  144. }
  145. for _, a := range to {
  146. if err = c.Rcpt(a.Address); err != nil {
  147. return
  148. }
  149. }
  150. wc, err := c.Data()
  151. if err != nil {
  152. return
  153. }
  154. defer wc.Close()
  155. _, err = m.buffer.WriteTo(wc)
  156. return
  157. }