Merge pull request #1 from gophish/master

Catching up to the main branch from gophish/master
pull/91/head
Justin Gray 2016-01-20 22:27:58 -06:00
commit cf97520ddf
29 changed files with 1291 additions and 948 deletions

View File

@ -3,11 +3,11 @@
gophish gophish
======= =======
[![Build Status](https://travis-ci.org/gophish/gophish.svg?branch=master)](https://travis-ci.org/gophish/gophish) [![Build Status](https://travis-ci.org/gophish/gophish.svg?branch=master)](https://travis-ci.org/gophish/gophish) [![GoDoc](https://godoc.org/github.com/gophish/gophish?status.svg)](https://godoc.org/github.com/gophish/gophish)
Open-Source Phishing Toolkit Open-Source Phishing Toolkit
Gophish is an open-source phishing toolkit designed for businesses and penetration testers. It provides the ability to quickly and easily setup and execute phishing engagements and security awareness training. [Gophish](https://getgophish.com) is an open-source phishing toolkit designed for businesses and penetration testers. It provides the ability to quickly and easily setup and execute phishing engagements and security awareness training.
###Current Status ###Current Status
**Update 01/12/2016** **Update 01/12/2016**

View File

@ -1,10 +1,21 @@
{ {
"admin_url" : "127.0.0.1:3333", "admin_server" : {
"phish_url" : "0.0.0.0:80", "listen_url" : "127.0.0.1:3333",
"use_tls" : false,
"cert_path" : "example.crt",
"key_path" : "example.key"
},
"phish_server" : {
"listen_url" : "0.0.0.0:80",
"use_tls" : false,
"cert_path" : "example.crt",
"key_path": "example.key"
},
"smtp" : { "smtp" : {
"host" : "smtp.example.com:25", "host" : "smtp.example.com:25",
"user" : "username", "user" : "username",
"pass" : "password" "pass" : "password"
}, },
"dbpath" : "gophish.db" "db_path" : "gophish.db",
"migrations_path" : "db/migrations/"
} }

View File

@ -13,12 +13,29 @@ type SMTPServer struct {
Password string `json:"password"` Password string `json:"password"`
} }
// AdminServer represents the Admin server configuration details
type AdminServer struct {
ListenURL string `json:"listen_url"`
UseTLS bool `json:"use_tls"`
CertPath string `json:"cert_path"`
KeyPath string `json:"key_path"`
}
// PhishServer represents the Phish server configuration details
type PhishServer struct {
ListenURL string `json:"listen_url"`
UseTLS bool `json:"use_tls"`
CertPath string `json:"cert_path"`
KeyPath string `json:"key_path"`
}
// Config represents the configuration information. // Config represents the configuration information.
type Config struct { type Config struct {
AdminURL string `json:"admin_url"` AdminConf AdminServer `json:"admin_server"`
PhishURL string `json:"phish_url"` PhishConf PhishServer `json:"phish_server"`
SMTP SMTPServer `json:"smtp"` SMTPConf SMTPServer `json:"smtp"`
DBPath string `json:"dbpath"` DBPath string `json:"db_path"`
MigrationsPath string `json:"migrations_path"`
} }
var Conf Config var Conf Config

View File

@ -10,14 +10,14 @@ import (
"time" "time"
"github.com/PuerkitoBio/goquery" "github.com/PuerkitoBio/goquery"
ctx "github.com/gorilla/context"
"github.com/gorilla/mux"
"github.com/jinzhu/gorm"
"github.com/jordan-wright/email"
"github.com/gophish/gophish/auth" "github.com/gophish/gophish/auth"
"github.com/gophish/gophish/models" "github.com/gophish/gophish/models"
"github.com/gophish/gophish/util" "github.com/gophish/gophish/util"
"github.com/gophish/gophish/worker" "github.com/gophish/gophish/worker"
ctx "github.com/gorilla/context"
"github.com/gorilla/mux"
"github.com/jinzhu/gorm"
"github.com/jordan-wright/email"
) )
// Worker is the worker that processes phishing events and updates campaigns. // Worker is the worker that processes phishing events and updates campaigns.
@ -92,6 +92,7 @@ func API_Campaigns_Id(w http.ResponseWriter, r *http.Request) {
id, _ := strconv.ParseInt(vars["id"], 0, 64) id, _ := strconv.ParseInt(vars["id"], 0, 64)
c, err := models.GetCampaign(id, ctx.Get(r, "user_id").(int64)) c, err := models.GetCampaign(id, ctx.Get(r, "user_id").(int64))
if err != nil { if err != nil {
Logger.Println(err)
JSONResponse(w, models.Response{Success: false, Message: "Campaign not found"}, http.StatusNotFound) JSONResponse(w, models.Response{Success: false, Message: "Campaign not found"}, http.StatusNotFound)
return return
} }

View File

@ -9,9 +9,9 @@ import (
"os" "os"
"testing" "testing"
"github.com/gorilla/handlers"
"github.com/gophish/gophish/config" "github.com/gophish/gophish/config"
"github.com/gophish/gophish/models" "github.com/gophish/gophish/models"
"github.com/gorilla/handlers"
"github.com/stretchr/testify/suite" "github.com/stretchr/testify/suite"
) )
@ -26,13 +26,14 @@ var as *httptest.Server = httptest.NewUnstartedServer(handlers.CombinedLoggingHa
func (s *ControllersSuite) SetupSuite() { func (s *ControllersSuite) SetupSuite() {
config.Conf.DBPath = ":memory:" config.Conf.DBPath = ":memory:"
config.Conf.MigrationsPath = "../db/migrations/"
err := models.Setup() err := models.Setup()
if err != nil { if err != nil {
s.T().Fatalf("Failed creating database: %v", err) s.T().Fatalf("Failed creating database: %v", err)
} }
s.Nil(err) s.Nil(err)
// Setup the admin server for use in testing // Setup the admin server for use in testing
as.Config.Addr = config.Conf.AdminURL as.Config.Addr = config.Conf.AdminConf.ListenURL
as.Start() as.Start()
// Get the API key to use for these tests // Get the API key to use for these tests
u, err := models.GetUser(1) u, err := models.GetUser(1)

View File

@ -0,0 +1,28 @@
-- +goose Up
-- SQL in section 'Up' is executed when this migration is applied
CREATE TABLE IF NOT EXISTS "users" ("id" integer primary key autoincrement,"username" varchar(255) NOT NULL UNIQUE,"hash" varchar(255),"api_key" varchar(255) NOT NULL UNIQUE );
CREATE TABLE IF NOT EXISTS "templates" ("id" integer primary key autoincrement,"user_id" bigint,"name" varchar(255),"subject" varchar(255),"text" varchar(255),"html" varchar(255),"modified_date" datetime );
CREATE TABLE IF NOT EXISTS "targets" ("id" integer primary key autoincrement,"first_name" varchar(255),"last_name" varchar(255),"email" varchar(255),"position" varchar(255) );
CREATE TABLE IF NOT EXISTS "smtp" ("smtp_id" integer primary key autoincrement,"campaign_id" bigint,"host" varchar(255),"username" varchar(255),"from_address" varchar(255) );
CREATE TABLE IF NOT EXISTS "results" ("id" integer primary key autoincrement,"campaign_id" bigint,"user_id" bigint,"r_id" varchar(255),"email" varchar(255),"first_name" varchar(255),"last_name" varchar(255),"status" varchar(255) NOT NULL ,"ip" varchar(255),"latitude" real,"longitude" real );
CREATE TABLE IF NOT EXISTS "pages" ("id" integer primary key autoincrement,"user_id" bigint,"name" varchar(255),"html" varchar(255),"modified_date" datetime );
CREATE TABLE IF NOT EXISTS "groups" ("id" integer primary key autoincrement,"user_id" bigint,"name" varchar(255),"modified_date" datetime );
CREATE TABLE IF NOT EXISTS "group_targets" ("group_id" bigint,"target_id" bigint );
CREATE TABLE IF NOT EXISTS "events" ("id" integer primary key autoincrement,"campaign_id" bigint,"email" varchar(255),"time" datetime,"message" varchar(255) );
CREATE TABLE IF NOT EXISTS "campaigns" ("id" integer primary key autoincrement,"user_id" bigint,"name" varchar(255) NOT NULL ,"created_date" datetime,"completed_date" datetime,"template_id" bigint,"page_id" bigint,"status" varchar(255),"url" varchar(255) );
CREATE TABLE IF NOT EXISTS "attachments" ("id" integer primary key autoincrement,"template_id" bigint,"content" varchar(255),"type" varchar(255),"name" varchar(255) );
-- +goose Down
-- SQL section 'Down' is executed when this migration is rolled back
DROP TABLE "attachments";
DROP TABLE "campaigns";
DROP TABLE "events";
DROP TABLE "group_targets";
DROP TABLE "groups";
DROP TABLE "pages";
DROP TABLE "results";
DROP TABLE "smtp";
DROP TABLE "targets";
DROP TABLE "templates";
DROP TABLE "users";

View File

@ -30,11 +30,12 @@ import (
"log" "log"
"net/http" "net/http"
"os" "os"
"sync"
"github.com/gorilla/handlers"
"github.com/gophish/gophish/config" "github.com/gophish/gophish/config"
"github.com/gophish/gophish/controllers" "github.com/gophish/gophish/controllers"
"github.com/gophish/gophish/models" "github.com/gophish/gophish/models"
"github.com/gorilla/handlers"
) )
var Logger = log.New(os.Stdout, " ", log.Ldate|log.Ltime|log.Lshortfile) var Logger = log.New(os.Stdout, " ", log.Ldate|log.Ltime|log.Lshortfile)
@ -45,9 +46,31 @@ func main() {
if err != nil { if err != nil {
fmt.Println(err) fmt.Println(err)
} }
wg := &sync.WaitGroup{}
wg.Add(1)
// Start the web servers // Start the web servers
Logger.Printf("Admin server started at http://%s\n", config.Conf.AdminURL) go func() {
go http.ListenAndServe(config.Conf.AdminURL, handlers.CombinedLoggingHandler(os.Stdout, controllers.CreateAdminRouter())) defer wg.Done()
Logger.Printf("Phishing server started at http://%s\n", config.Conf.PhishURL) if config.Conf.AdminConf.UseTLS { // use TLS for Admin web server if available
http.ListenAndServe(config.Conf.PhishURL, handlers.CombinedLoggingHandler(os.Stdout, controllers.CreatePhishingRouter())) Logger.Printf("Starting admin server at https://%s\n", config.Conf.AdminConf.ListenURL)
Logger.Fatal(http.ListenAndServeTLS(config.Conf.AdminConf.ListenURL, config.Conf.AdminConf.CertPath, config.Conf.AdminConf.KeyPath,
handlers.CombinedLoggingHandler(os.Stdout, controllers.CreateAdminRouter())))
} else {
Logger.Printf("Starting admin server at http://%s\n", config.Conf.AdminConf.ListenURL)
Logger.Fatal(http.ListenAndServe(config.Conf.AdminConf.ListenURL, handlers.CombinedLoggingHandler(os.Stdout, controllers.CreateAdminRouter())))
}
}()
wg.Add(1)
go func() {
defer wg.Done()
if config.Conf.PhishConf.UseTLS { // use TLS for Phish web server if available
Logger.Printf("Starting phishing server at https://%s\n", config.Conf.PhishConf.ListenURL)
Logger.Fatal(http.ListenAndServeTLS(config.Conf.PhishConf.ListenURL, config.Conf.PhishConf.CertPath, config.Conf.PhishConf.KeyPath,
handlers.CombinedLoggingHandler(os.Stdout, controllers.CreatePhishingRouter())))
} else {
Logger.Printf("Starting phishing server at http://%s\n", config.Conf.PhishConf.ListenURL)
Logger.Fatal(http.ListenAndServe(config.Conf.PhishConf.ListenURL, handlers.CombinedLoggingHandler(os.Stdout, controllers.CreatePhishingRouter())))
}
}()
wg.Wait()
} }

View File

@ -117,21 +117,28 @@ func GetCampaign(id int64, uid int64) (Campaign, error) {
c := Campaign{} c := Campaign{}
err := db.Where("id = ?", id).Where("user_id = ?", uid).Find(&c).Error err := db.Where("id = ?", id).Where("user_id = ?", uid).Find(&c).Error
if err != nil { if err != nil {
Logger.Printf("%s: campaign not found\n", err)
return c, err return c, err
} }
err = db.Model(&c).Related(&c.Results).Error err = db.Model(&c).Related(&c.Results).Error
if err != nil { if err != nil {
Logger.Printf("%s: results not found for campaign\n", err)
return c, err return c, err
} }
err = db.Model(&c).Related(&c.Events).Error err = db.Model(&c).Related(&c.Events).Error
if err != nil { if err != nil {
Logger.Printf("%s: events not found for campaign\n", err)
return c, err return c, err
} }
err = db.Table("templates").Where("id=?", c.TemplateId).Find(&c.Template).Error err = db.Table("templates").Where("id=?", c.TemplateId).Find(&c.Template).Error
if err != nil { if err != nil {
Logger.Printf("%s: template not found for campaign\n", err)
return c, err return c, err
} }
err = db.Table("pages").Where("id=?", c.PageId).Find(&c.Page).Error err = db.Table("pages").Where("id=?", c.PageId).Find(&c.Page).Error
if err != nil {
Logger.Printf("%s: page not found for campaign\n", err)
}
return c, err return c, err
} }
@ -207,6 +214,7 @@ func PostCampaign(c *Campaign, uid int64) error {
//DeleteCampaign deletes the specified campaign //DeleteCampaign deletes the specified campaign
func DeleteCampaign(id int64) error { func DeleteCampaign(id int64) error {
Logger.Printf("Deleting campaign %d\n", id)
// Delete all the campaign results // Delete all the campaign results
err := db.Where("campaign_id=?", id).Delete(&Result{}).Error err := db.Where("campaign_id=?", id).Delete(&Result{}).Error
if err != nil { if err != nil {

View File

@ -8,6 +8,8 @@ import (
"log" "log"
"os" "os"
"bitbucket.org/liamstask/goose/lib/goose"
"github.com/gophish/gophish/config" "github.com/gophish/gophish/config"
"github.com/jinzhu/gorm" "github.com/jinzhu/gorm"
_ "github.com/mattn/go-sqlite3" // Blank import needed to import sqlite3 _ "github.com/mattn/go-sqlite3" // Blank import needed to import sqlite3
@ -62,6 +64,24 @@ func Setup() error {
if _, err = os.Stat(config.Conf.DBPath); err != nil || config.Conf.DBPath == ":memory:" { if _, err = os.Stat(config.Conf.DBPath); err != nil || config.Conf.DBPath == ":memory:" {
create_db = true create_db = true
} }
// Setup the goose configuration
migrateConf := &goose.DBConf{
MigrationsDir: config.Conf.MigrationsPath,
Env: "production",
Driver: goose.DBDriver{
Name: "sqlite3",
OpenStr: config.Conf.DBPath,
Import: "github.com/mattn/go-sqlite3",
Dialect: &goose.Sqlite3Dialect{},
},
}
// Get the latest possible migration
latest, err := goose.GetMostRecentDBVersion(migrateConf.MigrationsDir)
if err != nil {
Logger.Println(err)
return err
}
// Open our database connection
db, err = gorm.Open("sqlite3", config.Conf.DBPath) db, err = gorm.Open("sqlite3", config.Conf.DBPath)
db.LogMode(false) db.LogMode(false)
db.SetLogger(Logger) db.SetLogger(Logger)
@ -69,20 +89,14 @@ func Setup() error {
Logger.Println(err) Logger.Println(err)
return err return err
} }
//If the file already exists, delete it and recreate it // Migrate up to the latest version
err = goose.RunMigrationsOnDb(migrateConf, migrateConf.MigrationsDir, latest, db.DB())
if err != nil {
Logger.Println(err)
return err
}
//If the database didn't exist, we need to create the admin user
if create_db { if create_db {
Logger.Printf("Database not found... creating db at %s\n", config.Conf.DBPath)
db.CreateTable(User{})
db.CreateTable(Target{})
db.CreateTable(Result{})
db.CreateTable(Group{})
db.CreateTable(GroupTarget{})
db.CreateTable(Template{})
db.CreateTable(Attachment{})
db.CreateTable(Page{})
db.CreateTable(SMTP{})
db.CreateTable(Event{})
db.CreateTable(Campaign{})
//Create the default user //Create the default user
initUser := User{ initUser := User{
Username: "admin", Username: "admin",
@ -92,6 +106,7 @@ func Setup() error {
err = db.Save(&initUser).Error err = db.Save(&initUser).Error
if err != nil { if err != nil {
Logger.Println(err) Logger.Println(err)
return err
} }
} }
return nil return nil

View File

@ -16,6 +16,7 @@ var _ = check.Suite(&ModelsSuite{})
func (s *ModelsSuite) SetUpSuite(c *check.C) { func (s *ModelsSuite) SetUpSuite(c *check.C) {
config.Conf.DBPath = ":memory:" config.Conf.DBPath = ":memory:"
config.Conf.MigrationsPath = "../db/migrations/"
err := Setup() err := Setup()
if err != nil { if err != nil {
c.Fatalf("Failed creating database: %v", err) c.Fatalf("Failed creating database: %v", err)

30
static/css/main.css vendored
View File

@ -341,3 +341,33 @@
float:none !important; float:none !important;
} }
} }
/* Table Styling */
.modal-content .dataTable tbody td {
font-size: 16px;/* Smaller font on modal tables */
}
.dataTables_info{
font-size: 15px;
}
/* Sort Icons */
table.dataTable thead .sorting:after, table.dataTable thead .sorting_asc:after, table.dataTable thead .sorting_desc:after {
font-family: 'FontAwesome' !important;
position: relative !important;
display: initial !important;
top: initial!important;
right: initial!important;
left: 6px;
color: #1abc9c;
}
table.dataTable thead .sorting:after{
content: "\f0dc" !important;
color: initial;
}
table.dataTable thead .sorting_asc:after {
content: "\f0de" !important;
opacity: .8 !important;
}
table.dataTable thead .sorting_desc:after {
content: "\f0dd" !important;
opacity: .8 !important;
}

View File

@ -47,7 +47,7 @@ function deleteCampaign(){
if (confirm("Are you sure you want to delete: " + campaign.name + "?")) { if (confirm("Are you sure you want to delete: " + campaign.name + "?")) {
api.campaignId.delete(campaign.id) api.campaignId.delete(campaign.id)
.success(function(msg) { .success(function(msg) {
console.log(msg) location.href = '/campaigns'
}) })
.error(function(e) { .error(function(e) {
$("#modal\\.flashes").empty().append("<div style=\"text-align:center\" class=\"alert alert-danger\">\ $("#modal\\.flashes").empty().append("<div style=\"text-align:center\" class=\"alert alert-danger\">\
@ -56,6 +56,26 @@ function deleteCampaign(){
} }
} }
// Exports campaign results as a CSV file
function exportAsCSV() {
exportHTML = $("#exportButton").html()
$("#exportButton").html('<i class="fa fa-spinner fa-spin"></i>')
var csvString = Papa.unparse(campaign.results, {})
var csvData = new Blob([csvString], {
type: 'text/csv;charset=utf-8;'
});
if (navigator.msSaveBlob) {
navigator.msSaveBlob(csvData, 'results.csv');
} else {
var csvURL = window.URL.createObjectURL(csvData);
var dlLink = document.createElement('a');
dlLink.href = csvURL;
dlLink.setAttribute('download', 'results.csv');
dlLink.click();
}
$("#exportButton").html(exportHTML)
}
$(document).ready(function() { $(document).ready(function() {
campaign.id = window.location.pathname.split('/').slice(-1)[0] campaign.id = window.location.pathname.split('/').slice(-1)[0]
api.campaignId.get(campaign.id) api.campaignId.get(campaign.id)
@ -67,11 +87,15 @@ $(document).ready(function(){
// Setup tooltips // Setup tooltips
$('[data-toggle="tooltip"]').tooltip() $('[data-toggle="tooltip"]').tooltip()
// Setup our graphs // Setup our graphs
var timeline_data = {series:[{ var timeline_data = {
series: [{
name: "Events", name: "Events",
data: [] data: []
}]} }]
var email_data = {series:[]} }
var email_data = {
series: []
}
var email_legend = {} var email_legend = {}
var email_series_data = {} var email_series_data = {}
var timeline_opts = { var timeline_opts = {
@ -117,10 +141,17 @@ $(document).ready(function(){
}) })
// Setup the graphs // Setup the graphs
$.each(campaign.timeline, function(i, event) { $.each(campaign.timeline, function(i, event) {
timeline_data.series[0].data.push({meta : i, x: new Date(event.time), y:1}) timeline_data.series[0].data.push({
meta: i,
x: new Date(event.time),
y: 1
})
}) })
$.each(email_series_data, function(status, count) { $.each(email_series_data, function(status, count) {
email_data.series.push({meta: status, value: count}) email_data.series.push({
meta: status,
value: count
})
}) })
var timeline_chart = new Chartist.Line('#timeline_chart', timeline_data, timeline_opts) var timeline_chart = new Chartist.Line('#timeline_chart', timeline_data, timeline_opts)
// Setup the overview chart listeners // Setup the overview chart listeners
@ -201,7 +232,9 @@ $(document).ready(function(){
bubbles = [] bubbles = []
$.each(campaign.results, function(i, result) { $.each(campaign.results, function(i, result) {
// Check that it wasn't an internal IP // Check that it wasn't an internal IP
if (result.latitude == 0 && result.longitude == 0) { return true; } if (result.latitude == 0 && result.longitude == 0) {
return true;
}
newIP = true newIP = true
$.each(bubbles, function(i, bubble) { $.each(bubbles, function(i, bubble) {
if (bubble.ip == result.ip) { if (bubble.ip == result.ip) {

View File

@ -8,13 +8,19 @@ var labels = {
"Error": "label-danger" "Error": "label-danger"
} }
var campaigns = []
// Save attempts to POST to /campaigns/ // Save attempts to POST to /campaigns/
function save(){ function launch() {
if (!confirm("This will launch the campaign. Are you sure?")) {
return false;
}
groups = [] groups = []
$.each($("#groupTable").DataTable().rows().data(), function(i, group) { $.each($("#groupTable").DataTable().rows().data(), function(i, group) {
groups.push({name: group[0]}) groups.push({
name: group[0]
})
}) })
console.log(groups)
var campaign = { var campaign = {
name: $("#name").val(), name: $("#name").val(),
template: { template: {
@ -36,7 +42,7 @@ function save(){
api.campaigns.post(campaign) api.campaigns.post(campaign)
.success(function(data) { .success(function(data) {
successFlash("Campaign successfully launched!") successFlash("Campaign successfully launched!")
window.location = "/campaigns/" + campaign.id.toString() window.location = "/campaigns/" + data.id.toString()
}) })
.error(function(data) { .error(function(data) {
$("#modal\\.flashes").empty().append("<div style=\"text-align:center\" class=\"alert alert-danger\">\ $("#modal\\.flashes").empty().append("<div style=\"text-align:center\" class=\"alert alert-danger\">\
@ -50,6 +56,16 @@ function dismiss(){
$("#groupTable").dataTable().DataTable().clear().draw() $("#groupTable").dataTable().DataTable().clear().draw()
} }
function deleteCampaign(idx) {
if (confirm("Delete " + campaigns[idx].name + "?")) {
api.campaignId.delete(campaigns[idx].id)
.success(function(data) {
successFlash(data.message)
location.reload()
})
}
}
function edit(campaign) { function edit(campaign) {
// Clear the bloodhound instance // Clear the bloodhound instance
group_bh.clear(); group_bh.clear();
@ -61,8 +77,7 @@ function edit(campaign){
if (groups.length == 0) { if (groups.length == 0) {
modalError("No groups found!") modalError("No groups found!")
return false; return false;
} } else {
else {
group_bh.add(groups) group_bh.add(groups)
} }
}) })
@ -71,8 +86,7 @@ function edit(campaign){
if (templates.length == 0) { if (templates.length == 0) {
modalError("No templates found!") modalError("No templates found!")
return false return false
} } else {
else {
template_bh.add(templates) template_bh.add(templates)
} }
}) })
@ -81,8 +95,7 @@ function edit(campaign){
if (pages.length == 0) { if (pages.length == 0) {
modalError("No pages found!") modalError("No pages found!")
return false return false
} } else {
else {
page_bh.add(pages) page_bh.add(pages)
} }
}) })
@ -91,7 +104,14 @@ function edit(campaign){
$(document).ready(function() { $(document).ready(function() {
api.campaigns.get() api.campaigns.get()
.success(function(campaigns){ .success(function(cs) {
campaigns = cs
campaignTable = $("#campaignTable").DataTable({
columnDefs: [{
orderable: false,
targets: "no-sort"
}]
});
$("#loading").hide() $("#loading").hide()
if (campaigns.length > 0) { if (campaigns.length > 0) {
$("#campaignTable").show() $("#campaignTable").show()
@ -102,13 +122,14 @@ $(document).ready(function(){
campaign.name, campaign.name,
moment(campaign.created_date).format('MMMM Do YYYY, h:mm:ss a'), moment(campaign.created_date).format('MMMM Do YYYY, h:mm:ss a'),
"<span class=\"label " + label + "\">" + campaign.status + "</span>", "<span class=\"label " + label + "\">" + campaign.status + "</span>",
"<div class='pull-right'><a class='btn btn-primary' href='/campaigns/" + campaign.id + "'>\ "<div class='pull-right'><a class='btn btn-primary' href='/campaigns/" + campaign.id + "' data-toggle='tooltip' data-placement='left' title='View Results'>\
<i class='fa fa-bar-chart'></i>\ <i class='fa fa-bar-chart'></i>\
</a>\ </a>\
<button class='btn btn-danger' onclick='alert(\"test\")'>\ <button class='btn btn-danger' onclick='deleteCampaign(" + i + ")' data-toggle='tooltip' data-placement='left' title='Delete Campaign'>\
<i class='fa fa-trash-o'></i>\ <i class='fa fa-trash-o'></i>\
</button></div>" </button></div>"
]).draw() ]).draw()
$('[data-toggle="tooltip"]').tooltip()
}) })
} else { } else {
$("#emptyMessage").show() $("#emptyMessage").show()
@ -131,9 +152,16 @@ $(document).ready(function(){
return false; return false;
}) })
// Create the group typeahead objects // Create the group typeahead objects
groupTable = $("#groupTable").DataTable() groupTable = $("#groupTable").DataTable({
columnDefs: [{
orderable: false,
targets: "no-sort"
}]
})
group_bh = new Bloodhound({ group_bh = new Bloodhound({
datumTokenizer: function(g) { return Bloodhound.tokenizers.whitespace(g.name) }, datumTokenizer: function(g) {
return Bloodhound.tokenizers.whitespace(g.name)
},
queryTokenizer: Bloodhound.tokenizers.whitespace, queryTokenizer: Bloodhound.tokenizers.whitespace,
local: [] local: []
}) })
@ -142,13 +170,16 @@ $(document).ready(function(){
hint: true, hint: true,
highlight: true, highlight: true,
minLength: 1 minLength: 1
}, }, {
{
name: "groups", name: "groups",
source: group_bh, source: group_bh,
templates: { templates: {
empty: function(data) {return '<div class="tt-suggestion">No groups matched that query</div>' }, empty: function(data) {
suggestion: function(data){ return '<div>' + data.name + '</div>' } return '<div class="tt-suggestion">No groups matched that query</div>'
},
suggestion: function(data) {
return '<div>' + data.name + '</div>'
}
} }
}) })
.bind('typeahead:select', function(ev, group) { .bind('typeahead:select', function(ev, group) {
@ -159,7 +190,9 @@ $(document).ready(function(){
}); });
// Create the template typeahead objects // Create the template typeahead objects
template_bh = new Bloodhound({ template_bh = new Bloodhound({
datumTokenizer: function(t) { return Bloodhound.tokenizers.whitespace(t.name) }, datumTokenizer: function(t) {
return Bloodhound.tokenizers.whitespace(t.name)
},
queryTokenizer: Bloodhound.tokenizers.whitespace, queryTokenizer: Bloodhound.tokenizers.whitespace,
local: [] local: []
}) })
@ -168,13 +201,16 @@ $(document).ready(function(){
hint: true, hint: true,
highlight: true, highlight: true,
minLength: 1 minLength: 1
}, }, {
{
name: "templates", name: "templates",
source: template_bh, source: template_bh,
templates: { templates: {
empty: function(data) {return '<div class="tt-suggestion">No templates matched that query</div>' }, empty: function(data) {
suggestion: function(data){ return '<div>' + data.name + '</div>' } return '<div class="tt-suggestion">No templates matched that query</div>'
},
suggestion: function(data) {
return '<div>' + data.name + '</div>'
}
} }
}) })
.bind('typeahead:select', function(ev, template) { .bind('typeahead:select', function(ev, template) {
@ -185,7 +221,9 @@ $(document).ready(function(){
}); });
// Create the landing page typeahead objects // Create the landing page typeahead objects
page_bh = new Bloodhound({ page_bh = new Bloodhound({
datumTokenizer: function(p) { return Bloodhound.tokenizers.whitespace(p.name) }, datumTokenizer: function(p) {
return Bloodhound.tokenizers.whitespace(p.name)
},
queryTokenizer: Bloodhound.tokenizers.whitespace, queryTokenizer: Bloodhound.tokenizers.whitespace,
local: [] local: []
}) })
@ -194,13 +232,16 @@ $(document).ready(function(){
hint: true, hint: true,
highlight: true, highlight: true,
minLength: 1 minLength: 1
}, }, {
{
name: "pages", name: "pages",
source: page_bh, source: page_bh,
templates: { templates: {
empty: function(data) {return '<div class="tt-suggestion">No pages matched that query</div>' }, empty: function(data) {
suggestion: function(data){ return '<div>' + data.name + '</div>' } return '<div class="tt-suggestion">No pages matched that query</div>'
},
suggestion: function(data) {
return '<div>' + data.name + '</div>'
}
} }
}) })
.bind('typeahead:select', function(ev, page) { .bind('typeahead:select', function(ev, page) {

View File

@ -9,6 +9,16 @@ var labels = {
"Error": "label-danger" "Error": "label-danger"
} }
function deleteCampaign(idx) {
if (confirm("Delete " + campaigns[idx].name + "?")) {
api.campaignId.delete(campaigns[idx].id)
.success(function(data) {
successFlash(data.message)
location.reload()
})
}
}
$(document).ready(function() { $(document).ready(function() {
api.campaigns.get() api.campaigns.get()
.success(function(cs) { .success(function(cs) {
@ -17,8 +27,15 @@ $(document).ready(function(){
if (campaigns.length > 0) { if (campaigns.length > 0) {
$("#dashboard").show() $("#dashboard").show()
// Create the overview chart data // Create the overview chart data
var overview_data = {labels:[],series:[[]]} var overview_data = {
var average_data = {series:[]} labels: [],
series: [
[]
]
}
var average_data = {
series: []
}
var overview_opts = { var overview_opts = {
axisX: { axisX: {
showGrid: false showGrid: false
@ -33,7 +50,11 @@ $(document).ready(function(){
showLabel: false showLabel: false
} }
var average = 0 var average = 0
campaignTable = $("#campaignTable").DataTable(); campaignTable = $("#campaignTable").DataTable({
columnDefs: [
{ orderable: false, targets: "no-sort" }
]
});
$.each(campaigns, function(i, campaign) { $.each(campaigns, function(i, campaign) {
var campaign_date = moment(campaign.created_date).format('MMMM Do YYYY h:mm:ss a') var campaign_date = moment(campaign.created_date).format('MMMM Do YYYY h:mm:ss a')
var label = labels[campaign.status] || "label-default"; var label = labels[campaign.status] || "label-default";
@ -42,10 +63,10 @@ $(document).ready(function(){
campaign.name, campaign.name,
campaign_date, campaign_date,
"<span class=\"label " + label + "\">" + campaign.status + "</span>", "<span class=\"label " + label + "\">" + campaign.status + "</span>",
"<div class='pull-right'><a class='btn btn-primary' href='/campaigns/" + campaign.id + "'>\ "<div class='pull-right'><a class='btn btn-primary' href='/campaigns/" + campaign.id + "' data-toggle='tooltip' data-placement='right' title='View Results'>\
<i class='fa fa-bar-chart'></i>\ <i class='fa fa-bar-chart'></i>\
</a>\ </a>\
<button class='btn btn-danger' onclick='deleteCampaign(" + i + ")'>\ <button class='btn btn-danger' onclick='deleteCampaign(" + i + ")' data-toggle='tooltip' data-placement='right' title='Delete Campaign'>\
<i class='fa fa-trash-o'></i>\ <i class='fa fa-trash-o'></i>\
</button></div>" </button></div>"
]).draw() ]).draw()
@ -60,11 +81,20 @@ $(document).ready(function(){
average += campaign.y average += campaign.y
// Add the data to the overview chart // Add the data to the overview chart
overview_data.labels.push(campaign_date) overview_data.labels.push(campaign_date)
overview_data.series[0].push({meta : i, value: campaign.y}) overview_data.series[0].push({
meta: i,
value: campaign.y
})
}) })
average = Math.floor(average / campaigns.length); average = Math.floor(average / campaigns.length);
average_data.series.push({meta: "Unsuccessful Phishes", value: 100 - average}) average_data.series.push({
average_data.series.push({meta: "Successful Phishes", value: average}) meta: "Unsuccessful Phishes",
value: 100 - average
})
average_data.series.push({
meta: "Successful Phishes",
value: average
})
// Build the charts // Build the charts
var average_chart = new Chartist.Pie("#average_chart", average_data, average_opts) var average_chart = new Chartist.Pie("#average_chart", average_data, average_opts)
var overview_chart = new Chartist.Line('#overview_chart', overview_data, overview_opts) var overview_chart = new Chartist.Line('#overview_chart', overview_data, overview_opts)

View File

@ -4,6 +4,7 @@
Author: Jordan Wright <github.com/jordan-wright> Author: Jordan Wright <github.com/jordan-wright>
*/ */
var pages = [] var pages = []
// Save attempts to POST to /templates/ // Save attempts to POST to /templates/
function save(idx) { function save(idx) {
var page = {} var page = {}
@ -69,7 +70,9 @@ function importSite(){
} }
function edit(idx) { function edit(idx) {
$("#modalSubmit").unbind('click').click(function(){save(idx)}) $("#modalSubmit").unbind('click').click(function() {
save(idx)
})
$("#html_editor").ckeditor() $("#html_editor").ckeditor()
var page = {} var page = {}
if (idx != -1) { if (idx != -1) {
@ -92,7 +95,13 @@ function load(){
$("#loading").hide() $("#loading").hide()
if (pages.length > 0) { if (pages.length > 0) {
$("#pagesTable").show() $("#pagesTable").show()
pagesTable = $("#pagesTable").DataTable(); pagesTable = $("#pagesTable").DataTable({
destroy: true,
columnDefs: [{
orderable: false,
targets: "no-sort"
}]
});
pagesTable.clear() pagesTable.clear()
$.each(pages, function(i, page) { $.each(pages, function(i, page) {
pagesTable.row.add([ pagesTable.row.add([
@ -125,13 +134,11 @@ $(document).ready(function(){
}); });
$('.modal').on('shown.bs.modal', function(event) { $('.modal').on('shown.bs.modal', function(event) {
// Keep track of the number of open modals // Keep track of the number of open modals
if ( typeof( $('body').data( 'fv_open_modals' ) ) == 'undefined' ) if (typeof($('body').data('fv_open_modals')) == 'undefined') {
{
$('body').data('fv_open_modals', 0); $('body').data('fv_open_modals', 0);
} }
// if the z-index of this modal has been set, ignore. // if the z-index of this modal has been set, ignore.
if ( $(this).hasClass( 'fv-modal-stack' ) ) if ($(this).hasClass('fv-modal-stack')) {
{
return; return;
} }
$(this).addClass('fv-modal-stack'); $(this).addClass('fv-modal-stack');

View File

@ -15,7 +15,9 @@ var icons = {
// Save attempts to POST to /templates/ // Save attempts to POST to /templates/
function save(idx) { function save(idx) {
var template = {attachments:[]} var template = {
attachments: []
}
template.name = $("#name").val() template.name = $("#name").val()
template.subject = $("#subject").val() template.subject = $("#subject").val()
template.html = CKEDITOR.instances["html_editor"].getData(); template.html = CKEDITOR.instances["html_editor"].getData();
@ -78,7 +80,19 @@ function deleteTemplate(idx){
} }
function attach(files) { function attach(files) {
attachmentsTable = $("#attachmentsTable").DataTable(); attachmentsTable = $("#attachmentsTable").DataTable({
destroy: true,
"order": [
[1, "asc"]
],
columnDefs: [{
orderable: false,
targets: "no-sort"
}, {
sClass: "datatable_hidden",
targets: [3, 4]
}]
});
$.each(files, function(i, file) { $.each(files, function(i, file) {
var reader = new FileReader(); var reader = new FileReader();
/* Make this a datatable */ /* Make this a datatable */
@ -101,23 +115,30 @@ function attach(files){
} }
function edit(idx) { function edit(idx) {
$("#modalSubmit").unbind('click').click(function(){save(idx)}) $("#modalSubmit").unbind('click').click(function() {
$("#attachmentUpload").unbind('click').click(function(){this.value=null}) save(idx)
})
$("#attachmentUpload").unbind('click').click(function() {
this.value = null
})
$("#html_editor").ckeditor() $("#html_editor").ckeditor()
$("#attachmentsTable").show() $("#attachmentsTable").show()
attachmentsTable = null attachmentsTable = $('#attachmentsTable').DataTable({
if ( $.fn.dataTable.isDataTable('#attachmentsTable') ) { destroy: true,
attachmentsTable = $('#attachmentsTable').DataTable(); "order": [
} [1, "asc"]
else { ],
attachmentsTable = $("#attachmentsTable").DataTable({ columnDefs: [{
"aoColumnDefs" : [{ orderable: false,
"targets" : [3,4], targets: "no-sort"
"sClass" : "datatable_hidden" }, {
sClass: "datatable_hidden",
targets: [3, 4]
}] }]
}); });
var template = {
attachments: []
} }
var template = {attachments:[]}
if (idx != -1) { if (idx != -1) {
template = templates[idx] template = templates[idx]
$("#name").val(template.name) $("#name").val(template.name)
@ -178,7 +199,13 @@ function load(){
$("#loading").hide() $("#loading").hide()
if (templates.length > 0) { if (templates.length > 0) {
$("#templateTable").show() $("#templateTable").show()
templateTable = $("#templateTable").DataTable(); templateTable = $("#templateTable").DataTable({
destroy: true,
columnDefs: [{
orderable: false,
targets: "no-sort"
}]
});
templateTable.clear() templateTable.clear()
$.each(templates, function(i, template) { $.each(templates, function(i, template) {
templateTable.row.add([ templateTable.row.add([
@ -211,13 +238,11 @@ $(document).ready(function(){
}); });
$('.modal').on('shown.bs.modal', function(event) { $('.modal').on('shown.bs.modal', function(event) {
// Keep track of the number of open modals // Keep track of the number of open modals
if ( typeof( $('body').data( 'fv_open_modals' ) ) == 'undefined' ) if (typeof($('body').data('fv_open_modals')) == 'undefined') {
{
$('body').data('fv_open_modals', 0); $('body').data('fv_open_modals', 0);
} }
// if the z-index of this modal has been set, ignore. // if the z-index of this modal has been set, ignore.
if ( $(this).hasClass( 'fv-modal-stack' ) ) if ($(this).hasClass('fv-modal-stack')) {
{
return; return;
} }
$(this).addClass('fv-modal-stack'); $(this).addClass('fv-modal-stack');

View File

@ -25,6 +25,7 @@ function save(idx){
successFlash("Group updated successfully!") successFlash("Group updated successfully!")
load() load()
dismiss() dismiss()
$("#modal").modal('hide')
}) })
.error(function(data) { .error(function(data) {
modalError(data.responseJSON.message) modalError(data.responseJSON.message)
@ -37,6 +38,7 @@ function save(idx){
successFlash("Group added successfully!") successFlash("Group added successfully!")
load() load()
dismiss() dismiss()
$("#modal").modal('hide')
}) })
.error(function(data) { .error(function(data) {
modalError(data.responseJSON.message) modalError(data.responseJSON.message)
@ -48,12 +50,19 @@ function dismiss(){
$("#targetsTable").dataTable().DataTable().clear().draw() $("#targetsTable").dataTable().DataTable().clear().draw()
$("#name").val("") $("#name").val("")
$("#modal\\.flashes").empty() $("#modal\\.flashes").empty()
$("#modal").modal('hide')
} }
function edit(idx) { function edit(idx) {
targets = $("#targetsTable").dataTable() targets = $("#targetsTable").dataTable({
$("#modalSubmit").unbind('click').click(function(){save(idx)}) destroy: true, // Destroy any other instantiated table - http://datatables.net/manual/tech-notes/3#destroy
columnDefs: [{
orderable: false,
targets: "no-sort"
}]
})
$("#modalSubmit").unbind('click').click(function() {
save(idx)
})
if (idx == -1) { if (idx == -1) {
group = {} group = {}
} else { } else {
@ -119,7 +128,13 @@ function load(){
groups = gs groups = gs
$("#emptyMessage").hide() $("#emptyMessage").hide()
$("#groupTable").show() $("#groupTable").show()
groupTable = $("#groupTable").DataTable(); groupTable = $("#groupTable").DataTable({
destroy: true,
columnDefs: [{
orderable: false,
targets: "no-sort"
}]
});
groupTable.clear(); groupTable.clear();
$.each(groups, function(i, group) { $.each(groups, function(i, group) {
var targets = "" var targets = ""
@ -176,4 +191,7 @@ $(document).ready(function(){
.remove() .remove()
.draw(); .draw();
}) })
$("#modal").on("hide.bs.modal", function() {
dismiss()
})
}) })

View File

@ -1,15 +1,13 @@
function errorFlash(message) { function errorFlash(message) {
$("#flashes").empty() $("#flashes").empty()
$("#flashes").append("<div style=\"text-align:center\" class=\"alert alert-danger\">\ $("#flashes").append("<div style=\"text-align:center\" class=\"alert alert-danger\">\
<i class=\"fa fa-exclamation-circle\"></i> " + message + "</div>" <i class=\"fa fa-exclamation-circle\"></i> " + message + "</div>")
)
} }
function successFlash(message) { function successFlash(message) {
$("#flashes").empty() $("#flashes").empty()
$("#flashes").append("<div style=\"text-align:center\" class=\"alert alert-success\">\ $("#flashes").append("<div style=\"text-align:center\" class=\"alert alert-success\">\
<i class=\"fa fa-check-circle\"></i> " + message + "</div>" <i class=\"fa fa-check-circle\"></i> " + message + "</div>")
)
} }
function modalError(message) { function modalError(message) {
@ -51,7 +49,7 @@ var api = {
}, },
// delete() - Deletes a campaign at DELETE /campaigns/:id // delete() - Deletes a campaign at DELETE /campaigns/:id
delete: function(id) { delete: function(id) {
return query("/campaigns/" + id, "DELETE", data) return query("/campaigns/" + id, "DELETE", {})
} }
}, },
// groups contains the endpoints for /groups // groups contains the endpoints for /groups

6
static/js/papaparse.min.js vendored Normal file

File diff suppressed because one or more lines are too long

View File

@ -30,22 +30,11 @@
<h1 class="page-header" id="page-title">Results for campaign.name</h1> <h1 class="page-header" id="page-title">Results for campaign.name</h1>
</div> </div>
<div class="row"> <div class="row">
<!-- <button type="button" id="exportButton" class="btn btn-primary" onclick="exportAsCSV()">
<div class="btn-group"> <i class="fa fa-file-excel-o"></i> Export CSV
<button type="button" class="btn btn-primary dropdown-toggle" data-toggle="dropdown">
<i class="fa fa-cogs fa-lg"></i>
<span class="caret"></span>
</button> </button>
<ul class="dropdown-menu"> <button type="button" class="btn btn-danger" data-toggle="tooltip" onclick="deleteCampaign()">
<li><a href="#">Export</a> <i class="fa fa-trash-o fa-lg"></i> Delete
</li>
<li><a href="#">Relaunch</a>
</li>
</ul>
</div>
-->
<button type="button" class="btn btn-danger" data-toggle="tooltip" data-placement="right" title="Delete Campaign" onclick="deleteCampaign()">
<i class="fa fa-times fa-lg"></i>
</button> </button>
</div> </div>
<br /> <br />
@ -114,5 +103,6 @@
<script src="/js/d3.min.js"></script> <script src="/js/d3.min.js"></script>
<script src="/js/topojson.min.js"></script> <script src="/js/topojson.min.js"></script>
<script src="/js/datamaps.min.js"></script> <script src="/js/datamaps.min.js"></script>
<script src="/js/papaparse.min.js"></script>
<script src="/js/app/campaign_results.js"></script> <script src="/js/app/campaign_results.js"></script>
{{end}} {{end}}

View File

@ -47,7 +47,7 @@
<th>Name</th> <th>Name</th>
<th>Created Date</th> <th>Created Date</th>
<th>Status</th> <th>Status</th>
<th class="col-md-2"></th> <th class="col-md-2 no-sort"></th>
</tr> </tr>
</thead> </thead>
<tbody> <tbody>
@ -113,7 +113,7 @@
<table id="groupTable" class="table table-hover table-striped table-condensed"> <table id="groupTable" class="table table-hover table-striped table-condensed">
<thead> <thead>
<th>Group Name</th> <th>Group Name</th>
<th></th> <th class="no-sort"></th>
<tbody> <tbody>
</tbody> </tbody>
</table> </table>
@ -121,7 +121,7 @@
</div> </div>
<div class="modal-footer"> <div class="modal-footer">
<button type="button" class="btn btn-default" data-dismiss="modal">Close</button> <button type="button" class="btn btn-default" data-dismiss="modal">Close</button>
<button type="button" class="btn btn-primary" onclick="save()">Save changes</button> <button type="button" class="btn btn-primary" onclick="launch()"><i class="fa fa-envelope"></i> Launch Campaign</button>
</div> </div>
</div> </div>
</div> </div>

View File

@ -67,7 +67,7 @@
<th>Name</th> <th>Name</th>
<th>Created Date</th> <th>Created Date</th>
<th>Status</th> <th>Status</th>
<th class="col-md-2 col-sm-2"></th> <th class="col-md-2 col-sm-2 no-sort"></th>
</tr> </tr>
</thead> </thead>
<tbody> <tbody>

View File

@ -44,7 +44,7 @@
<tr> <tr>
<th>Name</th> <th>Name</th>
<th>Last Modified Date</th> <th>Last Modified Date</th>
<th class="col-md-2"></th> <th class="col-md-2 no-sort"></th>
</tr> </tr>
</thead> </thead>
<tbody> <tbody>

View File

@ -46,7 +46,7 @@
<tr> <tr>
<th>Name</th> <th>Name</th>
<th>Modified Date</th> <th>Modified Date</th>
<th class="col-md-2"></th> <th class="col-md-2 no-sort"></th>
</tr> </tr>
</thead> </thead>
<tbody> <tbody>
@ -102,11 +102,11 @@
<table id="attachmentsTable" class="table"> <table id="attachmentsTable" class="table">
<thead> <thead>
<tr> <tr>
<th class="col-md-1"></th> <th class="col-md-1 no-sort"></th>
<th class="col-md-10">Name</th> <th class="col-md-10">Name</th>
<th class="col-md-1"></th> <th class="col-md-1 no-sort"></th>
<th class="datatable_hidden">Content</th> <th class="datatable_hidden no-sort">Content</th>
<th class="datatable_hidden">Type</th> <th class="datatable_hidden no-sort">Type</th>
</tr> </tr>
</thead> </thead>
<tbody> <tbody>

View File

@ -47,7 +47,7 @@
<th>Name</th> <th>Name</th>
<th>Members</th> <th>Members</th>
<th>Modified Date</th> <th>Modified Date</th>
<th class="col-md-2"></th> <th class="col-md-2 no-sort"></th>
</tr> </tr>
</thead> </thead>
<tbody> <tbody>
@ -94,20 +94,20 @@
</form> </form>
</div> </div>
<br /> <br />
<table id="targetsTable" class="table table-hover table-striped"> <table id="targetsTable" class="table table-hover table-striped table-condensed">
<thead> <thead>
<tr> <tr>
<th>First Name</th> <th>First Name</th>
<th>Last Name</th> <th>Last Name</th>
<th>Email</th> <th>Email</th>
<th>Position</th> <th>Position</th>
<th></th> <th class="no-sort"></th>
<tbody> <tbody>
</tbody> </tbody>
</table> </table>
</div> </div>
<div class="modal-footer"> <div class="modal-footer">
<button type="button" class="btn btn-default" onclick="dismiss()">Close</button> <button type="button" class="btn btn-default" data-dismiss="modal">Close</button>
<button type="button" class="btn btn-primary" id="modalSubmit">Save changes</button> <button type="button" class="btn btn-primary" id="modalSubmit">Save changes</button>
</div> </div>
</div> </div>

28
util/doc.go Normal file
View File

@ -0,0 +1,28 @@
/*
gophish - Open-Source Phishing Framework
The MIT License (MIT)
Copyright (c) 2013 Jordan Wright
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.
*/
// Package util provides misc. utility functions for gophish
package util

28
worker/doc.go Normal file
View File

@ -0,0 +1,28 @@
/*
gophish - Open-Source Phishing Framework
The MIT License (MIT)
Copyright (c) 2013 Jordan Wright
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.
*/
// Package worker contains the functionality for the background worker process.
package worker

View File

@ -9,8 +9,8 @@ import (
"strings" "strings"
"text/template" "text/template"
"github.com/jordan-wright/email"
"github.com/gophish/gophish/models" "github.com/gophish/gophish/models"
"github.com/jordan-wright/email"
) )
// Logger is the logger for the worker // Logger is the logger for the worker
@ -119,6 +119,10 @@ func processCampaign(c *models.Campaign) {
if err != nil { if err != nil {
Logger.Println(err) Logger.Println(err)
} }
err = c.AddEvent(models.Event{Email: t.Email, Message: models.EVENT_SENT})
if err != nil {
Logger.Println(err)
}
} }
} }
err = c.UpdateStatus(models.CAMPAIGN_EMAILS_SENT) err = c.UpdateStatus(models.CAMPAIGN_EMAILS_SENT)