/* * Copyright 2022, Digi International Inc. * * This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ // Constants. const ELEMENT_ETHERNET = "ethernet"; const ELEMENT_WIFI = "wifi"; const FIELD_DEFAULT_GATEWAY = "_default_gateway"; const FIELD_DNS1_ADDRESS = "_dns1_addr"; const FIELD_DNS2_ADDRESS = "_dns2_addr"; const FIELD_ENABLE = "_enable"; const FIELD_ENCRYPTION_TYPE = "_enc_type"; const FIELD_ERROR = "_error"; const FIELD_MAC_ADDRESS = "_mac"; const FIELD_IP_ADDRESS = "_ip_addr"; const FIELD_IP_MODE = "_ip_mode"; const FIELD_PARAM = "_param"; const FIELD_PASSWORD = "_password"; const FIELD_SAVE_BUTTON = "_save_button"; const FIELD_SSID = "_ssid"; const FIELD_SUBNET_MASK = "_subnet_mask"; const ID_DNS1 = "dns1"; const ID_DNS2 = "dns2"; const ID_ENABLE = "enable"; const ID_ETH0_TITLE = "eth0_title"; const ID_GATEWAY = "gateway"; const ID_IP = "ip"; const ID_IP_MODE = "type"; const ID_INTERFACE = "interface"; const ID_MAC = "mac"; const ID_NETMASK = "netmask"; const ID_PANEL_CONTAINER = "_panel_container"; const ID_PASSWORD = "psk"; const ID_SECURITY_MODE = "sec_mode"; const ID_SSID = "ssid"; const ID_TOGGLE_BUTTON = "_toggle_button"; const IP_MODE_DHCP = "dhcp"; const IP_MODE_STATIC = "static"; const ERROR_IP_VALUE_FORMAT = "Value must follow this format: XXX.XXX.XXX.XXX"; const ERROR_PASSWORD_INVALID = "Password must be between 8 and 63 characters long"; const ERROR_SSID_INVALID = "SSID value is not valid"; const ERROR_UNKNOWN = "Unknown error saving configuration."; const MESSAGE_LOADING_CONFIGURATION = "Loading configuration..."; const MESSAGE_SAVING_CONFIGURATION = "Saving configuration..."; const MESSAGE_CONFIGURATION_SAVED = "Configuration saved successfully!"; const PREFIX_ETHERNET = "eth"; const PREFIX_WIFI = "wlan"; const REGEX_IP = '^(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]?)$'; const REGEX_SSID = '^[^!#;+\\]/"\t][^+\\]/"\t]{0,31}$'; const REGEX_PASSWORD = '^[\u0020-\u007e]{8,63}$'; const ALL_ELEMENTS = "all"; // Variables. var readingNetworkInfo = false; var passwordChanged = false; // Initializes the network page. function initializeNetworkPage() { // Sanity checks. if (!isNetworkShowing()) return; // Read network configuration. readElements = [ELEMENT_ETHERNET, ELEMENT_WIFI]; readConfiguration(readElements, ALL_ELEMENTS); } // Gets the device configuration the given elements. function readConfiguration(readElements, parseElement) { // Execute only in the network page. if (!isNetworkShowing() || readingNetworkInfo) return; // Flag reading variable. readingNetworkInfo = true; // Hide the info popup. showInfoPopup(false); // Show the loading popup. showLoadingPopup(true, MESSAGE_LOADING_CONFIGURATION); // Send request to retrieve configuration. $.post( "http://" + getServerAddress() + "/ajax/get_config", JSON.stringify({ "elements": readElements }), function(data) { // Process only in the network page. if (!isNetworkShowing()) return; // Process answer. processReadConfigurationResponse(data, parseElement); } ).fail(function(response) { // Flag reading variable. readingNetworkInfo = false; // Clear the element being read. elementReading = ""; // Process only in the network page. if (!isNetworkShowing()) return; // Hide the loading panel. showLoadingPopup(false); // Process error. processAjaxErrorResponse(response); // Update controls. updateAllControls(); }); } // Processes the response of the read configuration request. function processReadConfigurationResponse(response, parseElement) { // Check if there was any error in the request. if (!checkErrorResponse(response, false)) { // Fill network info. fillNetworkInfo(JSON.parse(response[ID_DATA]), parseElement); } else { // Flag reading variable. readingNetworkInfo = false; // Hide the loading panel. showLoadingPopup(false); } } // Fills network information. function fillNetworkInfo(response, parseElement) { var numEthernetIfaces = 0; var numWifiIfaces = 0; for (const [element, elementData] of Object.entries(response)) { if (element != ELEMENT_ETHERNET && element != ELEMENT_WIFI) continue; for (const [iface, ifaceData] of Object.entries(elementData)) { if (!iface.startsWith(PREFIX_WIFI) && !iface.startsWith(PREFIX_ETHERNET)) continue; if (parseElement != ALL_ELEMENTS && iface != parseElement) continue; var enabled = ifaceData[ID_ENABLE]; var ipMode = ifaceData[ID_IP_MODE]; if (iface.startsWith(PREFIX_ETHERNET)) numEthernetIfaces += 1; else if (iface.startsWith(PREFIX_WIFI)) numWifiIfaces += 1; // Fill MAC. document.getElementById(iface + FIELD_MAC_ADDRESS).innerText = ifaceData[ID_MAC]; // Set enable state. document.getElementById(iface + FIELD_ENABLE).checked = enabled; // Set IP mode. if (ipMode == 0) document.getElementById(iface + FIELD_IP_MODE).value = IP_MODE_STATIC; else document.getElementById(iface + FIELD_IP_MODE).value = IP_MODE_DHCP; // Fill the rest of the fields. document.getElementById(iface + FIELD_IP_ADDRESS).value = ifaceData[ID_IP]; document.getElementById(iface + FIELD_SUBNET_MASK).value = ifaceData[ID_NETMASK]; document.getElementById(iface + FIELD_DEFAULT_GATEWAY).value = ifaceData[ID_GATEWAY]; document.getElementById(iface + FIELD_DNS1_ADDRESS).value = ifaceData[ID_DNS1]; document.getElementById(iface + FIELD_DNS2_ADDRESS).value = ifaceData[ID_DNS2]; // Specific Wifi fields. if (iface.startsWith(PREFIX_WIFI)) { document.getElementById(iface + FIELD_SSID).value = ifaceData[ID_SSID]; document.getElementById(iface + FIELD_ENCRYPTION_TYPE).selectedIndex = ifaceData[ID_SECURITY_MODE]; if (ifaceData[ID_SECURITY_MODE] != 0) document.getElementById(iface + FIELD_PASSWORD).value = "dummy_password"; passwordChanged = false; } // Update controls. updateInterfaceControls(iface); } } // Update interfaces visibility. if (parseElement == ALL_ELEMENTS) { if (numEthernetIfaces == 1) { document.getElementById(IFACE_ETH1).style.display = "none"; document.getElementById(IFACE_ETH2).style.display = "none"; document.getElementById(ID_ETH0_TITLE).innerHTML = "Ethernet"; } else if (numEthernetIfaces == 2) { document.getElementById(IFACE_ETH2).style.display = "none"; } if (numWifiIfaces == 0) document.getElementById(IFACE_WIFI).style.display = "none"; } // Flag reading variable. readingNetworkInfo = false; // Hide the loading panel. showLoadingPopup(false); } // Updates all controls of the page. function updateAllControls() { updateInterfaceControls(IFACE_ETH0); updateInterfaceControls(IFACE_ETH1); updateInterfaceControls(IFACE_ETH2); updateInterfaceControls(IFACE_WIFI); } // Updates the given interface controls based on its configuration. function updateInterfaceControls(iface) { // Initialize variables. var ipGroup = document.getElementById(iface + FIELD_IP_ADDRESS + FIELD_PARAM); var ipField = document.getElementById(iface + FIELD_IP_ADDRESS); var subnetGroup = document.getElementById(iface + FIELD_SUBNET_MASK + FIELD_PARAM); var subnetField = document.getElementById(iface + FIELD_SUBNET_MASK); var gatewayGroup = document.getElementById(iface + FIELD_DEFAULT_GATEWAY + FIELD_PARAM); var gatewayField = document.getElementById(iface + FIELD_DEFAULT_GATEWAY); var dns1Group = document.getElementById(iface + FIELD_DNS1_ADDRESS + FIELD_PARAM); var dns1Field = document.getElementById(iface + FIELD_DNS1_ADDRESS); var dns2Group = document.getElementById(iface + FIELD_DNS2_ADDRESS + FIELD_PARAM); var dns2Field = document.getElementById(iface + FIELD_DNS2_ADDRESS); var ipModeGroup = document.getElementById(iface + FIELD_IP_MODE + FIELD_PARAM); var ipModeField = document.getElementById(iface + FIELD_IP_MODE); var enableField = document.getElementById(iface + FIELD_ENABLE); var enabled = enableField.checked; var ipMode = ipModeField.value; var ssidGroup, encryptionTypeGroup, encryptionTypeField, passwordGroup, usePassword; if (iface.startsWith(PREFIX_WIFI)) { ssidGroup = document.getElementById(iface + FIELD_SSID + FIELD_PARAM); encryptionTypeGroup = document.getElementById(iface + FIELD_ENCRYPTION_TYPE + FIELD_PARAM); encryptionTypeField = document.getElementById(iface + FIELD_ENCRYPTION_TYPE); passwordGroup = document.getElementById(iface + FIELD_PASSWORD + FIELD_PARAM); usePassword = encryptionTypeField.value != 0; if (usePassword) passwordGroup.style.display = "block"; else passwordGroup.style.display = "none"; } // Determine if IP fields can be edited. if (ipMode == IP_MODE_DHCP) { if (!ipField.classList.contains(CLASS_INPUT_DISABLED)) ipField.classList.add(CLASS_INPUT_DISABLED); if (!subnetField.classList.contains(CLASS_INPUT_DISABLED)) subnetField.classList.add(CLASS_INPUT_DISABLED); if (!gatewayField.classList.contains(CLASS_INPUT_DISABLED)) gatewayField.classList.add(CLASS_INPUT_DISABLED); if (!dns1Field.classList.contains(CLASS_INPUT_DISABLED)) dns1Field.classList.add(CLASS_INPUT_DISABLED); if (!dns2Field.classList.contains(CLASS_INPUT_DISABLED)) dns2Field.classList.add(CLASS_INPUT_DISABLED); } else if (ipMode == IP_MODE_STATIC) { if (ipField.classList.contains(CLASS_INPUT_DISABLED)) ipField.classList.remove(CLASS_INPUT_DISABLED); if (subnetField.classList.contains(CLASS_INPUT_DISABLED)) subnetField.classList.remove(CLASS_INPUT_DISABLED); if (gatewayField.classList.contains(CLASS_INPUT_DISABLED)) gatewayField.classList.remove(CLASS_INPUT_DISABLED); if (dns1Field.classList.contains(CLASS_INPUT_DISABLED)) dns1Field.classList.remove(CLASS_INPUT_DISABLED); if (dns2Field.classList.contains(CLASS_INPUT_DISABLED)) dns2Field.classList.remove(CLASS_INPUT_DISABLED); } // Validate the network interface. validateInterface(iface); } // Validates the given network interface. function validateInterface(iface) { // Initialize vars. var valid = true; var saveButton = document.getElementById(iface + FIELD_SAVE_BUTTON); var enableField = document.getElementById(iface + FIELD_ENABLE); var ipModeField = document.getElementById(iface + FIELD_IP_MODE); var ipField = document.getElementById(iface + FIELD_IP_ADDRESS); var subnetField = document.getElementById(iface + FIELD_SUBNET_MASK); var gatewayField = document.getElementById(iface + FIELD_DEFAULT_GATEWAY); var dns1Field = document.getElementById(iface + FIELD_DNS1_ADDRESS); var dns2Field = document.getElementById(iface + FIELD_DNS2_ADDRESS); var ipMode = ipModeField.value; var forceValid = false; // Validate fields. if (ipMode == IP_MODE_DHCP) forceValid = true; valid &= validateIPField(iface + FIELD_IP_ADDRESS, iface + FIELD_IP_ADDRESS + FIELD_ERROR, forceValid); valid &= validateIPField(iface + FIELD_SUBNET_MASK, iface + FIELD_SUBNET_MASK + FIELD_ERROR, forceValid); valid &= validateIPField(iface + FIELD_DEFAULT_GATEWAY, iface + FIELD_DEFAULT_GATEWAY + FIELD_ERROR, forceValid); valid &= validateIPField(iface + FIELD_DNS1_ADDRESS, iface + FIELD_DNS1_ADDRESS + FIELD_ERROR, forceValid); valid &= validateIPField(iface + FIELD_DNS2_ADDRESS, iface + FIELD_DNS2_ADDRESS + FIELD_ERROR, forceValid); if (iface.startsWith(PREFIX_WIFI)) { valid &= validateField(iface + FIELD_SSID, iface + FIELD_SSID + FIELD_ERROR, REGEX_SSID, ERROR_SSID_INVALID, false); if (parseInt(document.getElementById(iface + FIELD_ENCRYPTION_TYPE).value) != 0) valid &= validateField(iface + FIELD_PASSWORD, iface + FIELD_PASSWORD + FIELD_ERROR, REGEX_PASSWORD, ERROR_PASSWORD_INVALID, false); } // Check errors. if (!valid) { if (!saveButton.classList.contains(CLASS_CONFIG_BUTTON_DISABLED)) saveButton.classList.add(CLASS_CONFIG_BUTTON_DISABLED); } else { if (saveButton.classList.contains(CLASS_CONFIG_BUTTON_DISABLED)) saveButton.classList.remove(CLASS_CONFIG_BUTTON_DISABLED); } } // Validates the given IP field. function validateIPField(fieldID, errorID, forceValid) { return validateField(fieldID, errorID, REGEX_IP, ERROR_IP_VALUE_FORMAT, forceValid); } // Validates the given field. function validateField(fieldID, errorID, regexPattern, errorMessage, forceValid) { // Initialize vars. var field = document.getElementById(fieldID); var fieldError = document.getElementById(errorID); var isValid = true; var error = ""; // Sanity checks. if (field == null || fieldError == null) return false; // Check if value is valid. if (!forceValid) { var value = field.value; if (value.length == 0) { isValid = false; error = ERROR_FIELD_EMPTY; } else if (!value.match(regexPattern)) { isValid = false; error = errorMessage; } } // Update controls. if (isValid) { if (field.classList.contains(CLASS_INPUT_ERROR)) field.classList.remove(CLASS_INPUT_ERROR); fieldError.innerHTML = " "; fieldError.style.display = "none"; } else { if (!field.classList.contains(CLASS_INPUT_ERROR)) field.classList.add(CLASS_INPUT_ERROR); fieldError.innerHTML = error; fieldError.style.display = "block"; } return isValid; } // Retrieves the given network interface form data. function getInterfaceData(element, iface) { // Initialize variables. var data = {}; var ifaceData = {}; var networkData = {}; var enable = document.getElementById(iface + FIELD_ENABLE).checked; var ipMode = document.getElementById(iface + FIELD_IP_MODE).value; // Fill network data. networkData[ID_ENABLE] = enable; if (ipMode == IP_MODE_STATIC) { networkData[ID_IP_MODE] = 0; networkData[ID_IP] = document.getElementById(iface + FIELD_IP_ADDRESS).value; networkData[ID_NETMASK] = document.getElementById(iface + FIELD_SUBNET_MASK).value; networkData[ID_GATEWAY] = document.getElementById(iface + FIELD_DEFAULT_GATEWAY).value; networkData[ID_DNS1] = document.getElementById(iface + FIELD_DNS1_ADDRESS).value; networkData[ID_DNS2] = document.getElementById(iface + FIELD_DNS2_ADDRESS).value; } else { networkData[ID_IP_MODE] = 1; } if (iface.startsWith(PREFIX_WIFI)) { networkData[ID_SSID] = document.getElementById(iface + FIELD_SSID).value; networkData[ID_SECURITY_MODE] = parseInt(document.getElementById(iface + FIELD_ENCRYPTION_TYPE).value); if (networkData[ID_SECURITY_MODE] != 0 && passwordChanged) networkData[ID_PASSWORD] = document.getElementById(iface + FIELD_PASSWORD).value; } ifaceData[iface] = networkData; data[element] = ifaceData; return data; } // Saves the network settings for the given interface. function saveInterface(element, iface) { // Execute only in the network page. if (!isNetworkShowing()) return; // Hide the info popup. showInfoPopup(false); // Show the loading popup. showLoadingPopup(true, MESSAGE_SAVING_CONFIGURATION); // Send request to set new configuration. $.post( "http://" + getServerAddress() + "/ajax/set_config", JSON.stringify({ "configuration": getInterfaceData(element, iface) }), function(data) { // Process only in the network page. if (!isNetworkShowing()) return; // Process answer. processSaveInterfaceResponse(data, element, iface); } ).fail(function(response) { // Process only in the network page. if (!isNetworkShowing()) return; // Hide the loading panel. showLoadingPopup(false); // Process error. processAjaxErrorResponse(response); }); } // Processes the save network request response. function processSaveInterfaceResponse(response, element, iface) { // Check for error in the response. if (!checkErrorResponse(response, false)) { var statusFound = false; // Check status in the response. if (response[ID_DATA] != null) { data = JSON.parse(response[ID_DATA]); for (const [elementEntry, elementData] of Object.entries(data)) { if (elementEntry != element) continue; for (const [ifaceEntry, ifaceData] of Object.entries(elementData)) { if (ifaceEntry != iface) continue; if (ifaceData[ID_STATUS] != null) { statusFound = true; if (ifaceData[ID_STATUS] != 0) toastr.error(ifaceData[ID_DESC]); else toastr.success(MESSAGE_CONFIGURATION_SAVED); } } } } if (!statusFound) toastr.error(ERROR_UNKNOWN); } // Hide the loading panel. showLoadingPopup(false); } // Toggles the visibility of the given interface panel. function togglePanelVisibility(interface) { // Initialize variables. var panelButton = document.getElementById(interface + ID_TOGGLE_BUTTON); // Sanity checks. if (panelButton == null) return; // Check visibility. if (panelButton.classList.contains(CLASS_ARROW_UP)) { $("#" + interface + ID_PANEL_CONTAINER).slideUp("fast", function() { panelButton.classList.remove(CLASS_ARROW_UP); panelButton.classList.add(CLASS_ARROW_DOWN); }); } else { $("#" + interface + ID_PANEL_CONTAINER).slideDown("fast", function() { panelButton.classList.remove(CLASS_ARROW_DOWN); panelButton.classList.add(CLASS_ARROW_UP); }); } }