2017-12-09 21:42:07 +00:00
|
|
|
package models
|
|
|
|
|
|
|
|
import (
|
|
|
|
"encoding/base64"
|
|
|
|
"fmt"
|
|
|
|
"io"
|
|
|
|
"net/mail"
|
|
|
|
"strings"
|
|
|
|
|
|
|
|
"github.com/gophish/gomail"
|
2018-06-19 02:37:59 +00:00
|
|
|
"github.com/gophish/gophish/config"
|
2018-05-04 00:07:41 +00:00
|
|
|
log "github.com/gophish/gophish/logger"
|
2017-12-09 21:42:07 +00:00
|
|
|
"github.com/gophish/gophish/mailer"
|
|
|
|
)
|
|
|
|
|
2018-06-09 02:20:52 +00:00
|
|
|
// PreviewPrefix is the standard prefix added to the rid parameter when sending
|
|
|
|
// test emails.
|
|
|
|
const PreviewPrefix = "preview-"
|
|
|
|
|
|
|
|
// EmailRequest is the structure of a request
|
2017-12-09 21:42:07 +00:00
|
|
|
// to send a test email to test an SMTP connection.
|
|
|
|
// This type implements the mailer.Mail interface.
|
2018-06-09 02:20:52 +00:00
|
|
|
type EmailRequest struct {
|
|
|
|
Id int64 `json:"-"`
|
|
|
|
Template Template `json:"template"`
|
|
|
|
TemplateId int64 `json:"-"`
|
|
|
|
Page Page `json:"page"`
|
|
|
|
PageId int64 `json:"-"`
|
|
|
|
SMTP SMTP `json:"smtp"`
|
|
|
|
URL string `json:"url"`
|
|
|
|
Tracker string `json:"tracker" gorm:"-"`
|
|
|
|
TrackingURL string `json:"tracking_url" gorm:"-"`
|
|
|
|
UserId int64 `json:"-"`
|
|
|
|
ErrorChan chan (error) `json:"-" gorm:"-"`
|
|
|
|
RId string `json:"id"`
|
|
|
|
FromAddress string `json:"-"`
|
|
|
|
BaseRecipient
|
|
|
|
}
|
|
|
|
|
|
|
|
func (s *EmailRequest) getBaseURL() string {
|
|
|
|
return s.URL
|
|
|
|
}
|
|
|
|
|
|
|
|
func (s *EmailRequest) getFromAddress() string {
|
|
|
|
return s.FromAddress
|
2017-12-09 21:42:07 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// Validate ensures the SendTestEmailRequest structure
|
|
|
|
// is valid.
|
2018-06-09 02:20:52 +00:00
|
|
|
func (s *EmailRequest) Validate() error {
|
2017-12-09 21:42:07 +00:00
|
|
|
switch {
|
|
|
|
case s.Email == "":
|
|
|
|
return ErrEmailNotSpecified
|
2018-06-09 02:20:52 +00:00
|
|
|
case s.FromAddress == "" && s.SMTP.FromAddress == "":
|
|
|
|
return ErrFromAddressNotSpecified
|
2017-12-09 21:42:07 +00:00
|
|
|
}
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
|
|
|
// Backoff treats temporary errors as permanent since this is expected to be a
|
|
|
|
// synchronous operation. It returns any errors given back to the ErrorChan
|
2018-06-09 02:20:52 +00:00
|
|
|
func (s *EmailRequest) Backoff(reason error) error {
|
2017-12-09 21:42:07 +00:00
|
|
|
s.ErrorChan <- reason
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
|
|
|
// Error returns an error on the ErrorChan.
|
2018-06-09 02:20:52 +00:00
|
|
|
func (s *EmailRequest) Error(err error) error {
|
2017-12-09 21:42:07 +00:00
|
|
|
s.ErrorChan <- err
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
|
|
|
// Success returns nil on the ErrorChan to indicate that the email was sent
|
|
|
|
// successfully.
|
2018-06-09 02:20:52 +00:00
|
|
|
func (s *EmailRequest) Success() error {
|
2017-12-09 21:42:07 +00:00
|
|
|
s.ErrorChan <- nil
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
2018-06-09 02:20:52 +00:00
|
|
|
// PostEmailRequest stores a SendTestEmailRequest in the database.
|
|
|
|
func PostEmailRequest(s *EmailRequest) error {
|
|
|
|
// Generate an ID to be used in the underlying Result object
|
|
|
|
rid, err := generateResultId()
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
s.RId = fmt.Sprintf("%s%s", PreviewPrefix, rid)
|
|
|
|
return db.Save(&s).Error
|
|
|
|
}
|
|
|
|
|
|
|
|
// GetEmailRequestByResultId retrieves the EmailRequest by the underlying rid
|
|
|
|
// parameter.
|
|
|
|
func GetEmailRequestByResultId(id string) (EmailRequest, error) {
|
|
|
|
s := EmailRequest{}
|
|
|
|
err := db.Table("email_requests").Where("r_id=?", id).First(&s).Error
|
|
|
|
return s, err
|
|
|
|
}
|
|
|
|
|
2017-12-09 21:42:07 +00:00
|
|
|
// Generate fills in the details of a gomail.Message with the contents
|
|
|
|
// from the SendTestEmailRequest.
|
2018-06-09 02:20:52 +00:00
|
|
|
func (s *EmailRequest) Generate(msg *gomail.Message) error {
|
|
|
|
f, err := mail.ParseAddress(s.FromAddress)
|
2017-12-09 21:42:07 +00:00
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
fn := f.Name
|
|
|
|
if fn == "" {
|
|
|
|
fn = f.Address
|
|
|
|
}
|
|
|
|
msg.SetAddressHeader("From", f.Address, f.Name)
|
|
|
|
|
2018-06-09 02:20:52 +00:00
|
|
|
ptx, err := NewPhishingTemplateContext(s, s.BaseRecipient, s.RId)
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
2020-04-16 00:55:43 +00:00
|
|
|
// Getting all template params.. current + extended.
|
|
|
|
allTemplateParams := GetAllTemplateParams(ptx)
|
2018-06-09 02:20:52 +00:00
|
|
|
|
2020-04-16 00:55:43 +00:00
|
|
|
url, err := ExecuteTemplate(s.URL, allTemplateParams)
|
2018-01-13 23:49:42 +00:00
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
s.URL = url
|
|
|
|
|
2018-06-19 02:37:59 +00:00
|
|
|
// Add the transparency headers
|
|
|
|
msg.SetHeader("X-Mailer", config.ServerName)
|
2018-12-15 21:42:32 +00:00
|
|
|
if conf.ContactAddress != "" {
|
|
|
|
msg.SetHeader("X-Gophish-Contact", conf.ContactAddress)
|
2018-06-19 02:37:59 +00:00
|
|
|
}
|
|
|
|
|
2017-12-09 21:42:07 +00:00
|
|
|
// Parse the customHeader templates
|
|
|
|
for _, header := range s.SMTP.Headers {
|
2020-04-16 00:55:43 +00:00
|
|
|
key, err := ExecuteTemplate(header.Key, allTemplateParams)
|
2017-12-09 21:42:07 +00:00
|
|
|
if err != nil {
|
2018-05-04 00:07:41 +00:00
|
|
|
log.Error(err)
|
2017-12-09 21:42:07 +00:00
|
|
|
}
|
|
|
|
|
2020-04-16 00:55:43 +00:00
|
|
|
value, err := ExecuteTemplate(header.Value, allTemplateParams)
|
2017-12-09 21:42:07 +00:00
|
|
|
if err != nil {
|
2018-05-04 00:07:41 +00:00
|
|
|
log.Error(err)
|
2017-12-09 21:42:07 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// Add our header immediately
|
|
|
|
msg.SetHeader(key, value)
|
|
|
|
}
|
|
|
|
|
|
|
|
// Parse remaining templates
|
2020-04-16 00:55:43 +00:00
|
|
|
subject, err := ExecuteTemplate(s.Template.Subject, allTemplateParams)
|
2017-12-09 21:42:07 +00:00
|
|
|
if err != nil {
|
2018-05-04 00:07:41 +00:00
|
|
|
log.Error(err)
|
2017-12-09 21:42:07 +00:00
|
|
|
}
|
2018-02-10 19:46:08 +00:00
|
|
|
// don't set the Subject header if it is blank
|
|
|
|
if len(subject) != 0 {
|
|
|
|
msg.SetHeader("Subject", subject)
|
|
|
|
}
|
2017-12-09 21:42:07 +00:00
|
|
|
|
|
|
|
msg.SetHeader("To", s.FormatAddress())
|
|
|
|
if s.Template.Text != "" {
|
2020-04-16 00:55:43 +00:00
|
|
|
text, err := ExecuteTemplate(s.Template.Text, allTemplateParams)
|
2017-12-09 21:42:07 +00:00
|
|
|
if err != nil {
|
2018-05-04 00:07:41 +00:00
|
|
|
log.Error(err)
|
2017-12-09 21:42:07 +00:00
|
|
|
}
|
|
|
|
msg.SetBody("text/plain", text)
|
|
|
|
}
|
|
|
|
if s.Template.HTML != "" {
|
2020-04-16 00:55:43 +00:00
|
|
|
html, err := ExecuteTemplate(s.Template.HTML, allTemplateParams)
|
2017-12-09 21:42:07 +00:00
|
|
|
if err != nil {
|
2018-05-04 00:07:41 +00:00
|
|
|
log.Error(err)
|
2017-12-09 21:42:07 +00:00
|
|
|
}
|
|
|
|
if s.Template.Text == "" {
|
|
|
|
msg.SetBody("text/html", html)
|
|
|
|
} else {
|
|
|
|
msg.AddAlternative("text/html", html)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
// Attach the files
|
|
|
|
for _, a := range s.Template.Attachments {
|
|
|
|
msg.Attach(func(a Attachment) (string, gomail.FileSetting, gomail.FileSetting) {
|
|
|
|
h := map[string][]string{"Content-ID": {fmt.Sprintf("<%s>", a.Name)}}
|
|
|
|
return a.Name, gomail.SetCopyFunc(func(w io.Writer) error {
|
|
|
|
decoder := base64.NewDecoder(base64.StdEncoding, strings.NewReader(a.Content))
|
|
|
|
_, err = io.Copy(w, decoder)
|
|
|
|
return err
|
|
|
|
}), gomail.SetHeader(h)
|
|
|
|
}(a))
|
|
|
|
}
|
|
|
|
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
|
|
|
// GetDialer returns the mailer.Dialer for the underlying SMTP object
|
2018-06-09 02:20:52 +00:00
|
|
|
func (s *EmailRequest) GetDialer() (mailer.Dialer, error) {
|
2017-12-09 21:42:07 +00:00
|
|
|
return s.SMTP.GetDialer()
|
|
|
|
}
|