github.com/jonathaningram/gophish@v0.3.1-0.20170829042651-ac3fe6aeae6c/controllers/api.go (about) 1 package controllers 2 3 import ( 4 "bytes" 5 "crypto/tls" 6 "encoding/json" 7 "errors" 8 "fmt" 9 "net/http" 10 "strconv" 11 "strings" 12 "text/template" 13 "time" 14 15 "github.com/PuerkitoBio/goquery" 16 "github.com/gophish/gophish/auth" 17 ctx "github.com/gophish/gophish/context" 18 "github.com/gophish/gophish/models" 19 "github.com/gophish/gophish/util" 20 "github.com/gophish/gophish/worker" 21 "github.com/gorilla/mux" 22 "github.com/jinzhu/gorm" 23 "github.com/jordan-wright/email" 24 ) 25 26 // Worker is the worker that processes phishing events and updates campaigns. 27 var Worker *worker.Worker 28 29 func init() { 30 Worker = worker.New() 31 go Worker.Start() 32 } 33 34 // API (/api) provides access to api documentation 35 func API(w http.ResponseWriter, r *http.Request) { 36 switch { 37 case r.Method == "GET": 38 templates := template.New("template") 39 _, err := templates.ParseFiles("templates/docs.html") 40 if err != nil { 41 Logger.Println(err) 42 } 43 template.Must(templates, err).ExecuteTemplate(w, "base", nil) 44 } 45 } 46 47 // API (/api/reset) resets a user's API key 48 func API_Reset(w http.ResponseWriter, r *http.Request) { 49 switch { 50 case r.Method == "POST": 51 u := ctx.Get(r, "user").(models.User) 52 u.ApiKey = auth.GenerateSecureKey() 53 err := models.PutUser(&u) 54 if err != nil { 55 http.Error(w, "Error setting API Key", http.StatusInternalServerError) 56 } else { 57 JSONResponse(w, models.Response{Success: true, Message: "API Key successfully reset!", Data: u.ApiKey}, http.StatusOK) 58 } 59 } 60 } 61 62 // API_Campaigns returns a list of campaigns if requested via GET. 63 // If requested via POST, API_Campaigns creates a new campaign and returns a reference to it. 64 func API_Campaigns(w http.ResponseWriter, r *http.Request) { 65 switch { 66 case r.Method == "GET": 67 cs, err := models.GetCampaigns(ctx.Get(r, "user_id").(int64)) 68 if err != nil { 69 Logger.Println(err) 70 } 71 JSONResponse(w, cs, http.StatusOK) 72 //POST: Create a new campaign and return it as JSON 73 case r.Method == "POST": 74 c := models.Campaign{} 75 // Put the request into a campaign 76 err := json.NewDecoder(r.Body).Decode(&c) 77 if err != nil { 78 JSONResponse(w, models.Response{Success: false, Message: "Invalid JSON structure"}, http.StatusBadRequest) 79 return 80 } 81 err = models.PostCampaign(&c, ctx.Get(r, "user_id").(int64)) 82 if err != nil { 83 JSONResponse(w, models.Response{Success: false, Message: err.Error()}, http.StatusBadRequest) 84 return 85 } 86 JSONResponse(w, c, http.StatusCreated) 87 } 88 } 89 90 // API_Campaigns_Summary returns the summary for the current user's campaigns 91 func API_Campaigns_Summary(w http.ResponseWriter, r *http.Request) { 92 switch { 93 case r.Method == "GET": 94 cs, err := models.GetCampaignSummaries(ctx.Get(r, "user_id").(int64)) 95 if err != nil { 96 Logger.Println(err) 97 JSONResponse(w, models.Response{Success: false, Message: err.Error()}, http.StatusInternalServerError) 98 return 99 } 100 JSONResponse(w, cs, http.StatusOK) 101 } 102 } 103 104 // API_Campaigns_Id returns details about the requested campaign. If the campaign is not 105 // valid, API_Campaigns_Id returns null. 106 func API_Campaigns_Id(w http.ResponseWriter, r *http.Request) { 107 vars := mux.Vars(r) 108 id, _ := strconv.ParseInt(vars["id"], 0, 64) 109 c, err := models.GetCampaign(id, ctx.Get(r, "user_id").(int64)) 110 if err != nil { 111 Logger.Println(err) 112 JSONResponse(w, models.Response{Success: false, Message: "Campaign not found"}, http.StatusNotFound) 113 return 114 } 115 switch { 116 case r.Method == "GET": 117 JSONResponse(w, c, http.StatusOK) 118 case r.Method == "DELETE": 119 err = models.DeleteCampaign(id) 120 if err != nil { 121 JSONResponse(w, models.Response{Success: false, Message: "Error deleting campaign"}, http.StatusInternalServerError) 122 return 123 } 124 JSONResponse(w, models.Response{Success: true, Message: "Campaign deleted successfully!"}, http.StatusOK) 125 } 126 } 127 128 // API_Campaigns_Id_Results returns just the results for a given campaign to 129 // significantly reduce the information returned. 130 func API_Campaigns_Id_Results(w http.ResponseWriter, r *http.Request) { 131 vars := mux.Vars(r) 132 id, _ := strconv.ParseInt(vars["id"], 0, 64) 133 cr, err := models.GetCampaignResults(id, ctx.Get(r, "user_id").(int64)) 134 if err != nil { 135 Logger.Println(err) 136 JSONResponse(w, models.Response{Success: false, Message: "Campaign not found"}, http.StatusNotFound) 137 return 138 } 139 if r.Method == "GET" { 140 JSONResponse(w, cr, http.StatusOK) 141 return 142 } 143 } 144 145 // API_Campaigns_Id_Summary returns just the summary for a given campaign. 146 func API_Campaign_Id_Summary(w http.ResponseWriter, r *http.Request) { 147 vars := mux.Vars(r) 148 id, _ := strconv.ParseInt(vars["id"], 0, 64) 149 switch { 150 case r.Method == "GET": 151 cs, err := models.GetCampaignSummary(id, ctx.Get(r, "user_id").(int64)) 152 if err != nil { 153 if err == gorm.ErrRecordNotFound { 154 JSONResponse(w, models.Response{Success: false, Message: "Campaign not found"}, http.StatusNotFound) 155 } else { 156 JSONResponse(w, models.Response{Success: false, Message: err.Error()}, http.StatusInternalServerError) 157 } 158 Logger.Println(err) 159 return 160 } 161 JSONResponse(w, cs, http.StatusOK) 162 } 163 } 164 165 // API_Campaigns_Id_Complete effectively "ends" a campaign. 166 // Future phishing emails clicked will return a simple "404" page. 167 func API_Campaigns_Id_Complete(w http.ResponseWriter, r *http.Request) { 168 vars := mux.Vars(r) 169 id, _ := strconv.ParseInt(vars["id"], 0, 64) 170 switch { 171 case r.Method == "GET": 172 err := models.CompleteCampaign(id, ctx.Get(r, "user_id").(int64)) 173 if err != nil { 174 JSONResponse(w, models.Response{Success: false, Message: "Error completing campaign"}, http.StatusInternalServerError) 175 return 176 } 177 JSONResponse(w, models.Response{Success: true, Message: "Campaign completed successfully!"}, http.StatusOK) 178 } 179 } 180 181 // API_Groups returns a list of groups if requested via GET. 182 // If requested via POST, API_Groups creates a new group and returns a reference to it. 183 func API_Groups(w http.ResponseWriter, r *http.Request) { 184 switch { 185 case r.Method == "GET": 186 gs, err := models.GetGroups(ctx.Get(r, "user_id").(int64)) 187 if err != nil { 188 JSONResponse(w, models.Response{Success: false, Message: "No groups found"}, http.StatusNotFound) 189 return 190 } 191 JSONResponse(w, gs, http.StatusOK) 192 //POST: Create a new group and return it as JSON 193 case r.Method == "POST": 194 g := models.Group{} 195 // Put the request into a group 196 err := json.NewDecoder(r.Body).Decode(&g) 197 if err != nil { 198 JSONResponse(w, models.Response{Success: false, Message: "Invalid JSON structure"}, http.StatusBadRequest) 199 return 200 } 201 _, err = models.GetGroupByName(g.Name, ctx.Get(r, "user_id").(int64)) 202 if err != gorm.ErrRecordNotFound { 203 JSONResponse(w, models.Response{Success: false, Message: "Group name already in use"}, http.StatusConflict) 204 return 205 } 206 g.ModifiedDate = time.Now().UTC() 207 g.UserId = ctx.Get(r, "user_id").(int64) 208 err = models.PostGroup(&g) 209 if err != nil { 210 JSONResponse(w, models.Response{Success: false, Message: err.Error()}, http.StatusBadRequest) 211 return 212 } 213 JSONResponse(w, g, http.StatusCreated) 214 } 215 } 216 217 // API_Groups_Summary returns a summary of the groups owned by the current user. 218 func API_Groups_Summary(w http.ResponseWriter, r *http.Request) { 219 switch { 220 case r.Method == "GET": 221 gs, err := models.GetGroupSummaries(ctx.Get(r, "user_id").(int64)) 222 if err != nil { 223 Logger.Println(err) 224 JSONResponse(w, models.Response{Success: false, Message: err.Error()}, http.StatusInternalServerError) 225 return 226 } 227 JSONResponse(w, gs, http.StatusOK) 228 } 229 } 230 231 // API_Groups_Id returns details about the requested group. 232 // If the group is not valid, API_Groups_Id returns null. 233 func API_Groups_Id(w http.ResponseWriter, r *http.Request) { 234 vars := mux.Vars(r) 235 id, _ := strconv.ParseInt(vars["id"], 0, 64) 236 g, err := models.GetGroup(id, ctx.Get(r, "user_id").(int64)) 237 if err != nil { 238 JSONResponse(w, models.Response{Success: false, Message: "Group not found"}, http.StatusNotFound) 239 return 240 } 241 switch { 242 case r.Method == "GET": 243 JSONResponse(w, g, http.StatusOK) 244 case r.Method == "DELETE": 245 err = models.DeleteGroup(&g) 246 if err != nil { 247 JSONResponse(w, models.Response{Success: false, Message: "Error deleting group"}, http.StatusInternalServerError) 248 return 249 } 250 JSONResponse(w, models.Response{Success: true, Message: "Group deleted successfully!"}, http.StatusOK) 251 case r.Method == "PUT": 252 // Change this to get from URL and uid (don't bother with id in r.Body) 253 g = models.Group{} 254 err = json.NewDecoder(r.Body).Decode(&g) 255 if g.Id != id { 256 JSONResponse(w, models.Response{Success: false, Message: "Error: /:id and group_id mismatch"}, http.StatusInternalServerError) 257 return 258 } 259 g.ModifiedDate = time.Now().UTC() 260 g.UserId = ctx.Get(r, "user_id").(int64) 261 err = models.PutGroup(&g) 262 if err != nil { 263 JSONResponse(w, models.Response{Success: false, Message: err.Error()}, http.StatusBadRequest) 264 return 265 } 266 JSONResponse(w, g, http.StatusOK) 267 } 268 } 269 270 // API_Groups_Id_Summary returns a summary of the groups owned by the current user. 271 func API_Groups_Id_Summary(w http.ResponseWriter, r *http.Request) { 272 switch { 273 case r.Method == "GET": 274 vars := mux.Vars(r) 275 id, _ := strconv.ParseInt(vars["id"], 0, 64) 276 g, err := models.GetGroupSummary(id, ctx.Get(r, "user_id").(int64)) 277 if err != nil { 278 JSONResponse(w, models.Response{Success: false, Message: "Group not found"}, http.StatusNotFound) 279 return 280 } 281 JSONResponse(w, g, http.StatusOK) 282 } 283 } 284 285 // API_Templates handles the functionality for the /api/templates endpoint 286 func API_Templates(w http.ResponseWriter, r *http.Request) { 287 switch { 288 case r.Method == "GET": 289 ts, err := models.GetTemplates(ctx.Get(r, "user_id").(int64)) 290 if err != nil { 291 Logger.Println(err) 292 } 293 JSONResponse(w, ts, http.StatusOK) 294 //POST: Create a new template and return it as JSON 295 case r.Method == "POST": 296 t := models.Template{} 297 // Put the request into a template 298 err := json.NewDecoder(r.Body).Decode(&t) 299 if err != nil { 300 JSONResponse(w, models.Response{Success: false, Message: "Invalid JSON structure"}, http.StatusBadRequest) 301 return 302 } 303 _, err = models.GetTemplateByName(t.Name, ctx.Get(r, "user_id").(int64)) 304 if err != gorm.ErrRecordNotFound { 305 JSONResponse(w, models.Response{Success: false, Message: "Template name already in use"}, http.StatusConflict) 306 return 307 } 308 t.ModifiedDate = time.Now().UTC() 309 t.UserId = ctx.Get(r, "user_id").(int64) 310 err = models.PostTemplate(&t) 311 if err == models.ErrTemplateNameNotSpecified { 312 JSONResponse(w, models.Response{Success: false, Message: err.Error()}, http.StatusBadRequest) 313 return 314 } 315 if err == models.ErrTemplateMissingParameter { 316 JSONResponse(w, models.Response{Success: false, Message: err.Error()}, http.StatusBadRequest) 317 return 318 } 319 if err != nil { 320 JSONResponse(w, models.Response{Success: false, Message: "Error inserting template into database"}, http.StatusInternalServerError) 321 Logger.Println(err) 322 return 323 } 324 JSONResponse(w, t, http.StatusCreated) 325 } 326 } 327 328 // API_Templates_Id handles the functions for the /api/templates/:id endpoint 329 func API_Templates_Id(w http.ResponseWriter, r *http.Request) { 330 vars := mux.Vars(r) 331 id, _ := strconv.ParseInt(vars["id"], 0, 64) 332 t, err := models.GetTemplate(id, ctx.Get(r, "user_id").(int64)) 333 if err != nil { 334 JSONResponse(w, models.Response{Success: false, Message: "Template not found"}, http.StatusNotFound) 335 return 336 } 337 switch { 338 case r.Method == "GET": 339 JSONResponse(w, t, http.StatusOK) 340 case r.Method == "DELETE": 341 err = models.DeleteTemplate(id, ctx.Get(r, "user_id").(int64)) 342 if err != nil { 343 JSONResponse(w, models.Response{Success: false, Message: "Error deleting template"}, http.StatusInternalServerError) 344 return 345 } 346 JSONResponse(w, models.Response{Success: true, Message: "Template deleted successfully!"}, http.StatusOK) 347 case r.Method == "PUT": 348 t = models.Template{} 349 err = json.NewDecoder(r.Body).Decode(&t) 350 if err != nil { 351 Logger.Println(err) 352 } 353 if t.Id != id { 354 JSONResponse(w, models.Response{Success: false, Message: "Error: /:id and template_id mismatch"}, http.StatusBadRequest) 355 return 356 } 357 t.ModifiedDate = time.Now().UTC() 358 t.UserId = ctx.Get(r, "user_id").(int64) 359 err = models.PutTemplate(&t) 360 if err != nil { 361 JSONResponse(w, models.Response{Success: false, Message: err.Error()}, http.StatusBadRequest) 362 return 363 } 364 JSONResponse(w, t, http.StatusOK) 365 } 366 } 367 368 // API_Pages handles requests for the /api/pages/ endpoint 369 func API_Pages(w http.ResponseWriter, r *http.Request) { 370 switch { 371 case r.Method == "GET": 372 ps, err := models.GetPages(ctx.Get(r, "user_id").(int64)) 373 if err != nil { 374 Logger.Println(err) 375 } 376 JSONResponse(w, ps, http.StatusOK) 377 //POST: Create a new page and return it as JSON 378 case r.Method == "POST": 379 p := models.Page{} 380 // Put the request into a page 381 err := json.NewDecoder(r.Body).Decode(&p) 382 if err != nil { 383 JSONResponse(w, models.Response{Success: false, Message: "Invalid request"}, http.StatusBadRequest) 384 return 385 } 386 // Check to make sure the name is unique 387 _, err = models.GetPageByName(p.Name, ctx.Get(r, "user_id").(int64)) 388 if err != gorm.ErrRecordNotFound { 389 JSONResponse(w, models.Response{Success: false, Message: "Page name already in use"}, http.StatusConflict) 390 Logger.Println(err) 391 return 392 } 393 p.ModifiedDate = time.Now().UTC() 394 p.UserId = ctx.Get(r, "user_id").(int64) 395 err = models.PostPage(&p) 396 if err != nil { 397 JSONResponse(w, models.Response{Success: false, Message: err.Error()}, http.StatusInternalServerError) 398 return 399 } 400 JSONResponse(w, p, http.StatusCreated) 401 } 402 } 403 404 // API_Pages_Id contains functions to handle the GET'ing, DELETE'ing, and PUT'ing 405 // of a Page object 406 func API_Pages_Id(w http.ResponseWriter, r *http.Request) { 407 vars := mux.Vars(r) 408 id, _ := strconv.ParseInt(vars["id"], 0, 64) 409 p, err := models.GetPage(id, ctx.Get(r, "user_id").(int64)) 410 if err != nil { 411 JSONResponse(w, models.Response{Success: false, Message: "Page not found"}, http.StatusNotFound) 412 return 413 } 414 switch { 415 case r.Method == "GET": 416 JSONResponse(w, p, http.StatusOK) 417 case r.Method == "DELETE": 418 err = models.DeletePage(id, ctx.Get(r, "user_id").(int64)) 419 if err != nil { 420 JSONResponse(w, models.Response{Success: false, Message: "Error deleting page"}, http.StatusInternalServerError) 421 return 422 } 423 JSONResponse(w, models.Response{Success: true, Message: "Page Deleted Successfully"}, http.StatusOK) 424 case r.Method == "PUT": 425 p = models.Page{} 426 err = json.NewDecoder(r.Body).Decode(&p) 427 if err != nil { 428 Logger.Println(err) 429 } 430 if p.Id != id { 431 JSONResponse(w, models.Response{Success: false, Message: "/:id and /:page_id mismatch"}, http.StatusBadRequest) 432 return 433 } 434 p.ModifiedDate = time.Now().UTC() 435 p.UserId = ctx.Get(r, "user_id").(int64) 436 err = models.PutPage(&p) 437 if err != nil { 438 JSONResponse(w, models.Response{Success: false, Message: "Error updating page: " + err.Error()}, http.StatusInternalServerError) 439 return 440 } 441 JSONResponse(w, p, http.StatusOK) 442 } 443 } 444 445 // API_SMTP handles requests for the /api/smtp/ endpoint 446 func API_SMTP(w http.ResponseWriter, r *http.Request) { 447 switch { 448 case r.Method == "GET": 449 ss, err := models.GetSMTPs(ctx.Get(r, "user_id").(int64)) 450 if err != nil { 451 Logger.Println(err) 452 } 453 JSONResponse(w, ss, http.StatusOK) 454 //POST: Create a new SMTP and return it as JSON 455 case r.Method == "POST": 456 s := models.SMTP{} 457 // Put the request into a page 458 err := json.NewDecoder(r.Body).Decode(&s) 459 if err != nil { 460 JSONResponse(w, models.Response{Success: false, Message: "Invalid request"}, http.StatusBadRequest) 461 return 462 } 463 // Check to make sure the name is unique 464 _, err = models.GetSMTPByName(s.Name, ctx.Get(r, "user_id").(int64)) 465 if err != gorm.ErrRecordNotFound { 466 JSONResponse(w, models.Response{Success: false, Message: "SMTP name already in use"}, http.StatusConflict) 467 Logger.Println(err) 468 return 469 } 470 s.ModifiedDate = time.Now().UTC() 471 s.UserId = ctx.Get(r, "user_id").(int64) 472 err = models.PostSMTP(&s) 473 if err != nil { 474 JSONResponse(w, models.Response{Success: false, Message: err.Error()}, http.StatusInternalServerError) 475 return 476 } 477 JSONResponse(w, s, http.StatusCreated) 478 } 479 } 480 481 // API_SMTP_Id contains functions to handle the GET'ing, DELETE'ing, and PUT'ing 482 // of a SMTP object 483 func API_SMTP_Id(w http.ResponseWriter, r *http.Request) { 484 vars := mux.Vars(r) 485 id, _ := strconv.ParseInt(vars["id"], 0, 64) 486 s, err := models.GetSMTP(id, ctx.Get(r, "user_id").(int64)) 487 if err != nil { 488 JSONResponse(w, models.Response{Success: false, Message: "SMTP not found"}, http.StatusNotFound) 489 return 490 } 491 switch { 492 case r.Method == "GET": 493 JSONResponse(w, s, http.StatusOK) 494 case r.Method == "DELETE": 495 err = models.DeleteSMTP(id, ctx.Get(r, "user_id").(int64)) 496 if err != nil { 497 JSONResponse(w, models.Response{Success: false, Message: "Error deleting SMTP"}, http.StatusInternalServerError) 498 return 499 } 500 JSONResponse(w, models.Response{Success: true, Message: "SMTP Deleted Successfully"}, http.StatusOK) 501 case r.Method == "PUT": 502 s = models.SMTP{} 503 err = json.NewDecoder(r.Body).Decode(&s) 504 if err != nil { 505 Logger.Println(err) 506 } 507 if s.Id != id { 508 JSONResponse(w, models.Response{Success: false, Message: "/:id and /:smtp_id mismatch"}, http.StatusBadRequest) 509 return 510 } 511 err = s.Validate() 512 if err != nil { 513 JSONResponse(w, models.Response{Success: false, Message: err.Error()}, http.StatusBadRequest) 514 return 515 } 516 s.ModifiedDate = time.Now().UTC() 517 s.UserId = ctx.Get(r, "user_id").(int64) 518 err = models.PutSMTP(&s) 519 if err != nil { 520 JSONResponse(w, models.Response{Success: false, Message: "Error updating page"}, http.StatusInternalServerError) 521 return 522 } 523 JSONResponse(w, s, http.StatusOK) 524 } 525 } 526 527 // API_Import_Group imports a CSV of group members 528 func API_Import_Group(w http.ResponseWriter, r *http.Request) { 529 ts, err := util.ParseCSV(r) 530 if err != nil { 531 JSONResponse(w, models.Response{Success: false, Message: "Error parsing CSV"}, http.StatusInternalServerError) 532 return 533 } 534 JSONResponse(w, ts, http.StatusOK) 535 return 536 } 537 538 // API_Import_Email allows for the importing of email. 539 // Returns a Message object 540 func API_Import_Email(w http.ResponseWriter, r *http.Request) { 541 if r.Method != "POST" { 542 JSONResponse(w, models.Response{Success: false, Message: "Method not allowed"}, http.StatusBadRequest) 543 return 544 } 545 ir := struct { 546 Content string `json:"content"` 547 ConvertLinks bool `json:"convert_links"` 548 }{} 549 err := json.NewDecoder(r.Body).Decode(&ir) 550 if err != nil { 551 JSONResponse(w, models.Response{Success: false, Message: "Error decoding JSON Request"}, http.StatusBadRequest) 552 return 553 } 554 e, err := email.NewEmailFromReader(strings.NewReader(ir.Content)) 555 if err != nil { 556 Logger.Println(err) 557 } 558 // If the user wants to convert links to point to 559 // the landing page, let's make it happen by changing up 560 // e.HTML 561 if ir.ConvertLinks { 562 d, err := goquery.NewDocumentFromReader(bytes.NewReader(e.HTML)) 563 if err != nil { 564 JSONResponse(w, models.Response{Success: false, Message: err.Error()}, http.StatusBadRequest) 565 return 566 } 567 d.Find("a").Each(func(i int, a *goquery.Selection) { 568 a.SetAttr("href", "{{.URL}}") 569 }) 570 h, err := d.Html() 571 if err != nil { 572 JSONResponse(w, models.Response{Success: false, Message: err.Error()}, http.StatusInternalServerError) 573 return 574 } 575 e.HTML = []byte(h) 576 } 577 er := emailResponse{ 578 Subject: e.Subject, 579 Text: string(e.Text), 580 HTML: string(e.HTML), 581 } 582 JSONResponse(w, er, http.StatusOK) 583 return 584 } 585 586 // API_Import_Site allows for the importing of HTML from a website 587 // Without "include_resources" set, it will merely place a "base" tag 588 // so that all resources can be loaded relative to the given URL. 589 func API_Import_Site(w http.ResponseWriter, r *http.Request) { 590 cr := cloneRequest{} 591 if r.Method != "POST" { 592 JSONResponse(w, models.Response{Success: false, Message: "Method not allowed"}, http.StatusBadRequest) 593 return 594 } 595 err := json.NewDecoder(r.Body).Decode(&cr) 596 if err != nil { 597 JSONResponse(w, models.Response{Success: false, Message: "Error decoding JSON Request"}, http.StatusBadRequest) 598 return 599 } 600 if err = cr.validate(); err != nil { 601 JSONResponse(w, models.Response{Success: false, Message: err.Error()}, http.StatusBadRequest) 602 return 603 } 604 tr := &http.Transport{ 605 TLSClientConfig: &tls.Config{ 606 InsecureSkipVerify: true, 607 }, 608 } 609 client := &http.Client{Transport: tr} 610 resp, err := client.Get(cr.URL) 611 if err != nil { 612 JSONResponse(w, models.Response{Success: false, Message: err.Error()}, http.StatusBadRequest) 613 return 614 } 615 // Insert the base href tag to better handle relative resources 616 d, err := goquery.NewDocumentFromResponse(resp) 617 if err != nil { 618 JSONResponse(w, models.Response{Success: false, Message: err.Error()}, http.StatusBadRequest) 619 return 620 } 621 // Assuming we don't want to include resources, we'll need a base href 622 if d.Find("head base").Length() == 0 { 623 d.Find("head").PrependHtml(fmt.Sprintf("<base href=\"%s\">", cr.URL)) 624 } 625 forms := d.Find("form") 626 forms.Each(func(i int, f *goquery.Selection) { 627 // We'll want to store where we got the form from 628 // (the current URL) 629 url := f.AttrOr("action", cr.URL) 630 if !strings.HasPrefix(url, "http") { 631 url = fmt.Sprintf("%s%s", cr.URL, url) 632 } 633 f.PrependHtml(fmt.Sprintf("<input type=\"hidden\" name=\"__original_url\" value=\"%s\"/>", url)) 634 }) 635 h, err := d.Html() 636 if err != nil { 637 JSONResponse(w, models.Response{Success: false, Message: err.Error()}, http.StatusInternalServerError) 638 return 639 } 640 cs := cloneResponse{HTML: h} 641 JSONResponse(w, cs, http.StatusOK) 642 return 643 } 644 645 // API_Send_Test_Email sends a test email using the template name 646 // and Target given. 647 func API_Send_Test_Email(w http.ResponseWriter, r *http.Request) { 648 s := &models.SendTestEmailRequest{} 649 if r.Method != "POST" { 650 JSONResponse(w, models.Response{Success: false, Message: "Method not allowed"}, http.StatusBadRequest) 651 return 652 } 653 err := json.NewDecoder(r.Body).Decode(s) 654 if err != nil { 655 JSONResponse(w, models.Response{Success: false, Message: "Error decoding JSON Request"}, http.StatusBadRequest) 656 return 657 } 658 // Validate the given request 659 if err = s.Validate(); err != nil { 660 JSONResponse(w, models.Response{Success: false, Message: err.Error()}, http.StatusBadRequest) 661 return 662 } 663 664 // If a Template is not specified use a default 665 if s.Template.Name == "" { 666 //default message body 667 text := "It works!\n\nThis is an email letting you know that your gophish\nconfiguration was successful.\n" + 668 "Here are the details:\n\nWho you sent from: {{.From}}\n\nWho you sent to: \n" + 669 "{{if .FirstName}} First Name: {{.FirstName}}\n{{end}}" + 670 "{{if .LastName}} Last Name: {{.LastName}}\n{{end}}" + 671 "{{if .Position}} Position: {{.Position}}\n{{end}}" + 672 "{{if .TrackingURL}} Tracking URL: {{.TrackingURL}}\n{{end}}" + 673 "\nNow go send some phish!" 674 t := models.Template{ 675 Subject: "Default Email from Gophish", 676 Text: text, 677 } 678 s.Template = t 679 // Try to lookup the Template by name 680 } else { 681 // Get the Template requested by name 682 s.Template, err = models.GetTemplateByName(s.Template.Name, ctx.Get(r, "user_id").(int64)) 683 if err == gorm.ErrRecordNotFound { 684 Logger.Printf("Error - Template %s does not exist", s.Template.Name) 685 JSONResponse(w, models.Response{Success: false, Message: models.ErrTemplateNotFound.Error()}, http.StatusBadRequest) 686 } else if err != nil { 687 Logger.Println(err) 688 JSONResponse(w, models.Response{Success: false, Message: err.Error()}, http.StatusBadRequest) 689 return 690 } 691 } 692 693 // If a complete sending profile is provided use it 694 if err := s.SMTP.Validate(); err != nil { 695 // Otherwise get the SMTP requested by name 696 s.SMTP, err = models.GetSMTPByName(s.SMTP.Name, ctx.Get(r, "user_id").(int64)) 697 if err == gorm.ErrRecordNotFound { 698 Logger.Printf("Error - Sending profile %s does not exist", s.SMTP.Name) 699 JSONResponse(w, models.Response{Success: false, Message: models.ErrSMTPNotFound.Error()}, http.StatusBadRequest) 700 } else if err != nil { 701 Logger.Println(err) 702 JSONResponse(w, models.Response{Success: false, Message: err.Error()}, http.StatusBadRequest) 703 return 704 } 705 } 706 707 // Send the test email 708 err = worker.SendTestEmail(s) 709 if err != nil { 710 JSONResponse(w, models.Response{Success: false, Message: err.Error()}, http.StatusInternalServerError) 711 return 712 } 713 JSONResponse(w, models.Response{Success: true, Message: "Email Sent"}, http.StatusOK) 714 return 715 } 716 717 // JSONResponse attempts to set the status code, c, and marshal the given interface, d, into a response that 718 // is written to the given ResponseWriter. 719 func JSONResponse(w http.ResponseWriter, d interface{}, c int) { 720 dj, err := json.MarshalIndent(d, "", " ") 721 if err != nil { 722 http.Error(w, "Error creating JSON response", http.StatusInternalServerError) 723 Logger.Println(err) 724 } 725 w.Header().Set("Content-Type", "application/json") 726 w.WriteHeader(c) 727 fmt.Fprintf(w, "%s", dj) 728 } 729 730 type cloneRequest struct { 731 URL string `json:"url"` 732 IncludeResources bool `json:"include_resources"` 733 } 734 735 func (cr *cloneRequest) validate() error { 736 if cr.URL == "" { 737 return errors.New("No URL Specified") 738 } 739 return nil 740 } 741 742 type cloneResponse struct { 743 HTML string `json:"html"` 744 } 745 746 type emailResponse struct { 747 Text string `json:"text"` 748 HTML string `json:"html"` 749 Subject string `json:"subject"` 750 }