'
- $.each(campaign.timeline, function (i, event) {
- if (!event.email || event.email == record.email) {
- // Add the event
- results += '
' +
- '
'
- results +=
- '
' +
- '
' +
- '
' + escapeHtml(event.message) +
- '
' + moment.utc(event.time).local().format('MMMM Do YYYY h:mm:ss a') + ''
- if (event.details) {
- details = JSON.parse(event.details)
- if (event.message == "Clicked Link" || event.message == "Submitted Data") {
- deviceView = renderDevice(details)
- if (deviceView) {
- results += deviceView
- }
- }
- if (event.message == "Submitted Data") {
- results += '
'
- results += '
View Details
'
- }
- if (details.payload) {
- results += '
'
- results += '
'
- results += ' Parameter | Value(s) |
'
- $.each(Object.keys(details.payload), function (i, param) {
- if (param == "rid") {
- return true;
- }
- results += ' '
- results += ' ' + escapeHtml(param) + ' | '
- results += ' ' + escapeHtml(details.payload[param]) + ' | '
- results += '
'
- })
- results += '
'
- results += '
'
- }
- if (details.error) {
- results += '
View Details
'
- results += '
'
- results += 'Error ' + details.error
- results += '
'
- }
- }
- results += '
'
- }
- })
- // Add the scheduled send event at the bottom
- if (record.status == "Scheduled" || record.status == "Retrying") {
- results += '
' +
- '
'
- results +=
- '
' +
- '
' +
- '
' + "Scheduled to send at " + record.send_date + ''
- }
- results += '
'
- return results
-}
-
-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)) +
- '
Event: ' + this.point.message + '
Email:
' + this.point.email + ''
- }
- },
- 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",
- lineWidth: 1,
- turboThreshold: 0
- }]
- })
-}
-
-/* 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].count, left, top).
- attr({
- 'text-anchor': 'middle',
- 'font-size': '24px',
- 'font-weight': 'bold',
- 'fill': chartopts['colors'][0],
- 'font-family': 'Helvetica,Arial,sans-serif'
- }).add();
- },
- render: function () {
- this.innerText.attr({
- text: chartopts['data'][0].count
- })
- }
- }
- },
- title: {
- text: chartopts['title']
- },
- plotOptions: {
- pie: {
- innerSize: '80%',
- dataLabels: {
- enabled: false
- }
- }
- },
- credits: {
- enabled: false
- },
- tooltip: {
- formatter: function () {
- if (this.key == undefined) {
- return false
- }
- return '
\u25CF' + this.point.name + ':
' + this.y + '%'
- }
- },
- series: [{
- data: chartopts['data'],
- colors: chartopts['colors'],
- }]
- })
-}
-
-/* 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)
-}
-
-/**
- * Creates a status label for use in the results datatable
- * @param {string} status
- * @param {moment(datetime)} send_date
- */
-function createStatusLabel(status, send_date) {
- var label = statuses[status].label || "label-default";
- var statusColumn = "
" + status + ""
- // Add the tooltip if the email is scheduled to be sent
- if (status == "Scheduled" || status == "Retrying") {
- var sendDateMessage = "Scheduled to send at " + send_date
- statusColumn = "
" + status + ""
- }
- return statusColumn
-}
-
-/* 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)
- .success(function (c) {
- campaign = c
- /* Update the timeline */
- var timeline_series_data = []
- $.each(campaign.timeline, function (i, event) {
- var event_date = moment.utc(event.time).local()
- 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
- })
- /* Update the results donut chart */
- var email_series_data = {}
- // 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]++;
- if (result.reported) {
- email_series_data['Email Reported']++
- }
- // Backfill status values
- var step = progressListing.indexOf(result.status)
- for (var i = 0; i < step; i++) {
- email_series_data[progressListing[i]]++
- }
- })
- $.each(email_series_data, function (status, count) {
- var email_data = []
- if (!(status in statusMapping)) {
- return true
- }
- email_data.push({
- name: status,
- y: Math.floor((count / campaign.results.length) * 100),
- count: count
- })
- email_data.push({
- name: '',
- y: 100 - Math.floor((count / campaign.results.length) * 100)
- })
- var chart = $("#" + statusMapping[status] + "_chart").highcharts()
- chart.series[0].update({
- data: email_data
- })
- })
-
- /* Update the datatable */
- resultsTable = $("#resultsTable").DataTable()
- 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) {
- rowData[8] = moment(result.send_date).format('MMMM Do YYYY, h:mm:ss a')
- rowData[7] = result.reported
- rowData[6] = result.status
- resultsTable.row(i).data(rowData)
- if (row.child.isShown()) {
- $(row.node()).find("#caret").removeClass("fa-caret-right")
- $(row.node()).find("#caret").addClass("fa-caret-down")
- row.child(renderTimeline(row.data()))
- }
- return false
- }
- })
- })
- resultsTable.draw(false)
- /* Update the map information */
- updateMap(campaign.results)
- $('[data-toggle="tooltip"]').tooltip()
- $("#refresh_message").hide()
- $("#refresh_btn").show()
- })
-}
-
-function load() {
- campaign.id = window.location.pathname.split('/').slice(-1)[0]
- var use_map = JSON.parse(localStorage.getItem('gophish.use_map'))
- api.campaignId.results(campaign.id)
- .success(function (c) {
- campaign = c
- if (campaign) {
- $("title").text(c.name + " - Gophish")
- $("#loading").hide()
- $("#campaignResults").show()
- // Set the title
- $("#page-title").text("Results for " + c.name)
- if (c.status == "Completed") {
- $('#complete_button')[0].disabled = true;
- $('#complete_button').text('Completed!');
- doPoll = false;
- }
- // 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()
- }
- })
- // Setup the results table
- resultsTable = $("#resultsTable").DataTable({
- destroy: true,
- "order": [
- [2, "asc"]
- ],
- columnDefs: [{
- orderable: false,
- targets: "no-sort"
- }, {
- className: "details-control",
- "targets": [1]
- }, {
- "visible": false,
- "targets": [0, 8]
- },
- {
- "render": function (data, type, row) {
- return createStatusLabel(data, row[8])
- },
- "targets": [6]
- },
- {
- className: "text-center",
- "render": function (reported, type, row) {
- if (type == "display") {
- if (reported) {
- return "
"
- }
- return "
"
- }
- return reported
- },
- "targets": [7]
- }
- ]
- });
- resultsTable.clear();
- 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) {
- resultsTable.row.add([
- result.id,
- "
",
- escapeHtml(result.first_name) || "",
- escapeHtml(result.last_name) || "",
- escapeHtml(result.email) || "",
- escapeHtml(result.position) || "",
- result.status,
- result.reported,
- moment(result.send_date).format('MMMM Do YYYY, h:mm:ss a')
- ])
- email_series_data[result.status]++;
- if (result.reported) {
- email_series_data['Email Reported']++
- }
- // Backfill status values
- var step = progressListing.indexOf(result.status)
- for (var i = 0; i < step; i++) {
- email_series_data[progressListing[i]]++
- }
- })
- resultsTable.draw();
- // Setup tooltips
- $('[data-toggle="tooltip"]').tooltip()
- // Setup the individual timelines
- $('#resultsTable tbody').on('click', 'td.details-control', function () {
- 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")
- } 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');
- }
- });
- // Setup the graphs
- $.each(campaign.timeline, function (i, event) {
- if (event.message == "Campaign Created") {
- return true
- }
- var event_date = moment.utc(event.time).local()
- timeline_series_data.push({
- email: event.email,
- message: event.message,
- x: event_date.valueOf(),
- y: 1,
- marker: {
- fillColor: statuses[event.message].color
- }
- })
- })
- renderTimelineChart({
- data: timeline_series_data
- })
- $.each(email_series_data, function (status, count) {
- var email_data = []
- if (!(status in statusMapping)) {
- return true
- }
- email_data.push({
- name: status,
- y: Math.floor((count / campaign.results.length) * 100),
- count: count
- })
- email_data.push({
- name: '',
- y: 100 - Math.floor((count / campaign.results.length) * 100)
- })
- var chart = renderPieChart({
- elemId: statusMapping[status] + '_chart',
- title: status,
- name: status,
- data: email_data,
- colors: [statuses[status].color, '#dddddd']
- })
- })
-
- if (use_map) {
- $("#resultsMapContainer").show()
- map = new Datamap({
- element: document.getElementById("resultsMap"),
- responsive: true,
- fills: {
- defaultFill: "#ffffff",
- point: "#283F50"
- },
- geographyConfig: {
- highlightFillColor: "#1abc9c",
- borderColor: "#283F50"
- },
- bubblesConfig: {
- borderColor: "#283F50"
- }
- });
- }
- updateMap(campaign.results)
- }
- })
- .error(function () {
- $("#loading").hide()
- errorFlash(" Campaign not found!")
- })
-}
-
-var setRefresh
-
-function refresh() {
- if (!doPoll) {
- return;
- }
- $("#refresh_message").show()
- $("#refresh_btn").hide()
- poll()
- clearTimeout(setRefresh)
- setRefresh = setTimeout(refresh, 60000)
-};
-
-function report_mail(rid, cid) {
- Swal.fire({
- title: "Are you sure?",
- text: "This result will be flagged as reported (RID: " + rid + ")",
- type: "question",
- animation: false,
- showCancelButton: true,
- confirmButtonText: "Continue",
- confirmButtonColor: "#428bca",
- reverseButtons: true,
- allowOutsideClick: false,
- showLoaderOnConfirm: true
- }).then(function (result) {
- if (result.value){
- api.campaignId.get(cid).success((function(c) {
- report_url = c.url;
- report_url += report_url.endsWith("/") ? "" : "/";
- report_url += "report?rid=" + rid;
- $.ajax({
- url: report_url,
- method: "GET",
- success: function(data) {
- refresh();
- }
- });
- }));
- }
- })
-}
-
-$(document).ready(function () {
- Highcharts.setOptions({
- global: {
- useUTC: false
- }
- })
- load();
-
- // Start the polling loop
- setRefresh = setTimeout(refresh, 60000)
-})
+var map = null
+var doPoll = true;
+var customEventsPieCharts = true; // Include pie charts for custom events or not
+ // Setting to true will add the custom events to statusMapping as wel as adding HTML chart elements.
+
+// statuses is a helper map to point result statuses to ui classes
+var statuses = {
+
+ "Email Sent": {
+ color: "#1abc9c",
+ label: "label-success",
+ icon: "fa-envelope",
+ point: "ct-point-sent"
+ },
+ "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"
+ },
+ "Email Opened": {
+ color: "#f9bf3b",
+ label: "label-warning",
+ icon: "fa-envelope-open",
+ point: "ct-point-opened"
+ },
+ "Clicked Link": {
+ color: "#F39C12",
+ label: "label-clicked",
+ icon: "fa-mouse-pointer",
+ point: "ct-point-clicked"
+ },
+ "Success": {
+ color: "#f05b4f",
+ label: "label-danger",
+ icon: "fa-exclamation",
+ point: "ct-point-clicked"
+ },
+ //not a status, but is used for the campaign timeline and user timeline
+ "Email Reported": {
+ color: "#45d6ef",
+ label: "label-info",
+ icon: "fa-bullhorn",
+ point: "ct-point-reported"
+ },
+ "Error": {
+ color: "#6c7a89",
+ label: "label-default",
+ icon: "fa-times",
+ point: "ct-point-error"
+ },
+ "Error Sending Email": {
+ color: "#6c7a89",
+ label: "label-default",
+ icon: "fa-times",
+ point: "ct-point-error"
+ },
+ "Submitted Data": {
+ color: "#f05b4f",
+ label: "label-danger",
+ icon: "fa-exclamation",
+ point: "ct-point-clicked"
+ },
+ "Unknown": {
+ color: "#6c7a89",
+ label: "label-default",
+ icon: "fa-question",
+ point: "ct-point-error"
+ },
+ "Sending": {
+ color: "#428bca",
+ label: "label-primary",
+ icon: "fa-spinner",
+ point: "ct-point-sending"
+ },
+ "Retrying": {
+ color: "#6c7a89",
+ label: "label-default",
+ icon: "fa-clock-o",
+ point: "ct-point-error"
+ },
+ "Scheduled": {
+ color: "#428bca",
+ label: "label-primary",
+ icon: "fa-clock-o",
+ point: "ct-point-sending"
+ },
+ "Campaign Created": {
+ label: "label-success",
+ icon: "fa-rocket"
+ }
+}
+
+var statusMapping = {
+ "Email Sent": "sent",
+ "Email Opened": "opened",
+ "Clicked Link": "clicked",
+ "Submitted Data": "submitted_data",
+ "Email Reported": "reported",
+ //"Opened Word Document" : "opened_word_document"
+}
+
+// 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"
+]
+
+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.fire({
+ 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,
+ showLoaderOnConfirm: true,
+ preConfirm: function () {
+ return new Promise(function (resolve, reject) {
+ api.campaignId.delete(campaign.id)
+ .success(function (msg) {
+ resolve()
+ })
+ .error(function (data) {
+ reject(data.responseJSON.message)
+ })
+ })
+ }
+ }).then(function (result) {
+ if(result.value){
+ Swal.fire(
+ 'Campaign Deleted!',
+ 'This campaign has been deleted!',
+ 'success'
+ );
+ }
+ $('button:contains("OK")').on('click', function () {
+ location.href = '/campaigns'
+ })
+ })
+}
+
+// Completes a campaign after prompting the user
+function completeCampaign() {
+ Swal.fire({
+ 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,
+ showLoaderOnConfirm: true,
+ preConfirm: function () {
+ return new Promise(function (resolve, reject) {
+ api.campaignId.complete(campaign.id)
+ .success(function (msg) {
+ resolve()
+ })
+ .error(function (data) {
+ reject(data.responseJSON.message)
+ })
+ })
+ }
+ }).then(function (result) {
+ if (result.value){
+ Swal.fire(
+ '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
+ var filename = campaign.name + ' - ' + capitalize(scope) + '.csv'
+ switch (scope) {
+ case "results":
+ csvScope = campaign.results
+ break;
+ case "events":
+ csvScope = campaign.timeline
+ break;
+ }
+ if (!csvScope) {
+ return
+ }
+ $("#exportButton").html('
')
+ var csvString = Papa.unparse(csvScope, {
+ 'escapeFormulae': true
+ })
+ var csvData = new Blob([csvString], {
+ type: 'text/csv;charset=utf-8;'
+ });
+ if (navigator.msSaveBlob) {
+ navigator.msSaveBlob(csvData, filename);
+ } else {
+ var csvURL = window.URL.createObjectURL(csvData);
+ var dlLink = document.createElement('a');
+ dlLink.href = csvURL;
+ dlLink.setAttribute('download', filename)
+ 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 = $('