/*
 邮件发送操作包装，<br/>
 暂支持邮件发送功能。<br/>
 邮件内容若想模板化，请在应用程序中处理。<br/>
 暂不支持附件
*/
package spiderutil

import (
	"bytes"
	"crypto/tls"
	"fmt"
	"log"
	"net"
	"net/smtp"
	"strings"
)

const (
	SPLIT = "\r\n"
)

//邮箱认证信息
type MailAuth struct {
	SmtpHost string //邮箱服务器
	SmtpPort int    //邮箱端口
	User     string //用户
	Pwd      string //密码
}
type Mail struct {
	Auth    *MailAuth
	Message *Message
}

//邮件消息
type Message struct {
	Subject string   //主题
	From    string   //来自
	To      []string //接收人,可以是多个接收人
	//Cc      []string //抄送人,可以是多个接收人
	Body string //html邮件内容
}

func New(smtpHost string, smtpPort int, user, pwd string) *Mail {
	auth := &MailAuth{
		SmtpHost: smtpHost,
		SmtpPort: smtpPort,
		User:     user,
		Pwd:      pwd,
	}
	m := &Mail{
		Auth: auth,
	}
	return m
}

//发送邮件
func (m *Mail) SendMail(sub, from string, to []string, body string) {
	msg := &Message{
		Subject: sub,
		From:    from,
		To:      to,
		Body:    body,
	}
	m.Message = msg
	go m.sendMailJob()
}

//发送邮件
func (m *Mail) sendMailJob() error {
	auth := m.Auth
	mes := m.Message
	buf := bytes.NewBuffer(nil)
	buf.WriteString("Subject: " + mes.Subject + SPLIT)
	buf.WriteString("MIME-Version: 1.0" + SPLIT)
	buf.WriteString("From: " + mes.From + SPLIT)
	buf.WriteString("To: " + strings.Join(mes.To, ";") + SPLIT)
	//buf.WriteString("Cc: " + strings.Join(mes.Cc, ";") + SPLIT)
	buf.WriteString("Content-Type: " + "text/html; charset=UTF-8" + SPLIT)
	buf.WriteString(SPLIT + mes.Body)
	smtpauth := smtp.PlainAuth(
		"",
		auth.User,
		auth.Pwd,
		auth.SmtpHost,
	)
	err := m.sendMailUsingTLS(
		fmt.Sprintf("%s:%d", auth.SmtpHost, auth.SmtpPort),
		smtpauth,
		auth.User,
		mes.To,
		buf.Bytes(),
	)
	if err != nil {
		fmt.Println(err.Error())
	}
	return err
}

func (m *Mail) dial(addr string) (*smtp.Client, error) {
	conn, err := tls.Dial("tcp", addr, nil)
	if err != nil {
		log.Println("Dialing Error:", err)
		return nil, err
	}
	//分解主机端口字符串
	host, _, _ := net.SplitHostPort(addr)
	return smtp.NewClient(conn, host)
}

//安全协议发送邮件
func (m *Mail) sendMailUsingTLS(addr string, auth smtp.Auth, from string,
	to []string, msg []byte) (err error) {

	//create smtp client
	c, err := m.dial(addr)
	if err != nil {
		log.Println("Create smpt client error:", err)
		return err
	}
	defer c.Close()

	if auth != nil {
		if ok, _ := c.Extension("AUTH"); ok {
			if err = c.Auth(auth); err != nil {
				log.Println("Error during AUTH", err)
				return err
			}
		}
	}

	if err = c.Mail(from); err != nil {
		return err
	}

	for _, addr := range to {
		if err = c.Rcpt(addr); err != nil {
			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()
}
