/* Pi-hole: A black hole for Internet advertisements * (c) 2020 Pi-hole, LLC (https://pi-hole.net) * Network-wide ad blocking via your own hardware. * * This file is copyright under the latest version of the EUPL. * Please see LICENSE file for your rights under this license. */ /* global moment:false */ // Credit: https://stackoverflow.com/a/4835406 function escapeHtml(text) { var map = { "&": "&", "<": "<", ">": ">", '"': """, "'": "'" }; return text.replace(/[&<>"']/g, function (m) { return map[m]; }); } function unescapeHtml(text) { var map = { "&": "&", "<": "<", ">": ">", """: '"', "'": "'" }; return text.replace(/&(?:amp|lt|gt|quot|#039);/g, function (m) { return map[m]; }); } // Helper function for converting Objects to Arrays after sorting the keys function objectToArray(obj) { var arr = []; var idx = []; var keys = Object.keys(obj); keys.sort(function (a, b) { return a - b; }); for (var i = 0; i < keys.length; i++) { arr.push(obj[keys[i]]); idx.push(keys[i]); } return [idx, arr]; } function padNumber(num) { return ("00" + num).substr(-2, 2); } var info = null; // TODO clear this up; there shouldn't be a global var here function showAlert(type, icon, title, message) { var opts = {}; title = " " + title + "
"; switch (type) { case "info": opts = { type: "info", icon: "far fa-clock", title: title, message: message }; info = $.notify(opts); break; case "success": opts = { type: "success", icon: icon, title: title, message: message }; if (info) { info.update(opts); } else { $.notify(opts); } break; case "warning": opts = { type: "warning", icon: "fas fa-exclamation-triangle", title: title, message: message }; if (info) { info.update(opts); } else { $.notify(opts); } break; case "error": opts = { type: "danger", icon: "fas fa-times", title: " Error, something went wrong!
", message: message }; if (info) { info.update(opts); } else { $.notify(opts); } break; default: } } function datetime(date, html) { var format = html === false ? "Y-MM-DD HH:mm:ss z" : "Y-MM-DD []HH:mm:ss z"; return moment.unix(Math.floor(date)).format(format); } function disableAll() { $("input").prop("disabled", true); $("select").prop("disabled", true); $("button").prop("disabled", true); $("textarea").prop("disabled", true); } function enableAll() { $("input").prop("disabled", false); $("select").prop("disabled", false); $("button").prop("disabled", false); $("textarea").prop("disabled", false); // Enable custom input field only if applicable var ip = $("#select") ? $("#select").val() : null; if (ip !== null && ip !== "custom") { $("#ip-custom").prop("disabled", true); } } // Pi-hole IPv4/CIDR validator by DL6ER, see regexr.com/50csh function validateIPv4CIDR(ip) { // One IPv4 element is 8bit: 0 - 256 var ipv4elem = "(25[0-5]|2[0-4][0-9]|1[0-9][0-9]|[1-9][0-9]?|0)"; // CIDR for IPv4 is 1 - 32 bit var v4cidr = "(\\/([1-9]|[1-2][0-9]|3[0-2])){0,1}"; var ipv4validator = new RegExp( "^" + ipv4elem + "\\." + ipv4elem + "\\." + ipv4elem + "\\." + ipv4elem + v4cidr + "$" ); return ipv4validator.test(ip); } // Pi-hole IPv6/CIDR validator by DL6ER, see regexr.com/50csn function validateIPv6CIDR(ip) { // One IPv6 element is 16bit: 0000 - FFFF var ipv6elem = "[0-9A-Fa-f]{1,4}"; // CIDR for IPv6 is 1- 128 bit var v6cidr = "(\\/([1-9]|[1-9][0-9]|1[0-1][0-9]|12[0-8])){0,1}"; var ipv6validator = new RegExp( "^(((?:" + ipv6elem + "))*((?::" + ipv6elem + "))*::((?:" + ipv6elem + "))*((?::" + ipv6elem + "))*|((?:" + ipv6elem + "))((?::" + ipv6elem + ")){7})" + v6cidr + "$" ); return ipv6validator.test(ip); } function validateMAC(mac) { var macvalidator = new RegExp(/^([\da-fA-F]{2}:){5}([\da-fA-F]{2})$/); return macvalidator.test(mac); } function validateHostname(name) { var namevalidator = new RegExp(/[^<>;"]/); return namevalidator.test(name); } // set bootstrap-select defaults function setBsSelectDefaults() { var bsSelectDefaults = $.fn.selectpicker.Constructor.DEFAULTS; bsSelectDefaults.noneSelectedText = "none selected"; bsSelectDefaults.selectedTextFormat = "count > 1"; bsSelectDefaults.actionsBox = true; bsSelectDefaults.width = "fit"; bsSelectDefaults.container = "body"; bsSelectDefaults.dropdownAlignRight = "auto"; bsSelectDefaults.selectAllText = "All"; bsSelectDefaults.deselectAllText = "None"; bsSelectDefaults.countSelectedText = function (num, total) { if (num === total) { return "All selected (" + num + ")"; } return num + " selected"; }; } function stateSaveCallback(itemName, data) { localStorage.setItem(itemName, JSON.stringify(data)); } function stateLoadCallback(itemName) { // Receive previous state from client's local storage area var data = localStorage.getItem(itemName); // Return if not available if (data === null) { return null; } // Parse JSON string data = JSON.parse(data); // Clear possible filtering settings data.columns.forEach(function (value, index) { data.columns[index].search.search = ""; }); // Always start on the first page to show most recent queries data.start = 0; // Always start with empty search field data.search.search = ""; // Apply loaded state to table return data; } function getGraphType() { // Only return line if `barchart_chkbox` is explicitly set to false. Else return bar return localStorage.getItem("barchart_chkbox") === "false" ? "line" : "bar"; } function addFromQueryLog(domain, list) { var token = $("#token").text(); var alertModal = $("#alertModal"); var alProcessing = alertModal.find(".alProcessing"); var alSuccess = alertModal.find(".alSuccess"); var alFailure = alertModal.find(".alFailure"); var alNetworkErr = alertModal.find(".alFailure #alNetErr"); var alCustomErr = alertModal.find(".alFailure #alCustomErr"); var alList = "#alList"; var alDomain = "#alDomain"; // Exit the function here if the Modal is already shown (multiple running interlock) if (alertModal.css("display") !== "none") { return; } var listtype = list === "white" ? "Whitelist" : "Blacklist"; alProcessing.children(alDomain).html(domain); alProcessing.children(alList).html(listtype); alertModal.modal("show"); // add Domain to List after Modal has faded in alertModal.one("shown.bs.modal", function () { $.ajax({ url: "scripts/pi-hole/php/groups.php", method: "post", data: { domain: domain, list: list, token: token, action: "replace_domain", comment: "Added from Query Log" }, success: function (response) { alProcessing.hide(); if (!response.success) { // Failure alNetworkErr.hide(); alCustomErr.html(response.message); alFailure.fadeIn(1000); setTimeout(function () { alertModal.modal("hide"); }, 10000); } else { // Success alSuccess.children(alDomain).html(domain); alSuccess.children(alList).html(listtype); alSuccess.fadeIn(1000); setTimeout(function () { alertModal.modal("hide"); }, 2000); } }, error: function () { // Network Error alProcessing.hide(); alNetworkErr.show(); alFailure.fadeIn(1000); setTimeout(function () { alertModal.modal("hide"); }, 8000); } }); }); // Reset Modal after it has faded out alertModal.one("hidden.bs.modal", function () { alProcessing.show(); alSuccess.add(alFailure).hide(); alProcessing.add(alSuccess).children(alDomain).html("").end().children(alList).html(""); alCustomErr.html(""); }); } window.utils = (function () { return { escapeHtml: escapeHtml, unescapeHtml: unescapeHtml, objectToArray: objectToArray, padNumber: padNumber, showAlert: showAlert, datetime: datetime, disableAll: disableAll, enableAll: enableAll, validateIPv4CIDR: validateIPv4CIDR, validateIPv6CIDR: validateIPv6CIDR, setBsSelectDefaults: setBsSelectDefaults, stateSaveCallback: stateSaveCallback, stateLoadCallback: stateLoadCallback, getGraphType: getGraphType, validateMAC: validateMAC, validateHostname: validateHostname, addFromQueryLog: addFromQueryLog }; })();