代码拉取完成,页面将自动刷新
package utils
import (
"context"
"crypto/tls"
"encoding/base64"
"errors"
"fmt"
"net"
"net/smtp"
"strings"
"time"
)
const (
dialTLS uint32 = iota + 1
startTLS
)
type mail struct {
host string
port string
username string
password string
subject string
content string
contentType string // plain / html
timeout time.Duration
encryption uint32
}
// MailContact 邮件联系人
type MailContact struct {
name string
email string
}
func NewMailContact(name, email string) *MailContact {
mc := MailContact{
name: name,
email: email,
}
return &mc
}
// String 将邮件联系人结构转写为邮件内容格式
func (c *MailContact) String() string {
if len(c.name) == 0 {
return fmt.Sprintf("<%s>", c.email)
} else {
return fmt.Sprintf("\"=?utf-8?B?%s?=\"<%s>", base64enc(c.name), c.email)
}
}
func base64enc(src string) string {
return base64.StdEncoding.EncodeToString(StringToBytes(src))
}
// buildContent 构造邮件内容, 区分text/plain和text/html
func buildContent(from *MailContact, toList []*MailContact, subject string, content string, ctype string) string {
builder := strings.Builder{}
builder.WriteString("MIME-Version: 1.0\n")
builder.WriteString(fmt.Sprintf("Date: %s\n", time.Now().Format(time.RFC1123Z)))
builder.WriteString(fmt.Sprintf("From: %s\n", from.String()))
builder.WriteString("To: ")
for i, to := range toList {
if i != 0 {
builder.WriteString(", ")
}
builder.WriteString(to.String())
}
builder.WriteString("\n")
builder.WriteString(fmt.Sprintf("Subject: =?utf-8?B?%s?=\n", base64enc(subject)))
builder.WriteString(fmt.Sprintf("Content-Type: text/%s; charset=UTF-8\n", ctype))
builder.WriteString("\n")
builder.WriteString(content)
return builder.String()
}
// NewMail 构造新邮件实例
//
// NewMail().Server(host, port).Auth(username, password).Timeout(5*time.Second).WithTLS().WithHTML().Content(subject, content).Send(sendto, from)
// 构造实例 -> 服务器信息 -> 用户认证信息 -> 设置连接超时(optional) -> 启用SSL(optional) -> 使用text/html(optional) -> 填写邮件内容 -> 投递邮件
func NewMail() *mail {
return &mail{contentType: "plain"}
}
// Auth 用户身份认证
func (m *mail) Auth(username, password string) *mail {
m.username = username
m.password = password
return m
}
// Content 邮件内容
func (m *mail) Content(subject, content string) *mail {
m.subject = subject
m.content = content
return m
}
// Send 投递邮件
// 如果未启用TLS加密,则调用标准库smtp.SendMail
// 若启用TLS,则建立TLS连接
func (m *mail) Send(sendto []*MailContact, from *MailContact) (err error) {
// 使用context控制超时时间
timeout := m.timeout
if timeout == 0 {
timeout = 10 * time.Second
}
ctx, cancel := context.WithTimeout(context.Background(), timeout)
defer cancel()
// 构造邮件内容
from.email = m.username
msg := StringToBytes(buildContent(from, sendto, m.subject, m.content, m.contentType))
// 创建连接
var conn net.Conn
conn, err = m.connect(ctx, "tcp")
if err != nil {
return
}
defer conn.Close()
// 通过通道接收邮件发送错误消息
errCh := make(chan error)
go func() {
toMailList := make([]string, len(sendto))
for i, mc := range sendto {
toMailList[i] = mc.email
}
errCh <- m.write(conn, toMailList, msg)
close(errCh)
}()
// 超时机制
select {
case <-ctx.Done():
err = errors.New("connection time out")
case err = <-errCh:
// returns the error
}
return
}
// Server 邮件服务器
func (m *mail) Server(host, port string) *mail {
m.host = host
m.port = port
return m
}
// Timeout 指定连接超时时间,默认10秒
func (m *mail) Timeout(timeout time.Duration) *mail {
m.timeout = timeout
return m
}
// WithHTML 使用text/html而不是text/plain
func (m *mail) WithHTML() *mail {
m.contentType = "html"
return m
}
// WithStartTLS 在连接中使用StartTLS
func (m *mail) WithStartTLS() *mail {
m.encryption = startTLS
return m
}
// WithTLS 使用SSL
func (m *mail) WithTLS() *mail {
m.encryption = dialTLS
return m
}
// connect 创建连接
func (m *mail) connect(ctx context.Context, network string) (conn net.Conn, err error) {
if m.encryption == dialTLS {
d := &tls.Dialer{
Config: &tls.Config{ServerName: m.host},
}
conn, err = d.DialContext(ctx, network, m.host+":"+m.port)
} else {
d := &net.Dialer{}
conn, err = d.DialContext(ctx, network, m.host+":"+m.port)
}
return
}
// write 向连接中写入邮件内容
func (m *mail) write(conn net.Conn, sendto []string, msg []byte) (err error) {
client, err := smtp.NewClient(conn, m.host)
if err != nil {
return
}
// quit will send a QUIT command and close the connection
defer client.Quit()
// StartTLS
if m.encryption == startTLS {
err = client.StartTLS(&tls.Config{ServerName: m.host})
if err != nil {
return
}
}
var auth smtp.Auth
if m.encryption == dialTLS || m.encryption == startTLS {
// PlainAuth
auth = smtp.PlainAuth("", m.username, m.password, m.host)
} else {
// 非加密方式需要使用自定义Auth
auth = &loginAuth{m.username, m.password}
}
// smtp auth command
err = client.Auth(auth)
if err != nil {
return
}
// smtp mail command
err = client.Mail(m.username)
if err != nil {
return
}
// smtp rcpt command
for _, to := range sendto {
if err = client.Rcpt(to); err != nil {
return
}
}
// smtp data command
w, err := client.Data()
if err != nil {
return
}
defer w.Close()
if _, err = w.Write(msg); err != nil {
return
}
return
}
// ################
// custom LOGIN Auth against smtp.Auth
// ################
type loginAuth struct {
username, password string
}
func (a *loginAuth) Start(server *smtp.ServerInfo) (string, []byte, error) {
return "LOGIN", nil, nil
}
func (a *loginAuth) Next(fromServer []byte, more bool) ([]byte, error) {
if more {
switch string(fromServer) {
case "Username:":
return StringToBytes(a.username), nil
case "Password:":
return StringToBytes(a.password), nil
default:
return nil, errors.New("unknown server cmd")
}
}
return nil, nil
}
此处可能存在不合适展示的内容,页面不予展示。您可通过相关编辑功能自查并修改。
如您确认内容无涉及 不当用语 / 纯广告导流 / 暴力 / 低俗色情 / 侵权 / 盗版 / 虚假 / 无价值内容或违法国家有关法律法规的内容,可点击提交进行申诉,我们将尽快为您处理。