gitea源码

mailer.go 10.0KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301
  1. // Copyright 2019 The Gitea Authors. All rights reserved.
  2. // SPDX-License-Identifier: MIT
  3. package setting
  4. import (
  5. "context"
  6. "net"
  7. "net/mail"
  8. "strings"
  9. "text/template"
  10. "time"
  11. "code.gitea.io/gitea/modules/log"
  12. "github.com/kballard/go-shellquote"
  13. )
  14. // Mailer represents mail service.
  15. type Mailer struct {
  16. // Mailer
  17. Name string `ini:"NAME"`
  18. From string `ini:"FROM"`
  19. EnvelopeFrom string `ini:"ENVELOPE_FROM"`
  20. OverrideEnvelopeFrom bool `ini:"-"`
  21. FromName string `ini:"-"`
  22. FromEmail string `ini:"-"`
  23. SendAsPlainText bool `ini:"SEND_AS_PLAIN_TEXT"`
  24. SubjectPrefix string `ini:"SUBJECT_PREFIX"`
  25. OverrideHeader map[string][]string `ini:"-"`
  26. // Embed attachment images as inline base64 img src attribute
  27. EmbedAttachmentImages bool
  28. // SMTP sender
  29. Protocol string `ini:"PROTOCOL"`
  30. SMTPAddr string `ini:"SMTP_ADDR"`
  31. SMTPPort string `ini:"SMTP_PORT"`
  32. User string `ini:"USER"`
  33. Passwd string `ini:"PASSWD"`
  34. EnableHelo bool `ini:"ENABLE_HELO"`
  35. HeloHostname string `ini:"HELO_HOSTNAME"`
  36. ForceTrustServerCert bool `ini:"FORCE_TRUST_SERVER_CERT"`
  37. UseClientCert bool `ini:"USE_CLIENT_CERT"`
  38. ClientCertFile string `ini:"CLIENT_CERT_FILE"`
  39. ClientKeyFile string `ini:"CLIENT_KEY_FILE"`
  40. // Sendmail sender
  41. SendmailPath string `ini:"SENDMAIL_PATH"`
  42. SendmailArgs []string `ini:"-"`
  43. SendmailTimeout time.Duration `ini:"SENDMAIL_TIMEOUT"`
  44. SendmailConvertCRLF bool `ini:"SENDMAIL_CONVERT_CRLF"`
  45. // Customization
  46. FromDisplayNameFormat string `ini:"FROM_DISPLAY_NAME_FORMAT"`
  47. FromDisplayNameFormatTemplate *template.Template `ini:"-"`
  48. }
  49. // MailService the global mailer
  50. var MailService *Mailer
  51. func loadMailsFrom(rootCfg ConfigProvider) {
  52. loadMailerFrom(rootCfg)
  53. loadRegisterMailFrom(rootCfg)
  54. loadNotifyMailFrom(rootCfg)
  55. loadIncomingEmailFrom(rootCfg)
  56. }
  57. func loadMailerFrom(rootCfg ConfigProvider) {
  58. sec := rootCfg.Section("mailer")
  59. // Check mailer setting.
  60. if !sec.Key("ENABLED").MustBool() {
  61. return
  62. }
  63. // Handle Deprecations and map on to new configuration
  64. // DEPRECATED should not be removed because users maybe upgrade from lower version to the latest version
  65. // if these are removed, the warning will not be shown
  66. deprecatedSetting(rootCfg, "mailer", "MAILER_TYPE", "mailer", "PROTOCOL", "v1.19.0")
  67. if sec.HasKey("MAILER_TYPE") && !sec.HasKey("PROTOCOL") {
  68. if sec.Key("MAILER_TYPE").String() == "sendmail" {
  69. sec.Key("PROTOCOL").MustString("sendmail")
  70. }
  71. }
  72. deprecatedSetting(rootCfg, "mailer", "HOST", "mailer", "SMTP_ADDR", "v1.19.0")
  73. if sec.HasKey("HOST") && !sec.HasKey("SMTP_ADDR") {
  74. givenHost := sec.Key("HOST").String()
  75. addr, port, err := net.SplitHostPort(givenHost)
  76. if err != nil && strings.Contains(err.Error(), "missing port in address") {
  77. addr = givenHost
  78. } else if err != nil {
  79. log.Fatal("Invalid mailer.HOST (%s): %v", givenHost, err)
  80. }
  81. if addr == "" {
  82. addr = "127.0.0.1"
  83. }
  84. sec.Key("SMTP_ADDR").MustString(addr)
  85. sec.Key("SMTP_PORT").MustString(port)
  86. }
  87. deprecatedSetting(rootCfg, "mailer", "IS_TLS_ENABLED", "mailer", "PROTOCOL", "v1.19.0")
  88. if sec.HasKey("IS_TLS_ENABLED") && !sec.HasKey("PROTOCOL") {
  89. if sec.Key("IS_TLS_ENABLED").MustBool() {
  90. sec.Key("PROTOCOL").MustString("smtps")
  91. } else {
  92. sec.Key("PROTOCOL").MustString("smtp+starttls")
  93. }
  94. }
  95. deprecatedSetting(rootCfg, "mailer", "DISABLE_HELO", "mailer", "ENABLE_HELO", "v1.19.0")
  96. if sec.HasKey("DISABLE_HELO") && !sec.HasKey("ENABLE_HELO") {
  97. sec.Key("ENABLE_HELO").MustBool(!sec.Key("DISABLE_HELO").MustBool())
  98. }
  99. deprecatedSetting(rootCfg, "mailer", "SKIP_VERIFY", "mailer", "FORCE_TRUST_SERVER_CERT", "v1.19.0")
  100. if sec.HasKey("SKIP_VERIFY") && !sec.HasKey("FORCE_TRUST_SERVER_CERT") {
  101. sec.Key("FORCE_TRUST_SERVER_CERT").MustBool(sec.Key("SKIP_VERIFY").MustBool())
  102. }
  103. deprecatedSetting(rootCfg, "mailer", "USE_CERTIFICATE", "mailer", "USE_CLIENT_CERT", "v1.19.0")
  104. if sec.HasKey("USE_CERTIFICATE") && !sec.HasKey("USE_CLIENT_CERT") {
  105. sec.Key("USE_CLIENT_CERT").MustBool(sec.Key("USE_CERTIFICATE").MustBool())
  106. }
  107. deprecatedSetting(rootCfg, "mailer", "CERT_FILE", "mailer", "CLIENT_CERT_FILE", "v1.19.0")
  108. if sec.HasKey("CERT_FILE") && !sec.HasKey("CLIENT_CERT_FILE") {
  109. sec.Key("CERT_FILE").MustString(sec.Key("CERT_FILE").String())
  110. }
  111. deprecatedSetting(rootCfg, "mailer", "KEY_FILE", "mailer", "CLIENT_KEY_FILE", "v1.19.0")
  112. if sec.HasKey("KEY_FILE") && !sec.HasKey("CLIENT_KEY_FILE") {
  113. sec.Key("KEY_FILE").MustString(sec.Key("KEY_FILE").String())
  114. }
  115. deprecatedSetting(rootCfg, "mailer", "ENABLE_HTML_ALTERNATIVE", "mailer", "SEND_AS_PLAIN_TEXT", "v1.19.0")
  116. if sec.HasKey("ENABLE_HTML_ALTERNATIVE") && !sec.HasKey("SEND_AS_PLAIN_TEXT") {
  117. sec.Key("SEND_AS_PLAIN_TEXT").MustBool(!sec.Key("ENABLE_HTML_ALTERNATIVE").MustBool(false))
  118. }
  119. if sec.HasKey("PROTOCOL") && sec.Key("PROTOCOL").String() == "smtp+startls" {
  120. log.Error("Deprecated fallback `[mailer]` `PROTOCOL = smtp+startls` present. Use `[mailer]` `PROTOCOL = smtp+starttls`` instead. This fallback will be removed in v1.19.0")
  121. sec.Key("PROTOCOL").SetValue("smtp+starttls")
  122. }
  123. // Set default values & validate
  124. sec.Key("NAME").MustString(AppName)
  125. sec.Key("PROTOCOL").In("", []string{"smtp", "smtps", "smtp+starttls", "smtp+unix", "sendmail", "dummy"})
  126. sec.Key("ENABLE_HELO").MustBool(true)
  127. sec.Key("FORCE_TRUST_SERVER_CERT").MustBool(false)
  128. sec.Key("USE_CLIENT_CERT").MustBool(false)
  129. sec.Key("SENDMAIL_PATH").MustString("sendmail")
  130. sec.Key("SENDMAIL_TIMEOUT").MustDuration(5 * time.Minute)
  131. sec.Key("SENDMAIL_CONVERT_CRLF").MustBool(true)
  132. sec.Key("FROM").MustString(sec.Key("USER").String())
  133. // Now map the values on to the MailService
  134. MailService = &Mailer{}
  135. if err := sec.MapTo(MailService); err != nil {
  136. log.Fatal("Unable to map [mailer] section on to MailService. Error: %v", err)
  137. }
  138. overrideHeader := rootCfg.Section("mailer.override_header").Keys()
  139. MailService.OverrideHeader = make(map[string][]string)
  140. for _, key := range overrideHeader {
  141. MailService.OverrideHeader[key.Name()] = key.Strings(",")
  142. }
  143. // Infer SMTPPort if not set
  144. if MailService.SMTPPort == "" {
  145. switch MailService.Protocol {
  146. case "smtp":
  147. MailService.SMTPPort = "25"
  148. case "smtps":
  149. MailService.SMTPPort = "465"
  150. case "smtp+starttls":
  151. MailService.SMTPPort = "587"
  152. }
  153. }
  154. // Infer Protocol
  155. if MailService.Protocol == "" {
  156. if strings.ContainsAny(MailService.SMTPAddr, "/\\") {
  157. MailService.Protocol = "smtp+unix"
  158. } else {
  159. switch MailService.SMTPPort {
  160. case "25":
  161. MailService.Protocol = "smtp"
  162. case "465":
  163. MailService.Protocol = "smtps"
  164. case "587":
  165. MailService.Protocol = "smtp+starttls"
  166. default:
  167. log.Error("unable to infer unspecified mailer.PROTOCOL from mailer.SMTP_PORT = %q, assume using smtps", MailService.SMTPPort)
  168. MailService.Protocol = "smtps"
  169. if MailService.SMTPPort == "" {
  170. MailService.SMTPPort = "465"
  171. }
  172. }
  173. }
  174. }
  175. // we want to warn if users use SMTP on a non-local IP;
  176. // we might as well take the opportunity to check that it has an IP at all
  177. // This check is not needed for sendmail
  178. switch MailService.Protocol {
  179. case "sendmail":
  180. var err error
  181. MailService.SendmailArgs, err = shellquote.Split(sec.Key("SENDMAIL_ARGS").String())
  182. if err != nil {
  183. log.Error("Failed to parse Sendmail args: '%s' with error %v", sec.Key("SENDMAIL_ARGS").String(), err)
  184. }
  185. case "smtp", "smtps", "smtp+starttls", "smtp+unix":
  186. ips := tryResolveAddr(MailService.SMTPAddr)
  187. if MailService.Protocol == "smtp" {
  188. for _, ip := range ips {
  189. if !ip.IP.IsLoopback() {
  190. log.Warn("connecting over insecure SMTP protocol to non-local address is not recommended")
  191. break
  192. }
  193. }
  194. }
  195. case "dummy": // just mention and do nothing
  196. }
  197. if MailService.From != "" {
  198. parsed, err := mail.ParseAddress(MailService.From)
  199. if err != nil {
  200. log.Fatal("Invalid mailer.FROM (%s): %v", MailService.From, err)
  201. }
  202. MailService.FromName = parsed.Name
  203. MailService.FromEmail = parsed.Address
  204. } else {
  205. log.Error("no mailer.FROM provided, email system may not work.")
  206. }
  207. MailService.FromDisplayNameFormatTemplate, _ = template.New("mailFrom").Parse("{{ .DisplayName }}")
  208. if MailService.FromDisplayNameFormat != "" {
  209. template, err := template.New("mailFrom").Parse(MailService.FromDisplayNameFormat)
  210. if err != nil {
  211. log.Error("mailer.FROM_DISPLAY_NAME_FORMAT is no valid template: %v", err)
  212. } else {
  213. MailService.FromDisplayNameFormatTemplate = template
  214. }
  215. }
  216. switch MailService.EnvelopeFrom {
  217. case "":
  218. MailService.OverrideEnvelopeFrom = false
  219. case "<>":
  220. MailService.EnvelopeFrom = ""
  221. MailService.OverrideEnvelopeFrom = true
  222. default:
  223. parsed, err := mail.ParseAddress(MailService.EnvelopeFrom)
  224. if err != nil {
  225. log.Fatal("Invalid mailer.ENVELOPE_FROM (%s): %v", MailService.EnvelopeFrom, err)
  226. }
  227. MailService.OverrideEnvelopeFrom = true
  228. MailService.EnvelopeFrom = parsed.Address
  229. }
  230. }
  231. func loadRegisterMailFrom(rootCfg ConfigProvider) {
  232. if !rootCfg.Section("service").Key("REGISTER_EMAIL_CONFIRM").MustBool() {
  233. return
  234. } else if MailService == nil {
  235. log.Warn("Register Mail Service: Mail Service is not enabled")
  236. return
  237. }
  238. Service.RegisterEmailConfirm = true
  239. }
  240. func loadNotifyMailFrom(rootCfg ConfigProvider) {
  241. if !rootCfg.Section("service").Key("ENABLE_NOTIFY_MAIL").MustBool() {
  242. return
  243. } else if MailService == nil {
  244. log.Warn("Notify Mail Service: Mail Service is not enabled")
  245. return
  246. }
  247. Service.EnableNotifyMail = true
  248. }
  249. func tryResolveAddr(addr string) []net.IPAddr {
  250. if strings.HasPrefix(addr, "[") && strings.HasSuffix(addr, "]") {
  251. addr = addr[1 : len(addr)-1]
  252. }
  253. ip := net.ParseIP(addr)
  254. if ip != nil {
  255. return []net.IPAddr{{IP: ip}}
  256. }
  257. ctx, cancel := context.WithTimeout(context.Background(), 2*time.Second)
  258. defer cancel()
  259. ips, err := net.DefaultResolver.LookupIPAddr(ctx, addr)
  260. if err != nil {
  261. log.Warn("could not look up mailer.SMTP_ADDR: %v", err)
  262. return nil
  263. }
  264. return ips
  265. }