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