github.com/cloudreve/Cloudreve/v3@v3.0.0-20240224133659-3edb00a6484c/pkg/email/smtp.go (about) 1 package email 2 3 import ( 4 "fmt" 5 "github.com/google/uuid" 6 "time" 7 8 "github.com/cloudreve/Cloudreve/v3/pkg/util" 9 "github.com/go-mail/mail" 10 ) 11 12 // SMTP SMTP协议发送邮件 13 type SMTP struct { 14 Config SMTPConfig 15 ch chan *mail.Message 16 chOpen bool 17 } 18 19 // SMTPConfig SMTP发送配置 20 type SMTPConfig struct { 21 Name string // 发送者名 22 Address string // 发送者地址 23 ReplyTo string // 回复地址 24 Host string // 服务器主机名 25 Port int // 服务器端口 26 User string // 用户名 27 Password string // 密码 28 Encryption bool // 是否启用加密 29 Keepalive int // SMTP 连接保留时长 30 } 31 32 // NewSMTPClient 新建SMTP发送队列 33 func NewSMTPClient(config SMTPConfig) *SMTP { 34 client := &SMTP{ 35 Config: config, 36 ch: make(chan *mail.Message, 30), 37 chOpen: false, 38 } 39 40 client.Init() 41 42 return client 43 } 44 45 // Send 发送邮件 46 func (client *SMTP) Send(to, title, body string) error { 47 if !client.chOpen { 48 return ErrChanNotOpen 49 } 50 m := mail.NewMessage() 51 m.SetAddressHeader("From", client.Config.Address, client.Config.Name) 52 m.SetAddressHeader("Reply-To", client.Config.ReplyTo, client.Config.Name) 53 m.SetHeader("To", to) 54 m.SetHeader("Subject", title) 55 m.SetHeader("Message-ID", fmt.Sprintf("<%s@%s>", uuid.NewString(), "cloudreve")) 56 m.SetBody("text/html", body) 57 client.ch <- m 58 return nil 59 } 60 61 // Close 关闭发送队列 62 func (client *SMTP) Close() { 63 if client.ch != nil { 64 close(client.ch) 65 } 66 } 67 68 // Init 初始化发送队列 69 func (client *SMTP) Init() { 70 go func() { 71 defer func() { 72 if err := recover(); err != nil { 73 client.chOpen = false 74 util.Log().Error("Exception while sending email: %s, queue will be reset in 10 seconds.", err) 75 time.Sleep(time.Duration(10) * time.Second) 76 client.Init() 77 } 78 }() 79 80 d := mail.NewDialer(client.Config.Host, client.Config.Port, client.Config.User, client.Config.Password) 81 d.Timeout = time.Duration(client.Config.Keepalive+5) * time.Second 82 client.chOpen = true 83 // 是否启用 SSL 84 d.SSL = false 85 if client.Config.Encryption { 86 d.SSL = true 87 } 88 d.StartTLSPolicy = mail.OpportunisticStartTLS 89 90 var s mail.SendCloser 91 var err error 92 open := false 93 for { 94 select { 95 case m, ok := <-client.ch: 96 if !ok { 97 util.Log().Debug("Email queue closing...") 98 client.chOpen = false 99 return 100 } 101 if !open { 102 if s, err = d.Dial(); err != nil { 103 panic(err) 104 } 105 open = true 106 } 107 if err := mail.Send(s, m); err != nil { 108 util.Log().Warning("Failed to send email: %s", err) 109 } else { 110 util.Log().Debug("Email sent.") 111 } 112 // 长时间没有新邮件,则关闭SMTP连接 113 case <-time.After(time.Duration(client.Config.Keepalive) * time.Second): 114 if open { 115 if err := s.Close(); err != nil { 116 util.Log().Warning("Failed to close SMTP connection: %s", err) 117 } 118 open = false 119 } 120 } 121 } 122 }() 123 }