2017-04-06 03:20:00 +00:00
|
|
|
var map = null
|
|
|
|
var doPoll = true;
|
|
|
|
|
|
|
|
// statuses is a helper map to point result statuses to ui classes
|
|
|
|
var statuses = {
|
|
|
|
"Email Sent": {
|
2017-08-06 02:11:50 +00:00
|
|
|
color: "#1abc9c",
|
2017-04-06 03:20:00 +00:00
|
|
|
label: "label-success",
|
|
|
|
icon: "fa-envelope",
|
|
|
|
point: "ct-point-sent"
|
|
|
|
},
|
2017-08-06 02:11:50 +00:00
|
|
|
"Emails Sent": {
|
|
|
|
color: "#1abc9c",
|
|
|
|
label: "label-success",
|
|
|
|
icon: "fa-envelope",
|
|
|
|
point: "ct-point-sent"
|
|
|
|
},
|
|
|
|
"In progress": {
|
|
|
|
label: "label-primary"
|
|
|
|
},
|
|
|
|
"Queued": {
|
|
|
|
label: "label-info"
|
|
|
|
},
|
|
|
|
"Completed": {
|
|
|
|
label: "label-success"
|
|
|
|
},
|
2017-04-06 03:20:00 +00:00
|
|
|
"Email Opened": {
|
2017-08-06 02:11:50 +00:00
|
|
|
color: "#f9bf3b",
|
2017-04-06 03:20:00 +00:00
|
|
|
label: "label-warning",
|
|
|
|
icon: "fa-envelope",
|
|
|
|
point: "ct-point-opened"
|
|
|
|
},
|
|
|
|
"Clicked Link": {
|
2017-08-06 02:11:50 +00:00
|
|
|
color: "#F39C12",
|
2017-04-06 03:20:00 +00:00
|
|
|
label: "label-clicked",
|
|
|
|
icon: "fa-mouse-pointer",
|
|
|
|
point: "ct-point-clicked"
|
|
|
|
},
|
|
|
|
"Success": {
|
2017-08-06 02:11:50 +00:00
|
|
|
color: "#f05b4f",
|
2017-04-06 03:20:00 +00:00
|
|
|
label: "label-danger",
|
|
|
|
icon: "fa-exclamation",
|
|
|
|
point: "ct-point-clicked"
|
|
|
|
},
|
|
|
|
"Error": {
|
2017-08-06 02:11:50 +00:00
|
|
|
color: "#6c7a89",
|
2017-04-06 03:20:00 +00:00
|
|
|
label: "label-default",
|
|
|
|
icon: "fa-times",
|
|
|
|
point: "ct-point-error"
|
|
|
|
},
|
|
|
|
"Error Sending Email": {
|
2017-08-06 02:11:50 +00:00
|
|
|
color: "#6c7a89",
|
2017-04-06 03:20:00 +00:00
|
|
|
label: "label-default",
|
|
|
|
icon: "fa-times",
|
|
|
|
point: "ct-point-error"
|
|
|
|
},
|
|
|
|
"Submitted Data": {
|
2017-08-06 02:11:50 +00:00
|
|
|
color: "#f05b4f",
|
2017-04-06 03:20:00 +00:00
|
|
|
label: "label-danger",
|
|
|
|
icon: "fa-exclamation",
|
|
|
|
point: "ct-point-clicked"
|
|
|
|
},
|
|
|
|
"Unknown": {
|
2017-08-06 02:11:50 +00:00
|
|
|
color: "#6c7a89",
|
2017-04-06 03:20:00 +00:00
|
|
|
label: "label-default",
|
|
|
|
icon: "fa-question",
|
|
|
|
point: "ct-point-error"
|
|
|
|
},
|
|
|
|
"Sending": {
|
2017-08-06 02:11:50 +00:00
|
|
|
color: "#428bca",
|
2017-04-06 03:20:00 +00:00
|
|
|
label: "label-primary",
|
|
|
|
icon: "fa-spinner",
|
|
|
|
point: "ct-point-sending"
|
|
|
|
},
|
|
|
|
"Campaign Created": {
|
|
|
|
label: "label-success",
|
|
|
|
icon: "fa-rocket"
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2017-08-06 02:11:50 +00:00
|
|
|
var statusMapping = {
|
|
|
|
"Email Sent": "sent",
|
|
|
|
"Email Opened": "opened",
|
|
|
|
"Clicked Link": "clicked",
|
|
|
|
"Submitted Data": "submitted_data",
|
|
|
|
}
|
|
|
|
|
|
|
|
// This is an underwhelming attempt at an enum
|
|
|
|
// until I have time to refactor this appropriately.
|
|
|
|
var progressListing = [
|
|
|
|
"Email Sent",
|
|
|
|
"Email Opened",
|
|
|
|
"Clicked Link",
|
|
|
|
"Submitted Data"
|
|
|
|
]
|
|
|
|
|
2017-04-06 03:20:00 +00:00
|
|
|
var campaign = {}
|
|
|
|
var bubbles = []
|
|
|
|
|
|
|
|
function dismiss() {
|
|
|
|
$("#modal\\.flashes").empty()
|
|
|
|
$("#modal").modal('hide')
|
|
|
|
$("#resultsTable").dataTable().DataTable().clear().draw()
|
|
|
|
}
|
|
|
|
|
|
|
|
// Deletes a campaign after prompting the user
|
|
|
|
function deleteCampaign() {
|
|
|
|
swal({
|
|
|
|
title: "Are you sure?",
|
|
|
|
text: "This will delete the campaign. This can't be undone!",
|
|
|
|
type: "warning",
|
|
|
|
animation: false,
|
|
|
|
showCancelButton: true,
|
|
|
|
confirmButtonText: "Delete Campaign",
|
|
|
|
confirmButtonColor: "#428bca",
|
|
|
|
reverseButtons: true,
|
|
|
|
allowOutsideClick: false,
|
2017-08-06 02:11:50 +00:00
|
|
|
showLoaderOnConfirm: true,
|
|
|
|
preConfirm: function () {
|
|
|
|
return new Promise(function (resolve, reject) {
|
2017-04-06 03:20:00 +00:00
|
|
|
api.campaignId.delete(campaign.id)
|
2017-08-06 02:11:50 +00:00
|
|
|
.success(function (msg) {
|
2017-04-06 03:20:00 +00:00
|
|
|
resolve()
|
|
|
|
})
|
2017-08-06 02:11:50 +00:00
|
|
|
.error(function (data) {
|
2017-04-06 03:20:00 +00:00
|
|
|
reject(data.responseJSON.message)
|
|
|
|
})
|
|
|
|
})
|
|
|
|
}
|
2017-08-06 02:11:50 +00:00
|
|
|
}).then(function () {
|
2017-04-06 03:20:00 +00:00
|
|
|
swal(
|
|
|
|
'Campaign Deleted!',
|
|
|
|
'This campaign has been deleted!',
|
|
|
|
'success'
|
|
|
|
);
|
2017-08-06 02:11:50 +00:00
|
|
|
$('button:contains("OK")').on('click', function () {
|
2017-04-06 03:20:00 +00:00
|
|
|
location.href = '/campaigns'
|
|
|
|
})
|
|
|
|
})
|
|
|
|
}
|
|
|
|
|
|
|
|
// Completes a campaign after prompting the user
|
|
|
|
function completeCampaign() {
|
|
|
|
swal({
|
|
|
|
title: "Are you sure?",
|
|
|
|
text: "Gophish will stop processing events for this campaign",
|
|
|
|
type: "warning",
|
|
|
|
animation: false,
|
|
|
|
showCancelButton: true,
|
|
|
|
confirmButtonText: "Complete Campaign",
|
|
|
|
confirmButtonColor: "#428bca",
|
|
|
|
reverseButtons: true,
|
|
|
|
allowOutsideClick: false,
|
2017-08-06 02:11:50 +00:00
|
|
|
showLoaderOnConfirm: true,
|
|
|
|
preConfirm: function () {
|
|
|
|
return new Promise(function (resolve, reject) {
|
2017-04-06 03:20:00 +00:00
|
|
|
api.campaignId.complete(campaign.id)
|
2017-08-06 02:11:50 +00:00
|
|
|
.success(function (msg) {
|
2017-04-06 03:20:00 +00:00
|
|
|
resolve()
|
|
|
|
})
|
2017-08-06 02:11:50 +00:00
|
|
|
.error(function (data) {
|
2017-04-06 03:20:00 +00:00
|
|
|
reject(data.responseJSON.message)
|
|
|
|
})
|
|
|
|
})
|
|
|
|
}
|
2017-08-06 02:11:50 +00:00
|
|
|
}).then(function () {
|
2017-04-06 03:20:00 +00:00
|
|
|
swal(
|
|
|
|
'Campaign Completed!',
|
|
|
|
'This campaign has been completed!',
|
|
|
|
'success'
|
|
|
|
);
|
|
|
|
$('#complete_button')[0].disabled = true;
|
|
|
|
$('#complete_button').text('Completed!')
|
|
|
|
doPoll = false;
|
|
|
|
})
|
|
|
|
}
|
|
|
|
|
|
|
|
// Exports campaign results as a CSV file
|
|
|
|
function exportAsCSV(scope) {
|
|
|
|
exportHTML = $("#exportButton").html()
|
|
|
|
var csvScope = null
|
2017-09-20 01:33:26 +00:00
|
|
|
var filename = campaign.name + ' - ' + capitalize(scope) + '.csv'
|
2017-04-06 03:20:00 +00:00
|
|
|
switch (scope) {
|
|
|
|
case "results":
|
|
|
|
csvScope = campaign.results
|
|
|
|
break;
|
|
|
|
case "events":
|
|
|
|
csvScope = campaign.timeline
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
if (!csvScope) {
|
|
|
|
return
|
|
|
|
}
|
|
|
|
$("#exportButton").html('<i class="fa fa-spinner fa-spin"></i>')
|
|
|
|
var csvString = Papa.unparse(csvScope, {})
|
|
|
|
var csvData = new Blob([csvString], {
|
|
|
|
type: 'text/csv;charset=utf-8;'
|
|
|
|
});
|
|
|
|
if (navigator.msSaveBlob) {
|
2017-09-20 01:33:26 +00:00
|
|
|
navigator.msSaveBlob(csvData, filename);
|
2017-04-06 03:20:00 +00:00
|
|
|
} else {
|
|
|
|
var csvURL = window.URL.createObjectURL(csvData);
|
|
|
|
var dlLink = document.createElement('a');
|
|
|
|
dlLink.href = csvURL;
|
2017-09-20 01:33:26 +00:00
|
|
|
dlLink.setAttribute('download', filename)
|
2017-04-06 03:20:00 +00:00
|
|
|
document.body.appendChild(dlLink)
|
|
|
|
dlLink.click();
|
|
|
|
document.body.removeChild(dlLink)
|
|
|
|
}
|
|
|
|
$("#exportButton").html(exportHTML)
|
|
|
|
}
|
|
|
|
|
|
|
|
function replay(event_idx) {
|
|
|
|
request = campaign.timeline[event_idx]
|
|
|
|
details = JSON.parse(request.details)
|
|
|
|
url = null
|
|
|
|
form = $('<form>').attr({
|
2017-08-06 02:11:50 +00:00
|
|
|
method: 'POST',
|
|
|
|
target: '_blank',
|
|
|
|
})
|
|
|
|
/* Create a form object and submit it */
|
|
|
|
$.each(Object.keys(details.payload), function (i, param) {
|
|
|
|
if (param == "rid") {
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
if (param == "__original_url") {
|
|
|
|
url = details.payload[param];
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
$('<input>').attr({
|
|
|
|
name: param,
|
|
|
|
}).val(details.payload[param]).appendTo(form);
|
|
|
|
})
|
|
|
|
/* Ensure we know where to send the user */
|
|
|
|
// Prompt for the URL
|
2017-04-06 03:20:00 +00:00
|
|
|
swal({
|
|
|
|
title: 'Where do you want the credentials submitted to?',
|
|
|
|
input: 'text',
|
|
|
|
showCancelButton: true,
|
|
|
|
inputPlaceholder: "http://example.com/login",
|
|
|
|
inputValue: url || "",
|
2017-08-06 02:11:50 +00:00
|
|
|
inputValidator: function (value) {
|
|
|
|
return new Promise(function (resolve, reject) {
|
2017-04-06 03:20:00 +00:00
|
|
|
if (value) {
|
|
|
|
resolve();
|
|
|
|
} else {
|
|
|
|
reject('Invalid URL.');
|
|
|
|
}
|
|
|
|
});
|
|
|
|
}
|
2017-08-06 02:11:50 +00:00
|
|
|
}).then(function (result) {
|
2017-04-06 03:20:00 +00:00
|
|
|
url = result
|
|
|
|
submitForm()
|
|
|
|
})
|
|
|
|
return
|
|
|
|
submitForm()
|
|
|
|
|
|
|
|
function submitForm() {
|
|
|
|
form.attr({
|
|
|
|
action: url
|
|
|
|
})
|
|
|
|
form.appendTo('body').submit().remove()
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
function renderTimeline(data) {
|
|
|
|
record = {
|
|
|
|
"first_name": data[2],
|
|
|
|
"last_name": data[3],
|
|
|
|
"email": data[4],
|
|
|
|
"position": data[5]
|
|
|
|
}
|
|
|
|
results = '<div class="timeline col-sm-12 well well-lg">' +
|
|
|
|
'<h6>Timeline for ' + escapeHtml(record.first_name) + ' ' + escapeHtml(record.last_name) +
|
|
|
|
'</h6><span class="subtitle">Email: ' + escapeHtml(record.email) + '</span>' +
|
|
|
|
'<div class="timeline-graph col-sm-6">'
|
2017-08-06 02:11:50 +00:00
|
|
|
$.each(campaign.timeline, function (i, event) {
|
2017-04-06 03:20:00 +00:00
|
|
|
if (!event.email || event.email == record.email) {
|
|
|
|
// Add the event
|
|
|
|
results += '<div class="timeline-entry">' +
|
|
|
|
' <div class="timeline-bar"></div>'
|
|
|
|
results +=
|
|
|
|
' <div class="timeline-icon ' + statuses[event.message].label + '">' +
|
|
|
|
' <i class="fa ' + statuses[event.message].icon + '"></i></div>' +
|
|
|
|
' <div class="timeline-message">' + escapeHtml(event.message) +
|
2017-08-29 03:48:49 +00:00
|
|
|
' <span class="timeline-date">' + moment.utc(event.time).local().format('MMMM Do YYYY h:mm a') + '</span>'
|
2017-04-06 03:20:00 +00:00
|
|
|
if (event.details) {
|
|
|
|
if (event.message == "Submitted Data") {
|
|
|
|
results += '<div class="timeline-replay-button"><button onclick="replay(' + i + ')" class="btn btn-success">'
|
|
|
|
results += '<i class="fa fa-refresh"></i> Replay Credentials</button></div>'
|
|
|
|
results += '<div class="timeline-event-details"><i class="fa fa-caret-right"></i> View Details</div>'
|
|
|
|
}
|
|
|
|
details = JSON.parse(event.details)
|
|
|
|
if (details.payload) {
|
|
|
|
results += '<div class="timeline-event-results">'
|
|
|
|
results += ' <table class="table table-condensed table-bordered table-striped">'
|
|
|
|
results += ' <thead><tr><th>Parameter</th><th>Value(s)</tr></thead><tbody>'
|
2017-08-06 02:11:50 +00:00
|
|
|
$.each(Object.keys(details.payload), function (i, param) {
|
2017-04-06 03:20:00 +00:00
|
|
|
if (param == "rid") {
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
results += ' <tr>'
|
|
|
|
results += ' <td>' + escapeHtml(param) + '</td>'
|
|
|
|
results += ' <td>' + escapeHtml(details.payload[param]) + '</td>'
|
|
|
|
results += ' </tr>'
|
|
|
|
})
|
|
|
|
results += ' </tbody></table>'
|
|
|
|
results += '</div>'
|
|
|
|
}
|
|
|
|
if (details.error) {
|
|
|
|
results += '<div class="timeline-event-details"><i class="fa fa-caret-right"></i> View Details</div>'
|
|
|
|
results += '<div class="timeline-event-results">'
|
|
|
|
results += '<span class="label label-default">Error</span> ' + details.error
|
|
|
|
results += '</div>'
|
|
|
|
}
|
|
|
|
}
|
|
|
|
results += '</div></div>'
|
|
|
|
}
|
|
|
|
})
|
|
|
|
results += '</div></div>'
|
|
|
|
return results
|
|
|
|
}
|
|
|
|
|
2017-08-06 02:11:50 +00:00
|
|
|
var renderTimelineChart = function (chartopts) {
|
|
|
|
return Highcharts.chart('timeline_chart', {
|
|
|
|
chart: {
|
|
|
|
zoomType: 'x',
|
|
|
|
type: 'line',
|
|
|
|
height: "200px"
|
|
|
|
},
|
|
|
|
title: {
|
|
|
|
text: 'Campaign Timeline'
|
|
|
|
},
|
|
|
|
xAxis: {
|
|
|
|
type: 'datetime',
|
|
|
|
dateTimeLabelFormats: {
|
|
|
|
second: '%l:%M:%S',
|
|
|
|
minute: '%l:%M',
|
|
|
|
hour: '%l:%M',
|
|
|
|
day: '%b %d, %Y',
|
|
|
|
week: '%b %d, %Y',
|
|
|
|
month: '%b %Y'
|
|
|
|
}
|
|
|
|
},
|
|
|
|
yAxis: {
|
|
|
|
min: 0,
|
|
|
|
max: 2,
|
|
|
|
visible: false,
|
|
|
|
tickInterval: 1,
|
|
|
|
labels: {
|
|
|
|
enabled: false
|
|
|
|
},
|
|
|
|
title: {
|
|
|
|
text: ""
|
|
|
|
}
|
|
|
|
},
|
|
|
|
tooltip: {
|
|
|
|
formatter: function () {
|
|
|
|
return Highcharts.dateFormat('%A, %b %d %l:%M:%S %P', new Date(this.x)) +
|
|
|
|
'<br>Event: ' + this.point.message + '<br>Email: <b>' + this.point.email + '</b>'
|
|
|
|
}
|
|
|
|
},
|
|
|
|
legend: {
|
|
|
|
enabled: false
|
|
|
|
},
|
|
|
|
plotOptions: {
|
|
|
|
series: {
|
|
|
|
marker: {
|
|
|
|
enabled: true,
|
|
|
|
symbol: 'circle',
|
|
|
|
radius: 3
|
|
|
|
},
|
|
|
|
cursor: 'pointer',
|
|
|
|
},
|
|
|
|
line: {
|
|
|
|
states: {
|
|
|
|
hover: {
|
|
|
|
lineWidth: 1
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
},
|
|
|
|
credits: {
|
|
|
|
enabled: false
|
|
|
|
},
|
|
|
|
series: [{
|
|
|
|
data: chartopts['data'],
|
|
|
|
dashStyle: "shortdash",
|
|
|
|
color: "#cccccc",
|
2017-09-27 02:29:15 +00:00
|
|
|
lineWidth: 1,
|
|
|
|
turboThreshold: 0
|
2017-08-06 02:11:50 +00:00
|
|
|
}]
|
|
|
|
})
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Renders a pie chart using the provided chartops */
|
|
|
|
var renderPieChart = function (chartopts) {
|
|
|
|
return Highcharts.chart(chartopts['elemId'], {
|
|
|
|
chart: {
|
|
|
|
type: 'pie',
|
|
|
|
events: {
|
|
|
|
load: function () {
|
|
|
|
var chart = this,
|
|
|
|
rend = chart.renderer,
|
|
|
|
pie = chart.series[0],
|
|
|
|
left = chart.plotLeft + pie.center[0],
|
|
|
|
top = chart.plotTop + pie.center[1];
|
|
|
|
this.innerText = rend.text(chartopts['data'][0].y, left, top).
|
2017-09-17 04:30:04 +00:00
|
|
|
attr({
|
|
|
|
'text-anchor': 'middle',
|
|
|
|
'font-size': '24px',
|
|
|
|
'font-weight': 'bold',
|
|
|
|
'fill': chartopts['colors'][0],
|
|
|
|
'font-family': 'Helvetica,Arial,sans-serif'
|
|
|
|
}).add();
|
2017-08-06 02:11:50 +00:00
|
|
|
},
|
|
|
|
render: function () {
|
2017-09-17 04:30:04 +00:00
|
|
|
this.innerText.attr({
|
|
|
|
text: chartopts['data'][0].y
|
|
|
|
})
|
2017-08-06 02:11:50 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
},
|
|
|
|
title: {
|
|
|
|
text: chartopts['title']
|
|
|
|
},
|
|
|
|
plotOptions: {
|
|
|
|
pie: {
|
|
|
|
innerSize: '80%',
|
|
|
|
dataLabels: {
|
|
|
|
enabled: false
|
|
|
|
}
|
|
|
|
}
|
|
|
|
},
|
|
|
|
credits: {
|
|
|
|
enabled: false
|
|
|
|
},
|
|
|
|
tooltip: {
|
|
|
|
formatter: function () {
|
|
|
|
if (this.key == undefined) {
|
|
|
|
return false
|
|
|
|
}
|
|
|
|
return '<span style="color:' + this.color + '">\u25CF</span>' + this.point.name + ': <b>' + this.y + '</b><br/>'
|
|
|
|
}
|
|
|
|
},
|
|
|
|
series: [{
|
|
|
|
data: chartopts['data'],
|
|
|
|
colors: chartopts['colors'],
|
|
|
|
}]
|
|
|
|
})
|
|
|
|
}
|
2017-04-06 03:20:00 +00:00
|
|
|
|
2017-09-17 04:30:04 +00:00
|
|
|
/* Updates the bubbles on the map
|
|
|
|
|
|
|
|
@param {campaign.result[]} results - The campaign results to process
|
|
|
|
*/
|
|
|
|
var updateMap = function (results) {
|
|
|
|
if (!map) {
|
|
|
|
return
|
|
|
|
}
|
|
|
|
bubbles = []
|
|
|
|
$.each(campaign.results, function (i, result) {
|
|
|
|
// Check that it wasn't an internal IP
|
|
|
|
if (result.latitude == 0 && result.longitude == 0) {
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
newIP = true
|
|
|
|
$.each(bubbles, function (i, bubble) {
|
|
|
|
if (bubble.ip == result.ip) {
|
|
|
|
bubbles[i].radius += 1
|
|
|
|
newIP = false
|
|
|
|
return false
|
|
|
|
}
|
|
|
|
})
|
|
|
|
if (newIP) {
|
|
|
|
bubbles.push({
|
|
|
|
latitude: result.latitude,
|
|
|
|
longitude: result.longitude,
|
|
|
|
name: result.ip,
|
|
|
|
fillKey: "point",
|
|
|
|
radius: 2
|
|
|
|
})
|
|
|
|
}
|
|
|
|
})
|
|
|
|
map.bubbles(bubbles)
|
|
|
|
}
|
|
|
|
|
2017-04-06 03:20:00 +00:00
|
|
|
/* poll - Queries the API and updates the UI with the results
|
|
|
|
*
|
|
|
|
* Updates:
|
|
|
|
* * Timeline Chart
|
|
|
|
* * Email (Donut) Chart
|
|
|
|
* * Map Bubbles
|
|
|
|
* * Datatables
|
|
|
|
*/
|
|
|
|
function poll() {
|
|
|
|
api.campaignId.results(campaign.id)
|
2017-08-06 02:11:50 +00:00
|
|
|
.success(function (c) {
|
2017-04-06 03:20:00 +00:00
|
|
|
campaign = c
|
2017-08-06 02:11:50 +00:00
|
|
|
/* Update the timeline */
|
|
|
|
var timeline_series_data = []
|
|
|
|
$.each(campaign.timeline, function (i, event) {
|
2017-08-29 03:48:49 +00:00
|
|
|
var event_date = moment.utc(event.time).local()
|
2017-08-06 02:11:50 +00:00
|
|
|
timeline_series_data.push({
|
|
|
|
email: event.email,
|
|
|
|
x: event_date.valueOf(),
|
2017-04-06 03:20:00 +00:00
|
|
|
y: 1
|
|
|
|
})
|
|
|
|
})
|
2017-08-06 02:11:50 +00:00
|
|
|
var timeline_series_data = []
|
|
|
|
$.each(campaign.timeline, function (i, event) {
|
2017-08-29 03:48:49 +00:00
|
|
|
var event_date = moment.utc(event.time).local()
|
2017-08-06 02:11:50 +00:00
|
|
|
timeline_series_data.push({
|
|
|
|
email: event.email,
|
|
|
|
message: event.message,
|
|
|
|
x: event_date.valueOf(),
|
|
|
|
y: 1,
|
|
|
|
marker: {
|
|
|
|
fillColor: statuses[event.message].color
|
|
|
|
}
|
|
|
|
})
|
|
|
|
})
|
|
|
|
var timeline_chart = $("#timeline_chart").highcharts()
|
|
|
|
timeline_chart.series[0].update({
|
|
|
|
data: timeline_series_data
|
|
|
|
})
|
2017-04-06 03:20:00 +00:00
|
|
|
/* Update the results donut chart */
|
|
|
|
var email_series_data = {}
|
2017-08-06 02:11:50 +00:00
|
|
|
// Load the initial data
|
|
|
|
Object.keys(statusMapping).forEach(function (k) {
|
|
|
|
email_series_data[k] = 0
|
|
|
|
});
|
|
|
|
$.each(campaign.results, function (i, result) {
|
|
|
|
email_series_data[result.status]++;
|
|
|
|
// Backfill status values
|
|
|
|
var step = progressListing.indexOf(result.status)
|
|
|
|
for (var i = 0; i < step; i++) {
|
|
|
|
email_series_data[progressListing[i]]++
|
2017-04-06 03:20:00 +00:00
|
|
|
}
|
|
|
|
})
|
2017-08-06 02:11:50 +00:00
|
|
|
$.each(email_series_data, function (status, count) {
|
|
|
|
var email_data = []
|
|
|
|
if (!(status in statusMapping)) {
|
|
|
|
return true
|
|
|
|
}
|
|
|
|
email_data.push({
|
|
|
|
name: status,
|
|
|
|
y: count
|
|
|
|
})
|
|
|
|
email_data.push({
|
|
|
|
name: '',
|
|
|
|
y: campaign.results.length - count
|
|
|
|
})
|
|
|
|
var chart = $("#" + statusMapping[status] + "_chart").highcharts()
|
|
|
|
chart.series[0].update({
|
|
|
|
data: email_data
|
2017-04-06 03:20:00 +00:00
|
|
|
})
|
|
|
|
})
|
|
|
|
/* Update the datatable */
|
|
|
|
resultsTable = $("#resultsTable").DataTable()
|
2017-08-06 02:11:50 +00:00
|
|
|
resultsTable.rows().every(function (i, tableLoop, rowLoop) {
|
|
|
|
var row = this.row(i)
|
|
|
|
var rowData = row.data()
|
|
|
|
var rid = rowData[0]
|
|
|
|
$.each(campaign.results, function (j, result) {
|
|
|
|
if (result.id == rid) {
|
|
|
|
var label = statuses[result.status].label || "label-default";
|
|
|
|
rowData[6] = "<span class=\"label " + label + "\">" + result.status + "</span>"
|
2017-09-27 02:29:15 +00:00
|
|
|
resultsTable.row(i).data(rowData)
|
2017-08-06 02:11:50 +00:00
|
|
|
if (row.child.isShown()) {
|
|
|
|
row.child(renderTimeline(row.data()))
|
2017-04-06 03:20:00 +00:00
|
|
|
}
|
2017-08-06 02:11:50 +00:00
|
|
|
return false
|
|
|
|
}
|
2017-04-06 03:20:00 +00:00
|
|
|
})
|
2017-08-06 02:11:50 +00:00
|
|
|
})
|
2017-09-27 02:29:15 +00:00
|
|
|
resultsTable.draw(false)
|
2017-08-06 02:11:50 +00:00
|
|
|
/* Update the map information */
|
2017-09-17 04:30:04 +00:00
|
|
|
updateMap(campaign.results)
|
2017-04-06 03:20:00 +00:00
|
|
|
$("#refresh_message").hide()
|
|
|
|
$("#refresh_btn").show()
|
|
|
|
})
|
|
|
|
}
|
|
|
|
|
|
|
|
function load() {
|
|
|
|
campaign.id = window.location.pathname.split('/').slice(-1)[0]
|
2017-09-17 04:30:04 +00:00
|
|
|
var use_map = JSON.parse(localStorage.getItem('gophish.use_map'))
|
2017-04-06 03:20:00 +00:00
|
|
|
api.campaignId.results(campaign.id)
|
2017-08-06 02:11:50 +00:00
|
|
|
.success(function (c) {
|
2017-04-06 03:20:00 +00:00
|
|
|
campaign = c
|
|
|
|
if (campaign) {
|
|
|
|
$("title").text(c.name + " - Gophish")
|
|
|
|
$("#loading").hide()
|
|
|
|
$("#campaignResults").show()
|
2017-08-06 02:11:50 +00:00
|
|
|
// Set the title
|
2017-04-06 03:20:00 +00:00
|
|
|
$("#page-title").text("Results for " + c.name)
|
|
|
|
if (c.status == "Completed") {
|
|
|
|
$('#complete_button')[0].disabled = true;
|
|
|
|
$('#complete_button').text('Completed!');
|
|
|
|
doPoll = false;
|
|
|
|
}
|
|
|
|
// Setup tooltips
|
|
|
|
$('[data-toggle="tooltip"]').tooltip()
|
2017-08-06 02:11:50 +00:00
|
|
|
// Setup viewing the details of a result
|
|
|
|
$("#resultsTable").on("click", ".timeline-event-details", function () {
|
|
|
|
// Show the parameters
|
|
|
|
payloadResults = $(this).parent().find(".timeline-event-results")
|
|
|
|
if (payloadResults.is(":visible")) {
|
|
|
|
$(this).find("i").removeClass("fa-caret-down")
|
|
|
|
$(this).find("i").addClass("fa-caret-right")
|
|
|
|
payloadResults.hide()
|
|
|
|
} else {
|
|
|
|
$(this).find("i").removeClass("fa-caret-right")
|
|
|
|
$(this).find("i").addClass("fa-caret-down")
|
|
|
|
payloadResults.show()
|
2017-04-06 03:20:00 +00:00
|
|
|
}
|
2017-08-06 02:11:50 +00:00
|
|
|
})
|
|
|
|
// Setup the results table
|
2017-04-06 03:20:00 +00:00
|
|
|
resultsTable = $("#resultsTable").DataTable({
|
|
|
|
destroy: true,
|
|
|
|
"order": [
|
|
|
|
[2, "asc"]
|
|
|
|
],
|
|
|
|
columnDefs: [{
|
|
|
|
orderable: false,
|
|
|
|
targets: "no-sort"
|
|
|
|
}, {
|
|
|
|
className: "details-control",
|
|
|
|
"targets": [1]
|
|
|
|
}, {
|
|
|
|
"visible": false,
|
|
|
|
"targets": [0]
|
|
|
|
}]
|
|
|
|
});
|
|
|
|
resultsTable.clear();
|
2017-08-06 02:11:50 +00:00
|
|
|
var email_series_data = {}
|
|
|
|
var timeline_series_data = []
|
|
|
|
Object.keys(statusMapping).forEach(function (k) {
|
|
|
|
email_series_data[k] = 0
|
|
|
|
});
|
|
|
|
$.each(campaign.results, function (i, result) {
|
|
|
|
label = statuses[result.status].label || "label-default";
|
|
|
|
resultsTable.row.add([
|
|
|
|
result.id,
|
|
|
|
"<i class=\"fa fa-caret-right\"></i>",
|
|
|
|
escapeHtml(result.first_name) || "",
|
|
|
|
escapeHtml(result.last_name) || "",
|
|
|
|
escapeHtml(result.email) || "",
|
|
|
|
escapeHtml(result.position) || "",
|
|
|
|
"<span class=\"label " + label + "\">" + result.status + "</span>"
|
2017-09-27 02:29:15 +00:00
|
|
|
])
|
2017-08-06 02:11:50 +00:00
|
|
|
email_series_data[result.status]++;
|
|
|
|
// Backfill status values
|
|
|
|
var step = progressListing.indexOf(result.status)
|
|
|
|
for (var i = 0; i < step; i++) {
|
|
|
|
email_series_data[progressListing[i]]++
|
|
|
|
}
|
|
|
|
})
|
2017-09-27 02:29:15 +00:00
|
|
|
resultsTable.draw();
|
2017-08-06 02:11:50 +00:00
|
|
|
// Setup the individual timelines
|
|
|
|
$('#resultsTable tbody').on('click', 'td.details-control', function () {
|
2017-04-06 03:20:00 +00:00
|
|
|
var tr = $(this).closest('tr');
|
|
|
|
var row = resultsTable.row(tr);
|
|
|
|
if (row.child.isShown()) {
|
|
|
|
// This row is already open - close it
|
|
|
|
row.child.hide();
|
|
|
|
tr.removeClass('shown');
|
|
|
|
$(this).find("i").removeClass("fa-caret-down")
|
|
|
|
$(this).find("i").addClass("fa-caret-right")
|
|
|
|
row.invalidate('dom').draw(false)
|
|
|
|
} else {
|
|
|
|
// Open this row
|
|
|
|
$(this).find("i").removeClass("fa-caret-right")
|
|
|
|
$(this).find("i").addClass("fa-caret-down")
|
|
|
|
row.child(renderTimeline(row.data())).show();
|
|
|
|
tr.addClass('shown');
|
|
|
|
row.invalidate('dom').draw(false)
|
|
|
|
}
|
|
|
|
});
|
|
|
|
// Setup the graphs
|
2017-08-06 02:11:50 +00:00
|
|
|
$.each(campaign.timeline, function (i, event) {
|
2017-08-29 03:48:49 +00:00
|
|
|
var event_date = moment.utc(event.time).local()
|
2017-08-06 02:11:50 +00:00
|
|
|
timeline_series_data.push({
|
|
|
|
email: event.email,
|
|
|
|
message: event.message,
|
|
|
|
x: event_date.valueOf(),
|
|
|
|
y: 1,
|
|
|
|
marker: {
|
|
|
|
fillColor: statuses[event.message].color
|
|
|
|
}
|
2017-04-06 03:20:00 +00:00
|
|
|
})
|
|
|
|
})
|
2017-08-06 02:11:50 +00:00
|
|
|
renderTimelineChart({
|
|
|
|
data: timeline_series_data
|
2017-04-06 03:20:00 +00:00
|
|
|
})
|
2017-08-06 02:11:50 +00:00
|
|
|
$.each(email_series_data, function (status, count) {
|
|
|
|
var email_data = []
|
|
|
|
if (!(status in statusMapping)) {
|
|
|
|
return true
|
2017-04-06 03:20:00 +00:00
|
|
|
}
|
2017-08-06 02:11:50 +00:00
|
|
|
email_data.push({
|
|
|
|
name: status,
|
|
|
|
y: count
|
2017-04-06 03:20:00 +00:00
|
|
|
})
|
2017-08-06 02:11:50 +00:00
|
|
|
email_data.push({
|
|
|
|
name: '',
|
|
|
|
y: campaign.results.length - count
|
|
|
|
})
|
|
|
|
var chart = renderPieChart({
|
|
|
|
elemId: statusMapping[status] + '_chart',
|
|
|
|
title: status,
|
|
|
|
name: status,
|
|
|
|
data: email_data,
|
|
|
|
colors: [statuses[status].color, '#dddddd']
|
|
|
|
})
|
|
|
|
})
|
2017-09-17 04:30:04 +00:00
|
|
|
if (use_map) {
|
|
|
|
$("#resultsMapContainer").show()
|
2017-04-06 03:20:00 +00:00
|
|
|
map = new Datamap({
|
|
|
|
element: document.getElementById("resultsMap"),
|
|
|
|
responsive: true,
|
|
|
|
fills: {
|
|
|
|
defaultFill: "#ffffff",
|
|
|
|
point: "#283F50"
|
|
|
|
},
|
|
|
|
geographyConfig: {
|
|
|
|
highlightFillColor: "#1abc9c",
|
|
|
|
borderColor: "#283F50"
|
|
|
|
},
|
|
|
|
bubblesConfig: {
|
|
|
|
borderColor: "#283F50"
|
|
|
|
}
|
|
|
|
});
|
|
|
|
}
|
2017-09-17 04:30:04 +00:00
|
|
|
updateMap(campaign.results)
|
2017-04-06 03:20:00 +00:00
|
|
|
}
|
|
|
|
})
|
2017-08-06 02:11:50 +00:00
|
|
|
.error(function () {
|
2017-04-06 03:20:00 +00:00
|
|
|
$("#loading").hide()
|
|
|
|
errorFlash(" Campaign not found!")
|
|
|
|
})
|
|
|
|
}
|
|
|
|
|
|
|
|
var setRefresh
|
2017-09-17 04:30:04 +00:00
|
|
|
|
2017-04-06 03:20:00 +00:00
|
|
|
function refresh() {
|
|
|
|
if (!doPoll) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
$("#refresh_message").show()
|
|
|
|
$("#refresh_btn").hide()
|
|
|
|
poll()
|
|
|
|
clearTimeout(setRefresh)
|
|
|
|
setRefresh = setTimeout(refresh, 60000)
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
|
2017-08-06 02:11:50 +00:00
|
|
|
$(document).ready(function () {
|
|
|
|
Highcharts.setOptions({
|
|
|
|
global: {
|
|
|
|
useUTC: false
|
|
|
|
}
|
|
|
|
})
|
2017-04-06 03:20:00 +00:00
|
|
|
load();
|
2017-08-06 02:11:50 +00:00
|
|
|
|
2017-04-06 03:20:00 +00:00
|
|
|
// Start the polling loop
|
|
|
|
setRefresh = setTimeout(refresh, 60000)
|
2017-09-17 04:30:04 +00:00
|
|
|
})
|