| // Copyright (c) 2012 The Chromium Authors. All rights reserved. |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| #include <string> |
| #include <utility> |
| |
| #include "base/json/json_reader.h" |
| #include "base/json/json_writer.h" |
| #include "base/logging.h" |
| #include "base/macros.h" |
| #include "base/memory/ptr_util.h" |
| #include "base/strings/string_util.h" |
| #include "base/values.h" |
| #include "chromeos/network/network_profile_handler.h" |
| #include "chromeos/network/network_state.h" |
| #include "chromeos/network/network_util.h" |
| #include "chromeos/network/onc/onc_signature.h" |
| #include "chromeos/network/onc/onc_translation_tables.h" |
| #include "chromeos/network/onc/onc_translator.h" |
| #include "chromeos/network/onc/onc_utils.h" |
| #include "chromeos/network/shill_property_util.h" |
| #include "components/onc/onc_constants.h" |
| #include "third_party/cros_system_api/dbus/service_constants.h" |
| |
| namespace chromeos { |
| namespace onc { |
| |
| namespace { |
| |
| // Converts |str| to a base::Value of the given |type|. If the conversion fails, |
| // returns NULL. |
| std::unique_ptr<base::Value> ConvertStringToValue(const std::string& str, |
| base::Value::Type type) { |
| std::unique_ptr<base::Value> value; |
| if (type == base::Value::Type::STRING) { |
| value.reset(new base::Value(str)); |
| } else { |
| value = base::JSONReader::Read(str); |
| } |
| if (value && value->GetType() != type) |
| return nullptr; |
| |
| return value; |
| } |
| |
| // This class implements the translation of properties from the given |
| // |shill_dictionary| to a new ONC object of signature |onc_signature|. Using |
| // recursive calls to CreateTranslatedONCObject of new instances, nested objects |
| // are translated. |
| class ShillToONCTranslator { |
| public: |
| ShillToONCTranslator(const base::DictionaryValue& shill_dictionary, |
| ::onc::ONCSource onc_source, |
| const OncValueSignature& onc_signature, |
| const NetworkState* network_state) |
| : shill_dictionary_(&shill_dictionary), |
| onc_source_(onc_source), |
| onc_signature_(&onc_signature), |
| network_state_(network_state) { |
| field_translation_table_ = GetFieldTranslationTable(onc_signature); |
| } |
| |
| ShillToONCTranslator(const base::DictionaryValue& shill_dictionary, |
| ::onc::ONCSource onc_source, |
| const OncValueSignature& onc_signature, |
| const FieldTranslationEntry* field_translation_table, |
| const NetworkState* network_state) |
| : shill_dictionary_(&shill_dictionary), |
| onc_source_(onc_source), |
| onc_signature_(&onc_signature), |
| field_translation_table_(field_translation_table), |
| network_state_(network_state) {} |
| |
| // Translates the associated Shill dictionary and creates an ONC object of the |
| // given signature. |
| std::unique_ptr<base::DictionaryValue> CreateTranslatedONCObject(); |
| |
| private: |
| void TranslateEthernet(); |
| void TranslateOpenVPN(); |
| void TranslateIPsec(); |
| void TranslateThirdPartyVPN(); |
| void TranslateVPN(); |
| void TranslateWiFiWithState(); |
| void TranslateWiMAXWithState(); |
| void TranslateCellularWithState(); |
| void TranslateCellularDevice(); |
| void TranslateNetworkWithState(); |
| void TranslateIPConfig(); |
| void TranslateSavedOrStaticIPConfig(); |
| void TranslateSavedIPConfig(); |
| void TranslateStaticIPConfig(); |
| void TranslateEap(); |
| |
| // Creates an ONC object from |dictionary| according to the signature |
| // associated to |onc_field_name| and adds it to |onc_object_| at |
| // |onc_field_name|. |
| void TranslateAndAddNestedObject(const std::string& onc_field_name, |
| const base::DictionaryValue& dictionary); |
| |
| // Creates an ONC object from |shill_dictionary_| according to the signature |
| // associated to |onc_field_name| and adds it to |onc_object_| at |
| // |onc_field_name|. |
| void TranslateAndAddNestedObject(const std::string& onc_field_name); |
| |
| // Sets |onc_field_name| in dictionary |onc_dictionary_name| in |onc_object_| |
| // to |value| if the dictionary exists. |
| void SetNestedOncValue(const std::string& onc_dictionary_name, |
| const std::string& onc_field_name, |
| const base::Value& value); |
| |
| // Translates a list of nested objects and adds the list to |onc_object_| at |
| // |onc_field_name|. If there are errors while parsing individual objects or |
| // if the resulting list contains no entries, the result will not be added to |
| // |onc_object_|. |
| void TranslateAndAddListOfObjects(const std::string& onc_field_name, |
| const base::ListValue& list); |
| |
| // Applies function CopyProperty to each field of |value_signature| and its |
| // base signatures. |
| void CopyPropertiesAccordingToSignature( |
| const OncValueSignature* value_signature); |
| |
| // Applies function CopyProperty to each field of |onc_signature_| and its |
| // base signatures. |
| void CopyPropertiesAccordingToSignature(); |
| |
| // If |shill_property_name| is defined in |field_signature|, copies this |
| // entry from |shill_dictionary_| to |onc_object_| if it exists. |
| void CopyProperty(const OncFieldSignature* field_signature); |
| |
| // If existent, translates the entry at |shill_property_name| in |
| // |shill_dictionary_| using |table|. It is an error if no matching table |
| // entry is found. Writes the result as entry at |onc_field_name| in |
| // |onc_object_|. |
| void TranslateWithTableAndSet(const std::string& shill_property_name, |
| const StringTranslationEntry table[], |
| const std::string& onc_field_name); |
| |
| // Returns the name of the Shill service provided in |shill_dictionary_| |
| // for debugging. |
| std::string GetName(); |
| |
| const base::DictionaryValue* shill_dictionary_; |
| ::onc::ONCSource onc_source_; |
| const OncValueSignature* onc_signature_; |
| const FieldTranslationEntry* field_translation_table_; |
| std::unique_ptr<base::DictionaryValue> onc_object_; |
| const NetworkState* network_state_; |
| |
| DISALLOW_COPY_AND_ASSIGN(ShillToONCTranslator); |
| }; |
| |
| std::unique_ptr<base::DictionaryValue> |
| ShillToONCTranslator::CreateTranslatedONCObject() { |
| onc_object_.reset(new base::DictionaryValue); |
| if (onc_signature_ == &kNetworkWithStateSignature) { |
| TranslateNetworkWithState(); |
| } else if (onc_signature_ == &kEthernetSignature) { |
| TranslateEthernet(); |
| } else if (onc_signature_ == &kVPNSignature) { |
| TranslateVPN(); |
| } else if (onc_signature_ == &kOpenVPNSignature) { |
| TranslateOpenVPN(); |
| } else if (onc_signature_ == &kIPsecSignature) { |
| TranslateIPsec(); |
| } else if (onc_signature_ == &kThirdPartyVPNSignature) { |
| TranslateThirdPartyVPN(); |
| } else if (onc_signature_ == &kWiFiWithStateSignature) { |
| TranslateWiFiWithState(); |
| } else if (onc_signature_ == &kWiMAXWithStateSignature) { |
| TranslateWiMAXWithState(); |
| } else if (onc_signature_ == &kCellularWithStateSignature) { |
| if (field_translation_table_ == kCellularDeviceTable) |
| TranslateCellularDevice(); |
| else |
| TranslateCellularWithState(); |
| } else if (onc_signature_ == &kIPConfigSignature) { |
| TranslateIPConfig(); |
| } else if (onc_signature_ == &kSavedIPConfigSignature) { |
| TranslateSavedIPConfig(); |
| } else if (onc_signature_ == &kStaticIPConfigSignature) { |
| TranslateStaticIPConfig(); |
| } else if (onc_signature_ == &kEAPSignature) { |
| TranslateEap(); |
| } else { |
| CopyPropertiesAccordingToSignature(); |
| } |
| return std::move(onc_object_); |
| } |
| |
| void ShillToONCTranslator::TranslateEthernet() { |
| std::string shill_network_type; |
| shill_dictionary_->GetStringWithoutPathExpansion(shill::kTypeProperty, |
| &shill_network_type); |
| const char* onc_auth = ::onc::ethernet::kAuthenticationNone; |
| if (shill_network_type == shill::kTypeEthernetEap) |
| onc_auth = ::onc::ethernet::k8021X; |
| onc_object_->SetStringWithoutPathExpansion(::onc::ethernet::kAuthentication, |
| onc_auth); |
| } |
| |
| void ShillToONCTranslator::TranslateOpenVPN() { |
| if (shill_dictionary_->HasKey(shill::kOpenVPNVerifyX509NameProperty)) |
| TranslateAndAddNestedObject(::onc::openvpn::kVerifyX509); |
| |
| // Shill supports only one RemoteCertKU but ONC requires a list. If existing, |
| // wraps the value into a list. |
| std::string certKU; |
| if (shill_dictionary_->GetStringWithoutPathExpansion( |
| shill::kOpenVPNRemoteCertKUProperty, &certKU)) { |
| std::unique_ptr<base::ListValue> certKUs(new base::ListValue); |
| certKUs->AppendString(certKU); |
| onc_object_->SetWithoutPathExpansion(::onc::openvpn::kRemoteCertKU, |
| std::move(certKUs)); |
| } |
| |
| for (const OncFieldSignature* field_signature = onc_signature_->fields; |
| field_signature->onc_field_name != NULL; ++field_signature) { |
| const std::string& onc_field_name = field_signature->onc_field_name; |
| if (onc_field_name == ::onc::openvpn::kRemoteCertKU || |
| onc_field_name == ::onc::openvpn::kServerCAPEMs) { |
| CopyProperty(field_signature); |
| continue; |
| } |
| |
| std::string shill_property_name; |
| const base::Value* shill_value = NULL; |
| if (!field_translation_table_ || |
| !GetShillPropertyName(field_signature->onc_field_name, |
| field_translation_table_, &shill_property_name) || |
| !shill_dictionary_->GetWithoutPathExpansion(shill_property_name, |
| &shill_value)) { |
| continue; |
| } |
| |
| std::unique_ptr<base::Value> translated; |
| std::string shill_str; |
| if (shill_value->GetAsString(&shill_str)) { |
| // Shill wants all Provider/VPN fields to be strings. Translates these |
| // strings back to the correct ONC type. |
| translated = ConvertStringToValue( |
| shill_str, field_signature->value_signature->onc_type); |
| |
| if (translated.get() == NULL) { |
| LOG(ERROR) << "Shill property '" << shill_property_name |
| << "' with value " << *shill_value |
| << " couldn't be converted to base::Value::Type " |
| << field_signature->value_signature->onc_type << ": " |
| << GetName(); |
| } else { |
| onc_object_->SetWithoutPathExpansion(onc_field_name, |
| std::move(translated)); |
| } |
| } else { |
| LOG(ERROR) << "Shill property '" << shill_property_name << "' has value " |
| << *shill_value << ", but expected a string: " << GetName(); |
| } |
| } |
| } |
| |
| void ShillToONCTranslator::TranslateIPsec() { |
| CopyPropertiesAccordingToSignature(); |
| if (shill_dictionary_->HasKey(shill::kL2tpIpsecXauthUserProperty)) |
| TranslateAndAddNestedObject(::onc::ipsec::kXAUTH); |
| std::string client_cert_id; |
| shill_dictionary_->GetStringWithoutPathExpansion( |
| shill::kL2tpIpsecClientCertIdProperty, &client_cert_id); |
| std::string authentication_type = |
| client_cert_id.empty() ? ::onc::ipsec::kPSK : ::onc::ipsec::kCert; |
| onc_object_->SetStringWithoutPathExpansion(::onc::ipsec::kAuthenticationType, |
| authentication_type); |
| } |
| |
| void ShillToONCTranslator::TranslateThirdPartyVPN() { |
| CopyPropertiesAccordingToSignature(); |
| |
| // For third-party VPNs, |shill::kProviderHostProperty| is used to store the |
| // provider's extension ID. |
| std::string shill_extension_id; |
| shill_dictionary_->GetStringWithoutPathExpansion(shill::kHostProperty, |
| &shill_extension_id); |
| onc_object_->SetStringWithoutPathExpansion( |
| ::onc::third_party_vpn::kExtensionID, shill_extension_id); |
| } |
| |
| void ShillToONCTranslator::TranslateVPN() { |
| CopyPropertiesAccordingToSignature(); |
| |
| // Parse Shill Provider dictionary. Note, this may not exist, e.g. if we are |
| // just translating network state in network_util::TranslateNetworkStateToONC. |
| const base::DictionaryValue* provider = NULL; |
| if (!shill_dictionary_->GetDictionaryWithoutPathExpansion( |
| shill::kProviderProperty, &provider)) { |
| return; |
| } |
| std::string shill_provider_type, onc_provider_type; |
| provider->GetStringWithoutPathExpansion(shill::kTypeProperty, |
| &shill_provider_type); |
| if (!TranslateStringToONC(kVPNTypeTable, shill_provider_type, |
| &onc_provider_type)) { |
| return; |
| } |
| onc_object_->SetStringWithoutPathExpansion(::onc::vpn::kType, |
| onc_provider_type); |
| std::string shill_provider_host; |
| if (onc_provider_type != ::onc::vpn::kThirdPartyVpn && |
| provider->GetStringWithoutPathExpansion(shill::kHostProperty, |
| &shill_provider_host)) { |
| onc_object_->SetStringWithoutPathExpansion(::onc::vpn::kHost, |
| shill_provider_host); |
| } |
| |
| // Translate the nested dictionary. |
| std::string provider_type_dictionary; |
| if (onc_provider_type == ::onc::vpn::kTypeL2TP_IPsec) { |
| TranslateAndAddNestedObject(::onc::vpn::kIPsec, *provider); |
| TranslateAndAddNestedObject(::onc::vpn::kL2TP, *provider); |
| provider_type_dictionary = ::onc::vpn::kIPsec; |
| } else { |
| TranslateAndAddNestedObject(onc_provider_type, *provider); |
| provider_type_dictionary = onc_provider_type; |
| } |
| |
| bool save_credentials; |
| if (onc_provider_type != ::onc::vpn::kThirdPartyVpn && |
| shill_dictionary_->GetBooleanWithoutPathExpansion( |
| shill::kSaveCredentialsProperty, &save_credentials)) { |
| SetNestedOncValue(provider_type_dictionary, ::onc::vpn::kSaveCredentials, |
| base::Value(save_credentials)); |
| } |
| } |
| |
| void ShillToONCTranslator::TranslateWiFiWithState() { |
| std::string shill_security; |
| std::string shill_key_mgmt; |
| if (shill_dictionary_->GetStringWithoutPathExpansion( |
| shill::kSecurityClassProperty, &shill_security) && |
| shill_security == shill::kSecurityWep && |
| shill_dictionary_->GetStringWithoutPathExpansion( |
| shill::kEapKeyMgmtProperty, &shill_key_mgmt) && |
| shill_key_mgmt == shill::kKeyManagementIEEE8021X) { |
| onc_object_->SetStringWithoutPathExpansion(::onc::wifi::kSecurity, |
| ::onc::wifi::kWEP_8021X); |
| } else { |
| TranslateWithTableAndSet(shill::kSecurityClassProperty, kWiFiSecurityTable, |
| ::onc::wifi::kSecurity); |
| } |
| |
| bool unknown_encoding = true; |
| std::string ssid = shill_property_util::GetSSIDFromProperties( |
| *shill_dictionary_, false /* verbose_logging */, &unknown_encoding); |
| if (!unknown_encoding && !ssid.empty()) |
| onc_object_->SetStringWithoutPathExpansion(::onc::wifi::kSSID, ssid); |
| |
| bool link_monitor_disable; |
| if (shill_dictionary_->GetBooleanWithoutPathExpansion( |
| shill::kLinkMonitorDisableProperty, &link_monitor_disable)) { |
| onc_object_->SetBooleanWithoutPathExpansion( |
| ::onc::wifi::kAllowGatewayARPPolling, !link_monitor_disable); |
| } |
| |
| CopyPropertiesAccordingToSignature(); |
| TranslateAndAddNestedObject(::onc::wifi::kEAP); |
| } |
| |
| void ShillToONCTranslator::TranslateWiMAXWithState() { |
| CopyPropertiesAccordingToSignature(); |
| TranslateAndAddNestedObject(::onc::wimax::kEAP); |
| } |
| |
| void ShillToONCTranslator::TranslateCellularWithState() { |
| CopyPropertiesAccordingToSignature(); |
| TranslateWithTableAndSet(shill::kActivationStateProperty, |
| kActivationStateTable, |
| ::onc::cellular::kActivationState); |
| TranslateWithTableAndSet(shill::kNetworkTechnologyProperty, |
| kNetworkTechnologyTable, |
| ::onc::cellular::kNetworkTechnology); |
| const base::DictionaryValue* dictionary = NULL; |
| if (shill_dictionary_->GetDictionaryWithoutPathExpansion( |
| shill::kServingOperatorProperty, &dictionary)) { |
| TranslateAndAddNestedObject(::onc::cellular::kServingOperator, *dictionary); |
| } |
| if (shill_dictionary_->GetDictionaryWithoutPathExpansion( |
| shill::kCellularApnProperty, &dictionary)) { |
| TranslateAndAddNestedObject(::onc::cellular::kAPN, *dictionary); |
| } |
| if (shill_dictionary_->GetDictionaryWithoutPathExpansion( |
| shill::kCellularLastGoodApnProperty, &dictionary)) { |
| TranslateAndAddNestedObject(::onc::cellular::kLastGoodAPN, *dictionary); |
| } |
| if (shill_dictionary_->GetDictionaryWithoutPathExpansion( |
| shill::kPaymentPortalProperty, &dictionary)) { |
| TranslateAndAddNestedObject(::onc::cellular::kPaymentPortal, *dictionary); |
| } |
| |
| const base::DictionaryValue* device_dictionary = NULL; |
| bool requires_roaming = false; |
| shill_dictionary_->GetDictionaryWithoutPathExpansion(shill::kDeviceProperty, |
| &device_dictionary); |
| if (device_dictionary) { |
| // Merge the Device dictionary with this one (Cellular) using the |
| // CellularDevice signature. |
| ShillToONCTranslator nested_translator( |
| *device_dictionary, onc_source_, kCellularWithStateSignature, |
| kCellularDeviceTable, network_state_); |
| std::unique_ptr<base::DictionaryValue> nested_object = |
| nested_translator.CreateTranslatedONCObject(); |
| onc_object_->MergeDictionary(nested_object.get()); |
| |
| /// Get the requires_roaming from the Device dictionary. |
| device_dictionary->GetBooleanWithoutPathExpansion( |
| shill::kProviderRequiresRoamingProperty, &requires_roaming); |
| } |
| if (requires_roaming) { |
| onc_object_->SetStringWithoutPathExpansion( |
| ::onc::cellular::kRoamingState, ::onc::cellular::kRoamingRequired); |
| } else { |
| TranslateWithTableAndSet(shill::kRoamingStateProperty, kRoamingStateTable, |
| ::onc::cellular::kRoamingState); |
| } |
| } |
| |
| void ShillToONCTranslator::TranslateCellularDevice() { |
| CopyPropertiesAccordingToSignature(); |
| const base::DictionaryValue* shill_sim_lock_status = NULL; |
| if (shill_dictionary_->GetDictionaryWithoutPathExpansion( |
| shill::kSIMLockStatusProperty, &shill_sim_lock_status)) { |
| TranslateAndAddNestedObject(::onc::cellular::kSIMLockStatus, |
| *shill_sim_lock_status); |
| } |
| const base::DictionaryValue* shill_home_provider = NULL; |
| if (shill_dictionary_->GetDictionaryWithoutPathExpansion( |
| shill::kHomeProviderProperty, &shill_home_provider)) { |
| TranslateAndAddNestedObject(::onc::cellular::kHomeProvider, |
| *shill_home_provider); |
| } |
| const base::ListValue* shill_apns = NULL; |
| if (shill_dictionary_->GetListWithoutPathExpansion( |
| shill::kCellularApnListProperty, &shill_apns)) { |
| TranslateAndAddListOfObjects(::onc::cellular::kAPNList, *shill_apns); |
| } |
| const base::ListValue* shill_found_networks = NULL; |
| if (shill_dictionary_->GetListWithoutPathExpansion( |
| shill::kFoundNetworksProperty, &shill_found_networks)) { |
| TranslateAndAddListOfObjects(::onc::cellular::kFoundNetworks, |
| *shill_found_networks); |
| } |
| } |
| |
| void ShillToONCTranslator::TranslateNetworkWithState() { |
| CopyPropertiesAccordingToSignature(); |
| |
| std::string shill_network_type; |
| shill_dictionary_->GetStringWithoutPathExpansion(shill::kTypeProperty, |
| &shill_network_type); |
| std::string onc_network_type = ::onc::network_type::kEthernet; |
| if (shill_network_type != shill::kTypeEthernet && |
| shill_network_type != shill::kTypeEthernetEap) { |
| TranslateStringToONC(kNetworkTypeTable, shill_network_type, |
| &onc_network_type); |
| } |
| // Translate nested Cellular, WiFi, etc. properties. |
| if (!onc_network_type.empty()) { |
| onc_object_->SetStringWithoutPathExpansion(::onc::network_config::kType, |
| onc_network_type); |
| TranslateAndAddNestedObject(onc_network_type); |
| } |
| |
| // Since Name is a read only field in Shill unless it's a VPN, it is copied |
| // here, but not when going the other direction (if it's not a VPN). |
| std::string name; |
| shill_dictionary_->GetStringWithoutPathExpansion(shill::kNameProperty, &name); |
| onc_object_->SetStringWithoutPathExpansion(::onc::network_config::kName, |
| name); |
| |
| // Limit ONC state to "NotConnected", "Connected", or "Connecting". |
| std::string state; |
| if (shill_dictionary_->GetStringWithoutPathExpansion(shill::kStateProperty, |
| &state)) { |
| std::string onc_state = ::onc::connection_state::kNotConnected; |
| if (NetworkState::StateIsConnected(state)) { |
| onc_state = ::onc::connection_state::kConnected; |
| } else if (NetworkState::StateIsConnecting(state)) { |
| onc_state = ::onc::connection_state::kConnecting; |
| } |
| onc_object_->SetStringWithoutPathExpansion( |
| ::onc::network_config::kConnectionState, onc_state); |
| // Only set 'RestrictedConnectivity' if captive portal state is true. |
| if (NetworkState::NetworkStateIsCaptivePortal(*shill_dictionary_)) { |
| onc_object_->SetBooleanWithoutPathExpansion( |
| ::onc::network_config::kRestrictedConnectivity, true); |
| } |
| } |
| |
| // 'ErrorState' reflects the most recent error maintained in NetworkState |
| // (which may not match Shill's Error or PreviousError properties). Non |
| // visible networks (with null network_state_) do not set ErrorState. |
| if (network_state_) { |
| std::string error_state = network_state_->GetErrorState(); |
| if (!error_state.empty()) { |
| onc_object_->SetStringWithoutPathExpansion( |
| ::onc::network_config::kErrorState, error_state); |
| } |
| } |
| |
| std::string profile_path; |
| if (onc_source_ != ::onc::ONC_SOURCE_UNKNOWN && |
| shill_dictionary_->GetStringWithoutPathExpansion(shill::kProfileProperty, |
| &profile_path)) { |
| std::string source; |
| if (onc_source_ == ::onc::ONC_SOURCE_DEVICE_POLICY) |
| source = ::onc::network_config::kSourceDevicePolicy; |
| else if (onc_source_ == ::onc::ONC_SOURCE_USER_POLICY) |
| source = ::onc::network_config::kSourceUserPolicy; |
| else if (profile_path == NetworkProfileHandler::GetSharedProfilePath()) |
| source = ::onc::network_config::kSourceDevice; |
| else if (!profile_path.empty()) |
| source = ::onc::network_config::kSourceUser; |
| else |
| source = ::onc::network_config::kSourceNone; |
| onc_object_->SetStringWithoutPathExpansion(::onc::network_config::kSource, |
| source); |
| } |
| |
| // Use a human-readable aa:bb format for any hardware MAC address. Note: |
| // this property is provided by the caller but is not part of the Shill |
| // Service properties (it is copied from the Device properties). |
| std::string address; |
| if (shill_dictionary_->GetStringWithoutPathExpansion(shill::kAddressProperty, |
| &address)) { |
| onc_object_->SetStringWithoutPathExpansion( |
| ::onc::network_config::kMacAddress, |
| network_util::FormattedMacAddress(address)); |
| } |
| |
| // Shill's Service has an IPConfig property (note the singular), not an |
| // IPConfigs property. However, we require the caller of the translation to |
| // patch the Shill dictionary before passing it to the translator. |
| const base::ListValue* shill_ipconfigs = NULL; |
| if (shill_dictionary_->GetListWithoutPathExpansion(shill::kIPConfigsProperty, |
| &shill_ipconfigs)) { |
| TranslateAndAddListOfObjects(::onc::network_config::kIPConfigs, |
| *shill_ipconfigs); |
| } |
| |
| const base::DictionaryValue* saved_ipconfig = nullptr; |
| if (shill_dictionary_->GetDictionaryWithoutPathExpansion( |
| shill::kSavedIPConfigProperty, &saved_ipconfig)) { |
| TranslateAndAddNestedObject(::onc::network_config::kSavedIPConfig, |
| *saved_ipconfig); |
| } |
| |
| // Translate the StaticIPConfig object and set the IP config types. |
| const base::DictionaryValue* static_ipconfig = nullptr; |
| if (shill_dictionary_->GetDictionaryWithoutPathExpansion( |
| shill::kStaticIPConfigProperty, &static_ipconfig)) { |
| std::string ip_address; |
| if (static_ipconfig->GetStringWithoutPathExpansion(shill::kAddressProperty, |
| &ip_address) && |
| !ip_address.empty()) { |
| onc_object_->SetStringWithoutPathExpansion( |
| ::onc::network_config::kIPAddressConfigType, |
| ::onc::network_config::kIPConfigTypeStatic); |
| } |
| const base::ListValue* name_servers = nullptr; |
| if (static_ipconfig->GetListWithoutPathExpansion( |
| shill::kNameServersProperty, &name_servers) && |
| !name_servers->empty()) { |
| onc_object_->SetStringWithoutPathExpansion( |
| ::onc::network_config::kNameServersConfigType, |
| ::onc::network_config::kIPConfigTypeStatic); |
| } |
| if (!ip_address.empty() || (name_servers && !name_servers->empty())) { |
| TranslateAndAddNestedObject(::onc::network_config::kStaticIPConfig, |
| *static_ipconfig); |
| } |
| } |
| |
| std::string proxy_config_str; |
| if (shill_dictionary_->GetStringWithoutPathExpansion( |
| shill::kProxyConfigProperty, &proxy_config_str) && |
| !proxy_config_str.empty()) { |
| std::unique_ptr<base::DictionaryValue> proxy_config_value( |
| ReadDictionaryFromJson(proxy_config_str)); |
| if (proxy_config_value) { |
| std::unique_ptr<base::DictionaryValue> proxy_settings = |
| ConvertProxyConfigToOncProxySettings(std::move(proxy_config_value)); |
| if (proxy_settings) { |
| onc_object_->SetWithoutPathExpansion( |
| ::onc::network_config::kProxySettings, std::move(proxy_settings)); |
| } |
| } |
| } |
| } |
| |
| void ShillToONCTranslator::TranslateIPConfig() { |
| CopyPropertiesAccordingToSignature(); |
| std::string shill_ip_method; |
| shill_dictionary_->GetStringWithoutPathExpansion(shill::kMethodProperty, |
| &shill_ip_method); |
| std::string type; |
| if (shill_ip_method == shill::kTypeIPv4 || |
| shill_ip_method == shill::kTypeDHCP) { |
| type = ::onc::ipconfig::kIPv4; |
| } else if (shill_ip_method == shill::kTypeIPv6 || |
| shill_ip_method == shill::kTypeDHCP6) { |
| type = ::onc::ipconfig::kIPv6; |
| } else { |
| return; // Ignore unhandled IPConfig types, e.g. bootp, zeroconf, ppp |
| } |
| |
| onc_object_->SetStringWithoutPathExpansion(::onc::ipconfig::kType, type); |
| } |
| |
| void ShillToONCTranslator::TranslateSavedOrStaticIPConfig() { |
| CopyPropertiesAccordingToSignature(); |
| |
| // Static and Saved IPConfig in Shill are always of type IPv4. Set this type |
| // in ONC, but not if the object would be empty except the type. |
| if (!onc_object_->empty()) { |
| onc_object_->SetStringWithoutPathExpansion(::onc::ipconfig::kType, |
| ::onc::ipconfig::kIPv4); |
| } |
| } |
| |
| void ShillToONCTranslator::TranslateSavedIPConfig() { |
| TranslateSavedOrStaticIPConfig(); |
| } |
| |
| void ShillToONCTranslator::TranslateStaticIPConfig() { |
| TranslateSavedOrStaticIPConfig(); |
| } |
| |
| void ShillToONCTranslator::TranslateEap() { |
| CopyPropertiesAccordingToSignature(); |
| |
| // Translate EAP Outer and Inner values if EAP.EAP exists and is not empty. |
| std::string shill_eap; |
| if (shill_dictionary_->GetStringWithoutPathExpansion( |
| shill::kEapMethodProperty, &shill_eap) && |
| !shill_eap.empty()) { |
| TranslateWithTableAndSet(shill::kEapMethodProperty, kEAPOuterTable, |
| ::onc::eap::kOuter); |
| TranslateWithTableAndSet(shill::kEapPhase2AuthProperty, |
| kEAP_TTLS_InnerTable, ::onc::eap::kInner); |
| } |
| } |
| |
| void ShillToONCTranslator::TranslateAndAddNestedObject( |
| const std::string& onc_field_name) { |
| TranslateAndAddNestedObject(onc_field_name, *shill_dictionary_); |
| } |
| |
| void ShillToONCTranslator::TranslateAndAddNestedObject( |
| const std::string& onc_field_name, |
| const base::DictionaryValue& dictionary) { |
| const OncFieldSignature* field_signature = |
| GetFieldSignature(*onc_signature_, onc_field_name); |
| if (!field_signature) { |
| NOTREACHED() << "Unable to find signature for field: " << onc_field_name; |
| return; |
| } |
| ShillToONCTranslator nested_translator(dictionary, onc_source_, |
| *field_signature->value_signature, |
| network_state_); |
| std::unique_ptr<base::DictionaryValue> nested_object = |
| nested_translator.CreateTranslatedONCObject(); |
| if (nested_object->empty()) |
| return; |
| onc_object_->SetWithoutPathExpansion(onc_field_name, |
| std::move(nested_object)); |
| } |
| |
| void ShillToONCTranslator::SetNestedOncValue( |
| const std::string& onc_dictionary_name, |
| const std::string& onc_field_name, |
| const base::Value& value) { |
| base::DictionaryValue* nested = nullptr; |
| if (!onc_object_->GetDictionaryWithoutPathExpansion(onc_dictionary_name, |
| &nested)) { |
| nested = onc_object_->SetDictionaryWithoutPathExpansion( |
| onc_dictionary_name, base::MakeUnique<base::DictionaryValue>()); |
| } |
| nested->SetWithoutPathExpansion(onc_field_name, |
| base::MakeUnique<base::Value>(value)); |
| } |
| |
| void ShillToONCTranslator::TranslateAndAddListOfObjects( |
| const std::string& onc_field_name, |
| const base::ListValue& list) { |
| const OncFieldSignature* field_signature = |
| GetFieldSignature(*onc_signature_, onc_field_name); |
| if (field_signature->value_signature->onc_type != base::Value::Type::LIST) { |
| LOG(ERROR) << "ONC Field name: '" << onc_field_name << "' has type '" |
| << field_signature->value_signature->onc_type |
| << "', expected: base::Value::Type::LIST: " << GetName(); |
| return; |
| } |
| DCHECK(field_signature->value_signature->onc_array_entry_signature); |
| std::unique_ptr<base::ListValue> result(new base::ListValue()); |
| for (base::ListValue::const_iterator it = list.begin(); it != list.end(); |
| ++it) { |
| const base::DictionaryValue* shill_value = NULL; |
| if (!it->GetAsDictionary(&shill_value)) |
| continue; |
| ShillToONCTranslator nested_translator( |
| *shill_value, onc_source_, |
| *field_signature->value_signature->onc_array_entry_signature, |
| network_state_); |
| std::unique_ptr<base::DictionaryValue> nested_object = |
| nested_translator.CreateTranslatedONCObject(); |
| // If the nested object couldn't be parsed, simply omit it. |
| if (nested_object->empty()) |
| continue; |
| result->Append(std::move(nested_object)); |
| } |
| // If there are no entries in the list, there is no need to expose this field. |
| if (result->empty()) |
| return; |
| onc_object_->SetWithoutPathExpansion(onc_field_name, std::move(result)); |
| } |
| |
| void ShillToONCTranslator::CopyPropertiesAccordingToSignature() { |
| CopyPropertiesAccordingToSignature(onc_signature_); |
| } |
| |
| void ShillToONCTranslator::CopyPropertiesAccordingToSignature( |
| const OncValueSignature* value_signature) { |
| if (value_signature->base_signature) |
| CopyPropertiesAccordingToSignature(value_signature->base_signature); |
| if (!value_signature->fields) |
| return; |
| for (const OncFieldSignature* field_signature = value_signature->fields; |
| field_signature->onc_field_name != NULL; ++field_signature) { |
| CopyProperty(field_signature); |
| } |
| } |
| |
| void ShillToONCTranslator::CopyProperty( |
| const OncFieldSignature* field_signature) { |
| std::string shill_property_name; |
| const base::Value* shill_value = NULL; |
| if (!field_translation_table_ || |
| !GetShillPropertyName(field_signature->onc_field_name, |
| field_translation_table_, &shill_property_name) || |
| !shill_dictionary_->GetWithoutPathExpansion(shill_property_name, |
| &shill_value)) { |
| return; |
| } |
| |
| if (shill_value->GetType() != field_signature->value_signature->onc_type) { |
| LOG(ERROR) << "Shill property '" << shill_property_name << "' with value " |
| << *shill_value << " has base::Value::Type " |
| << shill_value->GetType() << " but ONC field '" |
| << field_signature->onc_field_name << "' requires type " |
| << field_signature->value_signature->onc_type << ": " |
| << GetName(); |
| return; |
| } |
| |
| onc_object_->SetWithoutPathExpansion( |
| field_signature->onc_field_name, |
| base::MakeUnique<base::Value>(*shill_value)); |
| } |
| |
| void ShillToONCTranslator::TranslateWithTableAndSet( |
| const std::string& shill_property_name, |
| const StringTranslationEntry table[], |
| const std::string& onc_field_name) { |
| std::string shill_value; |
| if (!shill_dictionary_->GetStringWithoutPathExpansion(shill_property_name, |
| &shill_value)) { |
| return; |
| } |
| std::string onc_value; |
| if (TranslateStringToONC(table, shill_value, &onc_value)) { |
| onc_object_->SetStringWithoutPathExpansion(onc_field_name, onc_value); |
| return; |
| } |
| LOG(ERROR) << "Shill property '" << shill_property_name << "' with value " |
| << shill_value << " couldn't be translated to ONC: " << GetName(); |
| } |
| |
| std::string ShillToONCTranslator::GetName() { |
| DCHECK(shill_dictionary_); |
| std::string name; |
| shill_dictionary_->GetStringWithoutPathExpansion(shill::kNameProperty, &name); |
| return name; |
| } |
| |
| } // namespace |
| |
| std::unique_ptr<base::DictionaryValue> TranslateShillServiceToONCPart( |
| const base::DictionaryValue& shill_dictionary, |
| ::onc::ONCSource onc_source, |
| const OncValueSignature* onc_signature, |
| const NetworkState* network_state) { |
| CHECK(onc_signature != NULL); |
| |
| ShillToONCTranslator translator(shill_dictionary, onc_source, *onc_signature, |
| network_state); |
| return translator.CreateTranslatedONCObject(); |
| } |
| |
| } // namespace onc |
| } // namespace chromeos |