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