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