| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141 |
- package infra
- import (
- "crypto/tls"
- "fmt"
- "net"
- "net/smtp"
- "strings"
- )
- // SMTPMailConfig SMTP 发信参数
- type SMTPMailConfig struct {
- Host string
- Port int
- User string
- Password string
- FromEmail string
- FromName string
- }
- // SendSMTPMail 通过 SMTP 发送纯文本邮件(支持 465 SSL / 587 STARTTLS)
- func SendSMTPMail(cfg SMTPMailConfig, to, subject, body string) error {
- if cfg.Host == "" || cfg.FromEmail == "" {
- return fmt.Errorf("SMTP 未配置完整")
- }
- to = strings.TrimSpace(to)
- if to == "" {
- return fmt.Errorf("收件人邮箱为空")
- }
- port := cfg.Port
- if port <= 0 {
- port = 465
- }
- from := cfg.FromEmail
- fromHeader := from
- if cfg.FromName != "" {
- fromHeader = fmt.Sprintf("%s <%s>", cfg.FromName, from)
- }
- msg := buildPlainTextMessage(fromHeader, to, subject, body)
- addr := fmt.Sprintf("%s:%d", cfg.Host, port)
- if port == 465 {
- return sendSMTPSImplicitTLS(addr, cfg, from, to, msg)
- }
- return sendSMTPStartTLS(addr, cfg, from, to, msg)
- }
- func buildPlainTextMessage(from, to, subject, body string) []byte {
- var b strings.Builder
- b.WriteString("From: " + from + "\r\n")
- b.WriteString("To: " + to + "\r\n")
- b.WriteString("Subject: " + subject + "\r\n")
- b.WriteString("MIME-Version: 1.0\r\n")
- b.WriteString("Content-Type: text/plain; charset=UTF-8\r\n")
- b.WriteString("\r\n")
- b.WriteString(body)
- return []byte(b.String())
- }
- func sendSMTPSImplicitTLS(addr string, cfg SMTPMailConfig, from, to string, msg []byte) error {
- tlsCfg := &tls.Config{ServerName: cfg.Host}
- conn, err := tls.Dial("tcp", addr, tlsCfg)
- if err != nil {
- return fmt.Errorf("连接 SMTP 失败: %w", err)
- }
- defer conn.Close()
- client, err := smtp.NewClient(conn, cfg.Host)
- if err != nil {
- return fmt.Errorf("创建 SMTP 客户端失败: %w", err)
- }
- defer client.Close()
- if cfg.User != "" {
- if err := client.Auth(smtp.PlainAuth("", cfg.User, cfg.Password, cfg.Host)); err != nil {
- return fmt.Errorf("SMTP 认证失败: %w", err)
- }
- }
- if err := client.Mail(from); err != nil {
- return fmt.Errorf("设置发件人失败: %w", err)
- }
- if err := client.Rcpt(to); err != nil {
- return fmt.Errorf("设置收件人失败: %w", err)
- }
- w, err := client.Data()
- if err != nil {
- return fmt.Errorf("打开邮件正文失败: %w", err)
- }
- if _, err := w.Write(msg); err != nil {
- return fmt.Errorf("写入邮件正文失败: %w", err)
- }
- if err := w.Close(); err != nil {
- return fmt.Errorf("关闭邮件正文失败: %w", err)
- }
- return client.Quit()
- }
- func sendSMTPStartTLS(addr string, cfg SMTPMailConfig, from, to string, msg []byte) error {
- conn, err := net.Dial("tcp", addr)
- if err != nil {
- return fmt.Errorf("连接 SMTP 失败: %w", err)
- }
- defer conn.Close()
- host := cfg.Host
- client, err := smtp.NewClient(conn, host)
- if err != nil {
- return fmt.Errorf("创建 SMTP 客户端失败: %w", err)
- }
- defer client.Close()
- if ok, _ := client.Extension("STARTTLS"); ok {
- if err := client.StartTLS(&tls.Config{ServerName: host}); err != nil {
- return fmt.Errorf("STARTTLS 失败: %w", err)
- }
- }
- if cfg.User != "" {
- if err := client.Auth(smtp.PlainAuth("", cfg.User, cfg.Password, host)); err != nil {
- return fmt.Errorf("SMTP 认证失败: %w", err)
- }
- }
- if err := client.Mail(from); err != nil {
- return fmt.Errorf("设置发件人失败: %w", err)
- }
- if err := client.Rcpt(to); err != nil {
- return fmt.Errorf("设置收件人失败: %w", err)
- }
- w, err := client.Data()
- if err != nil {
- return fmt.Errorf("打开邮件正文失败: %w", err)
- }
- if _, err := w.Write(msg); err != nil {
- return fmt.Errorf("写入邮件正文失败: %w", err)
- }
- if err := w.Close(); err != nil {
- return fmt.Errorf("关闭邮件正文失败: %w", err)
- }
- return client.Quit()
- }
|