email.go 3.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159
  1. package email
  2. import (
  3. "crypto/tls"
  4. "fmt"
  5. "math/rand"
  6. "net/smtp"
  7. "net/textproto"
  8. "github.com/knadh/listmonk/internal/messenger"
  9. "github.com/knadh/smtppool"
  10. )
  11. const emName = "email"
  12. // Server represents an SMTP server's credentials.
  13. type Server struct {
  14. Username string `json:"username"`
  15. Password string `json:"password"`
  16. AuthProtocol string `json:"auth_protocol"`
  17. TLSEnabled bool `json:"tls_enabled"`
  18. TLSSkipVerify bool `json:"tls_skip_verify"`
  19. EmailHeaders map[string]string `json:"email_headers"`
  20. // Rest of the options are embedded directly from the smtppool lib.
  21. // The JSON tag is for config unmarshal to work.
  22. smtppool.Opt `json:",squash"`
  23. pool *smtppool.Pool
  24. }
  25. // Emailer is the SMTP e-mail messenger.
  26. type Emailer struct {
  27. servers []*Server
  28. }
  29. // New returns an SMTP e-mail Messenger backend with a the given SMTP servers.
  30. func New(servers ...Server) (*Emailer, error) {
  31. e := &Emailer{
  32. servers: make([]*Server, 0, len(servers)),
  33. }
  34. for _, srv := range servers {
  35. s := srv
  36. var auth smtp.Auth
  37. switch s.AuthProtocol {
  38. case "cram":
  39. auth = smtp.CRAMMD5Auth(s.Username, s.Password)
  40. case "plain":
  41. auth = smtp.PlainAuth("", s.Username, s.Password, s.Host)
  42. case "login":
  43. auth = &smtppool.LoginAuth{Username: s.Username, Password: s.Password}
  44. case "", "none":
  45. default:
  46. return nil, fmt.Errorf("unknown SMTP auth type '%s'", s.AuthProtocol)
  47. }
  48. s.Opt.Auth = auth
  49. // TLS config.
  50. if s.TLSEnabled {
  51. s.TLSConfig = &tls.Config{}
  52. if s.TLSSkipVerify {
  53. s.TLSConfig.InsecureSkipVerify = s.TLSSkipVerify
  54. } else {
  55. s.TLSConfig.ServerName = s.Host
  56. }
  57. }
  58. pool, err := smtppool.New(s.Opt)
  59. if err != nil {
  60. return nil, err
  61. }
  62. s.pool = pool
  63. e.servers = append(e.servers, &s)
  64. }
  65. return e, nil
  66. }
  67. // Name returns the Server's name.
  68. func (e *Emailer) Name() string {
  69. return emName
  70. }
  71. // Push pushes a message to the server.
  72. func (e *Emailer) Push(m messenger.Message) error {
  73. // If there are more than one SMTP servers, send to a random
  74. // one from the list.
  75. var (
  76. ln = len(e.servers)
  77. srv *Server
  78. )
  79. if ln > 1 {
  80. srv = e.servers[rand.Intn(ln)]
  81. } else {
  82. srv = e.servers[0]
  83. }
  84. // Are there attachments?
  85. var files []smtppool.Attachment
  86. if m.Attachments != nil {
  87. files = make([]smtppool.Attachment, 0, len(m.Attachments))
  88. for _, f := range m.Attachments {
  89. a := smtppool.Attachment{
  90. Filename: f.Name,
  91. Header: f.Header,
  92. Content: make([]byte, len(f.Content)),
  93. }
  94. copy(a.Content, f.Content)
  95. files = append(files, a)
  96. }
  97. }
  98. em := smtppool.Email{
  99. From: m.From,
  100. To: m.To,
  101. Subject: m.Subject,
  102. Attachments: files,
  103. }
  104. em.Headers = textproto.MIMEHeader{}
  105. // Attach e-mail level headers.
  106. if len(m.Headers) > 0 {
  107. em.Headers = m.Headers
  108. }
  109. // Attach SMTP level headers.
  110. if len(srv.EmailHeaders) > 0 {
  111. for k, v := range srv.EmailHeaders {
  112. em.Headers.Set(k, v)
  113. }
  114. }
  115. switch m.ContentType {
  116. case "plain":
  117. em.Text = []byte(m.Body)
  118. default:
  119. em.HTML = m.Body
  120. if len(m.AltBody) > 0 {
  121. em.Text = m.AltBody
  122. }
  123. }
  124. return srv.pool.Send(em)
  125. }
  126. // Flush flushes the message queue to the server.
  127. func (e *Emailer) Flush() error {
  128. return nil
  129. }
  130. // Close closes the SMTP pools.
  131. func (e *Emailer) Close() error {
  132. for _, s := range e.servers {
  133. s.pool.Close()
  134. }
  135. return nil
  136. }