blob: b0a380bc17f90df696d4d678bd194e7719e35ebc [file] [log] [blame]
// 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.
*/
/**
* Namespace for generic UI manipulation.
*/
var ui = {};
/**
* Namespace for WiFi dialog.
*/
var wiFiDialog = {};
/**
* Namespace for VPN dialog.
*/
var vpnDialog = {};
/**
* Namespace for certificate dialog.
*/
var certDialog = {};
/**
* Namespace for load dialog.
*/
var loadDialog = {};
/**
* Namespace for save dialog.
*/
var saveDialog = {};
/**
* Namespace for ONC manipulation.
*/
var onc = {};
/**
* Namespace for all other functions in main.js.
*/
var main = {
/**
* Current content of onc. We expect this to be validated when it
* is updated and to thus always be valid. This means at load time
* and at "save changes" time in the modal dialogs, it will be
* validated. And it also means that at save time the value can be
* written directly out without any further validation.
*/
'oncCurrent': {
'NetworkConfigurations': [],
'Certificates': []
}
};
/**
* Dictionary from identifier to dialog objects.
*/
main.dialogs = {
'cert': certDialog,
'load': loadDialog,
'save': saveDialog,
'vpn': vpnDialog,
'wifi': wiFiDialog,
};
/**
* Opens the dialog identified by the given ID prefix.
* @param {String} idPrefix HTML ID prefix of menu item and dialog
* (ie. 'wifi').
*/
ui.openDialog = function(idPrefix) {
// Close anything else that is already open - though none should be.
ui.dismissDialog();
var dialogDoms = $('#' + idPrefix + '-dialog');
$('#overlay')[0].style.display = '-webkit-box';
$('#overlay').keydown(function(event) {
if (event.which == 27)
ui.dismissDialog();
if (event.which == 13) {
// Try to apply.
$('#apply-button', '#' + idPrefix + '-dialog').click();
}
});
dialogDoms.show();
$('#cancel-button', dialogDoms).click(ui.onDialogCancelPress);
$('#apply-header', dialogDoms).html('');
$('#apply-errors', dialogDoms).html('');
$('#apply-warnings', dialogDoms).html('');
main.dialogs[idPrefix].init();
};
/**
* 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.
*/
ui.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
*/
ui.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;
};
/**
* Dismiss/hide the active modal dialog.
*/
ui.dismissDialog = function() {
$('#overlay').hide();
for (var dialog in main.dialogs)
$('#' + dialog + '-dialog').hide();
ui.updateSummary();
};
/**
* Handle cancel button press on any dialog.
*/
ui.onDialogCancelPress = function() {
ui.dismissDialog();
};
/**
* Handle edit button press in the list of networks and certs.
* @param {String} dialogId id for dialog to open
* @param {Integer} index offset inside arrays of main.oncCurrent
*/
ui.onEditPress = function(dialogId, index) {
var dialog = main.dialogs[dialogId];
ui.openDialog(dialogId);
var result;
var oncData;
if (dialogId == 'cert') {
oncData = main.oncCurrent.Certificates[index];
result = onc.validateCertificate(index, main.oncCurrent);
} else {
oncData = main.oncCurrent.NetworkConfigurations[index];
result = onc.validateNetwork(index, main.oncCurrent);
}
ui.showMessages(result, '#' + dialogId + '-dialog');
dialog.setToUi(oncData);
dialog.setUiVisibility();
};
/**
* Handle delete button press in the list of networks and certs.
* @param {Object} oncData ONC blob for the entity to delete.
*/
ui.onDeletePress = function(oncData) {
// Currently we always trust the user. If the user wants to delete
// a certificate that is referenced by a network, allow it. Upon
// updating the summary they should see that the network referencing
// it now has an error.
// TODO: Use the "Remove" tag when the entity was loaded (vs created
// this session).
onc.deleteEntity(oncData.GUID, main.oncCurrent);
ui.updateSummary();
};
/**
* Reset the given file picker. The file picker must be its parent's
* last child.
* @param {String} domId Query string to find picker.
*/
ui.resetFilePicker = function(domId) {
var pickerClone = $(domId).clone();
var parent = $(domId).parent()[0];
parent.removeChild($(domId)[0]);
parent.appendChild(pickerClone[0]);
}
/**
* Called to hide/show fields according to settings.
*/
wiFiDialog.setUiVisibility = function() {
wiFiDialog.setWifiSecurityVisible();
wiFiDialog.setEapVisible();
wiFiDialog.setCredentialsVisible();
};
/**
* Based on current Wi-Fi security setting, set visible configuration.
*/
wiFiDialog.setWifiSecurityVisible = function() {
var security = $('#security').val();
if (security == 'WEP-PSK' || security == 'WPA-PSK') {
$('#passphrase-div').show();
} else {
$('#passphrase-div').hide();
}
if (security == 'WPA-EAP') {
$('#8021x-div').show();
} else {
$('#8021x-div').hide();
}
};
/**
* Based on current EAP settings, return if password is required for
* connection.
*/
wiFiDialog.wifiRequiresPassword = 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.
*/
wiFiDialog.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.
*/
wiFiDialog.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.
*/
wiFiDialog.wifiRequiresClientCertficate = function() {
return $('#eap').val() == 'EAP-TLS';
};
/**
* Based on current EAP setting, set visible configuration.
*/
wiFiDialog.setEapVisible = function() {
if (wiFiDialog.wifiRequiresPhase2Method()) {
$('#phase2-div').show();
} else {
$('#phase2-div').hide();
}
if (wiFiDialog.wifiRequiresServerCertificate()) {
$('#eap-server-ca').show();
} else {
$('#eap-server-ca').hide();
}
if (wiFiDialog.wifiRequiresClientCertficate()) {
$('#eap-client-cert').show();
} else {
$('#eap-client-cert').hide();
}
};
/**
* Based on current VPN setting, set visible configuration.
*/
vpnDialog.setUiVisibility = function() {
var setting = $('#vpn-type').val();
if (setting == 'L2TP-IPsec-PSK') {
$('#vpn-psk-div').show();
$('#vpn-cert-div').hide();
} else if (setting == 'L2TP-IPsec-Cert' || setting == 'OpenVPN') {
$('#vpn-psk-div').hide();
$('#vpn-cert-div').show();
}
if ($('#vpn-save-credentials').is(':checked'))
$('#vpn-user-cred').show();
else
$('#vpn-user-cred').hide();
};
/**
* Set visible credentials based on kind of EAP chosen.
*/
wiFiDialog.setCredentialsVisible = function() {
if ($('#save-credentials').is(':checked'))
$('#phase2-auth-cred').show();
else
$('#phase2-auth-cred').hide();
if (wiFiDialog.wifiRequiresPassword())
$('#div-password', '#phase2-auth-cred').show();
else
$('#div-password', '#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;
};
/**
* Convert a data string into hex representation.
* @param {String} str Data string
* @return {String} Hex representation of data string.
*/
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;
};
/**
* Validate the given string is a valid hex number.
* @param {String} str Hex string.
* @return {Boolean} Indicates if a valid hex string
*/
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;
};
/**
* Validate and convert the WiFi configuration to ONC.
* @result {Object} Result array containing warnings, errors, and the
* ONC NetworkConfiguration object for WiFi.
*/
wiFiDialog.getFromUi = function() {
var network = {};
if (wiFiDialog.oncBase)
network = wiFiDialog.oncBase;
network.GUID = $('#wifi-guid').val();
network.Name = $('#ssid').val();
network.Type = 'WiFi';
onc.setUpAssocArray(network, 'WiFi');
network.WiFi.AutoConnect = $('#auto-connect').is(':checked');
network.WiFi.HiddenSSID = $('#hidden-ssid').is(':checked') != false;
network.WiFi.Security = $('#security').val();
network.WiFi.SSID = $('#ssid').val();
if ($('#wifi-proxy-url').val())
network.ProxyURL = $('#wifi-proxy-url').val();
else
delete network.ProxyUrl;
switch (network.WiFi.Security) {
case 'WEP-PSK':
case 'WPA-PSK':
network.WiFi.Passphrase = $('#passphrase').val();
delete network.WiFi.EAP;
break;
case 'WPA-EAP':
onc.setUpAssocArray(network.WiFi, 'EAP');
network.WiFi.EAP.Outer = $('#eap').val();
network.WiFi.EAP.UseSystemCAs = $('#wifi-server-ca').val() != 'ignore';
if ($('#save-credentials').is(':checked')) {
network.WiFi.EAP.SaveCredentials = true;
// Don't bother getting the username/password if save
// credentials is off. That would be an inconsistent state.
network.WiFi.EAP.Identity = $('#wifi-identity').val();
network.WiFi.EAP.Password = $('#wifi-password').val();
}
if (wiFiDialog.wifiRequiresServerCertificate()) {
if ($('#wifi-server-ca').val() != 'default') {
network.WiFi.EAP.ServerCARef = $('#wifi-server-ca').val();
}
}
if (wiFiDialog.wifiRequiresClientCertficate()) {
network.WiFi.EAP.ClientCertType = 'Pattern';
onc.setUpAssocArray(network.WiFi.EAP, 'ClientCertPattern');
if ($('#wifi-client-ca').val() != 'empty') {
network.WiFi.EAP.ClientCertPattern.IssuerCARef =
$('#wifi-client-ca').val();
}
network.WiFi.EAP.ClientCertPattern.EnrollmentUri =
$('#wifi-enrollment-uri').val();
}
delete network.WiFi.Passphrase;
break;
}
if (network.WiFi.Security == 'WEP-PSK') {
var asciiLengths = [5, 13, 16, 29];
if (asciiLengths.indexOf(network.WiFi.Passphrase.length) != -1) {
// Store the WEP passphrase as hex as required by ONC.
network.WiFi.Passphrase = '0x' + main.toHex(network.WiFi.Passphrase);
} else {
var hexNumber = network.WiFi.Passphrase;
if (hexNumber.substr(0, 2) == '0x')
hexNumber = hexNumber.substr(2);
network.WiFi.Passphrase = '0x' + hexNumber.toLowerCase();
}
}
return network;
};
vpnDialog.getUserCredentialsFromUi = function(container) {
var saveCredentials = $('#vpn-save-credentials').is(':checked');
container.SaveCredentials = saveCredentials;
if (saveCredentials) {
container.Username = $('#vpn-username').val();
container.Password = $('#vpn-password').val();
} else {
delete container.Username;
delete container.Password;
}
}
vpnDialog.getCertsFromUi = function(container) {
var serverCa = $('#vpn-server-ca').val();
if (serverCa != 'empty') {
container.ServerCARef = serverCa;
}
container.ClientCertType = 'Pattern';
var clientCa = $('#vpn-client-ca').val();
onc.setUpAssocArray(container, 'ClientCertPattern');
if (clientCa != 'empty') {
container.ClientCertPattern.IssuerCARef = clientCa;
}
if ($('#vpn-enrollment-uri').val()) {
container.ClientCertPattern.EnrollmentUri = $('#vpn-enrollment-uri').val();
} else {
delete container.ClientCertPattern.EnrollmentUri;
}
}
/**
* Validate and convert the VPN configuration to ONC.
* @result {Object} ONC NetworkConfiguration object for VPN.
*/
vpnDialog.getFromUi = function() {
var network = {};
if (vpnDialog.oncBase)
network = vpnDialog.oncBase;
network.GUID = $('#vpn-guid').val();
network.Name = $('#vpn-name').val();
network.Type = 'VPN';
onc.setUpAssocArray(network, 'VPN');
network.VPN.Host = $('#vpn-host').val();
var vpnType = $('#vpn-type').val();
if (vpnType == 'L2TP-IPsec-PSK' || vpnType == 'L2TP-IPsec-Cert') {
network.VPN.Type = 'L2TP-IPsec';
onc.setUpAssocArray(network.VPN, 'IPsec');
network.VPN.IPsec.IKEVersion = 1;
onc.setUpAssocArray(network.VPN, 'L2TP');
vpnDialog.getUserCredentialsFromUi(network.VPN.L2TP);
} else {
network.VPN.Type = 'OpenVPN';
onc.setUpAssocArray(network.VPN, 'OpenVPN');
vpnDialog.getUserCredentialsFromUi(network.VPN.OpenVPN);
}
if ($('#vpn-proxy-url').val())
network.ProxyURL = $('#vpn-proxy-url').val();
else
delete network.ProxyURL;
if (vpnType == 'L2TP-IPsec-Cert') {
container.AuthenticationType = 'Cert';
vpnDialog.getCertsFromUi(network.VPN.IPsec);
delete network.VPN.IPsec.PSK;
} else if (vpnType == 'L2TP-IPsec-PSK') {
network.VPN.IPsec.AuthenticationType = 'PSK';
if ($('#vpn-psk').val())
network.VPN.IPsec.PSK = $('#vpn-psk').val();
else
delete network.VPN.IPsec.PSK;
delete network.VPN.IPsec.ServerCARef;
delete network.VPN.IPsec.ClientCertPattern;
} else if ($('#vpn-type').val() == 'OpenVPN') {
vpnDialog.getCertsFromUi(network.VPN.OpenVPN);
}
return network;
};
/**
* 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.
*/
ui.convertMessagesToHtml = function(messageList) {
if (!messageList.length)
return '';
messages = [];
for (var i = 0; i < messageList.length; i++) {
var messageFormat = messageList[i];
var message;
if (messageFormat.length > 1) {
message = chrome.i18n.getMessage(messageFormat[0],
messageFormat.slice(1));
} else {
message = chrome.i18n.getMessage(messageFormat[0]);
}
if (!message) {
message = 'NO TRANSLATION FOR: ' + messageFormat[0];
}
messages.push(message);
}
return '<p>' + messages.join('</p><p>') + '<\p>';
};
/**
* Update the save button to the current configuration.
*/
saveDialog.updateSaveLink = function() {
var saveLink = $('#save-link');
var saveLinkDiv = $('#save-link-div');
saveLinkDiv.hide(); // In case something fails.
var oncString = JSON.stringify(main.oncCurrent, null, 2);
var configArray = oncString.split('').map(function(c) {
return c.charCodeAt(0);
});
var base64 = Base64.encode(main.arrayToUint8Array(configArray));
saveLinkDiv.show();
saveLink.attr('href', 'data:application/octet-stream;base64,' + base64);
};
/**
* Create an onc blob based on the input onc blob, updating with the
* given entity. The entities within the blob will be references to
* (not copies of) the original blob and entity.
* @param {Object} entity ONC entity to use as a replacement
* @param {Object} type Either NetworkConfigurations or Certificates.
* @param {Object} oncData ONC to update
* @returns {Boolean} Indicates if successful.
*/
onc.createUpdate = function(entity, type, oncData) {
if (oncData == null)
oncData = main.oncCurrent;
var resultOnc = {
'NetworkConfigurations': [],
'Certificates': []
};
resultOnc.NetworkConfigurations = oncData.NetworkConfigurations.slice(0);
resultOnc.Certificates = oncData.Certificates.slice(0);
if (!('GUID' in entity)) {
// Issue warning?
return resultOnc;
}
function replaceGuid(array) {
for (var i = 0; i < array.length; ++i) {
if (array[i].GUID == entity.GUID) {
array[i] = entity;
return;
}
}
array.push(entity);
}
if (type == 'NetworkConfigurations')
replaceGuid(resultOnc.NetworkConfigurations);
if (type == 'Certificates')
replaceGuid(resultOnc.Certificates);
return resultOnc;
};
/**
* Set up an associative array (JSON object) in the given ONC object. If there is
* already one, leave it alone.
* @param {Object} outer parent ONC object
* @param {String} newAssocArray String name of associative array.
*/
onc.setUpAssocArray = function(outer, newAssocArray) {
if (!(newAssocArray in outer) || !(outer[newAssocArray] instanceof Object)) {
outer[newAssocArray] = {};
}
}
/**
* Set up an associative array (JSON object) in the given ONC object. If there is
* already one, leave it alone.
* @param {Object} outer parent ONC object
* @param {String} newAssocArray String name of associative array.
*/
onc.setUpArray = function(outer, newArray) {
if (newArray in outer || !(outer[newArray] instanceof Array))
outer[newArray] = [];
}
/**
* Update array in place so that it has name in it iff value is non-zero.
* @param {Array} array input/output array.
* @param {Scalar} name name to find in array.
* @param {Integer} value value to use to determine if name should appear.
*/
onc.setBitArray = function(array, name, value) {
if (value) {
if (array.indexOf(name) < 0)
array.push(name);
} else {
var index = array.indexOf(name);
if (index >= 0)
array.splice(index, 1);
}
}
/**
* Depending on the results, apply the changes or show warnings.
* @param {Object} result Result of compiling the UI to ONC.
* @param {Object} oncTest ONC resulting from applying change..
* @param {Object} dialog DOM node of dialog.
*/
ui.showMessagesAndApply = function(result, oncTest, dialog) {
ui.showMessages(result, dialog);
// Require the user to fix errors.
if (result.errors.length)
return;
main.oncCurrent = oncTest;
ui.dismissDialog();
};
/**
* Handle apply button press on the WiFi modal dialog. Responsible for
* showing errors, blocking save, and dismissing modal dialog.
*/
wiFiDialog.onApplyPress = function() {
var newWiFi = wiFiDialog.getFromUi();
var oncTest = onc.createUpdate(newWiFi, 'NetworkConfigurations');
var result = onc.validateNetwork(onc.findNetwork(newWiFi.GUID, oncTest),
oncTest);
ui.showMessagesAndApply(result, oncTest, $('#wifi-dialog')[0]);
};
/**
* Handle save button press on the VPN modal dialog. Responsible for
* showing errors, blocking save, and dismissing modal dialog.
*/
vpnDialog.onApplyPress = function() {
var newVpn = vpnDialog.getFromUi();
var oncTest = onc.createUpdate(newVpn, 'NetworkConfigurations');
var result = onc.validateNetwork(onc.findNetwork(newVpn.GUID, oncTest),
oncTest);
ui.showMessagesAndApply(result, oncTest, $('#vpn-dialog')[0]);
};
/**
* Called to initialize the save dialog.
*/
saveDialog.init = function() {
saveDialog.updateSaveLink();
$('#cancel-button', '#save-dialog').focus();
};
/**
* Called to hide/unhide UI fields.
*/
saveDialog.setUiVisibility = function() {
};
/**
* Update a certificate dropdown with the currently loaded certificates.
* Currently only Certificate Authority certs are listed.
* @param {Object} optionList DOM option list to update.
* @param {Boolean} clearFirst Clear the option list before adding.
*/
ui.updateCertificateDropdown = function(optionList, clearFirst) {
if (clearFirst) optionList.options.length = 0;
optionList.disabled = false;
for (var i = 0; i < main.oncCurrent.Certificates.length; ++i) {
var oncCert = main.oncCurrent.Certificates[i];
if (oncCert.Type != 'Authority')
continue;
var certDescription = ui.formatCertificate(oncCert, 1);
optionList.options.add(new Option(certDescription, oncCert.GUID));
}
if (optionList.options.length == 0) {
optionList.options.add(new Option(chrome.i18n.getMessage
('certificateEmpty'), 'empty'));
optionList.disabled = true;
}
};
/**
* Find a cert in the cert list by GUID.
* @returns {Integer} index into certificate array or -1 if not found.
*/
onc.findCert = function(guid, oncData) {
for (var i = 0; i < oncData.Certificates.length; ++i) {
if (oncData.Certificates[i].GUID == guid)
return i;
}
return -1;
};
/**
* Find a network in the network list by GUID.
* @returns {Integer} index into certificate array or -1 if not found.
*/
onc.findNetwork = function(guid, oncData) {
for (var i = 0; i < oncData.NetworkConfigurations.length; ++i) {
if (oncData.NetworkConfigurations[i].GUID == guid)
return i;
}
return -1;
};
/**
* Deletes an entity by GUID.
* @params {String} guid GUID of entity (cert or network) to delete.
* @params {Object} oncData ONC blob to update.
* @returns {Boolean} Indicates if successful.
*/
onc.deleteEntity = function(guid, oncData) {
var index = onc.findCert(guid, oncData);
var array = null;
var isCert = false;
if (index >= 0) {
array = oncData.Certificates;
isCert = true;
} else {
index = onc.findNetwork(guid, oncData);
if (index >= 0)
array = oncData.NetworkConfigurations;
}
if (!array)
return false;
array = array.slice(0, index).concat(array.slice(index + 1));
if (isCert)
oncData.Certificates = array;
else
oncData.NetworkConfigurations = array;
return true;
};
/**
* Takes an X509 PEM-formatted certificate and interprets its contents.
* @param {String} x509 base64 X509 certificate
* @returns {Object} Result of asn1.interpretCert on certificate.
*/
main.interpretCertFromX509Pem = function(x509Pem) {
var der = Base64.unarmor(x509Pem);
var asn1Data = asn1.parseAsn1(der);
return asn1.interpretCert(asn1Data);
};
/**
* Update the certificate summary based on the currently loaded cert.
*/
certDialog.updateBox = function() {
if (!certDialog.certData || !('X509' in certDialog.certData)) {
$('#cert-instructions').show();
$('#cert-summary').hide();
return;
}
$('#cert-instructions').hide();
$('#cert-summary').show();
var cert = main.interpretCertFromX509Pem(certDialog.certData.X509);
function updateEntity(table, entity) {
var fields = $('.cert-fill', table);
for (var i = 0; i < fields.length; ++i) {
if (fields[i].id in entity)
fields[i].innerText = entity[fields[i].id];
else
fields[i].innerText = '';
}
}
updateEntity($('#subject', '#cert-summary'), cert.subject);
updateEntity($('#issuer', '#cert-summary'), cert.issuer);
};
/**
* Handle a drag and drop file list. We validate the certificate
* file, show a summary of the certificate, and store the
* contents for later in certData.
* @param {Array.<Object>} files drag and drop file list.
*/
certDialog.handleCertFileList = function(files) {
certDialog.loadedCert = null;
for (var i = 0; i < files.length; ++i) {
var file = files[i];
var reader = new FileReader();
reader.onload = function(theFile) {
var derUint8;
if (file.name.match(/\.pem$/) || file.name.match(/\.crt$/)) {
var der = Base64.unarmor(this.result);
derUint8 = main.arrayToUint8Array(der);
} else if (file.name.match(/\.der/)) {
// TODO: This is currently broken
var der = this.result;
derUint8 = main.arrayToUint8Array(der);
}
certDialog.certData = {};
if (derUint8)
certDialog.certData.X509 = Base64.encode(derUint8);
// Create a new ONC object.
var newCert = certDialog.getFromUi();
var oncTest = onc.createUpdate(newCert, 'Certificates');
var results = onc.validateCertificate(
onc.findCert(newCert.GUID, oncTest), oncTest);
ui.showMessages(results, '#cert-dialog');
certDialog.updateBox();
};
reader.readAsBinaryString(file);
}
return false;
};
/**
* Set up the UI with the given certificate ONC. Error checking is
* not performed.
* @param {Object} oncCert Certificate
*/
certDialog.setToUi = function(oncCert) {
certDialog.oncBase = oncCert;
certDialog.certData.X509 = oncCert.X509;
if ('Trust' in oncCert) {
for (var i = 0; i < oncCert.Trust.length; ++i) {
if (oncCert.Trust[i] == 'Web')
$('#web-trust')[0].checked = true;
}
}
$('#cert-type').val(oncCert.Type);
certDialog.updateBox();
};
/**
* Called to hide/unhide UI fields.
*/
certDialog.setUiVisibility = function() {
}
/**
* Validate and convert the certificate configuration to ONC.
* @result {Object} ONC Certificate object.
*/
certDialog.getFromUi = function() {
// TODO: Handle or deprecate PKCS12 loading (in which case
// we load PKCS8).
var oncCert = {};
if ('oncBase' in certDialog)
oncCert = certDialog.oncBase;
onc.setUpArray(oncCert, 'Trust');
onc.setBitArray(oncCert.Trust, 'Web', $('#web-trust').is(':checked'));
oncCert.GUID = $('#cert-guid').val();
oncCert.Type = $('#cert-type').val();
if ('X509' in certDialog.certData)
oncCert.X509 = certDialog.certData.X509;
return oncCert;
};
/**
* Configure the given DOM id as a drag and drop target for certificates.
* @param {String} id DOM id.
*/
certDialog.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) {
certDialog.handleCertFileList(event.dataTransfer.files);
}, false);
};
/**
* Configure the certificate file picker.
*/
certDialog.configureCertFilePicker = function() {
$('#cert-files').change(function(event) {
certDialog.handleCertFileList(event.target.files);
});
};
/**
* Handles save of certificate.
*/
certDialog.onApplyPress = function() {
var newCert = certDialog.getFromUi();
var oncTest = onc.createUpdate(newCert, 'Certificates');
var results = onc.validateCertificate(
onc.findCert(newCert.GUID, oncTest), oncTest);
ui.showMessagesAndApply(results, oncTest, $('#cert-dialog')[0]);
};
/**
* Shows the error or warning mark as appropriate for the givn entity.
* @param {Object} result Validation result.
* @param {DOM} entityDom DOM of entity in the list.
*/
ui.setEntityStatus = function(result, entityDom) {
var whichMark = null;
if (result.errors.length)
whichMark = '#error-mark';
else if (result.warnings.length)
whichMark = '#warning-mark';
if (whichMark) {
$(whichMark, entityDom).show();
// if you click on the warning/error, edit the entry.
$(whichMark, entityDom).click(function(event) {
$('#edit', this.parentNode).click();
});
}
};
/**
* Format a network configuration for display.
* @params {Object} oncNetwork ONC network configuration
*/
ui.formatNetwork = function(oncNetwork) {
return oncNetwork.Name + '<br>' +
oncNetwork.Type;
};
/**
* Handle edit press on a network in the summary list.
*/
ui.onNetworkEditPress = function() {
var parent = $(this).parent();
var index = parent.data('index')
if (parent.data('onc').Type == 'WiFi')
ui.onEditPress('wifi', index);
else
ui.onEditPress('vpn', index);
};
/**
* Format a network configuration for display.
* @params {Object} oncNetwork ONC network configuration
* @params {Integer} maxLines Maximum lines available for description.
Defaults to no maximum.w
*/
ui.formatCertificate = function(oncCert, maxLines) {
var result = ''
var cert = main.interpretCertFromX509Pem(oncCert.X509);
if ('commonName' in cert.subject) {
result += cert.subject.commonName;
}
if (maxLines == undefined || maxLines > 1) {
result += '<br>\n';
}
result += ' [';
if ('commonName' in cert.issuer) {
result += cert.issuer.commonName;
}
result += ']';
return result;
};
/**
* Handle edit press on a network in the summary list.
*/
ui.onCertificateEditPress = function() {
var parent = $(this).parent();
var index = parent.data('index')
ui.onEditPress('cert', index);
};
/**
* Show the summary of the current ONC settings for a given kind of entity.
* @param {Array.<Object>} list List of entities (networks or certificates)
* @param {String} id query id of the list's top level DOM.
* @param {Function} validator Called to validate this entity.
* @param {Function} formatter Called to format entity to HTML.
* @param {Function} editCallback Called to handle edit press of this entity.
*/
ui.showSummaryList = function(list, id, validator, formatter, editCallback) {
for (var i = 0; i < list.length; ++i) {
var oncEntity = list[i];
var result = validator(i, main.oncCurrent);
var newDom = $('#template', id).clone();
newDom[0].id = '';
$(id).append(newDom);
$('#left', newDom).html(formatter(oncEntity));
ui.setEntityStatus(result, newDom);
var editButton = $('#edit', newDom);
editButton.parent().data('onc', oncEntity);
editButton.parent().data('index', i);
editButton.click(editCallback);
// For opaque entities which we do not know how to edit, simply
// remove the edit button. User can still preserve such entities
// and delete them, but not modify them.
if (result.hasOpaqueEntity)
editButton[0].parentNode.removeChild(editButton[0]);
var deleteButton = $('#delete', newDom);
deleteButton.click(function() {
ui.onDeletePress($(this).parent().data('onc'));
});
newDom.hover(
function() {
$('.action', this).show();
},
function() {
$('.action', this).hide();
});
newDom[0].style.display = '-webkit-box';
}
};
/**
* Update the summary (right) pane.
*/
ui.updateSummary = function() {
var rightPane = $('#right-pane')[0];
// Remove all entities other than the template.
var entities = $('.entity');
for (var i = 0; i < entities.length; ++i) {
if (entities[i].style.display != '')
entities[i].parentNode.removeChild(entities[i]);
}
ui.showSummaryList(main.oncCurrent.NetworkConfigurations,
'#network-configurations',
onc.validateNetwork,
ui.formatNetwork,
ui.onNetworkEditPress);
ui.showSummaryList(main.oncCurrent.Certificates,
'#certificates',
onc.validateCertificate,
ui.formatCertificate,
ui.onCertificateEditPress);
};
/**
* Called to initialize the WiFi dialog.
*/
wiFiDialog.init = function() {
$('#ssid').val('');
$('#hidden-ssid')[0].checked = false;
$('#auto-connect')[0].checked = false;
ui.setSelectedI18n('#security', 'securityNone');
$('#passphrase').val('');
$('#wifi-proxy-url').val('');
ui.setSelectedI18n('#eap', 'acronymPeap');
ui.setSelectedI18n('#phase2', 'automatic');
$('#save-credentials')[0].checked = false;
$('#wifi-identity').val('');
$('#wifi-password').val('');
$('#wifi-server-ca').val('');
$('#wifi-client-ca').val('');
$('#wifi-guid').val(main.createGuid());
var serverCaDom = $('#wifi-server-ca')[0];
serverCaDom.options.length = 0;
serverCaDom.options.add(new Option(chrome.i18n.getMessage
('useAnyDefaultCA'), 'default'));
ui.updateCertificateDropdown(serverCaDom, false);
serverCaDom.options.add(new Option(chrome.i18n.getMessage
('doNotCheckCA'), 'ignore'));
ui.updateCertificateDropdown($('#wifi-client-ca')[0], true);
$('#apply-button', '#wifi-dialog').click(wiFiDialog.onApplyPress);
$('#security').change(function() {
wiFiDialog.setUiVisibility();
});
$('#eap').change(wiFiDialog.setUiVisibility);
$('#save-credentials').click(function() {
wiFiDialog.setUiVisibility();
});
wiFiDialog.setUiVisibility();
$('#ssid').focus();
};
/**
* Called to initialize the VPN dialog.
*/
vpnDialog.init = function() {
$('#vpn-name').val('');
$('#vpn-host').val('');
$('#vpn-psk').val('');
$('#vpn-save-credentials')[0].checked = false;
$('#vpn-username').val('');
$('#vpn-password').val('');
$('#vpn-enrollment-uri').val('');
$('#vpn-guid').val(main.createGuid());
ui.updateCertificateDropdown($('#vpn-server-ca')[0], true);
ui.updateCertificateDropdown($('#vpn-client-ca')[0], true);
$('#apply-button', '#vpn-dialog').click(vpnDialog.onApplyPress);
$('#vpn-type').change(vpnDialog.setUiVisibility);
$('#vpn-save-credentials').change(vpnDialog.setUiVisibility);
vpnDialog.setUiVisibility();
$('#vpn-name').focus();
};
loadDialog.init = function() {
$('#apply-button', '#load-dialog').click(loadDialog.onApplyPress);
// Reset the file picker.
ui.resetFilePicker('#load-file');
loadDialog.configureLoadFilePicker();
$('#load-file').focus();
};
/**
* Called to initialize the cert dialog.
*/
certDialog.init = function() {
certDialog.certData = {};
$('#cert-guid').val(main.createGuid());
$('#web-trust')[0].checked = false;
ui.setSelectedI18n('#cert-type', 'certificateTypeAuthority');
$('#apply-button', '#cert-dialog').click(certDialog.onApplyPress);
ui.resetFilePicker('#cert-files');
certDialog.configureDragDropTarget('cert-summary');
certDialog.configureCertFilePicker();
$('#cert-files').focus();
certDialog.updateBox();
};
loadDialog.setUiVisibility = function() {
return;
};
/**
* Set up the UI with given WiFi ONC configuration.
* @param {Object} netconfig WiFi ONC object
**/
wiFiDialog.setToUi = function(netConfig) {
// Preserve any existing settings.
wiFiDialog.oncBase = netConfig;
$('#wifi-guid').val(netConfig.GUID);
wifiConfig = netConfig.WiFi;
$('#ssid').val(wifiConfig.SSID);
if ('AutoConnect' in wifiConfig)
$('#auto-connect')[0].checked = wifiConfig.AutoConnect != false;
if ('HiddenSSID' in wifiConfig)
$('#hidden-ssid')[0].checked = 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-PSK' &&
wifiConfig.Passphrase.substr(0,2) == '0x')
wifiConfig.Passphrase = wifiConfig.Passphrase.substr(2);
$('#passphrase').val(wifiConfig.Passphrase);
}
$('#security').val(wifiConfig.Security);
if ('EAP' in wifiConfig && wifiConfig.Security == 'WPA-EAP') {
var eapConfig = netConfig.WiFi.EAP;
switch (eapConfig.Outer) {
case 'PEAP':
case 'EAP-TTLS':
case 'EAP-TLS':
case 'LEAP':
$('#eap').val(eapConfig.Outer);
break;
}
if ('Identity' in eapConfig) {
$('#wifi-identity').val(eapConfig.Identity);
}
if ('Password' in eapConfig) {
$('#wifi-password').val(eapConfig.Password);
}
if ('SaveCredentials' in eapConfig && eapConfig.SaveCredentials)
$('#save-credentials')[0].checked = true;
if (onc.findCert(eapConfig.ServerCARef, main.oncCurrent) >= 0)
$('#wifi-server-ca').val(eapConfig.ServerCARef);
else if (!('UseSystemCAs' in eapConfig) || eapConfig.UseSystemCAs) {
$('#wifi-server-ca').val('default');
} else {
$('#wifi-server-ca').val('ignore');
}
if ('ClientCertPattern' in eapConfig) {
// TODO: handle more complex client cert patterns.
var certPattern = eapConfig.ClientCertPattern;
if ('IssuerCARef' in certPattern)
$('#wifi-client-ca').val(certPattern.IssuerCARef);
if ('EnrollmentUri' in certPattern)
$('#wifi-enrollment-uri').val(certPattern.EnrollmentUri);
}
if ('ProxyURL' in netConfig)
$('#wifi-proxy-url').val(netConfig.ProxyURL);
}
};
/**
* Set up the UI with an object from ONC that has certificate information.
* @param {Object} netconfig VPN ONC object
**/
vpnDialog.setCertToUi = function(vpnConfig) {
if (onc.findCert(vpnConfig.ServerCARef, main.oncCurrent) >= 0)
$('#vpn-server-ca').val(vpnConfig.ServerCARef);
if (vpnConfig.ClientCertType == 'Pattern') {
if (onc.findCert(vpnConfig.ClientCertPattern.IssuerCARef,
main.oncCurrent) >= 0) {
$('#vpn-client-ca').val(vpnConfig.ClientCertPattern.IssuerCARef);
}
if ('EnrollmentUri' in vpnConfig.ClientCertPattern)
$('#vpn-enrollment-uri').val(vpnConfig.ClientCertPattern.EnrollmentUri);
}
}
/**
* Set up the UI with an object from ONC that has user credentials.
* @param {Object} netconfig VPN ONC object
**/
vpnDialog.setUserCredentialsToUi = function(vpnConfig) {
if ('Username' in vpnConfig)
$('#vpn-username').val(vpnConfig.Username);
if ('Password' in vpnConfig)
$('#vpn-password').val(vpnConfig.Password);
if ('SaveCredentials' in vpnConfig && vpnConfig.SaveCredentials)
$('#vpn-save-credentials')[0].checked = true;
}
/**
* Set up the UI with given VPN ONC configuration.
* @param {Object} netconfig VPN ONC object
**/
vpnDialog.setToUi = function(netConfig) {
vpnDialog.oncBase = netConfig;
$('#vpn-name').val(netConfig.Name);
$('#vpn-guid').val(netConfig.GUID);
if ('ProxyURL' in netConfig)
$('#vpn-proxy-url').val(netConfig.ProxyURL);
var vpnConfig = netConfig.VPN;
$('#vpn-type').val(vpnConfig.Type);
$('#vpn-host').val(vpnConfig.Host);
if (vpnConfig.Type == 'L2TP-IPsec') {
var ipsecConfig = vpnConfig.IPsec;
if (ipsecConfig.AuthenticationType == 'PSK') {
$('#vpn-type').val('L2TP-IPsec-PSK');
if ('PSK' in ipsecConfig)
$('#vpn-psk').val(ipsecConfig.PSK);
} else if (ipsecConfig.AuthenticationType == 'Cert') {
$('#vpn-type').val('L2TP-IPsec-Cert');
vpnDialog.setCertToUi(ipsecConfig);
}
vpnDialog.setUserCredentialsToUi(vpnConfig.L2TP);
} else {
$('#vpn-type').val('OpenVPN');
vpnDialog.setCertToUi(vpnConfig.OpenVPN);
vpnDialog.setUserCredentialsToUi(vpnConfig.OpenVPN);
}
};
/**
* Validate an ONC Client Certificate. This handles both the client
* certificate reference and certificate pattern cases.
* @param {Object} network NetworkConfiguration ONC object.
* @param {Object} result Result object indicating errors and warnings.
*/
onc.validateClientCert = function(outer, index, oncData, result) {
var network = oncData.NetworkConfigurations[index];
if (!('ClientCertType' in outer)) {
result.errors.push(['errorLoadRequiredObjectMissing', 'ClientCertType']);
return result;
}
if (outer.ClientCertType == 'Ref') {
if (!('ClientCertRef' in outer)) {
result.errors.push(['errorLoadRequiredObjectMissing', 'ClientCertRef']);
return result;
}
if (onc.findCert(outer.ClientCertRef, oncData) < 0) {
result.errors.push(['errorBadCertReference',
network.Name, 'ClientCertRef', outer.ClientCertRef]);
}
} else if (outer.ClientCertType == 'Pattern') {
if (!('ClientCertPattern' in outer)) {
result.errors.push(['errorLoadRequiredObjectMissing',
'ClientCertPattern']);
return result;
}
var pattern = outer.ClientCertPattern;
if (!('IssuerCARef' in pattern) &&
!('Subject' in pattern)) {
result.errors.push(['errorMissingClientCA', network.Name]);
return result;
}
if ('IssuerCARef' in pattern &&
onc.findCert(pattern.IssuerCARef, oncData) < 0) {
result.errors.push(['errorBadCertReference',
network.Name, 'IssuerCARef', pattern.IssuerCARef]);
}
} else {
result.errors.push(['errorLoadRequiredObjectMissing', 'ClientCertType']);
}
return result;
};
onc.validate = function(oncData, result) {
if (!result)
result = { 'errors': [], 'warnings': [], 'hasOpaqueEntity': false };
var hasOne;
for (var field in oncData) {
if (field == 'NetworkConfigurations' ||
field == 'Certificates')
hasOne = true;
else
result.warnings.push(['warningUnrecognizedTopLevelField', field]);
}
if (!hasOne) {
result.errors.push(['errorEmptyOnc']);
}
if (!('NetworkConfigurations' in oncData))
oncData.NetworkConfigurations = [];
for (var i = 0; i < oncData.NetworkConfigurations.length; ++i)
onc.validateNetwork(i, oncData, result);
if (!('Certificates' in oncData))
oncData.Certificates = [];
for (var i = 0; i < oncData.Certificates.length; ++i)
onc.validateCertificate(i, oncData, result);
return result;
};
/**
* Load the given ONC configuration and show any errors in errorDom.
* @param {String} config ONC formatted string.
*/
loadDialog.loadConfig = function(config) {
var result = { 'errors': [], 'warnings': [], 'hasOpaqueEntity': false };
try {
config = JSON.parse(config);
} catch(e) {
result.errors.push(['errorDuringLoad', e.toString()]);
}
if (!result.errors.length && typeof(config) != 'object') {
result.errors.push(['errorDuringLoad', e.toString()]);
}
if (!result.errors.length) {
result = onc.validate(config, result);
}
// Display the errors we found (if any)
if (result.errors.length == 0) {
$('#apply-header', '#load-dialog').html(chrome.i18n.getMessage
('loadSucceeded'));
} else {
// Clear everything out if we had errors.
$('#load-file-form')[0].reset();
}
ui.showMessages(result, '#load-dialog');
loadDialog.oncToLoad = config;
loadDialog.oncToLoadResult = result;
};
// TODO(kmixter): Remove these two levels of functions once we get the LGTM (just
// here to help gerrit with diffing code).
(function() {
/**
* Validate the given certificate ONC data.
* @param {String} index index into Certificates array in ONC.
* @param {Object} oncData ONC data
* @param {Object} result Validation results data that will be update.
*/
onc.validateCertificate = function(index, oncData, result) {
if (!result)
result = { 'errors': [], 'warnings': [], 'hasOpaqueEntity': false };
var certificate = oncData.Certificates[index];
if (!('GUID' in certificate)) {
result.errors.push(['errorCertificateMissingGUID']);
return result;
}
if (!('Type' in certificate)) {
result.errors.push(['errorLoadRequiredObjectMissing', 'Type']);
return result;
}
if ('X509' in certificate) {
var der = Base64.unarmor(certificate.X509);
var asn1Data = asn1.parseAsn1(der);
if (!asn1Data) {
result.errors.push(['errorX509CertificateIsInvalid', certificate.GUID]);
return result;
}
var cert = asn1.interpretCert(asn1Data);
if (!asn1Data) {
result.errors.push(['errorX509CertificateIsInvalid', certificate.GUID]);
}
} else if ('PKCS12' in certificate) {
// TODO: support or deprecate PKCS12.
result.errors.push(['errorPKCS12CertificatesNotYetSupported',
certificate.GUID]);
result.hasOpaqueEntity = true;
} else {
result.errors.push(['errorCertificateMissingPayloadField',
certificate.GUID]);
result.hasOpaqueEntity = true;
}
return result;
};
})();
// TODO(kmixter): Remove these two levels of functions once we get the LGTM (just
// here to help gerrit with diffing code).
(function() {
(function() {
/**
* Validate the given NetworkConfiguration ONC object.
* @param {Integer} index Index into NetworkConfigurations.
* @param {Object} oncData Complete ONC
* @param {Object} result Incoming results of validation, updated.
* @returns {Object} Results object
*/
onc.validateNetwork = function(index, oncData, result) {
var netConfig = oncData.NetworkConfigurations[index];
if (!result)
result = { 'errors': [], 'warnings': [], 'hasOpaqueEntity': false };
var requiredNetworkConfigurationKeys = [ 'GUID', 'Type' ];
for (var j = 0; j < requiredNetworkConfigurationKeys.length; ++j) {
var key = requiredNetworkConfigurationKeys[j];
if (!(key in netConfig)) {
result.errors.push(['errorLoadRequiredObjectMissing', key]);
return result;
}
}
if (!('Name' in netConfig) || netConfig.Name == '') {
result.errors.push(['errorMissingNetworkName', netConfig.Type]);
return result;
}
if (netConfig.Type == 'WiFi') {
onc.validateWiFiNetwork(index, oncData, result);
} else if (netConfig.Type == 'VPN') {
onc.validateVpnNetwork(index, oncData, result);
} else {
result.warnings.push(['errorLoadUnknownNetworkConfigType',
netConfig.Type]);
result.hasOpaqueEntity = true;
}
return result;
};
(function() {
/**
* Validate a WiFi NetworkConfiguration ONC object.
* @param {Integer} index Index into NetworkConfiguration ONC object.
* @param {Object} oncData ONC configuration
* @param {Object} result Result object indicating errors and warnings.
* @returns {Object} Result of validation.
*/
onc.validateWiFiNetwork = function(index, oncData, result) {
var netConfig = oncData.NetworkConfigurations[index];
if (!('WiFi' in netConfig)) {
result.errors.push(['errorLoadRequiredObjectMissing', 'WiFi']);
return result;
}
var wifiConfig = netConfig.WiFi;
if (!('SSID' in wifiConfig) || wifiConfig.SSID == '') {
result.errors.push(['errorWiFiNetworkMissingSSID', 'SSID']);
return result;
}
if (!('Security' in wifiConfig)) {
result.errors.push(['errorLoadRequiredObjectMissing', 'Security']);
return result;
}
switch (wifiConfig.Security) {
case 'None':
break;
case 'WEP-PSK':
result.warnings.push(['warningWEPInherentlyUnsafe', netConfig.Name]);
if ('Passphrase' in netConfig.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 hexLengths = [10, 26, 32, 58];
var passphrase = netConfig.WiFi.Passphrase;
if (passphrase.substr(0, 2) != '0x' ||
hexLengths.indexOf(passphrase.length - 2) == -1)
result.errors.push(['errorWEPKeyInvalidLength',
netConfig.Name]);
} else {
result.error.push(['errorPassphraseMissing',
netConfig.Name]);
}
break;
case 'WPA-PSK':
if ('Passphrase' in netConfig.WiFi) {
if (netConfig.WiFi.Passphrase.length < 8) {
result.warnings.push(['warningShortWPAPassphraseUnsafe',
netConfig.WiFi.Security,
netConfig.Name,
netConfig.WiFi.Passphrase.length]);
}
} else {
result.errors.push(['errorPassphraseMissing',
netConfig.Name]);
}
break;
case 'WPA-EAP':
if (!('EAP' in netConfig.WiFi)) {
result.errors.push(['errorLoadRequiredObjectMissing', 'EAP']);
break;
}
var eapConfig = netConfig.WiFi.EAP;
if (wiFiDialog.wifiRequiresServerCertificate()) {
if ('ServerCARef' in eapConfig &&
onc.findCert(eapConfig.ServerCARef, oncData) < 0) {
result.errors.push(['errorBadCertReference',
netConfig.Name, 'ServerCARef', eapConfig.ServerCARef]);
}
}
if (wiFiDialog.wifiRequiresClientCertficate()) {
onc.validateClientCert(eapConfig, index, oncData, result);
}
if (wiFiDialog.wifiRequiresPassword()) {
if (!('Password' in eapConfig))
result.warnings.push(['warningEAPPasswordEmpty',
netConfig.Name]);
}
if (!('Outer' in eapConfig)) {
result.errors.push(['errorLoadRequiredObjectMissing',
'WiFi.EAP.Outer']);
return result;
}
switch (eapConfig.Outer) {
case 'PEAP':
case 'EAP-TTLS':
case 'EAP-TLS':
case 'LEAP':
break;
default:
result.errors.push(['errorLoadUnhandledEapType',
eapConfig.Outer, netConfig.Name]);
return result;
}
if (!('Identity' in eapConfig) || eapConfig.Identity == '') {
result.warnings.push(['warningIdentityMissing', netConfig.Name]);
}
break;
default:
result.errors.push(['errorLoadUnhandledSecurityType',
wifiConfig.Security, netConfig.Name]);
}
return result;
};
/**
* Validate a VPN NetworkConfiguration ONC object's user credentials.
* @param (Object) container ONC container for user credentials.
* @param {Integer} index Index into NetworkConfiguration ONC object.
* @param {Object} oncData ONC configuration
* @param {Object} result Result object indicating errors and warnings.
* @returns {Object} Result of validation.
*/
onc.validateVpnUserCredentials = function(container, index,
oncData, result) {
var netConfig = oncData.NetworkConfigurations[index];
if (!('Username' in container) || container.Username == '') {
result.warnings.push(['warningIdentityMissing', netConfig.Name]);
}
if (!('Password' in container) || container.Password == '') {
result.warnings.push(['warningPasswordMissing', netConfig.Name]);
}
}
/**
* Validate a VPN NetworkConfiguration ONC object's certificates.
* @param (Object) container ONC container for certificates.
* @param {Integer} index Index into NetworkConfiguration ONC object.
* @param {Object} oncData ONC configuration
* @param {Object} result Result object indicating errors and warnings.
* @returns {Object} Result of validation.
*/
onc.validateVpnCerts = function(container, index, oncData, result) {
var netConfig = oncData.NetworkConfigurations[index];
if (!('ServerCARef' in container))
result.errors.push(['errorMissingVPNServerCA', netConfig.Name]);
else if (onc.findCert(container.ServerCARef, oncData) < 0) {
result.errors.push(['errorBadCertReference',
netConfig.Name, 'ServerCARef',
container.ServerCARef]);
}
onc.validateClientCert(container, index, oncData, result);
}
/**
* Validate a VPN NetworkConfiguration ONC object.
* @param {Integer} index Index into NetworkConfiguration ONC object.
* @param {Object} oncData ONC configuration
* @param {Object} result Result object indicating errors and warnings.
* @returns {Object} Result of validation.
*/
onc.validateVpnNetwork = function(index, oncData, result) {
var netConfig = oncData.NetworkConfigurations[index];
if (!('VPN' in netConfig)) {
result.errors.push(['errorLoadRequiredObjectMissing', 'VPN']);
return result;
}
var vpnConfig = netConfig.VPN;
if (!('Type' in vpnConfig)) {
result.errors.push(['errorLoadRequiredObjectMissing', 'VPN.Type']);
}
if (!('Host' in vpnConfig) || vpnConfig.Host == '') {
result.errors.push(['errorLoadRequiredObjectMissing', 'VPN.Host']);
}
if (vpnConfig.Type == 'L2TP-IPsec') {
if (!('L2TP' in vpnConfig)) {
result.errors.push(['errorLoadRequiredObjectMissing',
'VPN.L2TP']);
return result;
}
onc.validateVpnUserCredentials(vpnConfig.L2TP, index,
oncData, result)
if (!('IPsec' in vpnConfig)) {
result.errors.push(['errorLoadRequiredObjectMissing',
'VPN.IPsec']);
return result;
}
var ipsecConfig = vpnConfig.IPsec;
if (!('IKEVersion' in ipsecConfig)) {
result.errors.push(['errorLoadRequiredObjectMissing',
'VPN.IPsec.IKEVersion']);
return result;
}
if (!('AuthenticationType' in ipsecConfig)) {
result.errors.push(['errorLoadRequiredObjectMissing',
'VPN.IPsec.AuthenticationType']);
return result;
}
if (ipsecConfig.AuthenticationType != 'PSK' &&
ipsecConfig.AuthenticationType != 'Cert') {
result.errors.push(['errorUnsupportedValue',
'VPN.IPsec.AuthenticationType',
ipsecConfig.AuthenticationType]);
}
if (ipsecConfig.AuthenticationType == 'PSK') {
if (!('PSK' in ipsecConfig))
result.warnings.push(['warningPreSharedKeyMissing', netConfig.Name]);
} else {
onc.validateVpnCerts(ipsecConfig, index, oncData, result);
}
} else if (vpnConfig.Type == 'OpenVPN') {
onc.validateVpnUserCredentials(vpnConfig.OpenVPN, index,
oncData, result)
onc.validateVpnCerts(vpnConfig.OpenVPN, index, oncData, result);
} else {
result.warnings.push(['errorUnsupportedVPNType', netConfig.Name,
vpnConfig.Type]);
result.hasOpaqueEntity = true;
return result;
}
return result;
};
})();
})();
})();
/**
* Show the results in the dialog.
* @param {Object} result Result of compiling the UI to ONC.
* @param {Object} dialog DOM node of dialog.
*/
ui.showMessages = function(result, dialog) {
$('#apply-errors', dialog).html(ui.convertMessagesToHtml
(result.errors));
$('#apply-warnings', dialog).html(ui.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.
*/
loadDialog.handleLoadFile = function(files) {
$('#apply-errors').html('');
for (var i = 0; i < files.length; ++i) {
var file = files[i];
var reader = new FileReader();
reader.onload = function(theFile) {
loadDialog.loadConfig(this.result);
};
reader.readAsBinaryString(file);
}
return false;
};
/**
* Configures the ONC load file picker.
*/
loadDialog.configureLoadFilePicker = function() {
$('#load-file').change(function(event) {
loadDialog.handleLoadFile(event.target.files);
});
};
/**
* Handles apply button press in load dialog.
*/
loadDialog.onApplyPress = function() {
// Ignore apply button if nothing was loaded or there are errors.
main.oncCurrent = loadDialog.oncToLoad;
ui.dismissDialog();
};
/**
* Handles loading the body of the extension. Called from onload
* event handler.
*/
ui.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() {
ui.translateText();
$('div.selectable').click(function() {
var id = $(this)[0].id;
ui.openDialog(id.split('-')[0]);
});
// Allow clicking anywhere on the text next to a checkbox to toggle
// the checkbox.
$('.checkable').mousedown(function(event) {
if (event.target.type != 'checkbox')
$(':checkbox', this).trigger('click');
});
});