mirror of https://github.com/gophish/gophish
Merge pull request #169 from gophish/78-store-smtp-settings
Added storage of SMTP settings to database. Closes #78pull/186/head
commit
af91483cd4
|
@ -35,7 +35,7 @@ func API(w http.ResponseWriter, r *http.Request) {
|
||||||
templates := template.New("template")
|
templates := template.New("template")
|
||||||
_, err := templates.ParseFiles("templates/docs.html")
|
_, err := templates.ParseFiles("templates/docs.html")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
fmt.Println(err)
|
Logger.Println(err)
|
||||||
}
|
}
|
||||||
template.Must(templates, err).ExecuteTemplate(w, "base", nil)
|
template.Must(templates, err).ExecuteTemplate(w, "base", nil)
|
||||||
}
|
}
|
||||||
|
@ -63,7 +63,7 @@ func API_Campaigns(w http.ResponseWriter, r *http.Request) {
|
||||||
case r.Method == "GET":
|
case r.Method == "GET":
|
||||||
cs, err := models.GetCampaigns(ctx.Get(r, "user_id").(int64))
|
cs, err := models.GetCampaigns(ctx.Get(r, "user_id").(int64))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
fmt.Println(err)
|
Logger.Println(err)
|
||||||
}
|
}
|
||||||
JSONResponse(w, cs, http.StatusOK)
|
JSONResponse(w, cs, http.StatusOK)
|
||||||
//POST: Create a new campaign and return it as JSON
|
//POST: Create a new campaign and return it as JSON
|
||||||
|
@ -146,8 +146,8 @@ func API_Groups(w http.ResponseWriter, r *http.Request) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// API_Groups_Id returns details about the requested campaign. If the campaign is not
|
// API_Groups_Id returns details about the requested campaign. If the group is not
|
||||||
// valid, API_Campaigns_Id returns null.
|
// valid, API_Groups_Id returns null.
|
||||||
func API_Groups_Id(w http.ResponseWriter, r *http.Request) {
|
func API_Groups_Id(w http.ResponseWriter, r *http.Request) {
|
||||||
vars := mux.Vars(r)
|
vars := mux.Vars(r)
|
||||||
id, _ := strconv.ParseInt(vars["id"], 0, 64)
|
id, _ := strconv.ParseInt(vars["id"], 0, 64)
|
||||||
|
@ -191,7 +191,7 @@ func API_Templates(w http.ResponseWriter, r *http.Request) {
|
||||||
case r.Method == "GET":
|
case r.Method == "GET":
|
||||||
ts, err := models.GetTemplates(ctx.Get(r, "user_id").(int64))
|
ts, err := models.GetTemplates(ctx.Get(r, "user_id").(int64))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
fmt.Println(err)
|
Logger.Println(err)
|
||||||
}
|
}
|
||||||
JSONResponse(w, ts, http.StatusOK)
|
JSONResponse(w, ts, http.StatusOK)
|
||||||
//POST: Create a new template and return it as JSON
|
//POST: Create a new template and return it as JSON
|
||||||
|
@ -274,7 +274,7 @@ func API_Pages(w http.ResponseWriter, r *http.Request) {
|
||||||
case r.Method == "GET":
|
case r.Method == "GET":
|
||||||
ps, err := models.GetPages(ctx.Get(r, "user_id").(int64))
|
ps, err := models.GetPages(ctx.Get(r, "user_id").(int64))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
fmt.Println(err)
|
Logger.Println(err)
|
||||||
}
|
}
|
||||||
JSONResponse(w, ps, http.StatusOK)
|
JSONResponse(w, ps, http.StatusOK)
|
||||||
//POST: Create a new page and return it as JSON
|
//POST: Create a new page and return it as JSON
|
||||||
|
@ -345,6 +345,88 @@ func API_Pages_Id(w http.ResponseWriter, r *http.Request) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// API_SMTP handles requests for the /api/smtp/ endpoint
|
||||||
|
func API_SMTP(w http.ResponseWriter, r *http.Request) {
|
||||||
|
switch {
|
||||||
|
case r.Method == "GET":
|
||||||
|
ss, err := models.GetSMTPs(ctx.Get(r, "user_id").(int64))
|
||||||
|
if err != nil {
|
||||||
|
Logger.Println(err)
|
||||||
|
}
|
||||||
|
JSONResponse(w, ss, http.StatusOK)
|
||||||
|
//POST: Create a new SMTP and return it as JSON
|
||||||
|
case r.Method == "POST":
|
||||||
|
s := models.SMTP{}
|
||||||
|
// Put the request into a page
|
||||||
|
err := json.NewDecoder(r.Body).Decode(&s)
|
||||||
|
if err != nil {
|
||||||
|
JSONResponse(w, models.Response{Success: false, Message: "Invalid request"}, http.StatusBadRequest)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
// Check to make sure the name is unique
|
||||||
|
_, err = models.GetSMTPByName(s.Name, ctx.Get(r, "user_id").(int64))
|
||||||
|
if err != gorm.RecordNotFound {
|
||||||
|
JSONResponse(w, models.Response{Success: false, Message: "SMTP name already in use"}, http.StatusConflict)
|
||||||
|
Logger.Println(err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
s.ModifiedDate = time.Now()
|
||||||
|
s.UserId = ctx.Get(r, "user_id").(int64)
|
||||||
|
err = models.PostSMTP(&s)
|
||||||
|
if err != nil {
|
||||||
|
JSONResponse(w, models.Response{Success: false, Message: err.Error()}, http.StatusInternalServerError)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
JSONResponse(w, s, http.StatusCreated)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// API_SMTP_Id contains functions to handle the GET'ing, DELETE'ing, and PUT'ing
|
||||||
|
// of a SMTP object
|
||||||
|
func API_SMTP_Id(w http.ResponseWriter, r *http.Request) {
|
||||||
|
vars := mux.Vars(r)
|
||||||
|
id, _ := strconv.ParseInt(vars["id"], 0, 64)
|
||||||
|
s, err := models.GetSMTP(id, ctx.Get(r, "user_id").(int64))
|
||||||
|
if err != nil {
|
||||||
|
JSONResponse(w, models.Response{Success: false, Message: "SMTP not found"}, http.StatusNotFound)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
switch {
|
||||||
|
case r.Method == "GET":
|
||||||
|
JSONResponse(w, s, http.StatusOK)
|
||||||
|
case r.Method == "DELETE":
|
||||||
|
err = models.DeleteSMTP(id, ctx.Get(r, "user_id").(int64))
|
||||||
|
if err != nil {
|
||||||
|
JSONResponse(w, models.Response{Success: false, Message: "Error deleting SMTP"}, http.StatusInternalServerError)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
JSONResponse(w, models.Response{Success: true, Message: "SMTP Deleted Successfully"}, http.StatusOK)
|
||||||
|
case r.Method == "PUT":
|
||||||
|
s = models.SMTP{}
|
||||||
|
err = json.NewDecoder(r.Body).Decode(&s)
|
||||||
|
if err != nil {
|
||||||
|
Logger.Println(err)
|
||||||
|
}
|
||||||
|
if s.Id != id {
|
||||||
|
JSONResponse(w, models.Response{Success: false, Message: "/:id and /:smtp_id mismatch"}, http.StatusBadRequest)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
err = s.Validate()
|
||||||
|
if err != nil {
|
||||||
|
JSONResponse(w, models.Response{Success: false, Message: "Invalid attributes given"}, http.StatusBadRequest)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
s.ModifiedDate = time.Now()
|
||||||
|
s.UserId = ctx.Get(r, "user_id").(int64)
|
||||||
|
err = models.PutSMTP(&s)
|
||||||
|
if err != nil {
|
||||||
|
JSONResponse(w, models.Response{Success: false, Message: "Error updating page"}, http.StatusInternalServerError)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
JSONResponse(w, s, http.StatusOK)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// API_Import_Group imports a CSV of group members
|
// API_Import_Group imports a CSV of group members
|
||||||
func API_Import_Group(w http.ResponseWriter, r *http.Request) {
|
func API_Import_Group(w http.ResponseWriter, r *http.Request) {
|
||||||
ts, err := util.ParseCSV(r)
|
ts, err := util.ParseCSV(r)
|
||||||
|
@ -438,16 +520,50 @@ func API_Send_Test_Email(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
|
||||||
}
|
}
|
||||||
// Get the template requested by name
|
|
||||||
s.Template, err = models.GetTemplateByName(s.Template.Name, ctx.Get(r, "user_id").(int64))
|
// If a Template is not specified use a default
|
||||||
if err == gorm.RecordNotFound {
|
if s.Template.Name == "" {
|
||||||
Logger.Printf("Error - Template %s does not exist", s.Template.Name)
|
//default message body
|
||||||
JSONResponse(w, models.Response{Success: false, Message: models.ErrTemplateNotFound.Error()}, http.StatusBadRequest)
|
text := "It works!\n\nThis is an email letting you know that your gophish\nconfiguration was successful.\n" +
|
||||||
} else if err != nil {
|
"Here are the details:\n\nWho you sent from: {{.From}}\n\nWho you sent to: \n" +
|
||||||
Logger.Println(err)
|
"{{if .FirstName}} First Name: {{.FirstName}}\n{{end}}" +
|
||||||
JSONResponse(w, models.Response{Success: false, Message: err.Error()}, http.StatusBadRequest)
|
"{{if .LastName}} Last Name: {{.LastName}}\n{{end}}" +
|
||||||
return
|
"{{if .Position}} Position: {{.Position}}\n{{end}}" +
|
||||||
|
"{{if .TrackingURL}} Tracking URL: {{.TrackingURL}}\n{{end}}" +
|
||||||
|
"\nNow go send some phish!"
|
||||||
|
t := models.Template{
|
||||||
|
Subject: "Default Email from Gophish",
|
||||||
|
Text: text,
|
||||||
|
}
|
||||||
|
s.Template = t
|
||||||
|
// Try to lookup the Template by name
|
||||||
|
} else {
|
||||||
|
// Get the Template requested by name
|
||||||
|
s.Template, err = models.GetTemplateByName(s.Template.Name, ctx.Get(r, "user_id").(int64))
|
||||||
|
if err == gorm.RecordNotFound {
|
||||||
|
Logger.Printf("Error - Template %s does not exist", s.Template.Name)
|
||||||
|
JSONResponse(w, models.Response{Success: false, Message: models.ErrTemplateNotFound.Error()}, http.StatusBadRequest)
|
||||||
|
} else if err != nil {
|
||||||
|
Logger.Println(err)
|
||||||
|
JSONResponse(w, models.Response{Success: false, Message: err.Error()}, http.StatusBadRequest)
|
||||||
|
return
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// If a complete sending profile is provided use it
|
||||||
|
if err := s.SMTP.Validate(); err != nil {
|
||||||
|
// Otherwise get the SMTP requested by name
|
||||||
|
s.SMTP, err = models.GetSMTPByName(s.SMTP.Name, ctx.Get(r, "user_id").(int64))
|
||||||
|
if err == gorm.RecordNotFound {
|
||||||
|
Logger.Printf("Error - Sending profile %s does not exist", s.SMTP.Name)
|
||||||
|
JSONResponse(w, models.Response{Success: false, Message: models.ErrSMTPNotFound.Error()}, http.StatusBadRequest)
|
||||||
|
} else if err != nil {
|
||||||
|
Logger.Println(err)
|
||||||
|
JSONResponse(w, models.Response{Success: false, Message: err.Error()}, http.StatusBadRequest)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Send the test email
|
// Send the test email
|
||||||
err = worker.SendTestEmail(s)
|
err = worker.SendTestEmail(s)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|
|
@ -35,6 +35,7 @@ func CreateAdminRouter() http.Handler {
|
||||||
router.HandleFunc("/templates", Use(Templates, mid.RequireLogin))
|
router.HandleFunc("/templates", Use(Templates, mid.RequireLogin))
|
||||||
router.HandleFunc("/users", Use(Users, mid.RequireLogin))
|
router.HandleFunc("/users", Use(Users, mid.RequireLogin))
|
||||||
router.HandleFunc("/landing_pages", Use(LandingPages, mid.RequireLogin))
|
router.HandleFunc("/landing_pages", Use(LandingPages, mid.RequireLogin))
|
||||||
|
router.HandleFunc("/sending_profiles", Use(SendingProfiles, mid.RequireLogin))
|
||||||
router.HandleFunc("/register", Use(Register, mid.RequireLogin))
|
router.HandleFunc("/register", Use(Register, mid.RequireLogin))
|
||||||
router.HandleFunc("/settings", Use(Settings, mid.RequireLogin))
|
router.HandleFunc("/settings", Use(Settings, mid.RequireLogin))
|
||||||
// Create the API routes
|
// Create the API routes
|
||||||
|
@ -50,6 +51,8 @@ func CreateAdminRouter() http.Handler {
|
||||||
api.HandleFunc("/templates/{id:[0-9]+}", Use(API_Templates_Id, mid.RequireAPIKey))
|
api.HandleFunc("/templates/{id:[0-9]+}", Use(API_Templates_Id, mid.RequireAPIKey))
|
||||||
api.HandleFunc("/pages/", Use(API_Pages, mid.RequireAPIKey))
|
api.HandleFunc("/pages/", Use(API_Pages, mid.RequireAPIKey))
|
||||||
api.HandleFunc("/pages/{id:[0-9]+}", Use(API_Pages_Id, mid.RequireAPIKey))
|
api.HandleFunc("/pages/{id:[0-9]+}", Use(API_Pages_Id, mid.RequireAPIKey))
|
||||||
|
api.HandleFunc("/smtp/", Use(API_SMTP, mid.RequireAPIKey))
|
||||||
|
api.HandleFunc("/smtp/{id:[0-9]+}", Use(API_SMTP_Id, mid.RequireAPIKey))
|
||||||
api.HandleFunc("/util/send_test_email", Use(API_Send_Test_Email, mid.RequireAPIKey))
|
api.HandleFunc("/util/send_test_email", Use(API_Send_Test_Email, mid.RequireAPIKey))
|
||||||
api.HandleFunc("/import/group", API_Import_Group)
|
api.HandleFunc("/import/group", API_Import_Group)
|
||||||
api.HandleFunc("/import/email", API_Import_Email)
|
api.HandleFunc("/import/email", API_Import_Email)
|
||||||
|
@ -69,6 +72,8 @@ func CreateAdminRouter() http.Handler {
|
||||||
csrfHandler.ExemptGlob("/api/templates/*")
|
csrfHandler.ExemptGlob("/api/templates/*")
|
||||||
csrfHandler.ExemptGlob("/api/pages")
|
csrfHandler.ExemptGlob("/api/pages")
|
||||||
csrfHandler.ExemptGlob("/api/pages/*")
|
csrfHandler.ExemptGlob("/api/pages/*")
|
||||||
|
csrfHandler.ExemptGlob("/api/smtp")
|
||||||
|
csrfHandler.ExemptGlob("/api/smtp/*")
|
||||||
csrfHandler.ExemptGlob("/api/import/*")
|
csrfHandler.ExemptGlob("/api/import/*")
|
||||||
csrfHandler.ExemptGlob("/api/util/*")
|
csrfHandler.ExemptGlob("/api/util/*")
|
||||||
csrfHandler.ExemptGlob("/static/*")
|
csrfHandler.ExemptGlob("/static/*")
|
||||||
|
@ -269,7 +274,7 @@ func CampaignID(w http.ResponseWriter, r *http.Request) {
|
||||||
Title string
|
Title string
|
||||||
Flashes []interface{}
|
Flashes []interface{}
|
||||||
Token string
|
Token string
|
||||||
}{Title: "Dashboard", User: ctx.Get(r, "user").(models.User), Token: nosurf.Token(r)}
|
}{Title: "Campaign Results", User: ctx.Get(r, "user").(models.User), Token: nosurf.Token(r)}
|
||||||
getTemplate(w, "campaign_results").ExecuteTemplate(w, "base", params)
|
getTemplate(w, "campaign_results").ExecuteTemplate(w, "base", params)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -281,7 +286,7 @@ func Templates(w http.ResponseWriter, r *http.Request) {
|
||||||
Title string
|
Title string
|
||||||
Flashes []interface{}
|
Flashes []interface{}
|
||||||
Token string
|
Token string
|
||||||
}{Title: "Dashboard", User: ctx.Get(r, "user").(models.User), Token: nosurf.Token(r)}
|
}{Title: "Email Templates", User: ctx.Get(r, "user").(models.User), Token: nosurf.Token(r)}
|
||||||
getTemplate(w, "templates").ExecuteTemplate(w, "base", params)
|
getTemplate(w, "templates").ExecuteTemplate(w, "base", params)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -293,7 +298,7 @@ func Users(w http.ResponseWriter, r *http.Request) {
|
||||||
Title string
|
Title string
|
||||||
Flashes []interface{}
|
Flashes []interface{}
|
||||||
Token string
|
Token string
|
||||||
}{Title: "Dashboard", User: ctx.Get(r, "user").(models.User), Token: nosurf.Token(r)}
|
}{Title: "Users & Groups", User: ctx.Get(r, "user").(models.User), Token: nosurf.Token(r)}
|
||||||
getTemplate(w, "users").ExecuteTemplate(w, "base", params)
|
getTemplate(w, "users").ExecuteTemplate(w, "base", params)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -305,10 +310,22 @@ func LandingPages(w http.ResponseWriter, r *http.Request) {
|
||||||
Title string
|
Title string
|
||||||
Flashes []interface{}
|
Flashes []interface{}
|
||||||
Token string
|
Token string
|
||||||
}{Title: "Dashboard", User: ctx.Get(r, "user").(models.User), Token: nosurf.Token(r)}
|
}{Title: "Landing Pages", User: ctx.Get(r, "user").(models.User), Token: nosurf.Token(r)}
|
||||||
getTemplate(w, "landing_pages").ExecuteTemplate(w, "base", params)
|
getTemplate(w, "landing_pages").ExecuteTemplate(w, "base", params)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// SendingProfiles handles the default path and template execution
|
||||||
|
func SendingProfiles(w http.ResponseWriter, r *http.Request) {
|
||||||
|
// Example of using session - will be removed.
|
||||||
|
params := struct {
|
||||||
|
User models.User
|
||||||
|
Title string
|
||||||
|
Flashes []interface{}
|
||||||
|
Token string
|
||||||
|
}{Title: "Sending Profiles", User: ctx.Get(r, "user").(models.User), Token: nosurf.Token(r)}
|
||||||
|
getTemplate(w, "sending_profiles").ExecuteTemplate(w, "base", params)
|
||||||
|
}
|
||||||
|
|
||||||
// Settings handles the changing of settings
|
// Settings handles the changing of settings
|
||||||
func Settings(w http.ResponseWriter, r *http.Request) {
|
func Settings(w http.ResponseWriter, r *http.Request) {
|
||||||
switch {
|
switch {
|
||||||
|
@ -318,7 +335,7 @@ func Settings(w http.ResponseWriter, r *http.Request) {
|
||||||
Title string
|
Title string
|
||||||
Flashes []interface{}
|
Flashes []interface{}
|
||||||
Token string
|
Token string
|
||||||
}{Title: "Dashboard", User: ctx.Get(r, "user").(models.User), Token: nosurf.Token(r)}
|
}{Title: "Settings", User: ctx.Get(r, "user").(models.User), Token: nosurf.Token(r)}
|
||||||
getTemplate(w, "settings").ExecuteTemplate(w, "base", params)
|
getTemplate(w, "settings").ExecuteTemplate(w, "base", params)
|
||||||
case r.Method == "POST":
|
case r.Method == "POST":
|
||||||
err := auth.ChangePassword(r)
|
err := auth.ChangePassword(r)
|
||||||
|
|
|
@ -0,0 +1,22 @@
|
||||||
|
|
||||||
|
-- +goose Up
|
||||||
|
-- SQL in section 'Up' is executed when this migration is applied
|
||||||
|
-- Move the relationship between campaigns and smtp to campaigns
|
||||||
|
ALTER TABLE campaigns ADD COLUMN "smtp_id" bigint;
|
||||||
|
-- Create a new table to store smtp records
|
||||||
|
DROP TABLE smtp;
|
||||||
|
CREATE TABLE smtp(
|
||||||
|
id integer primary key autoincrement,
|
||||||
|
user_id bigint,
|
||||||
|
interface_type varchar(255),
|
||||||
|
name varchar(255),
|
||||||
|
host varchar(255),
|
||||||
|
username varchar(255),
|
||||||
|
password varchar(255),
|
||||||
|
from_address varchar(255),
|
||||||
|
modified_date datetime default CURRENT_TIMESTAMP,
|
||||||
|
ignore_cert_errors BOOLEAN
|
||||||
|
);
|
||||||
|
-- +goose Down
|
||||||
|
-- SQL section 'Down' is executed when this migration is rolled back
|
||||||
|
|
|
@ -23,6 +23,7 @@ type Campaign struct {
|
||||||
Results []Result `json:"results,omitempty"`
|
Results []Result `json:"results,omitempty"`
|
||||||
Groups []Group `json:"groups,omitempty"`
|
Groups []Group `json:"groups,omitempty"`
|
||||||
Events []Event `json:"timeline,omitemtpy"`
|
Events []Event `json:"timeline,omitemtpy"`
|
||||||
|
SMTPId int64 `json:"-"`
|
||||||
SMTP SMTP `json:"smtp"`
|
SMTP SMTP `json:"smtp"`
|
||||||
URL string `json:"url"`
|
URL string `json:"url"`
|
||||||
}
|
}
|
||||||
|
@ -39,6 +40,9 @@ var ErrTemplateNotSpecified = errors.New("No email template specified")
|
||||||
// ErrPageNotSpecified indicates a landing page was not provided for the campaign
|
// ErrPageNotSpecified indicates a landing page was not provided for the campaign
|
||||||
var ErrPageNotSpecified = errors.New("No landing page specified")
|
var ErrPageNotSpecified = errors.New("No landing page specified")
|
||||||
|
|
||||||
|
// ErrSMTPNotSpecified indicates a sending profile was not provided for the campaign
|
||||||
|
var ErrSMTPNotSpecified = errors.New("No sending profile specified")
|
||||||
|
|
||||||
// ErrTemplateNotFound indicates the template specified does not exist in the database
|
// ErrTemplateNotFound indicates the template specified does not exist in the database
|
||||||
var ErrTemplateNotFound = errors.New("Template not found")
|
var ErrTemplateNotFound = errors.New("Template not found")
|
||||||
|
|
||||||
|
@ -48,6 +52,9 @@ var ErrGroupNotFound = errors.New("Group not found")
|
||||||
// ErrPageNotFound indicates a page specified by the user does not exist in the database
|
// ErrPageNotFound indicates a page specified by the user does not exist in the database
|
||||||
var ErrPageNotFound = errors.New("Page not found")
|
var ErrPageNotFound = errors.New("Page not found")
|
||||||
|
|
||||||
|
// ErrSMTPNotFound indicates a sending profile specified by the user does not exist in the database
|
||||||
|
var ErrSMTPNotFound = errors.New("Sending profile not found")
|
||||||
|
|
||||||
// Validate checks to make sure there are no invalid fields in a submitted campaign
|
// Validate checks to make sure there are no invalid fields in a submitted campaign
|
||||||
func (c *Campaign) Validate() error {
|
func (c *Campaign) Validate() error {
|
||||||
switch {
|
switch {
|
||||||
|
@ -59,8 +66,10 @@ func (c *Campaign) Validate() error {
|
||||||
return ErrTemplateNotSpecified
|
return ErrTemplateNotSpecified
|
||||||
case c.Page.Name == "":
|
case c.Page.Name == "":
|
||||||
return ErrPageNotSpecified
|
return ErrPageNotSpecified
|
||||||
|
case c.SMTP.Name == "":
|
||||||
|
return ErrSMTPNotSpecified
|
||||||
}
|
}
|
||||||
return c.SMTP.Validate()
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// SendTestEmailRequest is the structure of a request
|
// SendTestEmailRequest is the structure of a request
|
||||||
|
@ -80,13 +89,10 @@ type SendTestEmailRequest struct {
|
||||||
// is valid.
|
// is valid.
|
||||||
func (s *SendTestEmailRequest) Validate() error {
|
func (s *SendTestEmailRequest) Validate() error {
|
||||||
switch {
|
switch {
|
||||||
case s.Template.Name == "":
|
|
||||||
return ErrTemplateNotSpecified
|
|
||||||
case s.Email == "":
|
case s.Email == "":
|
||||||
return ErrEmailNotSpecified
|
return ErrEmailNotSpecified
|
||||||
}
|
}
|
||||||
// Finally, check the SMTP settings
|
return nil
|
||||||
return s.SMTP.Validate()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// UpdateStatus changes the campaign status appropriately
|
// UpdateStatus changes the campaign status appropriately
|
||||||
|
@ -137,6 +143,10 @@ func GetCampaigns(uid int64) ([]Campaign, error) {
|
||||||
if err != nil {
|
if err != nil {
|
||||||
Logger.Println(err)
|
Logger.Println(err)
|
||||||
}
|
}
|
||||||
|
err = db.Table("SMTP").Where("id=?", cs[i].SMTPId).Find(&cs[i].SMTP).Error
|
||||||
|
if err != nil {
|
||||||
|
Logger.Println(err)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
return cs, err
|
return cs, err
|
||||||
}
|
}
|
||||||
|
@ -168,6 +178,10 @@ func GetCampaign(id int64, uid int64) (Campaign, error) {
|
||||||
if err != nil {
|
if err != nil {
|
||||||
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
|
||||||
|
if err != nil {
|
||||||
|
Logger.Printf("%s: sending profile not found for campaign\n", err)
|
||||||
|
}
|
||||||
return c, err
|
return c, err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -214,6 +228,17 @@ func PostCampaign(c *Campaign, uid int64) error {
|
||||||
}
|
}
|
||||||
c.Page = p
|
c.Page = p
|
||||||
c.PageId = p.Id
|
c.PageId = p.Id
|
||||||
|
// Check to make sure the sending profile exists
|
||||||
|
s, err := GetSMTPByName(c.SMTP.Name, uid)
|
||||||
|
if err == gorm.RecordNotFound {
|
||||||
|
Logger.Printf("Error - Sending profile %s does not exist", s.Name)
|
||||||
|
return ErrPageNotFound
|
||||||
|
} else if err != nil {
|
||||||
|
Logger.Println(err)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
c.SMTP = s
|
||||||
|
c.SMTPId = s.Id
|
||||||
// Insert into the DB
|
// Insert into the DB
|
||||||
err = db.Save(c).Error
|
err = db.Save(c).Error
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|
|
@ -63,6 +63,40 @@ func (s *ModelsSuite) TestPostGroupNoTargets(c *check.C) {
|
||||||
c.Assert(err, check.Equals, ErrNoTargetsSpecified)
|
c.Assert(err, check.Equals, ErrNoTargetsSpecified)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (s *ModelsSuite) TestPostSMTP(c *check.C) {
|
||||||
|
smtp := SMTP{
|
||||||
|
Name: "Test SMTP",
|
||||||
|
Host: "1.1.1.1:25",
|
||||||
|
FromAddress: "Foo Bar <foo@example.com>",
|
||||||
|
UserId: 1,
|
||||||
|
}
|
||||||
|
err = PostSMTP(&smtp)
|
||||||
|
c.Assert(err, check.Equals, nil)
|
||||||
|
ss, err := GetSMTPs(1)
|
||||||
|
c.Assert(err, check.Equals, nil)
|
||||||
|
c.Assert(len(ss), check.Equals, 1)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *ModelsSuite) TestPostSMTPNoHost(c *check.C) {
|
||||||
|
smtp := SMTP{
|
||||||
|
Name: "Test SMTP",
|
||||||
|
FromAddress: "Foo Bar <foo@example.com>",
|
||||||
|
UserId: 1,
|
||||||
|
}
|
||||||
|
err = PostSMTP(&smtp)
|
||||||
|
c.Assert(err, check.Equals, ErrHostNotSpecified)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *ModelsSuite) TestPostSMTPNoFrom(c *check.C) {
|
||||||
|
smtp := SMTP{
|
||||||
|
Name: "Test SMTP",
|
||||||
|
UserId: 1,
|
||||||
|
Host: "1.1.1.1:25",
|
||||||
|
}
|
||||||
|
err = PostSMTP(&smtp)
|
||||||
|
c.Assert(err, check.Equals, ErrFromAddressNotSpecified)
|
||||||
|
}
|
||||||
|
|
||||||
func (s *ModelsSuite) TestPostPage(c *check.C) {
|
func (s *ModelsSuite) TestPostPage(c *check.C) {
|
||||||
html := `<html>
|
html := `<html>
|
||||||
<head></head>
|
<head></head>
|
||||||
|
|
|
@ -1,16 +1,23 @@
|
||||||
package models
|
package models
|
||||||
|
|
||||||
import "errors"
|
import (
|
||||||
|
"errors"
|
||||||
|
"net/mail"
|
||||||
|
"time"
|
||||||
|
)
|
||||||
|
|
||||||
// SMTP contains the attributes needed to handle the sending of campaign emails
|
// SMTP contains the attributes needed to handle the sending of campaign emails
|
||||||
type SMTP struct {
|
type SMTP struct {
|
||||||
SMTPId int64 `json:"-" gorm:"column:smtp_id; primary_key:yes"`
|
Id int64 `json:"id" gorm:"column:id; primary_key:yes"`
|
||||||
CampaignId int64 `json:"-" gorm:"column:campaign_id"`
|
UserId int64 `json:"-" gorm:"column:user_id"`
|
||||||
Host string `json:"host"`
|
Interface string `json:"interface_type" gorm:"column:interface_type"`
|
||||||
Username string `json:"username,omitempty"`
|
Name string `json:"name"`
|
||||||
Password string `json:"password,omitempty" sql:"-"`
|
Host string `json:"host"`
|
||||||
FromAddress string `json:"from_address"`
|
Username string `json:"username,omitempty"`
|
||||||
IgnoreCertErrors bool `json:"ignore_cert_errors"`
|
Password string `json:"password,omitempty"`
|
||||||
|
FromAddress string `json:"from_address"`
|
||||||
|
IgnoreCertErrors bool `json:"ignore_cert_errors"`
|
||||||
|
ModifiedDate time.Time `json:"modified_date"`
|
||||||
}
|
}
|
||||||
|
|
||||||
// ErrFromAddressNotSpecified is thrown when there is no "From" address
|
// ErrFromAddressNotSpecified is thrown when there is no "From" address
|
||||||
|
@ -34,5 +41,76 @@ func (s *SMTP) Validate() error {
|
||||||
case s.Host == "":
|
case s.Host == "":
|
||||||
return ErrHostNotSpecified
|
return ErrHostNotSpecified
|
||||||
}
|
}
|
||||||
return nil
|
_, err := mail.ParseAddress(s.FromAddress)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetSMTPs returns the SMTPs owned by the given user.
|
||||||
|
func GetSMTPs(uid int64) ([]SMTP, error) {
|
||||||
|
ss := []SMTP{}
|
||||||
|
err := db.Where("user_id=?", uid).Find(&ss).Error
|
||||||
|
if err != nil {
|
||||||
|
Logger.Println(err)
|
||||||
|
}
|
||||||
|
return ss, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetSMTP returns the SMTP, if it exists, specified by the given id and user_id.
|
||||||
|
func GetSMTP(id int64, uid int64) (SMTP, error) {
|
||||||
|
s := SMTP{}
|
||||||
|
err := db.Where("user_id=? and id=?", uid, id).Find(&s).Error
|
||||||
|
if err != nil {
|
||||||
|
Logger.Println(err)
|
||||||
|
}
|
||||||
|
return s, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetSMTPByName returns the SMTP, if it exists, specified by the given name and user_id.
|
||||||
|
func GetSMTPByName(n string, uid int64) (SMTP, error) {
|
||||||
|
s := SMTP{}
|
||||||
|
err := db.Where("user_id=? and name=?", uid, n).Find(&s).Error
|
||||||
|
if err != nil {
|
||||||
|
Logger.Println(err)
|
||||||
|
}
|
||||||
|
return s, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// PostSMTP creates a new SMTP in the database.
|
||||||
|
func PostSMTP(s *SMTP) error {
|
||||||
|
err := s.Validate()
|
||||||
|
if err != nil {
|
||||||
|
Logger.Println(err)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
// Insert into the DB
|
||||||
|
err = db.Save(s).Error
|
||||||
|
if err != nil {
|
||||||
|
Logger.Println(err)
|
||||||
|
}
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
// PutSMTP edits an existing SMTP in the database.
|
||||||
|
// Per the PUT Method RFC, it presumes all data for a SMTP is provided.
|
||||||
|
func PutSMTP(s *SMTP) error {
|
||||||
|
err := s.Validate()
|
||||||
|
if err != nil {
|
||||||
|
Logger.Println(err)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
err = db.Where("id=?", s.Id).Save(s).Error
|
||||||
|
if err != nil {
|
||||||
|
Logger.Println(err)
|
||||||
|
}
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
// DeleteSMTP deletes an existing SMTP in the database.
|
||||||
|
// An error is returned if a SMTP with the given user id and SMTP id is not found.
|
||||||
|
func DeleteSMTP(id int64, uid int64) error {
|
||||||
|
err = db.Where("user_id=?", uid).Delete(SMTP{Id: id}).Error
|
||||||
|
if err != nil {
|
||||||
|
Logger.Println(err)
|
||||||
|
}
|
||||||
|
return err
|
||||||
}
|
}
|
||||||
|
|
Binary file not shown.
|
@ -31,11 +31,7 @@ function launch() {
|
||||||
name: $("#page").val()
|
name: $("#page").val()
|
||||||
},
|
},
|
||||||
smtp: {
|
smtp: {
|
||||||
from_address: $("input[name=from]").val(),
|
name: $("#profile").val()
|
||||||
host: $("input[name=host]").val(),
|
|
||||||
username: $("input[name=username]").val(),
|
|
||||||
password: $("input[name=password]").val(),
|
|
||||||
ignore_cert_errors: $("#ignore_cert_errors").prop("checked")
|
|
||||||
},
|
},
|
||||||
groups: groups
|
groups: groups
|
||||||
}
|
}
|
||||||
|
@ -70,11 +66,7 @@ function sendTestEmail() {
|
||||||
name: $("#page").val()
|
name: $("#page").val()
|
||||||
},
|
},
|
||||||
smtp: {
|
smtp: {
|
||||||
from_address: $("input[name=from]").val(),
|
name: $("#profile").val()
|
||||||
host: $("input[name=host]").val(),
|
|
||||||
username: $("input[name=username]").val(),
|
|
||||||
password: $("input[name=password]").val(),
|
|
||||||
ignore_cert_errors: $("#ignore_cert_errors").prop("checked")
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
btnHtml = $("#sendTestModalSubmit").html()
|
btnHtml = $("#sendTestModalSubmit").html()
|
||||||
|
@ -95,6 +87,12 @@ function sendTestEmail() {
|
||||||
|
|
||||||
function dismiss() {
|
function dismiss() {
|
||||||
$("#modal\\.flashes").empty()
|
$("#modal\\.flashes").empty()
|
||||||
|
$("#name").val("")
|
||||||
|
$("#template").val("")
|
||||||
|
$("#page").val("")
|
||||||
|
$("#url").val("")
|
||||||
|
$("#profile").val("")
|
||||||
|
$("#groupSelect").val("")
|
||||||
$("#modal").modal('hide')
|
$("#modal").modal('hide')
|
||||||
$("#groupTable").dataTable().DataTable().clear().draw()
|
$("#groupTable").dataTable().DataTable().clear().draw()
|
||||||
}
|
}
|
||||||
|
@ -114,6 +112,7 @@ function edit(campaign) {
|
||||||
group_bh.clear();
|
group_bh.clear();
|
||||||
template_bh.clear();
|
template_bh.clear();
|
||||||
page_bh.clear();
|
page_bh.clear();
|
||||||
|
profile_bh.clear();
|
||||||
if (campaign == "new") {
|
if (campaign == "new") {
|
||||||
api.groups.get()
|
api.groups.get()
|
||||||
.success(function(groups) {
|
.success(function(groups) {
|
||||||
|
@ -142,9 +141,79 @@ function edit(campaign) {
|
||||||
page_bh.add(pages)
|
page_bh.add(pages)
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
api.SMTP.get()
|
||||||
|
.success(function(profiles) {
|
||||||
|
if (profiles.length == 0){
|
||||||
|
modalError("No profiles found!")
|
||||||
|
return false
|
||||||
|
} else {
|
||||||
|
profile_bh.add(profiles)
|
||||||
|
}
|
||||||
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function copy(idx) {
|
||||||
|
group_bh.clear();
|
||||||
|
template_bh.clear();
|
||||||
|
page_bh.clear();
|
||||||
|
profile_bh.clear();
|
||||||
|
api.groups.get()
|
||||||
|
.success(function(groups) {
|
||||||
|
if (groups.length == 0) {
|
||||||
|
modalError("No groups found!")
|
||||||
|
return false;
|
||||||
|
} else {
|
||||||
|
group_bh.add(groups)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
api.templates.get()
|
||||||
|
.success(function(templates) {
|
||||||
|
if (templates.length == 0) {
|
||||||
|
modalError("No templates found!")
|
||||||
|
return false
|
||||||
|
} else {
|
||||||
|
template_bh.add(templates)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
api.pages.get()
|
||||||
|
.success(function(pages) {
|
||||||
|
if (pages.length == 0) {
|
||||||
|
modalError("No pages found!")
|
||||||
|
return false
|
||||||
|
} else {
|
||||||
|
page_bh.add(pages)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
api.SMTP.get()
|
||||||
|
.success(function(profiles) {
|
||||||
|
if (profiles.length == 0) {
|
||||||
|
modalError("No profiles found!")
|
||||||
|
return false
|
||||||
|
} else {
|
||||||
|
profile_bh.add(profiles)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
// Set our initial values
|
||||||
|
var campaign = campaigns[idx]
|
||||||
|
$("#name").val("Copy of " + campaign.name)
|
||||||
|
$("#template").val(campaign.template.name)
|
||||||
|
$("#page").val(campaign.page.name)
|
||||||
|
$("#profile").val(campaign.smtp.name)
|
||||||
|
$("#url").val(campaign.url)
|
||||||
|
$.each(campaign.groups, function(i, group){
|
||||||
|
groupTable.row.add([
|
||||||
|
group.name,
|
||||||
|
'<span style="cursor:pointer;"><i class="fa fa-trash-o"></i></span>'
|
||||||
|
]).draw()
|
||||||
|
$("#groupTable").on("click", "span>i.fa-trash-o", function() {
|
||||||
|
groupTable.row($(this).parents('tr'))
|
||||||
|
.remove()
|
||||||
|
.draw();
|
||||||
|
})
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
$(document).ready(function() {
|
$(document).ready(function() {
|
||||||
// Setup multiple modals
|
// Setup multiple modals
|
||||||
// Code based on http://miles-by-motorcycle.com/static/bootstrap-modal/index.html
|
// Code based on http://miles-by-motorcycle.com/static/bootstrap-modal/index.html
|
||||||
|
@ -183,6 +252,9 @@ $(document).ready(function() {
|
||||||
}
|
}
|
||||||
}, this));
|
}, this));
|
||||||
};
|
};
|
||||||
|
$('#modal').on('hidden.bs.modal', function(event) {
|
||||||
|
dismiss()
|
||||||
|
});
|
||||||
api.campaigns.get()
|
api.campaigns.get()
|
||||||
.success(function(cs) {
|
.success(function(cs) {
|
||||||
campaigns = cs
|
campaigns = cs
|
||||||
|
@ -204,6 +276,9 @@ $(document).ready(function() {
|
||||||
"<div class='pull-right'><a class='btn btn-primary' href='/campaigns/" + campaign.id + "' data-toggle='tooltip' data-placement='left' title='View Results'>\
|
"<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>\
|
||||||
|
<span data-toggle='modal' data-target='#modal'><button class='btn btn-primary' data-toggle='tooltip' data-placement='left' title='Copy Campaign' onclick='copy(" + i + ")'>\
|
||||||
|
<i class='fa fa-copy'></i>\
|
||||||
|
</button></span>\
|
||||||
<button class='btn btn-danger' onclick='deleteCampaign(" + i + ")' data-toggle='tooltip' data-placement='left' title='Delete Campaign'>\
|
<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>"
|
||||||
|
@ -329,4 +404,35 @@ $(document).ready(function() {
|
||||||
.bind('typeahead:autocomplete', function(ev, page) {
|
.bind('typeahead:autocomplete', function(ev, page) {
|
||||||
$("#page").typeahead('val', page.name)
|
$("#page").typeahead('val', page.name)
|
||||||
});
|
});
|
||||||
|
// Create the sending profile typeahead objects
|
||||||
|
profile_bh = new Bloodhound({
|
||||||
|
datumTokenizer: function(s) {
|
||||||
|
return Bloodhound.tokenizers.whitespace(s.name)
|
||||||
|
},
|
||||||
|
queryTokenizer: Bloodhound.tokenizers.whitespace,
|
||||||
|
local: []
|
||||||
|
})
|
||||||
|
profile_bh.initialize()
|
||||||
|
$("#profile.typeahead.form-control").typeahead({
|
||||||
|
hint: true,
|
||||||
|
highlight: true,
|
||||||
|
minLength: 1
|
||||||
|
}, {
|
||||||
|
name: "profiles",
|
||||||
|
source: profile_bh,
|
||||||
|
templates: {
|
||||||
|
empty: function(data) {
|
||||||
|
return '<div class="tt-suggestion">No profiles matched that query</div>'
|
||||||
|
},
|
||||||
|
suggestion: function(data) {
|
||||||
|
return '<div>' + data.name + '</div>'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.bind('typeahead:select', function(ev, profile) {
|
||||||
|
$("#profile").typeahead('val', profile.name)
|
||||||
|
})
|
||||||
|
.bind('typeahead:autocomplete', function(ev, profile) {
|
||||||
|
$("#profile").typeahead('val', profile.name)
|
||||||
|
});
|
||||||
})
|
})
|
||||||
|
|
|
@ -39,9 +39,10 @@ function dismiss() {
|
||||||
$("#modal\\.flashes").empty()
|
$("#modal\\.flashes").empty()
|
||||||
$("#name").val("")
|
$("#name").val("")
|
||||||
$("#html_editor").val("")
|
$("#html_editor").val("")
|
||||||
$("#newLandingPageModal").find("input[type='checkbox']").prop("checked", false)
|
$("#url").val("")
|
||||||
|
$("#modal").find("input[type='checkbox']").prop("checked", false)
|
||||||
$("#capture_passwords").hide()
|
$("#capture_passwords").hide()
|
||||||
$("#newLandingPageModal").modal('hide')
|
$("#modal").modal('hide')
|
||||||
}
|
}
|
||||||
|
|
||||||
function deletePage(idx) {
|
function deletePage(idx) {
|
||||||
|
@ -127,10 +128,10 @@ function load() {
|
||||||
pagesTable.row.add([
|
pagesTable.row.add([
|
||||||
page.name,
|
page.name,
|
||||||
moment(page.modified_date).format('MMMM Do YYYY, h:mm:ss a'),
|
moment(page.modified_date).format('MMMM Do YYYY, h:mm:ss a'),
|
||||||
"<div class='pull-right'><span data-toggle='modal' data-target='#newLandingPageModal'><button class='btn btn-primary' data-toggle='tooltip' data-placement='left' title='Edit Page' onclick='edit(" + i + ")'>\
|
"<div class='pull-right'><span data-toggle='modal' data-target='#modal'><button class='btn btn-primary' data-toggle='tooltip' data-placement='left' title='Edit Page' onclick='edit(" + i + ")'>\
|
||||||
<i class='fa fa-pencil'></i>\
|
<i class='fa fa-pencil'></i>\
|
||||||
</button></span>\
|
</button></span>\
|
||||||
<span data-toggle='modal' data-target='#newLandingPageModal'><button class='btn btn-primary' data-toggle='tooltip' data-placement='left' title='Copy Page' onclick='copy(" + i + ")'>\
|
<span data-toggle='modal' data-target='#modal'><button class='btn btn-primary' data-toggle='tooltip' data-placement='left' title='Copy Page' onclick='copy(" + i + ")'>\
|
||||||
<i class='fa fa-copy'></i>\
|
<i class='fa fa-copy'></i>\
|
||||||
</button></span>\
|
</button></span>\
|
||||||
<button class='btn btn-danger' data-toggle='tooltip' data-placement='left' title='Delete Page' onclick='deletePage(" + i + ")'>\
|
<button class='btn btn-danger' data-toggle='tooltip' data-placement='left' title='Delete Page' onclick='deletePage(" + i + ")'>\
|
||||||
|
@ -187,6 +188,9 @@ $(document).ready(function() {
|
||||||
}
|
}
|
||||||
}, this));
|
}, this));
|
||||||
};
|
};
|
||||||
|
$('#modal').on('hidden.bs.modal', function(event) {
|
||||||
|
dismiss()
|
||||||
|
});
|
||||||
$("#capture_credentials_checkbox").change(function(){
|
$("#capture_credentials_checkbox").change(function(){
|
||||||
$("#capture_passwords").toggle()
|
$("#capture_passwords").toggle()
|
||||||
})
|
})
|
||||||
|
|
|
@ -0,0 +1,209 @@
|
||||||
|
var profiles = []
|
||||||
|
|
||||||
|
// Attempts to send a test email by POSTing to /campaigns/
|
||||||
|
function sendTestEmail() {
|
||||||
|
var test_email_request = {
|
||||||
|
template: {},
|
||||||
|
first_name: $("input[name=to_first_name]").val(),
|
||||||
|
last_name: $("input[name=to_last_name]").val(),
|
||||||
|
email: $("input[name=to_email]").val(),
|
||||||
|
position: $("input[name=to_position]").val(),
|
||||||
|
url: '',
|
||||||
|
smtp: {
|
||||||
|
from_address: $("#from").val(),
|
||||||
|
host: $("#host").val(),
|
||||||
|
username: $("#username").val(),
|
||||||
|
password: $("#password").val(),
|
||||||
|
ignore_cert_errors: $("#ignore_cert_errors").prop("checked")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
btnHtml = $("#sendTestModalSubmit").html()
|
||||||
|
$("#sendTestModalSubmit").html('<i class="fa fa-spinner fa-spin"></i> Sending')
|
||||||
|
// Send the test email
|
||||||
|
api.send_test_email(test_email_request)
|
||||||
|
.success(function(data) {
|
||||||
|
$("#sendTestEmailModal\\.flashes").empty().append("<div style=\"text-align:center\" class=\"alert alert-success\">\
|
||||||
|
<i class=\"fa fa-check-circle\"></i> Email Sent!</div>")
|
||||||
|
$("#sendTestModalSubmit").html(btnHtml)
|
||||||
|
})
|
||||||
|
.error(function(data) {
|
||||||
|
$("#sendTestEmailModal\\.flashes").empty().append("<div style=\"text-align:center\" class=\"alert alert-danger\">\
|
||||||
|
<i class=\"fa fa-exclamation-circle\"></i> " + data.responseJSON.message + "</div>")
|
||||||
|
$("#sendTestModalSubmit").html(btnHtml)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// Save attempts to POST to /smtp/
|
||||||
|
function save(idx) {
|
||||||
|
var profile = {}
|
||||||
|
profile.name = $("#name").val()
|
||||||
|
profile.interface_type = $("#interface_type").val()
|
||||||
|
profile.from_address = $("#from").val()
|
||||||
|
profile.host = $("#host").val()
|
||||||
|
profile.username = $("#username").val()
|
||||||
|
profile.password = $("#password").val()
|
||||||
|
profile.ignore_cert_errors = $("#ignore_cert_errors").prop("checked")
|
||||||
|
if (idx != -1) {
|
||||||
|
profile.id = profiles[idx].id
|
||||||
|
api.SMTPId.put(profile)
|
||||||
|
.success(function(data) {
|
||||||
|
successFlash("Profile edited successfully!")
|
||||||
|
load()
|
||||||
|
dismiss()
|
||||||
|
})
|
||||||
|
} else {
|
||||||
|
// Submit the profile
|
||||||
|
api.SMTP.post(profile)
|
||||||
|
.success(function(data) {
|
||||||
|
successFlash("Profile added successfully!")
|
||||||
|
load()
|
||||||
|
dismiss()
|
||||||
|
})
|
||||||
|
.error(function(data) {
|
||||||
|
modalError(data.responseJSON.message)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function dismiss() {
|
||||||
|
$("#modal\\.flashes").empty()
|
||||||
|
$("#name").val("")
|
||||||
|
$("#interface_type").val("SMTP")
|
||||||
|
$("#from").val("")
|
||||||
|
$("#host").val("")
|
||||||
|
$("#username").val("")
|
||||||
|
$("#password").val("")
|
||||||
|
$("#ignore_cert_errors").prop("checked", true)
|
||||||
|
$("#modal").modal('hide')
|
||||||
|
}
|
||||||
|
|
||||||
|
function deleteProfile(idx) {
|
||||||
|
if (confirm("Delete " + profiles[idx].name + "?")) {
|
||||||
|
api.SMTPId.delete(profiles[idx].id)
|
||||||
|
.success(function(data) {
|
||||||
|
successFlash(data.message)
|
||||||
|
load()
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function edit(idx) {
|
||||||
|
$("#modalSubmit").unbind('click').click(function() {
|
||||||
|
save(idx)
|
||||||
|
})
|
||||||
|
var profile = {}
|
||||||
|
if (idx != -1) {
|
||||||
|
profile = profiles[idx]
|
||||||
|
$("#name").val(profile.name)
|
||||||
|
$("#interface_type").val(profile.interface_type)
|
||||||
|
$("#from").val(profile.from_address)
|
||||||
|
$("#host").val(profile.host)
|
||||||
|
$("#username").val(profile.username)
|
||||||
|
$("#password").val(profile.password)
|
||||||
|
$("#ignore_cert_errors").prop("checked", profile.ignore_cert_errors)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function copy(idx) {
|
||||||
|
$("#modalSubmit").unbind('click').click(function() {
|
||||||
|
save(-1)
|
||||||
|
})
|
||||||
|
var profile = {}
|
||||||
|
profile = profiles[idx]
|
||||||
|
$("#name").val("Copy of " + profile.name)
|
||||||
|
$("#interface_type").val(profile.interface_type)
|
||||||
|
$("#from").val(profile.from_address)
|
||||||
|
$("#host").val(profile.host)
|
||||||
|
$("#username").val(profile.username)
|
||||||
|
$("#password").val(profile.password)
|
||||||
|
$("#ignore_cert_errors").prop("checked", profile.ignore_cert_errors)
|
||||||
|
}
|
||||||
|
|
||||||
|
function load() {
|
||||||
|
$("#profileTable").hide()
|
||||||
|
$("#emptyMessage").hide()
|
||||||
|
$("#loading").show()
|
||||||
|
api.SMTP.get()
|
||||||
|
.success(function(ss) {
|
||||||
|
profiles = ss
|
||||||
|
$("#loading").hide()
|
||||||
|
if (profiles.length > 0) {
|
||||||
|
$("#profileTable").show()
|
||||||
|
profileTable = $("#profileTable").DataTable({
|
||||||
|
destroy: true,
|
||||||
|
columnDefs: [{
|
||||||
|
orderable: false,
|
||||||
|
targets: "no-sort"
|
||||||
|
}]
|
||||||
|
});
|
||||||
|
profileTable.clear()
|
||||||
|
$.each(profiles, function(i, profile) {
|
||||||
|
profileTable.row.add([
|
||||||
|
profile.name,
|
||||||
|
profile.interface_type,
|
||||||
|
moment(profile.modified_date).format('MMMM Do YYYY, h:mm:ss a'),
|
||||||
|
"<div class='pull-right'><span data-toggle='modal' data-target='#modal'><button class='btn btn-primary' data-toggle='tooltip' data-placement='left' title='Edit Profile' onclick='edit(" + i + ")'>\
|
||||||
|
<i class='fa fa-pencil'></i>\
|
||||||
|
</button></span>\
|
||||||
|
<span data-toggle='modal' data-target='#modal'><button class='btn btn-primary' data-toggle='tooltip' data-placement='left' title='Copy Profile' onclick='copy(" + i + ")'>\
|
||||||
|
<i class='fa fa-copy'></i>\
|
||||||
|
</button></span>\
|
||||||
|
<button class='btn btn-danger' data-toggle='tooltip' data-placement='left' title='Delete Profile' onclick='deleteProfile(" + i + ")'>\
|
||||||
|
<i class='fa fa-trash-o'></i>\
|
||||||
|
</button></div>"
|
||||||
|
]).draw()
|
||||||
|
})
|
||||||
|
$('[data-toggle="tooltip"]').tooltip()
|
||||||
|
} else {
|
||||||
|
$("#emptyMessage").show()
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.error(function() {
|
||||||
|
$("#loading").hide()
|
||||||
|
errorFlash("Error fetching profiles")
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
$(document).ready(function() {
|
||||||
|
// Setup multiple modals
|
||||||
|
// Code based on http://miles-by-motorcycle.com/static/bootstrap-modal/index.html
|
||||||
|
$('.modal').on('hidden.bs.modal', function(event) {
|
||||||
|
$(this).removeClass('fv-modal-stack');
|
||||||
|
$('body').data('fv_open_modals', $('body').data('fv_open_modals') - 1);
|
||||||
|
});
|
||||||
|
$('.modal').on('shown.bs.modal', function(event) {
|
||||||
|
// Keep track of the number of open modals
|
||||||
|
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')) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
$(this).addClass('fv-modal-stack');
|
||||||
|
// Increment the number of open modals
|
||||||
|
$('body').data('fv_open_modals', $('body').data('fv_open_modals') + 1);
|
||||||
|
// Setup the appropriate z-index
|
||||||
|
$(this).css('z-index', 1040 + (10 * $('body').data('fv_open_modals')));
|
||||||
|
$('.modal-backdrop').not('.fv-modal-stack').css('z-index', 1039 + (10 * $('body').data('fv_open_modals')));
|
||||||
|
$('.modal-backdrop').not('fv-modal-stack').addClass('fv-modal-stack');
|
||||||
|
});
|
||||||
|
$.fn.modal.Constructor.prototype.enforceFocus = function() {
|
||||||
|
$(document)
|
||||||
|
.off('focusin.bs.modal') // guard against infinite focus loop
|
||||||
|
.on('focusin.bs.modal', $.proxy(function(e) {
|
||||||
|
if (
|
||||||
|
this.$element[0] !== e.target && !this.$element.has(e.target).length
|
||||||
|
// CKEditor compatibility fix start.
|
||||||
|
&& !$(e.target).closest('.cke_dialog, .cke').length
|
||||||
|
// CKEditor compatibility fix end.
|
||||||
|
) {
|
||||||
|
this.$element.trigger('focus');
|
||||||
|
}
|
||||||
|
}, this));
|
||||||
|
};
|
||||||
|
$('#modal').on('hidden.bs.modal', function(event) {
|
||||||
|
dismiss()
|
||||||
|
});
|
||||||
|
load()
|
||||||
|
})
|
|
@ -64,6 +64,7 @@ function dismiss() {
|
||||||
$("#modal\\.flashes").empty()
|
$("#modal\\.flashes").empty()
|
||||||
$("#attachmentsTable").dataTable().DataTable().clear().draw()
|
$("#attachmentsTable").dataTable().DataTable().clear().draw()
|
||||||
$("#name").val("")
|
$("#name").val("")
|
||||||
|
$("#subject").val("")
|
||||||
$("#text_editor").val("")
|
$("#text_editor").val("")
|
||||||
$("#html_editor").val("")
|
$("#html_editor").val("")
|
||||||
$("#modal").modal('hide')
|
$("#modal").modal('hide')
|
||||||
|
@ -320,5 +321,8 @@ $(document).ready(function() {
|
||||||
}
|
}
|
||||||
}, this));
|
}, this));
|
||||||
};
|
};
|
||||||
|
$('#modal').on('hidden.bs.modal', function(event) {
|
||||||
|
dismiss()
|
||||||
|
});
|
||||||
load()
|
load()
|
||||||
})
|
})
|
||||||
|
|
|
@ -58,7 +58,7 @@ var api = {
|
||||||
get: function() {
|
get: function() {
|
||||||
return query("/groups/", "GET", {}, false)
|
return query("/groups/", "GET", {}, false)
|
||||||
},
|
},
|
||||||
// post() - Posts a campaign to POST /groups
|
// post() - Posts a group to POST /groups
|
||||||
post: function(group) {
|
post: function(group) {
|
||||||
return query("/groups/", "POST", group, false)
|
return query("/groups/", "POST", group, false)
|
||||||
}
|
}
|
||||||
|
@ -69,11 +69,11 @@ var api = {
|
||||||
get: function(id) {
|
get: function(id) {
|
||||||
return query("/groups/" + id, "GET", {}, false)
|
return query("/groups/" + id, "GET", {}, false)
|
||||||
},
|
},
|
||||||
// put() - Puts a campaign to PUT /groups/:id
|
// put() - Puts a group to PUT /groups/:id
|
||||||
put: function(group) {
|
put: function(group) {
|
||||||
return query("/groups/" + group.id, "PUT", group, false)
|
return query("/groups/" + group.id, "PUT", group, false)
|
||||||
},
|
},
|
||||||
// delete() - Deletes a campaign at DELETE /groups/:id
|
// delete() - Deletes a group at DELETE /groups/:id
|
||||||
delete: function(id) {
|
delete: function(id) {
|
||||||
return query("/groups/" + id, "DELETE", {}, false)
|
return query("/groups/" + id, "DELETE", {}, false)
|
||||||
}
|
}
|
||||||
|
@ -84,7 +84,7 @@ var api = {
|
||||||
get: function() {
|
get: function() {
|
||||||
return query("/templates/", "GET", {}, false)
|
return query("/templates/", "GET", {}, false)
|
||||||
},
|
},
|
||||||
// post() - Posts a campaign to POST /templates
|
// post() - Posts a template to POST /templates
|
||||||
post: function(template) {
|
post: function(template) {
|
||||||
return query("/templates/", "POST", template, false)
|
return query("/templates/", "POST", template, false)
|
||||||
}
|
}
|
||||||
|
@ -95,11 +95,11 @@ var api = {
|
||||||
get: function(id) {
|
get: function(id) {
|
||||||
return query("/templates/" + id, "GET", {}, false)
|
return query("/templates/" + id, "GET", {}, false)
|
||||||
},
|
},
|
||||||
// put() - Puts a campaign to PUT /templates/:id
|
// put() - Puts a template to PUT /templates/:id
|
||||||
put: function(template) {
|
put: function(template) {
|
||||||
return query("/templates/" + template.id, "PUT", template, false)
|
return query("/templates/" + template.id, "PUT", template, false)
|
||||||
},
|
},
|
||||||
// delete() - Deletes a campaign at DELETE /templates/:id
|
// delete() - Deletes a template at DELETE /templates/:id
|
||||||
delete: function(id) {
|
delete: function(id) {
|
||||||
return query("/templates/" + id, "DELETE", {}, false)
|
return query("/templates/" + id, "DELETE", {}, false)
|
||||||
}
|
}
|
||||||
|
@ -110,26 +110,52 @@ var api = {
|
||||||
get: function() {
|
get: function() {
|
||||||
return query("/pages/", "GET", {}, false)
|
return query("/pages/", "GET", {}, false)
|
||||||
},
|
},
|
||||||
// post() - Posts a campaign to POST /pages
|
// post() - Posts a page to POST /pages
|
||||||
post: function(page) {
|
post: function(page) {
|
||||||
return query("/pages/", "POST", page, false)
|
return query("/pages/", "POST", page, false)
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
// templateId contains the endpoints for /templates/:id
|
// pageId contains the endpoints for /pages/:id
|
||||||
pageId: {
|
pageId: {
|
||||||
// get() - Queries the API for GET /templates/:id
|
// get() - Queries the API for GET /pages/:id
|
||||||
get: function(id) {
|
get: function(id) {
|
||||||
return query("/pages/" + id, "GET", {}, false)
|
return query("/pages/" + id, "GET", {}, false)
|
||||||
},
|
},
|
||||||
// put() - Puts a campaign to PUT /templates/:id
|
// put() - Puts a page to PUT /pages/:id
|
||||||
put: function(page) {
|
put: function(page) {
|
||||||
return query("/pages/" + page.id, "PUT", page, false)
|
return query("/pages/" + page.id, "PUT", page, false)
|
||||||
},
|
},
|
||||||
// delete() - Deletes a campaign at DELETE /templates/:id
|
// delete() - Deletes a page at DELETE /pages/:id
|
||||||
delete: function(id) {
|
delete: function(id) {
|
||||||
return query("/pages/" + id, "DELETE", {}, false)
|
return query("/pages/" + id, "DELETE", {}, false)
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
// SMTP contains the endpoints for /smtp
|
||||||
|
SMTP: {
|
||||||
|
// get() - Queries the API for GET /smtp
|
||||||
|
get: function() {
|
||||||
|
return query("/smtp/", "GET", {}, false)
|
||||||
|
},
|
||||||
|
// post() - Posts a SMTP to POST /smtp
|
||||||
|
post: function(smtp) {
|
||||||
|
return query("/smtp/", "POST", smtp, false)
|
||||||
|
}
|
||||||
|
},
|
||||||
|
// SMTPId contains the endpoints for /smtp/:id
|
||||||
|
SMTPId: {
|
||||||
|
// get() - Queries the API for GET /smtp/:id
|
||||||
|
get: function(id) {
|
||||||
|
return query("/smtp/" + id, "GET", {}, false)
|
||||||
|
},
|
||||||
|
// put() - Puts a SMTP to PUT /smtp/:id
|
||||||
|
put: function(smtp) {
|
||||||
|
return query("/smtp/" + smtp.id, "PUT", smtp, false)
|
||||||
|
},
|
||||||
|
// delete() - Deletes a SMTP at DELETE /smtp/:id
|
||||||
|
delete: function(id) {
|
||||||
|
return query("/smtp/" + id, "DELETE", {}, false)
|
||||||
|
}
|
||||||
|
},
|
||||||
// import handles all of the "import" functions in the api
|
// import handles all of the "import" functions in the api
|
||||||
import_email: function(raw) {
|
import_email: function(raw) {
|
||||||
return query("/import/email", "POST", {}, false)
|
return query("/import/email", "POST", {}, false)
|
||||||
|
|
|
@ -63,9 +63,9 @@
|
||||||
</li>
|
</li>
|
||||||
<li><a href="/landing_pages">Landing Pages</a>
|
<li><a href="/landing_pages">Landing Pages</a>
|
||||||
</li>
|
</li>
|
||||||
<li><a href="/settings">Settings</a>
|
<li><a href="/sending_profiles">Sending Profiles</a>
|
||||||
</li>
|
</li>
|
||||||
<li><a href="/api/">API Documentation</a>
|
<li><a href="/settings">Settings</a>
|
||||||
</li>
|
</li>
|
||||||
<li>
|
<li>
|
||||||
{{if .User}}
|
{{if .User}}
|
||||||
|
|
|
@ -13,8 +13,13 @@
|
||||||
</li>
|
</li>
|
||||||
<li><a href="/landing_pages">Landing Pages</a>
|
<li><a href="/landing_pages">Landing Pages</a>
|
||||||
</li>
|
</li>
|
||||||
|
<li><a href="/sending_profiles">Sending Profiles</a>
|
||||||
|
</li>
|
||||||
<li><a href="/settings">Settings</a>
|
<li><a href="/settings">Settings</a>
|
||||||
</li>
|
</li>
|
||||||
|
<li><hr></li>
|
||||||
|
<li><a href="/gophish_user_guide.pdf">User Guide</a>
|
||||||
|
</li>
|
||||||
<li><a href="/api/">API Documentation</a>
|
<li><a href="/api/">API Documentation</a>
|
||||||
</li>
|
</li>
|
||||||
</ul>
|
</ul>
|
||||||
|
|
|
@ -13,8 +13,13 @@
|
||||||
</li>
|
</li>
|
||||||
<li><a href="/landing_pages">Landing Pages</a>
|
<li><a href="/landing_pages">Landing Pages</a>
|
||||||
</li>
|
</li>
|
||||||
|
<li><a href="/sending_profiles">Sending Profiles</a>
|
||||||
|
</li>
|
||||||
<li><a href="/settings">Settings</a>
|
<li><a href="/settings">Settings</a>
|
||||||
</li>
|
</li>
|
||||||
|
<li><hr></li>
|
||||||
|
<li><a href="/gophish_user_guide.pdf">User Guide</a>
|
||||||
|
</li>
|
||||||
<li><a href="/api/">API Documentation</a>
|
<li><a href="/api/">API Documentation</a>
|
||||||
</li>
|
</li>
|
||||||
</ul>
|
</ul>
|
||||||
|
@ -60,7 +65,7 @@
|
||||||
<div class="modal-dialog" role="document">
|
<div class="modal-dialog" role="document">
|
||||||
<div class="modal-content">
|
<div class="modal-content">
|
||||||
<div class="modal-header">
|
<div class="modal-header">
|
||||||
<button type="button" class="close" data-dismiss="modal" aria-label="Close"><span aria-hidden="true">×</span></button>
|
<button type="button" class="close" data-dismiss="modal" aria-label="Close" onclick="dismiss()"><span aria-hidden="true">×</span></button>
|
||||||
<h4 class="modal-title" id="campaignModalLabel">New Campaign</h4>
|
<h4 class="modal-title" id="campaignModalLabel">New Campaign</h4>
|
||||||
</div>
|
</div>
|
||||||
<div class="modal-body">
|
<div class="modal-body">
|
||||||
|
@ -76,34 +81,13 @@
|
||||||
<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"/>
|
||||||
<br/>
|
<label class="control-label" for="profile">Sending Profile:</label>
|
||||||
<div class="panel panel-default">
|
<div class="input-group">
|
||||||
<div class="panel-heading" role="tab">
|
<input type="text" class="typeahead form-control" placeholder="Sending Profile" id="profile"/>
|
||||||
<a role="button" class="collapsed" data-toggle="collapse" href="#smtpPanel" aria-expanded="false" aria-controls="#smtpPanel">
|
<span class="input-group-btn">
|
||||||
SMTP Options <i class="pull-right glyphicon"></i>
|
<button type="button" data-toggle="modal" data-target="#sendTestEmailModal" class="btn btn-primary typeahead-button"><i class="fa fa-envelope"></i> Send Test Email</button>
|
||||||
</a>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
<div class="panel-collapse collapse" id="smtpPanel" role="tabpanel">
|
|
||||||
<div class="panel-body">
|
|
||||||
<label class="control-label" for="from">From:</label>
|
|
||||||
<input type="text" class="form-control" placeholder="First Last <test@example.com>" value="" name="from">
|
|
||||||
<br />
|
|
||||||
<label class="control-label" for="smtp_server">Host:</label>
|
|
||||||
<input type="text" class="form-control" placeholder="smtp.example.com:25" value="" name="host">
|
|
||||||
<br />
|
|
||||||
<label class="control-label" for="smtp_server">Username:</label>
|
|
||||||
<input type="text" class="form-control" placeholder="Username" value="" name="username">
|
|
||||||
<br />
|
|
||||||
<label class="control-label" for="smtp_server">Password:</label>
|
|
||||||
<input type="password" class="form-control" placeholder="Password" value="" name="password">
|
|
||||||
<div class="checkbox checkbox-primary">
|
|
||||||
<input id="ignore_cert_errors" type="checkbox" checked>
|
|
||||||
<label for="ignore_cert_errors">Ignore Certificate Errors <i class="fa fa-question-circle" data-toggle="tooltip" data-placement="right" title="Ignore common certificate errors such as self-signed certs (exposes you to MiTM attacks - use carefully!)"></i></label>
|
|
||||||
</div>
|
|
||||||
<button type="button" data-toggle="modal" data-target="#sendTestEmailModal" class="btn btn-primary"><i class="fa fa-envelope"></i> Send Test Email</button>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<label class="control-label" for="users">Groups:</label>
|
<label class="control-label" for="users">Groups:</label>
|
||||||
<form id="groupForm">
|
<form id="groupForm">
|
||||||
<div class="input-group">
|
<div class="input-group">
|
||||||
|
@ -124,7 +108,7 @@
|
||||||
</div>
|
</div>
|
||||||
</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" onclick="dismiss()">Close</button>
|
||||||
<button type="button" id="launchButton" class="btn btn-primary" onclick="launch()"><i class="fa fa-rocket"></i> Launch Campaign</button>
|
<button type="button" id="launchButton" class="btn btn-primary" onclick="launch()"><i class="fa fa-rocket"></i> Launch Campaign</button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
@ -13,8 +13,13 @@
|
||||||
</li>
|
</li>
|
||||||
<li><a href="/landing_pages">Landing Pages</a>
|
<li><a href="/landing_pages">Landing Pages</a>
|
||||||
</li>
|
</li>
|
||||||
|
<li><a href="/sending_profiles">Sending Profiles</a>
|
||||||
|
</li>
|
||||||
<li><a href="/settings">Settings</a>
|
<li><a href="/settings">Settings</a>
|
||||||
</li>
|
</li>
|
||||||
|
<li><hr></li>
|
||||||
|
<li><a href="/gophish_user_guide.pdf">User Guide</a>
|
||||||
|
</li>
|
||||||
<li><a href="/api/">API Documentation</a>
|
<li><a href="/api/">API Documentation</a>
|
||||||
</li>
|
</li>
|
||||||
</ul>
|
</ul>
|
||||||
|
|
|
@ -13,8 +13,13 @@
|
||||||
</li>
|
</li>
|
||||||
<li class="active"><a href="/landing_pages">Landing Pages</a>
|
<li class="active"><a href="/landing_pages">Landing Pages</a>
|
||||||
</li>
|
</li>
|
||||||
|
<li><a href="/sending_profiles">Sending Profiles</a>
|
||||||
|
</li>
|
||||||
<li><a href="/settings">Settings</a>
|
<li><a href="/settings">Settings</a>
|
||||||
</li>
|
</li>
|
||||||
|
<li><hr></li>
|
||||||
|
<li><a href="/gophish_user_guide.pdf">User Guide</a>
|
||||||
|
</li>
|
||||||
<li><a href="/api/">API Documentation</a>
|
<li><a href="/api/">API Documentation</a>
|
||||||
</li>
|
</li>
|
||||||
</ul>
|
</ul>
|
||||||
|
@ -27,7 +32,7 @@
|
||||||
</h1>
|
</h1>
|
||||||
<div id="flashes" class="row"></div>
|
<div id="flashes" class="row"></div>
|
||||||
<div class="row">
|
<div class="row">
|
||||||
<button type="button" class="btn btn-primary" onclick="edit(-1)" data-toggle="modal" data-target="#newLandingPageModal"><i class="fa fa-plus"></i> New Page</button>
|
<button type="button" class="btn btn-primary" onclick="edit(-1)" data-toggle="modal" data-target="#modal"><i class="fa fa-plus"></i> New Page</button>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div id="loading">
|
<div id="loading">
|
||||||
|
@ -53,13 +58,13 @@
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<!-- Modal -->
|
<!-- Modal -->
|
||||||
<div class="modal fade" id="newLandingPageModal" tabindex="-1" role="dialog" aria-labelledby="modalLabel">
|
<div class="modal fade" id="modal" tabindex="-1" role="dialog" aria-labelledby="modalLabel">
|
||||||
<div class="modal-dialog" role="document">
|
<div class="modal-dialog" role="document">
|
||||||
<div class="modal-content">
|
<div class="modal-content">
|
||||||
<!-- New Template Modal -->
|
<!-- New Template Modal -->
|
||||||
<div class="modal-header">
|
<div class="modal-header">
|
||||||
<button type="button" class="close" data-dismiss="modal" aria-label="Close"><span aria-hidden="true">×</span></button>
|
<button type="button" class="close" data-dismiss="modal" aria-label="Close" onclick="dismiss()"><span aria-hidden="true">×</span></button>
|
||||||
<h4 class="modal-title" id="pageModalLabel">New Landing Page</h4>
|
<h4 class="modal-title" id="modalLabel">New Landing Page</h4>
|
||||||
</div>
|
</div>
|
||||||
<div class="modal-body">
|
<div class="modal-body">
|
||||||
<div class="row" id="modal.flashes"></div>
|
<div class="row" id="modal.flashes"></div>
|
||||||
|
@ -100,7 +105,7 @@
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<!-- Modal -->
|
<!-- Modal -->
|
||||||
<div class="modal fade" id="importSiteModal" tabindex="-1" role="dialog" aria-labelledby="modalLabel">
|
<div class="modal fade" id="importSiteModal" tabindex="-1" role="dialog" aria-labelledby="importSiteModalLabel">
|
||||||
<div class="modal-dialog" role="document">
|
<div class="modal-dialog" role="document">
|
||||||
<div class="modal-content">
|
<div class="modal-content">
|
||||||
<!-- New Template Modal -->
|
<!-- New Template Modal -->
|
||||||
|
|
|
@ -0,0 +1,142 @@
|
||||||
|
{{define "body"}}
|
||||||
|
<div class="container-fluid">
|
||||||
|
<div class="row">
|
||||||
|
<div class="col-sm-3 col-md-2 sidebar">
|
||||||
|
<ul class="nav nav-sidebar">
|
||||||
|
<li><a href="/">Dashboard</a>
|
||||||
|
</li>
|
||||||
|
<li><a href="/campaigns">Campaigns</a>
|
||||||
|
</li>
|
||||||
|
<li><a href="/users">Users & Groups</a>
|
||||||
|
</li>
|
||||||
|
<li><a href="/templates">Email Templates</a>
|
||||||
|
</li>
|
||||||
|
<li><a href="/landing_pages">Landing Pages</a>
|
||||||
|
</li>
|
||||||
|
<li class="active"><a href="/sending_profiles">Sending Profiles</a>
|
||||||
|
</li>
|
||||||
|
<li><a href="/settings">Settings</a>
|
||||||
|
</li>
|
||||||
|
<li><hr></li>
|
||||||
|
<li><a href="/gophish_user_guide.pdf">User Guide</a>
|
||||||
|
</li>
|
||||||
|
<li><a href="/api/">API Documentation</a>
|
||||||
|
</li>
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="col-sm-9 col-sm-offset-3 col-md-10 col-md-offset-2 main">
|
||||||
|
<h1 class="page-header">
|
||||||
|
Sending Profiles
|
||||||
|
</h1>
|
||||||
|
<div id="flashes" class="row"></div>
|
||||||
|
<div class="row">
|
||||||
|
<button type="button" class="btn btn-primary" onclick="edit(-1)" data-toggle="modal" data-target="#modal"><i class="fa fa-plus"></i> New Profile</button>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div id="loading">
|
||||||
|
<i class="fa fa-spinner fa-spin fa-4x"></i>
|
||||||
|
</div>
|
||||||
|
<div id="emptyMessage" class="row" style="display:none;">
|
||||||
|
<div class="alert alert-info">
|
||||||
|
No profiles created yet. Let's create one!
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="row">
|
||||||
|
<table id="profileTable" class="table" style="display:none;">
|
||||||
|
<thead>
|
||||||
|
<tr>
|
||||||
|
<th class="col-md-4">Name</th>
|
||||||
|
<th>Interface Type</th>
|
||||||
|
<th>Last Modified Date</th>
|
||||||
|
<th class="col-md-2 no-sort"></th>
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
<tbody>
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<!-- Modal -->
|
||||||
|
<div class="modal fade" id="modal" tabindex="-1" role="dialog" aria-labelledby="modalLabel">
|
||||||
|
<div class="modal-dialog" role="document">
|
||||||
|
<div class="modal-content">
|
||||||
|
<!-- New Template Modal -->
|
||||||
|
<div class="modal-header">
|
||||||
|
<button type="button" class="close" data-dismiss="modal" aria-label="Close" onclick="dismiss()"><span aria-hidden="true">×</span></button>
|
||||||
|
<h4 class="modal-title" id="profileModalLabel">New Sending Profile</h4>
|
||||||
|
</div>
|
||||||
|
<div class="modal-body">
|
||||||
|
<div class="row" id="modal.flashes"></div>
|
||||||
|
<div class="form-group">
|
||||||
|
<label class="control-label" for="name">Name:</label>
|
||||||
|
<input type="text" class="form-control" placeholder="Profile name" id="name" autofocus/>
|
||||||
|
<label class="control-label" for="interface_type">Interface Type:</label>
|
||||||
|
<input type="text" class="form-control" value="SMTP" id="interface_type" disabled/>
|
||||||
|
<label class="control-label" for="from">From:</label>
|
||||||
|
<input type="text" class="form-control" placeholder="First Last <test@example.com>" id="from" required/>
|
||||||
|
<label class="control-label" for="host">Host:</label>
|
||||||
|
<input type="text" class="form-control" placeholder="smtp.example.com:25" id="host" required/>
|
||||||
|
<label class="control-label" for="username">Username:</label>
|
||||||
|
<input type="text" class="form-control" placeholder="Username" id="username"/>
|
||||||
|
<label class="control-label" for="password">Password:</label>
|
||||||
|
<input type="text" class="form-control" placeholder="Password" id="password"/>
|
||||||
|
<div class="checkbox checkbox-primary">
|
||||||
|
<input id="ignore_cert_errors" type="checkbox" checked>
|
||||||
|
<label for="ignore_cert_errors">Ignore Certificate Errors <i class="fa fa-question-circle" data-toggle="tooltip" data-placement="right" title="Ignore common certificate errors such as self-signed certs (exposes you to MiTM attacks - use carefully!)"></i></label>
|
||||||
|
</div>
|
||||||
|
<button type="button" data-toggle="modal" data-target="#sendTestEmailModal" class="btn btn-primary"><i class="fa fa-envelope"></i> Send Test Email</button>
|
||||||
|
<!-- disable sendTestEmail functionality on sending profile page until update handling of /util/send_test_email -->
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="modal-footer">
|
||||||
|
<button type="button" data-dismiss="modal" class="btn btn-default" onclick="dismiss()">Cancel</button>
|
||||||
|
<button type="button" class="btn btn-primary" id="modalSubmit">Save Profile</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<!-- Send Test Email Modal -->
|
||||||
|
<div class="modal" id="sendTestEmailModal" tabindex="-1" role="dialog" aria-labelledby="modalLabel">
|
||||||
|
<div class="modal-dialog" role="document">
|
||||||
|
<div class="modal-content">
|
||||||
|
<!-- New Email Modal -->
|
||||||
|
<div class="modal-header">
|
||||||
|
<button type="button" class="close" data-dismiss="modal" aria-label="Close"><span aria-hidden="true">×</span></button>
|
||||||
|
<h4 class="modal-title" id="sendTestEmailModalTitle">Send Test Email</h4>
|
||||||
|
</div>
|
||||||
|
<div class="modal-body">
|
||||||
|
<div class="row" id="sendTestEmailModal.flashes"></div>
|
||||||
|
<div class="row">
|
||||||
|
<div class="col-sm-12">
|
||||||
|
<label class="control-label" for="to">Send Test Email to:</label>
|
||||||
|
</div>
|
||||||
|
<br>
|
||||||
|
<div class="col-sm-2">
|
||||||
|
<input type="text" class="form-control" placeholder="First Name" name="to_first_name">
|
||||||
|
</div>
|
||||||
|
<div class="col-sm-2">
|
||||||
|
<input type="text" class="form-control" placeholder="Last Name" name="to_last_name">
|
||||||
|
</div>
|
||||||
|
<div class="col-sm-4">
|
||||||
|
<input type="email" class="form-control" placeholder="Email" name="to_email" required>
|
||||||
|
</div>
|
||||||
|
<div class="col-sm-4">
|
||||||
|
<input type="text" class="form-control" placeholder="Position" name="to_position">
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="modal-footer">
|
||||||
|
<button type="button" data-dismiss="modal" class="btn btn-default">Cancel</button>
|
||||||
|
<button type="button" class="btn btn-primary" id="sendTestModalSubmit" onclick="sendTestEmail()"><i class="fa fa-envelope"></i> Send</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
{{end}}
|
||||||
|
{{define "scripts"}}
|
||||||
|
<script src="/js/ckeditor/ckeditor.js"></script>
|
||||||
|
<script src="/js/ckeditor/adapters/jquery.js"></script>
|
||||||
|
<script src="/js/app/sending_profiles.js"></script>
|
||||||
|
{{end}}
|
|
@ -13,8 +13,13 @@
|
||||||
</li>
|
</li>
|
||||||
<li><a href="/landing_pages">Landing Pages</a>
|
<li><a href="/landing_pages">Landing Pages</a>
|
||||||
</li>
|
</li>
|
||||||
|
<li><a href="/sending_profiles">Sending Profiles</a>
|
||||||
|
</li>
|
||||||
<li class="active"><a href="/settings">Settings</a>
|
<li class="active"><a href="/settings">Settings</a>
|
||||||
</li>
|
</li>
|
||||||
|
<li><hr></li>
|
||||||
|
<li><a href="/gophish_user_guide.pdf">User Guide</a>
|
||||||
|
</li>
|
||||||
<li><a href="/api/">API Documentation</a>
|
<li><a href="/api/">API Documentation</a>
|
||||||
</li>
|
</li>
|
||||||
</ul>
|
</ul>
|
||||||
|
|
|
@ -13,8 +13,13 @@
|
||||||
</li>
|
</li>
|
||||||
<li><a href="/landing_pages">Landing Pages</a>
|
<li><a href="/landing_pages">Landing Pages</a>
|
||||||
</li>
|
</li>
|
||||||
|
<li><a href="/sending_profiles">Sending Profiles</a>
|
||||||
|
</li>
|
||||||
<li><a href="/settings">Settings</a>
|
<li><a href="/settings">Settings</a>
|
||||||
</li>
|
</li>
|
||||||
|
<li><hr></li>
|
||||||
|
<li><a href="/gophish_user_guide.pdf">User Guide</a>
|
||||||
|
</li>
|
||||||
<li><a href="/api/">API Documentation</a>
|
<li><a href="/api/">API Documentation</a>
|
||||||
</li>
|
</li>
|
||||||
</ul>
|
</ul>
|
||||||
|
@ -60,7 +65,7 @@
|
||||||
<div class="modal-content">
|
<div class="modal-content">
|
||||||
<!-- New Template Modal -->
|
<!-- New Template Modal -->
|
||||||
<div class="modal-header">
|
<div class="modal-header">
|
||||||
<button type="button" class="close" data-dismiss="modal" aria-label="Close"><span aria-hidden="true">×</span></button>
|
<button type="button" class="close" data-dismiss="modal" aria-label="Close" onclick="dismiss()"><span aria-hidden="true">×</span></button>
|
||||||
<h4 class="modal-title" id="templateModalLabel">New Template</h4>
|
<h4 class="modal-title" id="templateModalLabel">New Template</h4>
|
||||||
</div>
|
</div>
|
||||||
<div class="modal-body">
|
<div class="modal-body">
|
||||||
|
|
|
@ -13,8 +13,13 @@
|
||||||
</li>
|
</li>
|
||||||
<li><a href="/landing_pages">Landing Pages</a>
|
<li><a href="/landing_pages">Landing Pages</a>
|
||||||
</li>
|
</li>
|
||||||
|
<li><a href="/sending_profiles">Sending Profiles</a>
|
||||||
|
</li>
|
||||||
<li><a href="/settings">Settings</a>
|
<li><a href="/settings">Settings</a>
|
||||||
</li>
|
</li>
|
||||||
|
<li><hr></li>
|
||||||
|
<li><a href="/gophish_user_guide.pdf">User Guide</a>
|
||||||
|
</li>
|
||||||
<li><a href="/api/">API Documentation</a>
|
<li><a href="/api/">API Documentation</a>
|
||||||
</li>
|
</li>
|
||||||
</ul>
|
</ul>
|
||||||
|
|
Loading…
Reference in New Issue