gophish/controllers/api.go

773 lines
26 KiB
Go
Raw Normal View History

package controllers
import (
"bytes"
"crypto/tls"
2014-01-13 02:00:20 +00:00
"encoding/json"
"errors"
"fmt"
"net/http"
"strconv"
"strings"
"time"
2015-08-24 01:42:47 +00:00
"github.com/PuerkitoBio/goquery"
"github.com/gophish/gophish/auth"
2016-09-15 03:24:51 +00:00
ctx "github.com/gophish/gophish/context"
log "github.com/gophish/gophish/logger"
"github.com/gophish/gophish/models"
"github.com/gophish/gophish/util"
"github.com/gorilla/mux"
"github.com/jinzhu/gorm"
"github.com/jordan-wright/email"
"github.com/sirupsen/logrus"
)
// APIReset (/api/reset) resets the currently authenticated user's API key
2018-12-16 03:38:51 +00:00
func (as *AdminServer) APIReset(w http.ResponseWriter, r *http.Request) {
switch {
case r.Method == "POST":
u := ctx.Get(r, "user").(models.User)
u.ApiKey = auth.GenerateSecureKey()
err := models.PutUser(&u)
if err != nil {
http.Error(w, "Error setting API Key", http.StatusInternalServerError)
} else {
JSONResponse(w, models.Response{Success: true, Message: "API Key successfully reset!", Data: u.ApiKey}, http.StatusOK)
}
}
}
2018-12-16 03:38:51 +00:00
// APICampaigns returns a list of campaigns if requested via GET.
// If requested via POST, APICampaigns creates a new campaign and returns a reference to it.
func (as *AdminServer) APICampaigns(w http.ResponseWriter, r *http.Request) {
switch {
case r.Method == "GET":
cs, err := models.GetCampaigns(ctx.Get(r, "user_id").(int64))
if err != nil {
log.Error(err)
}
JSONResponse(w, cs, http.StatusOK)
//POST: Create a new campaign and return it as JSON
case r.Method == "POST":
c := models.Campaign{}
// Put the request into a campaign
err := json.NewDecoder(r.Body).Decode(&c)
if err != nil {
JSONResponse(w, models.Response{Success: false, Message: "Invalid JSON structure"}, http.StatusBadRequest)
return
}
err = models.PostCampaign(&c, ctx.Get(r, "user_id").(int64))
if err != nil {
JSONResponse(w, models.Response{Success: false, Message: err.Error()}, http.StatusBadRequest)
return
}
// If the campaign is scheduled to launch immediately, send it to the worker.
// Otherwise, the worker will pick it up at the scheduled time
2018-12-16 03:38:51 +00:00
if c.Status == models.CampaignInProgress {
go as.worker.LaunchCampaign(c)
}
JSONResponse(w, c, http.StatusCreated)
}
}
2018-12-16 03:38:51 +00:00
// APICampaignsSummary returns the summary for the current user's campaigns
func (as *AdminServer) APICampaignsSummary(w http.ResponseWriter, r *http.Request) {
switch {
case r.Method == "GET":
cs, err := models.GetCampaignSummaries(ctx.Get(r, "user_id").(int64))
if err != nil {
log.Error(err)
JSONResponse(w, models.Response{Success: false, Message: err.Error()}, http.StatusInternalServerError)
return
}
JSONResponse(w, cs, http.StatusOK)
}
}
2018-12-16 03:38:51 +00:00
// APICampaign returns details about the requested campaign. If the campaign is not
// valid, APICampaign returns null.
func (as *AdminServer) APICampaign(w http.ResponseWriter, r *http.Request) {
vars := mux.Vars(r)
id, _ := strconv.ParseInt(vars["id"], 0, 64)
c, err := models.GetCampaign(id, ctx.Get(r, "user_id").(int64))
if err != nil {
log.Error(err)
JSONResponse(w, models.Response{Success: false, Message: "Campaign not found"}, http.StatusNotFound)
return
}
switch {
case r.Method == "GET":
JSONResponse(w, c, http.StatusOK)
case r.Method == "DELETE":
err = models.DeleteCampaign(id)
if err != nil {
JSONResponse(w, models.Response{Success: false, Message: "Error deleting campaign"}, http.StatusInternalServerError)
return
}
JSONResponse(w, models.Response{Success: true, Message: "Campaign deleted successfully!"}, http.StatusOK)
}
}
2018-12-16 03:38:51 +00:00
// APICampaignResults returns just the results for a given campaign to
// significantly reduce the information returned.
2018-12-16 03:38:51 +00:00
func (as *AdminServer) APICampaignResults(w http.ResponseWriter, r *http.Request) {
vars := mux.Vars(r)
id, _ := strconv.ParseInt(vars["id"], 0, 64)
cr, err := models.GetCampaignResults(id, ctx.Get(r, "user_id").(int64))
if err != nil {
log.Error(err)
JSONResponse(w, models.Response{Success: false, Message: "Campaign not found"}, http.StatusNotFound)
return
}
if r.Method == "GET" {
JSONResponse(w, cr, http.StatusOK)
return
}
}
2018-12-16 03:38:51 +00:00
// APICampaignSummary returns the summary for a given campaign.
func (as *AdminServer) APICampaignSummary(w http.ResponseWriter, r *http.Request) {
vars := mux.Vars(r)
id, _ := strconv.ParseInt(vars["id"], 0, 64)
switch {
case r.Method == "GET":
cs, err := models.GetCampaignSummary(id, ctx.Get(r, "user_id").(int64))
if err != nil {
if err == gorm.ErrRecordNotFound {
JSONResponse(w, models.Response{Success: false, Message: "Campaign not found"}, http.StatusNotFound)
} else {
JSONResponse(w, models.Response{Success: false, Message: err.Error()}, http.StatusInternalServerError)
}
log.Error(err)
return
}
JSONResponse(w, cs, http.StatusOK)
}
}
2018-12-16 03:38:51 +00:00
// APICampaignComplete effectively "ends" a campaign.
// Future phishing emails clicked will return a simple "404" page.
2018-12-16 03:38:51 +00:00
func (as *AdminServer) APICampaignComplete(w http.ResponseWriter, r *http.Request) {
vars := mux.Vars(r)
id, _ := strconv.ParseInt(vars["id"], 0, 64)
switch {
case r.Method == "GET":
err := models.CompleteCampaign(id, ctx.Get(r, "user_id").(int64))
if err != nil {
JSONResponse(w, models.Response{Success: false, Message: "Error completing campaign"}, http.StatusInternalServerError)
return
}
JSONResponse(w, models.Response{Success: true, Message: "Campaign completed successfully!"}, http.StatusOK)
}
}
2018-12-16 03:38:51 +00:00
// APIGroups returns a list of groups if requested via GET.
// If requested via POST, APIGroups creates a new group and returns a reference to it.
func (as *AdminServer) APIGroups(w http.ResponseWriter, r *http.Request) {
switch {
case r.Method == "GET":
gs, err := models.GetGroups(ctx.Get(r, "user_id").(int64))
if err != nil {
JSONResponse(w, models.Response{Success: false, Message: "No groups found"}, http.StatusNotFound)
2014-02-06 19:30:05 +00:00
return
}
JSONResponse(w, gs, http.StatusOK)
//POST: Create a new group and return it as JSON
case r.Method == "POST":
g := models.Group{}
// Put the request into a group
err := json.NewDecoder(r.Body).Decode(&g)
if err != nil {
JSONResponse(w, models.Response{Success: false, Message: "Invalid JSON structure"}, http.StatusBadRequest)
return
}
_, err = models.GetGroupByName(g.Name, ctx.Get(r, "user_id").(int64))
if err != gorm.ErrRecordNotFound {
JSONResponse(w, models.Response{Success: false, Message: "Group name already in use"}, http.StatusConflict)
return
}
g.ModifiedDate = time.Now().UTC()
g.UserId = ctx.Get(r, "user_id").(int64)
err = models.PostGroup(&g)
if err != nil {
JSONResponse(w, models.Response{Success: false, Message: err.Error()}, http.StatusBadRequest)
return
}
JSONResponse(w, g, http.StatusCreated)
}
}
2018-12-16 03:38:51 +00:00
// APIGroupsSummary returns a summary of the groups owned by the current user.
func (as *AdminServer) APIGroupsSummary(w http.ResponseWriter, r *http.Request) {
switch {
case r.Method == "GET":
gs, err := models.GetGroupSummaries(ctx.Get(r, "user_id").(int64))
if err != nil {
log.Error(err)
JSONResponse(w, models.Response{Success: false, Message: err.Error()}, http.StatusInternalServerError)
return
}
JSONResponse(w, gs, http.StatusOK)
}
}
2018-12-16 03:38:51 +00:00
// APIGroup returns details about the requested group.
// If the group is not valid, APIGroup returns null.
func (as *AdminServer) APIGroup(w http.ResponseWriter, r *http.Request) {
vars := mux.Vars(r)
id, _ := strconv.ParseInt(vars["id"], 0, 64)
g, err := models.GetGroup(id, ctx.Get(r, "user_id").(int64))
if err != nil {
JSONResponse(w, models.Response{Success: false, Message: "Group not found"}, http.StatusNotFound)
return
}
switch {
case r.Method == "GET":
JSONResponse(w, g, http.StatusOK)
case r.Method == "DELETE":
err = models.DeleteGroup(&g)
if err != nil {
JSONResponse(w, models.Response{Success: false, Message: "Error deleting group"}, http.StatusInternalServerError)
return
}
JSONResponse(w, models.Response{Success: true, Message: "Group deleted successfully!"}, http.StatusOK)
case r.Method == "PUT":
// Change this to get from URL and uid (don't bother with id in r.Body)
g = models.Group{}
err = json.NewDecoder(r.Body).Decode(&g)
if g.Id != id {
JSONResponse(w, models.Response{Success: false, Message: "Error: /:id and group_id mismatch"}, http.StatusInternalServerError)
return
}
g.ModifiedDate = time.Now().UTC()
g.UserId = ctx.Get(r, "user_id").(int64)
err = models.PutGroup(&g)
if err != nil {
JSONResponse(w, models.Response{Success: false, Message: err.Error()}, http.StatusBadRequest)
return
}
JSONResponse(w, g, http.StatusOK)
}
}
2018-12-16 03:38:51 +00:00
// APIGroupSummary returns a summary of the groups owned by the current user.
func (as *AdminServer) APIGroupSummary(w http.ResponseWriter, r *http.Request) {
switch {
case r.Method == "GET":
vars := mux.Vars(r)
id, _ := strconv.ParseInt(vars["id"], 0, 64)
g, err := models.GetGroupSummary(id, ctx.Get(r, "user_id").(int64))
if err != nil {
JSONResponse(w, models.Response{Success: false, Message: "Group not found"}, http.StatusNotFound)
return
}
JSONResponse(w, g, http.StatusOK)
}
}
2018-12-16 03:38:51 +00:00
// APITemplates handles the functionality for the /api/templates endpoint
func (as *AdminServer) APITemplates(w http.ResponseWriter, r *http.Request) {
switch {
case r.Method == "GET":
ts, err := models.GetTemplates(ctx.Get(r, "user_id").(int64))
if err != nil {
log.Error(err)
}
JSONResponse(w, ts, http.StatusOK)
//POST: Create a new template and return it as JSON
case r.Method == "POST":
t := models.Template{}
// Put the request into a template
err := json.NewDecoder(r.Body).Decode(&t)
if err != nil {
JSONResponse(w, models.Response{Success: false, Message: "Invalid JSON structure"}, http.StatusBadRequest)
return
}
_, err = models.GetTemplateByName(t.Name, ctx.Get(r, "user_id").(int64))
if err != gorm.ErrRecordNotFound {
JSONResponse(w, models.Response{Success: false, Message: "Template name already in use"}, http.StatusConflict)
return
}
t.ModifiedDate = time.Now().UTC()
t.UserId = ctx.Get(r, "user_id").(int64)
err = models.PostTemplate(&t)
if err == models.ErrTemplateNameNotSpecified {
JSONResponse(w, models.Response{Success: false, Message: err.Error()}, http.StatusBadRequest)
return
}
if err == models.ErrTemplateMissingParameter {
JSONResponse(w, models.Response{Success: false, Message: err.Error()}, http.StatusBadRequest)
return
}
if err != nil {
JSONResponse(w, models.Response{Success: false, Message: "Error inserting template into database"}, http.StatusInternalServerError)
log.Error(err)
return
}
JSONResponse(w, t, http.StatusCreated)
}
2014-03-17 03:02:06 +00:00
}
2018-12-16 03:38:51 +00:00
// APITemplate handles the functions for the /api/templates/:id endpoint
func (as *AdminServer) APITemplate(w http.ResponseWriter, r *http.Request) {
vars := mux.Vars(r)
id, _ := strconv.ParseInt(vars["id"], 0, 64)
t, err := models.GetTemplate(id, ctx.Get(r, "user_id").(int64))
if err != nil {
JSONResponse(w, models.Response{Success: false, Message: "Template not found"}, http.StatusNotFound)
return
}
switch {
case r.Method == "GET":
JSONResponse(w, t, http.StatusOK)
case r.Method == "DELETE":
err = models.DeleteTemplate(id, ctx.Get(r, "user_id").(int64))
if err != nil {
JSONResponse(w, models.Response{Success: false, Message: "Error deleting template"}, http.StatusInternalServerError)
return
}
JSONResponse(w, models.Response{Success: true, Message: "Template deleted successfully!"}, http.StatusOK)
case r.Method == "PUT":
t = models.Template{}
err = json.NewDecoder(r.Body).Decode(&t)
if err != nil {
log.Error(err)
}
if t.Id != id {
JSONResponse(w, models.Response{Success: false, Message: "Error: /:id and template_id mismatch"}, http.StatusBadRequest)
return
}
t.ModifiedDate = time.Now().UTC()
t.UserId = ctx.Get(r, "user_id").(int64)
err = models.PutTemplate(&t)
if err != nil {
JSONResponse(w, models.Response{Success: false, Message: err.Error()}, http.StatusBadRequest)
return
}
JSONResponse(w, t, http.StatusOK)
}
2014-03-17 03:02:06 +00:00
}
2018-12-16 03:38:51 +00:00
// APIPages handles requests for the /api/pages/ endpoint
func (as *AdminServer) APIPages(w http.ResponseWriter, r *http.Request) {
switch {
case r.Method == "GET":
ps, err := models.GetPages(ctx.Get(r, "user_id").(int64))
if err != nil {
log.Error(err)
}
JSONResponse(w, ps, http.StatusOK)
//POST: Create a new page and return it as JSON
case r.Method == "POST":
p := models.Page{}
// Put the request into a page
err := json.NewDecoder(r.Body).Decode(&p)
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.GetPageByName(p.Name, ctx.Get(r, "user_id").(int64))
if err != gorm.ErrRecordNotFound {
JSONResponse(w, models.Response{Success: false, Message: "Page name already in use"}, http.StatusConflict)
log.Error(err)
return
}
p.ModifiedDate = time.Now().UTC()
p.UserId = ctx.Get(r, "user_id").(int64)
err = models.PostPage(&p)
if err != nil {
JSONResponse(w, models.Response{Success: false, Message: err.Error()}, http.StatusInternalServerError)
return
}
JSONResponse(w, p, http.StatusCreated)
}
}
2018-12-16 03:38:51 +00:00
// APIPage contains functions to handle the GET'ing, DELETE'ing, and PUT'ing
// of a Page object
2018-12-16 03:38:51 +00:00
func (as *AdminServer) APIPage(w http.ResponseWriter, r *http.Request) {
vars := mux.Vars(r)
id, _ := strconv.ParseInt(vars["id"], 0, 64)
p, err := models.GetPage(id, ctx.Get(r, "user_id").(int64))
if err != nil {
JSONResponse(w, models.Response{Success: false, Message: "Page not found"}, http.StatusNotFound)
return
}
switch {
case r.Method == "GET":
JSONResponse(w, p, http.StatusOK)
case r.Method == "DELETE":
err = models.DeletePage(id, ctx.Get(r, "user_id").(int64))
if err != nil {
JSONResponse(w, models.Response{Success: false, Message: "Error deleting page"}, http.StatusInternalServerError)
return
}
JSONResponse(w, models.Response{Success: true, Message: "Page Deleted Successfully"}, http.StatusOK)
case r.Method == "PUT":
p = models.Page{}
err = json.NewDecoder(r.Body).Decode(&p)
if err != nil {
log.Error(err)
}
if p.Id != id {
JSONResponse(w, models.Response{Success: false, Message: "/:id and /:page_id mismatch"}, http.StatusBadRequest)
return
}
p.ModifiedDate = time.Now().UTC()
p.UserId = ctx.Get(r, "user_id").(int64)
err = models.PutPage(&p)
if err != nil {
JSONResponse(w, models.Response{Success: false, Message: "Error updating page: " + err.Error()}, http.StatusInternalServerError)
return
}
JSONResponse(w, p, http.StatusOK)
}
}
2018-12-16 03:38:51 +00:00
// APISendingProfiles handles requests for the /api/smtp/ endpoint
func (as *AdminServer) APISendingProfiles(w http.ResponseWriter, r *http.Request) {
2016-02-22 03:09:14 +00:00
switch {
case r.Method == "GET":
ss, err := models.GetSMTPs(ctx.Get(r, "user_id").(int64))
if err != nil {
log.Error(err)
2016-02-22 03:09:14 +00:00
}
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.ErrRecordNotFound {
2016-02-22 03:09:14 +00:00
JSONResponse(w, models.Response{Success: false, Message: "SMTP name already in use"}, http.StatusConflict)
log.Error(err)
2016-02-22 03:09:14 +00:00
return
}
s.ModifiedDate = time.Now().UTC()
2016-02-22 03:09:14 +00:00
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)
}
}
2018-12-16 03:38:51 +00:00
// APISendingProfile contains functions to handle the GET'ing, DELETE'ing, and PUT'ing
// of a SMTP object
2018-12-16 03:38:51 +00:00
func (as *AdminServer) APISendingProfile(w http.ResponseWriter, r *http.Request) {
2016-02-22 03:09:14 +00:00
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 {
log.Error(err)
2016-02-22 03:09:14 +00:00
}
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: err.Error()}, http.StatusBadRequest)
2016-02-22 03:09:14 +00:00
return
}
s.ModifiedDate = time.Now().UTC()
2016-02-22 03:09:14 +00:00
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)
}
}
2018-12-16 03:38:51 +00:00
// APIImportGroup imports a CSV of group members
func (as *AdminServer) APIImportGroup(w http.ResponseWriter, r *http.Request) {
ts, err := util.ParseCSV(r)
if err != nil {
JSONResponse(w, models.Response{Success: false, Message: "Error parsing CSV"}, http.StatusInternalServerError)
return
}
JSONResponse(w, ts, http.StatusOK)
return
}
2018-12-16 03:38:51 +00:00
// APIImportEmail allows for the importing of email.
// Returns a Message object
2018-12-16 03:38:51 +00:00
func (as *AdminServer) APIImportEmail(w http.ResponseWriter, r *http.Request) {
if r.Method != "POST" {
JSONResponse(w, models.Response{Success: false, Message: "Method not allowed"}, http.StatusBadRequest)
return
}
ir := struct {
Content string `json:"content"`
ConvertLinks bool `json:"convert_links"`
}{}
err := json.NewDecoder(r.Body).Decode(&ir)
if err != nil {
JSONResponse(w, models.Response{Success: false, Message: "Error decoding JSON Request"}, http.StatusBadRequest)
return
}
e, err := email.NewEmailFromReader(strings.NewReader(ir.Content))
if err != nil {
log.Error(err)
}
// If the user wants to convert links to point to
// the landing page, let's make it happen by changing up
// e.HTML
if ir.ConvertLinks {
d, err := goquery.NewDocumentFromReader(bytes.NewReader(e.HTML))
if err != nil {
JSONResponse(w, models.Response{Success: false, Message: err.Error()}, http.StatusBadRequest)
return
}
d.Find("a").Each(func(i int, a *goquery.Selection) {
a.SetAttr("href", "{{.URL}}")
})
h, err := d.Html()
if err != nil {
JSONResponse(w, models.Response{Success: false, Message: err.Error()}, http.StatusInternalServerError)
return
}
e.HTML = []byte(h)
}
er := emailResponse{
Subject: e.Subject,
Text: string(e.Text),
HTML: string(e.HTML),
}
JSONResponse(w, er, http.StatusOK)
return
}
2018-12-16 03:38:51 +00:00
// APIImportSite allows for the importing of HTML from a website
// Without "include_resources" set, it will merely place a "base" tag
// so that all resources can be loaded relative to the given URL.
2018-12-16 03:38:51 +00:00
func (as *AdminServer) APIImportSite(w http.ResponseWriter, r *http.Request) {
cr := cloneRequest{}
if r.Method != "POST" {
JSONResponse(w, models.Response{Success: false, Message: "Method not allowed"}, http.StatusBadRequest)
return
}
err := json.NewDecoder(r.Body).Decode(&cr)
if err != nil {
JSONResponse(w, models.Response{Success: false, Message: "Error decoding JSON Request"}, http.StatusBadRequest)
return
}
if err = cr.validate(); err != nil {
JSONResponse(w, models.Response{Success: false, Message: err.Error()}, http.StatusBadRequest)
return
}
tr := &http.Transport{
TLSClientConfig: &tls.Config{
InsecureSkipVerify: true,
},
}
client := &http.Client{Transport: tr}
resp, err := client.Get(cr.URL)
if err != nil {
JSONResponse(w, models.Response{Success: false, Message: err.Error()}, http.StatusBadRequest)
return
}
2015-08-24 01:42:47 +00:00
// Insert the base href tag to better handle relative resources
d, err := goquery.NewDocumentFromResponse(resp)
if err != nil {
JSONResponse(w, models.Response{Success: false, Message: err.Error()}, http.StatusBadRequest)
return
}
2015-08-24 01:42:47 +00:00
// Assuming we don't want to include resources, we'll need a base href
if d.Find("head base").Length() == 0 {
d.Find("head").PrependHtml(fmt.Sprintf("<base href=\"%s\">", cr.URL))
2015-08-24 01:42:47 +00:00
}
forms := d.Find("form")
forms.Each(func(i int, f *goquery.Selection) {
// We'll want to store where we got the form from
// (the current URL)
url := f.AttrOr("action", cr.URL)
if !strings.HasPrefix(url, "http") {
url = fmt.Sprintf("%s%s", cr.URL, url)
}
f.PrependHtml(fmt.Sprintf("<input type=\"hidden\" name=\"__original_url\" value=\"%s\"/>", url))
})
2015-08-24 01:42:47 +00:00
h, err := d.Html()
if err != nil {
JSONResponse(w, models.Response{Success: false, Message: err.Error()}, http.StatusInternalServerError)
return
}
cs := cloneResponse{HTML: h}
JSONResponse(w, cs, http.StatusOK)
return
}
2018-12-16 03:38:51 +00:00
// APISendTestEmail sends a test email using the template name
// and Target given.
2018-12-16 03:38:51 +00:00
func (as *AdminServer) APISendTestEmail(w http.ResponseWriter, r *http.Request) {
s := &models.EmailRequest{
ErrorChan: make(chan error),
UserId: ctx.Get(r, "user_id").(int64),
}
if r.Method != "POST" {
JSONResponse(w, models.Response{Success: false, Message: "Method not allowed"}, http.StatusBadRequest)
return
}
err := json.NewDecoder(r.Body).Decode(s)
if err != nil {
JSONResponse(w, models.Response{Success: false, Message: "Error decoding JSON Request"}, http.StatusBadRequest)
return
}
storeRequest := false
// If a Template is not specified use a default
if s.Template.Name == "" {
//default message body
text := "It works!\n\nThis is an email letting you know that your gophish\nconfiguration was successful.\n" +
2016-02-27 14:37:02 +00:00
"Here are the details:\n\nWho you sent from: {{.From}}\n\nWho you sent to: \n" +
"{{if .FirstName}} First Name: {{.FirstName}}\n{{end}}" +
"{{if .LastName}} Last Name: {{.LastName}}\n{{end}}" +
"{{if .Position}} Position: {{.Position}}\n{{end}}" +
"\nNow go send some phish!"
t := models.Template{
Subject: "Default Email from Gophish",
2016-02-27 14:37:02 +00:00
Text: text,
}
s.Template = t
} else {
// Get the Template requested by name
s.Template, err = models.GetTemplateByName(s.Template.Name, s.UserId)
if err == gorm.ErrRecordNotFound {
log.WithFields(logrus.Fields{
"template": s.Template.Name,
}).Error("Template does not exist")
JSONResponse(w, models.Response{Success: false, Message: models.ErrTemplateNotFound.Error()}, http.StatusBadRequest)
return
} else if err != nil {
log.Error(err)
JSONResponse(w, models.Response{Success: false, Message: err.Error()}, http.StatusBadRequest)
return
}
s.TemplateId = s.Template.Id
// We'll only save the test request to the database if there is a
// user-specified template to use.
storeRequest = true
}
if s.Page.Name != "" {
s.Page, err = models.GetPageByName(s.Page.Name, s.UserId)
if err == gorm.ErrRecordNotFound {
log.WithFields(logrus.Fields{
"page": s.Page.Name,
}).Error("Page does not exist")
JSONResponse(w, models.Response{Success: false, Message: models.ErrPageNotFound.Error()}, http.StatusBadRequest)
return
} else if err != nil {
log.Error(err)
JSONResponse(w, models.Response{Success: false, Message: err.Error()}, http.StatusBadRequest)
return
}
s.PageId = s.Page.Id
}
// If a complete sending profile is provided use it
if err := s.SMTP.Validate(); err != nil {
// Otherwise get the SMTP requested by name
smtp, lookupErr := models.GetSMTPByName(s.SMTP.Name, s.UserId)
// If the Sending Profile doesn't exist, let's err on the side
// of caution and assume that the validation failure was more important.
if lookupErr != nil {
log.Error(err)
JSONResponse(w, models.Response{Success: false, Message: err.Error()}, http.StatusBadRequest)
return
}
s.SMTP = smtp
2016-02-22 03:09:14 +00:00
}
s.FromAddress = s.SMTP.FromAddress
// Validate the given request
if err = s.Validate(); err != nil {
JSONResponse(w, models.Response{Success: false, Message: err.Error()}, http.StatusBadRequest)
return
}
// Store the request if this wasn't the default template
if storeRequest {
err = models.PostEmailRequest(s)
if err != nil {
log.Error(err)
JSONResponse(w, models.Response{Success: false, Message: err.Error()}, http.StatusInternalServerError)
return
}
}
// Send the test email
err = as.worker.SendTestEmail(s)
if err != nil {
log.Error(err)
JSONResponse(w, models.Response{Success: false, Message: err.Error()}, http.StatusInternalServerError)
return
}
JSONResponse(w, models.Response{Success: true, Message: "Email Sent"}, http.StatusOK)
return
}
// JSONResponse attempts to set the status code, c, and marshal the given interface, d, into a response that
// is written to the given ResponseWriter.
func JSONResponse(w http.ResponseWriter, d interface{}, c int) {
dj, err := json.MarshalIndent(d, "", " ")
if err != nil {
http.Error(w, "Error creating JSON response", http.StatusInternalServerError)
log.Error(err)
}
2014-01-13 02:00:20 +00:00
w.Header().Set("Content-Type", "application/json")
w.WriteHeader(c)
fmt.Fprintf(w, "%s", dj)
2014-01-13 02:00:20 +00:00
}
type cloneRequest struct {
URL string `json:"url"`
IncludeResources bool `json:"include_resources"`
}
func (cr *cloneRequest) validate() error {
if cr.URL == "" {
return errors.New("No URL Specified")
}
return nil
}
type cloneResponse struct {
HTML string `json:"html"`
}
type emailResponse struct {
Text string `json:"text"`
HTML string `json:"html"`
Subject string `json:"subject"`
}