github.com/noname1007/gophish@v0.9.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  		return s, err
   150  	}
   151  	err = db.Where("smtp_id=?", s.Id).Find(&s.Headers).Error
   152  	if err != nil && err != gorm.ErrRecordNotFound {
   153  		log.Error(err)
   154  		return s, err
   155  	}
   156  	return s, err
   157  }
   158  
   159  // GetSMTPByName returns the SMTP, if it exists, specified by the given name and user_id.
   160  func GetSMTPByName(n string, uid int64) (SMTP, error) {
   161  	s := SMTP{}
   162  	err := db.Where("user_id=? and name=?", uid, n).Find(&s).Error
   163  	if err != nil {
   164  		log.Error(err)
   165  		return s, err
   166  	}
   167  	err = db.Where("smtp_id=?", s.Id).Find(&s.Headers).Error
   168  	if err != nil && err != gorm.ErrRecordNotFound {
   169  		log.Error(err)
   170  	}
   171  	return s, err
   172  }
   173  
   174  // PostSMTP creates a new SMTP in the database.
   175  func PostSMTP(s *SMTP) error {
   176  	err := s.Validate()
   177  	if err != nil {
   178  		log.Error(err)
   179  		return err
   180  	}
   181  	// Insert into the DB
   182  	err = db.Save(s).Error
   183  	if err != nil {
   184  		log.Error(err)
   185  	}
   186  	// Save custom headers
   187  	for i := range s.Headers {
   188  		s.Headers[i].SMTPId = s.Id
   189  		err := db.Save(&s.Headers[i]).Error
   190  		if err != nil {
   191  			log.Error(err)
   192  			return err
   193  		}
   194  	}
   195  	return err
   196  }
   197  
   198  // PutSMTP edits an existing SMTP in the database.
   199  // Per the PUT Method RFC, it presumes all data for a SMTP is provided.
   200  func PutSMTP(s *SMTP) error {
   201  	err := s.Validate()
   202  	if err != nil {
   203  		log.Error(err)
   204  		return err
   205  	}
   206  	err = db.Where("id=?", s.Id).Save(s).Error
   207  	if err != nil {
   208  		log.Error(err)
   209  	}
   210  	// Delete all custom headers, and replace with new ones
   211  	err = db.Where("smtp_id=?", s.Id).Delete(&Header{}).Error
   212  	if err != nil && err != gorm.ErrRecordNotFound {
   213  		log.Error(err)
   214  		return err
   215  	}
   216  	// Save custom headers
   217  	for i := range s.Headers {
   218  		s.Headers[i].SMTPId = s.Id
   219  		err := db.Save(&s.Headers[i]).Error
   220  		if err != nil {
   221  			log.Error(err)
   222  			return err
   223  		}
   224  	}
   225  	return err
   226  }
   227  
   228  // DeleteSMTP deletes an existing SMTP in the database.
   229  // An error is returned if a SMTP with the given user id and SMTP id is not found.
   230  func DeleteSMTP(id int64, uid int64) error {
   231  	// Delete all custom headers
   232  	err := db.Where("smtp_id=?", id).Delete(&Header{}).Error
   233  	if err != nil {
   234  		log.Error(err)
   235  		return err
   236  	}
   237  	err = db.Where("user_id=?", uid).Delete(SMTP{Id: id}).Error
   238  	if err != nil {
   239  		log.Error(err)
   240  	}
   241  	return err
   242  }