Nelze vybrat více než 25 témat Téma musí začínat písmenem nebo číslem, může obsahovat pomlčky („-“) a může být dlouhé až 35 znaků.

email.go 3.1KB

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