| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467 | 
							- 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"
 
- 	"github.com/astaxie/beego"
 
- )
 
- var (
 
- 	imageRegex  = regexp.MustCompile(`(src|background)=["'](.*?)["']`)
 
- 	schemeRegxp = regexp.MustCompile(`^[a-zA-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, tlsconfig)
 
- 	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
 
- 	}
 
- 	//if err := ct.StartTLS(tlsconfig);err != nil {
 
- 	//	log.Println("StartTLS Error:",err,c.host,c.port)
 
- 	//	return err
 
- 	//}
 
- 	//if err := ct.StartTLS(tlsconfig);err != nil {
 
- 	//	fmt.Println(err)
 
- 	//	return err
 
- 	//}
 
- 	fmt.Println(c.smtpAuth)
 
- 	if ok,s := ct.Extension("AUTH"); ok {
 
- 		beego.Info(s)
 
- 		// 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 <[email protected]>"
 
- 	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 <[email protected]>"
 
- 	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
 
- }
 
 
  |