smtp支持ssl

This commit is contained in:
Yun
2025-10-30 14:13:18 +08:00
parent 4522af82f3
commit e0939d8728
3 changed files with 115 additions and 18 deletions
+1
View File
@@ -7,6 +7,7 @@ type EmialConfigDataMailgun struct {
}
type EmailConfigDataSmtp struct {
IsSSL bool `json:"is_ssl"` // 是否SSL
Username string `json:"username"` // 邮箱账号
Password string `json:"password"` // 授权码
Host string `json:"host"` // SMTP 服务器【默认smtpdm.aliyun.com】
+83 -1
View File
@@ -3,6 +3,7 @@ package smtp
import (
"bytes"
"context"
"crypto/tls"
"encoding/base64"
"errors"
"fmt"
@@ -45,7 +46,6 @@ func (l *Smtp) SetOption(ctx context.Context, opt ...interfaces.Option) (interfa
return nil, errors.New("not smtp")
}
l.auth = smtp.PlainAuth("", l.Options.Smtp.Username, l.Options.Smtp.Password, l.Options.Smtp.Host)
return l, nil
}
@@ -53,6 +53,16 @@ func (l *Smtp) Send(ctx context.Context, message interfaces.Message) error {
if l.Options.Smtp == nil {
return errors.New("not init")
}
if l.Options.Smtp.IsSSL {
//
return l.SendSSL(ctx, message)
}
return l.SendPlain(ctx, message)
}
// 明文
func (l *Smtp) SendPlain(ctx context.Context, message interfaces.Message) error {
// .Auth()
buffer := bytes.NewBuffer(nil)
boundary := "YunBoundaryYun"
@@ -148,10 +158,82 @@ func (l *Smtp) Send(ctx context.Context, message interfaces.Message) error {
buffer.WriteString("\r\n--" + boundary + "--\r\n")
b := buffer.Bytes()
l.auth = smtp.PlainAuth("", l.Options.Smtp.Username, l.Options.Smtp.Password, l.Options.Smtp.Host)
err := smtp.SendMail(l.Options.Smtp.Host+":"+l.Options.Smtp.Port, l.auth, l.Options.Smtp.Username, message.To, b)
return err
}
// SSL
func (l *Smtp) SendSSL(ctx context.Context, message interfaces.Message) error {
// 邮件内容格式(必须符合SMTP协议规范)
contentType := "Content-Type: text/html; charset=UTF-8"
msg := []byte("To: " + message.To[0] + "\r\n" +
"From: " + message.Form + "\r\n" +
"Subject: " + message.Subject + "\r\n" +
contentType + "\r\n\r\n" +
message.Body)
// 解析收件人(支持多个)
// toList := strings.Split(message.To, ",")
// 1. 建立TCP连接(SSL通常用465端口)
if l.Options.Smtp.Port == "" {
l.Options.Smtp.Port = "465"
}
host := l.Options.Smtp.Host + ":" + l.Options.Smtp.Port
conn, err := tls.Dial("tcp", host, &tls.Config{
InsecureSkipVerify: false, // 生产环境不建议跳过证书验证
ServerName: strings.Split(host, ":")[0], // 服务器域名(用于证书验证)
})
if err != nil {
return fmt.Errorf("连接服务器失败: %v", err)
}
defer conn.Close()
// 2. 创建SMTP客户端
client, err := smtp.NewClient(conn, strings.Split(host, ":")[0])
if err != nil {
return fmt.Errorf("创建SMTP客户端失败: %v", err)
}
// 3. 身份验证(使用登录邮箱和授权码)
auth := smtp.PlainAuth("", l.Options.Smtp.Username, l.Options.Smtp.Password, strings.Split(host, ":")[0])
if err := client.Auth(auth); err != nil {
return fmt.Errorf("身份验证失败: %v", err)
}
// 4. 设置发件人
if err := client.Mail(l.Options.Smtp.Username); err != nil {
return fmt.Errorf("设置发件人失败: %v", err)
}
// 5. 设置收件人(支持多个)
for _, addr := range message.To {
if err := client.Rcpt(addr); err != nil {
return fmt.Errorf("设置收件人 %s 失败: %v", addr, err)
}
}
// 6. 发送邮件内容
w, err := client.Data()
if err != nil {
return fmt.Errorf("获取数据写入流失败: %v", err)
}
_, err = w.Write(msg)
if err != nil {
return fmt.Errorf("写入邮件内容失败: %v", err)
}
err = w.Close()
if err != nil {
return fmt.Errorf("关闭写入流失败: %v", err)
}
// 7. 退出SMTP会话
return client.Quit()
}
// 格式化header
func (l *Smtp) writeHeader(buffer *bytes.Buffer, Header map[string]string) string {
header := ""
+31 -17
View File
@@ -3,8 +3,6 @@ package smtp_test
import (
"context"
"fmt"
"io"
"net/http"
"testing"
"code.yun.ink/pkg/mailx/interfaces"
@@ -15,35 +13,51 @@ func TestMail(t *testing.T) {
sm := smtp.NewSmtp()
ctx := context.Background()
// ini, err := sm.SetOption(ctx, interfaces.SetSmtp(&interfaces.EmailConfigDataSmtp{
// Username: "support@email.blueoceanpay.com",
// Password: "SupporT2017",
// ReplyTo: "",
// Host: "smtpdm-ap-southeast-1.aliyun.com",
// Port: "80",
// }))
// if err != nil {
// t.Fatal(err)
// }
ini, err := sm.SetOption(ctx, interfaces.SetSmtp(&interfaces.EmailConfigDataSmtp{
Username: "support@email.blueoceanpay.com",
Password: "SupporT2017",
IsSSL: true,
Username: "demo@yunink.net",
Password: "aAhVPzHydLoc9Fj8",
ReplyTo: "",
Host: "smtpdm-ap-southeast-1.aliyun.com",
Port: "80",
Host: "smtp.exmail.qq.com",
Port: "465",
}))
if err != nil {
t.Fatal(err)
}
req, err := http.Get("https://baidu.com")
if err != nil {
t.Fatal(err)
}
defer req.Body.Close()
by, err := io.ReadAll(req.Body)
if err != nil {
t.Fatal(err)
}
// req, err := http.Get("https://baidu.com")
// if err != nil {
// t.Fatal(err)
// }
// defer req.Body.Close()
// by, err := io.ReadAll(req.Body)
// if err != nil {
// t.Fatal(err)
// }
msg := interfaces.Message{
Form: "demo@yunink.net",
To: []string{"995116474@qq.com"},
Cc: []string{"287852692@qq.com"},
Bcc: []string{"1362716835@qq.com"},
ReplyTo: "huangxinyun@dreaminglife.cn",
Subject: "test mail",
Body: string(by),
Subject: "测试邮件",
Body: "这是测试邮件", //string(by),
Attachment: []interfaces.MessageAttachment{
// {
// Name: "/code/statistic/out.xlsx",