github.com/merlinepedra/gopphish-attack@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  }