mirror of https://github.com/gophish/gophish
Merge pull request #1 from gophish/master
Catching up to the main branch from gophish/masterpull/91/head
commit
cf97520ddf
|
@ -3,11 +3,11 @@
|
|||
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
|
||||
|
||||
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
|
||||
**Update 01/12/2016**
|
||||
|
|
17
config.json
17
config.json
|
@ -1,10 +1,21 @@
|
|||
{
|
||||
"admin_url" : "127.0.0.1:3333",
|
||||
"phish_url" : "0.0.0.0:80",
|
||||
"admin_server" : {
|
||||
"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" : {
|
||||
"host" : "smtp.example.com:25",
|
||||
"user" : "username",
|
||||
"pass" : "password"
|
||||
},
|
||||
"dbpath" : "gophish.db"
|
||||
"db_path" : "gophish.db",
|
||||
"migrations_path" : "db/migrations/"
|
||||
}
|
|
@ -13,12 +13,29 @@ type SMTPServer struct {
|
|||
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.
|
||||
type Config struct {
|
||||
AdminURL string `json:"admin_url"`
|
||||
PhishURL string `json:"phish_url"`
|
||||
SMTP SMTPServer `json:"smtp"`
|
||||
DBPath string `json:"dbpath"`
|
||||
AdminConf AdminServer `json:"admin_server"`
|
||||
PhishConf PhishServer `json:"phish_server"`
|
||||
SMTPConf SMTPServer `json:"smtp"`
|
||||
DBPath string `json:"db_path"`
|
||||
MigrationsPath string `json:"migrations_path"`
|
||||
}
|
||||
|
||||
var Conf Config
|
||||
|
|
|
@ -10,14 +10,14 @@ import (
|
|||
"time"
|
||||
|
||||
"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/models"
|
||||
"github.com/gophish/gophish/util"
|
||||
"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.
|
||||
|
@ -92,6 +92,7 @@ func API_Campaigns_Id(w http.ResponseWriter, r *http.Request) {
|
|||
id, _ := strconv.ParseInt(vars["id"], 0, 64)
|
||||
c, err := models.GetCampaign(id, ctx.Get(r, "user_id").(int64))
|
||||
if err != nil {
|
||||
Logger.Println(err)
|
||||
JSONResponse(w, models.Response{Success: false, Message: "Campaign not found"}, http.StatusNotFound)
|
||||
return
|
||||
}
|
||||
|
|
|
@ -9,9 +9,9 @@ import (
|
|||
"os"
|
||||
"testing"
|
||||
|
||||
"github.com/gorilla/handlers"
|
||||
"github.com/gophish/gophish/config"
|
||||
"github.com/gophish/gophish/models"
|
||||
"github.com/gorilla/handlers"
|
||||
"github.com/stretchr/testify/suite"
|
||||
)
|
||||
|
||||
|
@ -26,13 +26,14 @@ var as *httptest.Server = httptest.NewUnstartedServer(handlers.CombinedLoggingHa
|
|||
|
||||
func (s *ControllersSuite) SetupSuite() {
|
||||
config.Conf.DBPath = ":memory:"
|
||||
config.Conf.MigrationsPath = "../db/migrations/"
|
||||
err := models.Setup()
|
||||
if err != nil {
|
||||
s.T().Fatalf("Failed creating database: %v", err)
|
||||
}
|
||||
s.Nil(err)
|
||||
// Setup the admin server for use in testing
|
||||
as.Config.Addr = config.Conf.AdminURL
|
||||
as.Config.Addr = config.Conf.AdminConf.ListenURL
|
||||
as.Start()
|
||||
// Get the API key to use for these tests
|
||||
u, err := models.GetUser(1)
|
||||
|
|
|
@ -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";
|
33
gophish.go
33
gophish.go
|
@ -30,11 +30,12 @@ import (
|
|||
"log"
|
||||
"net/http"
|
||||
"os"
|
||||
"sync"
|
||||
|
||||
"github.com/gorilla/handlers"
|
||||
"github.com/gophish/gophish/config"
|
||||
"github.com/gophish/gophish/controllers"
|
||||
"github.com/gophish/gophish/models"
|
||||
"github.com/gorilla/handlers"
|
||||
)
|
||||
|
||||
var Logger = log.New(os.Stdout, " ", log.Ldate|log.Ltime|log.Lshortfile)
|
||||
|
@ -45,9 +46,31 @@ func main() {
|
|||
if err != nil {
|
||||
fmt.Println(err)
|
||||
}
|
||||
wg := &sync.WaitGroup{}
|
||||
wg.Add(1)
|
||||
// Start the web servers
|
||||
Logger.Printf("Admin server started at http://%s\n", config.Conf.AdminURL)
|
||||
go http.ListenAndServe(config.Conf.AdminURL, handlers.CombinedLoggingHandler(os.Stdout, controllers.CreateAdminRouter()))
|
||||
Logger.Printf("Phishing server started at http://%s\n", config.Conf.PhishURL)
|
||||
http.ListenAndServe(config.Conf.PhishURL, handlers.CombinedLoggingHandler(os.Stdout, controllers.CreatePhishingRouter()))
|
||||
go func() {
|
||||
defer wg.Done()
|
||||
if config.Conf.AdminConf.UseTLS { // use TLS for Admin web server if available
|
||||
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()
|
||||
}
|
||||
|
|
|
@ -117,21 +117,28 @@ func GetCampaign(id int64, uid int64) (Campaign, error) {
|
|||
c := Campaign{}
|
||||
err := db.Where("id = ?", id).Where("user_id = ?", uid).Find(&c).Error
|
||||
if err != nil {
|
||||
Logger.Printf("%s: campaign not found\n", err)
|
||||
return c, err
|
||||
}
|
||||
err = db.Model(&c).Related(&c.Results).Error
|
||||
if err != nil {
|
||||
Logger.Printf("%s: results not found for campaign\n", err)
|
||||
return c, err
|
||||
}
|
||||
err = db.Model(&c).Related(&c.Events).Error
|
||||
if err != nil {
|
||||
Logger.Printf("%s: events not found for campaign\n", err)
|
||||
return c, err
|
||||
}
|
||||
err = db.Table("templates").Where("id=?", c.TemplateId).Find(&c.Template).Error
|
||||
if err != nil {
|
||||
Logger.Printf("%s: template not found for campaign\n", err)
|
||||
return c, err
|
||||
}
|
||||
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
|
||||
}
|
||||
|
||||
|
@ -207,6 +214,7 @@ func PostCampaign(c *Campaign, uid int64) error {
|
|||
|
||||
//DeleteCampaign deletes the specified campaign
|
||||
func DeleteCampaign(id int64) error {
|
||||
Logger.Printf("Deleting campaign %d\n", id)
|
||||
// Delete all the campaign results
|
||||
err := db.Where("campaign_id=?", id).Delete(&Result{}).Error
|
||||
if err != nil {
|
||||
|
|
|
@ -8,6 +8,8 @@ import (
|
|||
"log"
|
||||
"os"
|
||||
|
||||
"bitbucket.org/liamstask/goose/lib/goose"
|
||||
|
||||
"github.com/gophish/gophish/config"
|
||||
"github.com/jinzhu/gorm"
|
||||
_ "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:" {
|
||||
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.LogMode(false)
|
||||
db.SetLogger(Logger)
|
||||
|
@ -69,20 +89,14 @@ func Setup() error {
|
|||
Logger.Println(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 {
|
||||
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
|
||||
initUser := User{
|
||||
Username: "admin",
|
||||
|
@ -92,6 +106,7 @@ func Setup() error {
|
|||
err = db.Save(&initUser).Error
|
||||
if err != nil {
|
||||
Logger.Println(err)
|
||||
return err
|
||||
}
|
||||
}
|
||||
return nil
|
||||
|
|
|
@ -16,6 +16,7 @@ var _ = check.Suite(&ModelsSuite{})
|
|||
|
||||
func (s *ModelsSuite) SetUpSuite(c *check.C) {
|
||||
config.Conf.DBPath = ":memory:"
|
||||
config.Conf.MigrationsPath = "../db/migrations/"
|
||||
err := Setup()
|
||||
if err != nil {
|
||||
c.Fatalf("Failed creating database: %v", err)
|
||||
|
|
|
@ -341,3 +341,33 @@
|
|||
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;
|
||||
}
|
||||
|
|
|
@ -47,7 +47,7 @@ function deleteCampaign(){
|
|||
if (confirm("Are you sure you want to delete: " + campaign.name + "?")) {
|
||||
api.campaignId.delete(campaign.id)
|
||||
.success(function(msg) {
|
||||
console.log(msg)
|
||||
location.href = '/campaigns'
|
||||
})
|
||||
.error(function(e) {
|
||||
$("#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() {
|
||||
campaign.id = window.location.pathname.split('/').slice(-1)[0]
|
||||
api.campaignId.get(campaign.id)
|
||||
|
@ -67,11 +87,15 @@ $(document).ready(function(){
|
|||
// Setup tooltips
|
||||
$('[data-toggle="tooltip"]').tooltip()
|
||||
// Setup our graphs
|
||||
var timeline_data = {series:[{
|
||||
var timeline_data = {
|
||||
series: [{
|
||||
name: "Events",
|
||||
data: []
|
||||
}]}
|
||||
var email_data = {series:[]}
|
||||
}]
|
||||
}
|
||||
var email_data = {
|
||||
series: []
|
||||
}
|
||||
var email_legend = {}
|
||||
var email_series_data = {}
|
||||
var timeline_opts = {
|
||||
|
@ -117,10 +141,17 @@ $(document).ready(function(){
|
|||
})
|
||||
// Setup the graphs
|
||||
$.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) {
|
||||
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)
|
||||
// Setup the overview chart listeners
|
||||
|
@ -201,7 +232,9 @@ $(document).ready(function(){
|
|||
bubbles = []
|
||||
$.each(campaign.results, function(i, result) {
|
||||
// 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
|
||||
$.each(bubbles, function(i, bubble) {
|
||||
if (bubble.ip == result.ip) {
|
||||
|
|
|
@ -8,13 +8,19 @@ var labels = {
|
|||
"Error": "label-danger"
|
||||
}
|
||||
|
||||
var campaigns = []
|
||||
|
||||
// Save attempts to POST to /campaigns/
|
||||
function save(){
|
||||
function launch() {
|
||||
if (!confirm("This will launch the campaign. Are you sure?")) {
|
||||
return false;
|
||||
}
|
||||
groups = []
|
||||
$.each($("#groupTable").DataTable().rows().data(), function(i, group) {
|
||||
groups.push({name: group[0]})
|
||||
groups.push({
|
||||
name: group[0]
|
||||
})
|
||||
})
|
||||
console.log(groups)
|
||||
var campaign = {
|
||||
name: $("#name").val(),
|
||||
template: {
|
||||
|
@ -36,7 +42,7 @@ function save(){
|
|||
api.campaigns.post(campaign)
|
||||
.success(function(data) {
|
||||
successFlash("Campaign successfully launched!")
|
||||
window.location = "/campaigns/" + campaign.id.toString()
|
||||
window.location = "/campaigns/" + data.id.toString()
|
||||
})
|
||||
.error(function(data) {
|
||||
$("#modal\\.flashes").empty().append("<div style=\"text-align:center\" class=\"alert alert-danger\">\
|
||||
|
@ -50,6 +56,16 @@ function dismiss(){
|
|||
$("#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) {
|
||||
// Clear the bloodhound instance
|
||||
group_bh.clear();
|
||||
|
@ -61,8 +77,7 @@ function edit(campaign){
|
|||
if (groups.length == 0) {
|
||||
modalError("No groups found!")
|
||||
return false;
|
||||
}
|
||||
else {
|
||||
} else {
|
||||
group_bh.add(groups)
|
||||
}
|
||||
})
|
||||
|
@ -71,8 +86,7 @@ function edit(campaign){
|
|||
if (templates.length == 0) {
|
||||
modalError("No templates found!")
|
||||
return false
|
||||
}
|
||||
else {
|
||||
} else {
|
||||
template_bh.add(templates)
|
||||
}
|
||||
})
|
||||
|
@ -81,8 +95,7 @@ function edit(campaign){
|
|||
if (pages.length == 0) {
|
||||
modalError("No pages found!")
|
||||
return false
|
||||
}
|
||||
else {
|
||||
} else {
|
||||
page_bh.add(pages)
|
||||
}
|
||||
})
|
||||
|
@ -91,7 +104,14 @@ function edit(campaign){
|
|||
|
||||
$(document).ready(function() {
|
||||
api.campaigns.get()
|
||||
.success(function(campaigns){
|
||||
.success(function(cs) {
|
||||
campaigns = cs
|
||||
campaignTable = $("#campaignTable").DataTable({
|
||||
columnDefs: [{
|
||||
orderable: false,
|
||||
targets: "no-sort"
|
||||
}]
|
||||
});
|
||||
$("#loading").hide()
|
||||
if (campaigns.length > 0) {
|
||||
$("#campaignTable").show()
|
||||
|
@ -102,13 +122,14 @@ $(document).ready(function(){
|
|||
campaign.name,
|
||||
moment(campaign.created_date).format('MMMM Do YYYY, h:mm:ss a'),
|
||||
"<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>\
|
||||
</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>\
|
||||
</button></div>"
|
||||
]).draw()
|
||||
$('[data-toggle="tooltip"]').tooltip()
|
||||
})
|
||||
} else {
|
||||
$("#emptyMessage").show()
|
||||
|
@ -131,9 +152,16 @@ $(document).ready(function(){
|
|||
return false;
|
||||
})
|
||||
// Create the group typeahead objects
|
||||
groupTable = $("#groupTable").DataTable()
|
||||
groupTable = $("#groupTable").DataTable({
|
||||
columnDefs: [{
|
||||
orderable: false,
|
||||
targets: "no-sort"
|
||||
}]
|
||||
})
|
||||
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,
|
||||
local: []
|
||||
})
|
||||
|
@ -142,13 +170,16 @@ $(document).ready(function(){
|
|||
hint: true,
|
||||
highlight: true,
|
||||
minLength: 1
|
||||
},
|
||||
{
|
||||
}, {
|
||||
name: "groups",
|
||||
source: group_bh,
|
||||
templates: {
|
||||
empty: function(data) {return '<div class="tt-suggestion">No groups matched that query</div>' },
|
||||
suggestion: function(data){ return '<div>' + data.name + '</div>' }
|
||||
empty: function(data) {
|
||||
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) {
|
||||
|
@ -159,7 +190,9 @@ $(document).ready(function(){
|
|||
});
|
||||
// Create the template typeahead objects
|
||||
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,
|
||||
local: []
|
||||
})
|
||||
|
@ -168,13 +201,16 @@ $(document).ready(function(){
|
|||
hint: true,
|
||||
highlight: true,
|
||||
minLength: 1
|
||||
},
|
||||
{
|
||||
}, {
|
||||
name: "templates",
|
||||
source: template_bh,
|
||||
templates: {
|
||||
empty: function(data) {return '<div class="tt-suggestion">No templates matched that query</div>' },
|
||||
suggestion: function(data){ return '<div>' + data.name + '</div>' }
|
||||
empty: function(data) {
|
||||
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) {
|
||||
|
@ -185,7 +221,9 @@ $(document).ready(function(){
|
|||
});
|
||||
// Create the landing page typeahead objects
|
||||
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,
|
||||
local: []
|
||||
})
|
||||
|
@ -194,13 +232,16 @@ $(document).ready(function(){
|
|||
hint: true,
|
||||
highlight: true,
|
||||
minLength: 1
|
||||
},
|
||||
{
|
||||
}, {
|
||||
name: "pages",
|
||||
source: page_bh,
|
||||
templates: {
|
||||
empty: function(data) {return '<div class="tt-suggestion">No pages matched that query</div>' },
|
||||
suggestion: function(data){ return '<div>' + data.name + '</div>' }
|
||||
empty: function(data) {
|
||||
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) {
|
||||
|
|
|
@ -9,6 +9,16 @@ var labels = {
|
|||
"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() {
|
||||
api.campaigns.get()
|
||||
.success(function(cs) {
|
||||
|
@ -17,8 +27,15 @@ $(document).ready(function(){
|
|||
if (campaigns.length > 0) {
|
||||
$("#dashboard").show()
|
||||
// Create the overview chart data
|
||||
var overview_data = {labels:[],series:[[]]}
|
||||
var average_data = {series:[]}
|
||||
var overview_data = {
|
||||
labels: [],
|
||||
series: [
|
||||
[]
|
||||
]
|
||||
}
|
||||
var average_data = {
|
||||
series: []
|
||||
}
|
||||
var overview_opts = {
|
||||
axisX: {
|
||||
showGrid: false
|
||||
|
@ -33,7 +50,11 @@ $(document).ready(function(){
|
|||
showLabel: false
|
||||
}
|
||||
var average = 0
|
||||
campaignTable = $("#campaignTable").DataTable();
|
||||
campaignTable = $("#campaignTable").DataTable({
|
||||
columnDefs: [
|
||||
{ orderable: false, targets: "no-sort" }
|
||||
]
|
||||
});
|
||||
$.each(campaigns, function(i, campaign) {
|
||||
var campaign_date = moment(campaign.created_date).format('MMMM Do YYYY h:mm:ss a')
|
||||
var label = labels[campaign.status] || "label-default";
|
||||
|
@ -42,10 +63,10 @@ $(document).ready(function(){
|
|||
campaign.name,
|
||||
campaign_date,
|
||||
"<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>\
|
||||
</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>\
|
||||
</button></div>"
|
||||
]).draw()
|
||||
|
@ -60,11 +81,20 @@ $(document).ready(function(){
|
|||
average += campaign.y
|
||||
// Add the data to the overview chart
|
||||
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_data.series.push({meta: "Unsuccessful Phishes", value: 100 - average})
|
||||
average_data.series.push({meta: "Successful Phishes", value: average})
|
||||
average_data.series.push({
|
||||
meta: "Unsuccessful Phishes",
|
||||
value: 100 - average
|
||||
})
|
||||
average_data.series.push({
|
||||
meta: "Successful Phishes",
|
||||
value: average
|
||||
})
|
||||
// Build the charts
|
||||
var average_chart = new Chartist.Pie("#average_chart", average_data, average_opts)
|
||||
var overview_chart = new Chartist.Line('#overview_chart', overview_data, overview_opts)
|
||||
|
|
|
@ -4,6 +4,7 @@
|
|||
Author: Jordan Wright <github.com/jordan-wright>
|
||||
*/
|
||||
var pages = []
|
||||
|
||||
// Save attempts to POST to /templates/
|
||||
function save(idx) {
|
||||
var page = {}
|
||||
|
@ -69,7 +70,9 @@ function importSite(){
|
|||
}
|
||||
|
||||
function edit(idx) {
|
||||
$("#modalSubmit").unbind('click').click(function(){save(idx)})
|
||||
$("#modalSubmit").unbind('click').click(function() {
|
||||
save(idx)
|
||||
})
|
||||
$("#html_editor").ckeditor()
|
||||
var page = {}
|
||||
if (idx != -1) {
|
||||
|
@ -92,7 +95,13 @@ function load(){
|
|||
$("#loading").hide()
|
||||
if (pages.length > 0) {
|
||||
$("#pagesTable").show()
|
||||
pagesTable = $("#pagesTable").DataTable();
|
||||
pagesTable = $("#pagesTable").DataTable({
|
||||
destroy: true,
|
||||
columnDefs: [{
|
||||
orderable: false,
|
||||
targets: "no-sort"
|
||||
}]
|
||||
});
|
||||
pagesTable.clear()
|
||||
$.each(pages, function(i, page) {
|
||||
pagesTable.row.add([
|
||||
|
@ -125,13 +134,11 @@ $(document).ready(function(){
|
|||
});
|
||||
$('.modal').on('shown.bs.modal', function(event) {
|
||||
// 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);
|
||||
}
|
||||
// if the z-index of this modal has been set, ignore.
|
||||
if ( $(this).hasClass( 'fv-modal-stack' ) )
|
||||
{
|
||||
if ($(this).hasClass('fv-modal-stack')) {
|
||||
return;
|
||||
}
|
||||
$(this).addClass('fv-modal-stack');
|
||||
|
|
|
@ -15,7 +15,9 @@ var icons = {
|
|||
|
||||
// Save attempts to POST to /templates/
|
||||
function save(idx) {
|
||||
var template = {attachments:[]}
|
||||
var template = {
|
||||
attachments: []
|
||||
}
|
||||
template.name = $("#name").val()
|
||||
template.subject = $("#subject").val()
|
||||
template.html = CKEDITOR.instances["html_editor"].getData();
|
||||
|
@ -78,7 +80,19 @@ function deleteTemplate(idx){
|
|||
}
|
||||
|
||||
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) {
|
||||
var reader = new FileReader();
|
||||
/* Make this a datatable */
|
||||
|
@ -101,23 +115,30 @@ function attach(files){
|
|||
}
|
||||
|
||||
function edit(idx) {
|
||||
$("#modalSubmit").unbind('click').click(function(){save(idx)})
|
||||
$("#attachmentUpload").unbind('click').click(function(){this.value=null})
|
||||
$("#modalSubmit").unbind('click').click(function() {
|
||||
save(idx)
|
||||
})
|
||||
$("#attachmentUpload").unbind('click').click(function() {
|
||||
this.value = null
|
||||
})
|
||||
$("#html_editor").ckeditor()
|
||||
$("#attachmentsTable").show()
|
||||
attachmentsTable = null
|
||||
if ( $.fn.dataTable.isDataTable('#attachmentsTable') ) {
|
||||
attachmentsTable = $('#attachmentsTable').DataTable();
|
||||
}
|
||||
else {
|
||||
attachmentsTable = $("#attachmentsTable").DataTable({
|
||||
"aoColumnDefs" : [{
|
||||
"targets" : [3,4],
|
||||
"sClass" : "datatable_hidden"
|
||||
attachmentsTable = $('#attachmentsTable').DataTable({
|
||||
destroy: true,
|
||||
"order": [
|
||||
[1, "asc"]
|
||||
],
|
||||
columnDefs: [{
|
||||
orderable: false,
|
||||
targets: "no-sort"
|
||||
}, {
|
||||
sClass: "datatable_hidden",
|
||||
targets: [3, 4]
|
||||
}]
|
||||
});
|
||||
var template = {
|
||||
attachments: []
|
||||
}
|
||||
var template = {attachments:[]}
|
||||
if (idx != -1) {
|
||||
template = templates[idx]
|
||||
$("#name").val(template.name)
|
||||
|
@ -178,7 +199,13 @@ function load(){
|
|||
$("#loading").hide()
|
||||
if (templates.length > 0) {
|
||||
$("#templateTable").show()
|
||||
templateTable = $("#templateTable").DataTable();
|
||||
templateTable = $("#templateTable").DataTable({
|
||||
destroy: true,
|
||||
columnDefs: [{
|
||||
orderable: false,
|
||||
targets: "no-sort"
|
||||
}]
|
||||
});
|
||||
templateTable.clear()
|
||||
$.each(templates, function(i, template) {
|
||||
templateTable.row.add([
|
||||
|
@ -211,13 +238,11 @@ $(document).ready(function(){
|
|||
});
|
||||
$('.modal').on('shown.bs.modal', function(event) {
|
||||
// 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);
|
||||
}
|
||||
// if the z-index of this modal has been set, ignore.
|
||||
if ( $(this).hasClass( 'fv-modal-stack' ) )
|
||||
{
|
||||
if ($(this).hasClass('fv-modal-stack')) {
|
||||
return;
|
||||
}
|
||||
$(this).addClass('fv-modal-stack');
|
||||
|
|
|
@ -25,6 +25,7 @@ function save(idx){
|
|||
successFlash("Group updated successfully!")
|
||||
load()
|
||||
dismiss()
|
||||
$("#modal").modal('hide')
|
||||
})
|
||||
.error(function(data) {
|
||||
modalError(data.responseJSON.message)
|
||||
|
@ -37,6 +38,7 @@ function save(idx){
|
|||
successFlash("Group added successfully!")
|
||||
load()
|
||||
dismiss()
|
||||
$("#modal").modal('hide')
|
||||
})
|
||||
.error(function(data) {
|
||||
modalError(data.responseJSON.message)
|
||||
|
@ -48,12 +50,19 @@ function dismiss(){
|
|||
$("#targetsTable").dataTable().DataTable().clear().draw()
|
||||
$("#name").val("")
|
||||
$("#modal\\.flashes").empty()
|
||||
$("#modal").modal('hide')
|
||||
}
|
||||
|
||||
function edit(idx) {
|
||||
targets = $("#targetsTable").dataTable()
|
||||
$("#modalSubmit").unbind('click').click(function(){save(idx)})
|
||||
targets = $("#targetsTable").dataTable({
|
||||
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) {
|
||||
group = {}
|
||||
} else {
|
||||
|
@ -119,7 +128,13 @@ function load(){
|
|||
groups = gs
|
||||
$("#emptyMessage").hide()
|
||||
$("#groupTable").show()
|
||||
groupTable = $("#groupTable").DataTable();
|
||||
groupTable = $("#groupTable").DataTable({
|
||||
destroy: true,
|
||||
columnDefs: [{
|
||||
orderable: false,
|
||||
targets: "no-sort"
|
||||
}]
|
||||
});
|
||||
groupTable.clear();
|
||||
$.each(groups, function(i, group) {
|
||||
var targets = ""
|
||||
|
@ -176,4 +191,7 @@ $(document).ready(function(){
|
|||
.remove()
|
||||
.draw();
|
||||
})
|
||||
$("#modal").on("hide.bs.modal", function() {
|
||||
dismiss()
|
||||
})
|
||||
})
|
||||
|
|
|
@ -1,15 +1,13 @@
|
|||
function errorFlash(message) {
|
||||
$("#flashes").empty()
|
||||
$("#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) {
|
||||
$("#flashes").empty()
|
||||
$("#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) {
|
||||
|
@ -51,7 +49,7 @@ var api = {
|
|||
},
|
||||
// delete() - Deletes a campaign at DELETE /campaigns/:id
|
||||
delete: function(id) {
|
||||
return query("/campaigns/" + id, "DELETE", data)
|
||||
return query("/campaigns/" + id, "DELETE", {})
|
||||
}
|
||||
},
|
||||
// groups contains the endpoints for /groups
|
||||
|
|
File diff suppressed because one or more lines are too long
|
@ -30,22 +30,11 @@
|
|||
<h1 class="page-header" id="page-title">Results for campaign.name</h1>
|
||||
</div>
|
||||
<div class="row">
|
||||
<!--
|
||||
<div class="btn-group">
|
||||
<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 type="button" id="exportButton" class="btn btn-primary" onclick="exportAsCSV()">
|
||||
<i class="fa fa-file-excel-o"></i> Export CSV
|
||||
</button>
|
||||
<ul class="dropdown-menu">
|
||||
<li><a href="#">Export</a>
|
||||
</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 type="button" class="btn btn-danger" data-toggle="tooltip" onclick="deleteCampaign()">
|
||||
<i class="fa fa-trash-o fa-lg"></i> Delete
|
||||
</button>
|
||||
</div>
|
||||
<br />
|
||||
|
@ -114,5 +103,6 @@
|
|||
<script src="/js/d3.min.js"></script>
|
||||
<script src="/js/topojson.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>
|
||||
{{end}}
|
||||
|
|
|
@ -47,7 +47,7 @@
|
|||
<th>Name</th>
|
||||
<th>Created Date</th>
|
||||
<th>Status</th>
|
||||
<th class="col-md-2"></th>
|
||||
<th class="col-md-2 no-sort"></th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
|
@ -113,7 +113,7 @@
|
|||
<table id="groupTable" class="table table-hover table-striped table-condensed">
|
||||
<thead>
|
||||
<th>Group Name</th>
|
||||
<th></th>
|
||||
<th class="no-sort"></th>
|
||||
<tbody>
|
||||
</tbody>
|
||||
</table>
|
||||
|
@ -121,7 +121,7 @@
|
|||
</div>
|
||||
<div class="modal-footer">
|
||||
<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>
|
||||
|
|
|
@ -67,7 +67,7 @@
|
|||
<th>Name</th>
|
||||
<th>Created Date</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>
|
||||
</thead>
|
||||
<tbody>
|
||||
|
|
|
@ -44,7 +44,7 @@
|
|||
<tr>
|
||||
<th>Name</th>
|
||||
<th>Last Modified Date</th>
|
||||
<th class="col-md-2"></th>
|
||||
<th class="col-md-2 no-sort"></th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
|
|
|
@ -46,7 +46,7 @@
|
|||
<tr>
|
||||
<th>Name</th>
|
||||
<th>Modified Date</th>
|
||||
<th class="col-md-2"></th>
|
||||
<th class="col-md-2 no-sort"></th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
|
@ -102,11 +102,11 @@
|
|||
<table id="attachmentsTable" class="table">
|
||||
<thead>
|
||||
<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-1"></th>
|
||||
<th class="datatable_hidden">Content</th>
|
||||
<th class="datatable_hidden">Type</th>
|
||||
<th class="col-md-1 no-sort"></th>
|
||||
<th class="datatable_hidden no-sort">Content</th>
|
||||
<th class="datatable_hidden no-sort">Type</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
|
|
|
@ -47,7 +47,7 @@
|
|||
<th>Name</th>
|
||||
<th>Members</th>
|
||||
<th>Modified Date</th>
|
||||
<th class="col-md-2"></th>
|
||||
<th class="col-md-2 no-sort"></th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
|
@ -94,20 +94,20 @@
|
|||
</form>
|
||||
</div>
|
||||
<br />
|
||||
<table id="targetsTable" class="table table-hover table-striped">
|
||||
<table id="targetsTable" class="table table-hover table-striped table-condensed">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>First Name</th>
|
||||
<th>Last Name</th>
|
||||
<th>Email</th>
|
||||
<th>Position</th>
|
||||
<th></th>
|
||||
<th class="no-sort"></th>
|
||||
<tbody>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
<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>
|
||||
</div>
|
||||
</div>
|
||||
|
|
|
@ -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
|
|
@ -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
|
|
@ -9,8 +9,8 @@ import (
|
|||
"strings"
|
||||
"text/template"
|
||||
|
||||
"github.com/jordan-wright/email"
|
||||
"github.com/gophish/gophish/models"
|
||||
"github.com/jordan-wright/email"
|
||||
)
|
||||
|
||||
// Logger is the logger for the worker
|
||||
|
@ -119,6 +119,10 @@ func processCampaign(c *models.Campaign) {
|
|||
if err != nil {
|
||||
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)
|
||||
|
|
Loading…
Reference in New Issue