// Copyright 2019 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
/**
* @fileoverview Utilities supporting network_config.mojom types. The strings
* returned in the getFooTypeString methods are used for looking up localized
* strings and for debugging. They are not intended to be drectly user facing.
*/
import {assert, assertNotReached} from '//resources/ash/common/assert.js';
import {loadTimeData} from '//resources/ash/common/load_time_data.m.js';
import {ActivationStateType, ApnProperties, AuthenticationType, ConfigProperties, DeviceStateProperties as MojomDeviceStateProperties, HiddenSsidMode, InhibitReason, IPConfigProperties, ManagedApnList, ManagedBoolean, ManagedInt32, ManagedProperties, ManagedString, ManagedStringList, ManagedSubjectAltNameMatchList, MatchType, NetworkStateProperties as MojomNetworkStateProperties, ProxyMode, SecurityType, SIMInfo, SIMLockStatus, SubjectAltName, SubjectAltName_Type, TetherStateProperties, TrafficCounterProperties, VpnType} from '//resources/mojo/chromeos/services/network_config/public/mojom/cros_network_config.mojom-webui.js';
import {ConnectionStateType, DeviceStateType, IPConfigType, NetworkType, OncSource, PolicySource, PortalState} from '//resources/mojo/chromeos/services/network_config/public/mojom/network_types.mojom-webui.js';
import {IPAddress} from '//resources/mojo/services/network/public/mojom/ip_address.mojom-webui.js';
// Used to indicate a saved but unknown credential value. Will appear as
// placeholder character in the credential (passphrase, password, etc.) field by
// default.
// See |kFakeCredential| in chromeos/network/policy_util.h.
/** @type {string} */
export const FAKE_CREDENTIAL = 'FAKE_CREDENTIAL_VPaJDV9x';
/**
* Regex expression to validate RFC compliant DNS characters.
*/
const VALID_DNS_CHARS_REGEX = RegExp('^[a-zA-Z0-9-\\.]*$');
export class OncMojo {
/**
* @param {number|undefined} value
* @return {string}
*/
static getEnumString(value) {
if (value === undefined) {
return 'undefined';
}
return value.toString();
}
/**
* @param {!ActivationStateType} value
* @return {string}
*/
static getActivationStateTypeString(value) {
switch (value) {
case ActivationStateType.kUnknown:
return 'Unknown';
case ActivationStateType.kNotActivated:
return 'NotActivated';
case ActivationStateType.kActivating:
return 'Activating';
case ActivationStateType.kPartiallyActivated:
return 'PartiallyActivated';
case ActivationStateType.kActivated:
return 'Activated';
case ActivationStateType.kNoService:
return 'NoService';
}
assertNotReached('Unexpected enum value: ' + OncMojo.getEnumString(value));
return '';
}
/**
* @param {string} value
* @return {!ActivationStateType}
*/
static getActivationStateTypeFromString(value) {
switch (value) {
case 'Unknown':
return ActivationStateType.kUnknown;
case 'NotActivated':
return ActivationStateType.kNotActivated;
case 'Activating':
return ActivationStateType.kActivating;
case 'PartiallyActivated':
return ActivationStateType.kPartiallyActivated;
case 'Activated':
return ActivationStateType.kActivated;
case 'NoService':
return ActivationStateType.kNoService;
}
assertNotReached('Unexpected value: ' + value);
return ActivationStateType.kUnknown;
}
/**
* @param {!PortalState} value
* @return {string}
*/
static getPortalStateString(value) {
switch (value) {
case PortalState.kUnknown:
return 'Unknown';
case PortalState.kOnline:
return 'Online';
case PortalState.kPortalSuspected:
return 'PortalSuspected';
case PortalState.kPortal:
return 'Portal';
case PortalState.kNoInternet:
return 'NoInternet';
}
assertNotReached('Unexpected enum value: ' + OncMojo.getEnumString(value));
return '';
}
/**
* @param {!ConnectionStateType} value
* @return {string}
*/
static getConnectionStateTypeString(value) {
switch (value) {
case ConnectionStateType.kOnline:
return 'Online';
case ConnectionStateType.kConnected:
return 'Connected';
case ConnectionStateType.kPortal:
return 'Portal';
case ConnectionStateType.kConnecting:
return 'Connecting';
case ConnectionStateType.kNotConnected:
return 'NotConnected';
}
assertNotReached('Unexpected enum value: ' + OncMojo.getEnumString(value));
return '';
}
/**
* @param {string} value
* @return {!ConnectionStateType}
*/
static getConnectionStateTypeFromString(value) {
switch (value) {
case 'Online':
return ConnectionStateType.kOnline;
case 'Connected':
return ConnectionStateType.kConnected;
case 'Portal':
return ConnectionStateType.kPortal;
case 'Connecting':
return ConnectionStateType.kConnecting;
case 'NotConnected':
return ConnectionStateType.kNotConnected;
}
assertNotReached('Unexpected value: ' + value);
return ConnectionStateType.kNotConnected;
}
/**
* @param {!ConnectionStateType} value
* @return {boolean}
*/
static connectionStateIsConnected(value) {
switch (value) {
case ConnectionStateType.kOnline:
case ConnectionStateType.kConnected:
case ConnectionStateType.kPortal:
return true;
case ConnectionStateType.kConnecting:
case ConnectionStateType.kNotConnected:
return false;
}
assertNotReached('Unexpected enum value: ' + OncMojo.getEnumString(value));
return false;
}
/**
* @param {!DeviceStateType} value
* @return {string}
*/
static getDeviceStateTypeString(value) {
switch (value) {
case DeviceStateType.kUninitialized:
return 'Uninitialized';
case DeviceStateType.kDisabled:
return 'Disabled';
case DeviceStateType.kDisabling:
return 'Disabling';
case DeviceStateType.kEnabling:
return 'Enabling';
case DeviceStateType.kEnabled:
return 'Enabled';
case DeviceStateType.kProhibited:
return 'Prohibited';
case DeviceStateType.kUnavailable:
return 'Unavailable';
}
assertNotReached('Unexpected enum value: ' + OncMojo.getEnumString(value));
return '';
}
/**
* @param {!DeviceStateType} value
* @return {boolean}
*/
static deviceStateIsIntermediate(value) {
switch (value) {
case DeviceStateType.kUninitialized:
case DeviceStateType.kDisabling:
case DeviceStateType.kEnabling:
case DeviceStateType.kUnavailable:
return true;
case DeviceStateType.kDisabled:
case DeviceStateType.kEnabled:
case DeviceStateType.kProhibited:
return false;
}
assertNotReached('Unexpected enum value: ' + OncMojo.getEnumString(value));
return false;
}
/**
* @param {?MojomDeviceStateProperties|undefined} device
* @return {boolean}
*/
static deviceIsInhibited(device) {
if (!device) {
return false;
}
return device.inhibitReason !== InhibitReason.kNotInhibited;
}
/**
* @param {?MojomDeviceStateProperties|undefined} device
* @return {boolean}
*/
static deviceIsFlashing(device) {
if (!device) {
return false;
}
return device.isFlashing;
}
/**
* @param {!NetworkType} value
* @return {string}
*/
static getNetworkTypeString(value) {
switch (value) {
case NetworkType.kAll:
return 'All';
case NetworkType.kCellular:
return 'Cellular';
case NetworkType.kEthernet:
return 'Ethernet';
case NetworkType.kMobile:
return 'Mobile';
case NetworkType.kTether:
return 'Tether';
case NetworkType.kVPN:
return 'VPN';
case NetworkType.kWireless:
return 'Wireless';
case NetworkType.kWiFi:
return 'WiFi';
}
assertNotReached('Unexpected enum value: ' + OncMojo.getEnumString(value));
return '';
}
/**
* @param {!NetworkType} value
* @return {boolean}
*/
static networkTypeIsMobile(value) {
switch (value) {
case NetworkType.kCellular:
case NetworkType.kMobile:
case NetworkType.kTether:
return true;
case NetworkType.kAll:
case NetworkType.kEthernet:
case NetworkType.kVPN:
case NetworkType.kWireless:
case NetworkType.kWiFi:
return false;
}
assertNotReached('Unexpected enum value: ' + OncMojo.getEnumString(value));
return false;
}
/**
* @param {!NetworkType} value
* @return {boolean}
*/
static networkTypeHasConfigurationFlow(value) {
// Cellular networks are considered "configured" by their SIM, and Instant
// Tethering networks do not have a configuration flow.
return !OncMojo.networkTypeIsMobile(value);
}
/**
* @param {string} value
* @return {!NetworkType}
*/
static getNetworkTypeFromString(value) {
switch (value) {
case 'All':
return NetworkType.kAll;
case 'Cellular':
return NetworkType.kCellular;
case 'Ethernet':
return NetworkType.kEthernet;
case 'Mobile':
return NetworkType.kMobile;
case 'Tether':
return NetworkType.kTether;
case 'VPN':
return NetworkType.kVPN;
case 'Wireless':
return NetworkType.kWireless;
case 'WiFi':
return NetworkType.kWiFi;
}
assertNotReached('Unexpected value: ' + value);
return NetworkType.kAll;
}
/**
* @param {!OncSource} value
* @return {string}
*/
static getOncSourceString(value) {
switch (value) {
case OncSource.kNone:
return 'None';
case OncSource.kDevice:
return 'Device';
case OncSource.kDevicePolicy:
return 'DevicePolicy';
case OncSource.kUser:
return 'User';
case OncSource.kUserPolicy:
return 'UserPolicy';
}
assertNotReached('Unexpected enum value: ' + OncMojo.getEnumString(value));
return '';
}
/**
* @param {!SecurityType} value
* @return {string}
*/
static getSecurityTypeString(value) {
switch (value) {
case SecurityType.kNone:
return 'None';
case SecurityType.kWep8021x:
return 'WEP-8021X';
case SecurityType.kWepPsk:
return 'WEP-PSK';
case SecurityType.kWpaEap:
return 'WPA-EAP';
case SecurityType.kWpaPsk:
return 'WPA-PSK';
}
assertNotReached('Unexpected enum value: ' + OncMojo.getEnumString(value));
return '';
}
/**
* @param {string} value
* @return {!SecurityType}
*/
static getSecurityTypeFromString(value) {
switch (value) {
case 'None':
return SecurityType.kNone;
case 'WEP-8021X':
return SecurityType.kWep8021x;
case 'WEP-PSK':
return SecurityType.kWepPsk;
case 'WPA-EAP':
return SecurityType.kWpaEap;
case 'WPA-PSK':
return SecurityType.kWpaPsk;
}
assertNotReached('Unexpected value: ' + value);
return SecurityType.kNone;
}
/**
* @param {!VpnType} value
* @return {string}
*/
static getVpnTypeString(value) {
switch (value) {
case VpnType.kIKEv2:
return 'IKEv2';
case VpnType.kL2TPIPsec:
return 'L2TP-IPsec';
case VpnType.kOpenVPN:
return 'OpenVPN';
case VpnType.kWireGuard:
return 'WireGuard';
case VpnType.kExtension:
return 'ThirdPartyVPN';
case VpnType.kArc:
return 'ARCVPN';
}
assertNotReached('Unexpected enum value: ' + OncMojo.getEnumString(value));
return '';
}
/**
* This infers the type from |key|, casts |value| (which should be a number)
* to the corresponding enum type, and converts it to a string. If |key| is
* known, then |value| is expected to match an enum value. Otherwise |value|
* is simply returned.
* @param {string} key
* @param {number|string} value
* @return {number|string}
*/
static getTypeString(key, value) {
if (key === 'activationState') {
return OncMojo.getActivationStateTypeString(
/** @type {!ActivationStateType} */ (value));
}
if (key === 'connectionState') {
return OncMojo.getConnectionStateTypeString(
/** @type {!ConnectionStateType} */ (value));
}
if (key === 'deviceState') {
return OncMojo.getDeviceStateTypeString(
/** @type {!DeviceStateType} */ (value));
}
if (key === 'type') {
return OncMojo.getNetworkTypeString(
/** @type {!NetworkType} */ (value));
}
if (key === 'source') {
return OncMojo.getOncSourceString(
/** @type {!OncSource} */ (value));
}
if (key === 'security') {
return OncMojo.getSecurityTypeString(
/** @type {!SecurityType} */ (value));
}
return value;
}
/**
* Policy indicators expect a per-property PolicySource, but sometimes we need
* to use the per-configuration OncSource (e.g. for unmanaged intrinsic
* properties like Security). This returns the corresponding PolicySource.
* @param {!OncSource} source
* @return {!PolicySource}
*/
static getEnforcedPolicySourceFromOncSource(source) {
switch (source) {
case OncSource.kNone:
case OncSource.kDevice:
case OncSource.kUser:
return PolicySource.kNone;
case OncSource.kDevicePolicy:
return PolicySource.kDevicePolicyEnforced;
case OncSource.kUserPolicy:
return PolicySource.kUserPolicyEnforced;
}
assert(source !== undefined, 'OncSource undefined');
assertNotReached('Invalid OncSource: ' + source.toString());
return PolicySource.kNone;
}
/**
* @param {!NetworkType} type
* @return {string}
*/
static getNetworkTypeDisplayName(type) {
return loadTimeData.getStringF(
'OncType' + OncMojo.getNetworkTypeString(type));
}
/**
* WARNING: The string returned by this method may contain malicious HTML and
* should not be used for Polymer bindings in CSS code. For additional
* information see b/286254915.
*
* @param {!MojomNetworkStateProperties} network
* @return {string}
*/
static getNetworkStateDisplayNameUnsafe(network) {
if (!network.name) {
return OncMojo.getNetworkTypeDisplayName(network.type);
}
if (network.type === NetworkType.kVPN &&
network.typeState.vpn.providerName) {
return loadTimeData.getStringF(
'vpnNameTemplate', network.typeState.vpn.providerName, network.name);
}
return network.name;
}
/**
* WARNING: The string returned by this method may contain malicious HTML and
* should not be used for Polymer bindings in CSS code. For additional
* information see b/286254915.
*
* @param {!ManagedProperties} network
* @return {string}
*/
static getNetworkNameUnsafe(network) {
if (!network.name || !network.name.activeValue) {
return OncMojo.getNetworkTypeDisplayName(network.type);
}
if (network.type === NetworkType.kVPN &&
network.typeProperties.vpn.providerName) {
return loadTimeData.getStringF(
'vpnNameTemplate', network.typeProperties.vpn.providerName,
network.name.activeValue);
}
return network.name.activeValue;
}
/**
* Gets the SignalStrength value from |network| based on network.type.
* @param {!MojomNetworkStateProperties} network
* @return {number} The signal strength value if it exists or 0.
*/
static getSignalStrength(network) {
switch (network.type) {
case NetworkType.kCellular:
return network.typeState.cellular.signalStrength;
case NetworkType.kTether:
return network.typeState.tether.signalStrength;
case NetworkType.kWiFi:
return network.typeState.wifi.signalStrength;
}
assertNotReached();
return 0;
}
/**
* Determines whether a connection to |network| can be attempted. Note that
* this function does not consider policies which may block a connection from
* succeeding.
* @param {!MojomNetworkStateProperties|
* !ManagedProperties} network
* @return {boolean} Whether the network can currently be connected; if the
* network is not connectable, it must first be configured.
*/
static isNetworkConnectable(network) {
// Networks without a configuration flow are always connectable since no
// additional configuration can be performed to attempt a connection.
if (!OncMojo.networkTypeHasConfigurationFlow(network.type)) {
return true;
}
return network.connectable;
}
/**
* @param {string} key
* @return {boolean}
*/
static isTypeKey(key) {
return key.startsWith('cellular') || key.startsWith('ethernet') ||
key.startsWith('tether') || key.startsWith('vpn') ||
key.startsWith('wifi');
}
/**
* This is a bit of a hack. To avoid adding 'typeProperties' to every type
* specific field name and translated string, we check for type specific
* key names and prepend 'typeProperties' for them.
* @param {string} key
* @return {string}
*/
static getManagedPropertyKey(key) {
if (OncMojo.isTypeKey(key)) {
key = 'typeProperties.' + key;
}
return key;
}
/**
* Returns a NetworkStateProperties object with type set and default values.
* @param {!NetworkType} type
* @param {?string=} opt_name Optional name, intended for testing.
* @return {!MojomNetworkStateProperties}
*/
static getDefaultNetworkState(type, opt_name) {
const result = {
connectable: false,
connectRequested: false,
connectionState: ConnectionStateType.kNotConnected,
guid: opt_name ? (opt_name + '_guid') : '',
name: opt_name || '',
portalState: PortalState.kUnknown,
priority: 0,
proxyMode: ProxyMode.kDirect,
prohibitedByPolicy: false,
source: OncSource.kNone,
type: type,
typeState: {},
};
switch (type) {
case NetworkType.kCellular:
result.typeState.cellular = {
iccid: '',
eid: '',
activationState: ActivationStateType.kUnknown,
networkTechnology: '',
roaming: false,
signalStrength: 0,
simLockEnabled: false,
simLocked: false,
simLockType: '',
hasNickName: false,
networkOperator: '',
};
break;
case NetworkType.kEthernet:
result.typeState.ethernet = {
authentication: AuthenticationType.kNone,
};
break;
case NetworkType.kTether:
result.typeState.tether = {
batteryPercentage: 0,
carrier: '',
hasConnectedToHost: false,
signalStrength: 0,
};
break;
case NetworkType.kVPN:
result.typeState.vpn = {
type: VpnType.kOpenVPN,
providerId: '',
providerName: '',
};
break;
case NetworkType.kWiFi:
result.typeState.wifi = {
bssid: '',
frequency: 0,
hexSsid: opt_name || '',
hiddenSsid: false,
security: SecurityType.kNone,
signalStrength: 0,
ssid: '',
passpointId: '',
visible: true,
};
break;
default:
assertNotReached();
}
return result;
}
/**
* Converts an ManagedProperties dictionary to NetworkStateProperties.
* Used to provide state properties to NetworkIcon.
* @param {!ManagedProperties} properties
* @return {!MojomNetworkStateProperties}
*/
static managedPropertiesToNetworkState(properties) {
const networkState = OncMojo.getDefaultNetworkState(properties.type);
networkState.connectable = properties.connectable;
networkState.connectionState = properties.connectionState;
networkState.guid = properties.guid;
if (properties.name) {
networkState.name = properties.name.activeValue;
}
if (properties.priority) {
networkState.priority = properties.priority.activeValue;
}
networkState.source = properties.source;
switch (properties.type) {
case NetworkType.kCellular:
const cellularProperties = properties.typeProperties.cellular;
networkState.typeState.cellular.iccid = cellularProperties.iccid || '';
networkState.typeState.cellular.eid = cellularProperties.eid || '';
networkState.typeState.cellular.activationState =
cellularProperties.activationState;
networkState.typeState.cellular.paymentPortal =
cellularProperties.paymentPortal;
networkState.typeState.cellular.networkTechnology =
cellularProperties.networkTechnology || '';
networkState.typeState.cellular.roaming =
cellularProperties.roamingState === 'Roaming';
networkState.typeState.cellular.signalStrength =
cellularProperties.signalStrength;
networkState.typeState.cellular.simLocked =
cellularProperties.simLocked;
networkState.typeState.cellular.simLockType =
cellularProperties.simLockType;
break;
case NetworkType.kEthernet:
networkState.typeState.ethernet.authentication =
OncMojo.getActiveValue(
properties.typeProperties.ethernet.authentication) === '8021X' ?
AuthenticationType.k8021x :
AuthenticationType.kNone;
break;
case NetworkType.kTether:
if (properties.typeProperties.tether) {
networkState.typeState.tether =
/** @type {!TetherStateProperties}*/ (
Object.assign({}, properties.typeProperties.tether));
}
break;
case NetworkType.kVPN:
networkState.typeState.vpn.providerName =
properties.typeProperties.vpn.providerName;
networkState.typeState.vpn.type = properties.typeProperties.vpn.type;
break;
case NetworkType.kWiFi:
const wifiProperties = properties.typeProperties.wifi;
networkState.typeState.wifi.bssid = wifiProperties.bssid || '';
networkState.typeState.wifi.frequency = wifiProperties.frequency;
networkState.typeState.wifi.hexSsid =
OncMojo.getActiveString(wifiProperties.hexSsid);
networkState.typeState.wifi.security = wifiProperties.security;
networkState.typeState.wifi.signalStrength =
wifiProperties.signalStrength;
networkState.typeState.wifi.ssid =
OncMojo.getActiveString(wifiProperties.ssid);
networkState.typeState.wifi.hiddenSsid =
!!OncMojo.getActiveValue(wifiProperties.hiddenSsid);
break;
}
return networkState;
}
/**
* Returns a ManagedProperties object with type, guid and name set, and all
* other required properties set to their default values.
* @param {!NetworkType} type
* @param {string} guid
* @param {string} name
* @return {!ManagedProperties}
*/
static getDefaultManagedProperties(type, guid, name) {
const result = {
connectionState: ConnectionStateType.kNotConnected,
source: OncSource.kNone,
type: type,
connectable: false,
guid: guid,
name: OncMojo.createManagedString(name),
ipAddressConfigType: OncMojo.createManagedString('DHCP'),
nameServersConfigType: OncMojo.createManagedString('DHCP'),
portalState: PortalState.kUnknown,
trafficCounterProperties: OncMojo.createTrafficCounterProperties(),
};
switch (type) {
case NetworkType.kCellular:
result.typeProperties = {
cellular: {
activationState: ActivationStateType.kUnknown,
signalStrength: 0,
simLocked: false,
simLockType: '',
supportNetworkScan: false,
},
};
break;
case NetworkType.kEthernet:
result.typeProperties = {
ethernet: {},
};
break;
case NetworkType.kTether:
result.typeProperties = {
tether: {
batteryPercentage: 0,
carrier: '',
hasConnectedToHost: false,
signalStrength: 0,
},
};
break;
case NetworkType.kVPN:
result.typeProperties = {
vpn: {
providerName: '',
type: VpnType.kOpenVPN,
openVpn: {},
},
};
break;
case NetworkType.kWiFi:
result.typeProperties = {
wifi: {
bssid: '',
frequency: 0,
ssid: OncMojo.createManagedString(''),
security: SecurityType.kNone,
signalStrength: 0,
isSyncable: false,
isConfiguredByActiveUser: false,
passpointId: '',
passpointMatchType: MatchType.kNoMatch,
},
};
break;
}
return result;
}
/**
* Returns a ConfigProperties object with a default networkType struct
* based on |type|.
* @param {!NetworkType} type
* @return {!ConfigProperties}
*/
static getDefaultConfigProperties(type) {
switch (type) {
case NetworkType.kCellular:
return {typeConfig: {cellular: {}}};
break;
case NetworkType.kEthernet:
return {typeConfig: {ethernet: {}}};
break;
case NetworkType.kVPN:
return {typeConfig: {vpn: {}}};
break;
case NetworkType.kWiFi:
// Note: wifi.security can not be changed, so |security| will be ignored
// for existing configurations.
return {
typeConfig: {
wifi: {
security: SecurityType.kNone,
hiddenSsid: HiddenSsidMode.kAutomatic,
},
},
};
break;
}
assertNotReached('Unexpected type: ' + type.toString());
return {typeConfig: {}};
}
/**
* Sets the value of a property in an mojo config dictionary.
* @param {!ConfigProperties} config
* @param {string} key The property key which may be nested, e.g. 'foo.bar'
* @param {boolean|number|string|!Object} value The property value
*/
static setConfigProperty(config, key, value) {
if (OncMojo.isTypeKey(key)) {
key = 'typeConfig.' + key;
}
while (true) {
const index = key.indexOf('.');
if (index < 0) {
break;
}
const keyComponent = key.substr(0, index);
if (!config.hasOwnProperty(keyComponent)) {
config[keyComponent] = {};
}
config = config[keyComponent];
key = key.substr(index + 1);
}
config[key] = value;
}
/**
* @param {!ManagedBoolean|
* !ManagedInt32|
* !ManagedString|
* !ManagedStringList|
* !ManagedApnList|
* !ManagedSubjectAltNameMatchList|
* null|undefined} property
* @return {boolean|number|string|!Array<string>|
* !Array<!ApnProperties>|undefined}
*/
static getActiveValue(property) {
if (!property) {
return undefined;
}
return property.activeValue;
}
/**
* @param {?ManagedString|undefined} property
* @return {string}
*/
static getActiveString(property) {
if (!property) {
return '';
}
return property.activeValue;
}
/**
* Returns IPConfigProperties for |type|. For IPv4, these will be the static
* properties if IPAddressConfigType is Static and StaticIPConfig is set.
* @param {!ManagedProperties} properties
* @param {!IPConfigType} desiredType
* @return {!IPConfigProperties|undefined}
*/
static getIPConfigForType(properties, desiredType) {
const ipConfigs = properties.ipConfigs;
let ipConfig;
if (ipConfigs) {
ipConfig = ipConfigs.find(ipconfig => ipconfig.type === desiredType);
if (ipConfig && desiredType !== IPConfigType.kIPv4) {
return ipConfig;
}
}
// Only populate static ip config properties for IPv4.
if (desiredType !== IPConfigType.kIPv4) {
return undefined;
}
if (!ipConfig) {
ipConfig = /** @type {!IPConfigProperties} */ ({routingPrefix: 0});
}
const staticIpConfig = properties.staticIpConfig;
if (!staticIpConfig) {
return ipConfig;
}
// Merge the appropriate static values into the result.
if (properties.ipAddressConfigType &&
properties.ipAddressConfigType.activeValue === 'Static') {
if (staticIpConfig.gateway) {
ipConfig.gateway = staticIpConfig.gateway.activeValue;
}
if (staticIpConfig.ipAddress) {
ipConfig.ipAddress = staticIpConfig.ipAddress.activeValue;
}
if (staticIpConfig.routingPrefix) {
ipConfig.routingPrefix = staticIpConfig.routingPrefix.activeValue;
}
ipConfig.type = staticIpConfig.type;
}
if (properties.nameServersConfigType &&
properties.nameServersConfigType.activeValue === 'Static') {
if (staticIpConfig.nameServers) {
ipConfig.nameServers = staticIpConfig.nameServers.activeValue;
}
}
return ipConfig;
}
/**
* Compares two IP config property dictionaries. Returns true if all
* properties specified in the new dictionary match the values in the existing
* dictionary.
* @param {!IPConfigProperties} staticValue
* @param {!IPConfigProperties} newValue
* @return {boolean} True if all properties set in |newValue| are equal to
* the corresponding properties in |staticValue|.
*/
static ipConfigPropertiesMatch(staticValue, newValue) {
if (staticValue.type !== newValue.type) {
return false;
}
if (newValue.gateway !== undefined &&
(staticValue.gateway !== newValue.gateway)) {
return false;
}
if (newValue.ipAddress !== undefined &&
staticValue.ipAddress !== newValue.ipAddress) {
return false;
}
if (staticValue.routingPrefix !== newValue.routingPrefix) {
return false;
}
return true;
}
/**
* Extracts existing ip config properties from |managedProperties| and applies
* |newValue| to |field|. Returns a ConfigProperties object with the
* IP Config related properties set, or null if no changes were applied.
* @param {!ManagedProperties} managedProperties
* @param {string} field
* @param {string|!Array<string>|
* !IPConfigProperties} newValue
* @return {?ConfigProperties}
*/
static getUpdatedIPConfigProperties(managedProperties, field, newValue) {
// Get an empty ONC dictionary and set just the IP Config properties that
// need to change.
let ipConfigType =
OncMojo.getActiveString(managedProperties.ipAddressConfigType) ||
'DHCP';
let nsConfigType =
OncMojo.getActiveString(managedProperties.nameServersConfigType) ||
'DHCP';
let staticIpConfig =
OncMojo.getIPConfigForType(managedProperties, IPConfigType.kIPv4);
let nameServers = staticIpConfig ? staticIpConfig.nameServers : undefined;
if (field === 'ipAddressConfigType') {
const newIpConfigType = /** @type {string} */ (newValue);
if (newIpConfigType === ipConfigType) {
return null;
}
ipConfigType = newIpConfigType;
} else if (field === 'nameServersConfigType') {
const newNsConfigType = /** @type {string} */ (newValue);
if (newNsConfigType === nsConfigType) {
return null;
}
nsConfigType = newNsConfigType;
} else if (field === 'staticIpConfig') {
const ipConfigValue =
/** @type {!IPConfigProperties} */ (newValue);
if (!ipConfigValue.ipAddress) {
console.error('Invalid StaticIPConfig: ' + JSON.stringify(newValue));
return null;
}
if (ipConfigType === 'Static' && staticIpConfig &&
OncMojo.ipConfigPropertiesMatch(staticIpConfig, ipConfigValue)) {
return null;
}
ipConfigType = 'Static';
staticIpConfig = ipConfigValue;
} else if (field === 'nameServers') {
const newNameServers = /** @type {!Array<string>} */ (newValue);
if (!newNameServers || !newNameServers.length) {
console.error('Invalid NameServers: ' + JSON.stringify(newValue));
}
if (nsConfigType === 'Static' &&
JSON.stringify(nameServers) === JSON.stringify(newNameServers)) {
return null;
}
nsConfigType = 'Static';
nameServers = newNameServers;
} else {
console.error('Unexpected field: ' + field);
return null;
}
// Set ONC IP config properties to existing values + new values.
const config = OncMojo.getDefaultConfigProperties(managedProperties.type);
config.ipAddressConfigType = ipConfigType;
config.nameServersConfigType = nsConfigType;
if (ipConfigType === 'Static') {
assert(staticIpConfig && staticIpConfig.ipAddress);
config.staticIpConfig = staticIpConfig;
}
if (nsConfigType === 'Static') {
assert(nameServers && nameServers.length);
config.staticIpConfig = config.staticIpConfig ||
/** @type {!IPConfigProperties}*/ ({routingPrefix: 0});
config.staticIpConfig.nameServers = nameServers;
}
return config;
}
/**
* @param {!ManagedProperties} properties
* @return {ManagedBoolean|undefined}
*/
static getManagedAutoConnect(properties) {
const type = properties.type;
switch (type) {
case NetworkType.kCellular:
return properties.typeProperties.cellular.autoConnect;
case NetworkType.kVPN:
return properties.typeProperties.vpn.autoConnect;
case NetworkType.kWiFi:
return properties.typeProperties.wifi.autoConnect;
}
return undefined;
}
/**
* @param {string} s
* @return {!ManagedString}
*/
static createManagedString(s) {
return {
activeValue: s,
policySource: PolicySource.kNone,
policyValue: undefined,
};
}
/**
* @param {number} n
* @return {!ManagedInt32}
*/
static createManagedInt(n) {
return {
activeValue: n,
policySource: PolicySource.kNone,
policyValue: 0,
};
}
/**
* @param {boolean} b
* @return {!ManagedBoolean}
*/
static createManagedBool(b) {
return {
activeValue: b,
policySource: PolicySource.kNone,
policyValue: false,
};
}
/**
* @return {!TrafficCounterProperties}
*/
static createTrafficCounterProperties() {
return {
lastResetTime: null,
autoReset: false,
userSpecifiedResetDay: 1,
};
}
/**
* Returns a string to translate for the user visible connection state.
* @param {!ConnectionStateType}
* connectionState
* @return {string}
*/
static getConnectionStateString(connectionState) {
switch (connectionState) {
case ConnectionStateType.kOnline:
case ConnectionStateType.kConnected:
case ConnectionStateType.kPortal:
return 'OncConnected';
case ConnectionStateType.kConnecting:
return 'OncConnecting';
case ConnectionStateType.kNotConnected:
return 'OncNotConnected';
}
assertNotReached();
return 'OncNotConnected';
}
/**
* Returns true the IPAddress bytes match.
* @param {?IPAddress|undefined} a
* @param {?IPAddress|undefined} b
* @return {boolean}
*/
static ipAddressMatch(a, b) {
if (!a || !b) {
return !!a === !!b;
}
const abytes = a.addressBytes;
const bbytes = b.addressBytes;
if (abytes.length !== bbytes.length) {
return false;
}
for (let i = 0; i < abytes.length; ++i) {
if (abytes[i] !== bbytes[i]) {
return false;
}
}
return true;
}
/**
* Returns true the SIMLockStatus properties match.
* @param {?SIMLockStatus|undefined} a
* @param {?SIMLockStatus|undefined} b
* @return {boolean}
*/
static simLockStatusMatch(a, b) {
if (!a || !b) {
return !!a === !!b;
}
return a.lockType === b.lockType && a.lockEnabled === b.lockEnabled &&
a.retriesLeft === b.retriesLeft;
}
/**
* Returns true if the SIMInfos match.
* @param {?Array<SIMInfo>|undefined} a
* @param {?Array<SIMInfo>|undefined} b
*/
static simInfosMatch(a, b) {
if (!a || !b) {
return !!a === !!b;
}
if (a.length !== b.length) {
return false;
}
for (let i = 0; i < a.length; i++) {
const acurrent = a[i];
const bcurrent = b[i];
if (acurrent.slotId !== bcurrent.slotId ||
acurrent.eid !== bcurrent.eid || acurrent.iccid !== bcurrent.iccid ||
acurrent.isPrimary !== bcurrent.isPrimary) {
return false;
}
}
return true;
}
/**
* Returns true if the APN properties match.
* @param {ApnProperties} a
* @param {ApnProperties} b
* @return {boolean}
*/
static apnMatch(a, b) {
if (!a || !b) {
return !!a === !!b;
}
return a.accessPointName === b.accessPointName && a.name === b.name &&
a.username === b.username && a.password === b.password;
}
/**
* Returns true if the APN List matches.
* @param {Array<!ApnProperties>|undefined} a
* @param {Array<!ApnProperties>|undefined} b
* @return {boolean}
*/
static apnListMatch(a, b) {
if (!a || !b) {
return !!a === !!b;
}
if (a.length !== b.length) {
return false;
}
return a.every((apn, index) => OncMojo.apnMatch(apn, b[index]));
}
/**
* Returns true if the portal state has restricted connectivity.
* @param {!PortalState|undefined} portal
* @return {boolean}
*/
static isRestrictedConnectivity(portal) {
if (portal === undefined) {
return false;
}
switch (portal) {
case PortalState.kUnknown:
case PortalState.kOnline:
return false;
case PortalState.kPortalSuspected:
case PortalState.kPortal:
case PortalState.kNoInternet:
return true;
}
assertNotReached();
return false;
}
/**
* Returns a string representation of the DomainSuffixMatch, formatted as a
* semicolon separated string of entries.
* See https://w1.fi/cgit/hostap/plain/wpa_supplicant/wpa_supplicant.conf.
* @param {!Array<!string>} domainSuffixMatch
* @return {string}
*/
static serializeDomainSuffixMatch(domainSuffixMatch) {
if (!domainSuffixMatch || domainSuffixMatch.length === 0) {
return '';
}
return domainSuffixMatch.join(';');
}
/**
* Converts the string representation of the DomainSuffixMatch to a mojo
* object. Returns null if `domainSuffixMatch` contains non-RFC compliant
* characters.
* @param {string} domainSuffixMatch
* @return {?Array<!string>}
*/
static deserializeDomainSuffixMatch(domainSuffixMatch) {
const entries = domainSuffixMatch.trim().split(';');
const result = [];
for (const e of entries) {
const value = VALID_DNS_CHARS_REGEX.exec(e);
if (!value || value.length !== 1) {
console.warn('Invalid Domain Suffix Match entry: ' + e);
return null;
}
const entry = value[0].trim();
if (entry !== '') {
result.push(value[0]);
}
}
return result;
}
/**
* Returns a string representation of the SubjectAlternativeNameMatch,
* formatted as a semicolon separated string of entries in the following
* format: <type>:<value>.
* See https://w1.fi/cgit/hostap/plain/wpa_supplicant/wpa_supplicant.conf.
* @param {!Array<!SubjectAltName>}
* subjectAltNameMatch
* @return {string}
*/
static serializeSubjectAltNameMatch(subjectAltNameMatch) {
if (!subjectAltNameMatch || subjectAltNameMatch.length === 0) {
return '';
}
const result = [];
for (const e of subjectAltNameMatch) {
let type;
switch (e.type) {
case SubjectAltName_Type.kEmail:
type = 'EMAIL';
break;
case SubjectAltName_Type.kDns:
type = 'DNS';
break;
case SubjectAltName_Type.kUri:
type = 'URI';
break;
default:
assertNotReached('Unknown subjectAltNameMatchType ' + e.type);
}
result.push(type + ':' + e.value);
}
return result.join(';');
}
/**
* Converts the string representation of the DomainSuffixMatch to a mojo
* object. Returns null if `subjectAltNameMatch` contains:
* - entries not in the format <type>:<value>;
* - a type other than 'EMAIL', 'DNS', 'URI';
* - a value with non-RFC compliant characters.
* @param {string} subjectAltNameMatch
* @return {?Array<!SubjectAltName>}
*/
static deserializeSubjectAltNameMatch(subjectAltNameMatch) {
const regValidEmailChars = RegExp('^[a-zA-Z0-9-\\.\\+_~@]*$');
const regValidUriChars =
RegExp('^[a-zA-Z0-9-\\._~:/?#\\[\\]@!$&\'()\\*\\+,;=]*$');
const entries = subjectAltNameMatch.trim().split(';');
const result =
/*@type {Array<!SubjectAltName>}*/[];
for (const entry of entries) {
if (entry === '') {
continue;
}
let type;
let value;
if (entry.toUpperCase().startsWith('EMAIL:')) {
type = SubjectAltName_Type.kEmail;
value = regValidEmailChars.exec(entry.substring(6));
} else if (entry.toUpperCase().startsWith('DNS:')) {
type = SubjectAltName_Type.kDns;
value = VALID_DNS_CHARS_REGEX.exec(entry.substring(4));
} else if (entry.toUpperCase().startsWith('URI:')) {
type = SubjectAltName_Type.kUri;
value = regValidUriChars.exec(entry.substring(4));
} else {
console.warn('Invalid Subject Alternative Name Match type ' + entry);
return null;
}
if (!value || value.length !== 1) {
console.warn('Invalid Subject Alternative Name Match value ' + entry);
return null;
}
result.push(/* @type {!SubjectAltName} */ {
type: type,
value: value[0],
});
}
return result;
}
}
/**
* The value of ApnProperties.attach must be equivalent to this value
* in order for an Attach APN to occur.
*/
OncMojo.USE_ATTACH_APN_NAME = 'attach';
/** @typedef {MojomDeviceStateProperties} */
OncMojo.DeviceStateProperties;
/** @typedef {MojomNetworkStateProperties} */
OncMojo.NetworkStateProperties;
/**
* @typedef {ManagedBoolean|
* ManagedInt32|
* ManagedString|
* ManagedStringList|
* ManagedApnList}
*/
OncMojo.ManagedProperty;
/**
* Modified version of IPConfigProperties to store routingPrefix as
* a human-readable netmask string instead of as a number. Used in
* network_ip_config.js.
* @typedef {{
* gateway: (string|undefined),
* ipAddress: (string|undefined),
* nameServers: (Array<string>|undefined),
* netmask: (string|undefined),
* type: !IPConfigType,
* webProxyAutoDiscoveryUrl: (string|undefined),
* }}
*/
OncMojo.IPConfigUIProperties;