3 var local_uri_prefix = "";
4 if (typeof(KISMET_URI_PREFIX) !== 'undefined')
5 local_uri_prefix = KISMET_URI_PREFIX;
13 href: local_uri_prefix + 'css/kismet.ui.datasources.css'
16 /* Convert a hop rate to human readable */
17 export const hop_to_human = (hop) => {
19 return hop + "/second";
28 return s + " seconds";
31 /* Sidebar: Channel coverage
33 * The channel coverage looks at the data sources and plots a moving graph
34 * of all channels and how they're covered; it reflects how the pattern will
35 * work, but not, necessarily, reality itself.
37 kismet_ui_sidebar.AddSidebarItem({
38 id: 'datasource_channel_coverage',
39 listTitle: '<i class="fa fa-bar-chart-o"></i> Channel Coverage',
40 clickCallback: function() {
45 var channelcoverage_backend_tid;
46 var channelcoverage_display_tid;
47 var channelcoverage_panel = null;
48 var channelcoverage_canvas = null;
49 var channelhop_canvas = null;
50 var channelcoverage_chart = null;
51 var channelhop_chart = null;
52 var cc_uuid_pos_map = {};
54 export const ChannelCoverage = () => {
55 var w = $(window).width() * 0.85;
56 var h = $(window).height() * 0.75;
59 if ($(window).width() < 450 || $(window).height() < 450) {
60 w = $(window).width() - 5;
61 h = $(window).height() - 5;
65 channelcoverage_chart = null;
66 channelhop_chart = null;
81 href: '#k-cc-tab-coverage'
83 .html("Channel Coverage")
90 href: '#k-cc-tab-estimate'
92 .html("Estimated Hopping")
98 id: 'k-cc-tab-coverage',
103 id: 'k-cc-cover-canvas',
110 id: 'k-cc-tab-estimate',
121 channelcoverage_panel = $.jsPanel({
122 id: 'channelcoverage',
123 headerTitle: '<i class="fa fa-bar-chart-o" /> Channel Coverage',
130 onclosed: function() {
131 clearTimeout(channelcoverage_backend_tid);
132 clearTimeout(channelcoverage_display_tid);
133 channelhop_chart = null;
134 channelhop_canvas = null;
135 channelcoverage_canvas = null;
136 channelcoverage_chart = null;
138 onresized: resize_channelcoverage,
139 onmaximized: resize_channelcoverage,
140 onnormalized: resize_channelcoverage,
142 .on('resize', function() {
143 resize_channelcoverage();
159 channelcoverage_backend_refresh();
160 channelcoverage_display_refresh();
163 function channelcoverage_backend_refresh() {
164 clearTimeout(channelcoverage_backend_tid);
166 if (channelcoverage_panel == null)
169 if (channelcoverage_panel.is(':hidden'))
172 $.get(local_uri_prefix + "datasource/all_sources.json")
173 .done(function(data) {
174 // Build a list of all devices we haven't seen before and set their
175 // initial positions to match
176 for (var di = 0; di < data.length; di++) {
177 if (data[di]['kismet.datasource.running'] == 0) {
178 if ((data[di]['kismet.datasource.uuid'] in cc_uuid_pos_map)) {
179 delete cc_uuid_pos_map[data[di]['kismet.datasource.uuid']];
181 } else if (!(data[di]['kismet.datasource.uuid'] in cc_uuid_pos_map)) {
182 cc_uuid_pos_map[data[di]['kismet.datasource.uuid']] = {
183 uuid: data[di]['kismet.datasource.uuid'],
184 name: data[di]['kismet.datasource.name'],
185 interface: data[di]['kismet.datasource.interface'],
186 hopping: data[di]['kismet.datasource.hopping'],
187 channel: data[di]['kismet.datasource.channel'],
188 channels: data[di]['kismet.datasource.hop_channels'],
189 offset: data[di]['kismet.datasource.hop_offset'],
190 position: data[di]['kismet.datasource.hop_offset'],
191 skip: data[di]['kismet.datasource.hop_shuffle_skip'],
198 channelcoverage_backend_tid = setTimeout(channelcoverage_backend_refresh, 5000);
202 function resize_channelcoverage() {
203 if (channelcoverage_panel == null)
206 var container = $('#k-cc-main', channelcoverage_panel.content);
208 var tabs = $('#k-cc-tab-ul', container);
210 var w = container.width();
211 var h = container.height() - tabs.outerHeight();
213 $('#k-cc-tab-estimate', container)
217 if (channelhop_canvas != null) {
222 if (channelhop_chart != null)
223 channelhop_chart.resize();
226 $('#k-cc-tab-coverage', container)
230 if (channelcoverage_canvas != null) {
231 channelcoverage_canvas
235 if (channelcoverage_chart != null)
236 channelcoverage_chart.resize();
241 function channelcoverage_display_refresh() {
242 clearTimeout(channelcoverage_display_tid);
244 if (channelcoverage_panel == null)
247 if (channelcoverage_panel.is(':hidden'))
250 // Now we know all the sources; make a list of all channels and figure
251 // out if we're on any of them; each entry in total_channel_list contains
252 // an array of UUIDs on this channel in this sequence
253 var total_channel_list = {}
255 for (var du in cc_uuid_pos_map) {
256 var d = cc_uuid_pos_map[du];
259 for (var ci = 0; ci < d['channels'].length; ci++) {
260 var chan = d['channels'][ci];
261 if (!(chan in total_channel_list)) {
262 total_channel_list[chan] = [ ];
265 if ((d['position'] % d['channels'].length) == ci) {
266 total_channel_list[chan].push(du);
270 // Increment the virtual channel position for the graph
271 if (d['skip'] == 0) {
272 d['position'] = d['position'] + 1;
274 d['position'] = d['position'] + d['skip'];
277 // Non-hopping sources are always on their channel
278 var chan = d['channel'];
280 if (!(chan in total_channel_list)) {
281 total_channel_list[chan] = [ du ];
283 total_channel_list[chan].push(du);
288 // Create the channel index for the x-axis, used in both the hopping and the coverage
290 var chantitles = new Array();
291 for (var ci in total_channel_list) {
295 // Perform a natural sort on it to get it in order
296 var ncollator = new Intl.Collator(undefined, {numeric: true, sensitivity: 'base'});
297 chantitles.sort(ncollator.compare);
299 // Create the source datasets for the animated estimated hopping graph, covering all
300 // channels and highlighting the channels we have a UUID in
301 var source_datasets = new Array()
305 for (var du in cc_uuid_pos_map) {
306 var d = cc_uuid_pos_map[du];
310 for (var ci in chantitles) {
311 var clist = total_channel_list[chantitles[ci]];
313 if (clist.indexOf(du) < 0) {
320 var color = "hsl(" + parseInt(255 * (ndev / Object.keys(cc_uuid_pos_map).length)) + ", 100%, 50%)";
322 source_datasets.push({
326 backgroundColor: color,
332 // Create the source list for the Y axis of the coverage graph; we make an intermediary
333 // which is sorted by name but includes UUID, then assemble the final one
334 var sourcetitles_tmp = new Array();
335 var sourcetitles = new Array();
337 for (var ci in cc_uuid_pos_map) {
338 sourcetitles_tmp.push({
339 name: cc_uuid_pos_map[ci].name,
344 sourcetitles_tmp.sort(function(a, b) {
345 return a.name.localeCompare(b.name);
349 for (var si in sourcetitles_tmp) {
350 sourcetitles.push(sourcetitles_tmp[si].name);
353 var bubble_dataset = new Array();
355 // Build the bubble data
357 for (var si in sourcetitles_tmp) {
358 var d = cc_uuid_pos_map[sourcetitles_tmp[si].uuid];
362 for (var ci in d.channels) {
363 var c = d.channels[ci];
365 var cp = chantitles.indexOf(c);
377 var cp = chantitles.indexOf(d.channel);
387 var color = "hsl(" + parseInt(255 * (ndev / Object.keys(cc_uuid_pos_map).length)) + ", 100%, 50%)";
389 bubble_dataset.push({
393 backgroundColor: color,
400 if (channelhop_canvas == null) {
401 channelhop_canvas = $('#k-cc-canvas', channelcoverage_panel.content);
405 if (chantitles.length < 14)
408 channelhop_chart = new Chart(channelhop_canvas, {
412 maintainAspectRatio: false,
414 xAxes: [{ barPercentage: bp, }],
419 datasets: source_datasets,
423 channelhop_chart.data.datasets = source_datasets;
424 channelhop_chart.data.labels = chantitles;
425 channelhop_chart.update(0);
428 if (channelcoverage_canvas == null && sourcetitles.length != 0) {
429 channelcoverage_canvas = $('#k-cc-cover-canvas', channelcoverage_panel.content);
431 channelcoverage_chart = new Chart(channelcoverage_canvas, {
436 text: 'Per-Source Channel Coverage',
442 maintainAspectRatio: false,
448 callback: function(value, index, values) {
449 return chantitles[value];
452 max: chantitles.length,
461 callback: function(value, index, values) {
462 return sourcetitles[value];
465 max: sourcetitles.length,
474 yLabels: sourcetitles,
475 datasets: bubble_dataset,
478 } else if (sourcetitles.length != 0) {
479 channelcoverage_chart.data.datasets = bubble_dataset;
481 channelcoverage_chart.data.labels = chantitles;
482 channelcoverage_chart.data.yLabels = sourcetitles;
484 channelcoverage_chart.options.scales.xAxes[0].ticks.min = 0;
485 channelcoverage_chart.options.scales.xAxes[0].ticks.max = chantitles.length;
487 channelcoverage_chart.options.scales.yAxes[0].ticks.min = 0;
488 channelcoverage_chart.options.scales.yAxes[0].ticks.max = sourcetitles.length;
490 channelcoverage_chart.update(0);
493 channelcoverage_display_tid = setTimeout(channelcoverage_display_refresh, 500);
496 /* Sidebar: Data sources (new)
498 * Data source management panel
500 kismet_ui_sidebar.AddSidebarItem({
501 id: 'datasource_sources2',
502 listTitle: '<i class="fa fa-cogs"></i> Data Sources',
504 clickCallback: function() {
511 function update_datasource2(data) {
512 if (!"ds_content" in ds_state)
515 var set_row = function(sdiv, id, title, content) {
516 var r = $('tr#' + id, sdiv);
519 r = $('<tr>', { id: id })
523 $('.k-ds-table', sdiv).append(r);
526 $('td:eq(0)', r).replaceWith($('<td>').append(title));
527 $('td:eq(1)', r).replaceWith($('<td>').append(content));
530 var top_row = function(sdiv, id, title, content) {
531 var r = $('tr#' + id, sdiv);
534 r = $('<tr>', { id: id })
538 $('.k-ds-table', sdiv).prepend(r);
541 $('td:eq(0)', r).replaceWith($('<td>').append(title));
542 $('td:eq(1)', r).replaceWith($('<td>').append(content));
545 for (var uuid of ds_state['remove_pending']) {
546 var sdiv = $('#' + uuid, ds_state['ds_content']);
547 $('.k-ds-modal', sdiv).hide();
549 ds_state['remove_pending'] = [];
552 // Defer if we're waiting for a command to finish; do NOTHING else
553 if ('defer_command_progress' in ds_state && ds_state['defer_command_progress'])
557 // Mark that we're loading interfaces
558 if (ds_state['done_interface_update']) {
559 $('#ds_loading_interfaces', ds_state['ds_content']).remove();
561 var loading_intf = $('#ds_loading_interfaces', ds_state['ds_content']);
563 if (loading_intf.length == 0) {
564 loading_intf = $('<div>', {
565 id: 'ds_loading_interfaces',
574 class: 'k-ds-source',
576 .html("<i class=\"fa fa-spin fa-cog\"></i> Finding available interfaces...")
579 $('<div>').html("Kismet is probing for available capture interfaces...")
582 loading_intf.accordion({ collapsible: true, active: false });
584 ds_state['ds_content'].append(loading_intf);
589 // Clean up missing probed interfaces
590 $('.interface', ds_state['ds_content']).each(function(i) {
593 for (var intf of ds_state['kismet_interfaces']) {
594 if ($(this).attr('id') === intf['kismet.datasource.probed.interface']) {
595 if (intf['kismet.datasource.probed.in_use_uuid'] !== '00000000-0000-0000-0000-000000000000') {
604 // console.log("removing interface", $(this).attr('id'));
609 // Clean up missing sources
610 $('.source', ds_state['ds_content']).each(function(i) {
613 for (var source of ds_state['kismet_sources']) {
614 if ($(this).attr('id') === source['kismet.datasource.uuid']) {
621 // console.log("removing source", $(this).attr('id'));
626 for (var intf of ds_state['kismet_interfaces']) {
627 if (intf['kismet.datasource.probed.in_use_uuid'] !== '00000000-0000-0000-0000-000000000000') {
628 $('#' + intf['kismet.datasource.probed.interface'], ds_state['ds_content']).remove();
632 var idiv = $('#' + intf['kismet.datasource.probed.interface'], ds_state['ds_content']);
634 if (idiv.length == 0) {
636 id: intf['kismet.datasource.probed.interface'],
637 class: 'accordion interface',
645 class: 'k-ds-source',
647 .html("Available Interface: " + intf['kismet.datasource.probed.interface'] + ' (' + intf['kismet.datasource.type_driver']['kismet.datasource.driver.type'] + ')')
652 class: 'k-ds_content',
656 var table = $('<table>', {
660 var wrapper = $('<div>', {
661 "style": "position: relative;",
664 var modal = $('<div>', {
668 class: 'k-ds-modal-content',
672 class: "k-ds-modal-message",
673 style: "font-size: 125%; margin-bottom: 5px;",
674 }).html("Loading...")
677 class: "fa fa-3x fa-cog fa-spin",
682 wrapper.append(table);
683 wrapper.append(modal);
686 $('.k-ds_content', idiv).append(wrapper);
688 idiv.accordion({ collapsible: true, active: false });
690 ds_state['ds_content'].append(idiv);
693 set_row(idiv, 'interface', '<b>Interface</b>', intf['kismet.datasource.probed.interface']);
694 set_row(idiv, 'driver', '<b>Capture Driver</b>', intf['kismet.datasource.type_driver']['kismet.datasource.driver.type']);
695 if (intf['kismet.datasource.probed.hardware'] !== '')
696 set_row(idiv, 'hardware', '<b>Hardware</b>', intf['kismet.datasource.probed.hardware']);
697 set_row(idiv, 'description', '<b>Type</b>', intf['kismet.datasource.type_driver']['kismet.datasource.driver.description']);
699 var addbutton = $('#add', idiv);
700 if (addbutton.length == 0) {
704 interface: intf['kismet.datasource.probed.interface'],
705 intftype: intf['kismet.datasource.type_driver']['kismet.datasource.driver.type'],
707 .html('Enable Source')
709 .on('click', function() {
710 var intf = $(this).attr('interface');
711 var idiv = $('#' + intf, ds_state['ds_content']);
713 $('.k-ds-modal-message', idiv).html("Opening datasource...");
714 $('.k-ds-modal', idiv).show();
717 "definition": $(this).attr('interface') + ':type=' + $(this).attr('intftype')
720 ds_state['defer_command_progress'] = true;
722 var postdata = "json=" + encodeURIComponent(JSON.stringify(jscmd));
723 $.post(local_uri_prefix + "datasource/add_source.cmd", postdata, "json")
725 ds_state['defer_command_progress'] = false;
732 set_row(idiv, 'addsource', $('<span>'), addbutton);
734 idiv.accordion("refresh");
736 // console.log("updating with ", ds_state['kismet_sources'].length);
738 for (var source of ds_state['kismet_sources']) {
739 var sdiv = $('#' + source['kismet.datasource.uuid'], ds_state['ds_content']);
741 if (sdiv.length == 0) {
743 id: source['kismet.datasource.uuid'],
744 class: 'accordion source',
762 class: 'k-ds-source',
764 .html(source['kismet.datasource.name'])
775 class: 'k-ds_content',
779 var wrapper = $('<div>', {
780 "style": "position: relative;",
783 var table = $('<table>', {
787 var modal = $('<div>', {
791 class: 'k-ds-modal-content',
795 class: "k-ds-modal-message",
796 style: "font-size: 150%; margin-bottom: 10px;",
797 }).html("Loading...")
800 class: "fa fa-3x fa-cog fa-spin",
805 wrapper.append(table);
806 wrapper.append(modal);
809 $('.k-ds_content', sdiv).append(wrapper);
811 sdiv.accordion({ collapsible: true, active: false });
813 ds_state['ds_content'].append(sdiv);
816 if (typeof(source['kismet.datasource.packets_rrd']) !== 'undefined' &&
817 source['kismet.datasource.packets_rrd'] != 0) {
820 kismet.RecalcRrdData(
821 source['kismet.datasource.packets_rrd'],
822 source['kismet.datasource.packets_rrd']['kismet.common.rrd.last_time'],
824 source['kismet.datasource.packets_rrd']['kismet.common.rrd.minute_vec'], {
825 transform: function(data, opt) {
828 var ret = new Array();
830 for (var ri = 0; ri < data.length; ri++) {
831 peak = Math.max(peak, data[ri]);
833 if ((ri % slices) == (slices - 1)) {
843 // Render the sparkline
844 $('#rrd', sdiv).sparkline(simple_rrd, {
848 barColor: kismet_theme.sparkline_main,
849 nullColor: kismet_theme.sparkline_main,
850 zeroColor: kismet_theme.sparkline_main,
854 // Find the channel buttons
855 var chanbuttons = $('#chanbuttons', sdiv);
857 if (chanbuttons.length == 0) {
858 // Make a new one of all possible channels
859 chanbuttons = $('<div>', {
861 uuid: source['kismet.datasource.uuid']
867 uuid: source['kismet.datasource.uuid']
870 .on('click', function(){
871 ds_state['defer_command_progress'] = true;
873 var uuid = $(this).attr('uuid');
875 $('button.chanbutton[uuid=' + uuid + ']', ds_state['ds_content']).each(function(i) {
876 chans.push($(this).attr('channel'));
883 var postdata = "json=" + encodeURIComponent(JSON.stringify(jscmd));
887 url: `${local_uri_prefix}datasource/by-uuid/${uuid}/set_channel.cmd`,
891 success: function(data) { },
895 ds_state['defer_command_progress'] = false;
897 $('button.chanbutton[uuid=' + uuid + ']', ds_state['ds_content']).each(function(i){
898 $(this).removeClass('disable-chan-system');
899 $(this).removeClass('enable-chan-system');
900 $(this).removeClass('disable-chan-user');
901 $(this).addClass('enable-chan-user');
906 for (var c of source['kismet.datasource.channels']) {
911 uuid: source['kismet.datasource.uuid'],
915 .on('click', function() {
916 var uuid = $(this).attr('uuid');
918 var sdiv = $('#' + uuid, ds_state['ds_content']);
919 sdiv.addClass("channel_pending");
921 // If we're in channel lock mode, we highlight a single channel
922 if ($('#lock[uuid=' + uuid + ']', ds_state['ds_content']).hasClass('enable-chan-user')) {
923 // Only do something if we're not selected
924 if (!($(this).hasClass('enable-chan-user'))) {
925 // Remove from all lock channels
926 $('button.chanbutton[uuid=' + uuid + ']').each(function(i) {
927 $(this).removeClass('enable-chan-user');
929 $('button.chanbutton[uuid=' + uuid + ']').removeClass('enable-chan-system');
931 $(this).addClass('enable-chan-user');
937 ds_state['defer_source_update'] = true;
938 ds_state['defer_command_progress'] = true;
940 // Clear any existing timer
941 if (uuid in ds_state['chantids'])
942 clearTimeout(ds_state['chantids'][uuid]);
944 // Immediately post w/out a timeout
948 "channel": $(this).attr('channel'),
951 $('.k-ds-modal-message', sdiv).html("Setting channel...");
952 $('.k-ds-modal', sdiv).show();
954 var postdata = "json=" + encodeURIComponent(JSON.stringify(jscmd));
958 url: `${local_uri_prefix}datasource/by-uuid/${uuid}/set_channel.cmd`,
962 success: function(data) {
963 data = kismet.sanitizeObject(data);
964 for (var u in ds_state['datasources']) {
965 if (ds_state['datasources'][u]['kismet.datasource.uuid'] == data['kismet.datasource.uuid']) {
966 ds_state['datasources'][u] = data;
967 ds_state['remove_pending'].push(uuid);
968 update_datasource2(null);
976 ds_state['remove_pending'].push(uuid);
977 ds_state['defer_command_progress'] = false;
978 sdiv.removeClass("channel_pending");
984 if ($(this).hasClass('enable-chan-user') || $(this).hasClass('enable-chan-system')) {
985 $(this).removeClass('enable-chan-user');
986 $(this).removeClass('enable-chan-system');
988 $(this).addClass('disable-chan-user');
990 $(this).removeClass('disable-chan-user');
991 $(this).addClass('enable-chan-user');
994 // Clear any old timer
995 if (uuid in ds_state['chantids'])
996 clearTimeout(ds_state['chantids'][uuid]);
998 // Set a timer to trigger in the future setting any channels
999 ds_state['chantids'][uuid] = setTimeout(function() {
1000 ds_state['defer_command_progress'] = true;
1001 ds_state['defer_source_update'] = true;
1003 var sdiv = $('#' + uuid, ds_state['ds_content']);
1004 sdiv.addClass("channel_pending");
1006 $('.k-ds-modal-message', sdiv).html("Setting channels...");
1007 $('.k-ds-modal', sdiv).show();
1011 $('button.chanbutton[uuid=' + uuid + ']', ds_state['ds_content']).each(function(i) {
1012 // If we're hopping, collect user and system
1013 if ($(this).hasClass('enable-chan-user') ||
1014 $(this).hasClass('enable-chan-system')) {
1015 ds_state['refresh' + uuid] = true;
1016 chans.push($(this).attr('channel'));
1026 var postdata = "json=" + encodeURIComponent(JSON.stringify(jscmd));
1029 url: `${local_uri_prefix}datasource/by-uuid/${uuid}/set_channel.cmd`,
1033 success: function(data) {
1034 data = kismet.sanitizeObject(data);
1035 for (var u in ds_state['datasources']) {
1036 if (ds_state['datasources'][u]['kismet.datasource.uuid'] == data['kismet.datasource.uuid']) {
1037 ds_state['datasources'][u] = data;
1038 ds_state['remove_pending'].push(uuid);
1039 update_datasource2(null);
1047 ds_state['remove_pending'].push(uuid);
1048 ds_state['defer_command_progress'] = false;
1049 sdiv.removeClass("channel_pending");
1058 var pausediv = $('#pausediv', sdiv);
1059 if (pausediv.length == 0) {
1060 pausediv = $('<div>', {
1062 uuid: source['kismet.datasource.uuid']
1068 uuid: source['kismet.datasource.uuid']
1071 .on('click', function() {
1072 ds_state['defer_command_progress'] = true;
1073 ds_state['defer_source_update'] = true;
1075 var uuid = $(this).attr('uuid');
1076 var sdiv = $('#' + uuid, ds_state['ds_content']);
1078 $('.k-ds-modal-message', sdiv).html("Activating datasource...");
1079 $('.k-ds-modal', sdiv).show();
1081 $('#closecmd[uuid=' + uuid + ']', ds_state['ds_content']).removeClass('enable-chan-user');
1082 $(this).addClass('enable-chan-user');
1084 $.get(local_uri_prefix + '/datasource/by-uuid/' + uuid + '/open_source.cmd')
1085 .done(function(data) {
1086 data = kismet.sanitizeObject(data);
1088 for (var u in ds_state['datasources']) {
1089 if (ds_state['datasources'][u]['kismet.datasource.uuid'] == data['kismet.datasource.uuid']) {
1090 ds_state['datasources'][u] = data;
1091 update_datasource2(null);
1096 .always(function() {
1097 ds_state['defer_command_progress'] = false;
1098 ds_state['remove_pending'].push(uuid);
1107 uuid: source['kismet.datasource.uuid']
1110 .on('click', function() {
1111 ds_state['defer_command_progress'] = true;
1112 ds_state['defer_source_update'] = true;
1114 var uuid = $(this).attr('uuid');
1115 var sdiv = $('#' + uuid, ds_state['ds_content']);
1117 $('.k-ds-modal-message', sdiv).html("Closing datasource...");
1118 $('.k-ds-modal', sdiv).show();
1120 $(this).addClass('enable-chan-user');
1121 $('#opencmd[uuid=' + uuid + ']', ds_state['ds_content']).removeClass('enable-chan-user');
1123 $.get(local_uri_prefix + '/datasource/by-uuid/' + uuid + '/close_source.cmd')
1124 .done(function(data) {
1125 data = kismet.sanitizeObject(data);
1127 for (var u in ds_state['datasources']) {
1128 if (ds_state['datasources'][u]['kismet.datasource.uuid'] == data['kismet.datasource.uuid']) {
1129 ds_state['remove_pending'].push(uuid);
1130 ds_state['datasources'][u] = data;
1131 update_datasource2(null);
1136 .always(function() {
1137 ds_state['remove_pending'].push(uuid);
1138 ds_state['defer_command_progress'] = false;
1147 uuid: source['kismet.datasource.uuid']
1150 .on('click', function() {
1151 ds_state['defer_command_progress'] = true;
1152 ds_state['defer_source_update'] = true;
1154 var uuid = $(this).attr('uuid');
1155 var sdiv = $('#' + uuid, ds_state['ds_content']);
1157 $('.k-ds-modal-message', sdiv).html("Disabling datasource...");
1158 $('.k-ds-modal', sdiv).show();
1160 $(this).addClass('enable-chan-user');
1161 $('#opencmd[uuid=' + uuid + ']', ds_state['ds_content']).removeClass('enable-chan-user');
1163 $.get(local_uri_prefix + '/datasource/by-uuid/' + uuid + '/disable_source.cmd')
1164 .done(function(data) {
1165 data = kismet.sanitizeObject(data);
1167 for (var u in ds_state['datasources']) {
1168 if (ds_state['datasources'][u]['kismet.datasource.uuid'] == data['kismet.datasource.uuid']) {
1169 ds_state['remove_pending'].push(uuid);
1170 ds_state['datasources'][u] = data;
1171 update_datasource2(null);
1176 .always(function() {
1177 ds_state['remove_pending'].push(uuid);
1178 ds_state['defer_command_progress'] = false;
1187 uuid: source['kismet.datasource.uuid']
1189 .html('Source is currently closed and inactive.')
1193 if (source['kismet.datasource.running']) {
1194 $('button#closecmd', sdiv).html("Close");
1195 $('button#opencmd', sdiv).html("Running");
1197 $('button#closecmd', sdiv).html("Closed");
1198 $('button#opencmd', sdiv).html("Activate");
1201 var quickopts = $('#quickopts', sdiv);
1202 if (quickopts.length == 0) {
1203 quickopts = $('<div>', {
1205 uuid: source['kismet.datasource.uuid']
1211 uuid: source['kismet.datasource.uuid']
1214 .on('click', function(){
1215 ds_state['defer_source_update'] = true;
1216 ds_state['defer_command_progress'] = true;
1218 var uuid = $(this).attr('uuid');
1219 var sdiv = $('#' + uuid, ds_state['ds_content']);
1221 $('.k-ds-modal-message', sdiv).html("Locking channels...");
1222 $('.k-ds-modal', sdiv).show();
1224 $('#hop[uuid=' + uuid + ']', ds_state['ds_content']).removeClass('enable-chan-user');
1225 $('#lock[uuid=' + uuid + ']', ds_state['ds_content']).addClass('enable-chan-user');
1227 var firstchanobj = $('button.chanbutton[uuid=' + uuid + ']', ds_state['ds_content']).first();
1229 var chan = firstchanobj.attr('channel');
1236 var postdata = "json=" + encodeURIComponent(JSON.stringify(jscmd));
1240 url: `${local_uri_prefix}datasource/by-uuid/${uuid}/set_channel.cmd`,
1244 success: function(data) {
1245 data = kismet.sanitizeObject(data);
1246 for (var u in ds_state['datasources']) {
1247 if (ds_state['datasources'][u]['kismet.datasource.uuid'] == data['kismet.datasource.uuid']) {
1248 ds_state['datasources'][u] = data;
1249 ds_state['remove_pending'].push(uuid);
1250 update_datasource2(null);
1258 ds_state['remove_pending'].push(uuid);
1261 $('button.chanbutton[uuid='+ uuid + ']', ds_state['ds_content']).each(function(i) {
1262 $(this).removeClass('enable-chan-system');
1263 $(this).removeClass('disable-chan-user');
1266 // Disable all but the first available channel
1267 firstchanobj.removeClass('disabled-chan-user');
1268 firstchanobj.removeClass('enable-chan-system');
1269 firstchanobj.addClass('enable-chan-user')
1277 uuid: source['kismet.datasource.uuid']
1280 .on('click', function(){
1281 ds_state['defer_source_update'] = true;
1282 ds_state['defer_command_progress'] = true;
1284 var uuid = $(this).attr('uuid');
1285 var sdiv = $('#' + uuid, ds_state['ds_content']);
1287 $('.k-ds-modal-message', sdiv).html("Setting channel hopping...");
1288 $('.k-ds-modal', sdiv).show();
1290 $('#hop[uuid=' + uuid + ']', ds_state['ds_content']).addClass('enable-chan-user');
1291 $('#lock[uuid=' + uuid + ']', ds_state['ds_content']).removeClass('enable-chan-user');
1294 $('button.chanbutton[uuid=' + uuid + ']', ds_state['ds_content']).each(function(i) {
1295 chans.push($(this).attr('channel'));
1304 var postdata = "json=" + encodeURIComponent(JSON.stringify(jscmd));
1308 url: `${local_uri_prefix}datasource/by-uuid/${uuid}/set_channel.cmd`,
1312 success: function(data) {
1313 data = kismet.sanitizeObject(data);
1314 for (var u in ds_state['datasources']) {
1315 if (ds_state['datasources'][u]['kismet.datasource.uuid'] == data['kismet.datasource.uuid']) {
1316 ds_state['datasources'][u] = data;
1317 ds_state['remove_pending'].push(uuid);
1318 update_datasource2(null);
1326 ds_state['remove_pending'].push(uuid);
1329 $('button.chanbutton[uuid='+ uuid + ']', ds_state['ds_content']).each(function(i) {
1330 // Disable all but the first available channel
1331 if ($(this).attr('channel') == 1) {
1332 $(this).removeClass('disabled-chan-user');
1333 $(this).removeClass('enable-chan-system');
1334 $(this).addClass('enable-chan-user')
1336 $(this).removeClass('enable-chan-system');
1337 $(this).removeClass('disable-chan-user');
1350 var uuid = source['kismet.datasource.uuid'];
1351 var hop_chans = source['kismet.datasource.hop_channels'];
1352 var lock_chan = source['kismet.datasource.channel'];
1353 var hopping = source['kismet.datasource.hopping'];
1355 if (!sdiv.hasClass('channel_pending')) {
1356 if (source['kismet.datasource.hopping']) {
1357 $('#hop', quickopts).addClass('enable-chan-user');
1358 $('#lock', quickopts).removeClass('enable-chan-user');
1359 $('#hoprate', quickopts).html(" (Hopping at " +
1360 hop_to_human(source['kismet.datasource.hop_rate']) + ")");
1361 $('#hoprate', quickopts).show();
1363 $('#hop', quickopts).removeClass('enable-chan-user');
1364 $('#lock', quickopts).addClass('enable-chan-user');
1365 $('#hoprate', quickopts).hide();
1368 $('button.chanbutton', chanbuttons).each(function(i) {
1369 var chan = $(this).attr('channel');
1371 // If locked, only highlight locked channel
1373 if (chan === lock_chan) {
1374 $(this).addClass('enable-chan-user');
1375 $(this).removeClass('enable-chan-system');
1377 $(this).removeClass('enable-chan-user');
1378 $(this).removeClass('enable-chan-system');
1384 // Flag the channel if it's found, and not explicitly disabled
1385 if (hop_chans.indexOf(chan) != -1 && !($(this).hasClass('disable-chan-user'))) {
1387 .addClass('enable-chan-system');
1390 .removeClass('enable-chan-system');
1394 if (source['kismet.datasource.running']) {
1395 $('#closecmd', pausediv).removeClass('enable-chan-user');
1396 $('#opencmd', pausediv).addClass('enable-chan-user');
1397 $('#pausetext', pausediv).hide();
1399 $('#closecmd', pausediv).addClass('enable-chan-user');
1400 $('#opencmd', pausediv).removeClass('enable-chan-user');
1401 $('#pausetext', pausediv).show();
1406 var s = source['kismet.datasource.interface'];
1408 if (source['kismet.datasource.interface'] !==
1409 source['kismet.datasource.capture_interface']) {
1410 s = s + "(" + source['kismet.datasource.capture_interface'] + ")";
1413 if (source['kismet.datasource.error']) {
1414 $('#error', sdiv).html('<i class="k-ds-error fa fa-exclamation-circle"></i>');
1415 top_row(sdiv, 'error', '<i class="k-ds-error fa fa-exclamation-circle"></i><b>Error</b>',
1416 source['kismet.datasource.error_reason']);
1418 $('#error', sdiv).empty();
1419 $('tr#error', sdiv).remove();
1422 if (!source['kismet.datasource.running']) {
1423 $('#paused', sdiv).html('<i class="k-ds-paused fa fa-pause-circle"></i>');
1425 $('#paused', sdiv).empty();
1428 set_row(sdiv, 'interface', '<b>Interface</b>', s);
1429 if (source['kismet.datasource.hardware'] !== '')
1430 set_row(sdiv, 'hardware', '<b>Hardware</b>', source['kismet.datasource.hardware']);
1431 set_row(sdiv, 'uuid', '<b>UUID</b>', source['kismet.datasource.uuid']);
1432 set_row(sdiv, 'packets', '<b>Packets</b>', source['kismet.datasource.num_packets']);
1435 if (source['kismet.datasource.remote']) {
1436 rts = 'Remote sources are not re-opened by Kismet, but will be re-opened when the ' +
1437 'remote source reconnects.';
1438 } else if (source['kismet.datasource.passive']) {
1439 rts = 'Passive sources are not directly managed by Kismet, they accept data ' +
1440 'from external services.';
1441 } else if (source['kismet.datasource.retry']) {
1442 rts = 'Kismet will try to re-open this source if an error occurs';
1443 if (source['kismet.datasource.retry_attempts'] &&
1444 source['kismet.datasource.running'] == 0) {
1445 rts = rts + ' (Tried ' + source['kismet.datasource.retry_attempts'] + ' times)';
1448 rts = 'Kismet will not re-open this source';
1451 set_row(sdiv, 'retry', '<b>Retry on Error</b>', rts);
1452 set_row(sdiv, 'pausing', '<b>Active</b>', pausediv);
1454 if (source['kismet.datasource.running']) {
1455 if (source['kismet.datasource.type_driver']['kismet.datasource.driver.tuning_capable']) {
1456 set_row(sdiv, 'chanopts', '<b>Channel Options</b>', quickopts);
1457 set_row(sdiv, 'channels', '<b>Channels</b>', chanbuttons);
1459 $('tr#chanopts', sdiv).remove();
1460 $('tr#channels', sdiv).remove();
1463 $('tr#chanopts', sdiv).remove();
1464 $('tr#channels', sdiv).remove();
1468 sdiv.accordion("refresh");
1475 export const DataSources2 = () => {
1476 var w = $(window).width() * 0.95;
1477 var h = $(window).height() * 0.75;
1480 if ($(window).width() < 450 || $(window).height() < 450) {
1481 w = $(window).width() - 5;
1482 h = $(window).height() - 5;
1487 ds_state['remove_pending'] = []
1488 ds_state['chantids'] = {}
1491 $('<div class="k-ds-contentdiv">');
1493 ds_state['closed'] = 0;
1495 ds_state['panel'] = $.jsPanel({
1497 headerTitle: '<i class="fa fa-cogs" /> Data Sources',
1499 iconfont: 'jsglyph',
1506 stop: function(event, ui) {
1507 $('div.accordion', ui.element).accordion("refresh");
1511 onmaximized: function() {
1512 $('div.accordion', this.content).accordion("refresh");
1515 onnormalized: function() {
1516 $('div.accordion', this.content).accordion("refresh");
1519 onclosed: function() {
1520 ds_state['closed'] = 1;
1522 if ('datasource_get_tid' in ds_state)
1523 clearTimeout(ds_state['datasource_get_tid']);
1524 if ('datasource_interface_tid' in ds_state)
1525 clearTimeout(ds_state['datasource_interface_tid']);
1540 ds_state["content"] = content;
1541 ds_state["ds_content"] = content;
1542 ds_state["kismet_sources"] = [];
1543 ds_state["kismet_interfaces"] = [];
1545 datasource_source_refresh(function(data) {
1546 update_datasource2(data);
1548 datasource_interface_refresh(function(data) {
1549 update_datasource2(data);
1553 /* Get the list of active sources */
1554 function datasource_source_refresh(cb) {
1555 var grab_sources = function(cb) {
1556 $.get(local_uri_prefix + "datasource/all_sources.json")
1557 .done(function(data) {
1558 ds_state['kismet_sources'] = kismet.sanitizeObject(data);
1560 ds_state['defer_source_update'] = false;
1562 .always(function() {
1563 if (ds_state['closed'] == 1)
1566 ds_state['datasource_get_tid'] = setTimeout(function() {
1567 datasource_source_refresh(cb)
1576 /* Get the list of potential interfaces */
1577 function datasource_interface_refresh(cb) {
1578 var grab_interfaces = function(cb) {
1581 url: local_uri_prefix + "datasource/list_interfaces.json",
1582 success: function(data) {
1583 ds_state['kismet_interfaces'] = kismet.sanitizeObject(data);
1584 ds_state['done_interface_update'] = true;
1586 ds_state['defer_interface_update'] = false;
1591 if (ds_state['closed'] == 1)
1594 ds_state['datasource_interface_tid'] = setTimeout(function() {
1595 datasource_interface_refresh(cb)
1600 grab_interfaces(cb);