// 2014-20 - ntop.org Date.prototype.format = function (format) { //author: meizz var o = { "M+": this.getMonth() + 1, //month "d+": this.getDate(), //day "h+": this.getHours(), //hour "m+": this.getMinutes(), //minute "s+": this.getSeconds(), //second "q+": Math.floor((this.getMonth() + 3) / 3), //quarter "S": this.getMilliseconds() //millisecond } if (/(y+)/.test(format)) format = format.replace(RegExp.$1, (this.getFullYear() + "").substr(4 - RegExp.$1.length)); for (var k in o) if (new RegExp("(" + k + ")").test(format)) format = format.replace(RegExp.$1, RegExp.$1.length == 1 ? o[k] : ("00" + o[k]).substr(("" + o[k]).length)); return format; } // Extended disable function jQuery.fn.extend({ disable: function (state) { return this.each(function () { var $this = $(this); if ($this.is('input, button, textarea, select')) this.disabled = state; else $this.toggleClass('disabled', state); }); } }); const NTOPNG_MIN_VISUAL_VALUE = 0.005; const REGEXES = { ipv4: String.raw`^(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)$`, ipv6: String.raw`^((([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5]):){3}([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])$|^\s*((([0-9A-Fa-f]{1,4}:){7}([0-9A-Fa-f]{1,4}|:))|(([0-9A-Fa-f]{1,4}:){6}(:[0-9A-Fa-f]{1,4}|((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3})|:))|(([0-9A-Fa-f]{1,4}:){5}(((:[0-9A-Fa-f]{1,4}){1,2})|:((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3})|:))|(([0-9A-Fa-f]{1,4}:){4}(((:[0-9A-Fa-f]{1,4}){1,3})|((:[0-9A-Fa-f]{1,4})?:((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3}))|:))|(([0-9A-Fa-f]{1,4}:){3}(((:[0-9A-Fa-f]{1,4}){1,4})|((:[0-9A-Fa-f]{1,4}){0,2}:((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3}))|:))|(([0-9A-Fa-f]{1,4}:){2}(((:[0-9A-Fa-f]{1,4}){1,5})|((:[0-9A-Fa-f]{1,4}){0,3}:((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3}))|:))|(([0-9A-Fa-f]{1,4}:){1}(((:[0-9A-Fa-f]{1,4}){1,6})|((:[0-9A-Fa-f]{1,4}){0,4}:((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3}))|:))|(:(((:[0-9A-Fa-f]{1,4}){1,7})|((:[0-9A-Fa-f]{1,4}){0,5}:((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3}))|:)))(%.+)?\s*)$`, domainName: String.raw`^([a-zA-Z]([a-zA-Z]|[0-9])?\.[a-zA-Z]{2,13}|[a-zA-Z0-9]([\-_.a-zA-Z0-9]{1,61}[a-zA-Z0-9])?\.[a-zA-Z]{2,13}|[a-zA-Z0-9]([\-_.a-zA-Z0-9]{1,61}[a-zA-Z0-9])?\.[a-zA-Z]{2,30}\.[a-zA-Z]{2,3})$`, port: String.raw`^([0-9]{1,4}|[1-5][0-9]{4}|6[0-4][0-9]{3}|65[0-4][0-9]{2}|655[0-2][0-9]|6553[0-5])$`, latency: String.raw`^([0-9]*[.])?[0-9]+$`, url: String.raw`^(https?\:\/\/[^\/\s]+(\/.*)?)$`, emailUrl: String.raw`^smtps?:\/\/[\-a-zA-Z0-9:.]{1,256}$`, macAddress: String.raw`^([0-9A-Fa-f]{2}:){5}([0-9A-Fa-f]{2})$`, hostname: String.raw`^(?!\s*$)[a-zA-Z0-9._: \-\/]{1,250}|^[a-zA-Z0-9._: \-\/]{1,250}@[0-9]{0,5}`, username: String.raw`^[a-zA-Z0-9._@!-?]{3,30}$`, singleword: String.raw`^(?=[a-zA-Z0-9._:\-]{3,253}$)(?!.*[_.:\-]{2})[^_.:\-].*[^_.:\-]$`, multiword: String.raw`^([a-zA-Z0-9._:\-\s]{3,253})$`, email: String.raw`^(([^<>()[\]\.,;:\s@\"]+(\.[^<>()[\]\.,;:\s@\"]+)*)|(\".+\"))@(([^<>()[\]\.,;:\s@\"]+\.)+[a-zA-Z]{2,})$`, emailCommaList: String.raw`^((?:[a-zA-Z0-9.!#$%&'*+\-\/=?^_\`\|~]+@[a-zA-Z0-9\-]+(?:\.[a-zA-Z0-9\-]+)*)|([a-zA-Z\d.!#$%&'*+\-\/=?^_\`\|~]{1,128}))(?:,((?:[a-zA-Z0-9.!#$%&'*+\-\/=?^_\`\|~]+@[a-zA-Z0-9\-]+(?:\.[a-zA-Z0-9\-]+)*)|([a-zA-Z\d.!#$%&'*+\-\/=?^_\`\|~]{1,128})))*$`, https: String.raw`^https?:\/\/.+$`, token: String.raw`^[0-9a-f]{32}`, score: String.raw`^[0-9]{1,5}`, telegram_channel: String.raw`^[0-9\-]{1,15}`, password: String.raw`^[\w\/$!\/()=?^*@_-]{5,31}$`, tls_certificate: String.raw`^[^=,]+=[^=,]+(,\s[^=,]+=[^=,]+)*$`, domain_name_not_strict: String.raw`^[a-zA-Z0-9\-_~]+((\.[a-zA-Z0-9\-_~]+)+)$`, non_quoted_text: String.raw`^[a-zA-Z0-9.-_]+$`, }; export default class NtopUtils { /* Show an overlay to hide loading */ static toggleOverlays(time = 500) { $(`.overlay`).toggle(time); } static showOverlays(time = 500) { $(`.overlay`).fadeIn(time); } static hideOverlays(time = 500) { $(`.overlay`).fadeOut(time); } static get REGEXES() { return REGEXES; } static getIPv4RegexWithCIDR() { const length = REGEXES.ipv4.length; return `${REGEXES.ipv4.substring(0, length - 1)}(\\/?)(\\b([0-9]|[12][0-9]|3[0-2])?\\b)$`; } static getIPv6RegexWithCIDR() { const length = REGEXES.ipv6.length; return `${REGEXES.ipv6.substring(0, length - 1)}(\\/?)\\b([0-9]|[1-9][0-9]|1[01][0-9]|12[0-8])?\\b$`; } /** * Resolve a hostname by doing a DNS Resolve. * @param {string} hostname The hostname to resolve */ static async resolveDNS(hostname = "ntop.org") { // resolve the hostname by doing a fetch request to the backend try { const request = await fetch(`${http_prefix}/lua/rest/v2/get/dns/resolve.lua?hostname=${hostname}`); const response = await request.json(); return response; } catch (err) { // prints out the error if the request fails console.error(`Something went wrong when resolving hostname: ${err}`) } // if the request has failed return a placeholder response // indicating the failure return { rc: -1, rc_str: "FAILED_HTTP_REQUEST" }; } /** * Replace the inputs which contain the [data-pattern] attribute * with the [pattern] attribute. */ static initDataPatterns() { // for each input with the data-pattern attribute // substitute the data-pattern with the right regexes $(`input[data-pattern]`).each(function () { // if the pattern is empty then print a warn inside the console const dataPattern = $(this).data('pattern'); if (!dataPattern) { console.warn(`An empty data-pattern on an input was found!`, this); return; } // build the regexp pattern for the input const pattern = dataPattern.split('|').map(p => REGEXES[p].toString()).join('|'); // load the pattern $(this).attr('pattern', pattern); // remove the data-pattern from the input $(this).removeAttr('data-pattern'); }); } static is_good_ipv4(ipv4) { return new RegExp(REGEXES.ipv4).test(ipv4); } static is_good_ipv6(ipv6) { return new RegExp(REGEXES.ipv6).test(ipv6); } static is_mac_address(mac) { return new RegExp(REGEXES.macAddress).test(mac); } static isNumeric(value) { return /^\d+$/.test(value); } static is_network_mask(what, optional_mask) { var elems = what.split("/"); var mask = null; var ip_addr; if (elems.length != 2) { if (!optional_mask) return null; else ip_addr = what; } else { ip_addr = elems[0]; if (!NtopUtils.isNumeric(elems[1])) return null; mask = parseInt(elems[1]); if (mask < 0) return null; } if (NtopUtils.is_good_ipv4(ip_addr)) { if (mask === null) mask = 32; else if (mask > 32) return null; return { type: "ipv4", address: ip_addr, mask: mask }; } else if (NtopUtils.is_good_ipv6(elems[0])) { if (mask === null) mask = 128; else if (mask > 128) return (false); return { type: "ipv6", address: ip_addr, mask: mask }; } return null; } static fbits(bits) { const sizes = ['bps', 'Kbps', 'Mbps', 'Gbps', 'Tbps']; if (typeof (bits) === "undefined") return "-"; if (bits == 0) return '0'; if ((bits > 0) && (bits < NTOPNG_MIN_VISUAL_VALUE)) return ('< ' + NTOPNG_MIN_VISUAL_VALUE + ' bps'); var bits_log1000 = Math.log(bits) / Math.log(1000) var i = parseInt(Math.floor(bits_log1000)); if (i < 0 || isNaN(i)) { i = 0; } else if (i >= sizes.length) { // prevents overflows return "> " + sizes[sizes.length - 1] } if (i <= 1) { return Math.round(bits / Math.pow(1000, i) * 100) / 100 + ' ' + sizes[i] } else { var ret = parseFloat(bits / Math.pow(1000, i)).toFixed(2) if (ret % 1 == 0) ret = Math.round(ret) return ret + ' ' + sizes[i] } } static export_rate(eps) { if (typeof (eps) === "undefined") return "-"; var sizes = ['exp/s', 'Kexp/s']; if (eps == 0) return '0'; if ((eps > 0) && (eps < NTOPNG_MIN_VISUAL_VALUE)) return ('< ' + NTOPNG_MIN_VISUAL_VALUE + ' exps/s'); var res = NtopUtils.scaleValue(eps, sizes, 1000); // Round to two decimal digits return Math.round(res[0] * 100) / 100 + ' ' + res[1]; } static exports_format(exports) { if (typeof (exports) === "undefined") return "-"; var exports_label = i18n_ext.exports.toLowerCase(); var sizes = [exports_label, 'K ' + exports_label]; if (exports == 0) return '0'; if ((exports > 0) && (exports < NTOPNG_MIN_VISUAL_VALUE)) return ('< ' + NTOPNG_MIN_VISUAL_VALUE + ' exps/s'); var res = NtopUtils.scaleValue(exports, sizes, 1000); // Round to two decimal digits return Math.round(res[0] * 100) / 100 + ' ' + res[1]; } static fbits_from_bytes(bytes) { if (typeof (bytes) === "undefined") return "-"; return (NtopUtils.fbits(bytes * 8)); } static fpackets(pps) { if (typeof (pps) === "undefined") return "-"; var sizes = ['pps', 'Kpps', 'Mpps', 'Gpps', 'Tpps']; if (pps == 0) return '0'; if ((pps > 0) && (pps < NTOPNG_MIN_VISUAL_VALUE)) return ('< ' + NTOPNG_MIN_VISUAL_VALUE + ' pps'); var res = NtopUtils.scaleValue(pps, sizes, 1000); // Round to two decimal digits return Math.round(res[0] * 100) / 100 + ' ' + res[1]; } static fpoints(pps) { if (typeof (pps) === "undefined") return "-"; var sizes = ['pt/s', 'Kpt/s', 'Mpt/s', 'Gpt/s', 'Tpt/s']; if (pps == 0) return '0'; if ((pps > 0) && (pps < NTOPNG_MIN_VISUAL_VALUE)) return ('< ' + NTOPNG_MIN_VISUAL_VALUE + ' pt/s'); var res = NtopUtils.scaleValue(pps, sizes, 1000); // Round to two decimal digits return Math.round(res[0] * 100) / 100 + ' ' + res[1]; } static fflows(fps) { if (typeof (fps) === "undefined") return "-"; var sizes = ['fps', 'Kfps', 'Mfps', 'Gfps', 'Tfps']; if (fps == 0) return '0'; if ((fps > 0) && (fps < NTOPNG_MIN_VISUAL_VALUE)) return ('< ' + NTOPNG_MIN_VISUAL_VALUE + ' fps'); var res = NtopUtils.scaleValue(fps, sizes, 1000); // Round to two decimal digits return Math.round(res[0] * 100) / 100 + ' ' + res[1]; } static fmsgs(mps) { if (typeof (mps) === "undefined") return "-"; var sizes = ['msg/s', 'Kmsg/s', 'Msg/s', 'Gmsg/s', 'Tmsg/s']; if (mps == 0) return '0'; if ((mps > 0) && (mps < NTOPNG_MIN_VISUAL_VALUE)) return ('< ' + NTOPNG_MIN_VISUAL_VALUE + ' mps'); var res = NtopUtils.scaleValue(mps, sizes, 1000); // Round to two decimal digits return Math.round(res[0] * 100) / 100 + ' ' + res[1]; } static fmillis(ms) { if (ms === undefined) return '-'; const sizes = ['ms']; const res = NtopUtils.scaleValue(ms, sizes, 1000); return Math.round(res[0] * 100) / 100 + ' ' + res[1]; } static fnone(val) { if (val === undefined) return '-'; return Math.round(val * 100) / 100; } static falerts(aps) { if (typeof (aps) === "undefined") return "-"; // Round to two decimal digits return Math.round(aps * 100) / 100 + ' alerts/s'; } static fint(value) { if (typeof (value) === "undefined") return "-"; var x = Math.round(value); return x.toString().replace(/\B(?=(\d{3})+(?!\d))/g, ","); } static ffloat(value) { if (typeof (value) === "undefined") return "-"; var x = Math.round(value * 100) / 100.; return x.toString().replace(/\B(?=(\d{3})+(?!\d))/g, ","); } static fpercent(value) { if (typeof (value) === "undefined") return "-"; return Math.round(value * 100) / 100 + " %"; } static percentage(value, total) { if (total > 0) { var pctg = Math.round((value * 10000) / total) if (pctg > 0) { /* Two decimals */ return (" [ " + (pctg / 100) + " % ] ") } } return ("") } static fdate(when) { var epoch = when * 1000; var d = new Date(epoch); return (d); } static capitaliseFirstLetter(string) { return string.charAt(0).toUpperCase() + string.slice(1); } static get_trend(actual, before) { if ((actual === undefined) || (before === undefined) || (actual == before)) { return (""); } else if (actual > before) { return (""); } else { return (""); } } static abbreviateString(str, len) { if (!str) return ""; if (str.length < len) return str; return str.substring(0, len) + "..."; } static toFixed2(num) { if (!num) return ""; return num.toFixed(2); } // Convert bytes to human readable format static bytesToSize(bytes) { if (typeof (bytes) === "undefined") return "-"; var precision = 2; var kilobyte = 1024; var megabyte = kilobyte * 1024; var gigabyte = megabyte * 1024; var terabyte = gigabyte * 1024; if ((bytes >= 0) && (bytes < kilobyte)) if (bytes != 0) return parseFloat(bytes.toFixed(precision)) + " Bytes"; else return parseFloat(bytes) + " Bytes"; else if ((bytes >= kilobyte) && (bytes < megabyte)) return parseFloat((bytes / kilobyte).toFixed(precision)) + ' KB'; else if ((bytes >= megabyte) && (bytes < gigabyte)) return parseFloat((bytes / megabyte).toFixed(precision)) + ' MB'; else if ((bytes >= gigabyte) && (bytes < terabyte)) return parseFloat((bytes / gigabyte).toFixed(precision)) + ' GB'; else if (bytes >= terabyte) return parseFloat((bytes / terabyte).toFixed(precision)) + ' TB'; else return parseFloat(bytes.toFixed(precision)) + ' Bytes'; } static drawTrend(current, last, withColor) { if (current == last) { return (""); } else if (current > last) { return (""); } else { return (""); } } static toggleAllTabs(enabled) { if (enabled === true) $("#historical-tabs-container").find("li").removeClass("disabled").find("a").attr("data-toggle", "tab"); else $("#historical-tabs-container").find("li").addClass("disabled").find("a").removeAttr("data-toggle"); } static disableAllDropdownsAndTabs() { $("select").each(function () { $(this).prop("disabled", true); }); NtopUtils.toggleAllTabs(false) } static enableAllDropdownsAndTabs() { $("select").each(function () { $(this).prop("disabled", false); }); NtopUtils.toggleAllTabs(true) } static capitalize(s) { return s && s[0].toUpperCase() + s.slice(1); } static addCommas(nStr) { nStr += ''; var x = nStr.split('.'); var x1 = x[0]; var x2 = x.length > 1 ? '.' + x[1] : ''; var rgx = /(\d+)(\d{3})/; while (rgx.test(x1)) { x1 = x1.replace(rgx, '$1' + ',' + '$2'); } return x1 + x2; } static scaleValue(val, sizes, scale, decimals) { if (val == 0) return [0, sizes[0]]; let factor = decimals ? (10 * decimals) : 10; var i = parseInt(Math.floor(Math.log(val) / Math.log(scale))); if (i < 0 || isNaN(i)) { i = 0; } else if (i >= sizes.length) { i = sizes.length - 1; } return [Math.round((val / Math.pow(scale, i)) * factor) / factor, sizes[i]]; } static formatValue(val, decimals) { var sizes = ['', 'K', 'M', 'G', 'T']; if (val == 0) return '0'; if ((val > 0) && (val < NTOPNG_MIN_VISUAL_VALUE)) return ('< ' + NTOPNG_MIN_VISUAL_VALUE); if (decimals == undefined) decimals = 0; var res = NtopUtils.scaleValue(val, sizes, 1000, decimals); return res[0] + res[1]; } static formatPackets(n) { return (NtopUtils.addCommas(n.toFixed(0)) + " Pkts"); } static bytesToVolume(bytes) { var sizes = ['Bytes', 'KB', 'MB', 'GB', 'TB']; if (bytes == 0) return '0 Bytes'; if ((bytes > 0) && (bytes < NTOPNG_MIN_VISUAL_VALUE)) return ('< ' + NTOPNG_MIN_VISUAL_VALUE + " Bytes"); var res = NtopUtils.scaleValue(bytes, sizes, 1024); return parseFloat(res[0]) + " " + res[1]; }; static bytesToVolumeAndLabel(bytes) { var sizes = ['Bytes', 'KB', 'MB', 'GB', 'TB']; if (bytes == 0) return '0 Bytes'; var i = parseInt(Math.floor(Math.log(bytes) / Math.log(1024))); return [(bytes / Math.pow(1024, i)).toFixed(2), sizes[i]]; }; static bitsToSize(bits, factor) { factor = factor || 1000; var sizes = ['bps', 'Kbps', 'Mbps', 'Gbps', 'Tbps']; if (bits == 0) return '0 bps'; if ((bits > 0) && (bits < NTOPNG_MIN_VISUAL_VALUE)) return ('< ' + NTOPNG_MIN_VISUAL_VALUE + " bps"); var res = NtopUtils.scaleValue(bits, sizes, factor); return res[0].toFixed(2) + " " + res[1]; }; static bitsToSize_no_comma(bits, factor) { factor = factor || 1000; var sizes = ['bps', 'Kbps', 'Mbps', 'Gbps', 'Tbps']; if (bits == 0) return '0 bps'; if ((bits > 0) && (bits < NTOPNG_MIN_VISUAL_VALUE)) return ('< ' + NTOPNG_MIN_VISUAL_VALUE + " bps"); var res = NtopUtils.scaleValue(bits, sizes, factor); return res[0] + " " + res[1]; }; static secondsToTime(seconds) { if (seconds < 1) { return ("< 1 sec") } let days = Math.floor(seconds / 86400) let hours = Math.floor((seconds / 3600) - (days * 24)) let minutes = Math.floor((seconds / 60) - (days * 1440) - (hours * 60)) let sec = seconds % 60 let msg = "", msg_array = [] if (days > 0) { let years = Math.floor(days / 365) if (years > 0) { days = days % 365 msg = years + " year" if (years > 1) { msg += "s" } msg_array.push(msg) msg = "" } msg = days + " day" if (days > 1) { msg += "s" } msg_array.push(msg) msg = "" } if (hours > 0) { if (hours < 10) { msg = "0" } msg += hours + ":"; } if (minutes < 10) { msg += "0" } msg += minutes + ":"; if (sec < 10) { msg += "0" } msg += sec; msg_array.push(msg) return msg_array.join(", ") } static msecToTime(msec) { if (msec >= 1000) { return NtopUtils.secondsToTime(msec / 1000); } else { var x = Math.round(msec * 1000) / 1000.; return x.toString().replace(/\B(?=(\d{3})+(?!\d))/g, ",") + " ms"; } } static epoch2Seen(epoch) { /* 08/01/13 15:12:37 [18 min, 13 sec ago] */ var d = new Date(epoch * 1000); var tdiff = Math.floor(((new Date()).getTime() / 1000) - epoch); return (d.format("dd/MM/yyyy hh:mm:ss") + " [" + NtopUtils.secondsToTime(tdiff) + " ago]"); } /* ticks for graph x axis */ static graphGetXAxisTicksFormat(diff_epoch) { var tickFormat; if (diff_epoch <= 86400) { tickFormat = "%H:%M:%S"; } else if (diff_epoch <= 2 * 86400) { tickFormat = "%b %e, %H:%M:%S"; } else { tickFormat = "%b %e"; } return (tickFormat); } static paramsExtend(defaults, override) { return $.extend({}, defaults, override); } static paramsToForm(form, params) { form = $(form); for (var k in params) { if (params.hasOwnProperty(k)) { var input = $(''); input.appendTo(form); } } return form; } /* * This function creates a javascript object where each k->v pair of the input object * translates into two pairs in the output object: a key_[i]->k and a val_[i]->v, where * i is an incremental index. * * The output object can then be serialized to an URL. This conversion is required for * handling special characters: since ntopng strips special characters in _GET keys, * _GET values must be used. * * This function performs the inverse conversion of lua paramsPairsDecode. * */ static paramsPairsEncode(params) { var i = 0; var res = {}; for (var k in params) { res["key_" + i] = k; res["val_" + i] = params[k]; i = i + 1; } return res; } static hostkey2hostInfo(host_key) { var info; host_key = host_key.replace(/____/g, ":"); host_key = host_key.replace(/___/g, "/"); host_key = host_key.replace(/__/g, "."); info = host_key.split("@"); return (info); } static handle_tab_state(nav_object, default_tab) { $('a', nav_object).click(function (e) { e.preventDefault(); }); // store the currently selected tab in the hash value $(" > li > a", nav_object).on("shown.bs.tab", function (e) { var id = $(e.target).attr("href").substr(1); if (history.replaceState) { // this will prevent the 'jump' to the hash history.replaceState(null, null, "#" + id); } else { // fallback window.location.hash = id; } }); // on load of the page: switch to the currently selected tab var hash = window.location.hash; if (!hash) hash = "#" + default_tab; $('a[href="' + hash + '"]', nav_object).tab('show'); } static _add_find_host_link(form, name, data) { $('').attr({ type: 'hidden', id: name, name: name, value: data, }).appendTo(form); } /* Used while searching hosts a and macs with typeahead */ static makeFindHostBeforeSubmitCallback(http_prefix) { return function (form, data) { if (data.context && data.context == "historical") { form.attr("action", http_prefix + "/lua/pro/db_search.lua"); if (data.type == "ip") { NtopUtils._add_find_host_link(form, "ip", data.ip); } else if (data.type == "mac") { NtopUtils._add_find_host_link(form, "mac", data.mac); } else if (data.type == "community_id") { NtopUtils._add_find_host_link(form, "community_id", data.community_id); } else if (data.type == "ja3_client") { NtopUtils._add_find_host_link(form, "ja3_client", data.ja3_client); } else if (data.type == "ja3_server") { NtopUtils._add_find_host_link(form, "ja3_server", data.ja3_server); } else /* "hostname" */ { NtopUtils._add_find_host_link(form, "name", data.hostname ? data.hostname : data.name); } } else { if (data.type == "mac") { form.attr("action", http_prefix + "/lua/mac_details.lua"); } else if (data.type == "network") { form.attr("action", http_prefix + "/lua/hosts_stats.lua"); NtopUtils._add_find_host_link(form, "network", data.network); } else if (data.type == "snmp") { form.attr("action", http_prefix + "/lua/pro/enterprise/snmp_interface_details.lua"); NtopUtils._add_find_host_link(form, "snmp_port_idx", data.snmp_port_idx); } else if (data.type == "snmp_device") { form.attr("action", http_prefix + "/lua/pro/enterprise/snmp_device_details.lua"); } else if (data.type == "asn") { form.attr("action", http_prefix + "/lua/hosts_stats.lua"); NtopUtils._add_find_host_link(form, "asn", data.asn); } else { form.attr("action", http_prefix + "/lua/host_details.lua"); NtopUtils._add_find_host_link(form, "mode", "restore"); } } return true; } } static tstampToDateString(html_tag, format, tdiff) { tdiff = tdiff || 0; var timestamp = parseInt(html_tag.html()) + tdiff; var localized = d3.time.format(format)(new Date(timestamp * 1000)); html_tag.html(localized).removeClass("hidden"); return localized; } static noHtml(s) { return s.replace(/<[^>]+>/g, ''); } static cleanCustomHostUrl(host) { /* Remove starting http(s). */ return host .replace(/^http:\/\//gi, '') .replace(/^https:\/\//gi, '') /* Remove starting www. */ .replace(/^www\./gi, '') /* Remove non-allowed characters */ .replace(/[^0-9a-zA-Z\.:\/_-]/gi, ''); } /* https://stackoverflow.com/questions/2090551/parse-query-string-in-javascript */ static parseQuery(queryString) { var query = {}; var pairs = (queryString[0] === '?' ? queryString.substr(1) : queryString).split('&'); for (var i = 0; i < pairs.length; i++) { var pair = pairs[i].split('='); query[decodeURIComponent(pair[0])] = decodeURIComponent(pair[1] || ''); } return query; } static getHistoryParameters(params) { var cur_params = NtopUtils.parseQuery(window.location.search); var new_params = $.extend(cur_params, params); var new_query = "?" + $.param(new_params, true); var baseUrl = [location.protocol, '//', location.host, location.pathname].join(''); return baseUrl + new_query; } // return true if the status code is different from 200 static check_status_code(status_code, status_text, $error_label) { const is_different = status_code != 200; if (is_different && $error_label != null) { let message = i18n_ext.request_failed_message; if (status_code && status_text) { message += `: ${status_code} - ${status_text}`; } $error_label.text(message).show(); } else if (is_different && $error_label == null) { alert(`${i18n_ext.request_failed_message}: ${status_code} - ${status_text}`); } return is_different; } // To be used in conjunction with httpdocs/templates/config_list_components/import_modal.html static importModalHelper(params) { if (!params.loadConfigXHR) { throw ("importModalHelper:: Missing 'loadConfigXHR' param"); } $(`input#import-input`).on('change', function () { $(`#btn-confirm-import`).removeAttr("disabled"); }); $(`#import-modal`).on('hidden.bs.modal', function () { $(`#import-input`).val(''); $("#import-error").hide().removeClass('text-warning').addClass('invalid-feedback'); $(`#btn-confirm-import`).attr("disabled", "disabled"); }); $("#import-modal").on("submit", "form", function (e) { e.preventDefault(); const $button = $('#btn-confirm-import'); $button.attr("disabled", ""); // read configuration file const file = $('#import-input')[0].files[0]; if (!file) { $("#import-error").text(`${i18n_ext.no_file}`).show(); $button.removeAttr("disabled"); return; } const reader = new FileReader(); reader.readAsText(file, "UTF-8"); reader.onload = function () { // Client-side configuration file format check let jsonConfiguration = null try { jsonConfiguration = JSON.parse(reader.result); } catch (e) { } if (!jsonConfiguration) { $("#import-error").text(i18n_ext.rest_consts[responseJSON.rc_str] || 'Not Implemented Yet').show(); $button.removeAttr("disabled"); return; } // Submit configuration file params.loadConfigXHR(reader.result) .done((response, status, xhr) => { if (response.rc < 0) { $("#import-error").text(response.rc_str).show(); return; } // if the operation was successful call the successCallback if (params.successCallback) { params.successCallback(response); } const key = $(`input[name='configuration']:checked`).val(); const body = (key == 'all') ? i18n("manage_configurations.messages.import_all_success") : i18n("manage_configurations.messages.import_success"); // show a success alert message ToastUtils.showToast({ id: 'import-configuration-alert', level: 'success', title: i18n("success"), body: body, delay: 2000 }); $("#import-modal").modal('hide'); }) .fail(({ responseJSON }) => { const PARTIAL_IMPORT_RC = -28; if (params.failureCallback) { params.failureCallback(responseJSON); } if (responseJSON && responseJSON.rc > 0) return; if (responseJSON.rc == PARTIAL_IMPORT_RC) $(`#import-error`).removeClass('invalid-feedback').addClass('text-warning'); $("#import-error").text(i18n_ext.rest_consts[responseJSON.rc_str] || i18n_ext.FAILED_HTTP_REQUEST).show(); }) .always(() => { $button.removeAttr("disabled"); }); }; }); } static serializeFormArray(serializedArray) { const serialized = {}; serializedArray.forEach((obj) => { /* if the object is an array */ if (obj.name.includes('[]')) { return; } else { // clean the string if (typeof obj.value === "string") { obj.value = obj.value.trim(); } serialized[obj.name] = obj.value; } }); return serialized; } static cleanForm(formSelector) { /* remove validation fields and tracks */ $(formSelector).find('input,select,textarea').each(function (i, input) { $(this).removeClass(`is-valid`).removeClass(`is-invalid`); }); /* reset all the values */ $(formSelector)[0].reset(); } /** * Make a fetch call with a timeout option */ static fetchWithTimeout(uri, options = {}, time = 5000) { const controller = new AbortController() const config = { ...options, signal: controller.signal } return fetch(uri, config) .then((response) => { if (!response.ok) { throw new Error(`${response.status}: ${response.statusText}`) } return response }) .catch((error) => { if (error.name === 'AbortError') { throw new Error('Response timed out') } }) } static setPref(action, csrf, success, failure) { if (action == undefined) { console.warn("An action key must be defined to set a preference!"); return; } const empty = () => { }; const request = $.post(`${http_prefix}/lua/update_prefs.lua`, { action: action, csrf: csrf }); request.done(success || empty); request.fail(failure || empty); } /** * Glue strings contained in array separated by a comma. * @param {array} array The array of strings. I.e. ["Hello", "World"] * @param {number} limit How many words the string contains * * @return {string} A string built by array's elements. i.e: "Hello, World" */ static arrayToListString(array, limit) { if (array == undefined) return ""; if (array.length > limit) { return array.slice(0, limit).join(", ") + ` ${i18n_ext.and_x_more.replace('$num', array.length - limit)}`; } return array.slice(0, limit).join(", "); } static buildURL(location, params = {}, hasReferer = false, refererParams = {}) { const url = new URL(location, window.location); for (const [name, value] of Object.entries(params)) { if (value || value === 0) url.searchParams.set(name, value); continue; } if (hasReferer) { const refUrl = new URL(window.location.href); for (const [name, value] of Object.entries(refererParams)) { if (!value) continue; refUrl.searchParams.set(name, value); } url.searchParams.set('referer', refUrl.toString()); } return url.toString(); } static getEditPoolLink(href, poolId) { const url = new URL(href, window.location); url.searchParams.set('pool_id', poolId); return url.toString(); } static getPoolLink(poolType, poolId = 0) { return `${http_prefix}/lua/rest/v2/get/${poolType}/pools.lua?pool=${poolId}`; } static async getPool(poolType, id = 0) { if (poolType === null) throw 'A pool type must be defined!'; try { const request = await fetch(NtopUtils.getPoolLink(poolType, id)); const pool = await request.json(); if (pool.rc < 0) { return [false, {}]; } return [true, pool.rsp[0]]; } catch (err) { return [false, {}]; } } /** * Save the scale of element inside the local storage * @param {object} $element * @param {object} scale */ static saveElementScale($element, scale = { width: 0, height: 0 }) { const key = NtopUtils.generateScaleElementKey($element); localStorage.setItem(key, JSON.stringify(scale)); } static generateScaleElementKey($element) { let identificator; const page = location.pathname; const elementId = $element.attr('id'); if (elementId !== "") { identificator = elementId; } else { const className = $element.attr('class'); identificator = className; } const key = `${identificator}-${page}-scale`; return key; } /** * Load the old scale value ofx element from the local storage * @param {object} $element */ static loadElementScale($element) { const key = NtopUtils.generateScaleElementKey($element); const currentValue = localStorage.getItem(key); if (currentValue == null) return undefined; return JSON.parse(currentValue); } static fillFieldIfValid($field, value) { if (value === undefined) { $field.val(''); } else { $field.val(value); } } static copyToClipboard(text, item) { const el = document.createElement('textarea'); el.value = text; el.setAttribute('readonly', ''); el.style.position = 'absolute'; el.style.left = '-9999px'; document.body.appendChild(el); el.select(); document.execCommand('copy'); document.body.removeChild(el); $(item).attr("title", "Copied!").tooltip("dispose").tooltip().tooltip("show"); $(item).removeAttr("data-bs-original-title") $(item).attr("title", text) } static stripTags(html) { let t = document.createElement("div"); t.innerHTML = html; return t.textContent || t.innerText || ""; } static shortenLabel(label, len, last_char) { let shortened_label = label if (label.length > len + 5) { if (last_char) { let last_index = label.lastIndexOf(last_char) const requested_label = label.slice(last_index) if (len > last_index) len = last_index shortened_label = label.slice(0, len) + "... " + requested_label } else { shortened_label = label.slice(0, len) + "..."; } } return shortened_label } static sortAlphabetically(a, b) { const nameA = a.label?.toUpperCase(); // ignore upper and lowercase const nameB = b.label?.toUpperCase(); // ignore upper and lowercase if (nameA < nameB) { return -1; } if (nameA > nameB) { return 1; } return 0; } /* This function, given a name and a value, return a string * formatted in the following way: * name [value] * If max_name_len is different from 0, then it's going to cut the name string * to max_name_len */ static formatNameValue(name, value, max_name_len) { let label = name; if (name != value) { if (max_name_len && typeof (max_name_len) == 'number') label = this.shortenLabel(label, max_name_len, '.'); label = `${label} [${value}]` } return label } /* This function, remove from a string the VLAN 0 * name@0 -> name */ static removeVlan(name) { let label = name const vlan_index = label.lastIndexOf('@'); if (vlan_index != -1) { const vlan = label.slice(vlan_index + 1); if (vlan == 0) { label = label.slice(0, vlan_index); } } return label } /* Format an object with label and value from a column row */ static formatGenericObj(obj, row) { let label = obj.label ? obj.label : obj.value; let key = obj.value; return label; } /* Format a country from a column object */ static formatCountry(obj, row) { let country_code = obj.value; let label = obj.label ? obj.label : obj.value; return `${label} `; } /* Format an host from a column object */ static formatHost(obj, row, is_client) { let label = ""; if (!obj) { return label; } /* Link */ let host_key = obj.ip; if (row.vlan_id && row.vlan_id.value) host_key = host_key + '@' + row.vlan_id.value; /* Label */ label = obj.label ? obj.label : obj.value; if (row.vlan_id && row.vlan_id.label) label += `@${row.vlan_id.label}`; const url = NtopUtils.buildURL(`${http_prefix}/lua/host_details.lua`, { host: host_key }); label = `${label}`; /* Country */ let country_obj = is_client ? row.cli_country : row.srv_country; if (!country_obj && row.country) country_obj = row.country; if (country_obj && country_obj.value) label += ` `; return label; } /* Format a network from a column object */ static formatNetwork(obj, row) { let label = ""; if (!obj) { return label; } /* Link */ let network_key = obj.value; /* Label */ label = obj.label ? obj.label : obj.value; if (row.vlan_id && row.vlan_id.label) label += `@${row.vlan_id.label}`; const url = NtopUtils.buildURL(`${http_prefix}/lua/hosts_stats.lua`, { network: network_key }); label = `${label}`; return label; } /* This function converts a mac address to a string*/ static convertMACAddress(a) { return a.toLowerCase().replace(/[^a-f0-9]/g, ''); } /* This function converts an ip to a number equale to the ip but without . or :: in case of ipv6 * this is needed in case of ordering */ static convertIPAddress(a) { var i, item; var m, n, t; var x, xa; if (!a) { return 0; } a = a.replace(/<[\s\S]*?>/g, ""); //IPv4:Port t = a.split(":"); if (t.length == 2) { m = t[0].split("."); } else { m = a.split("."); } n = a.split(":"); x = ""; xa = ""; if (m.length == 4) { // IPV4 for (i = 0; i < m.length; i++) { item = m[i]; if (item.length == 1) { x += "00" + item; } else if (item.length == 2) { x += "0" + item; } else { x += item; } } } else if (n.length > 0) { // IPV6 var count = 0; for (i = 0; i < n.length; i++) { item = n[i]; if (i > 0) { xa += ":"; } if (item.length === 0) { count += 0; } else if (item.length == 1) { xa += "000" + item; count += 4; } else if (item.length == 2) { xa += "00" + item; count += 4; } else if (item.length == 3) { xa += "0" + item; count += 4; } else { xa += item; count += 4; } } // Padding the :: n = xa.split(":"); var paddDone = 0; for (i = 0; i < n.length; i++) { item = n[i]; if (item.length === 0 && paddDone === 0) { for (var padding = 0; padding < (32 - count); padding++) { x += "0"; paddDone = 1; } } else { x += item; } } } return x; } /* Format an AS from a column object */ static formatASN(obj, row) { let label = ""; if (!obj) { return label; } /* Link */ let asn_key = obj.value; /* Label */ label = obj.label ? obj.label : obj.value; const url = NtopUtils.buildURL(`${http_prefix}/lua/hosts_stats.lua`, { asn: asn_key }); label = `${label}`; return label; } static createProgressBar(percentage) { return `
${percentage} %
` } static createBreakdown(percentage_1, percentage_2, label_1, label_2) { return `
${label_1}
${label_2}
` } /* Return the number of rows available in a table */ static getNumTableRows() { return [10, 20, 50, 100]; } static formatApexChartLabelFromXandName({ series, seriesIndex, dataPointIndex, w }) { const serie = w.config.series[seriesIndex]["data"][dataPointIndex]; const name = serie["name"] const y_value = serie["y"]; const host_name = serie["meta"]["label"]; const x_axis_title = w.config.xaxis.title.text; const y_axis_title = w.config.yaxis[0].title.text; return (`
${host_name}
${x_axis_title}: ${name}
${y_axis_title}: ${y_value}
`) } static apexChartJumpToAlerts(event, chartContext, config) { const { seriesIndex, dataPointIndex } = config; const { series } = config.config; if (seriesIndex === -1) return; if (series === undefined) return; const serie = series[seriesIndex]; const base_url = serie.base_url || series[0]['base_url'] const default_url = serie.start_url || series[0]['start_url'] if (base_url != null && default_url != null) { const search = serie.data[dataPointIndex].meta.url_query; location.href = `${base_url}?${default_url}${search}`; } } static apexChartJumpToHostDetails(event, chartContext, config) { const { seriesIndex, dataPointIndex } = config; const { series } = config.config; if (seriesIndex === -1) return; if (series === undefined) return; const serie = series[seriesIndex]; const base_url = serie.base_url || series[0]['base_url'] if (base_url != null) { const url = `${base_url}?${serie.data[dataPointIndex].meta.url_query}`; ntopng_url_manager.go_to_url(url); } } static formatApexChartLabelFromXandY({ series, seriesIndex, dataPointIndex, w }) { const serie = w.config.series[seriesIndex]["data"][dataPointIndex]; const x_value = serie["x"]; const y_value = serie["y"]; const host_name = serie["meta"]["label"]; const x_axis_title = w.config.xaxis.title.text; const y_axis_title = w.config.yaxis[0].title.text; return (`
${host_name}
${x_axis_title}: ${x_value}
${y_axis_title}: ${y_value}
`) } } $(function () { // if there are inputs with 'pattern' data attribute // then initialize them NtopUtils.initDataPatterns(); });