//go:build plus package mediasenders import ( "crypto/tls" "encoding/base64" "errors" teaconst "github.com/TeaOSLab/EdgeAPI/internal/const" "net" "net/mail" "net/smtp" "strconv" "time" ) type MailInfo struct { To string Subject string Body string } // EmailMedia 邮件媒介 type EmailMedia struct { SMTP string `yaml:"smtp" json:"smtp"` // SMTP地址:host:port Username string `yaml:"username" json:"username"` // 用户名 Password string `yaml:"password" json:"password"` // 密码 From string `yaml:"from" json:"from"` // 发件人 FromName string `yaml:"fromName" json:"fromName"` // 发件人名称 Protocol string `yaml:"protocol" json:"protocol"` // 协议:tcp/tls } // NewEmailMedia 获取新对象 func NewEmailMedia() *EmailMedia { return &EmailMedia{} } func (this *EmailMedia) Send(user string, subject string, body string, productName string, datetime string) (resp []byte, err error) { if len(this.SMTP) == 0 { return nil, errors.New("host address should be specified") } // 自动加端口 if _, _, err := net.SplitHostPort(this.SMTP); err != nil { this.SMTP += ":587" } if len(this.From) == 0 { this.From = this.Username } var contentType = "Content-Type: text/html; charset=UTF-8" var senderName = this.FromName if len(senderName) == 0 { if len(productName) > 0 { senderName = productName } else { senderName = teaconst.GlobalProductName } subject = "[" + senderName + "]" + subject } var msg = []byte("To: " + user + "\r\nFrom: " + strconv.Quote(senderName) + " <" + this.From + ">\r\nSubject: " + "=?utf-8?B?" + base64.StdEncoding.EncodeToString([]byte(subject)) + "?=" + "\r\n" + contentType + "\r\n\r\n" + body) return nil, this.SendMail(this.From, []string{user}, msg) } // RequireUser 是否需要用户标识 func (this *EmailMedia) RequireUser() bool { return true } // SendMail 发送邮件 func (this *EmailMedia) SendMail(from string, to []string, message []byte) error { _, err := mail.ParseAddress(from) if err != nil { return err } if len(to) == 0 { return errors.New("recipients should not be empty") } for _, to1 := range to { _, err := mail.ParseAddress(to1) if err != nil { return err } } client, err := this.Connect() if err != nil { return err } defer func() { _ = client.Quit() _ = client.Close() }() // To && From if err := client.Mail(from); err != nil { return err } for _, to1 := range to { if err := client.Rcpt(to1); err != nil { return err } } // Data w, err := client.Data() if err != nil { return err } _, err = w.Write(message) if err != nil { return err } err = w.Close() if err != nil { return err } return nil } // SendMails 发送邮件 func (this *EmailMedia) SendMails(mails []*MailInfo, productName string) error { if len(mails) == 0 { return nil } _, err := mail.ParseAddress(this.From) if err != nil { return err } client, err := this.Connect() if err != nil { return err } defer func() { _ = client.Quit() _ = client.Close() }() // 发送者 var senderName = this.FromName if len(senderName) == 0 { if len(productName) > 0 { senderName = productName } else { senderName = teaconst.GlobalProductName } } // 创建邮件 for _, mailInfo := range mails { if len(mailInfo.To) == 0 { continue } _, err = mail.ParseAddress(mailInfo.To) if err != nil { return err } if err := client.Mail(this.From); err != nil { return err } if err := client.Rcpt(mailInfo.To); err != nil { return err } // Data w, err := client.Data() if err != nil { return err } _, err = w.Write([]byte("To: " + mailInfo.To + "\r\nFrom: " + strconv.Quote(senderName) + " <" + this.From + ">\r\nSubject: " + mailInfo.Subject + "\r\n" + "Content-Type: text/html; charset=UTF-8" + "\r\n\r\n" + mailInfo.Body)) if err != nil { return err } err = w.Close() if err != nil { return err } } return nil } func (this *EmailMedia) Connect() (*smtp.Client, error) { var serverName = this.SMTP var username = this.Username var password = this.Password host, port, _ := net.SplitHostPort(serverName) var client *smtp.Client // TLS config var tlsConfig = &tls.Config{ InsecureSkipVerify: true, ServerName: host, } if this.Protocol == "tcp" || (len(this.Protocol) == 0 && port == "25") { // 25 port: default tcp port conn, err := net.DialTimeout("tcp", serverName, 10*time.Second) if err != nil { return nil, err } client, err = smtp.NewClient(conn, host) if err != nil { return nil, err } } else if port == "587" { // 587 port: prefer START_TLS conn, err := net.DialTimeout("tcp", serverName, 10*time.Second) if err != nil { conn, err := tls.Dial("tcp", serverName, tlsConfig) if err != nil { return nil, err } client, err = smtp.NewClient(conn, host) if err != nil { return nil, err } } else { client, err = smtp.NewClient(conn, host) if err != nil { return nil, err } err = client.StartTLS(tlsConfig) if err != nil { return nil, err } } } else { conn, err := tls.Dial("tcp", serverName, tlsConfig) if err != nil { conn, err := net.DialTimeout("tcp", serverName, 10*time.Second) if err != nil { return nil, err } client, err = smtp.NewClient(conn, host) if err != nil { return nil, err } err = client.StartTLS(tlsConfig) if err != nil { return nil, err } } else { client, err = smtp.NewClient(conn, host) if err != nil { return nil, err } } } // 认证 var auth = smtp.PlainAuth("", username, password, host) if err := client.Auth(auth); err != nil { _ = client.Quit() _ = client.Close() return nil, err } return client, nil }