mailer.go 5.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237
  1. // Copyright 2014 The Gogs Authors. All rights reserved.
  2. // Use of this source code is governed by a MIT-style
  3. // license that can be found in the LICENSE file.
  4. package notifications
  5. import (
  6. "bytes"
  7. "crypto/tls"
  8. "errors"
  9. "fmt"
  10. "html/template"
  11. "net"
  12. "net/mail"
  13. "net/smtp"
  14. "os"
  15. "strings"
  16. "time"
  17. "github.com/grafana/grafana/pkg/log"
  18. m "github.com/grafana/grafana/pkg/models"
  19. "github.com/grafana/grafana/pkg/setting"
  20. )
  21. var mailQueue chan *Message
  22. func initMailQueue() {
  23. mailQueue = make(chan *Message, 10)
  24. go processMailQueue()
  25. }
  26. func processMailQueue() {
  27. for {
  28. select {
  29. case msg := <-mailQueue:
  30. num, err := buildAndSend(msg)
  31. tos := strings.Join(msg.To, "; ")
  32. info := ""
  33. if err != nil {
  34. if len(msg.Info) > 0 {
  35. info = ", info: " + msg.Info
  36. }
  37. log.Error(4, fmt.Sprintf("Async sent email %d succeed, not send emails: %s%s err: %s", num, tos, info, err))
  38. } else {
  39. log.Trace(fmt.Sprintf("Async sent email %d succeed, sent emails: %s%s", num, tos, info))
  40. }
  41. }
  42. }
  43. }
  44. var addToMailQueue = func(msg *Message) {
  45. mailQueue <- msg
  46. }
  47. func sendToSmtpServer(recipients []string, msgContent []byte) error {
  48. host, port, err := net.SplitHostPort(setting.Smtp.Host)
  49. if err != nil {
  50. return err
  51. }
  52. tlsconfig := &tls.Config{
  53. InsecureSkipVerify: setting.Smtp.SkipVerify,
  54. ServerName: host,
  55. }
  56. if setting.Smtp.CertFile != "" {
  57. cert, err := tls.LoadX509KeyPair(setting.Smtp.CertFile, setting.Smtp.KeyFile)
  58. if err != nil {
  59. return err
  60. }
  61. tlsconfig.Certificates = []tls.Certificate{cert}
  62. }
  63. conn, err := net.DialTimeout("tcp", net.JoinHostPort(host, port), time.Second*10)
  64. if err != nil {
  65. return err
  66. }
  67. defer conn.Close()
  68. isSecureConn := false
  69. // Start TLS directly if the port ends with 465 (SMTPS protocol)
  70. if strings.HasSuffix(port, "465") {
  71. conn = tls.Client(conn, tlsconfig)
  72. isSecureConn = true
  73. }
  74. client, err := smtp.NewClient(conn, host)
  75. if err != nil {
  76. return err
  77. }
  78. hostname, err := os.Hostname()
  79. if err != nil {
  80. return err
  81. }
  82. if err = client.Hello(hostname); err != nil {
  83. return err
  84. }
  85. // If not using SMTPS, alway use STARTTLS if available
  86. hasStartTLS, _ := client.Extension("STARTTLS")
  87. if !isSecureConn && hasStartTLS {
  88. if err = client.StartTLS(tlsconfig); err != nil {
  89. return err
  90. }
  91. }
  92. canAuth, options := client.Extension("AUTH")
  93. if canAuth && len(setting.Smtp.User) > 0 {
  94. var auth smtp.Auth
  95. if strings.Contains(options, "CRAM-MD5") {
  96. auth = smtp.CRAMMD5Auth(setting.Smtp.User, setting.Smtp.Password)
  97. } else if strings.Contains(options, "PLAIN") {
  98. auth = smtp.PlainAuth("", setting.Smtp.User, setting.Smtp.Password, host)
  99. }
  100. if auth != nil {
  101. if err = client.Auth(auth); err != nil {
  102. return err
  103. }
  104. }
  105. }
  106. if fromAddress, err := mail.ParseAddress(setting.Smtp.FromAddress); err != nil {
  107. return err
  108. } else {
  109. if err = client.Mail(fromAddress.Address); err != nil {
  110. return err
  111. }
  112. }
  113. for _, rec := range recipients {
  114. if err = client.Rcpt(rec); err != nil {
  115. return err
  116. }
  117. }
  118. w, err := client.Data()
  119. if err != nil {
  120. return err
  121. }
  122. if _, err = w.Write([]byte(msgContent)); err != nil {
  123. return err
  124. }
  125. if err = w.Close(); err != nil {
  126. return err
  127. }
  128. return client.Quit()
  129. }
  130. func buildAndSend(msg *Message) (int, error) {
  131. log.Trace("Sending mails to: %s", strings.Join(msg.To, "; "))
  132. // get message body
  133. content := msg.Content()
  134. if len(msg.To) == 0 {
  135. return 0, fmt.Errorf("empty receive emails")
  136. } else if len(msg.Body) == 0 {
  137. return 0, fmt.Errorf("empty email body")
  138. }
  139. if msg.Massive {
  140. // send mail to multiple emails one by one
  141. num := 0
  142. for _, to := range msg.To {
  143. body := []byte("To: " + to + "\r\n" + content)
  144. err := sendToSmtpServer([]string{to}, body)
  145. if err != nil {
  146. return num, err
  147. }
  148. num++
  149. }
  150. return num, nil
  151. } else {
  152. body := []byte("To: " + strings.Join(msg.To, ";") + "\r\n" + content)
  153. // send to multiple emails in one message
  154. err := sendToSmtpServer(msg.To, body)
  155. if err != nil {
  156. return 0, err
  157. } else {
  158. return 1, nil
  159. }
  160. }
  161. }
  162. func buildEmailMessage(cmd *m.SendEmailCommand) (*Message, error) {
  163. if !setting.Smtp.Enabled {
  164. return nil, errors.New("Grafana mailing/smtp options not configured, contact your Grafana admin")
  165. }
  166. var buffer bytes.Buffer
  167. var err error
  168. var subjectText interface{}
  169. data := cmd.Data
  170. if data == nil {
  171. data = make(map[string]interface{}, 10)
  172. }
  173. setDefaultTemplateData(data, nil)
  174. err = mailTemplates.ExecuteTemplate(&buffer, cmd.Template, data)
  175. if err != nil {
  176. return nil, err
  177. }
  178. subjectData := data["Subject"].(map[string]interface{})
  179. subjectText, hasSubject := subjectData["value"]
  180. if !hasSubject {
  181. return nil, errors.New(fmt.Sprintf("Missing subject in Template %s", cmd.Template))
  182. }
  183. subjectTmpl, err := template.New("subject").Parse(subjectText.(string))
  184. if err != nil {
  185. return nil, err
  186. }
  187. var subjectBuffer bytes.Buffer
  188. err = subjectTmpl.ExecuteTemplate(&subjectBuffer, "subject", data)
  189. if err != nil {
  190. return nil, err
  191. }
  192. return &Message{
  193. To: cmd.To,
  194. From: setting.Smtp.FromAddress,
  195. Subject: subjectBuffer.String(),
  196. Body: buffer.String(),
  197. }, nil
  198. }