From 563368d64c207b6564aa41e3e13ac12fb6b3cd77 Mon Sep 17 00:00:00 2001 From: Alen BOHCELYAN Date: Mon, 13 Feb 2017 12:07:08 +0000 Subject: [PATCH] Localization base features development (Golang & JS), further need is definieng the localization string both in golang translation file and html pages. --- config.json | 4 +- controllers/api.go | 134 ++++++++++++++++---------------- controllers/route.go | 17 +++- gophish.go | 6 +- models/campaign.go | 19 ++--- models/group.go | 7 +- models/models.go | 3 +- models/page.go | 3 +- models/result.go | 1 + models/smtp.go | 7 +- models/template.go | 5 +- static/js/src/app/dashboard.js | 8 +- templates/base.html | 19 ++--- templates/campaign_results.html | 56 ++++++------- templates/campaigns.html | 74 +++++++++--------- templates/dashboard.html | 49 +++++++----- templates/landing_pages.html | 60 +++++++------- templates/login.html | 12 +-- templates/register.html | 12 +-- templates/sending_profiles.html | 78 +++++++++---------- templates/settings.html | 40 +++++----- templates/templates.html | 70 ++++++++--------- templates/users.html | 60 +++++++------- util/util.go | 10 +++ 24 files changed, 395 insertions(+), 359 deletions(-) diff --git a/config.json b/config.json index 44c07c0b..ff0976c6 100644 --- a/config.json +++ b/config.json @@ -1,7 +1,7 @@ { "admin_server" : { - "listen_url" : "127.0.0.1:3333", - "use_tls" : true, + "listen_url" : "0.0.0.0:3333", + "use_tls" : false, "cert_path" : "gophish_admin.crt", "key_path" : "gophish_admin.key" }, diff --git a/controllers/api.go b/controllers/api.go index a9ec4f03..cb8f08a1 100644 --- a/controllers/api.go +++ b/controllers/api.go @@ -16,11 +16,11 @@ import ( "github.com/gophish/gophish/auth" ctx "github.com/gophish/gophish/context" "github.com/gophish/gophish/models" - "github.com/gophish/gophish/util" "github.com/gophish/gophish/worker" "github.com/gorilla/mux" "github.com/jinzhu/gorm" "github.com/jordan-wright/email" + "../util" ) // Worker is the worker that processes phishing events and updates campaigns. @@ -52,9 +52,9 @@ func API_Reset(w http.ResponseWriter, r *http.Request) { u.ApiKey = auth.GenerateSecureKey() err := models.PutUser(&u) if err != nil { - http.Error(w, "Error setting API Key", http.StatusInternalServerError) + http.Error(w, util.T("Error setting API Key"), http.StatusInternalServerError) } else { - JSONResponse(w, models.Response{Success: true, Message: "API Key successfully reset!", Data: u.ApiKey}, http.StatusOK) + JSONResponse(w, models.Response{Success: true, Message: util.T("API Key successfully reset!"), Data: u.ApiKey}, http.StatusOK) } } } @@ -109,7 +109,7 @@ func API_Campaigns_Id(w http.ResponseWriter, r *http.Request) { c, err := models.GetCampaign(id, ctx.Get(r, "user_id").(int64)) if err != nil { Logger.Println(err) - JSONResponse(w, models.Response{Success: false, Message: "Campaign not found"}, http.StatusNotFound) + JSONResponse(w, models.Response{Success: false, Message: util.T("Campaign not found")}, http.StatusNotFound) return } switch { @@ -118,10 +118,10 @@ func API_Campaigns_Id(w http.ResponseWriter, r *http.Request) { case r.Method == "DELETE": err = models.DeleteCampaign(id) if err != nil { - JSONResponse(w, models.Response{Success: false, Message: "Error deleting campaign"}, http.StatusInternalServerError) + JSONResponse(w, models.Response{Success: false, Message: util.T("Error deleting campaign")}, http.StatusInternalServerError) return } - JSONResponse(w, models.Response{Success: true, Message: "Campaign deleted successfully!"}, http.StatusOK) + JSONResponse(w, models.Response{Success: true, Message: util.T("Campaign deleted successfully!")}, http.StatusOK) } } @@ -133,7 +133,7 @@ func API_Campaigns_Id_Results(w http.ResponseWriter, r *http.Request) { cr, err := models.GetCampaignResults(id, ctx.Get(r, "user_id").(int64)) if err != nil { Logger.Println(err) - JSONResponse(w, models.Response{Success: false, Message: "Campaign not found"}, http.StatusNotFound) + JSONResponse(w, models.Response{Success: false, Message: util.T("Campaign not found")}, http.StatusNotFound) return } if r.Method == "GET" { @@ -151,9 +151,9 @@ func API_Campaign_Id_Summary(w http.ResponseWriter, r *http.Request) { 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) + JSONResponse(w, models.Response{Success: false, Message: util.T("Campaign not found")}, http.StatusNotFound) } else { - JSONResponse(w, models.Response{Success: false, Message: err.Error()}, http.StatusInternalServerError) + JSONResponse(w, models.Response{Success: false, Message: util.T(err.Error())}, http.StatusInternalServerError) } Logger.Println(err) return @@ -171,10 +171,10 @@ func API_Campaigns_Id_Complete(w http.ResponseWriter, r *http.Request) { 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) + JSONResponse(w, models.Response{Success: false, Message: util.T("Error completing campaign")}, http.StatusInternalServerError) return } - JSONResponse(w, models.Response{Success: true, Message: "Campaign completed successfully!"}, http.StatusOK) + JSONResponse(w, models.Response{Success: true, Message: util.T("Campaign completed successfully!")}, http.StatusOK) } } @@ -185,7 +185,7 @@ func API_Groups(w http.ResponseWriter, r *http.Request) { 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) + JSONResponse(w, models.Response{Success: false, Message: util.T("No groups found")}, http.StatusNotFound) return } JSONResponse(w, gs, http.StatusOK) @@ -195,12 +195,12 @@ func API_Groups(w http.ResponseWriter, r *http.Request) { // 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) + JSONResponse(w, models.Response{Success: false, Message: util.T("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) + JSONResponse(w, models.Response{Success: false, Message: util.T("Group name already in use")}, http.StatusConflict) return } g.ModifiedDate = time.Now() @@ -235,7 +235,7 @@ func API_Groups_Id(w http.ResponseWriter, r *http.Request) { 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) + JSONResponse(w, models.Response{Success: false, Message: util.T("Group not found")}, http.StatusNotFound) return } switch { @@ -244,23 +244,23 @@ func API_Groups_Id(w http.ResponseWriter, r *http.Request) { case r.Method == "DELETE": err = models.DeleteGroup(&g) if err != nil { - JSONResponse(w, models.Response{Success: false, Message: "Error deleting group"}, http.StatusInternalServerError) + JSONResponse(w, models.Response{Success: false, Message: util.T("Error deleting group")}, http.StatusInternalServerError) return } - JSONResponse(w, models.Response{Success: true, Message: "Group deleted successfully!"}, http.StatusOK) + JSONResponse(w, models.Response{Success: true, Message: util.T("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) + JSONResponse(w, models.Response{Success: false, Message: util.T("Error: /:id and group_id mismatch")}, http.StatusInternalServerError) return } g.ModifiedDate = time.Now() 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) + JSONResponse(w, models.Response{Success: false, Message: util.T(err.Error())}, http.StatusBadRequest) return } JSONResponse(w, g, http.StatusOK) @@ -275,7 +275,7 @@ func API_Groups_Id_Summary(w http.ResponseWriter, r *http.Request) { 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) + JSONResponse(w, models.Response{Success: false, Message: util.T("Group not found")}, http.StatusNotFound) return } JSONResponse(w, g, http.StatusOK) @@ -297,27 +297,27 @@ func API_Templates(w http.ResponseWriter, r *http.Request) { // 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) + JSONResponse(w, models.Response{Success: false, Message: util.T("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) + JSONResponse(w, models.Response{Success: false, Message: util.T("Template name already in use")}, http.StatusConflict) return } t.ModifiedDate = time.Now() 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) + JSONResponse(w, models.Response{Success: false, Message: util.T(err.Error())}, http.StatusBadRequest) return } if err == models.ErrTemplateMissingParameter { - JSONResponse(w, models.Response{Success: false, Message: err.Error()}, http.StatusBadRequest) + JSONResponse(w, models.Response{Success: false, Message: util.T(err.Error())}, http.StatusBadRequest) return } if err != nil { - JSONResponse(w, models.Response{Success: false, Message: "Error inserting template into database"}, http.StatusInternalServerError) + JSONResponse(w, models.Response{Success: false, Message: util.T("Error inserting template into database")}, http.StatusInternalServerError) Logger.Println(err) return } @@ -331,7 +331,7 @@ func API_Templates_Id(w http.ResponseWriter, r *http.Request) { 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) + JSONResponse(w, models.Response{Success: false, Message: util.T("Template not found")}, http.StatusNotFound) return } switch { @@ -340,10 +340,10 @@ func API_Templates_Id(w http.ResponseWriter, r *http.Request) { 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) + JSONResponse(w, models.Response{Success: false, Message: util.T("Error deleting template")}, http.StatusInternalServerError) return } - JSONResponse(w, models.Response{Success: true, Message: "Template deleted successfully!"}, http.StatusOK) + JSONResponse(w, models.Response{Success: true, Message: util.T("Template deleted successfully!")}, http.StatusOK) case r.Method == "PUT": t = models.Template{} err = json.NewDecoder(r.Body).Decode(&t) @@ -351,14 +351,14 @@ func API_Templates_Id(w http.ResponseWriter, r *http.Request) { Logger.Println(err) } if t.Id != id { - JSONResponse(w, models.Response{Success: false, Message: "Error: /:id and template_id mismatch"}, http.StatusBadRequest) + JSONResponse(w, models.Response{Success: false, Message: util.T("Error: /:id and template_id mismatch")}, http.StatusBadRequest) return } t.ModifiedDate = time.Now() 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) + JSONResponse(w, models.Response{Success: false, Message: util.T(err.Error())}, http.StatusBadRequest) return } JSONResponse(w, t, http.StatusOK) @@ -380,13 +380,13 @@ func API_Pages(w http.ResponseWriter, r *http.Request) { // 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) + JSONResponse(w, models.Response{Success: false, Message: util.T("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) + JSONResponse(w, models.Response{Success: false, Message: util.T("Page name already in use")}, http.StatusConflict) Logger.Println(err) return } @@ -394,7 +394,7 @@ func API_Pages(w http.ResponseWriter, r *http.Request) { 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) + JSONResponse(w, models.Response{Success: false, Message: util.T(err.Error())}, http.StatusInternalServerError) return } JSONResponse(w, p, http.StatusCreated) @@ -408,7 +408,7 @@ func API_Pages_Id(w http.ResponseWriter, r *http.Request) { 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) + JSONResponse(w, models.Response{Success: false, Message: util.T("Page not found")}, http.StatusNotFound) return } switch { @@ -417,10 +417,10 @@ func API_Pages_Id(w http.ResponseWriter, r *http.Request) { 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) + JSONResponse(w, models.Response{Success: false, Message: util.T("Error deleting page")}, http.StatusInternalServerError) return } - JSONResponse(w, models.Response{Success: true, Message: "Page Deleted Successfully"}, http.StatusOK) + JSONResponse(w, models.Response{Success: true, Message: util.T("Page Deleted Successfully")}, http.StatusOK) case r.Method == "PUT": p = models.Page{} err = json.NewDecoder(r.Body).Decode(&p) @@ -428,14 +428,14 @@ func API_Pages_Id(w http.ResponseWriter, r *http.Request) { Logger.Println(err) } if p.Id != id { - JSONResponse(w, models.Response{Success: false, Message: "/:id and /:page_id mismatch"}, http.StatusBadRequest) + JSONResponse(w, models.Response{Success: false, Message: util.T("/:id and /:page_id mismatch")}, http.StatusBadRequest) return } p.ModifiedDate = time.Now() 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) + JSONResponse(w, models.Response{Success: false, Message: util.T("Error updating page: " + err.Error())}, http.StatusInternalServerError) return } JSONResponse(w, p, http.StatusOK) @@ -457,13 +457,13 @@ func API_SMTP(w http.ResponseWriter, r *http.Request) { // 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) + JSONResponse(w, models.Response{Success: false, Message: util.T("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 { - JSONResponse(w, models.Response{Success: false, Message: "SMTP name already in use"}, http.StatusConflict) + JSONResponse(w, models.Response{Success: false, Message: util.T("SMTP name already in use")}, http.StatusConflict) Logger.Println(err) return } @@ -471,7 +471,7 @@ func API_SMTP(w http.ResponseWriter, r *http.Request) { 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) + JSONResponse(w, models.Response{Success: false, Message: util.T(err.Error())}, http.StatusInternalServerError) return } JSONResponse(w, s, http.StatusCreated) @@ -485,7 +485,7 @@ func API_SMTP_Id(w http.ResponseWriter, r *http.Request) { 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) + JSONResponse(w, models.Response{Success: false, Message: util.T("SMTP not found")}, http.StatusNotFound) return } switch { @@ -494,10 +494,10 @@ func API_SMTP_Id(w http.ResponseWriter, r *http.Request) { 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) + JSONResponse(w, models.Response{Success: false, Message: util.T("Error deleting SMTP")}, http.StatusInternalServerError) return } - JSONResponse(w, models.Response{Success: true, Message: "SMTP Deleted Successfully"}, http.StatusOK) + JSONResponse(w, models.Response{Success: true, Message: util.T("SMTP Deleted Successfully")}, http.StatusOK) case r.Method == "PUT": s = models.SMTP{} err = json.NewDecoder(r.Body).Decode(&s) @@ -505,19 +505,19 @@ func API_SMTP_Id(w http.ResponseWriter, r *http.Request) { Logger.Println(err) } if s.Id != id { - JSONResponse(w, models.Response{Success: false, Message: "/:id and /:smtp_id mismatch"}, http.StatusBadRequest) + JSONResponse(w, models.Response{Success: false, Message: util.T("/: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) + JSONResponse(w, models.Response{Success: false, Message: util.T(err.Error())}, 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) + JSONResponse(w, models.Response{Success: false, Message: util.T("Error updating page")}, http.StatusInternalServerError) return } JSONResponse(w, s, http.StatusOK) @@ -528,7 +528,7 @@ func API_SMTP_Id(w http.ResponseWriter, r *http.Request) { func API_Import_Group(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) + JSONResponse(w, models.Response{Success: false, Message: util.T("Error parsing CSV")}, http.StatusInternalServerError) return } JSONResponse(w, ts, http.StatusOK) @@ -539,7 +539,7 @@ func API_Import_Group(w http.ResponseWriter, r *http.Request) { // Returns a Message object func API_Import_Email(w http.ResponseWriter, r *http.Request) { if r.Method != "POST" { - JSONResponse(w, models.Response{Success: false, Message: "Method not allowed"}, http.StatusBadRequest) + JSONResponse(w, models.Response{Success: false, Message: util.T("Method not allowed")}, http.StatusBadRequest) return } ir := struct { @@ -548,7 +548,7 @@ func API_Import_Email(w http.ResponseWriter, r *http.Request) { }{} err := json.NewDecoder(r.Body).Decode(&ir) if err != nil { - JSONResponse(w, models.Response{Success: false, Message: "Error decoding JSON Request"}, http.StatusBadRequest) + JSONResponse(w, models.Response{Success: false, Message: util.T("Error decoding JSON Request")}, http.StatusBadRequest) return } e, err := email.NewEmailFromReader(strings.NewReader(ir.Content)) @@ -561,7 +561,7 @@ func API_Import_Email(w http.ResponseWriter, r *http.Request) { 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) + JSONResponse(w, models.Response{Success: false, Message: util.T(err.Error())}, http.StatusBadRequest) return } d.Find("a").Each(func(i int, a *goquery.Selection) { @@ -569,7 +569,7 @@ func API_Import_Email(w http.ResponseWriter, r *http.Request) { }) h, err := d.Html() if err != nil { - JSONResponse(w, models.Response{Success: false, Message: err.Error()}, http.StatusInternalServerError) + JSONResponse(w, models.Response{Success: false, Message: util.T(err.Error())}, http.StatusInternalServerError) return } e.HTML = []byte(h) @@ -589,16 +589,16 @@ func API_Import_Email(w http.ResponseWriter, r *http.Request) { func API_Import_Site(w http.ResponseWriter, r *http.Request) { cr := cloneRequest{} if r.Method != "POST" { - JSONResponse(w, models.Response{Success: false, Message: "Method not allowed"}, http.StatusBadRequest) + JSONResponse(w, models.Response{Success: false, Message: util.T("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) + JSONResponse(w, models.Response{Success: false, Message: util.T("Error decoding JSON Request")}, http.StatusBadRequest) return } if err = cr.validate(); err != nil { - JSONResponse(w, models.Response{Success: false, Message: err.Error()}, http.StatusBadRequest) + JSONResponse(w, models.Response{Success: false, Message: util.T(err.Error())}, http.StatusBadRequest) return } tr := &http.Transport{ @@ -609,13 +609,13 @@ func API_Import_Site(w http.ResponseWriter, r *http.Request) { 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) + JSONResponse(w, models.Response{Success: false, Message: util.T(err.Error())}, http.StatusBadRequest) return } // 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) + JSONResponse(w, models.Response{Success: false, Message: util.T(err.Error())}, http.StatusBadRequest) return } // Assuming we don't want to include resources, we'll need a base href @@ -634,7 +634,7 @@ func API_Import_Site(w http.ResponseWriter, r *http.Request) { }) h, err := d.Html() if err != nil { - JSONResponse(w, models.Response{Success: false, Message: err.Error()}, http.StatusInternalServerError) + JSONResponse(w, models.Response{Success: false, Message: util.T(err.Error())}, http.StatusInternalServerError) return } cs := cloneResponse{HTML: h} @@ -647,17 +647,17 @@ func API_Import_Site(w http.ResponseWriter, r *http.Request) { func API_Send_Test_Email(w http.ResponseWriter, r *http.Request) { s := &models.SendTestEmailRequest{} if r.Method != "POST" { - JSONResponse(w, models.Response{Success: false, Message: "Method not allowed"}, http.StatusBadRequest) + JSONResponse(w, models.Response{Success: false, Message: util.T("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) + JSONResponse(w, models.Response{Success: false, Message: util.T("Error decoding JSON Request")}, http.StatusBadRequest) return } // Validate the given request if err = s.Validate(); err != nil { - JSONResponse(w, models.Response{Success: false, Message: err.Error()}, http.StatusBadRequest) + JSONResponse(w, models.Response{Success: false, Message: util.T(err.Error())}, http.StatusBadRequest) return } @@ -682,10 +682,10 @@ func API_Send_Test_Email(w http.ResponseWriter, r *http.Request) { s.Template, err = models.GetTemplateByName(s.Template.Name, ctx.Get(r, "user_id").(int64)) if err == gorm.ErrRecordNotFound { Logger.Printf("Error - Template %s does not exist", s.Template.Name) - JSONResponse(w, models.Response{Success: false, Message: models.ErrTemplateNotFound.Error()}, http.StatusBadRequest) + JSONResponse(w, models.Response{Success: false, Message: util.T(models.ErrTemplateNotFound.Error())}, http.StatusBadRequest) } else if err != nil { Logger.Println(err) - JSONResponse(w, models.Response{Success: false, Message: err.Error()}, http.StatusBadRequest) + JSONResponse(w, models.Response{Success: false, Message: util.T(err.Error())}, http.StatusBadRequest) return } } @@ -696,10 +696,10 @@ func API_Send_Test_Email(w http.ResponseWriter, r *http.Request) { s.SMTP, err = models.GetSMTPByName(s.SMTP.Name, ctx.Get(r, "user_id").(int64)) if err == gorm.ErrRecordNotFound { Logger.Printf("Error - Sending profile %s does not exist", s.SMTP.Name) - JSONResponse(w, models.Response{Success: false, Message: models.ErrSMTPNotFound.Error()}, http.StatusBadRequest) + JSONResponse(w, models.Response{Success: false, Message: util.T(models.ErrSMTPNotFound.Error())}, http.StatusBadRequest) } else if err != nil { Logger.Println(err) - JSONResponse(w, models.Response{Success: false, Message: err.Error()}, http.StatusBadRequest) + JSONResponse(w, models.Response{Success: false, Message: util.T(err.Error())}, http.StatusBadRequest) return } } @@ -707,10 +707,10 @@ func API_Send_Test_Email(w http.ResponseWriter, r *http.Request) { // Send the test email err = worker.SendTestEmail(s) if err != nil { - JSONResponse(w, models.Response{Success: false, Message: err.Error()}, http.StatusInternalServerError) + JSONResponse(w, models.Response{Success: false, Message: util.T(err.Error())}, http.StatusInternalServerError) return } - JSONResponse(w, models.Response{Success: true, Message: "Email Sent"}, http.StatusOK) + JSONResponse(w, models.Response{Success: true, Message: util.T("Email Sent")}, http.StatusOK) return } diff --git a/controllers/route.go b/controllers/route.go index 91e56d8e..e08167cc 100644 --- a/controllers/route.go +++ b/controllers/route.go @@ -21,6 +21,7 @@ import ( "github.com/gorilla/csrf" "github.com/gorilla/mux" "github.com/gorilla/sessions" + "../util" ) // Logger is used to send logging messages to stdout. @@ -241,7 +242,9 @@ func PhishHandler(w http.ResponseWriter, r *http.Request) { } } var htmlBuff bytes.Buffer - tmpl, err := template.New("html_template").Parse(p.HTML) + tmpl, err := template.New("html_template").Funcs(template.FuncMap{ + "T": util.T, + }).Parse(p.HTML) if err != nil { Logger.Println(err) http.NotFound(w, r) @@ -295,7 +298,9 @@ func Register(w http.ResponseWriter, r *http.Request) { case r.Method == "GET": params.Flashes = session.Flashes() session.Save(r, w) - templates := template.New("template") + templates := template.New("template").Funcs(template.FuncMap{ + "T": util.T, + }) _, err := templates.ParseFiles("templates/register.html", "templates/flashes.html") if err != nil { Logger.Println(err) @@ -455,7 +460,9 @@ func Login(w http.ResponseWriter, r *http.Request) { case r.Method == "GET": params.Flashes = session.Flashes() session.Save(r, w) - templates := template.New("template") + templates := template.New("template").Funcs(template.FuncMap{ + "T": util.T, + }) _, err := templates.ParseFiles("templates/login.html", "templates/flashes.html") if err != nil { Logger.Println(err) @@ -512,7 +519,9 @@ func Clone(w http.ResponseWriter, r *http.Request) { } func getTemplate(w http.ResponseWriter, tmpl string) *template.Template { - templates := template.New("template") + templates := template.New("template").Funcs(template.FuncMap{ + "T": util.T, + }) _, err := templates.ParseFiles("templates/base.html", "templates/"+tmpl+".html", "templates/flashes.html") if err != nil { Logger.Println(err) diff --git a/gophish.go b/gophish.go index 9034ca01..0b9e0de0 100644 --- a/gophish.go +++ b/gophish.go @@ -36,16 +36,18 @@ import ( "github.com/NYTimes/gziphandler" "github.com/gophish/gophish/auth" "github.com/gophish/gophish/config" - "github.com/gophish/gophish/controllers" + "./controllers" "github.com/gophish/gophish/models" - "github.com/gophish/gophish/util" + "./util" "github.com/gorilla/handlers" + "github.com/nicksnyder/go-i18n/i18n" ) var Logger = log.New(os.Stdout, " ", log.Ldate|log.Ltime|log.Lshortfile) func main() { // Setup the global variables and settings + i18n.MustLoadTranslationFile("translations/en-US.all.json") err := models.Setup() if err != nil { fmt.Println(err) diff --git a/models/campaign.go b/models/campaign.go index 9b6d9ecb..effe11a5 100644 --- a/models/campaign.go +++ b/models/campaign.go @@ -5,6 +5,7 @@ import ( "time" "github.com/jinzhu/gorm" + "../util" ) // Campaign is a struct representing a created campaign @@ -65,31 +66,31 @@ type CampaignStats struct { } // ErrCampaignNameNotSpecified indicates there was no template given by the user -var ErrCampaignNameNotSpecified = errors.New("Campaign name not specified") +var ErrCampaignNameNotSpecified = errors.New(util.T("Campaign name not specified")) // ErrGroupNotSpecified indicates there was no template given by the user -var ErrGroupNotSpecified = errors.New("No groups specified") +var ErrGroupNotSpecified = errors.New(util.T("No groups specified")) // ErrTemplateNotSpecified indicates there was no template given by the user -var ErrTemplateNotSpecified = errors.New("No email template specified") +var ErrTemplateNotSpecified = errors.New(util.T("No email template specified")) // ErrPageNotSpecified indicates a landing page was not provided for the campaign -var ErrPageNotSpecified = errors.New("No landing page specified") +var ErrPageNotSpecified = errors.New(util.T("No landing page specified")) // ErrSMTPNotSpecified indicates a sending profile was not provided for the campaign -var ErrSMTPNotSpecified = errors.New("No sending profile specified") +var ErrSMTPNotSpecified = errors.New(util.T("No sending profile specified")) // ErrTemplateNotFound indicates the template specified does not exist in the database -var ErrTemplateNotFound = errors.New("Template not found") +var ErrTemplateNotFound = errors.New(util.T("Template not found")) // ErrGroupnNotFound indicates a group specified by the user does not exist in the database -var ErrGroupNotFound = errors.New("Group not found") +var ErrGroupNotFound = errors.New(util.T("Group not found")) // 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(util.T("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") +var ErrSMTPNotFound = errors.New(util.T("Sending profile not found")) // Validate checks to make sure there are no invalid fields in a submitted campaign func (c *Campaign) Validate() error { diff --git a/models/group.go b/models/group.go index 6f53698a..ce4b37fe 100644 --- a/models/group.go +++ b/models/group.go @@ -6,6 +6,7 @@ import ( "time" "github.com/jinzhu/gorm" + "../util" ) // Group contains the fields needed for a user -> group mapping @@ -51,13 +52,13 @@ type Target struct { } // ErrNoEmailSpecified is thrown when no email is specified for the Target -var ErrEmailNotSpecified = errors.New("No email address specified") +var ErrEmailNotSpecified = errors.New(util.T("No email address specified")) // ErrGroupNameNotSpecified is thrown when a group name is not specified -var ErrGroupNameNotSpecified = errors.New("Group name not specified") +var ErrGroupNameNotSpecified = errors.New(util.T("Group name not specified")) // ErrNoTargetsSpecified is thrown when no targets are specified by the user -var ErrNoTargetsSpecified = errors.New("No targets specified") +var ErrNoTargetsSpecified = errors.New(util.T("No targets specified")) // Validate performs validation on a group given by the user func (g *Group) Validate() error { diff --git a/models/models.go b/models/models.go index 08e79e88..954bd9f8 100644 --- a/models/models.go +++ b/models/models.go @@ -14,13 +14,14 @@ import ( "github.com/gophish/gophish/config" "github.com/jinzhu/gorm" _ "github.com/mattn/go-sqlite3" // Blank import needed to import sqlite3 + "../util" ) var db *gorm.DB var err error // ErrUsernameTaken is thrown when a user attempts to register a username that is taken. -var ErrUsernameTaken = errors.New("username already taken") +var ErrUsernameTaken = errors.New(util.T("username already taken")) // Logger is a global logger used to show informational, warning, and error messages var Logger = log.New(os.Stdout, " ", log.Ldate|log.Ltime|log.Lshortfile) diff --git a/models/page.go b/models/page.go index 29ee4c3b..c68489b4 100644 --- a/models/page.go +++ b/models/page.go @@ -6,6 +6,7 @@ import ( "time" "github.com/PuerkitoBio/goquery" + "../util" ) // Page contains the fields used for a Page model @@ -21,7 +22,7 @@ type Page struct { } // ErrPageNameNotSpecified is thrown if the name of the landing page is blank. -var ErrPageNameNotSpecified = errors.New("Page Name not specified") +var ErrPageNameNotSpecified = errors.New(util.T("Page Name not specified")) // parseHTML parses the page HTML on save to handle the // capturing (or lack thereof!) of credentials and passwords diff --git a/models/result.go b/models/result.go index 6e8e9301..70c9db48 100644 --- a/models/result.go +++ b/models/result.go @@ -8,6 +8,7 @@ import ( "github.com/jinzhu/gorm" "github.com/oschwald/maxminddb-golang" + "../util" ) type mmCity struct { diff --git a/models/smtp.go b/models/smtp.go index 298217a2..f7763289 100644 --- a/models/smtp.go +++ b/models/smtp.go @@ -6,6 +6,7 @@ import ( "strconv" "strings" "time" + "../util" ) // SMTP contains the attributes needed to handle the sending of campaign emails @@ -24,14 +25,14 @@ type SMTP struct { // ErrFromAddressNotSpecified is thrown when there is no "From" address // specified in the SMTP configuration -var ErrFromAddressNotSpecified = errors.New("No From Address specified") +var ErrFromAddressNotSpecified = errors.New(util.T("No From Address specified")) // ErrHostNotSpecified is thrown when there is no Host specified // in the SMTP configuration -var ErrHostNotSpecified = errors.New("No SMTP Host specified") +var ErrHostNotSpecified = errors.New(util.T("No SMTP Host specified")) // ErrInvalidHost indicates that the SMTP server string is invalid -var ErrInvalidHost = errors.New("Invalid SMTP server address") +var ErrInvalidHost = errors.New(util.T("Invalid SMTP server address")) // TableName specifies the database tablename for Gorm to use func (s SMTP) TableName() string { diff --git a/models/template.go b/models/template.go index 38527340..5f4a246a 100644 --- a/models/template.go +++ b/models/template.go @@ -7,6 +7,7 @@ import ( "time" "github.com/jinzhu/gorm" + "../util" ) // Template models hold the attributes for an email template to be sent to targets @@ -22,10 +23,10 @@ type Template struct { } // ErrTemplateNameNotSpecified is thrown when a template name is not specified -var ErrTemplateNameNotSpecified = errors.New("Template name not specified") +var ErrTemplateNameNotSpecified = errors.New(util.T("Template name not specified")) // ErrTemplateMissingParameter is thrown when a needed parameter is not provided -var ErrTemplateMissingParameter = errors.New("Need to specify at least plaintext or HTML content") +var ErrTemplateMissingParameter = errors.New(util.T("Need to specify at least plaintext or HTML content")) // Validate checks the given template to make sure values are appropriate and complete func (t *Template) Validate() error { diff --git a/static/js/src/app/dashboard.js b/static/js/src/app/dashboard.js index 6bfd8acb..cb740543 100644 --- a/static/js/src/app/dashboard.js +++ b/static/js/src/app/dashboard.js @@ -10,7 +10,7 @@ var labels = { } function deleteCampaign(idx) { - if (confirm("Delete " + campaigns[idx].name + "?")) { + if (confirm(T("Delete ") + campaigns[idx].name + "?")) { api.campaignId.delete(campaigns[idx].id) .success(function(data) { successFlash(data.message) @@ -68,11 +68,11 @@ $(document).ready(function() { campaignTable.row.add([ escapeHtml(campaign.name), campaign_date, - "" + campaign.status + "", - "
\ + "" + T(campaign.status) + "", + "
\ \ \ -
" ]).draw() diff --git a/templates/base.html b/templates/base.html index 130f7946..b48f107c 100644 --- a/templates/base.html +++ b/templates/base.html @@ -33,7 +33,7 @@
{{else}} - + {{end}} @@ -79,6 +79,7 @@ + {{template "scripts" .}} diff --git a/templates/campaign_results.html b/templates/campaign_results.html index 6cb998a7..5b617110 100644 --- a/templates/campaign_results.html +++ b/templates/campaign_results.html @@ -3,24 +3,24 @@ @@ -132,22 +132,22 @@
diff --git a/templates/users.html b/templates/users.html index e58c1796..d72d572d 100644 --- a/templates/users.html +++ b/templates/users.html @@ -3,24 +3,24 @@
@@ -29,12 +29,12 @@

- Users & Groups + {{T "Users & Groups"}}

- +
 
@@ -42,16 +42,16 @@
- - - + + + @@ -66,35 +66,35 @@ - - - - + + + +
First NameLast NameEmailPosition{{T "First Name"}}{{T "Last Name"}}{{T "Email"}}{{T "Position"}}
diff --git a/util/util.go b/util/util.go index 269fa747..6a6185e1 100644 --- a/util/util.go +++ b/util/util.go @@ -21,11 +21,21 @@ import ( "github.com/gophish/gophish/models" "github.com/jordan-wright/email" + "github.com/nicksnyder/go-i18n/i18n" ) // Logger is used to send logging messages to stdout. var Logger = log.New(os.Stdout, " ", log.Ldate|log.Ltime|log.Lshortfile) +func T(text string) string{ + T, _ := i18n.Tfunc("en-US", "en-US") + result := T(text) + if result == text { + fmt.Println("NON-TRANSLATION %s", text) + } + return result +} + // ParseMail takes in an HTTP Request and returns an Email object // TODO: This function will likely be changed to take in a []byte func ParseMail(r *http.Request) (email.Email, error) {