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

340 lines
8.4 KiB

  1. // Copyright 2014 The Gogs Authors. All rights reserved.
  2. // Copyright 2017 The Gitea Authors. All rights reserved.
  3. // Use of this source code is governed by a MIT-style
  4. // license that can be found in the LICENSE file.
  5. package mailer
  6. import (
  7. "bytes"
  8. "crypto/tls"
  9. "fmt"
  10. "io"
  11. "net"
  12. "net/smtp"
  13. "os"
  14. "os/exec"
  15. "strings"
  16. "time"
  17. "code.gitea.io/gitea/modules/base"
  18. "code.gitea.io/gitea/modules/graceful"
  19. "code.gitea.io/gitea/modules/log"
  20. "code.gitea.io/gitea/modules/queue"
  21. "code.gitea.io/gitea/modules/setting"
  22. "github.com/jaytaylor/html2text"
  23. "gopkg.in/gomail.v2"
  24. )
  25. // Message mail body and log info
  26. type Message struct {
  27. Info string // Message information for log purpose.
  28. FromAddress string
  29. FromDisplayName string
  30. To []string
  31. Subject string
  32. Date time.Time
  33. Body string
  34. Headers map[string][]string
  35. }
  36. // ToMessage converts a Message to gomail.Message
  37. func (m *Message) ToMessage() *gomail.Message {
  38. msg := gomail.NewMessage()
  39. msg.SetAddressHeader("From", m.FromAddress, m.FromDisplayName)
  40. msg.SetHeader("To", m.To...)
  41. for header := range m.Headers {
  42. msg.SetHeader(header, m.Headers[header]...)
  43. }
  44. if len(setting.MailService.SubjectPrefix) > 0 {
  45. msg.SetHeader("Subject", setting.MailService.SubjectPrefix+" "+m.Subject)
  46. } else {
  47. msg.SetHeader("Subject", m.Subject)
  48. }
  49. msg.SetDateHeader("Date", m.Date)
  50. msg.SetHeader("X-Auto-Response-Suppress", "All")
  51. plainBody, err := html2text.FromString(m.Body)
  52. if err != nil || setting.MailService.SendAsPlainText {
  53. if strings.Contains(base.TruncateString(m.Body, 100), "<html>") {
  54. log.Warn("Mail contains HTML but configured to send as plain text.")
  55. }
  56. msg.SetBody("text/plain", plainBody)
  57. } else {
  58. msg.SetBody("text/plain", plainBody)
  59. msg.AddAlternative("text/html", m.Body)
  60. }
  61. return msg
  62. }
  63. // SetHeader adds additional headers to a message
  64. func (m *Message) SetHeader(field string, value ...string) {
  65. m.Headers[field] = value
  66. }
  67. // NewMessageFrom creates new mail message object with custom From header.
  68. func NewMessageFrom(to []string, fromDisplayName, fromAddress, subject, body string) *Message {
  69. log.Trace("NewMessageFrom (body):\n%s", body)
  70. return &Message{
  71. FromAddress: fromAddress,
  72. FromDisplayName: fromDisplayName,
  73. To: to,
  74. Subject: subject,
  75. Date: time.Now(),
  76. Body: body,
  77. Headers: map[string][]string{},
  78. }
  79. }
  80. // NewMessage creates new mail message object with default From header.
  81. func NewMessage(to []string, subject, body string) *Message {
  82. return NewMessageFrom(to, setting.MailService.FromName, setting.MailService.FromEmail, subject, body)
  83. }
  84. type loginAuth struct {
  85. username, password string
  86. }
  87. // LoginAuth SMTP AUTH LOGIN Auth Handler
  88. func LoginAuth(username, password string) smtp.Auth {
  89. return &loginAuth{username, password}
  90. }
  91. // Start start SMTP login auth
  92. func (a *loginAuth) Start(server *smtp.ServerInfo) (string, []byte, error) {
  93. return "LOGIN", []byte{}, nil
  94. }
  95. // Next next step of SMTP login auth
  96. func (a *loginAuth) Next(fromServer []byte, more bool) ([]byte, error) {
  97. if more {
  98. switch string(fromServer) {
  99. case "Username:":
  100. return []byte(a.username), nil
  101. case "Password:":
  102. return []byte(a.password), nil
  103. default:
  104. return nil, fmt.Errorf("unknown fromServer: %s", string(fromServer))
  105. }
  106. }
  107. return nil, nil
  108. }
  109. // Sender SMTP mail sender
  110. type smtpSender struct {
  111. }
  112. // Send send email
  113. func (s *smtpSender) Send(from string, to []string, msg io.WriterTo) error {
  114. opts := setting.MailService
  115. host, port, err := net.SplitHostPort(opts.Host)
  116. if err != nil {
  117. return err
  118. }
  119. tlsconfig := &tls.Config{
  120. InsecureSkipVerify: opts.SkipVerify,
  121. ServerName: host,
  122. }
  123. if opts.UseCertificate {
  124. cert, err := tls.LoadX509KeyPair(opts.CertFile, opts.KeyFile)
  125. if err != nil {
  126. return err
  127. }
  128. tlsconfig.Certificates = []tls.Certificate{cert}
  129. }
  130. conn, err := net.Dial("tcp", net.JoinHostPort(host, port))
  131. if err != nil {
  132. return err
  133. }
  134. defer conn.Close()
  135. isSecureConn := opts.IsTLSEnabled || (strings.HasSuffix(port, "465"))
  136. // Start TLS directly if the port ends with 465 (SMTPS protocol)
  137. if isSecureConn {
  138. conn = tls.Client(conn, tlsconfig)
  139. }
  140. client, err := smtp.NewClient(conn, host)
  141. if err != nil {
  142. return fmt.Errorf("NewClient: %v", err)
  143. }
  144. if !opts.DisableHelo {
  145. hostname := opts.HeloHostname
  146. if len(hostname) == 0 {
  147. hostname, err = os.Hostname()
  148. if err != nil {
  149. return err
  150. }
  151. }
  152. if err = client.Hello(hostname); err != nil {
  153. return fmt.Errorf("Hello: %v", err)
  154. }
  155. }
  156. // If not using SMTPS, always use STARTTLS if available
  157. hasStartTLS, _ := client.Extension("STARTTLS")
  158. if !isSecureConn && hasStartTLS {
  159. if err = client.StartTLS(tlsconfig); err != nil {
  160. return fmt.Errorf("StartTLS: %v", err)
  161. }
  162. }
  163. canAuth, options := client.Extension("AUTH")
  164. if canAuth && len(opts.User) > 0 {
  165. var auth smtp.Auth
  166. if strings.Contains(options, "CRAM-MD5") {
  167. auth = smtp.CRAMMD5Auth(opts.User, opts.Passwd)
  168. } else if strings.Contains(options, "PLAIN") {
  169. auth = smtp.PlainAuth("", opts.User, opts.Passwd, host)
  170. } else if strings.Contains(options, "LOGIN") {
  171. // Patch for AUTH LOGIN
  172. auth = LoginAuth(opts.User, opts.Passwd)
  173. }
  174. if auth != nil {
  175. if err = client.Auth(auth); err != nil {
  176. return fmt.Errorf("Auth: %v", err)
  177. }
  178. }
  179. }
  180. if err = client.Mail(from); err != nil {
  181. return fmt.Errorf("Mail: %v", err)
  182. }
  183. for _, rec := range to {
  184. if err = client.Rcpt(rec); err != nil {
  185. return fmt.Errorf("Rcpt: %v", err)
  186. }
  187. }
  188. w, err := client.Data()
  189. if err != nil {
  190. return fmt.Errorf("Data: %v", err)
  191. } else if _, err = msg.WriteTo(w); err != nil {
  192. return fmt.Errorf("WriteTo: %v", err)
  193. } else if err = w.Close(); err != nil {
  194. return fmt.Errorf("Close: %v", err)
  195. }
  196. return client.Quit()
  197. }
  198. // Sender sendmail mail sender
  199. type sendmailSender struct {
  200. }
  201. // Send send email
  202. func (s *sendmailSender) Send(from string, to []string, msg io.WriterTo) error {
  203. var err error
  204. var closeError error
  205. var waitError error
  206. args := []string{"-f", from, "-i"}
  207. args = append(args, setting.MailService.SendmailArgs...)
  208. args = append(args, to...)
  209. log.Trace("Sending with: %s %v", setting.MailService.SendmailPath, args)
  210. cmd := exec.Command(setting.MailService.SendmailPath, args...)
  211. pipe, err := cmd.StdinPipe()
  212. if err != nil {
  213. return err
  214. }
  215. if err = cmd.Start(); err != nil {
  216. return err
  217. }
  218. _, err = msg.WriteTo(pipe)
  219. // we MUST close the pipe or sendmail will hang waiting for more of the message
  220. // Also we should wait on our sendmail command even if something fails
  221. closeError = pipe.Close()
  222. waitError = cmd.Wait()
  223. if err != nil {
  224. return err
  225. } else if closeError != nil {
  226. return closeError
  227. } else {
  228. return waitError
  229. }
  230. }
  231. // Sender sendmail mail sender
  232. type dummySender struct {
  233. }
  234. // Send send email
  235. func (s *dummySender) Send(from string, to []string, msg io.WriterTo) error {
  236. buf := bytes.Buffer{}
  237. if _, err := msg.WriteTo(&buf); err != nil {
  238. return err
  239. }
  240. log.Info("Mail From: %s To: %v Body: %s", from, to, buf.String())
  241. return nil
  242. }
  243. var mailQueue queue.Queue
  244. // Sender sender for sending mail synchronously
  245. var Sender gomail.Sender
  246. // NewContext start mail queue service
  247. func NewContext() {
  248. // Need to check if mailQueue is nil because in during reinstall (user had installed
  249. // before but swithed install lock off), this function will be called again
  250. // while mail queue is already processing tasks, and produces a race condition.
  251. if setting.MailService == nil || mailQueue != nil {
  252. return
  253. }
  254. switch setting.MailService.MailerType {
  255. case "smtp":
  256. Sender = &smtpSender{}
  257. case "sendmail":
  258. Sender = &sendmailSender{}
  259. case "dummy":
  260. Sender = &dummySender{}
  261. }
  262. mailQueue = queue.CreateQueue("mail", func(data ...queue.Data) {
  263. for _, datum := range data {
  264. msg := datum.(*Message)
  265. gomailMsg := msg.ToMessage()
  266. log.Trace("New e-mail sending request %s: %s", gomailMsg.GetHeader("To"), msg.Info)
  267. if err := gomail.Send(Sender, gomailMsg); err != nil {
  268. log.Error("Failed to send emails %s: %s - %v", gomailMsg.GetHeader("To"), msg.Info, err)
  269. } else {
  270. log.Trace("E-mails sent %s: %s", gomailMsg.GetHeader("To"), msg.Info)
  271. }
  272. }
  273. }, &Message{})
  274. go graceful.GetManager().RunWithShutdownFns(mailQueue.Run)
  275. }
  276. // SendAsync send mail asynchronously
  277. func SendAsync(msg *Message) {
  278. go func() {
  279. _ = mailQueue.Push(msg)
  280. }()
  281. }
  282. // SendAsyncs send mails asynchronously
  283. func SendAsyncs(msgs []*Message) {
  284. go func() {
  285. for _, msg := range msgs {
  286. _ = mailQueue.Push(msg)
  287. }
  288. }()
  289. }