diff --git a/controllers/api.go b/controllers/api.go index 3d0db708..8e5c8684 100644 --- a/controllers/api.go +++ b/controllers/api.go @@ -13,6 +13,7 @@ import ( "github.com/jinzhu/gorm" "github.com/jordan-wright/gophish/auth" "github.com/jordan-wright/gophish/models" + "github.com/jordan-wright/gophish/util" "github.com/jordan-wright/gophish/worker" ) @@ -245,6 +246,15 @@ func API_Templates_Id(w http.ResponseWriter, r *http.Request) { } } +func API_Import_Group(w http.ResponseWriter, r *http.Request) { + Logger.Println("Parsing CSV....") + ts, err := util.ParseCSV(r) + if checkError(err, w, "Error deleting template", http.StatusInternalServerError) { + return + } + JSONResponse(w, ts, http.StatusOK) +} + // JSONResponse attempts to set the status code, c, and marshal the given interface, d, into a response that // is written to the given ResponseWriter. func JSONResponse(w http.ResponseWriter, d interface{}, c int) { diff --git a/controllers/route.go b/controllers/route.go index 1d671b55..7b8775ad 100644 --- a/controllers/route.go +++ b/controllers/route.go @@ -38,6 +38,7 @@ func CreateRouter() *nosurf.CSRFHandler { api.HandleFunc("/groups/{id:[0-9]+}", Use(API_Groups_Id, mid.RequireAPIKey)) api.HandleFunc("/templates/", Use(API_Templates, mid.RequireAPIKey)) api.HandleFunc("/templates/{id:[0-9]+}", Use(API_Templates_Id, mid.RequireAPIKey)) + api.HandleFunc("/import/group", API_Import_Group) // Setup static file serving router.PathPrefix("/").Handler(http.FileServer(http.Dir("./static/"))) @@ -48,6 +49,7 @@ func CreateRouter() *nosurf.CSRFHandler { csrfHandler.ExemptGlob("/api/campaigns/*") csrfHandler.ExemptGlob("/api/groups/*") csrfHandler.ExemptGlob("/api/templates/*") + csrfHandler.ExemptGlob("/api/import/*") csrfHandler.ExemptGlob("/static/*") return csrfHandler } diff --git a/models/group.go b/models/group.go index c53cf37d..cb74a913 100644 --- a/models/group.go +++ b/models/group.go @@ -21,8 +21,10 @@ type GroupTarget struct { } type Target struct { - Id int64 `json:"-"` - Email string `json:"email"` + Id int64 `json:"-"` + FirstName string `json:"first_name"` + LastName string `json:"last_name"` + Email string `json:"email"` } // GetGroups returns the groups owned by the given user. diff --git a/static/js/angular-file-upload-shim.min.js b/static/js/angular-file-upload-shim.min.js new file mode 100644 index 00000000..d7212cc5 --- /dev/null +++ b/static/js/angular-file-upload-shim.min.js @@ -0,0 +1,2 @@ +/*! 1.4.0 */ +!function(){function a(a){if(!a.__listeners){a.upload||(a.upload={}),a.__listeners=[];var b=a.upload.addEventListener;a.upload.addEventListener=function(c,d){a.__listeners[c]=d,b&&b.apply(this,arguments)}}}var b=function(){try{var a=new ActiveXObject("ShockwaveFlash.ShockwaveFlash");if(a)return!0}catch(b){if(void 0!=navigator.mimeTypes["application/x-shockwave-flash"])return!0}return!1},c=function(a,b){window.XMLHttpRequest.prototype[a]=b(window.XMLHttpRequest.prototype[a])};if(window.XMLHttpRequest&&(window.FormData?c("setRequestHeader",function(a){return function(b,c){if("__setXHR_"===b){var d=c(this);d instanceof Function&&d(this)}else a.apply(this,arguments)}}):(c("open",function(b){return function(c,d,e){a(this),this.__url=d,b.apply(this,[c,d,e])}}),c("getResponseHeader",function(a){return function(b){return this.__fileApiXHR?this.__fileApiXHR.getResponseHeader(b):a.apply(this,[b])}}),c("getAllResponseHeaders",function(a){return function(){return this.__fileApiXHR?this.__fileApiXHR.abort():null==a?null:a.apply(this)}}),c("abort",function(a){return function(){return this.__fileApiXHR?this.__fileApiXHR.abort():null==a?null:a.apply(this)}}),c("setRequestHeader",function(b){return function(c,d){if("__setXHR_"===c){a(this);var e=d(this);e instanceof Function&&e(this)}else this.__requestHeaders=this.__requestHeaders||{},this.__requestHeaders[c]=d,b.apply(this,arguments)}}),c("send",function(a){return function(){var c=this;if(arguments[0]&&arguments[0].__isShim){var d=arguments[0],e={url:c.__url,complete:function(a,b){!a&&c.__listeners.load&&c.__listeners.load({type:"load",loaded:c.__loaded,total:c.__total,target:c,lengthComputable:!0}),!a&&c.__listeners.loadend&&c.__listeners.loadend({type:"loadend",loaded:c.__loaded,total:c.__total,target:c,lengthComputable:!0}),"abort"===a&&c.__listeners.abort&&c.__listeners.abort({type:"abort",loaded:c.__loaded,total:c.__total,target:c,lengthComputable:!0}),void 0!==b.status&&Object.defineProperty(c,"status",{get:function(){return b.status}}),void 0!==b.statusText&&Object.defineProperty(c,"statusText",{get:function(){return b.statusText}}),Object.defineProperty(c,"readyState",{get:function(){return 4}}),void 0!==b.response&&Object.defineProperty(c,"response",{get:function(){return b.response}}),Object.defineProperty(c,"responseText",{get:function(){return b.responseText}}),c.__fileApiXHR=b,c.onreadystatechange()},fileprogress:function(a){a.target=c,c.__listeners.progress&&c.__listeners.progress(a),c.__total=a.total,c.__loaded=a.loaded},headers:c.__requestHeaders};e.data={},e.files={};for(var f=0;f',c=c.firstChild;var d=a.parentNode;d.insertBefore(c,a),d.removeChild(a),c.appendChild(a),a.__isWrapped=!0}},e=function(a){return function(b){var c=FileAPI.getFiles(b);b.target||(b.target={}),b.target.files=c,b.target.files.item=function(a){return b.target.files[a]||null},a(b)}},f=function(a,b){return("change"===b.toLowerCase()||"onchange"===b.toLowerCase())&&"file"==a.getAttribute("type")};HTMLInputElement.prototype.addEventListener&&(HTMLInputElement.prototype.addEventListener=function(a){return function(b,c,g,h){f(this,b)?(d(this),a.apply(this,[b,e(c),g,h])):a.apply(this,[b,c,g,h])}}(HTMLInputElement.prototype.addEventListener)),HTMLInputElement.prototype.attachEvent&&(HTMLInputElement.prototype.attachEvent=function(a){return function(b,c){f(this,b)?(d(this),a.apply(this,[b,e(c)])):a.apply(this,[b,c])}}(HTMLInputElement.prototype.attachEvent)),window.FormData=FormData=function(){return{append:function(a,b,c){this.data.push({key:a,val:b,name:c})},data:[],__isShim:!0}},function(){if(window.FileAPI||(window.FileAPI={}),!FileAPI.upload){var a,c,d,e,f,g=document.createElement("script"),h=document.getElementsByTagName("script");if(window.FileAPI.jsUrl)a=window.FileAPI.jsUrl;else if(window.FileAPI.jsPath)c=window.FileAPI.jsPath;else for(d=0;d-1){c=f.substring(0,e);break}null==FileAPI.staticPath&&(FileAPI.staticPath=c),g.setAttribute("src",a||c+"FileAPI.min.js"),document.getElementsByTagName("head")[0].appendChild(g),FileAPI.hasFlash=b()}}()}window.FileReader||(window.FileReader=function(){function a(a,c){var d={type:a,target:b,loaded:c.loaded,total:c.total,error:c.error};return null!=c.result&&(d.target.result=c.result),d}var b=this,c=!1;this.listeners={},this.addEventListener=function(a,c){b.listeners[a]=b.listeners[a]||[],b.listeners[a].push(c)},this.removeEventListener=function(a,c){b.listeners[a]&&b.listeners[a].splice(b.listeners[a].indexOf(c),1)},this.dispatchEvent=function(a){var c=b.listeners[a.type];if(c)for(var d=0;d0||navigator.msMaxTouchPoints>0)&&d.bind("touchend",function(a){a.preventDefault(),a.target.click()})}}]),a.directive("ngFileDropAvailable",["$parse","$timeout",function(a,b){return function(c,d,e){if("draggable"in document.createElement("span")){var f=a(e.ngFileDropAvailable);b(function(){f(c)})}}}]),a.directive("ngFileDrop",["$parse","$timeout",function(a,b){return function(c,d,e){function f(a,b){if(b.isDirectory){var c=b.createReader();i++,c.readEntries(function(b){for(var c=0;c0&&j[0].webkitGetAsEntry)for(var k=0;k +
diff --git a/templates/base.html b/templates/base.html index 57f5d66f..8453bd38 100644 --- a/templates/base.html +++ b/templates/base.html @@ -91,8 +91,10 @@ + + diff --git a/util/util.go b/util/util.go new file mode 100644 index 00000000..727a9213 --- /dev/null +++ b/util/util.go @@ -0,0 +1,74 @@ +package util + +import ( + "encoding/csv" + "fmt" + "io" + "net/http" + + "github.com/jordan-wright/gophish/models" +) + +func ParseCSV(r *http.Request) ([]models.Target, error) { + mr, err := r.MultipartReader() + ts := []models.Target{} + if err != nil { + return ts, err + } + for { + part, err := mr.NextPart() + if err == io.EOF { + break + } + // Skip the "submit" part + if part.FileName() == "" { + continue + } + defer part.Close() + reader := csv.NewReader(part) + reader.TrimLeadingSpace = true + record, err := reader.Read() + if err == io.EOF { + break + } + fi := -1 + li := -1 + ei := -1 + fn := "" + ln := "" + ea := "" + for i, v := range record { + fmt.Println(v) + switch { + case v == "First Name": + fi = i + case v == "Last Name": + li = i + case v == "Email": + ei = i + } + } + for { + record, err := reader.Read() + if err == io.EOF { + break + } + if fi != -1 { + fn = record[fi] + } + if li != -1 { + ln = record[li] + } + if ei != -1 { + ea = record[ei] + } + t := models.Target{ + FirstName: fn, + LastName: ln, + Email: ea, + } + ts = append(ts, t) + } + } + return ts, nil +}