🎣 Open-Source Phishing Toolkit
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.

188 lines
4.8 KiB

  1. package models
  2. import (
  3. "encoding/base64"
  4. "fmt"
  5. "io"
  6. "net/mail"
  7. "strings"
  8. "github.com/gophish/gomail"
  9. "github.com/gophish/gophish/config"
  10. log "github.com/gophish/gophish/logger"
  11. "github.com/gophish/gophish/mailer"
  12. )
  13. // PreviewPrefix is the standard prefix added to the rid parameter when sending
  14. // test emails.
  15. const PreviewPrefix = "preview-"
  16. // EmailRequest is the structure of a request
  17. // to send a test email to test an SMTP connection.
  18. // This type implements the mailer.Mail interface.
  19. type EmailRequest struct {
  20. Id int64 `json:"-"`
  21. Template Template `json:"template"`
  22. TemplateId int64 `json:"-"`
  23. Page Page `json:"page"`
  24. PageId int64 `json:"-"`
  25. SMTP SMTP `json:"smtp"`
  26. URL string `json:"url"`
  27. Tracker string `json:"tracker" gorm:"-"`
  28. TrackingURL string `json:"tracking_url" gorm:"-"`
  29. UserId int64 `json:"-"`
  30. ErrorChan chan (error) `json:"-" gorm:"-"`
  31. RId string `json:"id"`
  32. FromAddress string `json:"-"`
  33. BaseRecipient
  34. }
  35. func (s *EmailRequest) getBaseURL() string {
  36. return s.URL
  37. }
  38. func (s *EmailRequest) getFromAddress() string {
  39. return s.FromAddress
  40. }
  41. // Validate ensures the SendTestEmailRequest structure
  42. // is valid.
  43. func (s *EmailRequest) Validate() error {
  44. switch {
  45. case s.Email == "":
  46. return ErrEmailNotSpecified
  47. case s.FromAddress == "" && s.SMTP.FromAddress == "":
  48. return ErrFromAddressNotSpecified
  49. }
  50. return nil
  51. }
  52. // Backoff treats temporary errors as permanent since this is expected to be a
  53. // synchronous operation. It returns any errors given back to the ErrorChan
  54. func (s *EmailRequest) Backoff(reason error) error {
  55. s.ErrorChan <- reason
  56. return nil
  57. }
  58. // Error returns an error on the ErrorChan.
  59. func (s *EmailRequest) Error(err error) error {
  60. s.ErrorChan <- err
  61. return nil
  62. }
  63. // Success returns nil on the ErrorChan to indicate that the email was sent
  64. // successfully.
  65. func (s *EmailRequest) Success() error {
  66. s.ErrorChan <- nil
  67. return nil
  68. }
  69. // PostEmailRequest stores a SendTestEmailRequest in the database.
  70. func PostEmailRequest(s *EmailRequest) error {
  71. // Generate an ID to be used in the underlying Result object
  72. rid, err := generateResultId()
  73. if err != nil {
  74. return err
  75. }
  76. s.RId = fmt.Sprintf("%s%s", PreviewPrefix, rid)
  77. return db.Save(&s).Error
  78. }
  79. // GetEmailRequestByResultId retrieves the EmailRequest by the underlying rid
  80. // parameter.
  81. func GetEmailRequestByResultId(id string) (EmailRequest, error) {
  82. s := EmailRequest{}
  83. err := db.Table("email_requests").Where("r_id=?", id).First(&s).Error
  84. return s, err
  85. }
  86. // Generate fills in the details of a gomail.Message with the contents
  87. // from the SendTestEmailRequest.
  88. func (s *EmailRequest) Generate(msg *gomail.Message) error {
  89. f, err := mail.ParseAddress(s.FromAddress)
  90. if err != nil {
  91. return err
  92. }
  93. msg.SetAddressHeader("From", f.Address, f.Name)
  94. ptx, err := NewPhishingTemplateContext(s, s.BaseRecipient, s.RId)
  95. if err != nil {
  96. return err
  97. }
  98. url, err := ExecuteTemplate(s.URL, ptx)
  99. if err != nil {
  100. return err
  101. }
  102. s.URL = url
  103. // Add the transparency headers
  104. msg.SetHeader("X-Mailer", config.ServerName)
  105. if conf.ContactAddress != "" {
  106. msg.SetHeader("X-Gophish-Contact", conf.ContactAddress)
  107. }
  108. // Parse the customHeader templates
  109. for _, header := range s.SMTP.Headers {
  110. key, err := ExecuteTemplate(header.Key, ptx)
  111. if err != nil {
  112. log.Error(err)
  113. }
  114. value, err := ExecuteTemplate(header.Value, ptx)
  115. if err != nil {
  116. log.Error(err)
  117. }
  118. // Add our header immediately
  119. msg.SetHeader(key, value)
  120. }
  121. // Parse remaining templates
  122. subject, err := ExecuteTemplate(s.Template.Subject, ptx)
  123. if err != nil {
  124. log.Error(err)
  125. }
  126. // don't set the Subject header if it is blank
  127. if subject != "" {
  128. msg.SetHeader("Subject", subject)
  129. }
  130. msg.SetHeader("To", s.FormatAddress())
  131. if s.Template.Text != "" {
  132. text, err := ExecuteTemplate(s.Template.Text, ptx)
  133. if err != nil {
  134. log.Error(err)
  135. }
  136. msg.SetBody("text/plain", text)
  137. }
  138. if s.Template.HTML != "" {
  139. html, err := ExecuteTemplate(s.Template.HTML, ptx)
  140. if err != nil {
  141. log.Error(err)
  142. }
  143. if s.Template.Text == "" {
  144. msg.SetBody("text/html", html)
  145. } else {
  146. msg.AddAlternative("text/html", html)
  147. }
  148. }
  149. // Attach the files
  150. for _, a := range s.Template.Attachments {
  151. msg.Attach(func(a Attachment) (string, gomail.FileSetting, gomail.FileSetting) {
  152. h := map[string][]string{"Content-ID": {fmt.Sprintf("<%s>", a.Name)}}
  153. return a.Name, gomail.SetCopyFunc(func(w io.Writer) error {
  154. decoder := base64.NewDecoder(base64.StdEncoding, strings.NewReader(a.Content))
  155. _, err = io.Copy(w, decoder)
  156. return err
  157. }), gomail.SetHeader(h)
  158. }(a))
  159. }
  160. return nil
  161. }
  162. // GetDialer returns the mailer.Dialer for the underlying SMTP object
  163. func (s *EmailRequest) GetDialer() (mailer.Dialer, error) {
  164. return s.SMTP.GetDialer()
  165. }