mirror of https://github.com/cloudreve/Cloudreve
166 lines
3.9 KiB
Go
166 lines
3.9 KiB
Go
package email
|
||
|
||
import (
|
||
"context"
|
||
"fmt"
|
||
"strings"
|
||
"time"
|
||
|
||
"github.com/cloudreve/Cloudreve/v4/inventory"
|
||
"github.com/cloudreve/Cloudreve/v4/pkg/logging"
|
||
"github.com/cloudreve/Cloudreve/v4/pkg/setting"
|
||
"github.com/go-mail/mail"
|
||
"github.com/gofrs/uuid"
|
||
)
|
||
|
||
// SMTPPool SMTP协议发送邮件
|
||
type SMTPPool struct {
|
||
// Deprecated
|
||
Config SMTPConfig
|
||
|
||
config *setting.SMTP
|
||
ch chan *message
|
||
chOpen bool
|
||
l logging.Logger
|
||
}
|
||
|
||
// SMTPConfig SMTP发送配置
|
||
type SMTPConfig struct {
|
||
Name string // 发送者名
|
||
Address string // 发送者地址
|
||
ReplyTo string // 回复地址
|
||
Host string // 服务器主机名
|
||
Port int // 服务器端口
|
||
User string // 用户名
|
||
Password string // 密码
|
||
Encryption bool // 是否启用加密
|
||
Keepalive int // SMTPPool 连接保留时长
|
||
}
|
||
|
||
type message struct {
|
||
msg *mail.Message
|
||
cid string
|
||
userID int
|
||
}
|
||
|
||
// NewSMTPPool initializes a new SMTP based email sending queue.
|
||
func NewSMTPPool(config setting.Provider, logger logging.Logger) *SMTPPool {
|
||
client := &SMTPPool{
|
||
config: config.SMTP(context.Background()),
|
||
ch: make(chan *message, 30),
|
||
chOpen: false,
|
||
l: logger,
|
||
}
|
||
|
||
client.Init()
|
||
return client
|
||
}
|
||
|
||
// NewSMTPClient 新建SMTP发送队列
|
||
// Deprecated
|
||
func NewSMTPClient(config SMTPConfig) *SMTPPool {
|
||
client := &SMTPPool{
|
||
Config: config,
|
||
ch: make(chan *message, 30),
|
||
chOpen: false,
|
||
}
|
||
|
||
client.Init()
|
||
|
||
return client
|
||
}
|
||
|
||
// Send 发送邮件
|
||
func (client *SMTPPool) Send(ctx context.Context, to, title, body string) error {
|
||
if !client.chOpen {
|
||
return fmt.Errorf("SMTP pool is closed")
|
||
}
|
||
|
||
// 忽略通过QQ登录的邮箱
|
||
if strings.HasSuffix(to, "@login.qq.com") {
|
||
return nil
|
||
}
|
||
|
||
m := mail.NewMessage()
|
||
m.SetAddressHeader("From", client.config.From, client.config.FromName)
|
||
m.SetAddressHeader("Reply-To", client.config.ReplyTo, client.config.FromName)
|
||
m.SetHeader("To", to)
|
||
m.SetHeader("Subject", title)
|
||
m.SetHeader("Message-ID", fmt.Sprintf("<%s@%s>", uuid.Must(uuid.NewV4()).String(), "cloudreve"))
|
||
m.SetBody("text/html", body)
|
||
client.ch <- &message{
|
||
msg: m,
|
||
cid: logging.CorrelationID(ctx).String(),
|
||
userID: inventory.UserIDFromContext(ctx),
|
||
}
|
||
return nil
|
||
}
|
||
|
||
// Close 关闭发送队列
|
||
func (client *SMTPPool) Close() {
|
||
if client.ch != nil {
|
||
close(client.ch)
|
||
}
|
||
}
|
||
|
||
// Init 初始化发送队列
|
||
func (client *SMTPPool) Init() {
|
||
go func() {
|
||
client.l.Info("Initializing and starting SMTP email pool...")
|
||
defer func() {
|
||
if err := recover(); err != nil {
|
||
client.chOpen = false
|
||
client.l.Error("Exception while sending email: %s, queue will be reset in 10 seconds.", err)
|
||
time.Sleep(time.Duration(10) * time.Second)
|
||
client.Init()
|
||
}
|
||
}()
|
||
|
||
d := mail.NewDialer(client.config.Host, client.config.Port, client.config.User, client.config.Password)
|
||
d.Timeout = time.Duration(client.config.Keepalive+5) * time.Second
|
||
client.chOpen = true
|
||
// 是否启用 SSL
|
||
d.SSL = false
|
||
if client.config.ForceEncryption {
|
||
d.SSL = true
|
||
}
|
||
d.StartTLSPolicy = mail.OpportunisticStartTLS
|
||
|
||
var s mail.SendCloser
|
||
var err error
|
||
open := false
|
||
for {
|
||
select {
|
||
case m, ok := <-client.ch:
|
||
if !ok {
|
||
client.l.Info("Email queue closing...")
|
||
client.chOpen = false
|
||
return
|
||
}
|
||
|
||
if !open {
|
||
if s, err = d.Dial(); err != nil {
|
||
panic(err)
|
||
}
|
||
open = true
|
||
}
|
||
|
||
l := client.l.CopyWithPrefix(fmt.Sprintf("[Cid: %s]", m.cid))
|
||
if err := mail.Send(s, m.msg); err != nil {
|
||
l.Warning("Failed to send email: %s, Cid=%s", err, m.cid)
|
||
} else {
|
||
l.Info("Email sent to %q, title: %q.", m.msg.GetHeader("To"), m.msg.GetHeader("Subject"))
|
||
}
|
||
// 长时间没有新邮件,则关闭SMTP连接
|
||
case <-time.After(time.Duration(client.config.Keepalive) * time.Second):
|
||
if open {
|
||
if err := s.Close(); err != nil {
|
||
client.l.Warning("Failed to close SMTP connection: %s", err)
|
||
}
|
||
open = false
|
||
}
|
||
}
|
||
}
|
||
}()
|
||
}
|