diff --git a/controllers/api.go b/controllers/api.go index 66f1ce7d..f71a43a3 100644 --- a/controllers/api.go +++ b/controllers/api.go @@ -21,6 +21,7 @@ import ( "github.com/jinzhu/gorm" "github.com/jordan-wright/email" "github.com/gophish/gophish/util" + "github.com/gophish/gophish/translations" ) // Worker is the worker that processes phishing events and updates campaigns. @@ -52,9 +53,9 @@ func API_Reset(w http.ResponseWriter, r *http.Request) { u.ApiKey = auth.GenerateSecureKey() err := models.PutUser(&u) if err != nil { - http.Error(w, util.T("Error setting API Key"), http.StatusInternalServerError) + http.Error(w, translations.T("Error setting API Key"), http.StatusInternalServerError) } else { - JSONResponse(w, models.Response{Success: true, Message: util.T("API Key successfully reset!"), Data: u.ApiKey}, http.StatusOK) + JSONResponse(w, models.Response{Success: true, Message: translations.T("API Key successfully reset!"), Data: u.ApiKey}, http.StatusOK) } } } @@ -109,7 +110,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: util.T("Campaign not found")}, http.StatusNotFound) + JSONResponse(w, models.Response{Success: false, Message: translations.T("Campaign not found")}, http.StatusNotFound) return } switch { @@ -118,10 +119,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: util.T("Error deleting campaign")}, http.StatusInternalServerError) + JSONResponse(w, models.Response{Success: false, Message: translations.T("Error deleting campaign")}, http.StatusInternalServerError) return } - JSONResponse(w, models.Response{Success: true, Message: util.T("Campaign deleted successfully!")}, http.StatusOK) + JSONResponse(w, models.Response{Success: true, Message: translations.T("Campaign deleted successfully!")}, http.StatusOK) } } @@ -133,7 +134,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: util.T("Campaign not found")}, http.StatusNotFound) + JSONResponse(w, models.Response{Success: false, Message: translations.T("Campaign not found")}, http.StatusNotFound) return } if r.Method == "GET" { @@ -151,9 +152,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: util.T("Campaign not found")}, http.StatusNotFound) + JSONResponse(w, models.Response{Success: false, Message: translations.T("Campaign not found")}, http.StatusNotFound) } else { - JSONResponse(w, models.Response{Success: false, Message: util.T(err.Error())}, http.StatusInternalServerError) + JSONResponse(w, models.Response{Success: false, Message: translations.T(err.Error())}, http.StatusInternalServerError) } Logger.Println(err) return @@ -171,10 +172,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: util.T("Error completing campaign")}, http.StatusInternalServerError) + JSONResponse(w, models.Response{Success: false, Message: translations.T("Error completing campaign")}, http.StatusInternalServerError) return } - JSONResponse(w, models.Response{Success: true, Message: util.T("Campaign completed successfully!")}, http.StatusOK) + JSONResponse(w, models.Response{Success: true, Message: translations.T("Campaign completed successfully!")}, http.StatusOK) } } @@ -185,7 +186,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: util.T("No groups found")}, http.StatusNotFound) + JSONResponse(w, models.Response{Success: false, Message: translations.T("No groups found")}, http.StatusNotFound) return } JSONResponse(w, gs, http.StatusOK) @@ -195,12 +196,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: util.T("Invalid JSON structure")}, http.StatusBadRequest) + JSONResponse(w, models.Response{Success: false, Message: translations.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: util.T("Group name already in use")}, http.StatusConflict) + JSONResponse(w, models.Response{Success: false, Message: translations.T("Group name already in use")}, http.StatusConflict) return } g.ModifiedDate = time.Now() @@ -235,7 +236,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: util.T("Group not found")}, http.StatusNotFound) + JSONResponse(w, models.Response{Success: false, Message: translations.T("Group not found")}, http.StatusNotFound) return } switch { @@ -244,23 +245,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: util.T("Error deleting group")}, http.StatusInternalServerError) + JSONResponse(w, models.Response{Success: false, Message: translations.T("Error deleting group")}, http.StatusInternalServerError) return } - JSONResponse(w, models.Response{Success: true, Message: util.T("Group deleted successfully!")}, http.StatusOK) + JSONResponse(w, models.Response{Success: true, Message: translations.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: util.T("Error: /:id and group_id mismatch")}, http.StatusInternalServerError) + JSONResponse(w, models.Response{Success: false, Message: translations.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: util.T(err.Error())}, http.StatusBadRequest) + JSONResponse(w, models.Response{Success: false, Message: translations.T(err.Error())}, http.StatusBadRequest) return } JSONResponse(w, g, http.StatusOK) @@ -275,7 +276,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: util.T("Group not found")}, http.StatusNotFound) + JSONResponse(w, models.Response{Success: false, Message: translations.T("Group not found")}, http.StatusNotFound) return } JSONResponse(w, g, http.StatusOK) @@ -297,27 +298,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: util.T("Invalid JSON structure")}, http.StatusBadRequest) + JSONResponse(w, models.Response{Success: false, Message: translations.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: util.T("Template name already in use")}, http.StatusConflict) + JSONResponse(w, models.Response{Success: false, Message: translations.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: util.T(err.Error())}, http.StatusBadRequest) + JSONResponse(w, models.Response{Success: false, Message: translations.T(err.Error())}, http.StatusBadRequest) return } if err == models.ErrTemplateMissingParameter { - JSONResponse(w, models.Response{Success: false, Message: util.T(err.Error())}, http.StatusBadRequest) + JSONResponse(w, models.Response{Success: false, Message: translations.T(err.Error())}, http.StatusBadRequest) return } if err != nil { - JSONResponse(w, models.Response{Success: false, Message: util.T("Error inserting template into database")}, http.StatusInternalServerError) + JSONResponse(w, models.Response{Success: false, Message: translations.T("Error inserting template into database")}, http.StatusInternalServerError) Logger.Println(err) return } @@ -331,7 +332,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: util.T("Template not found")}, http.StatusNotFound) + JSONResponse(w, models.Response{Success: false, Message: translations.T("Template not found")}, http.StatusNotFound) return } switch { @@ -340,10 +341,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: util.T("Error deleting template")}, http.StatusInternalServerError) + JSONResponse(w, models.Response{Success: false, Message: translations.T("Error deleting template")}, http.StatusInternalServerError) return } - JSONResponse(w, models.Response{Success: true, Message: util.T("Template deleted successfully!")}, http.StatusOK) + JSONResponse(w, models.Response{Success: true, Message: translations.T("Template deleted successfully!")}, http.StatusOK) case r.Method == "PUT": t = models.Template{} err = json.NewDecoder(r.Body).Decode(&t) @@ -351,14 +352,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: util.T("Error: /:id and template_id mismatch")}, http.StatusBadRequest) + JSONResponse(w, models.Response{Success: false, Message: translations.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: util.T(err.Error())}, http.StatusBadRequest) + JSONResponse(w, models.Response{Success: false, Message: translations.T(err.Error())}, http.StatusBadRequest) return } JSONResponse(w, t, http.StatusOK) @@ -380,13 +381,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: util.T("Invalid request")}, http.StatusBadRequest) + JSONResponse(w, models.Response{Success: false, Message: translations.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: util.T("Page name already in use")}, http.StatusConflict) + JSONResponse(w, models.Response{Success: false, Message: translations.T("Page name already in use")}, http.StatusConflict) Logger.Println(err) return } @@ -394,7 +395,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: util.T(err.Error())}, http.StatusInternalServerError) + JSONResponse(w, models.Response{Success: false, Message: translations.T(err.Error())}, http.StatusInternalServerError) return } JSONResponse(w, p, http.StatusCreated) @@ -408,7 +409,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: util.T("Page not found")}, http.StatusNotFound) + JSONResponse(w, models.Response{Success: false, Message: translations.T("Page not found")}, http.StatusNotFound) return } switch { @@ -417,10 +418,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: util.T("Error deleting page")}, http.StatusInternalServerError) + JSONResponse(w, models.Response{Success: false, Message: translations.T("Error deleting page")}, http.StatusInternalServerError) return } - JSONResponse(w, models.Response{Success: true, Message: util.T("Page Deleted Successfully")}, http.StatusOK) + JSONResponse(w, models.Response{Success: true, Message: translations.T("Page Deleted Successfully")}, http.StatusOK) case r.Method == "PUT": p = models.Page{} err = json.NewDecoder(r.Body).Decode(&p) @@ -428,14 +429,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: util.T("/:id and /:page_id mismatch")}, http.StatusBadRequest) + JSONResponse(w, models.Response{Success: false, Message: translations.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: util.T("Error updating page:") + " " + err.Error()}, http.StatusInternalServerError) + JSONResponse(w, models.Response{Success: false, Message: translations.T("Error updating page:") + " " + err.Error()}, http.StatusInternalServerError) return } JSONResponse(w, p, http.StatusOK) @@ -457,13 +458,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: util.T("Invalid request")}, http.StatusBadRequest) + JSONResponse(w, models.Response{Success: false, Message: translations.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: util.T("SMTP name already in use")}, http.StatusConflict) + JSONResponse(w, models.Response{Success: false, Message: translations.T("SMTP name already in use")}, http.StatusConflict) Logger.Println(err) return } @@ -471,7 +472,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: util.T(err.Error())}, http.StatusInternalServerError) + JSONResponse(w, models.Response{Success: false, Message: translations.T(err.Error())}, http.StatusInternalServerError) return } JSONResponse(w, s, http.StatusCreated) @@ -485,7 +486,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: util.T("SMTP not found")}, http.StatusNotFound) + JSONResponse(w, models.Response{Success: false, Message: translations.T("SMTP not found")}, http.StatusNotFound) return } switch { @@ -494,10 +495,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: util.T("Error deleting SMTP")}, http.StatusInternalServerError) + JSONResponse(w, models.Response{Success: false, Message: translations.T("Error deleting SMTP")}, http.StatusInternalServerError) return } - JSONResponse(w, models.Response{Success: true, Message: util.T("SMTP Deleted Successfully")}, http.StatusOK) + JSONResponse(w, models.Response{Success: true, Message: translations.T("SMTP Deleted Successfully")}, http.StatusOK) case r.Method == "PUT": s = models.SMTP{} err = json.NewDecoder(r.Body).Decode(&s) @@ -505,19 +506,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: util.T("/:id and /:smtp_id mismatch")}, http.StatusBadRequest) + JSONResponse(w, models.Response{Success: false, Message: translations.T("/:id and /:smtp_id mismatch")}, http.StatusBadRequest) return } err = s.Validate() if err != nil { - JSONResponse(w, models.Response{Success: false, Message: util.T(err.Error())}, http.StatusBadRequest) + JSONResponse(w, models.Response{Success: false, Message: translations.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: util.T("Error updating page")}, http.StatusInternalServerError) + JSONResponse(w, models.Response{Success: false, Message: translations.T("Error updating page")}, http.StatusInternalServerError) return } JSONResponse(w, s, http.StatusOK) @@ -528,7 +529,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: util.T("Error parsing CSV")}, http.StatusInternalServerError) + JSONResponse(w, models.Response{Success: false, Message: translations.T("Error parsing CSV")}, http.StatusInternalServerError) return } JSONResponse(w, ts, http.StatusOK) @@ -539,7 +540,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: util.T("Method not allowed")}, http.StatusBadRequest) + JSONResponse(w, models.Response{Success: false, Message: translations.T("Method not allowed")}, http.StatusBadRequest) return } ir := struct { @@ -548,7 +549,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: util.T("Error decoding JSON Request")}, http.StatusBadRequest) + JSONResponse(w, models.Response{Success: false, Message: translations.T("Error decoding JSON Request")}, http.StatusBadRequest) return } e, err := email.NewEmailFromReader(strings.NewReader(ir.Content)) @@ -561,7 +562,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: util.T(err.Error())}, http.StatusBadRequest) + JSONResponse(w, models.Response{Success: false, Message: translations.T(err.Error())}, http.StatusBadRequest) return } d.Find("a").Each(func(i int, a *goquery.Selection) { @@ -569,7 +570,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: util.T(err.Error())}, http.StatusInternalServerError) + JSONResponse(w, models.Response{Success: false, Message: translations.T(err.Error())}, http.StatusInternalServerError) return } e.HTML = []byte(h) @@ -589,16 +590,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: util.T("Method not allowed")}, http.StatusBadRequest) + JSONResponse(w, models.Response{Success: false, Message: translations.T("Method not allowed")}, http.StatusBadRequest) return } err := json.NewDecoder(r.Body).Decode(&cr) if err != nil { - JSONResponse(w, models.Response{Success: false, Message: util.T("Error decoding JSON Request")}, http.StatusBadRequest) + JSONResponse(w, models.Response{Success: false, Message: translations.T("Error decoding JSON Request")}, http.StatusBadRequest) return } if err = cr.validate(); err != nil { - JSONResponse(w, models.Response{Success: false, Message: util.T(err.Error())}, http.StatusBadRequest) + JSONResponse(w, models.Response{Success: false, Message: translations.T(err.Error())}, http.StatusBadRequest) return } tr := &http.Transport{ @@ -609,13 +610,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: util.T(err.Error())}, http.StatusBadRequest) + JSONResponse(w, models.Response{Success: false, Message: translations.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: util.T(err.Error())}, http.StatusBadRequest) + JSONResponse(w, models.Response{Success: false, Message: translations.T(err.Error())}, http.StatusBadRequest) return } // Assuming we don't want to include resources, we'll need a base href @@ -634,7 +635,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: util.T(err.Error())}, http.StatusInternalServerError) + JSONResponse(w, models.Response{Success: false, Message: translations.T(err.Error())}, http.StatusInternalServerError) return } cs := cloneResponse{HTML: h} @@ -647,17 +648,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: util.T("Method not allowed")}, http.StatusBadRequest) + JSONResponse(w, models.Response{Success: false, Message: translations.T("Method not allowed")}, http.StatusBadRequest) return } err := json.NewDecoder(r.Body).Decode(s) if err != nil { - JSONResponse(w, models.Response{Success: false, Message: util.T("Error decoding JSON Request")}, http.StatusBadRequest) + JSONResponse(w, models.Response{Success: false, Message: translations.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: util.T(err.Error())}, http.StatusBadRequest) + JSONResponse(w, models.Response{Success: false, Message: translations.T(err.Error())}, http.StatusBadRequest) return } @@ -682,10 +683,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: util.T(models.ErrTemplateNotFound.Error())}, http.StatusBadRequest) + JSONResponse(w, models.Response{Success: false, Message: translations.T(models.ErrTemplateNotFound.Error())}, http.StatusBadRequest) } else if err != nil { Logger.Println(err) - JSONResponse(w, models.Response{Success: false, Message: util.T(err.Error())}, http.StatusBadRequest) + JSONResponse(w, models.Response{Success: false, Message: translations.T(err.Error())}, http.StatusBadRequest) return } } @@ -696,10 +697,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: util.T(models.ErrSMTPNotFound.Error())}, http.StatusBadRequest) + JSONResponse(w, models.Response{Success: false, Message: translations.T(models.ErrSMTPNotFound.Error())}, http.StatusBadRequest) } else if err != nil { Logger.Println(err) - JSONResponse(w, models.Response{Success: false, Message: util.T(err.Error())}, http.StatusBadRequest) + JSONResponse(w, models.Response{Success: false, Message: translations.T(err.Error())}, http.StatusBadRequest) return } } @@ -707,10 +708,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: util.T(err.Error())}, http.StatusInternalServerError) + JSONResponse(w, models.Response{Success: false, Message: translations.T(err.Error())}, http.StatusInternalServerError) return } - JSONResponse(w, models.Response{Success: true, Message: util.T("Email Sent")}, http.StatusOK) + JSONResponse(w, models.Response{Success: true, Message: translations.T("Email Sent")}, http.StatusOK) return } diff --git a/controllers/route.go b/controllers/route.go index 961599a3..d02a4b26 100644 --- a/controllers/route.go +++ b/controllers/route.go @@ -21,7 +21,7 @@ import ( "github.com/gorilla/csrf" "github.com/gorilla/mux" "github.com/gorilla/sessions" - "github.com/gophish/gophish/util" + "github.com/gophish/gophish/translations" ) // Logger is used to send logging messages to stdout. @@ -243,7 +243,7 @@ func PhishHandler(w http.ResponseWriter, r *http.Request) { } var htmlBuff bytes.Buffer tmpl, err := template.New("html_template").Funcs(template.FuncMap{ - "T": util.T, + "T": translations.T, }).Parse(p.HTML) if err != nil { Logger.Println(err) @@ -299,7 +299,7 @@ func Register(w http.ResponseWriter, r *http.Request) { params.Flashes = session.Flashes() session.Save(r, w) templates := template.New("template").Funcs(template.FuncMap{ - "T": util.T, + "T": translations.T, }) _, err := templates.ParseFiles("templates/register.html", "templates/flashes.html") if err != nil { @@ -313,7 +313,7 @@ func Register(w http.ResponseWriter, r *http.Request) { if succ { session.AddFlash(models.Flash{ Type: "success", - Message: util.T("Registration successful!."), + Message: translations.T("Registration successful!."), }) session.Save(r, w) http.Redirect(w, r, "/login", 302) @@ -429,9 +429,9 @@ func Settings(w http.ResponseWriter, r *http.Request) { getTemplate(w, "settings").ExecuteTemplate(w, "base", params) case r.Method == "POST": err := auth.ChangePassword(r) - msg := models.Response{Success: true, Message: util.T("Settings Updated Successfully")} + msg := models.Response{Success: true, Message: translations.T("Settings Updated Successfully")} if err == auth.ErrInvalidPassword { - msg.Message = util.T("Invalid Password") + msg.Message = translations.T("Invalid Password") msg.Success = false JSONResponse(w, msg, http.StatusBadRequest) return @@ -461,7 +461,7 @@ func Login(w http.ResponseWriter, r *http.Request) { params.Flashes = session.Flashes() session.Save(r, w) templates := template.New("template").Funcs(template.FuncMap{ - "T": util.T, + "T": translations.T, }) _, err := templates.ParseFiles("templates/login.html", "templates/flashes.html") if err != nil { @@ -480,7 +480,7 @@ func Login(w http.ResponseWriter, r *http.Request) { session.Save(r, w) http.Redirect(w, r, "/", 302) } else { - Flash(w, r, "danger", util.T("Invalid Username/Password")) + Flash(w, r, "danger", translations.T("Invalid Username/Password")) http.Redirect(w, r, "/login", 302) } } @@ -492,14 +492,14 @@ func Logout(w http.ResponseWriter, r *http.Request) { // Now that we are all registered, we can log the user in session := ctx.Get(r, "session").(*sessions.Session) delete(session.Values, "id") - Flash(w, r, "success", util.T("You have successfully logged out")) + Flash(w, r, "success", translations.T("You have successfully logged out")) http.Redirect(w, r, "/login", 302) } // Preview allows for the viewing of page html in a separate browser window func Preview(w http.ResponseWriter, r *http.Request) { if r.Method != "POST" { - http.Error(w, util.T("Method not allowed"), http.StatusBadRequest) + http.Error(w, translations.T("Method not allowed"), http.StatusBadRequest) return } fmt.Fprintf(w, "%s", r.FormValue("html")) @@ -509,18 +509,18 @@ func Preview(w http.ResponseWriter, r *http.Request) { func Clone(w http.ResponseWriter, r *http.Request) { vars := mux.Vars(r) if r.Method != "POST" { - http.Error(w, util.T("Method not allowed"), http.StatusBadRequest) + http.Error(w, translations.T("Method not allowed"), http.StatusBadRequest) return } if url, ok := vars["url"]; ok { Logger.Println(url) } - http.Error(w, util.T("No URL given."), http.StatusBadRequest) + http.Error(w, translations.T("No URL given."), http.StatusBadRequest) } func getTemplate(w http.ResponseWriter, tmpl string) *template.Template { templates := template.New("template").Funcs(template.FuncMap{ - "T": util.T, + "T": translations.T, }) _, err := templates.ParseFiles("templates/base.html", "templates/"+tmpl+".html", "templates/flashes.html") if err != nil { diff --git a/gophish.go b/gophish.go index c1eb72d2..f23e0d2e 100644 --- a/gophish.go +++ b/gophish.go @@ -39,6 +39,7 @@ import ( "github.com/gophish/gophish/controllers" "github.com/gophish/gophish/models" "github.com/gophish/gophish/util" + "github.com/gophish/gophish/translations" "github.com/gorilla/handlers" ) @@ -53,7 +54,7 @@ func main() { wg := &sync.WaitGroup{} wg.Add(1) - util.ChangeLang(config.Conf.AdminConf.Language) + translations.ChangeLang(config.Conf.AdminConf.Language) // Start the web servers go func() { defer wg.Done() diff --git a/middleware/middleware.go b/middleware/middleware.go index 115f5393..bda3e46f 100644 --- a/middleware/middleware.go +++ b/middleware/middleware.go @@ -10,7 +10,7 @@ import ( ctx "github.com/gophish/gophish/context" "github.com/gophish/gophish/models" "github.com/gorilla/csrf" - "github.com/gophish/gophish/util" + "github.com/gophish/gophish/translations" ) var CSRFExemptPrefixes = []string{ @@ -37,7 +37,7 @@ func GetContext(handler http.Handler) http.HandlerFunc { // Parse the request form err := r.ParseForm() if err != nil { - http.Error(w, util.T("Error parsing request"), http.StatusInternalServerError) + http.Error(w, translations.T("Error parsing request"), http.StatusInternalServerError) } // Set the context appropriately here. // Set the session @@ -78,7 +78,7 @@ func RequireAPIKey(handler http.Handler) http.HandlerFunc { } else { u, err := models.GetUserByAPIKey(ak) if err != nil { - JSONError(w, 400, util.T("Invalid API Key")) + JSONError(w, 400, translations.T("Invalid API Key")) return } r = ctx.Set(r, "user_id", u.Id) diff --git a/models/campaign.go b/models/campaign.go index 208f84ce..232ed84e 100644 --- a/models/campaign.go +++ b/models/campaign.go @@ -5,7 +5,7 @@ import ( "time" "github.com/jinzhu/gorm" - "github.com/gophish/gophish/util" + "github.com/gophish/gophish/translations" ) // Campaign is a struct representing a created campaign @@ -66,31 +66,31 @@ type CampaignStats struct { } // ErrCampaignNameNotSpecified indicates there was no template given by the user -var ErrCampaignNameNotSpecified = errors.New(util.T("Campaign name not specified")) +var ErrCampaignNameNotSpecified = errors.New(translations.T("Campaign name not specified")) // ErrGroupNotSpecified indicates there was no template given by the user -var ErrGroupNotSpecified = errors.New(util.T("No groups specified")) +var ErrGroupNotSpecified = errors.New(translations.T("No groups specified")) // ErrTemplateNotSpecified indicates there was no template given by the user -var ErrTemplateNotSpecified = errors.New(util.T("No email template specified")) +var ErrTemplateNotSpecified = errors.New(translations.T("No email template specified")) // ErrPageNotSpecified indicates a landing page was not provided for the campaign -var ErrPageNotSpecified = errors.New(util.T("No landing page specified")) +var ErrPageNotSpecified = errors.New(translations.T("No landing page specified")) // ErrSMTPNotSpecified indicates a sending profile was not provided for the campaign -var ErrSMTPNotSpecified = errors.New(util.T("No sending profile specified")) +var ErrSMTPNotSpecified = errors.New(translations.T("No sending profile specified")) // ErrTemplateNotFound indicates the template specified does not exist in the database -var ErrTemplateNotFound = errors.New(util.T("Template not found")) +var ErrTemplateNotFound = errors.New(translations.T("Template not found")) // ErrGroupnNotFound indicates a group specified by the user does not exist in the database -var ErrGroupNotFound = errors.New(util.T("Group not found")) +var ErrGroupNotFound = errors.New(translations.T("Group not found")) // ErrPageNotFound indicates a page specified by the user does not exist in the database -var ErrPageNotFound = errors.New(util.T("Page not found")) +var ErrPageNotFound = errors.New(translations.T("Page not found")) // ErrSMTPNotFound indicates a sending profile specified by the user does not exist in the database -var ErrSMTPNotFound = errors.New(util.T("Sending profile not found")) +var ErrSMTPNotFound = errors.New(translations.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 a685df56..be23ed07 100644 --- a/models/group.go +++ b/models/group.go @@ -6,7 +6,7 @@ import ( "time" "github.com/jinzhu/gorm" - "github.com/gophish/gophish/util" + "github.com/gophish/gophish/translations" ) // Group contains the fields needed for a user -> group mapping @@ -52,13 +52,13 @@ type Target struct { } // ErrNoEmailSpecified is thrown when no email is specified for the Target -var ErrEmailNotSpecified = errors.New(util.T("No email address specified")) +var ErrEmailNotSpecified = errors.New(translations.T("No email address specified")) // ErrGroupNameNotSpecified is thrown when a group name is not specified -var ErrGroupNameNotSpecified = errors.New(util.T("Group name not specified")) +var ErrGroupNameNotSpecified = errors.New(translations.T("Group name not specified")) // ErrNoTargetsSpecified is thrown when no targets are specified by the user -var ErrNoTargetsSpecified = errors.New(util.T("No targets specified")) +var ErrNoTargetsSpecified = errors.New(translations.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 0c3823f7..202c111c 100644 --- a/models/models.go +++ b/models/models.go @@ -14,14 +14,14 @@ import ( "github.com/gophish/gophish/config" "github.com/jinzhu/gorm" _ "github.com/mattn/go-sqlite3" // Blank import needed to import sqlite3 - "github.com/gophish/gophish/util" + "github.com/gophish/gophish/translations" ) 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(util.T("username already taken")) +var ErrUsernameTaken = errors.New(translations.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 9e630e94..b806b6f9 100644 --- a/models/page.go +++ b/models/page.go @@ -6,7 +6,7 @@ import ( "time" "github.com/PuerkitoBio/goquery" - "github.com/gophish/gophish/util" + "github.com/gophish/gophish/translations" ) // Page contains the fields used for a Page model @@ -22,7 +22,7 @@ type Page struct { } // ErrPageNameNotSpecified is thrown if the name of the landing page is blank. -var ErrPageNameNotSpecified = errors.New(util.T("Page Name not specified")) +var ErrPageNameNotSpecified = errors.New(translations.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 fe6eedb3..6e8e9301 100644 --- a/models/result.go +++ b/models/result.go @@ -8,7 +8,6 @@ import ( "github.com/jinzhu/gorm" "github.com/oschwald/maxminddb-golang" - "github.com/gophish/gophish/util" ) type mmCity struct { diff --git a/models/smtp.go b/models/smtp.go index 71acdeaf..3c6f55ef 100644 --- a/models/smtp.go +++ b/models/smtp.go @@ -6,7 +6,7 @@ import ( "strconv" "strings" "time" - "github.com/gophish/gophish/util" + "github.com/gophish/gophish/translations" ) // SMTP contains the attributes needed to handle the sending of campaign emails @@ -25,14 +25,14 @@ type SMTP struct { // ErrFromAddressNotSpecified is thrown when there is no "From" address // specified in the SMTP configuration -var ErrFromAddressNotSpecified = errors.New(util.T("No From Address specified")) +var ErrFromAddressNotSpecified = errors.New(translations.T("No From Address specified")) // ErrHostNotSpecified is thrown when there is no Host specified // in the SMTP configuration -var ErrHostNotSpecified = errors.New(util.T("No SMTP Host specified")) +var ErrHostNotSpecified = errors.New(translations.T("No SMTP Host specified")) // ErrInvalidHost indicates that the SMTP server string is invalid -var ErrInvalidHost = errors.New(util.T("Invalid SMTP server address")) +var ErrInvalidHost = errors.New(translations.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 0382cc65..266a9c02 100644 --- a/models/template.go +++ b/models/template.go @@ -7,7 +7,7 @@ import ( "time" "github.com/jinzhu/gorm" - "github.com/gophish/gophish/util" + "github.com/gophish/gophish/translations" ) // Template models hold the attributes for an email template to be sent to targets @@ -23,10 +23,10 @@ type Template struct { } // ErrTemplateNameNotSpecified is thrown when a template name is not specified -var ErrTemplateNameNotSpecified = errors.New(util.T("Template name not specified")) +var ErrTemplateNameNotSpecified = errors.New(translations.T("Template name not specified")) // ErrTemplateMissingParameter is thrown when a needed parameter is not provided -var ErrTemplateMissingParameter = errors.New(util.T("Need to specify at least plaintext or HTML content")) +var ErrTemplateMissingParameter = errors.New(translations.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/templates/base.html b/templates/base.html index b48f107c..2a5fb07e 100644 --- a/templates/base.html +++ b/templates/base.html @@ -47,7 +47,7 @@
  • {{T "Campaigns"}}
  • -
  • {{T "Users & Groups"}} +
  • {{T "Users & Groups"}}
  • {{T "Email Templates"}}
  • diff --git a/templates/campaign_results.html b/templates/campaign_results.html index ec98de43..0f1eb74c 100644 --- a/templates/campaign_results.html +++ b/templates/campaign_results.html @@ -7,7 +7,7 @@
  • {{T "Campaigns"}}
  • -
  • {{T "Users & Groups"}} +
  • {{T "Users & Groups"}}
  • {{T "Email Templates"}}
  • diff --git a/templates/campaigns.html b/templates/campaigns.html index 1b6bc559..54c030e6 100644 --- a/templates/campaigns.html +++ b/templates/campaigns.html @@ -7,7 +7,7 @@
  • {{T "Campaigns"}}
  • -
  • {{T "Users & Groups"}} +
  • {{T "Users & Groups"}}
  • {{T "Email Templates"}}
  • diff --git a/templates/dashboard.html b/templates/dashboard.html index b0d449d5..6dabb428 100644 --- a/templates/dashboard.html +++ b/templates/dashboard.html @@ -7,7 +7,7 @@
  • {{T "Campaigns"}}
  • -
  • {{T "Users & Groups"}} +
  • {{T "Users & Groups"}}
  • {{T "Email Templates"}}
  • diff --git a/templates/landing_pages.html b/templates/landing_pages.html index f19a9f6f..481dce6e 100644 --- a/templates/landing_pages.html +++ b/templates/landing_pages.html @@ -7,7 +7,7 @@
  • {{T "Campaigns"}}
  • -
  • {{T "Users & Groups"}} +
  • {{T "Users & Groups"}}
  • {{T "Email Templates"}}
  • diff --git a/templates/sending_profiles.html b/templates/sending_profiles.html index 45120dbc..3d3569a9 100644 --- a/templates/sending_profiles.html +++ b/templates/sending_profiles.html @@ -7,7 +7,7 @@
  • {{T "Campaigns"}}
  • -
  • {{T "Users & Groups"}} +
  • {{T "Users & Groups"}}
  • {{T "Email Templates"}}
  • diff --git a/templates/settings.html b/templates/settings.html index 24118ec9..4f464bd5 100644 --- a/templates/settings.html +++ b/templates/settings.html @@ -7,7 +7,7 @@
  • {{T "Campaigns"}}
  • -
  • {{T "Users & Groups"}} +
  • {{T "Users & Groups"}}
  • {{T "Email Templates"}}
  • diff --git a/templates/templates.html b/templates/templates.html index 7014dea9..c316177a 100644 --- a/templates/templates.html +++ b/templates/templates.html @@ -7,7 +7,7 @@
  • {{T "Campaigns"}}
  • -
  • {{T "Users & Groups"}} +
  • {{T "Users & Groups"}}
  • {{T "Email Templates"}}
  • diff --git a/templates/users.html b/templates/users.html index 0dde962d..a04df085 100644 --- a/templates/users.html +++ b/templates/users.html @@ -7,7 +7,7 @@
  • {{T "Campaigns"}}
  • -
  • {{T "Users & Groups"}} +
  • {{T "Users & Groups"}}
  • {{T "Email Templates"}}
  • @@ -29,7 +29,7 @@

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

    diff --git a/translations/translations.go b/translations/translations.go new file mode 100644 index 00000000..944b12fc --- /dev/null +++ b/translations/translations.go @@ -0,0 +1,24 @@ +package translations + +import ( + "strings" + "github.com/nicksnyder/go-i18n/i18n" +) + +var B i18n.TranslateFunc +var Lang = "en-US" + +func T(text string) string{ + if B == nil { + i18n.MustLoadTranslationFile("translations/" + strings.ToLower(Lang) + ".all.json") + B, _ = i18n.Tfunc(Lang) + } + + return B(text) +} + +func ChangeLang(lang string) { + Lang = lang + B = nil +} + diff --git a/util/util.go b/util/util.go index 66555e1c..269fa747 100644 --- a/util/util.go +++ b/util/util.go @@ -18,31 +18,13 @@ import ( "net/mail" "os" "time" - "strings" "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) -var B i18n.TranslateFunc -var Lang = "en-US" - -func T(text string) string{ - if B == nil { - i18n.MustLoadTranslationFile("translations/" + strings.ToLower(Lang) + ".all.json") - B, _ = i18n.Tfunc(Lang) - } - - return B(text) -} - -func ChangeLang(lang string) { - Lang = lang - B = nil -} // ParseMail takes in an HTTP Request and returns an Email object // TODO: This function will likely be changed to take in a []byte