diff --git a/pkg/mail/mail.go b/pkg/mail/mail.go new file mode 100644 index 0000000..e78fbd1 --- /dev/null +++ b/pkg/mail/mail.go @@ -0,0 +1,184 @@ +package mail + +import ( + "bytes" + "crypto/tls" + "encoding/base64" + "fmt" + "io/ioutil" + "net" + "net/smtp" + "strings" + "time" +) + +type Mail interface { + Authorize() + Send(message Message) error +} + +type mail struct { + username string + password string + host string + port string + auth smtp.Auth +} + +type Message struct { + From string + To []string + Cc []string + Bcc []string + Subject string + Body string + ContentType string + Attachment []Attachment +} + +type Attachment struct { + Name string + URL string + ContentType string + WithFile bool +} + +// NewSendMail 构造一个邮件发送对象 +func NewSendMail(username string, password string, host string, port string) Mail { + return &mail{ + username: username, + password: password, + host: host, + port: port, + } +} + +func (m *mail) Authorize() { + m.auth = smtp.PlainAuth("", m.username, m.password, m.host) +} + +func (m *mail) Send(message Message) error { + m.Authorize() + buffer := bytes.NewBuffer(nil) + boundary := "GoBoundary" + Header := make(map[string]string) + Header["From"] = message.From + Header["To"] = strings.Join(message.To, ";") + Header["Cc"] = strings.Join(message.Cc, ";") + Header["Bcc"] = strings.Join(message.Bcc, ";") + Header["Subject"] = message.Subject + Header["Content-Type"] = "multipart/mixed;boundary=" + boundary + Header["Mime-Version"] = "1.0" + Header["Date"] = time.Now().String() + m.writeHeader(buffer, Header) + + body := "\r\n--" + boundary + "\r\n" + body += "Content-Type:" + message.ContentType + "\r\n" + body += "\r\n" + message.Body + "\r\n" + buffer.WriteString(body) + + for i := 0; i < len(message.Attachment); i++ { + item := message.Attachment[i] + if item.WithFile { + attachment := "\r\n--" + boundary + "\r\n" + attachment += "Content-Transfer-Encoding:base64\r\n" + attachment += "Content-Disposition:attachment\r\n" + attachment += "Content-Type:" + item.ContentType + ";name=\"" + item.Name + "\"\r\n" + buffer.WriteString(attachment) + //defer func() { + // if err := recover(); err != nil { + // } + //}() + m.writeFile(buffer, item.URL) + } + } + + buffer.WriteString("\r\n--" + boundary + "--") + //smtp.SendMail(mail.Host+":"+mail.Port, mail.Auth, message.From, message.To, buffer.Bytes()) + + err := sendMailUsingTLS(m.host+":"+m.port, m.auth, message.From, message.To, buffer.Bytes()) + if err != nil { + return err + } + + return nil +} + +func (m *mail) writeHeader(buffer *bytes.Buffer, Header map[string]string) string { + header := "" + for key, value := range Header { + header += key + ":" + value + "\r\n" + } + header += "\r\n" + buffer.WriteString(header) + return header +} + +// read and write the file to buffer +func (m *mail) writeFile(buffer *bytes.Buffer, fileName string) { + file, err := ioutil.ReadFile(fileName) + if err != nil { + panic(err.Error()) + } + payload := make([]byte, base64.StdEncoding.EncodedLen(len(file))) + base64.StdEncoding.Encode(payload, file) + buffer.WriteString("\r\n") + for index, line := 0, len(payload); index < line; index++ { + buffer.WriteByte(payload[index]) + if (index+1)%76 == 0 { + buffer.WriteString("\r\n") + } + } +} + +func sendMailUsingTLS(addr string, auth smtp.Auth, from string, to []string, msg []byte) (err error) { + c, err := dial(addr) + if err != nil { + return err + } + + defer func(c *smtp.Client) { + _ = c.Close() + }(c) + + if auth != nil { + if ok, _ := c.Extension("AUTH"); ok { + if err = c.Auth(auth); err != nil { + return err + } + } + } + if err = c.Mail(from); err != nil { + return err + } + //tos := strings.Split(to, ";") + for _, addr := range to { + if err = c.Rcpt(addr); err != nil { + fmt.Print(err) + return err + } + } + w, err := c.Data() + if err != nil { + return err + } + _, err = w.Write(msg) + if err != nil { + return err + } + err = w.Close() + if err != nil { + return err + } + return c.Quit() +} + +func dial(addr string) (*smtp.Client, error) { + conn, err := tls.Dial("tcp", addr, nil) + if err != nil { + return nil, err + } + //分解主机端口字符串 + host, _, _ := net.SplitHostPort(addr) + return smtp.NewClient(conn, host) +}