smtp支持ssl
This commit is contained in:
@@ -7,6 +7,7 @@ type EmialConfigDataMailgun struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
type EmailConfigDataSmtp struct {
|
type EmailConfigDataSmtp struct {
|
||||||
|
IsSSL bool `json:"is_ssl"` // 是否SSL
|
||||||
Username string `json:"username"` // 邮箱账号
|
Username string `json:"username"` // 邮箱账号
|
||||||
Password string `json:"password"` // 授权码
|
Password string `json:"password"` // 授权码
|
||||||
Host string `json:"host"` // SMTP 服务器【默认smtpdm.aliyun.com】
|
Host string `json:"host"` // SMTP 服务器【默认smtpdm.aliyun.com】
|
||||||
|
|||||||
+83
-1
@@ -3,6 +3,7 @@ package smtp
|
|||||||
import (
|
import (
|
||||||
"bytes"
|
"bytes"
|
||||||
"context"
|
"context"
|
||||||
|
"crypto/tls"
|
||||||
"encoding/base64"
|
"encoding/base64"
|
||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
@@ -45,7 +46,6 @@ func (l *Smtp) SetOption(ctx context.Context, opt ...interfaces.Option) (interfa
|
|||||||
return nil, errors.New("not smtp")
|
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
|
return l, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -53,6 +53,16 @@ func (l *Smtp) Send(ctx context.Context, message interfaces.Message) error {
|
|||||||
if l.Options.Smtp == nil {
|
if l.Options.Smtp == nil {
|
||||||
return errors.New("not init")
|
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()
|
// .Auth()
|
||||||
buffer := bytes.NewBuffer(nil)
|
buffer := bytes.NewBuffer(nil)
|
||||||
boundary := "YunBoundaryYun"
|
boundary := "YunBoundaryYun"
|
||||||
@@ -148,10 +158,82 @@ func (l *Smtp) Send(ctx context.Context, message interfaces.Message) error {
|
|||||||
|
|
||||||
buffer.WriteString("\r\n--" + boundary + "--\r\n")
|
buffer.WriteString("\r\n--" + boundary + "--\r\n")
|
||||||
b := buffer.Bytes()
|
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)
|
err := smtp.SendMail(l.Options.Smtp.Host+":"+l.Options.Smtp.Port, l.auth, l.Options.Smtp.Username, message.To, b)
|
||||||
return err
|
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
|
// 格式化header
|
||||||
func (l *Smtp) writeHeader(buffer *bytes.Buffer, Header map[string]string) string {
|
func (l *Smtp) writeHeader(buffer *bytes.Buffer, Header map[string]string) string {
|
||||||
header := ""
|
header := ""
|
||||||
|
|||||||
+31
-17
@@ -3,8 +3,6 @@ package smtp_test
|
|||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
"fmt"
|
"fmt"
|
||||||
"io"
|
|
||||||
"net/http"
|
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
"code.yun.ink/pkg/mailx/interfaces"
|
"code.yun.ink/pkg/mailx/interfaces"
|
||||||
@@ -15,35 +13,51 @@ func TestMail(t *testing.T) {
|
|||||||
sm := smtp.NewSmtp()
|
sm := smtp.NewSmtp()
|
||||||
ctx := context.Background()
|
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{
|
ini, err := sm.SetOption(ctx, interfaces.SetSmtp(&interfaces.EmailConfigDataSmtp{
|
||||||
Username: "support@email.blueoceanpay.com",
|
IsSSL: true,
|
||||||
Password: "SupporT2017",
|
Username: "demo@yunink.net",
|
||||||
|
Password: "aAhVPzHydLoc9Fj8",
|
||||||
ReplyTo: "",
|
ReplyTo: "",
|
||||||
Host: "smtpdm-ap-southeast-1.aliyun.com",
|
Host: "smtp.exmail.qq.com",
|
||||||
Port: "80",
|
Port: "465",
|
||||||
}))
|
}))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal(err)
|
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 {
|
// req, err := http.Get("https://baidu.com")
|
||||||
t.Fatal(err)
|
// 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{
|
msg := interfaces.Message{
|
||||||
|
Form: "demo@yunink.net",
|
||||||
To: []string{"995116474@qq.com"},
|
To: []string{"995116474@qq.com"},
|
||||||
Cc: []string{"287852692@qq.com"},
|
Cc: []string{"287852692@qq.com"},
|
||||||
Bcc: []string{"1362716835@qq.com"},
|
Bcc: []string{"1362716835@qq.com"},
|
||||||
ReplyTo: "huangxinyun@dreaminglife.cn",
|
ReplyTo: "huangxinyun@dreaminglife.cn",
|
||||||
Subject: "test mail",
|
Subject: "测试邮件",
|
||||||
Body: string(by),
|
Body: "这是测试邮件", //string(by),
|
||||||
Attachment: []interfaces.MessageAttachment{
|
Attachment: []interfaces.MessageAttachment{
|
||||||
// {
|
// {
|
||||||
// Name: "/code/statistic/out.xlsx",
|
// Name: "/code/statistic/out.xlsx",
|
||||||
|
|||||||
Reference in New Issue
Block a user