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