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  }