|
|
@@ -2,46 +2,54 @@ package email
|
|
|
|
|
|
import (
|
|
|
"bytes"
|
|
|
+ "crypto/tls"
|
|
|
"fmt"
|
|
|
"html/template"
|
|
|
"net/mail"
|
|
|
"net/smtp"
|
|
|
|
|
|
+ "net"
|
|
|
+
|
|
|
+ "time"
|
|
|
+
|
|
|
"github.com/labstack/gommon/random"
|
|
|
)
|
|
|
|
|
|
type (
|
|
|
Email struct {
|
|
|
Auth smtp.Auth
|
|
|
+ Header map[string]string
|
|
|
Template *template.Template
|
|
|
smtpAddress string
|
|
|
}
|
|
|
|
|
|
Message struct {
|
|
|
- From string
|
|
|
- To string
|
|
|
- CC string
|
|
|
- Subject string
|
|
|
- Text string
|
|
|
- HTML string
|
|
|
- TemplateName string
|
|
|
- TemplateData interface{}
|
|
|
- Inlines []*File
|
|
|
- Attachments []*File
|
|
|
+ ID string `json:"id"`
|
|
|
+ From string `json:"from"`
|
|
|
+ To string `json:"to"`
|
|
|
+ CC string `json:"cc"`
|
|
|
+ Subject string `json:"subject"`
|
|
|
+ Text string `json:"text"`
|
|
|
+ HTML string `json:"html"`
|
|
|
+ TemplateName string `json:"template_name"`
|
|
|
+ TemplateData interface{} `json:"template_data"`
|
|
|
+ Inlines []*File `json:"inlines"`
|
|
|
+ Attachments []*File `json:"attachments"`
|
|
|
buffer *bytes.Buffer
|
|
|
boundary string
|
|
|
}
|
|
|
|
|
|
File struct {
|
|
|
- Name string
|
|
|
- Type string
|
|
|
- Content string
|
|
|
+ Name string `json:"name"`
|
|
|
+ Type string `json:"type"`
|
|
|
+ Content string `json:"content"`
|
|
|
}
|
|
|
)
|
|
|
|
|
|
func New(smtpAddress string) *Email {
|
|
|
return &Email{
|
|
|
smtpAddress: smtpAddress,
|
|
|
+ Header: map[string]string{},
|
|
|
}
|
|
|
}
|
|
|
|
|
|
@@ -62,20 +70,33 @@ func (m *Message) writeFile(f *File, disposition string) {
|
|
|
m.buffer.WriteString(f.Content + "\r\n")
|
|
|
}
|
|
|
|
|
|
-func (e *Email) Send(m *Message) error {
|
|
|
+func (e *Email) Send(m *Message) (err error) {
|
|
|
+ // Message header
|
|
|
m.buffer = new(bytes.Buffer)
|
|
|
m.boundary = random.String(16)
|
|
|
m.buffer.WriteString("MIME-Version: 1.0\r\n")
|
|
|
- m.buffer.WriteString(fmt.Sprintf("CC: %s\r\n", m.CC))
|
|
|
- m.buffer.WriteString(fmt.Sprintf("Subject: %s\r\n", m.Subject))
|
|
|
+ m.buffer.WriteString(fmt.Sprintf("Message-Id: %s\r\n", m.ID))
|
|
|
+ m.buffer.WriteString(fmt.Sprintf("Date: %s\r\n", time.Now().Format(time.RFC1123Z)))
|
|
|
+ m.buffer.WriteString(fmt.Sprintf("From: %s\r\n", m.From))
|
|
|
+ m.buffer.WriteString(fmt.Sprintf("To: %s\r\n", m.From))
|
|
|
+ if m.CC != "" {
|
|
|
+ m.buffer.WriteString(fmt.Sprintf("CC: %s\r\n", m.CC))
|
|
|
+ }
|
|
|
+ if m.Subject != "" {
|
|
|
+ m.buffer.WriteString(fmt.Sprintf("Subject: %s\r\n", m.Subject))
|
|
|
+ }
|
|
|
+ // Extra
|
|
|
+ for k, v := range e.Header {
|
|
|
+ m.buffer.WriteString(fmt.Sprintf("%s: %s\r\n", k, v))
|
|
|
+ }
|
|
|
m.buffer.WriteString(fmt.Sprintf("Content-Type: multipart/mixed; boundary=%s\r\n", m.boundary))
|
|
|
m.buffer.WriteString("\r\n")
|
|
|
|
|
|
// Message body
|
|
|
if m.TemplateName != "" {
|
|
|
buf := new(bytes.Buffer)
|
|
|
- if err := e.Template.ExecuteTemplate(buf, m.TemplateName, m.TemplateData); err != nil {
|
|
|
- return err
|
|
|
+ if err = e.Template.ExecuteTemplate(buf, m.TemplateName, m.TemplateData); err != nil {
|
|
|
+ return
|
|
|
}
|
|
|
m.writeText(buf.String(), "text/html")
|
|
|
} else if m.Text != "" {
|
|
|
@@ -96,39 +117,51 @@ func (e *Email) Send(m *Message) error {
|
|
|
m.buffer.WriteString("\r\n")
|
|
|
m.buffer.WriteString("--" + m.boundary + "--")
|
|
|
|
|
|
- // Send message
|
|
|
+ // Dial
|
|
|
c, err := smtp.Dial(e.smtpAddress)
|
|
|
- if e.Auth != nil {
|
|
|
- // Authenticate
|
|
|
- if err := c.Auth(e.Auth); err != nil {
|
|
|
+ if err != nil {
|
|
|
+ return
|
|
|
+ }
|
|
|
+ defer c.Close()
|
|
|
+
|
|
|
+ // Check if TLS is required
|
|
|
+ if ok, _ := c.Extension("STARTTLS"); ok {
|
|
|
+ host, _, _ := net.SplitHostPort(e.smtpAddress)
|
|
|
+ config := &tls.Config{ServerName: host}
|
|
|
+ if err = c.StartTLS(config); err != nil {
|
|
|
return err
|
|
|
}
|
|
|
}
|
|
|
- if err != nil {
|
|
|
- return err
|
|
|
+
|
|
|
+ // Authenticate
|
|
|
+ if e.Auth != nil {
|
|
|
+ if err = c.Auth(e.Auth); err != nil {
|
|
|
+ return
|
|
|
+ }
|
|
|
}
|
|
|
- defer c.Close()
|
|
|
+
|
|
|
+ // Send message
|
|
|
from, err := mail.ParseAddress(m.From)
|
|
|
if err != nil {
|
|
|
- return err
|
|
|
+ return
|
|
|
}
|
|
|
if err = c.Mail(from.Address); err != nil {
|
|
|
- return err
|
|
|
+ return
|
|
|
}
|
|
|
to, err := mail.ParseAddressList(m.To)
|
|
|
if err != nil {
|
|
|
- return err
|
|
|
+ return
|
|
|
}
|
|
|
for _, a := range to {
|
|
|
if err = c.Rcpt(a.Address); err != nil {
|
|
|
- return err
|
|
|
+ return
|
|
|
}
|
|
|
}
|
|
|
wc, err := c.Data()
|
|
|
if err != nil {
|
|
|
- return err
|
|
|
+ return
|
|
|
}
|
|
|
defer wc.Close()
|
|
|
_, err = m.buffer.WriteTo(wc)
|
|
|
- return err
|
|
|
+ return
|
|
|
}
|