github.com/edermi/gophish_mods@v0.7.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.EmailRequest{
   656  		ErrorChan: make(chan error),
   657  		UserId:    ctx.Get(r, "user_id").(int64),
   658  	}
   659  	if r.Method != "POST" {
   660  		JSONResponse(w, models.Response{Success: false, Message: "Method not allowed"}, http.StatusBadRequest)
   661  		return
   662  	}
   663  	err := json.NewDecoder(r.Body).Decode(s)
   664  	if err != nil {
   665  		JSONResponse(w, models.Response{Success: false, Message: "Error decoding JSON Request"}, http.StatusBadRequest)
   666  		return
   667  	}
   668  
   669  	storeRequest := false
   670  
   671  	// If a Template is not specified use a default
   672  	if s.Template.Name == "" {
   673  		//default message body
   674  		text := "It works!\n\nThis is an email letting you know that your gophish\nconfiguration was successful.\n" +
   675  			"Here are the details:\n\nWho you sent from: {{.From}}\n\nWho you sent to: \n" +
   676  			"{{if .FirstName}} First Name: {{.FirstName}}\n{{end}}" +
   677  			"{{if .LastName}} Last Name: {{.LastName}}\n{{end}}" +
   678  			"{{if .Position}} Position: {{.Position}}\n{{end}}" +
   679  			"\nNow go send some phish!"
   680  		t := models.Template{
   681  			Subject: "Default Email from Gophish",
   682  			Text:    text,
   683  		}
   684  		s.Template = t
   685  	} else {
   686  		// Get the Template requested by name
   687  		s.Template, err = models.GetTemplateByName(s.Template.Name, s.UserId)
   688  		if err == gorm.ErrRecordNotFound {
   689  			log.WithFields(logrus.Fields{
   690  				"template": s.Template.Name,
   691  			}).Error("Template does not exist")
   692  			JSONResponse(w, models.Response{Success: false, Message: models.ErrTemplateNotFound.Error()}, http.StatusBadRequest)
   693  			return
   694  		} else if err != nil {
   695  			log.Error(err)
   696  			JSONResponse(w, models.Response{Success: false, Message: err.Error()}, http.StatusBadRequest)
   697  			return
   698  		}
   699  		s.TemplateId = s.Template.Id
   700  		// We'll only save the test request to the database if there is a
   701  		// user-specified template to use.
   702  		storeRequest = true
   703  	}
   704  
   705  	if s.Page.Name != "" {
   706  		s.Page, err = models.GetPageByName(s.Page.Name, s.UserId)
   707  		if err == gorm.ErrRecordNotFound {
   708  			log.WithFields(logrus.Fields{
   709  				"page": s.Page.Name,
   710  			}).Error("Page does not exist")
   711  			JSONResponse(w, models.Response{Success: false, Message: models.ErrPageNotFound.Error()}, http.StatusBadRequest)
   712  			return
   713  		} else if err != nil {
   714  			log.Error(err)
   715  			JSONResponse(w, models.Response{Success: false, Message: err.Error()}, http.StatusBadRequest)
   716  			return
   717  		}
   718  		s.PageId = s.Page.Id
   719  	}
   720  
   721  	// If a complete sending profile is provided use it
   722  	if err := s.SMTP.Validate(); err != nil {
   723  		// Otherwise get the SMTP requested by name
   724  		smtp, lookupErr := models.GetSMTPByName(s.SMTP.Name, s.UserId)
   725  		// If the Sending Profile doesn't exist, let's err on the side
   726  		// of caution and assume that the validation failure was more important.
   727  		if lookupErr != nil {
   728  			log.Error(err)
   729  			JSONResponse(w, models.Response{Success: false, Message: err.Error()}, http.StatusBadRequest)
   730  			return
   731  		}
   732  		s.SMTP = smtp
   733  	}
   734  	s.FromAddress = s.SMTP.FromAddress
   735  
   736  	// Validate the given request
   737  	if err = s.Validate(); err != nil {
   738  		JSONResponse(w, models.Response{Success: false, Message: err.Error()}, http.StatusBadRequest)
   739  		return
   740  	}
   741  
   742  	// Store the request if this wasn't the default template
   743  	if storeRequest {
   744  		err = models.PostEmailRequest(s)
   745  		if err != nil {
   746  			log.Error(err)
   747  			JSONResponse(w, models.Response{Success: false, Message: err.Error()}, http.StatusInternalServerError)
   748  			return
   749  		}
   750  	}
   751  	// Send the test email
   752  	err = Worker.SendTestEmail(s)
   753  	if err != nil {
   754  		log.Error(err)
   755  		JSONResponse(w, models.Response{Success: false, Message: err.Error()}, http.StatusInternalServerError)
   756  		return
   757  	}
   758  	JSONResponse(w, models.Response{Success: true, Message: "Email Sent"}, http.StatusOK)
   759  	return
   760  }
   761  
   762  // JSONResponse attempts to set the status code, c, and marshal the given interface, d, into a response that
   763  // is written to the given ResponseWriter.
   764  func JSONResponse(w http.ResponseWriter, d interface{}, c int) {
   765  	dj, err := json.MarshalIndent(d, "", "  ")
   766  	if err != nil {
   767  		http.Error(w, "Error creating JSON response", http.StatusInternalServerError)
   768  		log.Error(err)
   769  	}
   770  	w.Header().Set("Content-Type", "application/json")
   771  	w.WriteHeader(c)
   772  	fmt.Fprintf(w, "%s", dj)
   773  }
   774  
   775  type cloneRequest struct {
   776  	URL              string `json:"url"`
   777  	IncludeResources bool   `json:"include_resources"`
   778  }
   779  
   780  func (cr *cloneRequest) validate() error {
   781  	if cr.URL == "" {
   782  		return errors.New("No URL Specified")
   783  	}
   784  	return nil
   785  }
   786  
   787  type cloneResponse struct {
   788  	HTML string `json:"html"`
   789  }
   790  
   791  type emailResponse struct {
   792  	Text    string `json:"text"`
   793  	HTML    string `json:"html"`
   794  	Subject string `json:"subject"`
   795  }