Files
waf-platform/EdgeAPI/internal/senders/mediasenders/media_email.go
2026-02-04 20:27:13 +08:00

280 lines
5.7 KiB
Go
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

//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
}