You can not select more than 25 topics Topics must start with a chinese character,a letter or number, can include dashes ('-') and can be up to 35 characters long.

mailer.go 5.8 kB

11 years ago
10 years ago
10 years ago
11 years ago
11 years ago
10 years ago
10 years ago
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262
  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 mailer
  5. import (
  6. "crypto/tls"
  7. "fmt"
  8. "net"
  9. "net/mail"
  10. "net/smtp"
  11. "os"
  12. "strings"
  13. "github.com/gogits/gogs/modules/log"
  14. "github.com/gogits/gogs/modules/setting"
  15. )
  16. type loginAuth struct {
  17. username, password string
  18. }
  19. // SMTP AUTH LOGIN Auth Handler
  20. func LoginAuth(username, password string) smtp.Auth {
  21. return &loginAuth{username, password}
  22. }
  23. func (a *loginAuth) Start(server *smtp.ServerInfo) (string, []byte, error) {
  24. return "LOGIN", []byte{}, nil
  25. }
  26. func (a *loginAuth) Next(fromServer []byte, more bool) ([]byte, error) {
  27. if more {
  28. switch string(fromServer) {
  29. case "Username:":
  30. return []byte(a.username), nil
  31. case "Password:":
  32. return []byte(a.password), nil
  33. default:
  34. return nil, fmt.Errorf("unknwon fromServer: %s", string(fromServer))
  35. }
  36. }
  37. return nil, nil
  38. }
  39. type Message struct {
  40. To []string
  41. From string
  42. Subject string
  43. ReplyTo string
  44. Body string
  45. Type string
  46. Massive bool
  47. Info string
  48. }
  49. // create mail content
  50. func (m Message) Content() string {
  51. // set mail type
  52. contentType := "text/plain; charset=UTF-8"
  53. if m.Type == "html" {
  54. contentType = "text/html; charset=UTF-8"
  55. }
  56. // create mail content
  57. content := "From: " + m.From + "\r\nReply-To: " + m.ReplyTo + "\r\nSubject: " + m.Subject + "\r\nContent-Type: " + contentType + "\r\n\r\n" + m.Body
  58. return content
  59. }
  60. var mailQueue chan *Message
  61. func NewMailerContext() {
  62. mailQueue = make(chan *Message, setting.Cfg.Section("mailer").Key("SEND_BUFFER_LEN").MustInt(10))
  63. go processMailQueue()
  64. }
  65. func processMailQueue() {
  66. for {
  67. select {
  68. case msg := <-mailQueue:
  69. num, err := Send(msg)
  70. tos := strings.Join(msg.To, "; ")
  71. info := ""
  72. if err != nil {
  73. if len(msg.Info) > 0 {
  74. info = ", info: " + msg.Info
  75. }
  76. log.Error(4, fmt.Sprintf("Async sent email %d succeed, not send emails: %s%s err: %s", num, tos, info, err))
  77. } else {
  78. log.Trace(fmt.Sprintf("Async sent email %d succeed, sent emails: %s%s", num, tos, info))
  79. }
  80. }
  81. }
  82. }
  83. // sendMail allows mail with self-signed certificates.
  84. func sendMail(settings *setting.Mailer, recipients []string, msgContent []byte) error {
  85. host, port, err := net.SplitHostPort(settings.Host)
  86. if err != nil {
  87. return err
  88. }
  89. tlsconfig := &tls.Config{
  90. InsecureSkipVerify: settings.SkipVerify,
  91. ServerName: host,
  92. }
  93. if settings.UseCertificate {
  94. cert, err := tls.LoadX509KeyPair(settings.CertFile, settings.KeyFile)
  95. if err != nil {
  96. return err
  97. }
  98. tlsconfig.Certificates = []tls.Certificate{cert}
  99. }
  100. conn, err := net.Dial("tcp", net.JoinHostPort(host, port))
  101. if err != nil {
  102. return err
  103. }
  104. defer conn.Close()
  105. isSecureConn := false
  106. // Start TLS directly if the port ends with 465 (SMTPS protocol)
  107. if strings.HasSuffix(port, "465") {
  108. conn = tls.Client(conn, tlsconfig)
  109. isSecureConn = true
  110. }
  111. client, err := smtp.NewClient(conn, host)
  112. if err != nil {
  113. return err
  114. }
  115. if !setting.MailService.DisableHelo {
  116. hostname := setting.MailService.HeloHostname
  117. if len(hostname) == 0 {
  118. hostname, err = os.Hostname()
  119. if err != nil {
  120. return err
  121. }
  122. }
  123. if err = client.Hello(hostname); err != nil {
  124. return err
  125. }
  126. }
  127. // If not using SMTPS, alway use STARTTLS if available
  128. hasStartTLS, _ := client.Extension("STARTTLS")
  129. if !isSecureConn && hasStartTLS {
  130. if err = client.StartTLS(tlsconfig); err != nil {
  131. return err
  132. }
  133. }
  134. canAuth, options := client.Extension("AUTH")
  135. if canAuth && len(settings.User) > 0 {
  136. var auth smtp.Auth
  137. if strings.Contains(options, "CRAM-MD5") {
  138. auth = smtp.CRAMMD5Auth(settings.User, settings.Passwd)
  139. } else if strings.Contains(options, "PLAIN") {
  140. auth = smtp.PlainAuth("", settings.User, settings.Passwd, host)
  141. } else if strings.Contains(options, "LOGIN") {
  142. // Patch for AUTH LOGIN
  143. auth = LoginAuth(settings.User, settings.Passwd)
  144. }
  145. if auth != nil {
  146. if err = client.Auth(auth); err != nil {
  147. return err
  148. }
  149. }
  150. }
  151. if fromAddress, err := mail.ParseAddress(settings.From); err != nil {
  152. return err
  153. } else {
  154. if err = client.Mail(fromAddress.Address); err != nil {
  155. return err
  156. }
  157. }
  158. for _, rec := range recipients {
  159. if err = client.Rcpt(rec); err != nil {
  160. return err
  161. }
  162. }
  163. w, err := client.Data()
  164. if err != nil {
  165. return err
  166. }
  167. if _, err = w.Write([]byte(msgContent)); err != nil {
  168. return err
  169. }
  170. if err = w.Close(); err != nil {
  171. return err
  172. }
  173. return client.Quit()
  174. }
  175. // Direct Send mail message
  176. func Send(msg *Message) (int, error) {
  177. log.Trace("Sending mails to: %s", strings.Join(msg.To, "; "))
  178. // get message body
  179. content := msg.Content()
  180. if len(msg.To) == 0 {
  181. return 0, fmt.Errorf("empty receive emails")
  182. } else if len(msg.Body) == 0 {
  183. return 0, fmt.Errorf("empty email body")
  184. }
  185. if msg.Massive {
  186. // send mail to multiple emails one by one
  187. num := 0
  188. for _, to := range msg.To {
  189. body := []byte("To: " + to + "\r\n" + content)
  190. err := sendMail(setting.MailService, []string{to}, body)
  191. if err != nil {
  192. return num, err
  193. }
  194. num++
  195. }
  196. return num, nil
  197. } else {
  198. body := []byte("To: " + strings.Join(msg.To, ";") + "\r\n" + content)
  199. // send to multiple emails in one message
  200. err := sendMail(setting.MailService, msg.To, body)
  201. if err != nil {
  202. return 0, err
  203. } else {
  204. return 1, nil
  205. }
  206. }
  207. }
  208. // Async Send mail message
  209. func SendAsync(msg *Message) {
  210. go func() {
  211. mailQueue <- msg
  212. }()
  213. }
  214. // Create html mail message
  215. func NewHtmlMessage(To []string, From, Subject, Body string) Message {
  216. return Message{
  217. To: To,
  218. From: setting.MailService.From,
  219. ReplyTo: From,
  220. Subject: Subject,
  221. Body: Body,
  222. Type: "html",
  223. }
  224. }