| // 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 "chromeos/network/onc/onc_translator.h" |
| |
| #include <string> |
| |
| #include "base/basictypes.h" |
| #include "base/json/json_reader.h" |
| #include "base/json/json_writer.h" |
| #include "base/logging.h" |
| #include "base/values.h" |
| #include "chromeos/network/network_state.h" |
| #include "chromeos/network/onc/onc_constants.h" |
| #include "chromeos/network/onc/onc_signature.h" |
| #include "chromeos/network/onc/onc_translation_tables.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. |
| scoped_ptr<base::Value> ConvertStringToValue(const std::string& str, |
| base::Value::Type type) { |
| base::Value* value; |
| if (type == base::Value::TYPE_STRING) { |
| value = base::Value::CreateStringValue(str); |
| } else { |
| value = base::JSONReader::Read(str); |
| } |
| |
| if (value == NULL || value->GetType() != type) { |
| delete value; |
| value = NULL; |
| } |
| return make_scoped_ptr(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, |
| const OncValueSignature& onc_signature) |
| : shill_dictionary_(&shill_dictionary), |
| onc_signature_(&onc_signature) { |
| field_translation_table_ = GetFieldTranslationTable(onc_signature); |
| } |
| |
| // Translates the associated Shill dictionary and creates an ONC object of the |
| // given signature. |
| scoped_ptr<base::DictionaryValue> CreateTranslatedONCObject(); |
| |
| private: |
| void TranslateOpenVPN(); |
| void TranslateVPN(); |
| void TranslateWiFiWithState(); |
| void TranslateCellularWithState(); |
| void TranslateNetworkWithState(); |
| |
| // 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); |
| |
| // 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); |
| |
| const base::DictionaryValue* shill_dictionary_; |
| const OncValueSignature* onc_signature_; |
| const FieldTranslationEntry* field_translation_table_; |
| scoped_ptr<base::DictionaryValue> onc_object_; |
| |
| DISALLOW_COPY_AND_ASSIGN(ShillToONCTranslator); |
| }; |
| |
| scoped_ptr<base::DictionaryValue> |
| ShillToONCTranslator::CreateTranslatedONCObject() { |
| onc_object_.reset(new base::DictionaryValue); |
| if (onc_signature_ == &kNetworkWithStateSignature) { |
| TranslateNetworkWithState(); |
| } else if (onc_signature_ == &kVPNSignature) { |
| TranslateVPN(); |
| } else if (onc_signature_ == &kOpenVPNSignature) { |
| TranslateOpenVPN(); |
| } else if (onc_signature_ == &kWiFiWithStateSignature) { |
| TranslateWiFiWithState(); |
| } else if (onc_signature_ == &kCellularWithStateSignature) { |
| TranslateCellularWithState(); |
| } else { |
| CopyPropertiesAccordingToSignature(); |
| } |
| return onc_object_.Pass(); |
| } |
| |
| void ShillToONCTranslator::TranslateOpenVPN() { |
| // 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( |
| flimflam::kOpenVPNRemoteCertKUProperty, &certKU)) { |
| scoped_ptr<base::ListValue> certKUs(new base::ListValue); |
| certKUs->AppendString(certKU); |
| onc_object_->SetWithoutPathExpansion(openvpn::kRemoteCertKU, |
| certKUs.release()); |
| } |
| |
| 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 == vpn::kSaveCredentials || |
| onc_field_name == openvpn::kRemoteCertKU || |
| onc_field_name == 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; |
| } |
| |
| scoped_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; |
| } else { |
| onc_object_->SetWithoutPathExpansion(onc_field_name, |
| translated.release()); |
| } |
| } else { |
| LOG(ERROR) << "Shill property '" << shill_property_name |
| << "' has value " << *shill_value |
| << ", but expected a string"; |
| } |
| } |
| } |
| |
| void ShillToONCTranslator::TranslateVPN() { |
| TranslateWithTableAndSet(flimflam::kProviderTypeProperty, kVPNTypeTable, |
| vpn::kType); |
| CopyPropertiesAccordingToSignature(); |
| |
| std::string vpn_type; |
| if (onc_object_->GetStringWithoutPathExpansion(vpn::kType, |
| &vpn_type)) { |
| if (vpn_type == vpn::kTypeL2TP_IPsec) { |
| TranslateAndAddNestedObject(vpn::kIPsec); |
| TranslateAndAddNestedObject(vpn::kL2TP); |
| } else { |
| TranslateAndAddNestedObject(vpn_type); |
| } |
| } |
| } |
| |
| void ShillToONCTranslator::TranslateWiFiWithState() { |
| TranslateWithTableAndSet(flimflam::kSecurityProperty, kWiFiSecurityTable, |
| wifi::kSecurity); |
| CopyPropertiesAccordingToSignature(); |
| } |
| |
| void ShillToONCTranslator::TranslateCellularWithState() { |
| CopyPropertiesAccordingToSignature(); |
| const base::DictionaryValue* serving_operator = NULL; |
| if (shill_dictionary_->GetDictionaryWithoutPathExpansion( |
| flimflam::kServingOperatorProperty, &serving_operator)) { |
| TranslateAndAddNestedObject(cellular::kServingOperator, *serving_operator); |
| } |
| } |
| |
| void ShillToONCTranslator::TranslateNetworkWithState() { |
| TranslateWithTableAndSet(flimflam::kTypeProperty, kNetworkTypeTable, |
| network_config::kType); |
| CopyPropertiesAccordingToSignature(); |
| |
| std::string network_type; |
| if (onc_object_->GetStringWithoutPathExpansion(network_config::kType, |
| &network_type)) { |
| TranslateAndAddNestedObject(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(flimflam::kNameProperty, |
| &name); |
| onc_object_->SetStringWithoutPathExpansion(network_config::kName, name); |
| |
| std::string state; |
| if (shill_dictionary_->GetStringWithoutPathExpansion(flimflam::kStateProperty, |
| &state)) { |
| std::string onc_state = connection_state::kNotConnected; |
| if (NetworkState::StateIsConnected(state)) { |
| onc_state = connection_state::kConnected; |
| } else if (NetworkState::StateIsConnecting(state)) { |
| onc_state = connection_state::kConnecting; |
| } |
| onc_object_->SetStringWithoutPathExpansion(network_config::kConnectionState, |
| onc_state); |
| } |
| } |
| |
| 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); |
| ShillToONCTranslator nested_translator(dictionary, |
| *field_signature->value_signature); |
| scoped_ptr<base::DictionaryValue> nested_object = |
| nested_translator.CreateTranslatedONCObject(); |
| if (nested_object->empty()) |
| return; |
| onc_object_->SetWithoutPathExpansion(onc_field_name, nested_object.release()); |
| } |
| |
| void ShillToONCTranslator::CopyPropertiesAccordingToSignature() { |
| CopyPropertiesAccordingToSignature(onc_signature_); |
| } |
| |
| void ShillToONCTranslator::CopyPropertiesAccordingToSignature( |
| const OncValueSignature* value_signature) { |
| if (value_signature->base_signature) |
| CopyPropertiesAccordingToSignature(value_signature->base_signature); |
| 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 << "."; |
| return; |
| } |
| |
| onc_object_->SetWithoutPathExpansion(field_signature->onc_field_name, |
| shill_value->DeepCopy()); |
| } |
| |
| 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"; |
| } |
| |
| } // namespace |
| |
| scoped_ptr<base::DictionaryValue> TranslateShillServiceToONCPart( |
| const base::DictionaryValue& shill_dictionary, |
| const OncValueSignature* onc_signature) { |
| CHECK(onc_signature != NULL); |
| |
| ShillToONCTranslator translator(shill_dictionary, *onc_signature); |
| return translator.CreateTranslatedONCObject(); |
| } |
| |
| } // namespace onc |
| } // namespace chromeos |