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  }