| // Copyright (c) 2011 The Chromium OS Authors. All rights reserved. |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| /** |
| * @fileoverview This file contains implementation for the main.html page. |
| */ |
| |
| /** |
| * Array of tabs presented in the UI. |
| */ |
| const tabs = ['wifi', 'vpn', 'cert', 'load', 'save']; |
| |
| /** |
| * List of certificates objects (X.509 and PKCS12) that have been imported. |
| * Each object has: |
| * x509: base64 encoded contents of x509 certificate |
| * guid: GUID for certificate |
| * subject: interpreted X.520 name of certificate. |
| */ |
| var certList = []; |
| |
| /** |
| * Namespace for all functions in main.js. |
| */ |
| var main = {}; |
| |
| /** |
| * Sets the tab identified by the given ID prefix as the active tab. |
| * @param {String} idPrefix HTML ID prefix of menu item and tab (ie. 'wifi'). |
| */ |
| main.setTabByIdPrefix = function(idPrefix) { |
| for (var i = 0; i < tabs.length; ++i) { |
| tabDoms = $('#' + tabs[i] + '-pane'); |
| itemDoms = $('#' + tabs[i] + '-item'); |
| if (idPrefix == tabs[i]) { |
| itemDoms.addClass('selected'); |
| tabDoms.show(); |
| if (tabs[i] == 'save') |
| main.updateSaveLink(); |
| } else { |
| itemDoms.removeClass('selected'); |
| tabDoms.hide(); |
| } |
| } |
| }; |
| |
| /** |
| * Return the currently selected option's i18n tag. |
| * @param {String} selector jquery selector for the option. |
| * @returns {String} i18n tag of current selected or '' if none selected. |
| */ |
| main.getSelectedI18n = function(selector) { |
| var dom = $(selector)[0]; |
| var selectedIndex = dom.selectedIndex; |
| if (selectedIndex < 0) |
| return ''; |
| return dom.options[selectedIndex].getAttribute('i18n'); |
| } |
| |
| /** |
| * Sets the current option based on i18n tag value. If no tag exists or |
| * none matches, make no changes. |
| * @param {String} selector jquery selector for the option. |
| * @param {String} i18n Value to set |
| */ |
| main.setSelectedI18n = function(selector, i18n) { |
| var dom = $(selector)[0]; |
| var toSelect = -1; |
| for (var i = 0; i < dom.options.length; ++i) { |
| if (dom.options[i].getAttribute('i18n') == i18n) { |
| toSelect = i; |
| break; |
| } |
| } |
| if (toSelect >= 0) |
| dom.selectedIndex = toSelect; |
| } |
| |
| /** |
| * Based on current Wi-Fi security setting, set visible configuration. |
| */ |
| main.setWifiSecurityVisible = function() { |
| var security = $('#security').val(); |
| if (security == 'WEP' || security == 'WPA' || |
| security == 'WPA2') { |
| $('#passphrase-div').show(); |
| } else { |
| $('#passphrase-div').hide(); |
| } |
| if (security == 'WPA2Enterprise') { |
| $('#8021x-div').show(); |
| } else { |
| $('#8021x-div').hide(); |
| } |
| }; |
| |
| /** |
| * Based on current EAP settings, return if username and password is required |
| * for connection. |
| */ |
| main.wifiRequiresUsernamePassword = function() { |
| var security = $('#eap').val(); |
| return (security == 'EAP-TTLS' || security == 'PEAP' || security == 'LEAP'); |
| }; |
| |
| /** |
| * Based on current EAP settings, return if an inner authentication protocol |
| * method setting is required. |
| */ |
| main.wifiRequiresPhase2Method = function() { |
| var security = $('#eap').val(); |
| return security == 'EAP-TTLS' || security == 'PEAP'; |
| }; |
| |
| /** |
| * Based on current EAP settings, return if a server certificate check is |
| * required. |
| */ |
| main.wifiRequiresServerCertificate = function() { |
| var security = $('#eap').val(); |
| return (security == 'EAP-TTLS' || security == 'PEAP' || |
| security == 'EAP-TLS'); |
| }; |
| |
| /** |
| * Based on current EAP settings, return if a client certificate check is |
| * required. |
| */ |
| main.wifiRequiresClientCertficate = function() { |
| return $('#eap').val() == 'EAP-TLS'; |
| }; |
| |
| /** |
| * Based on current EAP setting, set visible configuration. |
| */ |
| main.setEAPVisible = function() { |
| if (main.wifiRequiresUsernamePassword()) { |
| $('#phase2-auth').show(); |
| } else { |
| $('#phase2-auth').hide(); |
| } |
| |
| if (main.wifiRequiresPhase2Method()) { |
| $('#phase2-div').show(); |
| } else { |
| $('#phase2-div').hide(); |
| } |
| |
| if (main.wifiRequiresServerCertificate()) { |
| $('#eap-server-ca').show(); |
| } else { |
| $('#eap-server-ca').hide(); |
| } |
| |
| if (main.wifiRequiresClientCertficate()) { |
| $('#eap-client-cert').show(); |
| } else { |
| $('#eap-client-cert').hide(); |
| } |
| }; |
| |
| /** |
| * Based on current VPN setting, set visible configuration. |
| */ |
| main.setVPNTypeVisible = function() { |
| var setting = $('#vpn-type').val(); |
| var save = $('#l2tp-specify-credentials').is(':checked'); |
| if (setting == 'L2TP-IPsec-PSK') { |
| $('#l2tpipsec-psk-div').show(); |
| $('#l2tpipsec-cert-div').hide(); |
| } else { |
| $('#l2tpipsec-psk-div').hide(); |
| $('#l2tpipsec-cert-div').show(); |
| } |
| if (save) { |
| $('#l2tp-cred').show(); |
| } else { |
| $('#l2tp-cred').hide(); |
| } |
| }; |
| |
| /** |
| * Based on current setting of Save Credentials, set visible configuration. |
| */ |
| main.setCredentialsVisible = function() { |
| if ($('#specify-credentials').is(':checked')) |
| $('#phase2-auth-cred').show(); |
| else |
| $('#phase2-auth-cred').hide(); |
| }; |
| |
| /** |
| * Create GUID. |
| * @return {String} Returns a GUID string of format |
| * {XXXXXXXX-XXXX-XXXX-XXXXXXXXXXXXXXXX}. |
| */ |
| main.createGuid = function() { |
| var guidData = new Uint8Array(16); |
| crypto.getRandomValues(guidData); |
| var intervals = [ 4, 2, 2, 8 ]; |
| var guid = '{'; |
| var offset = 0; |
| for (var i = 0; i < intervals.length; ++i) { |
| if (i > 0) |
| guid += '-'; |
| for (var j = 0; j < intervals[i]; ++j) { |
| var hex = guidData[offset].toString(16); |
| if (hex.length == 1) |
| guid += '0'; |
| guid += hex; |
| ++offset; |
| } |
| } |
| guid += '}'; |
| return guid; |
| }; |
| |
| main.toHex = function(str) { |
| var result = ''; |
| for (var i = 0; i < str.length; i++) { |
| var byte = str.charCodeAt(i).toString(16); |
| if (byte.length == 1) |
| byte = '0' + byte; |
| result += byte; |
| } |
| return result; |
| }; |
| |
| main.isAllHex = function(str) { |
| var allHex = true; |
| var validHexChars = ['a', 'b', 'c', 'd', 'e', 'f', |
| '0', '1', '2', '3', '4', '5', |
| '6', '7', '8', '9']; |
| var lowercaseString = str.toLowerCase(); |
| for (var i = 0; i < str.length; i++) { |
| if (validHexChars.indexOf(lowercaseString[i]) == -1) { |
| allHex = false; |
| break; |
| } |
| } |
| return allHex; |
| }; |
| |
| main.checkNetworkValidity = function(network, result) { |
| if (!network.Name) { |
| result.warnings.push(['warningMissingNetworkName', network.Type]); |
| } |
| if (network.Type == 'WiFi' && 'WiFi' in network) { |
| if (!('SSID' in network.WiFi)) |
| result.warnings.push(['warningMissingSSIDName', network.Type, |
| network.Name]); |
| if (network.WiFi.Security == 'WEP') { |
| result.warnings.push(['warningWEPInherentlyUnsafe', network.Name]); |
| } |
| if (network.WiFi.Security == 'WEP' && |
| 'Passphrase' in network.WiFi) { |
| // 5/13/16/29 characters are needed for 64/128/152/256-bit WEP ascii keys |
| // 10/26/32/58 characters are needed for 64/128/152/256-bit WEP hex keys |
| // Note that the actual bits supplied here are only |
| // 40/104/128/232 bits, respectively, but WEP adds some |
| // randomness to make up the rest of the bits. |
| var asciiLengths = [5, 13, 16, 29]; |
| var hexLengths = [10, 26, 32, 58]; |
| if (asciiLengths.indexOf(network.WiFi.Passphrase.length) != -1) { |
| // Always store the passphrase as hex, to avoid possible |
| // encoding issues. |
| network.WiFi.Passphrase = |
| '0x' + main.toHex(network.WiFi.Passphrase.toLowerCase()); |
| } else if (hexLengths.indexOf(network.WiFi.Passphrase.length) != -1) { |
| if (!main.isAllHex(network.WiFi.Passphrase)) |
| result.errors.push(['errorWEPKeyNonHexCharacters', |
| network.WiFi.Passphrase, network.Name]); |
| network.WiFi.Passphrase = '0x' + network.WiFi.Passphrase.toLowerCase(); |
| } else { |
| result.errors.push(['errorWEPKeyInvalidLength', |
| network.Name, |
| network.WiFi.Passphrase.length]); |
| } |
| } |
| if ((network.WiFi.Security == 'WPA' || |
| network.WiFi.Security == 'WPA2') && |
| 'Passphrase' in network.WiFi && |
| network.WiFi.Passphrase.length < 8) { |
| result.warnings.push(['warningShortWPAPassphraseUnsafe', |
| network.WiFi.Security, |
| network.Name, |
| network.WiFi.Passphrase.length]); |
| } |
| if ('EAP' in network.WiFi) { |
| // Only check if we have password and identity for non-TLS |
| // protocols, since that uses a certificate instead. |
| if (network.WiFi.EAP.Outer != 'EAP-TLS') { |
| // We only check to see if one is specified without the other: |
| // if neither are specified, then we assume that the user |
| // didn't want to supply any credentials. If both, then we |
| // use them. |
| if ('Identity' in network.WiFi.EAP) { |
| if (network.WiFi.EAP.Identity.length == 0) |
| result.errors.push(['errorEAPIdentityEmpty', network.Name]); |
| if (!('Password' in network.WiFi.EAP)) |
| result.errors.push(['errorMissingEAPPassword', |
| network.WiFi.EAP.Identity, network.Name]); |
| } |
| if ('Password' in network.WiFi.EAP && !('Identity' in network.WiFi.EAP)) |
| result.errors.push(['errorMissingEAPIdentity', network.Name]); |
| } |
| } |
| } |
| if (network.Type == 'VPN') { |
| if (network.VPN.L2TPIPsec.Password && !network.VPN.L2TPIPsec.Username) { |
| result.errors.push(['errorMissingVPNIdentity', network.Name]); |
| } |
| if (network.VPN.Type == 'L2TP-IPsec-PSK' && !network.VPN.L2TPIPsec.PSK) { |
| result.errors.push(['errorMissingVPNPreSharedKey', |
| network.VPN.Type, network.Name]); |
| } |
| } |
| return result; |
| } |
| |
| /** |
| * Save all the configuration currently specified in the UI. |
| */ |
| main.saveConfig = function() { |
| var config = {}; |
| var result = { 'errors': [], 'warnings': [] }; |
| config.NetworkConfigurations = []; |
| var network = { |
| 'GUID': $('#wifi-guid').val(), |
| 'Name': $('#ssid').val(), // at least for today... |
| 'Type': 'WiFi', |
| 'WiFi': { |
| 'SSID': $('#ssid').val(), |
| 'HiddenSSID': $('#hidden-ssid').is(':checked') != false |
| } |
| }; |
| if ($('#wifi-proxy-url').val()) |
| network.ProxyURL = $('#wifi-proxy-url').val(); |
| network.WiFi.Security = $('#security').val(); |
| switch (network.WiFi.Security) { |
| case 'WEP': |
| case 'WPA': |
| case 'WPA2': |
| network.WiFi.Passphrase = $('#passphrase').val(); |
| break; |
| case 'WPA2Enterprise': |
| network.WiFi.Security = 'WPA2'; |
| network.WiFi.EAP = {}; |
| network.WiFi.EAP.Outer = $('#eap').val(); |
| network.WiFi.EAP.UseSystemCAs = $('#wifi-server-ca').val() != 'ignore'; |
| if ($('#specify-credentials').is(':checked')) { |
| network.WiFi.EAP.Identity = $('#wifi-identity').val(); |
| network.WiFi.EAP.Password = $('#wifi-password').val(); |
| } |
| if (main.wifiRequiresServerCertificate()) { |
| if ($('#wifi-server-ca').val() != 'empty') { |
| network.WiFi.EAP.ServerCARef = $('#wifi-server-ca').val(); |
| } else { |
| result.errors.push(['errorMissingServerCert', |
| network.Name, network.Type]); |
| } |
| } |
| if (main.wifiRequiresClientCertficate()) { |
| network.WiFi.EAP.ClientCertPattern = {}; |
| if ($('#wifi-client-ca').val() != 'empty') { |
| network.WiFi.EAP.ClientCertPattern.IssuerRef = |
| $('#wifi-client-ca').val(); |
| } else { |
| result.errors.push(['errorMissingClientCert', |
| network.Name, network.Type]); |
| } |
| network.WiFi.EAP.ClientCertPattern.EnrollmentUri = |
| $('#wifi-enrollment-uri').val(); |
| } |
| break; |
| } |
| network.WiFi.AutoConnect = $('#auto-connect').is(':checked'); |
| result = main.checkNetworkValidity(network, result); |
| config.NetworkConfigurations.push(network); |
| // Save VPN settings. |
| saveCredentials = $('#l2tp-specify-credentials').is(':checked'); |
| network = { |
| 'GUID': $('#vpn-guid').val(), |
| 'Name': $('#vpn-host').val(), |
| 'Type': 'VPN', |
| 'VPN': { |
| 'Type': $('#vpn-type').val(), |
| 'Host': $('#vpn-host').val(), |
| 'L2TPIPsec' : {} |
| } |
| }; |
| if ($('#vpn-proxy-url').val()) |
| network.ProxyURL = $('#vpn-proxy-url').val(); |
| if (saveCredentials) { |
| network.VPN.L2TPIPsec.Username = $('#vpn-username').val(); |
| network.VPN.L2TPIPsec.Password = $('#vpn-password').val(); |
| } |
| if ($('#vpn-type').val() == 'L2TP-IPsec-cert') { |
| var serverCa = $('#ipsec-server-ca').val(); |
| if (serverCa != 'empty') { |
| network.VPN.L2TPIPsec.ServerCARef = serverCa; |
| } else { |
| result.errors.push(['errorMissingServerCert', |
| network.Name, network.Type]); |
| } |
| var clientCa = $('#ipsec-client-ca').val(); |
| if (clientCa != 'empty') { |
| network.VPN.L2TPIPsec.ClientCertPattern = { |
| 'IssuerRef': clientCa |
| }; |
| } |
| } |
| if ($('#vpn-type').val() == 'L2TP-IPsec-PSK') { |
| if ($('#ipsec-psk').val()) |
| network.VPN.L2TPIPsec.PSK = $('#ipsec-psk').val(); |
| } |
| result = main.checkNetworkValidity(network, result); |
| config.NetworkConfigurations.push(network); |
| if (certList.length > 0) { |
| config.Certificates = []; |
| for (var i = 0; i < certList.length; ++i) { |
| var cert = certList[i]; |
| config.Certificates.push({'GUID': cert.guid, 'X509': cert.x509}); |
| } |
| } |
| try { |
| result.config = JSON.stringify(config); |
| } |
| catch (e) { |
| result.errors.push(['errorJSONStringify', e]); |
| } |
| return result; |
| }; |
| |
| /** |
| * Convert an array of numbers to the equivalent Uint8Array. |
| * @param {Array.<Number>} array Array of numbers to convert. |
| * @return {Uint8Array} Equivalent Uint8Array. |
| */ |
| main.arrayToUint8Array = function(array) { |
| var uint8array = new Uint8Array(array.length); |
| for (var i = 0; i < array.length; ++i) { |
| uint8array[i] = array[i]; |
| } |
| return uint8array; |
| }; |
| |
| /** |
| * Converts the given message list into localized HTML to display. |
| * @param {Array.<Array.<String>>} messageList array of messages to |
| * be converted. |
| */ |
| main.convertMessagesToHtml = function(messageList) { |
| messages = []; |
| for (var i = 0; i < messageList.length; i++) { |
| var message = messageList[i]; |
| if (message.length > 1) { |
| messages.push(chrome.i18n.getMessage(message[0], message.slice(1))); |
| } else { |
| messages.push(chrome.i18n.getMessage(message[0])); |
| } |
| } |
| return '<p>' + messages.join('</p><p>') + '<\p>'; |
| } |
| |
| /** |
| * Update the save button to the current UI configuration. |
| */ |
| main.updateSaveLink = function() { |
| var saveLink = $('#save-link'); |
| var saveLinkDiv = $('#save-link-div'); |
| saveLinkDiv.hide(); // In case something fails. |
| var configResult = main.saveConfig(); |
| if (configResult.errors.length == 0) { |
| $('#save-errors-header').html(chrome.i18n.getMessage('saveSucceeded')); |
| } else { |
| $('#save-errors-header').html(''); |
| } |
| $('#save-errors').html(main.convertMessagesToHtml(configResult.errors)); |
| $('#save-warnings').html(main.convertMessagesToHtml(configResult.warnings)); |
| if (configResult.errors.length == 0) { |
| var configArray = configResult.config.split('').map(function(c) { |
| return c.charCodeAt(0); |
| }); |
| config = Base64.encode(main.arrayToUint8Array(configArray)); |
| saveLinkDiv.show(); |
| saveLink.attr('href', 'data:application/octet-stream;base64,' + config); |
| } else { |
| saveLinkDiv.hide(); |
| saveLink.attr('href', 'about:blank'); |
| } |
| }; |
| |
| /** |
| * Update an option list with the currently loaded certificates. |
| * @param {Object} optionList DOM option list to update. |
| * @param {Boolean} clearFirst Clear the option list before adding. |
| */ |
| main.addCertificates = function(optionList, clearFirst) { |
| if (clearFirst) optionList.options.length = 0; |
| if (certList.length == 0 && optionList.options.length == 0) { |
| optionList.options.add(new Option( |
| chrome.i18n.getMessage('certificateEmpty'), 'empty')); |
| optionList.disabled = true; |
| } else { |
| optionList.disabled = false; |
| for (var i = 0; i < certList.length; ++i) { |
| optionList.options.add(new Option(certList[i].subject.organizationName + |
| ' / ' + certList[i].subject.commonName, |
| certList[i].guid)); |
| } |
| } |
| }; |
| |
| /** |
| * Find a cert in the cert list by GUID. |
| */ |
| main.findCertInList = function(guid) { |
| for (var i = 0; i < certList.length; ++i) { |
| if (certList[i].guid == guid) |
| return certList[i]; |
| } |
| return null; |
| } |
| |
| |
| /** |
| * Update the certificate lists across all tabs. |
| */ |
| main.updateCertLists = function() { |
| var certListDom = document.getElementById('cert-list'); |
| certListDom.innerHTML = ''; |
| for (var i = 0; i < certList.length; ++i) { |
| var cert = certList[i]; |
| certListDom.innerHTML += (cert.subject.organizationName + ' / ' + |
| cert.subject.commonName + ', ' + |
| cert.guid + '<br>'); |
| } |
| var serverCaDom = $('#wifi-server-ca')[0]; |
| serverCaDom.options.length = 0; |
| serverCaDom.options.add(new Option( |
| chrome.i18n.getMessage('useAnyDefaultCA'), 'default')); |
| main.addCertificates(serverCaDom, false); |
| serverCaDom.options.add(new Option( |
| chrome.i18n.getMessage('doNotCheckCA'), 'ignore')); |
| main.addCertificates($('#wifi-client-ca')[0], true); |
| main.addCertificates($('#ipsec-server-ca')[0], true); |
| main.addCertificates($('#ipsec-client-ca')[0], true); |
| }; |
| |
| /** |
| * Handle a drag and drop file list. |
| * @param {Array.<Object>} files drag and drop file list. |
| */ |
| main.handleCertFileList = function(files) { |
| $('#cert-errors').html(''); |
| for (var i = 0; i < files.length; ++i) { |
| var file = files[i]; |
| var reader = new FileReader(); |
| reader.onload = function(theFile) { |
| var asn1Data; |
| var derUint8; |
| // TODO: Handle or deprecate PKCS12 loading (in which case |
| // we load PKCS8). |
| if (file.name.match(/\.pem$/)) { |
| var der = Base64.unarmor(this.result); |
| asn1Data = asn1.parseAsn1(der); |
| derUint8 = main.arrayToUint8Array(der); |
| } else if (file.name.match(/\.der/)) { |
| var der = this.result; |
| // TODO: This is currently broken |
| asn1Data = asn1.parseAsn1(this.result); |
| } else { |
| $('#cert-errors').html(chrome.i18n.getMessage('fileNotSupported', |
| [file.name])); |
| return; |
| } |
| if (!asn1Data) { |
| $('#cert-errors').html(chrome.i18n.getMessage('fileNotSupported', |
| [file.name])); |
| } |
| var cert = asn1.interpretCert(asn1Data); |
| if (!cert) { |
| $('#cert-errors').html(chrome.i18n.getMessage('fileNotSupported', |
| [file.name])); |
| return; |
| } |
| certList.push({ |
| 'x509': Base64.encode(derUint8), |
| 'guid': main.createGuid(), |
| 'subject': cert.subject |
| }); |
| main.updateCertLists(); |
| }; |
| reader.readAsBinaryString(file); |
| } |
| return false; |
| }; |
| |
| /** |
| * Clear out the certificates in the certificate list. |
| */ |
| main.handleCertFileListClear = function() { |
| $('#cert-errors').html(''); |
| certList = []; |
| main.updateCertLists(); |
| } |
| |
| /** |
| * Configure the given DOM id as a drag and drop target for certificates. |
| * @param {String} id DOM id. |
| */ |
| main.configureDragDropTarget = function(id) { |
| function cancel(event) { |
| if (event.preventDefault) |
| event.preventDefault(); |
| return false; |
| } |
| var drop = $('#' + id)[0]; |
| drop.addEventListener('dragover', cancel, false); |
| drop.addEventListener('dragenter', cancel, false); |
| drop.addEventListener('drop', function(event) { |
| main.setTabByIdPrefix('cert'); |
| main.handleCertFileList(event.dataTransfer.files); |
| }, false); |
| }; |
| |
| /** |
| * Configure the certificate file picker. |
| */ |
| main.configureCertFilePicker = function() { |
| $('#cert-files').change(function(event) { |
| main.handleCertFileList(event.target.files); |
| }); |
| $('#cert-clear').click(function(event) { |
| main.handleCertFileListClear(); |
| }); |
| }; |
| |
| /** |
| * Update visibility of all form fields based on current settings. |
| */ |
| main.updateAllVisibility = function() { |
| main.setCredentialsVisible(); |
| main.setEAPVisible(); |
| main.setWifiSecurityVisible(); |
| main.setVPNTypeVisible(); |
| } |
| |
| /** |
| * Reset the entire UI and internal data structures to the original state |
| * to prepare for a load. |
| */ |
| main.resetUI = function() { |
| $('#load-header').html(''); |
| $('#load-errors').html(''); |
| $('#load-warnings').html(''); |
| $('#save-errors').html(''); |
| $('#save-errors-header').html(''); |
| $('#save-warnings').html(''); |
| $('#ssid').val(''); |
| $('#hidden-ssid')[0].checked = false; |
| $('#auto-connect')[0].checked = false; |
| main.setSelectedI18n('#security', 'securityNone'); |
| $('#passphrase').val(''); |
| $('#wifi-proxy-url').val(''); |
| main.setSelectedI18n('#eap', 'acronymPeap'); |
| main.setSelectedI18n('#phase2', 'automatic'); |
| $('#l2tp-specify-credentials')[0].checked = false; |
| $('#specify-credentials')[0].checked = false; |
| $('#username').val(''); |
| $('#password').val(''); |
| $('#wifi-server-ca').val(''); |
| $('#wifi-client-ca').val(''); |
| $('#wifi-guid').val(main.createGuid()); |
| $('#ipsec-enrollment-uri').val(''); |
| $('#vpn-guid').val(main.createGuid()); |
| main.updateAllVisibility(); |
| certList = []; |
| main.updateCertLists(); |
| }; |
| |
| /** |
| * Load the given ONC configuration and show any errors in errorDom. |
| * @param {String} config ONC formatted string. |
| */ |
| main.loadConfig = function(config) { |
| var result = { 'errors': [], 'warnings': [] }; |
| main.resetUI(); |
| try { |
| config = JSON.parse(config); |
| } catch(e) { |
| result.errors.push(['errorDuringLoad', e.toString()]); |
| return result; |
| } |
| if ('Certificates' in config) { |
| var certificates = config.Certificates; |
| for (var i = 0; i < certificates.length; ++i) { |
| var certificate = certificates[i]; |
| if (!('GUID' in certificate)) { |
| $('#load-errors').html('Certificate missing ' + key); |
| continue; |
| } |
| if ('X509' in certificate) { |
| var der = Base64.unarmor(certificate.X509); |
| var asn1Data = asn1.parseAsn1(der); |
| if (!asn1Data) { |
| main.setLoadError('Certificate with GUID ' + certificate.GUID + |
| ' is invalid.'); |
| continue; |
| } |
| var cert = asn1.interpretCert(asn1Data); |
| if (!asn1Data) { |
| main.setLoadError('Certificate with GUID ' + certificate.GUID + |
| ' is invalid.'); |
| } |
| certList.push({ |
| 'x509': certificate.X509, |
| 'guid': main.createGuid(), |
| 'subject': cert.subject |
| }); |
| } else if ('PKCS12' in certificate) { |
| // TODO: support or deprecate PKCS12. |
| main.setLoadError('PKCS12 certificates not yet supported'); |
| } else { |
| main.setLoadError('Certificate must have X509 or PKCS12 field'); |
| continue; |
| } |
| } |
| main.updateCertLists(); |
| } |
| if ('NetworkConfigurations' in config) { |
| var netConfigs = config.NetworkConfigurations; |
| var requiredNetworkConfigurationKeys = [ 'GUID', 'Type' ]; |
| networkConfigurationsLoop: |
| for (var i = 0; i < netConfigs.length; ++i) { |
| var netConfig = netConfigs[i]; |
| for (var j = 0; j < requiredNetworkConfigurationKeys.length; ++j) { |
| var key = requiredNetworkConfigurationKeys[j]; |
| if (!(key in netConfig)) { |
| result.errors.push(['errorLoadRequiredObjectMissing', key]); |
| continue networkConfigurationsLoop; |
| } |
| } |
| if (netConfig.Type == 'WiFi') { |
| if (!('WiFi' in netConfig)) { |
| result.errors.push(['errorLoadRequiredObjectMissing', 'WiFi']); |
| continue networkConfigurationsLoop; |
| } |
| var wifiConfig = netConfig.WiFi; |
| if (!('SSID' in wifiConfig)) { |
| result.errors.push(['errorLoadRequiredObjectMissing', 'SSID']); |
| continue networkConfigurationsLoop; |
| } |
| if (!('Security' in wifiConfig)) { |
| result.errors.push(['errorLoadRequiredObjectMissing', 'Security']); |
| continue networkConfigurationsLoop; |
| } |
| $('#wifi-guid').val(netConfig.GUID); |
| $('#ssid').val(wifiConfig.SSID); |
| if ('AutoConnect' in wifiConfig) |
| $('#auto-connect').val(wifiConfig.AutoConnect != false); |
| if ('HiddenSSID' in wifiConfig) |
| $('#hidden-ssid').val(wifiConfig.HiddenSSID != false); |
| if ('Passphrase' in wifiConfig) { |
| // Strip off any '0x' from hex passphrases. We'll correctly |
| // interpret it as a hex passphrase when we save and add the |
| // '0x' back on. |
| if (wifiConfig.Security == 'WEP' && |
| wifiConfig.Passphrase.substr(0,2) == '0x') |
| wifiConfig.Passphrase = wifiConfig.Passphrase.substr(2); |
| $('#passphrase').val(wifiConfig.Passphrase); |
| } |
| switch (wifiConfig.Security) { |
| case 'WEP': |
| case 'WPA': |
| case 'WPA2': |
| $('#security').val(wifiConfig.Security); |
| break; |
| default: |
| result.errors.push(['errorLoadUnhandledSecurityType', |
| wifiConfig.Security, netConfig.Name]); |
| continue networkConfigurationsLoop; |
| } |
| if ('EAP' in netConfig.WiFi && wifiConfig.Security == 'WPA2') { |
| $('#security').val('WPA2Enterprise'); |
| // TODO: potentially handle Dynamic WEP / 802.1X. |
| var eapConfig = netConfig.WiFi.EAP; |
| if (!('Outer' in eapConfig)) { |
| result.errors.push(['errorLoadRequiredObjectMissing', |
| 'WiFi.EAP.Outer']); |
| continue networkConfigurationsLoop; |
| } |
| switch (eapConfig.Outer) { |
| case 'PEAP': |
| case 'EAP-TTLS': |
| case 'EAP-TLS': |
| case 'LEAP': |
| $('#eap').val(eapConfig.Outer); |
| break; |
| default: |
| result.errors.push(['errorLoadUnhandledEapType', |
| eapConfig.Outer, netConfig.Name]); |
| continue networkConfigurationsLoop; |
| } |
| if ('Identity' in eapConfig) { |
| $('#wifi-identity').val(eapConfig.Identity); |
| } |
| if ('Password' in eapConfig) { |
| $('#wifi-password').val(eapConfig.Password); |
| } |
| if ('Identity' in eapConfig || 'Password' in eapConfig) |
| $('#specify-credentials')[0].checked = true; |
| if ('UseSystemCAs' in eapConfig && !eapConfig.UseSystemCAs) { |
| $('#wifi-server-ca').val('default'); |
| } else if (!('ServerCARef' in eapConfig)) { |
| $('#wifi-server-ca').val('ignore'); |
| } else { |
| if (main.findCertInList(eapConfig.ServerCARef) != null) |
| $('#wifi-server-ca').val(eapConfig.ServerCARef); |
| } |
| if ('ClientCertPattern' in eapConfig) { |
| // TODO: handle more complex client cert patterns. |
| var certPattern = eapConfig.ClientCertPattern; |
| if ('IssuerRef' in certPattern) |
| $('#wifi-client-ca').val(certPattern.IssuerRef); |
| if ('EnrollmentUri' in certPattern) |
| $('#wifi-enrollment-uri').val(certPattern.EnrollmentUri); |
| } |
| } |
| if ('ProxyURL' in netConfig) |
| $('#wifi-proxy-url').val(netConfig.ProxyURL); |
| // TODO: handle unrecognized/vendor fields. |
| } else if (netConfig.Type == 'VPN') { |
| $('#vpn-guid').val(netConfig.GUID); |
| if (!('VPN' in netConfig)) { |
| result.errors.push(['errorLoadRequiredObjectMissing', 'VPN']); |
| continue networkConfigurationsLoop; |
| } |
| var vpnConfig = netConfig.VPN; |
| if (!('Type' in vpnConfig)) { |
| result.errors.push(['errorLoadRequiredObjectMissing', 'VPN.Type']); |
| continue networkConfigurationsLoop; |
| } |
| $('#vpn-type').val(vpnConfig.Type); |
| if (!('Host' in vpnConfig)) { |
| result.errors.push(['errorLoadRequiredObjectMissing', 'VPN.Host']); |
| continue networkConfigurationsLoop; |
| } |
| $('#vpn-host').val(vpnConfig.Host); |
| if (!('L2TPIPsec' in vpnConfig)) { |
| result.errors.push(['errorLoadRequiredObjectMissing', |
| 'VPN.L2TPIPsec']); |
| continue networkConfigurationsLoop; |
| } |
| var l2tpConfig = vpnConfig.L2TPIPsec; |
| if ('PSK' in l2tpConfig) |
| $('#ipsec-psk').val(l2tpConfig.PSK); |
| if ('Username' in l2tpConfig) |
| $('#vpn-username').val(l2tpConfig.Username); |
| if ('Password' in l2tpConfig) |
| $('#vpn-password').val(l2tpConfig.Password); |
| if ('Username' in l2tpConfig || 'Password' in l2tpConfig) |
| $('#l2tp-specify-credentials')[0].checked = true; |
| if ('ProxyURL' in netConfig) |
| $('#vpn-proxy-url').val(netConfig.ProxyURL); |
| } else { |
| result.errors.push(['errorLoadUnknownNetworkConfigType', |
| netConfig.Type]); |
| continue networkConfigurationsLoop; |
| } |
| } |
| } |
| main.updateAllVisibility(); |
| |
| // Display the errors we found (if any) |
| if (result.errors.length == 0) { |
| $('#load-header').html(chrome.i18n.getMessage('loadSucceeded')); |
| } else { |
| // Clear everything out if we had errors. |
| main.resetUI(); |
| $('#load-file-form')[0].reset(); |
| } |
| $('#load-errors').html(main.convertMessagesToHtml(result.errors)); |
| $('#load-warnings').html(main.convertMessagesToHtml(result.warnings)); |
| }; |
| |
| /** |
| * Handle ONC file load event. |
| * @param {Array.<Object>} files Array of files in file upload format. |
| * @return {Boolean} Indicates the event needs to be passed to other objects. |
| */ |
| main.handleLoadFile = function(files) { |
| $('#load-errors').html(''); |
| for (var i = 0; i < files.length; ++i) { |
| var file = files[i]; |
| var reader = new FileReader(); |
| reader.onload = function(theFile) { |
| main.loadConfig(this.result); |
| }; |
| reader.readAsBinaryString(file); |
| } |
| return false; |
| }; |
| |
| /** |
| * Configures the ONC load file picker. |
| */ |
| main.configureLoadFilePicker = function() { |
| $('#load-file').change(function(event) { |
| main.handleLoadFile(event.target.files); |
| }); |
| }; |
| |
| /** |
| * Handles loading the body of the extension. Called from onload |
| * event handler. |
| */ |
| main.translateText = function() { |
| var i18nNodes = document.querySelectorAll('[i18n]'); |
| for (var i = 0; i < i18nNodes.length; ++i) { |
| var i18nId = i18nNodes[i].getAttribute('i18n'); |
| var translation = chrome.i18n.getMessage(i18nId); |
| if (translation == '') { |
| translation = 'NO TRANSLATION FOR: ' + i18nId; |
| } |
| i18nNodes[i].textContent = translation; |
| } |
| }; |
| |
| $(document).ready(function() { |
| main.resetUI(); |
| main.setTabByIdPrefix('wifi'); |
| main.configureDragDropTarget('cert-list'); |
| main.configureDragDropTarget('cert-item'); |
| main.configureCertFilePicker(); |
| main.configureLoadFilePicker(); |
| main.updateCertLists(); |
| main.translateText(); |
| $('div.selectable').click(function() { |
| var id = $(this)[0].id; |
| main.setTabByIdPrefix(id.split('-')[0]); |
| }); |
| $('#security').change(function() { |
| main.setWifiSecurityVisible(); |
| }); |
| $('#eap').change(main.setEAPVisible); |
| $('#vpn-type').change(main.setVPNTypeVisible); |
| $('#l2tp-specify-credentials').change(main.setVPNTypeVisible); |
| $('#specify-credentials').click(function() { |
| main.setCredentialsVisible(); |
| }); |
| }); |