github.com/topsteplocal/gophish@v0.6.0/models/smtp.go (about)

     1  package models
     2  
     3  import (
     4  	"crypto/tls"
     5  	"errors"
     6  	"net/mail"
     7  	"os"
     8  	"strconv"
     9  	"strings"
    10  	"time"
    11  
    12  	"github.com/gophish/gomail"
    13  	log "github.com/gophish/gophish/logger"
    14  	"github.com/gophish/gophish/mailer"
    15  	"github.com/jinzhu/gorm"
    16  )
    17  
    18  // Dialer is a wrapper around a standard gomail.Dialer in order
    19  // to implement the mailer.Dialer interface. This allows us to better
    20  // separate the mailer package as opposed to forcing a connection
    21  // between mailer and gomail.
    22  type Dialer struct {
    23  	*gomail.Dialer
    24  }
    25  
    26  // Dial wraps the gomail dialer's Dial command
    27  func (d *Dialer) Dial() (mailer.Sender, error) {
    28  	return d.Dialer.Dial()
    29  }
    30  
    31  // SMTP contains the attributes needed to handle the sending of campaign emails
    32  type SMTP struct {
    33  	Id               int64     `json:"id" gorm:"column:id; primary_key:yes"`
    34  	UserId           int64     `json:"-" gorm:"column:user_id"`
    35  	Interface        string    `json:"interface_type" gorm:"column:interface_type"`
    36  	Name             string    `json:"name"`
    37  	Host             string    `json:"host"`
    38  	Username         string    `json:"username,omitempty"`
    39  	Password         string    `json:"password,omitempty"`
    40  	FromAddress      string    `json:"from_address"`
    41  	IgnoreCertErrors bool      `json:"ignore_cert_errors"`
    42  	Headers          []Header  `json:"headers"`
    43  	ModifiedDate     time.Time `json:"modified_date"`
    44  }
    45  
    46  // Header contains the fields and methods for a sending profile to have
    47  // custom headers
    48  type Header struct {
    49  	Id     int64  `json:"-"`
    50  	SMTPId int64  `json:"-"`
    51  	Key    string `json:"key"`
    52  	Value  string `json:"value"`
    53  }
    54  
    55  // ErrFromAddressNotSpecified is thrown when there is no "From" address
    56  // specified in the SMTP configuration
    57  var ErrFromAddressNotSpecified = errors.New("No From Address specified")
    58  
    59  // ErrHostNotSpecified is thrown when there is no Host specified
    60  // in the SMTP configuration
    61  var ErrHostNotSpecified = errors.New("No SMTP Host specified")
    62  
    63  // ErrInvalidHost indicates that the SMTP server string is invalid
    64  var ErrInvalidHost = errors.New("Invalid SMTP server address")
    65  
    66  // TableName specifies the database tablename for Gorm to use
    67  func (s SMTP) TableName() string {
    68  	return "smtp"
    69  }
    70  
    71  // Validate ensures that SMTP configs/connections are valid
    72  func (s *SMTP) Validate() error {
    73  	switch {
    74  	case s.FromAddress == "":
    75  		return ErrFromAddressNotSpecified
    76  	case s.Host == "":
    77  		return ErrHostNotSpecified
    78  	}
    79  	_, err := mail.ParseAddress(s.FromAddress)
    80  	if err != nil {
    81  		return err
    82  	}
    83  	// Make sure addr is in host:port format
    84  	hp := strings.Split(s.Host, ":")
    85  	if len(hp) > 2 {
    86  		return ErrInvalidHost
    87  	} else if len(hp) < 2 {
    88  		hp = append(hp, "25")
    89  	}
    90  	_, err = strconv.Atoi(hp[1])
    91  	if err != nil {
    92  		return ErrInvalidHost
    93  	}
    94  	return err
    95  }
    96  
    97  // GetDialer returns a dialer for the given SMTP profile
    98  func (s *SMTP) GetDialer() (mailer.Dialer, error) {
    99  	// Setup the message and dial
   100  	hp := strings.Split(s.Host, ":")
   101  	if len(hp) < 2 {
   102  		hp = append(hp, "25")
   103  	}
   104  	// Any issues should have been caught in validation, but we'll
   105  	// double check here.
   106  	port, err := strconv.Atoi(hp[1])
   107  	if err != nil {
   108  		log.Error(err)
   109  		return nil, err
   110  	}
   111  	d := gomail.NewDialer(hp[0], port, s.Username, s.Password)
   112  	d.TLSConfig = &tls.Config{
   113  		ServerName:         s.Host,
   114  		InsecureSkipVerify: s.IgnoreCertErrors,
   115  	}
   116  	hostname, err := os.Hostname()
   117  	if err != nil {
   118  		log.Error(err)
   119  		hostname = "localhost"
   120  	}
   121  	d.LocalName = hostname
   122  	return &Dialer{d}, err
   123  }
   124  
   125  // GetSMTPs returns the SMTPs owned by the given user.
   126  func GetSMTPs(uid int64) ([]SMTP, error) {
   127  	ss := []SMTP{}
   128  	err := db.Where("user_id=?", uid).Find(&ss).Error
   129  	if err != nil {
   130  		log.Error(err)
   131  		return ss, err
   132  	}
   133  	for i := range ss {
   134  		err = db.Where("smtp_id=?", ss[i].Id).Find(&ss[i].Headers).Error
   135  		if err != nil && err != gorm.ErrRecordNotFound {
   136  			log.Error(err)
   137  			return ss, err
   138  		}
   139  	}
   140  	return ss, nil
   141  }
   142  
   143  // GetSMTP returns the SMTP, if it exists, specified by the given id and user_id.
   144  func GetSMTP(id int64, uid int64) (SMTP, error) {
   145  	s := SMTP{}
   146  	err := db.Where("user_id=? and id=?", uid, id).Find(&s).Error
   147  	if err != nil {
   148  		log.Error(err)
   149  	}
   150  	err = db.Where("smtp_id=?", s.Id).Find(&s.Headers).Error
   151  	if err != nil && err != gorm.ErrRecordNotFound {
   152  		log.Error(err)
   153  		return s, err
   154  	}
   155  	return s, err
   156  }
   157  
   158  // GetSMTPByName returns the SMTP, if it exists, specified by the given name and user_id.
   159  func GetSMTPByName(n string, uid int64) (SMTP, error) {
   160  	s := SMTP{}
   161  	err := db.Where("user_id=? and name=?", uid, n).Find(&s).Error
   162  	if err != nil {
   163  		log.Error(err)
   164  		return s, err
   165  	}
   166  	err = db.Where("smtp_id=?", s.Id).Find(&s.Headers).Error
   167  	if err != nil && err != gorm.ErrRecordNotFound {
   168  		log.Error(err)
   169  	}
   170  	return s, err
   171  }
   172  
   173  // PostSMTP creates a new SMTP in the database.
   174  func PostSMTP(s *SMTP) error {
   175  	err := s.Validate()
   176  	if err != nil {
   177  		log.Error(err)
   178  		return err
   179  	}
   180  	// Insert into the DB
   181  	err = db.Save(s).Error
   182  	if err != nil {
   183  		log.Error(err)
   184  	}
   185  	// Save custom headers
   186  	for i := range s.Headers {
   187  		s.Headers[i].SMTPId = s.Id
   188  		err := db.Save(&s.Headers[i]).Error
   189  		if err != nil {
   190  			log.Error(err)
   191  			return err
   192  		}
   193  	}
   194  	return err
   195  }
   196  
   197  // PutSMTP edits an existing SMTP in the database.
   198  // Per the PUT Method RFC, it presumes all data for a SMTP is provided.
   199  func PutSMTP(s *SMTP) error {
   200  	err := s.Validate()
   201  	if err != nil {
   202  		log.Error(err)
   203  		return err
   204  	}
   205  	err = db.Where("id=?", s.Id).Save(s).Error
   206  	if err != nil {
   207  		log.Error(err)
   208  	}
   209  	// Delete all custom headers, and replace with new ones
   210  	err = db.Where("smtp_id=?", s.Id).Delete(&Header{}).Error
   211  	if err != nil && err != gorm.ErrRecordNotFound {
   212  		log.Error(err)
   213  		return err
   214  	}
   215  	for i := range s.Headers {
   216  		s.Headers[i].SMTPId = s.Id
   217  		err := db.Save(&s.Headers[i]).Error
   218  		if err != nil {
   219  			log.Error(err)
   220  			return err
   221  		}
   222  	}
   223  	return err
   224  }
   225  
   226  // DeleteSMTP deletes an existing SMTP in the database.
   227  // An error is returned if a SMTP with the given user id and SMTP id is not found.
   228  func DeleteSMTP(id int64, uid int64) error {
   229  	// Delete all custom headers
   230  	err = db.Where("smtp_id=?", id).Delete(&Header{}).Error
   231  	if err != nil {
   232  		log.Error(err)
   233  		return err
   234  	}
   235  	err = db.Where("user_id=?", uid).Delete(SMTP{Id: id}).Error
   236  	if err != nil {
   237  		log.Error(err)
   238  	}
   239  	return err
   240  }