mirror of https://github.com/mindoc-org/mindoc.git
实现发送邮件支持SSL和TLS
parent
e9f386c444
commit
257a758b21
|
@ -69,7 +69,8 @@ smtp_port=25
|
||||||
form_user_name=admin@iminho.me
|
form_user_name=admin@iminho.me
|
||||||
#邮件有效期30分钟
|
#邮件有效期30分钟
|
||||||
mail_expired=30
|
mail_expired=30
|
||||||
|
#加密类型NONE 无认证、SSL 加密、LOGIN 普通用户登录
|
||||||
|
secure=LOGIN
|
||||||
|
|
||||||
###############配置PDF生成工具地址###################
|
###############配置PDF生成工具地址###################
|
||||||
wkhtmltopdf=D:/Program Files/wkhtmltopdf/bin/wkhtmltopdf.exe
|
wkhtmltopdf=D:/Program Files/wkhtmltopdf/bin/wkhtmltopdf.exe
|
||||||
|
|
|
@ -14,6 +14,7 @@ type SmtpConf struct {
|
||||||
SmtpPort int
|
SmtpPort int
|
||||||
FormUserName string
|
FormUserName string
|
||||||
MailExpired int
|
MailExpired int
|
||||||
|
Secure string
|
||||||
}
|
}
|
||||||
|
|
||||||
func GetMailConfig() *SmtpConf {
|
func GetMailConfig() *SmtpConf {
|
||||||
|
@ -24,7 +25,11 @@ func GetMailConfig() *SmtpConf {
|
||||||
form_user_name := beego.AppConfig.String("form_user_name")
|
form_user_name := beego.AppConfig.String("form_user_name")
|
||||||
enable_mail := beego.AppConfig.String("enable_mail")
|
enable_mail := beego.AppConfig.String("enable_mail")
|
||||||
mail_number := beego.AppConfig.DefaultInt("mail_number", 5)
|
mail_number := beego.AppConfig.DefaultInt("mail_number", 5)
|
||||||
|
secure := beego.AppConfig.DefaultString("secure","NONE")
|
||||||
|
|
||||||
|
if secure != "NONE" && secure != "LOGIN" && secure != "SSL" {
|
||||||
|
secure = "NONE"
|
||||||
|
}
|
||||||
c := &SmtpConf{
|
c := &SmtpConf{
|
||||||
EnableMail: strings.EqualFold(enable_mail, "true"),
|
EnableMail: strings.EqualFold(enable_mail, "true"),
|
||||||
MailNumber: mail_number,
|
MailNumber: mail_number,
|
||||||
|
@ -33,6 +38,7 @@ func GetMailConfig() *SmtpConf {
|
||||||
SmtpPassword: password,
|
SmtpPassword: password,
|
||||||
FormUserName: form_user_name,
|
FormUserName: form_user_name,
|
||||||
SmtpPort: smtp_port,
|
SmtpPort: smtp_port,
|
||||||
|
Secure:secure,
|
||||||
}
|
}
|
||||||
return c
|
return c
|
||||||
}
|
}
|
||||||
|
|
|
@ -2,19 +2,17 @@ package controllers
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"regexp"
|
"regexp"
|
||||||
"strconv"
|
|
||||||
"strings"
|
"strings"
|
||||||
"time"
|
"time"
|
||||||
|
"net/url"
|
||||||
|
|
||||||
"net/smtp"
|
"github.com/lifei6671/mindoc/mail"
|
||||||
|
|
||||||
"github.com/astaxie/beego"
|
"github.com/astaxie/beego"
|
||||||
"github.com/astaxie/beego/logs"
|
"github.com/astaxie/beego/logs"
|
||||||
"github.com/lifei6671/gocaptcha"
|
"github.com/lifei6671/gocaptcha"
|
||||||
"github.com/lifei6671/mindoc/conf"
|
"github.com/lifei6671/mindoc/conf"
|
||||||
"github.com/lifei6671/mindoc/models"
|
"github.com/lifei6671/mindoc/models"
|
||||||
"github.com/lifei6671/mindoc/utils"
|
"github.com/lifei6671/mindoc/utils"
|
||||||
"net/url"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
// AccountController 用户登录与注册
|
// AccountController 用户登录与注册
|
||||||
|
@ -256,27 +254,52 @@ func (c *AccountController) FindPassword() {
|
||||||
c.JsonResult(6003, "邮件发送失败")
|
c.JsonResult(6003, "邮件发送失败")
|
||||||
}
|
}
|
||||||
|
|
||||||
go func(mail_conf *conf.SmtpConf, email string, body string) {
|
go func(mailConf *conf.SmtpConf, email string, body string) {
|
||||||
auth := smtp.PlainAuth(
|
|
||||||
"",
|
|
||||||
mail_conf.SmtpUserName,
|
|
||||||
mail_conf.SmtpPassword,
|
|
||||||
mail_conf.SmtpHost,
|
|
||||||
)
|
|
||||||
|
|
||||||
mime := "MIME-version: 1.0;\nContent-Type: text/html; charset=\"UTF-8\";\n\n"
|
mailConfig := &mail.SMTPConfig{
|
||||||
subject := "Subject: 找回密码!\n"
|
Username: mailConf.SmtpUserName,
|
||||||
|
Password: mailConf.SmtpPassword,
|
||||||
err = smtp.SendMail(
|
Host: mailConf.SmtpHost,
|
||||||
mail_conf.SmtpHost+":"+strconv.Itoa(mail_conf.SmtpPort),
|
Port: mailConf.SmtpPort,
|
||||||
auth,
|
Secure: mailConf.Secure,
|
||||||
mail_conf.FormUserName,
|
Identity:"",
|
||||||
[]string{email},
|
|
||||||
[]byte(subject+mime+"\n"+body),
|
|
||||||
)
|
|
||||||
if err != nil {
|
|
||||||
beego.Error("邮件发送失败 => ", email, err)
|
|
||||||
}
|
}
|
||||||
|
beego.Info(mailConfig)
|
||||||
|
|
||||||
|
c := mail.NewSMTPClient(mailConfig)
|
||||||
|
m := mail.NewMail()
|
||||||
|
|
||||||
|
m.AddFrom(mailConf.FormUserName)
|
||||||
|
m.AddFromName(mailConf.FormUserName)
|
||||||
|
m.AddSubject("找回密码")
|
||||||
|
m.AddHTML(body)
|
||||||
|
m.AddTo(email)
|
||||||
|
|
||||||
|
if e := c.Send(m); e != nil {
|
||||||
|
beego.Error("发送邮件失败:" + e.Error())
|
||||||
|
} else {
|
||||||
|
beego.Info("邮件发送成功:" + email)
|
||||||
|
}
|
||||||
|
//auth := smtp.PlainAuth(
|
||||||
|
// "",
|
||||||
|
// mail_conf.SmtpUserName,
|
||||||
|
// mail_conf.SmtpPassword,
|
||||||
|
// mail_conf.SmtpHost,
|
||||||
|
//)
|
||||||
|
//
|
||||||
|
//mime := "MIME-version: 1.0;\nContent-Type: text/html; charset=\"UTF-8\";\n\n"
|
||||||
|
//subject := "Subject: 找回密码!\n"
|
||||||
|
//
|
||||||
|
//err = smtp.SendMail(
|
||||||
|
// mail_conf.SmtpHost+":"+strconv.Itoa(mail_conf.SmtpPort),
|
||||||
|
// auth,
|
||||||
|
// mail_conf.FormUserName,
|
||||||
|
// []string{email},
|
||||||
|
// []byte(subject+mime+"\n"+body),
|
||||||
|
//)
|
||||||
|
//if err != nil {
|
||||||
|
// beego.Error("邮件发送失败 => ", email, err)
|
||||||
|
//}
|
||||||
}(mail_conf, email, body)
|
}(mail_conf, email, body)
|
||||||
|
|
||||||
c.JsonResult(0, "ok", c.BaseUrl()+beego.URLFor("AccountController.Login"))
|
c.JsonResult(0, "ok", c.BaseUrl()+beego.URLFor("AccountController.Login"))
|
||||||
|
|
|
@ -0,0 +1,452 @@
|
||||||
|
package mail
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"crypto/md5"
|
||||||
|
"crypto/tls"
|
||||||
|
"encoding/base64"
|
||||||
|
"encoding/hex"
|
||||||
|
"errors"
|
||||||
|
"fmt"
|
||||||
|
"io/ioutil"
|
||||||
|
"log"
|
||||||
|
"net/mail"
|
||||||
|
"net/smtp"
|
||||||
|
"path"
|
||||||
|
"path/filepath"
|
||||||
|
"regexp"
|
||||||
|
"strconv"
|
||||||
|
"strings"
|
||||||
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
imageRegex = regexp.MustCompile(`(src|background)=["'](.*?)["']`)
|
||||||
|
schemeRegxp = regexp.MustCompile(`^[A-z]+://`)
|
||||||
|
)
|
||||||
|
|
||||||
|
// Mail will represent a formatted email
|
||||||
|
type Mail struct {
|
||||||
|
To []string
|
||||||
|
ToName []string
|
||||||
|
Subject string
|
||||||
|
HTML string
|
||||||
|
Text string
|
||||||
|
From string
|
||||||
|
Bcc []string
|
||||||
|
FromName string
|
||||||
|
ReplyTo string
|
||||||
|
Date string
|
||||||
|
Files map[string]string
|
||||||
|
Headers string
|
||||||
|
BaseDir string //内容中图片路径
|
||||||
|
Charset string //编码
|
||||||
|
RetReceipt string //回执地址,空白则禁用回执
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewMail returns a new Mail
|
||||||
|
func NewMail() Mail {
|
||||||
|
return Mail{}
|
||||||
|
}
|
||||||
|
|
||||||
|
// SMTPClient struct
|
||||||
|
type SMTPClient struct {
|
||||||
|
smtpAuth smtp.Auth
|
||||||
|
host string
|
||||||
|
port string
|
||||||
|
user string
|
||||||
|
secure string
|
||||||
|
}
|
||||||
|
|
||||||
|
// SMTPConfig 配置结构体
|
||||||
|
type SMTPConfig struct {
|
||||||
|
Username string
|
||||||
|
Password string
|
||||||
|
Host string
|
||||||
|
Port int
|
||||||
|
Secure string
|
||||||
|
Identity string
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *SMTPConfig) Address() string {
|
||||||
|
if s.Port == 0 {
|
||||||
|
s.Port = 25
|
||||||
|
}
|
||||||
|
return s.Host + `:` + strconv.Itoa(s.Port)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *SMTPConfig) Auth() smtp.Auth {
|
||||||
|
var auth smtp.Auth
|
||||||
|
s.Secure = strings.ToUpper(s.Secure)
|
||||||
|
switch s.Secure {
|
||||||
|
case "NONE":
|
||||||
|
auth = unencryptedAuth{smtp.PlainAuth(s.Identity, s.Username, s.Password, s.Host)}
|
||||||
|
case "LOGIN":
|
||||||
|
auth = LoginAuth(s.Username, s.Password)
|
||||||
|
case "SSL":
|
||||||
|
fallthrough
|
||||||
|
default:
|
||||||
|
//auth = smtp.PlainAuth(s.Identity, s.Username, s.Password, s.Host)
|
||||||
|
auth = unencryptedAuth{smtp.PlainAuth(s.Identity, s.Username, s.Password, s.Host)}
|
||||||
|
}
|
||||||
|
return auth
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewSMTPClient(conf *SMTPConfig) SMTPClient {
|
||||||
|
return SMTPClient{
|
||||||
|
smtpAuth: conf.Auth(),
|
||||||
|
host: conf.Host,
|
||||||
|
port: strconv.Itoa(conf.Port),
|
||||||
|
user: conf.Username,
|
||||||
|
secure: conf.Secure,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewMail returns a new Mail
|
||||||
|
func (c *SMTPClient) NewMail() Mail {
|
||||||
|
return NewMail()
|
||||||
|
}
|
||||||
|
|
||||||
|
// Send - It can be used for generic SMTP stuff
|
||||||
|
func (c *SMTPClient) Send(m Mail) error {
|
||||||
|
length := 0
|
||||||
|
if len(m.Charset) == 0 {
|
||||||
|
m.Charset = "utf-8"
|
||||||
|
}
|
||||||
|
boundary := "COSCMSBOUNDARYFORSMTPGOLIB"
|
||||||
|
var message bytes.Buffer
|
||||||
|
message.WriteString(fmt.Sprintf("X-SMTPAPI: %s\r\n", m.Headers))
|
||||||
|
//回执
|
||||||
|
if len(m.RetReceipt) > 0 {
|
||||||
|
message.WriteString(fmt.Sprintf("Return-Receipt-To: %s\r\n", m.RetReceipt))
|
||||||
|
message.WriteString(fmt.Sprintf("Disposition-Notification-To: %s\r\n", m.RetReceipt))
|
||||||
|
}
|
||||||
|
message.WriteString(fmt.Sprintf("From: %s <%s>\r\n", m.FromName, m.From))
|
||||||
|
if len(m.ReplyTo) > 0 {
|
||||||
|
message.WriteString(fmt.Sprintf("Return-Path: %s\r\n", m.ReplyTo))
|
||||||
|
}
|
||||||
|
length = len(m.To)
|
||||||
|
if length > 0 {
|
||||||
|
nameLength := len(m.ToName)
|
||||||
|
if nameLength > 0 {
|
||||||
|
message.WriteString(fmt.Sprintf("To: %s <%s>", m.ToName[0], m.To[0]))
|
||||||
|
} else {
|
||||||
|
message.WriteString(fmt.Sprintf("To: <%s>", m.To[0]))
|
||||||
|
}
|
||||||
|
for i := 1; i < length; i++ {
|
||||||
|
if nameLength > i {
|
||||||
|
message.WriteString(fmt.Sprintf(", %s <%s>", m.ToName[i], m.To[i]))
|
||||||
|
} else {
|
||||||
|
message.WriteString(fmt.Sprintf(", <%s>", m.To[i]))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
length = len(m.Bcc)
|
||||||
|
if length > 0 {
|
||||||
|
message.WriteString(fmt.Sprintf("Bcc: <%s>", m.Bcc[0]))
|
||||||
|
for i := 1; i < length; i++ {
|
||||||
|
message.WriteString(fmt.Sprintf(", <%s>", m.Bcc[i]))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
message.WriteString("\r\n")
|
||||||
|
message.WriteString(fmt.Sprintf("Subject: %s\r\n", m.Subject))
|
||||||
|
message.WriteString("MIME-Version: 1.0\r\n")
|
||||||
|
if m.Files != nil {
|
||||||
|
message.WriteString(fmt.Sprintf("Content-Type: multipart/mixed; boundary=\"%s\"\r\n\n--%s\r\n", boundary, boundary))
|
||||||
|
}
|
||||||
|
if len(m.HTML) > 0 {
|
||||||
|
//解析内容中的图片
|
||||||
|
rs := imageRegex.FindAllStringSubmatch(m.HTML, -1)
|
||||||
|
var embedImages string
|
||||||
|
for _, v := range rs {
|
||||||
|
surl := v[2]
|
||||||
|
if v2 := schemeRegxp.FindStringIndex(surl); v2 == nil {
|
||||||
|
filename := path.Base(surl)
|
||||||
|
directory := path.Dir(surl)
|
||||||
|
if directory == "." {
|
||||||
|
directory = ""
|
||||||
|
}
|
||||||
|
h := md5.New()
|
||||||
|
h.Write([]byte(surl + "@coscms.0"))
|
||||||
|
cid := hex.EncodeToString(h.Sum(nil))
|
||||||
|
if len(m.BaseDir) > 0 && !strings.HasSuffix(m.BaseDir, "/") {
|
||||||
|
m.BaseDir += "/"
|
||||||
|
}
|
||||||
|
if len(directory) > 0 && !strings.HasSuffix(directory, "/") {
|
||||||
|
directory += "/"
|
||||||
|
}
|
||||||
|
if str, err := m.ReadAttachment(m.BaseDir + directory + filename); err == nil {
|
||||||
|
re3 := regexp.MustCompile(v[1] + `=["']` + regexp.QuoteMeta(surl) + `["']`)
|
||||||
|
m.HTML = re3.ReplaceAllString(m.HTML, v[1]+`="cid:`+cid+`"`)
|
||||||
|
|
||||||
|
embedImages += fmt.Sprintf("--%s\r\n", boundary)
|
||||||
|
embedImages += fmt.Sprintf("Content-Type: application/octet-stream; name=\"%s\"; charset=\"%s\"\r\n", filename, m.Charset)
|
||||||
|
embedImages += fmt.Sprintf("Content-Description: %s\r\n", filename)
|
||||||
|
embedImages += fmt.Sprintf("Content-Disposition: inline; filename=\"%s\"; charset=\"%s\"\r\n", filename, m.Charset)
|
||||||
|
embedImages += fmt.Sprintf("Content-Transfer-Encoding: base64\r\nContent-ID: <%s>\r\n\r\n%s\r\n\n", cid, str)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
part := fmt.Sprintf("Content-Type: text/html\r\n\n%s\r\n\n", m.HTML)
|
||||||
|
message.WriteString(part)
|
||||||
|
message.WriteString(embedImages)
|
||||||
|
} else {
|
||||||
|
part := fmt.Sprintf("Content-Type: text/plain\r\n\n%s\r\n\n", m.Text)
|
||||||
|
message.WriteString(part)
|
||||||
|
}
|
||||||
|
if m.Files != nil {
|
||||||
|
for key, value := range m.Files {
|
||||||
|
message.WriteString(fmt.Sprintf("--%s\r\n", boundary))
|
||||||
|
message.WriteString("Content-Type: application/octect-stream\r\n")
|
||||||
|
message.WriteString("Content-Transfer-Encoding:base64\r\n")
|
||||||
|
message.WriteString(fmt.Sprintf("Content-Disposition: attachment; filename=\"%s\"; charset=\"%s\"\r\n\r\n%s\r\n\n", key, m.Charset, value))
|
||||||
|
}
|
||||||
|
message.WriteString(fmt.Sprintf("--%s--", boundary))
|
||||||
|
}
|
||||||
|
if c.secure == "SSL" || c.secure == "TLS" {
|
||||||
|
return c.SendTLS(m, message)
|
||||||
|
}
|
||||||
|
return smtp.SendMail(c.host+":"+c.port, c.smtpAuth, m.From, m.To, message.Bytes())
|
||||||
|
}
|
||||||
|
|
||||||
|
//SendTLS 通过TLS发送
|
||||||
|
func (c *SMTPClient) SendTLS(m Mail, message bytes.Buffer) error {
|
||||||
|
|
||||||
|
var ct *smtp.Client
|
||||||
|
var err error
|
||||||
|
// TLS config
|
||||||
|
//tlsconfig := &tls.Config{
|
||||||
|
// InsecureSkipVerify: true,
|
||||||
|
// ServerName: c.host,
|
||||||
|
//}
|
||||||
|
|
||||||
|
// Here is the key, you need to call tls.Dial instead of smtp.Dial
|
||||||
|
// for smtp servers running on 465 that require an ssl connection
|
||||||
|
// from the very beginning (no starttls)
|
||||||
|
conn, err := tls.Dial("tcp", c.host+":"+c.port, nil)
|
||||||
|
if err != nil {
|
||||||
|
log.Println(err, c.host)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
ct, err = smtp.NewClient(conn, c.host)
|
||||||
|
if err != nil {
|
||||||
|
log.Println(err)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
fmt.Println(c.smtpAuth)
|
||||||
|
// Auth
|
||||||
|
if err = ct.Auth(c.smtpAuth); err != nil {
|
||||||
|
log.Println("Auth Error:",
|
||||||
|
err,
|
||||||
|
c.user,
|
||||||
|
)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
// To && From
|
||||||
|
if err = ct.Mail(m.From); err != nil {
|
||||||
|
log.Println("Mail Error:", err, m.From)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, v := range m.To {
|
||||||
|
if err := ct.Rcpt(v); err != nil {
|
||||||
|
log.Println("Rcpt Error:", err, v)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Data
|
||||||
|
w, err := ct.Data()
|
||||||
|
if err != nil {
|
||||||
|
log.Println("Data Object Error:", err)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
_, err = w.Write(message.Bytes())
|
||||||
|
if err != nil {
|
||||||
|
log.Println("Write Data Object Error:", err)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
err = w.Close()
|
||||||
|
if err != nil {
|
||||||
|
log.Println("Data Object Close Error:", err)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
ct.Quit()
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// AddTo will take a valid email address and store it in the mail.
|
||||||
|
// It will return an error if the email is invalid.
|
||||||
|
func (m *Mail) AddTo(email string) error {
|
||||||
|
//Parses a single RFC 5322 address, e.g. "Barry Gibbs <bg@example.com>"
|
||||||
|
parsedAddess, e := mail.ParseAddress(email)
|
||||||
|
if e != nil {
|
||||||
|
return e
|
||||||
|
}
|
||||||
|
m.AddRecipient(parsedAddess)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// SetTos 设置收信人Email地址
|
||||||
|
func (m *Mail) SetTos(emails []string) {
|
||||||
|
m.To = emails
|
||||||
|
}
|
||||||
|
|
||||||
|
// AddToName will add a new receipient name to mail
|
||||||
|
func (m *Mail) AddToName(name string) {
|
||||||
|
m.ToName = append(m.ToName, name)
|
||||||
|
}
|
||||||
|
|
||||||
|
// AddRecipient will take an already parsed mail.Address
|
||||||
|
func (m *Mail) AddRecipient(receipient *mail.Address) {
|
||||||
|
m.To = append(m.To, receipient.Address)
|
||||||
|
if len(receipient.Name) > 0 {
|
||||||
|
m.ToName = append(m.ToName, receipient.Name)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// AddSubject will set the subject of the mail
|
||||||
|
func (m *Mail) AddSubject(s string) {
|
||||||
|
m.Subject = s
|
||||||
|
}
|
||||||
|
|
||||||
|
// AddHTML will set the body of the mail
|
||||||
|
func (m *Mail) AddHTML(html string) {
|
||||||
|
m.HTML = html
|
||||||
|
}
|
||||||
|
|
||||||
|
// AddText will set the body of the email
|
||||||
|
func (m *Mail) AddText(text string) {
|
||||||
|
m.Text = text
|
||||||
|
}
|
||||||
|
|
||||||
|
// AddFrom will set the senders email
|
||||||
|
func (m *Mail) AddFrom(from string) error {
|
||||||
|
//Parses a single RFC 5322 address, e.g. "Barry Gibbs <bg@example.com>"
|
||||||
|
parsedAddess, e := mail.ParseAddress(from)
|
||||||
|
if e != nil {
|
||||||
|
return e
|
||||||
|
}
|
||||||
|
m.From = parsedAddess.Address
|
||||||
|
m.FromName = parsedAddess.Name
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// AddBCC works like AddTo but for BCC
|
||||||
|
func (m *Mail) AddBCC(email string) error {
|
||||||
|
parsedAddess, e := mail.ParseAddress(email)
|
||||||
|
if e != nil {
|
||||||
|
return e
|
||||||
|
}
|
||||||
|
m.Bcc = append(m.Bcc, parsedAddess.Address)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// AddRecipientBCC works like AddRecipient but for BCC
|
||||||
|
func (m *Mail) AddRecipientBCC(email *mail.Address) {
|
||||||
|
m.Bcc = append(m.Bcc, email.Address)
|
||||||
|
}
|
||||||
|
|
||||||
|
// AddFromName will set the senders name
|
||||||
|
func (m *Mail) AddFromName(name string) {
|
||||||
|
m.FromName = name
|
||||||
|
}
|
||||||
|
|
||||||
|
// AddReplyTo will set the return address
|
||||||
|
func (m *Mail) AddReplyTo(reply string) {
|
||||||
|
m.ReplyTo = reply
|
||||||
|
}
|
||||||
|
|
||||||
|
// AddDate specifies the date
|
||||||
|
func (m *Mail) AddDate(date string) {
|
||||||
|
m.Date = date
|
||||||
|
}
|
||||||
|
|
||||||
|
// AddAttachment will include file/s in mail
|
||||||
|
func (m *Mail) AddAttachment(filePath string) error {
|
||||||
|
if m.Files == nil {
|
||||||
|
m.Files = make(map[string]string)
|
||||||
|
}
|
||||||
|
str, err := m.ReadAttachment(filePath)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
_, filename := filepath.Split(filePath)
|
||||||
|
m.Files[filename] = str
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// ReadAttachment reading attachment
|
||||||
|
func (m *Mail) ReadAttachment(filePath string) (string, error) {
|
||||||
|
file, e := ioutil.ReadFile(filePath)
|
||||||
|
if e != nil {
|
||||||
|
return "", e
|
||||||
|
}
|
||||||
|
encoded := base64.StdEncoding.EncodeToString(file)
|
||||||
|
totalChars := len(encoded)
|
||||||
|
maxLength := 500 //每行最大长度
|
||||||
|
totalLines := totalChars / maxLength
|
||||||
|
var buf bytes.Buffer
|
||||||
|
for i := 0; i < totalLines; i++ {
|
||||||
|
buf.WriteString(encoded[i*maxLength:(i+1)*maxLength] + "\n")
|
||||||
|
}
|
||||||
|
buf.WriteString(encoded[totalLines*maxLength:])
|
||||||
|
return buf.String(), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// AddHeaders addding header string
|
||||||
|
func (m *Mail) AddHeaders(headers string) {
|
||||||
|
m.Headers = headers
|
||||||
|
}
|
||||||
|
|
||||||
|
// =======================================================
|
||||||
|
// unencryptedAuth
|
||||||
|
// =======================================================
|
||||||
|
|
||||||
|
type unencryptedAuth struct {
|
||||||
|
smtp.Auth
|
||||||
|
}
|
||||||
|
|
||||||
|
func (a unencryptedAuth) Start(server *smtp.ServerInfo) (string, []byte, error) {
|
||||||
|
s := *server
|
||||||
|
s.TLS = true
|
||||||
|
return a.Auth.Start(&s)
|
||||||
|
}
|
||||||
|
|
||||||
|
// ======================================================
|
||||||
|
// loginAuth
|
||||||
|
// ======================================================
|
||||||
|
|
||||||
|
type loginAuth struct {
|
||||||
|
username, password string
|
||||||
|
}
|
||||||
|
|
||||||
|
// LoginAuth loginAuth方式认证
|
||||||
|
func LoginAuth(username, password string) smtp.Auth {
|
||||||
|
return &loginAuth{username, password}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (a *loginAuth) Start(server *smtp.ServerInfo) (string, []byte, error) {
|
||||||
|
if !server.TLS {
|
||||||
|
return "", nil, errors.New("unencrypted connection")
|
||||||
|
}
|
||||||
|
return "LOGIN", []byte(a.username), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (a *loginAuth) Next(fromServer []byte, more bool) ([]byte, error) {
|
||||||
|
if more {
|
||||||
|
switch string(fromServer) {
|
||||||
|
case "Username:":
|
||||||
|
return []byte(a.username), nil
|
||||||
|
case "Password:":
|
||||||
|
return []byte(a.password), nil
|
||||||
|
default:
|
||||||
|
return nil, errors.New("Unkown fromServer")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil, nil
|
||||||
|
}
|
|
@ -0,0 +1,29 @@
|
||||||
|
package mail
|
||||||
|
|
||||||
|
import (
|
||||||
|
"os"
|
||||||
|
"testing"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestSend(t *testing.T) {
|
||||||
|
conf := &SMTPConfig{
|
||||||
|
Username: "swh@adm***.com",
|
||||||
|
Password: "",
|
||||||
|
Host: "smtp.exmail.qq.com",
|
||||||
|
Port: 465,
|
||||||
|
Secure: "SSL",
|
||||||
|
}
|
||||||
|
c := NewSMTPClient(conf)
|
||||||
|
m := NewMail()
|
||||||
|
m.AddTo("brother <1556****@qq.com>")
|
||||||
|
m.AddFrom("hank <" + conf.Username + ">")
|
||||||
|
m.AddSubject("Testing")
|
||||||
|
m.AddText("Some text :)")
|
||||||
|
filepath, _ := os.Getwd()
|
||||||
|
m.AddAttachment(filepath + "/README.md")
|
||||||
|
if e := c.Send(m); e != nil {
|
||||||
|
t.Error(e)
|
||||||
|
} else {
|
||||||
|
t.Log("发送成功")
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,51 @@
|
||||||
|
package mail
|
||||||
|
|
||||||
|
import (
|
||||||
|
"net/mail"
|
||||||
|
)
|
||||||
|
|
||||||
|
func MailAddr(name string, address string) *mail.Address {
|
||||||
|
return &mail.Address{
|
||||||
|
Name: name,
|
||||||
|
Address: address,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
type Attachments struct {
|
||||||
|
Files []string
|
||||||
|
BaseDir string
|
||||||
|
}
|
||||||
|
|
||||||
|
//SendMail 发送电邮
|
||||||
|
func SendMail(subject string, content string, receiver, sender string,
|
||||||
|
bcc []string, smtpConfig *SMTPConfig, attachments *Attachments) error {
|
||||||
|
c := NewSMTPClient(smtpConfig)
|
||||||
|
m := NewMail()
|
||||||
|
err := m.AddTo(receiver) //receiver e.g. "Barry Gibbs <bg@example.com>"
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
err = m.AddFrom(sender)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
m.AddSubject(subject)
|
||||||
|
//m.AddText("Some text :)")
|
||||||
|
m.AddHTML(content)
|
||||||
|
if attachments != nil {
|
||||||
|
m.BaseDir = attachments.BaseDir
|
||||||
|
for _, v := range attachments.Files {
|
||||||
|
err = m.AddAttachment(v)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
for _, addr := range bcc {
|
||||||
|
err = m.AddBCC(addr)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return c.Send(m)
|
||||||
|
}
|
Loading…
Reference in New Issue