github.com/gophish/gophish@v0.12.2-0.20230915144530-8e7929441393/static/js/src/app/sending_profiles.js (about) 1 var profiles = [] 2 3 // Attempts to send a test email by POSTing to /campaigns/ 4 function sendTestEmail() { 5 var headers = []; 6 $.each($("#headersTable").DataTable().rows().data(), function (i, header) { 7 headers.push({ 8 key: unescapeHtml(header[0]), 9 value: unescapeHtml(header[1]), 10 }) 11 }) 12 var test_email_request = { 13 template: {}, 14 first_name: $("input[name=to_first_name]").val(), 15 last_name: $("input[name=to_last_name]").val(), 16 email: $("input[name=to_email]").val(), 17 position: $("input[name=to_position]").val(), 18 url: '', 19 smtp: { 20 from_address: $("#from").val(), 21 host: $("#host").val(), 22 username: $("#username").val(), 23 password: $("#password").val(), 24 ignore_cert_errors: $("#ignore_cert_errors").prop("checked"), 25 headers: headers, 26 } 27 } 28 btnHtml = $("#sendTestModalSubmit").html() 29 $("#sendTestModalSubmit").html('<i class="fa fa-spinner fa-spin"></i> Sending') 30 // Send the test email 31 api.send_test_email(test_email_request) 32 .success(function (data) { 33 $("#sendTestEmailModal\\.flashes").empty().append("<div style=\"text-align:center\" class=\"alert alert-success\">\ 34 <i class=\"fa fa-check-circle\"></i> Email Sent!</div>") 35 $("#sendTestModalSubmit").html(btnHtml) 36 }) 37 .error(function (data) { 38 $("#sendTestEmailModal\\.flashes").empty().append("<div style=\"text-align:center\" class=\"alert alert-danger\">\ 39 <i class=\"fa fa-exclamation-circle\"></i> " + escapeHtml(data.responseJSON.message) + "</div>") 40 $("#sendTestModalSubmit").html(btnHtml) 41 }) 42 } 43 44 // Save attempts to POST to /smtp/ 45 function save(idx) { 46 var profile = { 47 headers: [] 48 } 49 $.each($("#headersTable").DataTable().rows().data(), function (i, header) { 50 profile.headers.push({ 51 key: unescapeHtml(header[0]), 52 value: unescapeHtml(header[1]), 53 }) 54 }) 55 profile.name = $("#name").val() 56 profile.interface_type = $("#interface_type").val() 57 profile.from_address = $("#from").val() 58 profile.host = $("#host").val() 59 profile.username = $("#username").val() 60 profile.password = $("#password").val() 61 profile.ignore_cert_errors = $("#ignore_cert_errors").prop("checked") 62 if (idx != -1) { 63 profile.id = profiles[idx].id 64 api.SMTPId.put(profile) 65 .success(function (data) { 66 successFlash("Profile edited successfully!") 67 load() 68 dismiss() 69 }) 70 .error(function (data) { 71 modalError(data.responseJSON.message) 72 }) 73 } else { 74 // Submit the profile 75 api.SMTP.post(profile) 76 .success(function (data) { 77 successFlash("Profile added successfully!") 78 load() 79 dismiss() 80 }) 81 .error(function (data) { 82 modalError(data.responseJSON.message) 83 }) 84 } 85 } 86 87 function dismiss() { 88 $("#modal\\.flashes").empty() 89 $("#name").val("") 90 $("#interface_type").val("SMTP") 91 $("#from").val("") 92 $("#host").val("") 93 $("#username").val("") 94 $("#password").val("") 95 $("#ignore_cert_errors").prop("checked", true) 96 $("#headersTable").dataTable().DataTable().clear().draw() 97 $("#modal").modal('hide') 98 } 99 100 var dismissSendTestEmailModal = function () { 101 $("#sendTestEmailModal\\.flashes").empty() 102 $("#sendTestModalSubmit").html("<i class='fa fa-envelope'></i> Send") 103 } 104 105 106 var deleteProfile = function (idx) { 107 Swal.fire({ 108 title: "Are you sure?", 109 text: "This will delete the sending profile. This can't be undone!", 110 type: "warning", 111 animation: false, 112 showCancelButton: true, 113 confirmButtonText: "Delete " + escapeHtml(profiles[idx].name), 114 confirmButtonColor: "#428bca", 115 reverseButtons: true, 116 allowOutsideClick: false, 117 preConfirm: function () { 118 return new Promise(function (resolve, reject) { 119 api.SMTPId.delete(profiles[idx].id) 120 .success(function (msg) { 121 resolve() 122 }) 123 .error(function (data) { 124 reject(data.responseJSON.message) 125 }) 126 }) 127 } 128 }).then(function (result) { 129 if (result.value){ 130 Swal.fire( 131 'Sending Profile Deleted!', 132 'This sending profile has been deleted!', 133 'success' 134 ); 135 } 136 $('button:contains("OK")').on('click', function () { 137 location.reload() 138 }) 139 }) 140 } 141 142 function edit(idx) { 143 headers = $("#headersTable").dataTable({ 144 destroy: true, // Destroy any other instantiated table - http://datatables.net/manual/tech-notes/3#destroy 145 columnDefs: [{ 146 orderable: false, 147 targets: "no-sort" 148 }] 149 }) 150 151 $("#modalSubmit").unbind('click').click(function () { 152 save(idx) 153 }) 154 var profile = {} 155 if (idx != -1) { 156 $("#profileModalLabel").text("Edit Sending Profile") 157 profile = profiles[idx] 158 $("#name").val(profile.name) 159 $("#interface_type").val(profile.interface_type) 160 $("#from").val(profile.from_address) 161 $("#host").val(profile.host) 162 $("#username").val(profile.username) 163 $("#password").val(profile.password) 164 $("#ignore_cert_errors").prop("checked", profile.ignore_cert_errors) 165 $.each(profile.headers, function (i, record) { 166 addCustomHeader(record.key, record.value) 167 }); 168 } else { 169 $("#profileModalLabel").text("New Sending Profile") 170 } 171 } 172 173 function copy(idx) { 174 $("#modalSubmit").unbind('click').click(function () { 175 save(-1) 176 }) 177 var profile = {} 178 profile = profiles[idx] 179 $("#name").val("Copy of " + profile.name) 180 $("#interface_type").val(profile.interface_type) 181 $("#from").val(profile.from_address) 182 $("#host").val(profile.host) 183 $("#username").val(profile.username) 184 $("#password").val(profile.password) 185 $("#ignore_cert_errors").prop("checked", profile.ignore_cert_errors) 186 } 187 188 function load() { 189 $("#profileTable").hide() 190 $("#emptyMessage").hide() 191 $("#loading").show() 192 api.SMTP.get() 193 .success(function (ss) { 194 profiles = ss 195 $("#loading").hide() 196 if (profiles.length > 0) { 197 $("#profileTable").show() 198 profileTable = $("#profileTable").DataTable({ 199 destroy: true, 200 columnDefs: [{ 201 orderable: false, 202 targets: "no-sort" 203 }] 204 }); 205 profileTable.clear() 206 profileRows = [] 207 $.each(profiles, function (i, profile) { 208 profileRows.push([ 209 escapeHtml(profile.name), 210 profile.interface_type, 211 moment(profile.modified_date).format('MMMM Do YYYY, h:mm:ss a'), 212 "<div class='pull-right'><span data-toggle='modal' data-backdrop='static' data-target='#modal'><button class='btn btn-primary' data-toggle='tooltip' data-placement='left' title='Edit Profile' onclick='edit(" + i + ")'>\ 213 <i class='fa fa-pencil'></i>\ 214 </button></span>\ 215 <span data-toggle='modal' data-target='#modal'><button class='btn btn-primary' data-toggle='tooltip' data-placement='left' title='Copy Profile' onclick='copy(" + i + ")'>\ 216 <i class='fa fa-copy'></i>\ 217 </button></span>\ 218 <button class='btn btn-danger' data-toggle='tooltip' data-placement='left' title='Delete Profile' onclick='deleteProfile(" + i + ")'>\ 219 <i class='fa fa-trash-o'></i>\ 220 </button></div>" 221 ]) 222 }) 223 profileTable.rows.add(profileRows).draw() 224 $('[data-toggle="tooltip"]').tooltip() 225 } else { 226 $("#emptyMessage").show() 227 } 228 }) 229 .error(function () { 230 $("#loading").hide() 231 errorFlash("Error fetching profiles") 232 }) 233 } 234 235 function addCustomHeader(header, value) { 236 // Create new data row. 237 var newRow = [ 238 escapeHtml(header), 239 escapeHtml(value), 240 '<span style="cursor:pointer;"><i class="fa fa-trash-o"></i></span>' 241 ]; 242 243 // Check table to see if header already exists. 244 var headersTable = headers.DataTable(); 245 var existingRowIndex = headersTable 246 .column(0) // Email column has index of 2 247 .data() 248 .indexOf(escapeHtml(header)); 249 250 // Update or add new row as necessary. 251 if (existingRowIndex >= 0) { 252 headersTable 253 .row(existingRowIndex, { 254 order: "index" 255 }) 256 .data(newRow); 257 } else { 258 headersTable.row.add(newRow); 259 } 260 headersTable.draw(); 261 } 262 263 $(document).ready(function () { 264 // Setup multiple modals 265 // Code based on http://miles-by-motorcycle.com/static/bootstrap-modal/index.html 266 $('.modal').on('hidden.bs.modal', function (event) { 267 $(this).removeClass('fv-modal-stack'); 268 $('body').data('fv_open_modals', $('body').data('fv_open_modals') - 1); 269 }); 270 $('.modal').on('shown.bs.modal', function (event) { 271 // Keep track of the number of open modals 272 if (typeof ($('body').data('fv_open_modals')) == 'undefined') { 273 $('body').data('fv_open_modals', 0); 274 } 275 // if the z-index of this modal has been set, ignore. 276 if ($(this).hasClass('fv-modal-stack')) { 277 return; 278 } 279 $(this).addClass('fv-modal-stack'); 280 // Increment the number of open modals 281 $('body').data('fv_open_modals', $('body').data('fv_open_modals') + 1); 282 // Setup the appropriate z-index 283 $(this).css('z-index', 1040 + (10 * $('body').data('fv_open_modals'))); 284 $('.modal-backdrop').not('.fv-modal-stack').css('z-index', 1039 + (10 * $('body').data('fv_open_modals'))); 285 $('.modal-backdrop').not('fv-modal-stack').addClass('fv-modal-stack'); 286 }); 287 $.fn.modal.Constructor.prototype.enforceFocus = function () { 288 $(document) 289 .off('focusin.bs.modal') // guard against infinite focus loop 290 .on('focusin.bs.modal', $.proxy(function (e) { 291 if ( 292 this.$element[0] !== e.target && !this.$element.has(e.target).length 293 // CKEditor compatibility fix start. 294 && 295 !$(e.target).closest('.cke_dialog, .cke').length 296 // CKEditor compatibility fix end. 297 ) { 298 this.$element.trigger('focus'); 299 } 300 }, this)); 301 }; 302 // Scrollbar fix - https://stackoverflow.com/questions/19305821/multiple-modals-overlay 303 $(document).on('hidden.bs.modal', '.modal', function () { 304 $('.modal:visible').length && $(document.body).addClass('modal-open'); 305 }); 306 $('#modal').on('hidden.bs.modal', function (event) { 307 dismiss() 308 }); 309 $("#sendTestEmailModal").on("hidden.bs.modal", function (event) { 310 dismissSendTestEmailModal() 311 }) 312 // Code to deal with custom email headers 313 $("#addCustomHeader").on('click', function () { 314 headerKey = $("#headerKey").val(); 315 headerValue = $("#headerValue").val(); 316 317 if (headerKey == "" || headerValue == "") { 318 return false; 319 } 320 addCustomHeader(headerKey, headerValue); 321 // Reset user input. 322 $("#headerKey").val(''); 323 $("#headerValue").val(''); 324 $("#headerKey").focus(); 325 return false; 326 }); 327 // Handle Deletion 328 $("#headersTable").on("click", "span>i.fa-trash-o", function () { 329 headers.DataTable() 330 .row($(this).parents('tr')) 331 .remove() 332 .draw(); 333 }); 334 load() 335 })