github.com/bluestoneag/bluephish@v0.1.0/controllers/api/import.go (about) 1 package api 2 3 import ( 4 "bytes" 5 "crypto/tls" 6 "encoding/json" 7 "errors" 8 "fmt" 9 "net/http" 10 "strings" 11 12 "github.com/PuerkitoBio/goquery" 13 "github.com/bluestoneag/bluephish/dialer" 14 log "github.com/bluestoneag/bluephish/logger" 15 "github.com/bluestoneag/bluephish/models" 16 "github.com/bluestoneag/bluephish/util" 17 "github.com/jordan-wright/email" 18 ) 19 20 type cloneRequest struct { 21 URL string `json:"url"` 22 IncludeResources bool `json:"include_resources"` 23 } 24 25 func (cr *cloneRequest) validate() error { 26 if cr.URL == "" { 27 return errors.New("No URL Specified") 28 } 29 return nil 30 } 31 32 type cloneResponse struct { 33 HTML string `json:"html"` 34 } 35 36 type emailResponse struct { 37 Text string `json:"text"` 38 HTML string `json:"html"` 39 Subject string `json:"subject"` 40 } 41 42 // ImportGroup imports a CSV of group members 43 func (as *Server) ImportGroup(w http.ResponseWriter, r *http.Request) { 44 ts, err := util.ParseCSV(r) 45 if err != nil { 46 JSONResponse(w, models.Response{Success: false, Message: "Error parsing CSV"}, http.StatusInternalServerError) 47 return 48 } 49 JSONResponse(w, ts, http.StatusOK) 50 } 51 52 // ImportEmail allows for the importing of email. 53 // Returns a Message object 54 func (as *Server) ImportEmail(w http.ResponseWriter, r *http.Request) { 55 if r.Method != "POST" { 56 JSONResponse(w, models.Response{Success: false, Message: "Method not allowed"}, http.StatusBadRequest) 57 return 58 } 59 ir := struct { 60 Content string `json:"content"` 61 ConvertLinks bool `json:"convert_links"` 62 }{} 63 err := json.NewDecoder(r.Body).Decode(&ir) 64 if err != nil { 65 JSONResponse(w, models.Response{Success: false, Message: "Error decoding JSON Request"}, http.StatusBadRequest) 66 return 67 } 68 e, err := email.NewEmailFromReader(strings.NewReader(ir.Content)) 69 if err != nil { 70 log.Error(err) 71 } 72 // If the user wants to convert links to point to 73 // the landing page, let's make it happen by changing up 74 // e.HTML 75 if ir.ConvertLinks { 76 d, err := goquery.NewDocumentFromReader(bytes.NewReader(e.HTML)) 77 if err != nil { 78 JSONResponse(w, models.Response{Success: false, Message: err.Error()}, http.StatusBadRequest) 79 return 80 } 81 d.Find("a").Each(func(i int, a *goquery.Selection) { 82 a.SetAttr("href", "{{.URL}}") 83 }) 84 h, err := d.Html() 85 if err != nil { 86 JSONResponse(w, models.Response{Success: false, Message: err.Error()}, http.StatusInternalServerError) 87 return 88 } 89 e.HTML = []byte(h) 90 } 91 er := emailResponse{ 92 Subject: e.Subject, 93 Text: string(e.Text), 94 HTML: string(e.HTML), 95 } 96 JSONResponse(w, er, http.StatusOK) 97 } 98 99 // ImportSite allows for the importing of HTML from a website 100 // Without "include_resources" set, it will merely place a "base" tag 101 // so that all resources can be loaded relative to the given URL. 102 func (as *Server) ImportSite(w http.ResponseWriter, r *http.Request) { 103 cr := cloneRequest{} 104 if r.Method != "POST" { 105 JSONResponse(w, models.Response{Success: false, Message: "Method not allowed"}, http.StatusBadRequest) 106 return 107 } 108 err := json.NewDecoder(r.Body).Decode(&cr) 109 if err != nil { 110 JSONResponse(w, models.Response{Success: false, Message: "Error decoding JSON Request"}, http.StatusBadRequest) 111 return 112 } 113 if err = cr.validate(); err != nil { 114 JSONResponse(w, models.Response{Success: false, Message: err.Error()}, http.StatusBadRequest) 115 return 116 } 117 restrictedDialer := dialer.Dialer() 118 tr := &http.Transport{ 119 DialContext: restrictedDialer.DialContext, 120 TLSClientConfig: &tls.Config{ 121 InsecureSkipVerify: true, 122 }, 123 } 124 client := &http.Client{Transport: tr} 125 resp, err := client.Get(cr.URL) 126 if err != nil { 127 JSONResponse(w, models.Response{Success: false, Message: err.Error()}, http.StatusBadRequest) 128 return 129 } 130 // Insert the base href tag to better handle relative resources 131 d, err := goquery.NewDocumentFromResponse(resp) 132 if err != nil { 133 JSONResponse(w, models.Response{Success: false, Message: err.Error()}, http.StatusBadRequest) 134 return 135 } 136 // Assuming we don't want to include resources, we'll need a base href 137 if d.Find("head base").Length() == 0 { 138 d.Find("head").PrependHtml(fmt.Sprintf("<base href=\"%s\">", cr.URL)) 139 } 140 forms := d.Find("form") 141 forms.Each(func(i int, f *goquery.Selection) { 142 // We'll want to store where we got the form from 143 // (the current URL) 144 url := f.AttrOr("action", cr.URL) 145 if !strings.HasPrefix(url, "http") { 146 url = fmt.Sprintf("%s%s", cr.URL, url) 147 } 148 f.PrependHtml(fmt.Sprintf("<input type=\"hidden\" name=\"__original_url\" value=\"%s\"/>", url)) 149 }) 150 h, err := d.Html() 151 if err != nil { 152 JSONResponse(w, models.Response{Success: false, Message: err.Error()}, http.StatusInternalServerError) 153 return 154 } 155 cs := cloneResponse{HTML: h} 156 JSONResponse(w, cs, http.StatusOK) 157 }