onc_translator_shill_to_onc.cc revision 68043e1e95eeb07d5cae7aca370b26518b0867d6
1// Copyright (c) 2012 The Chromium Authors. All rights reserved. 2// Use of this source code is governed by a BSD-style license that can be 3// found in the LICENSE file. 4 5#include "chromeos/network/onc/onc_translator.h" 6 7#include <string> 8 9#include "base/basictypes.h" 10#include "base/json/json_reader.h" 11#include "base/json/json_writer.h" 12#include "base/logging.h" 13#include "base/values.h" 14#include "chromeos/network/network_state.h" 15#include "chromeos/network/onc/onc_constants.h" 16#include "chromeos/network/onc/onc_signature.h" 17#include "chromeos/network/onc/onc_translation_tables.h" 18#include "third_party/cros_system_api/dbus/service_constants.h" 19 20namespace chromeos { 21namespace onc { 22 23namespace { 24 25// Converts |str| to a base::Value of the given |type|. If the conversion fails, 26// returns NULL. 27scoped_ptr<base::Value> ConvertStringToValue(const std::string& str, 28 base::Value::Type type) { 29 base::Value* value; 30 if (type == base::Value::TYPE_STRING) { 31 value = base::Value::CreateStringValue(str); 32 } else { 33 value = base::JSONReader::Read(str); 34 } 35 36 if (value == NULL || value->GetType() != type) { 37 delete value; 38 value = NULL; 39 } 40 return make_scoped_ptr(value); 41} 42 43// This class implements the translation of properties from the given 44// |shill_dictionary| to a new ONC object of signature |onc_signature|. Using 45// recursive calls to CreateTranslatedONCObject of new instances, nested objects 46// are translated. 47class ShillToONCTranslator { 48 public: 49 ShillToONCTranslator(const base::DictionaryValue& shill_dictionary, 50 const OncValueSignature& onc_signature) 51 : shill_dictionary_(&shill_dictionary), 52 onc_signature_(&onc_signature) { 53 field_translation_table_ = GetFieldTranslationTable(onc_signature); 54 } 55 56 // Translates the associated Shill dictionary and creates an ONC object of the 57 // given signature. 58 scoped_ptr<base::DictionaryValue> CreateTranslatedONCObject(); 59 60 private: 61 void TranslateEthernet(); 62 void TranslateOpenVPN(); 63 void TranslateVPN(); 64 void TranslateWiFiWithState(); 65 void TranslateCellularWithState(); 66 void TranslateNetworkWithState(); 67 68 // Creates an ONC object from |dictionary| according to the signature 69 // associated to |onc_field_name| and adds it to |onc_object_| at 70 // |onc_field_name|. 71 void TranslateAndAddNestedObject(const std::string& onc_field_name, 72 const base::DictionaryValue& dictionary); 73 74 // Creates an ONC object from |shill_dictionary_| according to the signature 75 // associated to |onc_field_name| and adds it to |onc_object_| at 76 // |onc_field_name|. 77 void TranslateAndAddNestedObject(const std::string& onc_field_name); 78 79 // Applies function CopyProperty to each field of |value_signature| and its 80 // base signatures. 81 void CopyPropertiesAccordingToSignature( 82 const OncValueSignature* value_signature); 83 84 // Applies function CopyProperty to each field of |onc_signature_| and its 85 // base signatures. 86 void CopyPropertiesAccordingToSignature(); 87 88 // If |shill_property_name| is defined in |field_signature|, copies this 89 // entry from |shill_dictionary_| to |onc_object_| if it exists. 90 void CopyProperty(const OncFieldSignature* field_signature); 91 92 // If existent, translates the entry at |shill_property_name| in 93 // |shill_dictionary_| using |table|. It is an error if no matching table 94 // entry is found. Writes the result as entry at |onc_field_name| in 95 // |onc_object_|. 96 void TranslateWithTableAndSet(const std::string& shill_property_name, 97 const StringTranslationEntry table[], 98 const std::string& onc_field_name); 99 100 const base::DictionaryValue* shill_dictionary_; 101 const OncValueSignature* onc_signature_; 102 const FieldTranslationEntry* field_translation_table_; 103 scoped_ptr<base::DictionaryValue> onc_object_; 104 105 DISALLOW_COPY_AND_ASSIGN(ShillToONCTranslator); 106}; 107 108scoped_ptr<base::DictionaryValue> 109ShillToONCTranslator::CreateTranslatedONCObject() { 110 onc_object_.reset(new base::DictionaryValue); 111 if (onc_signature_ == &kNetworkWithStateSignature) { 112 TranslateNetworkWithState(); 113 } else if (onc_signature_ == &kEthernetSignature) { 114 TranslateEthernet(); 115 } else if (onc_signature_ == &kVPNSignature) { 116 TranslateVPN(); 117 } else if (onc_signature_ == &kOpenVPNSignature) { 118 TranslateOpenVPN(); 119 } else if (onc_signature_ == &kWiFiWithStateSignature) { 120 TranslateWiFiWithState(); 121 } else if (onc_signature_ == &kCellularWithStateSignature) { 122 TranslateCellularWithState(); 123 } else { 124 CopyPropertiesAccordingToSignature(); 125 } 126 return onc_object_.Pass(); 127} 128 129void ShillToONCTranslator::TranslateEthernet() { 130 std::string shill_network_type; 131 shill_dictionary_->GetStringWithoutPathExpansion(shill::kTypeProperty, 132 &shill_network_type); 133 const char* onc_auth = ethernet::kNone; 134 if (shill_network_type == shill::kTypeEthernetEap) 135 onc_auth = ethernet::k8021X; 136 onc_object_->SetStringWithoutPathExpansion(ethernet::kAuthentication, 137 onc_auth); 138} 139 140void ShillToONCTranslator::TranslateOpenVPN() { 141 // Shill supports only one RemoteCertKU but ONC requires a list. If existing, 142 // wraps the value into a list. 143 std::string certKU; 144 if (shill_dictionary_->GetStringWithoutPathExpansion( 145 shill::kOpenVPNRemoteCertKUProperty, &certKU)) { 146 scoped_ptr<base::ListValue> certKUs(new base::ListValue); 147 certKUs->AppendString(certKU); 148 onc_object_->SetWithoutPathExpansion(openvpn::kRemoteCertKU, 149 certKUs.release()); 150 } 151 152 for (const OncFieldSignature* field_signature = onc_signature_->fields; 153 field_signature->onc_field_name != NULL; ++field_signature) { 154 const std::string& onc_field_name = field_signature->onc_field_name; 155 if (onc_field_name == vpn::kSaveCredentials || 156 onc_field_name == openvpn::kRemoteCertKU || 157 onc_field_name == openvpn::kServerCAPEMs) { 158 CopyProperty(field_signature); 159 continue; 160 } 161 162 std::string shill_property_name; 163 const base::Value* shill_value = NULL; 164 if (!field_translation_table_ || 165 !GetShillPropertyName(field_signature->onc_field_name, 166 field_translation_table_, 167 &shill_property_name) || 168 !shill_dictionary_->GetWithoutPathExpansion(shill_property_name, 169 &shill_value)) { 170 continue; 171 } 172 173 scoped_ptr<base::Value> translated; 174 std::string shill_str; 175 if (shill_value->GetAsString(&shill_str)) { 176 // Shill wants all Provider/VPN fields to be strings. Translates these 177 // strings back to the correct ONC type. 178 translated = ConvertStringToValue( 179 shill_str, 180 field_signature->value_signature->onc_type); 181 182 if (translated.get() == NULL) { 183 LOG(ERROR) << "Shill property '" << shill_property_name 184 << "' with value " << *shill_value 185 << " couldn't be converted to base::Value::Type " 186 << field_signature->value_signature->onc_type; 187 } else { 188 onc_object_->SetWithoutPathExpansion(onc_field_name, 189 translated.release()); 190 } 191 } else { 192 LOG(ERROR) << "Shill property '" << shill_property_name 193 << "' has value " << *shill_value 194 << ", but expected a string"; 195 } 196 } 197} 198 199void ShillToONCTranslator::TranslateVPN() { 200 TranslateWithTableAndSet(shill::kProviderTypeProperty, kVPNTypeTable, 201 vpn::kType); 202 CopyPropertiesAccordingToSignature(); 203 204 std::string vpn_type; 205 if (onc_object_->GetStringWithoutPathExpansion(vpn::kType, 206 &vpn_type)) { 207 if (vpn_type == vpn::kTypeL2TP_IPsec) { 208 TranslateAndAddNestedObject(vpn::kIPsec); 209 TranslateAndAddNestedObject(vpn::kL2TP); 210 } else { 211 TranslateAndAddNestedObject(vpn_type); 212 } 213 } 214} 215 216void ShillToONCTranslator::TranslateWiFiWithState() { 217 TranslateWithTableAndSet(shill::kSecurityProperty, kWiFiSecurityTable, 218 wifi::kSecurity); 219 CopyPropertiesAccordingToSignature(); 220} 221 222void ShillToONCTranslator::TranslateCellularWithState() { 223 CopyPropertiesAccordingToSignature(); 224 const base::DictionaryValue* dictionary = NULL; 225 if (shill_dictionary_->GetDictionaryWithoutPathExpansion( 226 shill::kServingOperatorProperty, &dictionary)) { 227 TranslateAndAddNestedObject(cellular::kServingOperator, *dictionary); 228 } 229 if (shill_dictionary_->GetDictionaryWithoutPathExpansion( 230 shill::kCellularApnProperty, &dictionary)) { 231 TranslateAndAddNestedObject(cellular::kAPN, *dictionary); 232 } 233} 234 235void ShillToONCTranslator::TranslateNetworkWithState() { 236 CopyPropertiesAccordingToSignature(); 237 238 std::string shill_network_type; 239 shill_dictionary_->GetStringWithoutPathExpansion(shill::kTypeProperty, 240 &shill_network_type); 241 std::string onc_network_type = network_type::kEthernet; 242 if (shill_network_type != shill::kTypeEthernet && 243 shill_network_type != shill::kTypeEthernetEap) { 244 TranslateStringToONC( 245 kNetworkTypeTable, shill_network_type, &onc_network_type); 246 } 247 if (!onc_network_type.empty()) { 248 onc_object_->SetStringWithoutPathExpansion(network_config::kType, 249 onc_network_type); 250 TranslateAndAddNestedObject(onc_network_type); 251 } 252 253 // Since Name is a read only field in Shill unless it's a VPN, it is copied 254 // here, but not when going the other direction (if it's not a VPN). 255 std::string name; 256 shill_dictionary_->GetStringWithoutPathExpansion(shill::kNameProperty, 257 &name); 258 onc_object_->SetStringWithoutPathExpansion(network_config::kName, name); 259 260 std::string state; 261 if (shill_dictionary_->GetStringWithoutPathExpansion(shill::kStateProperty, 262 &state)) { 263 std::string onc_state = connection_state::kNotConnected; 264 if (NetworkState::StateIsConnected(state)) { 265 onc_state = connection_state::kConnected; 266 } else if (NetworkState::StateIsConnecting(state)) { 267 onc_state = connection_state::kConnecting; 268 } 269 onc_object_->SetStringWithoutPathExpansion(network_config::kConnectionState, 270 onc_state); 271 } 272} 273 274void ShillToONCTranslator::TranslateAndAddNestedObject( 275 const std::string& onc_field_name) { 276 TranslateAndAddNestedObject(onc_field_name, *shill_dictionary_); 277} 278 279void ShillToONCTranslator::TranslateAndAddNestedObject( 280 const std::string& onc_field_name, 281 const base::DictionaryValue& dictionary) { 282 const OncFieldSignature* field_signature = 283 GetFieldSignature(*onc_signature_, onc_field_name); 284 ShillToONCTranslator nested_translator(dictionary, 285 *field_signature->value_signature); 286 scoped_ptr<base::DictionaryValue> nested_object = 287 nested_translator.CreateTranslatedONCObject(); 288 if (nested_object->empty()) 289 return; 290 onc_object_->SetWithoutPathExpansion(onc_field_name, nested_object.release()); 291} 292 293void ShillToONCTranslator::CopyPropertiesAccordingToSignature() { 294 CopyPropertiesAccordingToSignature(onc_signature_); 295} 296 297void ShillToONCTranslator::CopyPropertiesAccordingToSignature( 298 const OncValueSignature* value_signature) { 299 if (value_signature->base_signature) 300 CopyPropertiesAccordingToSignature(value_signature->base_signature); 301 for (const OncFieldSignature* field_signature = value_signature->fields; 302 field_signature->onc_field_name != NULL; ++field_signature) { 303 CopyProperty(field_signature); 304 } 305} 306 307void ShillToONCTranslator::CopyProperty( 308 const OncFieldSignature* field_signature) { 309 std::string shill_property_name; 310 const base::Value* shill_value = NULL; 311 if (!field_translation_table_ || 312 !GetShillPropertyName(field_signature->onc_field_name, 313 field_translation_table_, 314 &shill_property_name) || 315 !shill_dictionary_->GetWithoutPathExpansion(shill_property_name, 316 &shill_value)) { 317 return; 318 } 319 320 if (shill_value->GetType() != field_signature->value_signature->onc_type) { 321 LOG(ERROR) << "Shill property '" << shill_property_name 322 << "' with value " << *shill_value 323 << " has base::Value::Type " << shill_value->GetType() 324 << " but ONC field '" << field_signature->onc_field_name 325 << "' requires type " 326 << field_signature->value_signature->onc_type << "."; 327 return; 328 } 329 330 onc_object_->SetWithoutPathExpansion(field_signature->onc_field_name, 331 shill_value->DeepCopy()); 332} 333 334void ShillToONCTranslator::TranslateWithTableAndSet( 335 const std::string& shill_property_name, 336 const StringTranslationEntry table[], 337 const std::string& onc_field_name) { 338 std::string shill_value; 339 if (!shill_dictionary_->GetStringWithoutPathExpansion(shill_property_name, 340 &shill_value)) { 341 return; 342 } 343 std::string onc_value; 344 if (TranslateStringToONC(table, shill_value, &onc_value)) { 345 onc_object_->SetStringWithoutPathExpansion(onc_field_name, onc_value); 346 return; 347 } 348 LOG(ERROR) << "Shill property '" << shill_property_name << "' with value " 349 << shill_value << " couldn't be translated to ONC"; 350} 351 352} // namespace 353 354scoped_ptr<base::DictionaryValue> TranslateShillServiceToONCPart( 355 const base::DictionaryValue& shill_dictionary, 356 const OncValueSignature* onc_signature) { 357 CHECK(onc_signature != NULL); 358 359 ShillToONCTranslator translator(shill_dictionary, *onc_signature); 360 return translator.CreateTranslatedONCObject(); 361} 362 363} // namespace onc 364} // namespace chromeos 365