mirror of https://github.com/gophish/gophish
121 lines
3.2 KiB
Go
121 lines
3.2 KiB
Go
package models
|
|
|
|
import (
|
|
"crypto/rand"
|
|
"fmt"
|
|
"math/big"
|
|
"net"
|
|
"net/mail"
|
|
"time"
|
|
|
|
log "github.com/gophish/gophish/logger"
|
|
"github.com/jinzhu/gorm"
|
|
"github.com/oschwald/maxminddb-golang"
|
|
)
|
|
|
|
type mmCity struct {
|
|
GeoPoint mmGeoPoint `maxminddb:"location"`
|
|
}
|
|
|
|
type mmGeoPoint struct {
|
|
Latitude float64 `maxminddb:"latitude"`
|
|
Longitude float64 `maxminddb:"longitude"`
|
|
}
|
|
|
|
// Result contains the fields for a result object,
|
|
// which is a representation of a target in a campaign.
|
|
type Result struct {
|
|
Id int64 `json:"-"`
|
|
CampaignId int64 `json:"-"`
|
|
UserId int64 `json:"-"`
|
|
RId string `json:"id"`
|
|
Email string `json:"email"`
|
|
FirstName string `json:"first_name"`
|
|
LastName string `json:"last_name"`
|
|
Position string `json:"position"`
|
|
Status string `json:"status" sql:"not null"`
|
|
IP string `json:"ip"`
|
|
Latitude float64 `json:"latitude"`
|
|
Longitude float64 `json:"longitude"`
|
|
SendDate time.Time `json:"send_date"`
|
|
Reported bool `json:"reported" sql:"not null"`
|
|
}
|
|
|
|
// UpdateStatus updates the status of the result in the database
|
|
func (r *Result) UpdateStatus(s string) error {
|
|
return db.Table("results").Where("id=?", r.Id).Update("status", s).Error
|
|
}
|
|
|
|
// UpdateReported updates when a user reports a campaign
|
|
func (r *Result) UpdateReported(s bool) error {
|
|
return db.Table("results").Where("id=?", r.Id).Update("reported", s).Error
|
|
}
|
|
|
|
// UpdateGeo updates the latitude and longitude of the result in
|
|
// the database given an IP address
|
|
func (r *Result) UpdateGeo(addr string) error {
|
|
// Open a connection to the maxmind db
|
|
mmdb, err := maxminddb.Open("static/db/geolite2-city.mmdb")
|
|
if err != nil {
|
|
log.Fatal(err)
|
|
}
|
|
defer mmdb.Close()
|
|
ip := net.ParseIP(addr)
|
|
var city mmCity
|
|
// Get the record
|
|
err = mmdb.Lookup(ip, &city)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
// Update the database with the record information
|
|
return db.Table("results").Where("id=?", r.Id).Updates(map[string]interface{}{
|
|
"ip": addr,
|
|
"latitude": city.GeoPoint.Latitude,
|
|
"longitude": city.GeoPoint.Longitude,
|
|
}).Error
|
|
}
|
|
|
|
// GenerateId generates a unique key to represent the result
|
|
// in the database
|
|
func (r *Result) GenerateId() error {
|
|
// Keep trying until we generate a unique key (shouldn't take more than one or two iterations)
|
|
const alphaNum = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789"
|
|
k := make([]byte, 7)
|
|
for {
|
|
for i := range k {
|
|
idx, err := rand.Int(rand.Reader, big.NewInt(int64(len(alphaNum))))
|
|
if err != nil {
|
|
return err
|
|
}
|
|
k[i] = alphaNum[idx.Int64()]
|
|
}
|
|
r.RId = string(k)
|
|
err := db.Table("results").Where("r_id=?", r.RId).First(&Result{}).Error
|
|
if err == gorm.ErrRecordNotFound {
|
|
break
|
|
}
|
|
}
|
|
return nil
|
|
}
|
|
|
|
// FormatAddress returns the email address to use in the "To" header of the email
|
|
func (r *Result) FormatAddress() string {
|
|
addr := r.Email
|
|
if r.FirstName != "" && r.LastName != "" {
|
|
a := &mail.Address{
|
|
Name: fmt.Sprintf("%s %s", r.FirstName, r.LastName),
|
|
Address: r.Email,
|
|
}
|
|
addr = a.String()
|
|
}
|
|
return addr
|
|
}
|
|
|
|
// GetResult returns the Result object from the database
|
|
// given the ResultId
|
|
func GetResult(rid string) (Result, error) {
|
|
r := Result{}
|
|
err := db.Where("r_id=?", rid).First(&r).Error
|
|
return r, err
|
|
}
|