mirror of https://github.com/gophish/gophish
Adding the ability to schedule campaigns. Fixes #21
parent
a8aac75f99
commit
082023aae0
|
@ -83,7 +83,6 @@ func API_Campaigns(w http.ResponseWriter, r *http.Request) {
|
||||||
JSONResponse(w, models.Response{Success: false, Message: err.Error()}, http.StatusBadRequest)
|
JSONResponse(w, models.Response{Success: false, Message: err.Error()}, http.StatusBadRequest)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
Worker.Queue <- &c
|
|
||||||
JSONResponse(w, c, http.StatusCreated)
|
JSONResponse(w, c, http.StatusCreated)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,9 @@
|
||||||
|
|
||||||
|
-- +goose Up
|
||||||
|
-- SQL in section 'Up' is executed when this migration is applied
|
||||||
|
ALTER TABLE "campaigns" ADD COLUMN "launch_date" DATETIME;
|
||||||
|
|
||||||
|
UPDATE "campaigns" SET "launch_date" = "created_date";
|
||||||
|
-- +goose Down
|
||||||
|
-- SQL section 'Down' is executed when this migration is rolled back
|
||||||
|
|
|
@ -2,7 +2,6 @@ package models
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/jinzhu/gorm"
|
"github.com/jinzhu/gorm"
|
||||||
|
@ -14,6 +13,7 @@ type Campaign struct {
|
||||||
UserId int64 `json:"-"`
|
UserId int64 `json:"-"`
|
||||||
Name string `json:"name" sql:"not null"`
|
Name string `json:"name" sql:"not null"`
|
||||||
CreatedDate time.Time `json:"created_date"`
|
CreatedDate time.Time `json:"created_date"`
|
||||||
|
LaunchDate time.Time `json:"launch_date"`
|
||||||
CompletedDate time.Time `json:"completed_date"`
|
CompletedDate time.Time `json:"completed_date"`
|
||||||
TemplateId int64 `json:"-"`
|
TemplateId int64 `json:"-"`
|
||||||
Template Template `json:"template"`
|
Template Template `json:"template"`
|
||||||
|
@ -139,7 +139,7 @@ func (c *Campaign) getDetails() error {
|
||||||
c.Page = Page{Name: "[Deleted]"}
|
c.Page = Page{Name: "[Deleted]"}
|
||||||
Logger.Printf("%s: page not found for campaign\n", err)
|
Logger.Printf("%s: page not found for campaign\n", err)
|
||||||
}
|
}
|
||||||
err = db.Table("SMTP").Where("id=?", c.SMTPId).Find(&c.SMTP).Error
|
err = db.Table("smtp").Where("id=?", c.SMTPId).Find(&c.SMTP).Error
|
||||||
if err != nil {
|
if err != nil {
|
||||||
// Check if the SMTP was deleted
|
// Check if the SMTP was deleted
|
||||||
if err != gorm.ErrRecordNotFound {
|
if err != gorm.ErrRecordNotFound {
|
||||||
|
@ -167,7 +167,7 @@ func GetCampaigns(uid int64) ([]Campaign, error) {
|
||||||
cs := []Campaign{}
|
cs := []Campaign{}
|
||||||
err := db.Model(&User{Id: uid}).Related(&cs).Error
|
err := db.Model(&User{Id: uid}).Related(&cs).Error
|
||||||
if err != nil {
|
if err != nil {
|
||||||
fmt.Println(err)
|
Logger.Println(err)
|
||||||
}
|
}
|
||||||
for i, _ := range cs {
|
for i, _ := range cs {
|
||||||
err = cs[i].getDetails()
|
err = cs[i].getDetails()
|
||||||
|
@ -190,6 +190,24 @@ func GetCampaign(id int64, uid int64) (Campaign, error) {
|
||||||
return c, err
|
return c, err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// GetQueuedCampaigns returns the campaigns that are queued up for this given minute
|
||||||
|
func GetQueuedCampaigns(t time.Time) ([]Campaign, error) {
|
||||||
|
cs := []Campaign{}
|
||||||
|
err := db.Where("launch_date <= ?", t).
|
||||||
|
Where("status = ?", CAMPAIGN_QUEUED).Find(&cs).Error
|
||||||
|
if err != nil {
|
||||||
|
Logger.Println(err)
|
||||||
|
}
|
||||||
|
Logger.Printf("Found %d Campaigns to run\n", len(cs))
|
||||||
|
for i, _ := range cs {
|
||||||
|
err = cs[i].getDetails()
|
||||||
|
if err != nil {
|
||||||
|
Logger.Println(err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return cs, err
|
||||||
|
}
|
||||||
|
|
||||||
// PostCampaign inserts a campaign and all associated records into the database.
|
// PostCampaign inserts a campaign and all associated records into the database.
|
||||||
func PostCampaign(c *Campaign, uid int64) error {
|
func PostCampaign(c *Campaign, uid int64) error {
|
||||||
if err := c.Validate(); err != nil {
|
if err := c.Validate(); err != nil {
|
||||||
|
@ -200,6 +218,9 @@ func PostCampaign(c *Campaign, uid int64) error {
|
||||||
c.CreatedDate = time.Now()
|
c.CreatedDate = time.Now()
|
||||||
c.CompletedDate = time.Time{}
|
c.CompletedDate = time.Time{}
|
||||||
c.Status = CAMPAIGN_QUEUED
|
c.Status = CAMPAIGN_QUEUED
|
||||||
|
if c.LaunchDate.IsZero() {
|
||||||
|
c.LaunchDate = time.Now()
|
||||||
|
}
|
||||||
// Check to make sure all the groups already exist
|
// Check to make sure all the groups already exist
|
||||||
for i, g := range c.Groups {
|
for i, g := range c.Groups {
|
||||||
c.Groups[i], err = GetGroupByName(g.Name, uid)
|
c.Groups[i], err = GetGroupByName(g.Name, uid)
|
||||||
|
|
|
@ -0,0 +1,373 @@
|
||||||
|
/*!
|
||||||
|
* Datetimepicker for Bootstrap 3
|
||||||
|
* version : 4.15.35
|
||||||
|
* https://github.com/Eonasdan/bootstrap-datetimepicker/
|
||||||
|
*/
|
||||||
|
.bootstrap-datetimepicker-widget {
|
||||||
|
list-style: none;
|
||||||
|
}
|
||||||
|
.bootstrap-datetimepicker-widget.dropdown-menu {
|
||||||
|
margin: 2px 0;
|
||||||
|
padding: 4px;
|
||||||
|
width: 19em;
|
||||||
|
}
|
||||||
|
@media (min-width: 768px) {
|
||||||
|
.bootstrap-datetimepicker-widget.dropdown-menu.timepicker-sbs {
|
||||||
|
width: 38em;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@media (min-width: 992px) {
|
||||||
|
.bootstrap-datetimepicker-widget.dropdown-menu.timepicker-sbs {
|
||||||
|
width: 38em;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@media (min-width: 1200px) {
|
||||||
|
.bootstrap-datetimepicker-widget.dropdown-menu.timepicker-sbs {
|
||||||
|
width: 38em;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.bootstrap-datetimepicker-widget.dropdown-menu:before,
|
||||||
|
.bootstrap-datetimepicker-widget.dropdown-menu:after {
|
||||||
|
content: '';
|
||||||
|
display: inline-block;
|
||||||
|
position: absolute;
|
||||||
|
}
|
||||||
|
.bootstrap-datetimepicker-widget.dropdown-menu.bottom:before {
|
||||||
|
border-left: 7px solid transparent;
|
||||||
|
border-right: 7px solid transparent;
|
||||||
|
border-bottom: 7px solid #cccccc;
|
||||||
|
border-bottom-color: rgba(0, 0, 0, 0.2);
|
||||||
|
top: -7px;
|
||||||
|
left: 7px;
|
||||||
|
}
|
||||||
|
.bootstrap-datetimepicker-widget.dropdown-menu.bottom:after {
|
||||||
|
border-left: 6px solid transparent;
|
||||||
|
border-right: 6px solid transparent;
|
||||||
|
border-bottom: 6px solid white;
|
||||||
|
top: -6px;
|
||||||
|
left: 8px;
|
||||||
|
}
|
||||||
|
.bootstrap-datetimepicker-widget.dropdown-menu.top:before {
|
||||||
|
border-left: 7px solid transparent;
|
||||||
|
border-right: 7px solid transparent;
|
||||||
|
border-top: 7px solid #cccccc;
|
||||||
|
border-top-color: rgba(0, 0, 0, 0.2);
|
||||||
|
bottom: -7px;
|
||||||
|
left: 6px;
|
||||||
|
}
|
||||||
|
.bootstrap-datetimepicker-widget.dropdown-menu.top:after {
|
||||||
|
border-left: 6px solid transparent;
|
||||||
|
border-right: 6px solid transparent;
|
||||||
|
border-top: 6px solid white;
|
||||||
|
bottom: -6px;
|
||||||
|
left: 7px;
|
||||||
|
}
|
||||||
|
.bootstrap-datetimepicker-widget.dropdown-menu.pull-right:before {
|
||||||
|
left: auto;
|
||||||
|
right: 6px;
|
||||||
|
}
|
||||||
|
.bootstrap-datetimepicker-widget.dropdown-menu.pull-right:after {
|
||||||
|
left: auto;
|
||||||
|
right: 7px;
|
||||||
|
}
|
||||||
|
.bootstrap-datetimepicker-widget .list-unstyled {
|
||||||
|
margin: 0;
|
||||||
|
}
|
||||||
|
.bootstrap-datetimepicker-widget a[data-action] {
|
||||||
|
padding: 6px 0;
|
||||||
|
}
|
||||||
|
.bootstrap-datetimepicker-widget a[data-action]:active {
|
||||||
|
box-shadow: none;
|
||||||
|
}
|
||||||
|
.bootstrap-datetimepicker-widget .timepicker-hour,
|
||||||
|
.bootstrap-datetimepicker-widget .timepicker-minute,
|
||||||
|
.bootstrap-datetimepicker-widget .timepicker-second {
|
||||||
|
width: 54px;
|
||||||
|
font-weight: bold;
|
||||||
|
font-size: 1.2em;
|
||||||
|
margin: 0;
|
||||||
|
}
|
||||||
|
.bootstrap-datetimepicker-widget button[data-action] {
|
||||||
|
padding: 6px;
|
||||||
|
}
|
||||||
|
.bootstrap-datetimepicker-widget .btn[data-action="incrementHours"]::after {
|
||||||
|
position: absolute;
|
||||||
|
width: 1px;
|
||||||
|
height: 1px;
|
||||||
|
margin: -1px;
|
||||||
|
padding: 0;
|
||||||
|
overflow: hidden;
|
||||||
|
clip: rect(0, 0, 0, 0);
|
||||||
|
border: 0;
|
||||||
|
content: "Increment Hours";
|
||||||
|
}
|
||||||
|
.bootstrap-datetimepicker-widget .btn[data-action="incrementMinutes"]::after {
|
||||||
|
position: absolute;
|
||||||
|
width: 1px;
|
||||||
|
height: 1px;
|
||||||
|
margin: -1px;
|
||||||
|
padding: 0;
|
||||||
|
overflow: hidden;
|
||||||
|
clip: rect(0, 0, 0, 0);
|
||||||
|
border: 0;
|
||||||
|
content: "Increment Minutes";
|
||||||
|
}
|
||||||
|
.bootstrap-datetimepicker-widget .btn[data-action="decrementHours"]::after {
|
||||||
|
position: absolute;
|
||||||
|
width: 1px;
|
||||||
|
height: 1px;
|
||||||
|
margin: -1px;
|
||||||
|
padding: 0;
|
||||||
|
overflow: hidden;
|
||||||
|
clip: rect(0, 0, 0, 0);
|
||||||
|
border: 0;
|
||||||
|
content: "Decrement Hours";
|
||||||
|
}
|
||||||
|
.bootstrap-datetimepicker-widget .btn[data-action="decrementMinutes"]::after {
|
||||||
|
position: absolute;
|
||||||
|
width: 1px;
|
||||||
|
height: 1px;
|
||||||
|
margin: -1px;
|
||||||
|
padding: 0;
|
||||||
|
overflow: hidden;
|
||||||
|
clip: rect(0, 0, 0, 0);
|
||||||
|
border: 0;
|
||||||
|
content: "Decrement Minutes";
|
||||||
|
}
|
||||||
|
.bootstrap-datetimepicker-widget .btn[data-action="showHours"]::after {
|
||||||
|
position: absolute;
|
||||||
|
width: 1px;
|
||||||
|
height: 1px;
|
||||||
|
margin: -1px;
|
||||||
|
padding: 0;
|
||||||
|
overflow: hidden;
|
||||||
|
clip: rect(0, 0, 0, 0);
|
||||||
|
border: 0;
|
||||||
|
content: "Show Hours";
|
||||||
|
}
|
||||||
|
.bootstrap-datetimepicker-widget .btn[data-action="showMinutes"]::after {
|
||||||
|
position: absolute;
|
||||||
|
width: 1px;
|
||||||
|
height: 1px;
|
||||||
|
margin: -1px;
|
||||||
|
padding: 0;
|
||||||
|
overflow: hidden;
|
||||||
|
clip: rect(0, 0, 0, 0);
|
||||||
|
border: 0;
|
||||||
|
content: "Show Minutes";
|
||||||
|
}
|
||||||
|
.bootstrap-datetimepicker-widget .btn[data-action="togglePeriod"]::after {
|
||||||
|
position: absolute;
|
||||||
|
width: 1px;
|
||||||
|
height: 1px;
|
||||||
|
margin: -1px;
|
||||||
|
padding: 0;
|
||||||
|
overflow: hidden;
|
||||||
|
clip: rect(0, 0, 0, 0);
|
||||||
|
border: 0;
|
||||||
|
content: "Toggle AM/PM";
|
||||||
|
}
|
||||||
|
.bootstrap-datetimepicker-widget .btn[data-action="clear"]::after {
|
||||||
|
position: absolute;
|
||||||
|
width: 1px;
|
||||||
|
height: 1px;
|
||||||
|
margin: -1px;
|
||||||
|
padding: 0;
|
||||||
|
overflow: hidden;
|
||||||
|
clip: rect(0, 0, 0, 0);
|
||||||
|
border: 0;
|
||||||
|
content: "Clear the picker";
|
||||||
|
}
|
||||||
|
.bootstrap-datetimepicker-widget .btn[data-action="today"]::after {
|
||||||
|
position: absolute;
|
||||||
|
width: 1px;
|
||||||
|
height: 1px;
|
||||||
|
margin: -1px;
|
||||||
|
padding: 0;
|
||||||
|
overflow: hidden;
|
||||||
|
clip: rect(0, 0, 0, 0);
|
||||||
|
border: 0;
|
||||||
|
content: "Set the date to today";
|
||||||
|
}
|
||||||
|
.bootstrap-datetimepicker-widget .picker-switch {
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
.bootstrap-datetimepicker-widget .picker-switch::after {
|
||||||
|
position: absolute;
|
||||||
|
width: 1px;
|
||||||
|
height: 1px;
|
||||||
|
margin: -1px;
|
||||||
|
padding: 0;
|
||||||
|
overflow: hidden;
|
||||||
|
clip: rect(0, 0, 0, 0);
|
||||||
|
border: 0;
|
||||||
|
content: "Toggle Date and Time Screens";
|
||||||
|
}
|
||||||
|
.bootstrap-datetimepicker-widget .picker-switch td {
|
||||||
|
padding: 0;
|
||||||
|
margin: 0;
|
||||||
|
height: auto;
|
||||||
|
width: auto;
|
||||||
|
line-height: inherit;
|
||||||
|
}
|
||||||
|
.bootstrap-datetimepicker-widget .picker-switch td span {
|
||||||
|
line-height: 2.5;
|
||||||
|
height: 2.5em;
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
.bootstrap-datetimepicker-widget table {
|
||||||
|
width: 100%;
|
||||||
|
margin: 0;
|
||||||
|
}
|
||||||
|
.bootstrap-datetimepicker-widget table td,
|
||||||
|
.bootstrap-datetimepicker-widget table th {
|
||||||
|
text-align: center;
|
||||||
|
border-radius: 4px;
|
||||||
|
}
|
||||||
|
.bootstrap-datetimepicker-widget table th {
|
||||||
|
height: 20px;
|
||||||
|
line-height: 20px;
|
||||||
|
width: 20px;
|
||||||
|
}
|
||||||
|
.bootstrap-datetimepicker-widget table th.picker-switch {
|
||||||
|
width: 145px;
|
||||||
|
}
|
||||||
|
.bootstrap-datetimepicker-widget table th.disabled,
|
||||||
|
.bootstrap-datetimepicker-widget table th.disabled:hover {
|
||||||
|
background: none;
|
||||||
|
color: #777777;
|
||||||
|
cursor: not-allowed;
|
||||||
|
}
|
||||||
|
.bootstrap-datetimepicker-widget table th.prev::after {
|
||||||
|
position: absolute;
|
||||||
|
width: 1px;
|
||||||
|
height: 1px;
|
||||||
|
margin: -1px;
|
||||||
|
padding: 0;
|
||||||
|
overflow: hidden;
|
||||||
|
clip: rect(0, 0, 0, 0);
|
||||||
|
border: 0;
|
||||||
|
content: "Previous Month";
|
||||||
|
}
|
||||||
|
.bootstrap-datetimepicker-widget table th.next::after {
|
||||||
|
position: absolute;
|
||||||
|
width: 1px;
|
||||||
|
height: 1px;
|
||||||
|
margin: -1px;
|
||||||
|
padding: 0;
|
||||||
|
overflow: hidden;
|
||||||
|
clip: rect(0, 0, 0, 0);
|
||||||
|
border: 0;
|
||||||
|
content: "Next Month";
|
||||||
|
}
|
||||||
|
.bootstrap-datetimepicker-widget table thead tr:first-child th {
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
||||||
|
.bootstrap-datetimepicker-widget table thead tr:first-child th:hover {
|
||||||
|
background: #eeeeee;
|
||||||
|
}
|
||||||
|
.bootstrap-datetimepicker-widget table td {
|
||||||
|
height: 54px;
|
||||||
|
line-height: 54px;
|
||||||
|
width: 54px;
|
||||||
|
}
|
||||||
|
.bootstrap-datetimepicker-widget table td.cw {
|
||||||
|
font-size: .8em;
|
||||||
|
height: 20px;
|
||||||
|
line-height: 20px;
|
||||||
|
color: #777777;
|
||||||
|
}
|
||||||
|
.bootstrap-datetimepicker-widget table td.day {
|
||||||
|
height: 20px;
|
||||||
|
line-height: 20px;
|
||||||
|
width: 20px;
|
||||||
|
}
|
||||||
|
.bootstrap-datetimepicker-widget table td.day:hover,
|
||||||
|
.bootstrap-datetimepicker-widget table td.hour:hover,
|
||||||
|
.bootstrap-datetimepicker-widget table td.minute:hover,
|
||||||
|
.bootstrap-datetimepicker-widget table td.second:hover {
|
||||||
|
background: #eeeeee;
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
||||||
|
.bootstrap-datetimepicker-widget table td.old,
|
||||||
|
.bootstrap-datetimepicker-widget table td.new {
|
||||||
|
color: #777777;
|
||||||
|
}
|
||||||
|
.bootstrap-datetimepicker-widget table td.today {
|
||||||
|
position: relative;
|
||||||
|
}
|
||||||
|
.bootstrap-datetimepicker-widget table td.today:before {
|
||||||
|
content: '';
|
||||||
|
display: inline-block;
|
||||||
|
border: solid transparent;
|
||||||
|
border-width: 0 0 7px 7px;
|
||||||
|
border-bottom-color: #337ab7;
|
||||||
|
border-top-color: rgba(0, 0, 0, 0.2);
|
||||||
|
position: absolute;
|
||||||
|
bottom: 4px;
|
||||||
|
right: 4px;
|
||||||
|
}
|
||||||
|
.bootstrap-datetimepicker-widget table td.active,
|
||||||
|
.bootstrap-datetimepicker-widget table td.active:hover {
|
||||||
|
background-color: #337ab7;
|
||||||
|
color: #ffffff;
|
||||||
|
text-shadow: 0 -1px 0 rgba(0, 0, 0, 0.25);
|
||||||
|
}
|
||||||
|
.bootstrap-datetimepicker-widget table td.active.today:before {
|
||||||
|
border-bottom-color: #fff;
|
||||||
|
}
|
||||||
|
.bootstrap-datetimepicker-widget table td.disabled,
|
||||||
|
.bootstrap-datetimepicker-widget table td.disabled:hover {
|
||||||
|
background: none;
|
||||||
|
color: #777777;
|
||||||
|
cursor: not-allowed;
|
||||||
|
}
|
||||||
|
.bootstrap-datetimepicker-widget table td span {
|
||||||
|
display: inline-block;
|
||||||
|
width: 54px;
|
||||||
|
height: 54px;
|
||||||
|
line-height: 54px;
|
||||||
|
margin: 2px 1.5px;
|
||||||
|
cursor: pointer;
|
||||||
|
border-radius: 4px;
|
||||||
|
}
|
||||||
|
.bootstrap-datetimepicker-widget table td span:hover {
|
||||||
|
background: #eeeeee;
|
||||||
|
}
|
||||||
|
.bootstrap-datetimepicker-widget table td span.active {
|
||||||
|
background-color: #337ab7;
|
||||||
|
color: #ffffff;
|
||||||
|
text-shadow: 0 -1px 0 rgba(0, 0, 0, 0.25);
|
||||||
|
}
|
||||||
|
.bootstrap-datetimepicker-widget table td span.old {
|
||||||
|
color: #777777;
|
||||||
|
}
|
||||||
|
.bootstrap-datetimepicker-widget table td span.disabled,
|
||||||
|
.bootstrap-datetimepicker-widget table td span.disabled:hover {
|
||||||
|
background: none;
|
||||||
|
color: #777777;
|
||||||
|
cursor: not-allowed;
|
||||||
|
}
|
||||||
|
.bootstrap-datetimepicker-widget.usetwentyfour td.hour {
|
||||||
|
height: 27px;
|
||||||
|
line-height: 27px;
|
||||||
|
}
|
||||||
|
.bootstrap-datetimepicker-widget.wider {
|
||||||
|
width: 21em;
|
||||||
|
}
|
||||||
|
.bootstrap-datetimepicker-widget .datepicker-decades .decade {
|
||||||
|
line-height: 1.8em !important;
|
||||||
|
}
|
||||||
|
.input-group.date .input-group-addon {
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
||||||
|
.sr-only {
|
||||||
|
position: absolute;
|
||||||
|
width: 1px;
|
||||||
|
height: 1px;
|
||||||
|
margin: -1px;
|
||||||
|
padding: 0;
|
||||||
|
overflow: hidden;
|
||||||
|
clip: rect(0, 0, 0, 0);
|
||||||
|
border: 0;
|
||||||
|
}
|
|
@ -31,8 +31,9 @@ function launch() {
|
||||||
name: $("#page").val()
|
name: $("#page").val()
|
||||||
},
|
},
|
||||||
smtp: {
|
smtp: {
|
||||||
name: $("#profile").val()
|
name: $("#profile").val()
|
||||||
},
|
},
|
||||||
|
launch_date: moment($("#launch_date").val(), "MM/DD/YYYY HH:mm").format(),
|
||||||
groups: groups
|
groups: groups
|
||||||
}
|
}
|
||||||
launchHtml = $("launchButton").html()
|
launchHtml = $("launchButton").html()
|
||||||
|
@ -66,7 +67,7 @@ function sendTestEmail() {
|
||||||
name: $("#page").val()
|
name: $("#page").val()
|
||||||
},
|
},
|
||||||
smtp: {
|
smtp: {
|
||||||
name: $("#profile").val()
|
name: $("#profile").val()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
btnHtml = $("#sendTestModalSubmit").html()
|
btnHtml = $("#sendTestModalSubmit").html()
|
||||||
|
@ -141,15 +142,15 @@ function edit(campaign) {
|
||||||
page_bh.add(pages)
|
page_bh.add(pages)
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
api.SMTP.get()
|
api.SMTP.get()
|
||||||
.success(function(profiles) {
|
.success(function(profiles) {
|
||||||
if (profiles.length == 0){
|
if (profiles.length == 0) {
|
||||||
modalError("No profiles found!")
|
modalError("No profiles found!")
|
||||||
return false
|
return false
|
||||||
} else {
|
} else {
|
||||||
profile_bh.add(profiles)
|
profile_bh.add(profiles)
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -201,22 +202,29 @@ function copy(idx) {
|
||||||
$("#page").val(campaign.page.name)
|
$("#page").val(campaign.page.name)
|
||||||
$("#profile").val(campaign.smtp.name)
|
$("#profile").val(campaign.smtp.name)
|
||||||
$("#url").val(campaign.url)
|
$("#url").val(campaign.url)
|
||||||
$.each(campaign.groups, function(i, group){
|
$.each(campaign.groups, function(i, group) {
|
||||||
groupTable.row.add([
|
groupTable.row.add([
|
||||||
group.name,
|
group.name,
|
||||||
'<span style="cursor:pointer;"><i class="fa fa-trash-o"></i></span>'
|
'<span style="cursor:pointer;"><i class="fa fa-trash-o"></i></span>'
|
||||||
]).draw()
|
]).draw()
|
||||||
$("#groupTable").on("click", "span>i.fa-trash-o", function() {
|
$("#groupTable").on("click", "span>i.fa-trash-o", function() {
|
||||||
groupTable.row($(this).parents('tr'))
|
groupTable.row($(this).parents('tr'))
|
||||||
.remove()
|
.remove()
|
||||||
.draw();
|
.draw();
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
$(document).ready(function() {
|
$(document).ready(function() {
|
||||||
// Setup multiple modals
|
$("#launch_date").datetimepicker({
|
||||||
// Code based on http://miles-by-motorcycle.com/static/bootstrap-modal/index.html
|
"widgetPositioning": {
|
||||||
|
"vertical": "bottom"
|
||||||
|
},
|
||||||
|
"showTodayButton": true,
|
||||||
|
"defaultDate": moment()
|
||||||
|
})
|
||||||
|
// Setup multiple modals
|
||||||
|
// Code based on http://miles-by-motorcycle.com/static/bootstrap-modal/index.html
|
||||||
$('.modal').on('hidden.bs.modal', function(event) {
|
$('.modal').on('hidden.bs.modal', function(event) {
|
||||||
$(this).removeClass('fv-modal-stack');
|
$(this).removeClass('fv-modal-stack');
|
||||||
$('body').data('fv_open_modals', $('body').data('fv_open_modals') - 1);
|
$('body').data('fv_open_modals', $('body').data('fv_open_modals') - 1);
|
||||||
|
@ -253,7 +261,7 @@ $(document).ready(function() {
|
||||||
}, this));
|
}, this));
|
||||||
};
|
};
|
||||||
$('#modal').on('hidden.bs.modal', function(event) {
|
$('#modal').on('hidden.bs.modal', function(event) {
|
||||||
dismiss()
|
dismiss()
|
||||||
});
|
});
|
||||||
api.campaigns.get()
|
api.campaigns.get()
|
||||||
.success(function(cs) {
|
.success(function(cs) {
|
||||||
|
|
File diff suppressed because it is too large
Load Diff
|
@ -21,6 +21,7 @@
|
||||||
<link href="/css/dataTables.bootstrap.css" rel="stylesheet">
|
<link href="/css/dataTables.bootstrap.css" rel="stylesheet">
|
||||||
<link href="/css/font-awesome.min.css" rel="stylesheet">
|
<link href="/css/font-awesome.min.css" rel="stylesheet">
|
||||||
<link href="/css/chartist.min.css" rel="stylesheet">
|
<link href="/css/chartist.min.css" rel="stylesheet">
|
||||||
|
<link href="/css/bootstrap-datetime.css" rel='stylesheet' type='text/css'>
|
||||||
<link href='https://fonts.googleapis.com/css?family=Roboto:700,500' rel='stylesheet' type='text/css'>
|
<link href='https://fonts.googleapis.com/css?family=Roboto:700,500' rel='stylesheet' type='text/css'>
|
||||||
<link href='https://fonts.googleapis.com/css?family=Source+Sans+Pro:400,300,600,700' rel='stylesheet' type='text/css'>
|
<link href='https://fonts.googleapis.com/css?family=Source+Sans+Pro:400,300,600,700' rel='stylesheet' type='text/css'>
|
||||||
<link href="/css/checkbox.css" rel="stylesheet">
|
<link href="/css/checkbox.css" rel="stylesheet">
|
||||||
|
|
|
@ -81,6 +81,8 @@
|
||||||
<br>
|
<br>
|
||||||
<label class="control-label" for="url">URL: <i class="fa fa-question-circle" data-toggle="tooltip" data-placement="right" title="Location of gophish listener (must be reachable by targets!)"></i></label>
|
<label class="control-label" for="url">URL: <i class="fa fa-question-circle" data-toggle="tooltip" data-placement="right" title="Location of gophish listener (must be reachable by targets!)"></i></label>
|
||||||
<input type="text" class="form-control" placeholder="http://192.168.1.1" id="url"/>
|
<input type="text" class="form-control" placeholder="http://192.168.1.1" id="url"/>
|
||||||
|
<label class="control-label" for="url">Schedule: </label>
|
||||||
|
<input type="text" class="form-control" id="launch_date"/>
|
||||||
<label class="control-label" for="profile">Sending Profile:</label>
|
<label class="control-label" for="profile">Sending Profile:</label>
|
||||||
<div class="input-group">
|
<div class="input-group">
|
||||||
<input type="text" class="typeahead form-control" placeholder="Sending Profile" id="profile"/>
|
<input type="text" class="typeahead form-control" placeholder="Sending Profile" id="profile"/>
|
||||||
|
@ -153,6 +155,7 @@
|
||||||
</div>
|
</div>
|
||||||
{{end}}
|
{{end}}
|
||||||
{{define "scripts"}}
|
{{define "scripts"}}
|
||||||
|
<script src="/js/bootstrap-datetime.js"></script>
|
||||||
<script src="/js/hogan.js"></script>
|
<script src="/js/hogan.js"></script>
|
||||||
<script src="/js/typeahead.min.js"></script>
|
<script src="/js/typeahead.min.js"></script>
|
||||||
<script src="/js/app/campaigns.js"></script>
|
<script src="/js/app/campaigns.js"></script>
|
||||||
|
|
|
@ -13,6 +13,7 @@ import (
|
||||||
"strconv"
|
"strconv"
|
||||||
"strings"
|
"strings"
|
||||||
"text/template"
|
"text/template"
|
||||||
|
"time"
|
||||||
|
|
||||||
"github.com/gophish/gophish/models"
|
"github.com/gophish/gophish/models"
|
||||||
"gopkg.in/gomail.v2"
|
"gopkg.in/gomail.v2"
|
||||||
|
@ -22,23 +23,29 @@ import (
|
||||||
var Logger = log.New(os.Stdout, " ", log.Ldate|log.Ltime|log.Lshortfile)
|
var Logger = log.New(os.Stdout, " ", log.Ldate|log.Ltime|log.Lshortfile)
|
||||||
|
|
||||||
// Worker is the background worker that handles watching for new campaigns and sending emails appropriately.
|
// Worker is the background worker that handles watching for new campaigns and sending emails appropriately.
|
||||||
type Worker struct {
|
type Worker struct{}
|
||||||
Queue chan *models.Campaign
|
|
||||||
}
|
|
||||||
|
|
||||||
// New creates a new worker object to handle the creation of campaigns
|
// New creates a new worker object to handle the creation of campaigns
|
||||||
func New() *Worker {
|
func New() *Worker {
|
||||||
return &Worker{
|
return &Worker{}
|
||||||
Queue: make(chan *models.Campaign),
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Start launches the worker to monitor the database for any jobs.
|
// Start launches the worker to poll the database every minute for any jobs.
|
||||||
// If a job is found, it launches the job
|
// If a job is found, it launches the job
|
||||||
func (w *Worker) Start() {
|
func (w *Worker) Start() {
|
||||||
Logger.Println("Background Worker Started Successfully - Waiting for Campaigns")
|
Logger.Println("Background Worker Started Successfully - Waiting for Campaigns")
|
||||||
for {
|
for t := range time.Tick(1 * time.Minute) {
|
||||||
processCampaign(<-w.Queue)
|
cs, err := models.GetQueuedCampaigns(t)
|
||||||
|
// Not really sure of a clean way to catch errors per campaign...
|
||||||
|
if err != nil {
|
||||||
|
Logger.Println(err)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
for _, c := range cs {
|
||||||
|
go func(c models.Campaign) {
|
||||||
|
processCampaign(&c)
|
||||||
|
}(c)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue