Ви не можете вибрати більше 25 тем Теми мають розпочинатися з літери або цифри, можуть містити дефіси (-) і не повинні перевищувати 35 символів.

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132
  1. // Copyright (c) 2020 Shivaram Lingamneni
  2. // released under the MIT license
  3. package email
  4. import (
  5. "errors"
  6. "fmt"
  7. "net"
  8. "regexp"
  9. "strings"
  10. "time"
  11. "github.com/ergochat/ergo/irc/smtp"
  12. )
  13. var (
  14. ErrBlacklistedAddress = errors.New("Email address is blacklisted")
  15. ErrInvalidAddress = errors.New("Email address is invalid")
  16. ErrNoMXRecord = errors.New("Couldn't resolve MX record")
  17. )
  18. type MTAConfig struct {
  19. Server string
  20. Port int
  21. Username string
  22. Password string
  23. }
  24. type MailtoConfig struct {
  25. // legacy config format assumed the use of an MTA/smarthost,
  26. // so server, port, etc. appear directly at top level
  27. // XXX: see https://github.com/go-yaml/yaml/issues/63
  28. MTAConfig `yaml:",inline"`
  29. Enabled bool
  30. Sender string
  31. HeloDomain string `yaml:"helo-domain"`
  32. RequireTLS bool `yaml:"require-tls"`
  33. VerifyMessageSubject string `yaml:"verify-message-subject"`
  34. DKIM DKIMConfig
  35. MTAReal MTAConfig `yaml:"mta"`
  36. BlacklistRegexes []string `yaml:"blacklist-regexes"`
  37. blacklistRegexes []*regexp.Regexp
  38. Timeout time.Duration
  39. }
  40. func (config *MailtoConfig) Postprocess(heloDomain string) (err error) {
  41. if config.Sender == "" {
  42. return errors.New("Invalid mailto sender address")
  43. }
  44. // check for MTA config fields at top level,
  45. // copy to MTAReal if present
  46. if config.Server != "" && config.MTAReal.Server == "" {
  47. config.MTAReal = config.MTAConfig
  48. }
  49. if config.HeloDomain == "" {
  50. config.HeloDomain = heloDomain
  51. }
  52. for _, reg := range config.BlacklistRegexes {
  53. compiled, err := regexp.Compile(fmt.Sprintf("^%s$", reg))
  54. if err != nil {
  55. return err
  56. }
  57. config.blacklistRegexes = append(config.blacklistRegexes, compiled)
  58. }
  59. if config.MTAConfig.Server != "" {
  60. // smarthost, nothing more to validate
  61. return nil
  62. }
  63. return config.DKIM.Postprocess()
  64. }
  65. // are we sending email directly, as opposed to deferring to an MTA?
  66. func (config *MailtoConfig) DirectSendingEnabled() bool {
  67. return config.MTAReal.Server == ""
  68. }
  69. // get the preferred MX record hostname, "" on error
  70. func lookupMX(domain string) (server string) {
  71. var minPref uint16
  72. results, err := net.LookupMX(domain)
  73. if err != nil {
  74. return
  75. }
  76. for _, result := range results {
  77. if minPref == 0 || result.Pref < minPref {
  78. server, minPref = result.Host, result.Pref
  79. }
  80. }
  81. return
  82. }
  83. func SendMail(config MailtoConfig, recipient string, msg []byte) (err error) {
  84. for _, reg := range config.blacklistRegexes {
  85. if reg.MatchString(recipient) {
  86. return ErrBlacklistedAddress
  87. }
  88. }
  89. if config.DKIM.Domain != "" {
  90. msg, err = DKIMSign(msg, config.DKIM)
  91. if err != nil {
  92. return
  93. }
  94. }
  95. var addr string
  96. var auth smtp.Auth
  97. if !config.DirectSendingEnabled() {
  98. addr = fmt.Sprintf("%s:%d", config.MTAReal.Server, config.MTAReal.Port)
  99. if config.MTAReal.Username != "" && config.MTAReal.Password != "" {
  100. auth = smtp.PlainAuth("", config.MTAReal.Username, config.MTAReal.Password, config.MTAReal.Server)
  101. }
  102. } else {
  103. idx := strings.IndexByte(recipient, '@')
  104. if idx == -1 {
  105. return ErrInvalidAddress
  106. }
  107. mx := lookupMX(recipient[idx+1:])
  108. if mx == "" {
  109. return ErrNoMXRecord
  110. }
  111. addr = fmt.Sprintf("%s:smtp", mx)
  112. }
  113. return smtp.SendMail(addr, auth, config.HeloDomain, config.Sender, []string{recipient}, msg, config.RequireTLS, config.Timeout)
  114. }