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/strings/string_util.h" 14 #include "base/values.h" 15 #include "chromeos/network/network_profile_handler.h" 16 #include "chromeos/network/network_state.h" 17 #include "chromeos/network/network_util.h" 18 #include "chromeos/network/onc/onc_signature.h" 19 #include "chromeos/network/onc/onc_translation_tables.h" 20 #include "chromeos/network/shill_property_util.h" 21 #include "components/onc/onc_constants.h" 22 #include "third_party/cros_system_api/dbus/service_constants.h" 23 24 namespace chromeos { 25 namespace onc { 26 27 namespace { 28 29 // Converts |str| to a base::Value of the given |type|. If the conversion fails, 30 // returns NULL. 31 scoped_ptr<base::Value> ConvertStringToValue(const std::string& str, 32 base::Value::Type type) { 33 base::Value* value; 34 if (type == base::Value::TYPE_STRING) { 35 value = new base::StringValue(str); 36 } else { 37 value = base::JSONReader::Read(str); 38 } 39 40 if (value == NULL || value->GetType() != type) { 41 delete value; 42 value = NULL; 43 } 44 return make_scoped_ptr(value); 45 } 46 47 // This class implements the translation of properties from the given 48 // |shill_dictionary| to a new ONC object of signature |onc_signature|. Using 49 // recursive calls to CreateTranslatedONCObject of new instances, nested objects 50 // are translated. 51 class ShillToONCTranslator { 52 public: 53 ShillToONCTranslator(const base::DictionaryValue& shill_dictionary, 54 ::onc::ONCSource onc_source, 55 const OncValueSignature& onc_signature) 56 : shill_dictionary_(&shill_dictionary), 57 onc_source_(onc_source), 58 onc_signature_(&onc_signature) { 59 field_translation_table_ = GetFieldTranslationTable(onc_signature); 60 } 61 62 ShillToONCTranslator(const base::DictionaryValue& shill_dictionary, 63 ::onc::ONCSource onc_source, 64 const OncValueSignature& onc_signature, 65 const FieldTranslationEntry* field_translation_table) 66 : shill_dictionary_(&shill_dictionary), 67 onc_source_(onc_source), 68 onc_signature_(&onc_signature), 69 field_translation_table_(field_translation_table) { 70 } 71 72 // Translates the associated Shill dictionary and creates an ONC object of the 73 // given signature. 74 scoped_ptr<base::DictionaryValue> CreateTranslatedONCObject(); 75 76 private: 77 void TranslateEthernet(); 78 void TranslateOpenVPN(); 79 void TranslateIPsec(); 80 void TranslateVPN(); 81 void TranslateWiFiWithState(); 82 void TranslateWiMAXWithState(); 83 void TranslateCellularWithState(); 84 void TranslateCellularDevice(); 85 void TranslateNetworkWithState(); 86 void TranslateIPConfig(); 87 void TranslateSavedOrStaticIPConfig(const std::string& nameserver_property); 88 void TranslateSavedIPConfig(); 89 void TranslateStaticIPConfig(); 90 91 // Creates an ONC object from |dictionary| according to the signature 92 // associated to |onc_field_name| and adds it to |onc_object_| at 93 // |onc_field_name|. 94 void TranslateAndAddNestedObject(const std::string& onc_field_name, 95 const base::DictionaryValue& dictionary); 96 97 // Creates an ONC object from |shill_dictionary_| according to the signature 98 // associated to |onc_field_name| and adds it to |onc_object_| at 99 // |onc_field_name|. 100 void TranslateAndAddNestedObject(const std::string& onc_field_name); 101 102 // Sets |onc_field_name| in dictionary |onc_dictionary_name| in |onc_object_| 103 // to |value| if the dictionary exists. 104 void SetNestedOncValue(const std::string& onc_dictionary_name, 105 const std::string& onc_field_name, 106 const base::Value& value); 107 108 // Translates a list of nested objects and adds the list to |onc_object_| at 109 // |onc_field_name|. If there are errors while parsing individual objects or 110 // if the resulting list contains no entries, the result will not be added to 111 // |onc_object_|. 112 void TranslateAndAddListOfObjects(const std::string& onc_field_name, 113 const base::ListValue& list); 114 115 // Applies function CopyProperty to each field of |value_signature| and its 116 // base signatures. 117 void CopyPropertiesAccordingToSignature( 118 const OncValueSignature* value_signature); 119 120 // Applies function CopyProperty to each field of |onc_signature_| and its 121 // base signatures. 122 void CopyPropertiesAccordingToSignature(); 123 124 // If |shill_property_name| is defined in |field_signature|, copies this 125 // entry from |shill_dictionary_| to |onc_object_| if it exists. 126 void CopyProperty(const OncFieldSignature* field_signature); 127 128 // If existent, translates the entry at |shill_property_name| in 129 // |shill_dictionary_| using |table|. It is an error if no matching table 130 // entry is found. Writes the result as entry at |onc_field_name| in 131 // |onc_object_|. 132 void TranslateWithTableAndSet(const std::string& shill_property_name, 133 const StringTranslationEntry table[], 134 const std::string& onc_field_name); 135 136 // Returns the name of the Shill service provided in |shill_dictionary_| 137 // for debugging. 138 std::string GetName(); 139 140 const base::DictionaryValue* shill_dictionary_; 141 ::onc::ONCSource onc_source_; 142 const OncValueSignature* onc_signature_; 143 const FieldTranslationEntry* field_translation_table_; 144 scoped_ptr<base::DictionaryValue> onc_object_; 145 146 DISALLOW_COPY_AND_ASSIGN(ShillToONCTranslator); 147 }; 148 149 scoped_ptr<base::DictionaryValue> 150 ShillToONCTranslator::CreateTranslatedONCObject() { 151 onc_object_.reset(new base::DictionaryValue); 152 if (onc_signature_ == &kNetworkWithStateSignature) { 153 TranslateNetworkWithState(); 154 } else if (onc_signature_ == &kEthernetSignature) { 155 TranslateEthernet(); 156 } else if (onc_signature_ == &kVPNSignature) { 157 TranslateVPN(); 158 } else if (onc_signature_ == &kOpenVPNSignature) { 159 TranslateOpenVPN(); 160 } else if (onc_signature_ == &kIPsecSignature) { 161 TranslateIPsec(); 162 } else if (onc_signature_ == &kWiFiWithStateSignature) { 163 TranslateWiFiWithState(); 164 } else if (onc_signature_ == &kWiMAXWithStateSignature) { 165 TranslateWiMAXWithState(); 166 } else if (onc_signature_ == &kCellularWithStateSignature) { 167 if (field_translation_table_ == kCellularDeviceTable) 168 TranslateCellularDevice(); 169 else 170 TranslateCellularWithState(); 171 } else if (onc_signature_ == &kIPConfigSignature) { 172 TranslateIPConfig(); 173 } else if (onc_signature_ == &kSavedIPConfigSignature) { 174 TranslateSavedIPConfig(); 175 } else if (onc_signature_ == &kStaticIPConfigSignature) { 176 TranslateStaticIPConfig(); 177 } else { 178 CopyPropertiesAccordingToSignature(); 179 } 180 return onc_object_.Pass(); 181 } 182 183 void ShillToONCTranslator::TranslateEthernet() { 184 std::string shill_network_type; 185 shill_dictionary_->GetStringWithoutPathExpansion(shill::kTypeProperty, 186 &shill_network_type); 187 const char* onc_auth = ::onc::ethernet::kAuthenticationNone; 188 if (shill_network_type == shill::kTypeEthernetEap) 189 onc_auth = ::onc::ethernet::k8021X; 190 onc_object_->SetStringWithoutPathExpansion(::onc::ethernet::kAuthentication, 191 onc_auth); 192 } 193 194 void ShillToONCTranslator::TranslateOpenVPN() { 195 if (shill_dictionary_->HasKey(shill::kOpenVPNVerifyX509NameProperty)) 196 TranslateAndAddNestedObject(::onc::openvpn::kVerifyX509); 197 198 // Shill supports only one RemoteCertKU but ONC requires a list. If existing, 199 // wraps the value into a list. 200 std::string certKU; 201 if (shill_dictionary_->GetStringWithoutPathExpansion( 202 shill::kOpenVPNRemoteCertKUProperty, &certKU)) { 203 scoped_ptr<base::ListValue> certKUs(new base::ListValue); 204 certKUs->AppendString(certKU); 205 onc_object_->SetWithoutPathExpansion(::onc::openvpn::kRemoteCertKU, 206 certKUs.release()); 207 } 208 209 for (const OncFieldSignature* field_signature = onc_signature_->fields; 210 field_signature->onc_field_name != NULL; ++field_signature) { 211 const std::string& onc_field_name = field_signature->onc_field_name; 212 if (onc_field_name == ::onc::openvpn::kRemoteCertKU || 213 onc_field_name == ::onc::openvpn::kServerCAPEMs) { 214 CopyProperty(field_signature); 215 continue; 216 } 217 218 std::string shill_property_name; 219 const base::Value* shill_value = NULL; 220 if (!field_translation_table_ || 221 !GetShillPropertyName(field_signature->onc_field_name, 222 field_translation_table_, 223 &shill_property_name) || 224 !shill_dictionary_->GetWithoutPathExpansion(shill_property_name, 225 &shill_value)) { 226 continue; 227 } 228 229 scoped_ptr<base::Value> translated; 230 std::string shill_str; 231 if (shill_value->GetAsString(&shill_str)) { 232 // Shill wants all Provider/VPN fields to be strings. Translates these 233 // strings back to the correct ONC type. 234 translated = ConvertStringToValue( 235 shill_str, 236 field_signature->value_signature->onc_type); 237 238 if (translated.get() == NULL) { 239 LOG(ERROR) << "Shill property '" << shill_property_name 240 << "' with value " << *shill_value 241 << " couldn't be converted to base::Value::Type " 242 << field_signature->value_signature->onc_type 243 << ": " << GetName(); 244 } else { 245 onc_object_->SetWithoutPathExpansion(onc_field_name, 246 translated.release()); 247 } 248 } else { 249 LOG(ERROR) << "Shill property '" << shill_property_name 250 << "' has value " << *shill_value 251 << ", but expected a string: " << GetName(); 252 } 253 } 254 } 255 256 void ShillToONCTranslator::TranslateIPsec() { 257 CopyPropertiesAccordingToSignature(); 258 if (shill_dictionary_->HasKey(shill::kL2tpIpsecXauthUserProperty)) 259 TranslateAndAddNestedObject(::onc::ipsec::kXAUTH); 260 std::string client_cert_id; 261 shill_dictionary_->GetStringWithoutPathExpansion( 262 shill::kL2tpIpsecClientCertIdProperty, &client_cert_id); 263 std::string authentication_type = 264 client_cert_id.empty() ? ::onc::ipsec::kPSK : ::onc::ipsec::kCert; 265 onc_object_->SetStringWithoutPathExpansion(::onc::ipsec::kAuthenticationType, 266 authentication_type); 267 } 268 269 void ShillToONCTranslator::TranslateVPN() { 270 CopyPropertiesAccordingToSignature(); 271 272 // Parse Shill Provider dictionary. Note, this may not exist, e.g. if we are 273 // just translating network state in network_util::TranslateNetworkStateToONC. 274 const base::DictionaryValue* provider = NULL; 275 if (!shill_dictionary_->GetDictionaryWithoutPathExpansion( 276 shill::kProviderProperty, &provider)) { 277 return; 278 } 279 std::string shill_provider_type, onc_provider_type; 280 provider->GetStringWithoutPathExpansion(shill::kTypeProperty, 281 &shill_provider_type); 282 if (!TranslateStringToONC( 283 kVPNTypeTable, shill_provider_type, &onc_provider_type)) { 284 return; 285 } 286 onc_object_->SetStringWithoutPathExpansion(::onc::vpn::kType, 287 onc_provider_type); 288 std::string provider_host; 289 if (provider->GetStringWithoutPathExpansion(shill::kHostProperty, 290 &provider_host)) { 291 onc_object_->SetStringWithoutPathExpansion(::onc::vpn::kHost, 292 provider_host); 293 } 294 295 // Translate the nested dictionary. 296 std::string provider_type_dictionary; 297 if (onc_provider_type == ::onc::vpn::kTypeL2TP_IPsec) { 298 TranslateAndAddNestedObject(::onc::vpn::kIPsec, *provider); 299 TranslateAndAddNestedObject(::onc::vpn::kL2TP, *provider); 300 provider_type_dictionary = ::onc::vpn::kIPsec; 301 } else { 302 TranslateAndAddNestedObject(onc_provider_type, *provider); 303 provider_type_dictionary = onc_provider_type; 304 } 305 306 bool save_credentials; 307 if (shill_dictionary_->GetBooleanWithoutPathExpansion( 308 shill::kSaveCredentialsProperty, &save_credentials)) { 309 SetNestedOncValue(provider_type_dictionary, 310 ::onc::vpn::kSaveCredentials, 311 base::FundamentalValue(save_credentials)); 312 } 313 } 314 315 void ShillToONCTranslator::TranslateWiFiWithState() { 316 TranslateWithTableAndSet( 317 shill::kSecurityProperty, kWiFiSecurityTable, ::onc::wifi::kSecurity); 318 std::string ssid = shill_property_util::GetSSIDFromProperties( 319 *shill_dictionary_, NULL /* ignore unknown encoding */); 320 if (!ssid.empty()) 321 onc_object_->SetStringWithoutPathExpansion(::onc::wifi::kSSID, ssid); 322 CopyPropertiesAccordingToSignature(); 323 TranslateAndAddNestedObject(::onc::wifi::kEAP); 324 } 325 326 void ShillToONCTranslator::TranslateWiMAXWithState() { 327 CopyPropertiesAccordingToSignature(); 328 TranslateAndAddNestedObject(::onc::wimax::kEAP); 329 } 330 331 void ShillToONCTranslator::TranslateCellularWithState() { 332 CopyPropertiesAccordingToSignature(); 333 TranslateWithTableAndSet(shill::kActivationStateProperty, 334 kActivationStateTable, 335 ::onc::cellular::kActivationState); 336 TranslateWithTableAndSet(shill::kRoamingStateProperty, 337 kRoamingStateTable, 338 ::onc::cellular::kRoamingState); 339 const base::DictionaryValue* dictionary = NULL; 340 if (shill_dictionary_->GetDictionaryWithoutPathExpansion( 341 shill::kServingOperatorProperty, &dictionary)) { 342 TranslateAndAddNestedObject(::onc::cellular::kServingOperator, *dictionary); 343 } 344 if (shill_dictionary_->GetDictionaryWithoutPathExpansion( 345 shill::kCellularApnProperty, &dictionary)) { 346 TranslateAndAddNestedObject(::onc::cellular::kAPN, *dictionary); 347 } 348 if (shill_dictionary_->GetDictionaryWithoutPathExpansion( 349 shill::kCellularLastGoodApnProperty, &dictionary)) { 350 TranslateAndAddNestedObject(::onc::cellular::kLastGoodAPN, *dictionary); 351 } 352 // Merge the Device dictionary with this one (Cellular) using the 353 // CellularDevice signature. 354 const base::DictionaryValue* device_dictionary = NULL; 355 if (!shill_dictionary_->GetDictionaryWithoutPathExpansion( 356 shill::kDeviceProperty, &device_dictionary)) { 357 return; 358 } 359 ShillToONCTranslator nested_translator(*device_dictionary, 360 onc_source_, 361 kCellularWithStateSignature, 362 kCellularDeviceTable); 363 scoped_ptr<base::DictionaryValue> nested_object = 364 nested_translator.CreateTranslatedONCObject(); 365 onc_object_->MergeDictionary(nested_object.get()); 366 } 367 368 void ShillToONCTranslator::TranslateCellularDevice() { 369 CopyPropertiesAccordingToSignature(); 370 const base::DictionaryValue* shill_sim_lock_status = NULL; 371 if (shill_dictionary_->GetDictionaryWithoutPathExpansion( 372 shill::kSIMLockStatusProperty, &shill_sim_lock_status)) { 373 TranslateAndAddNestedObject(::onc::cellular::kSIMLockStatus, 374 *shill_sim_lock_status); 375 } 376 const base::ListValue* shill_apns = NULL; 377 if (shill_dictionary_->GetListWithoutPathExpansion( 378 shill::kCellularApnListProperty, &shill_apns)) { 379 TranslateAndAddListOfObjects(::onc::cellular::kAPNList, *shill_apns); 380 } 381 const base::ListValue* shill_found_networks = NULL; 382 if (shill_dictionary_->GetListWithoutPathExpansion( 383 shill::kFoundNetworksProperty, &shill_found_networks)) { 384 TranslateAndAddListOfObjects(::onc::cellular::kFoundNetworks, 385 *shill_found_networks); 386 } 387 } 388 389 void ShillToONCTranslator::TranslateNetworkWithState() { 390 CopyPropertiesAccordingToSignature(); 391 392 std::string shill_network_type; 393 shill_dictionary_->GetStringWithoutPathExpansion(shill::kTypeProperty, 394 &shill_network_type); 395 std::string onc_network_type = ::onc::network_type::kEthernet; 396 if (shill_network_type != shill::kTypeEthernet && 397 shill_network_type != shill::kTypeEthernetEap) { 398 TranslateStringToONC( 399 kNetworkTypeTable, shill_network_type, &onc_network_type); 400 } 401 // Translate nested Cellular, WiFi, etc. properties. 402 if (!onc_network_type.empty()) { 403 onc_object_->SetStringWithoutPathExpansion(::onc::network_config::kType, 404 onc_network_type); 405 TranslateAndAddNestedObject(onc_network_type); 406 } 407 408 // Since Name is a read only field in Shill unless it's a VPN, it is copied 409 // here, but not when going the other direction (if it's not a VPN). 410 std::string name; 411 shill_dictionary_->GetStringWithoutPathExpansion(shill::kNameProperty, &name); 412 onc_object_->SetStringWithoutPathExpansion(::onc::network_config::kName, 413 name); 414 415 // Limit ONC state to "NotConnected", "Connected", or "Connecting". 416 std::string state; 417 if (shill_dictionary_->GetStringWithoutPathExpansion(shill::kStateProperty, 418 &state)) { 419 std::string onc_state = ::onc::connection_state::kNotConnected; 420 if (NetworkState::StateIsConnected(state)) { 421 onc_state = ::onc::connection_state::kConnected; 422 } else if (NetworkState::StateIsConnecting(state)) { 423 onc_state = ::onc::connection_state::kConnecting; 424 } 425 onc_object_->SetStringWithoutPathExpansion( 426 ::onc::network_config::kConnectionState, onc_state); 427 // Only set 'RestrictedConnectivity' if true. 428 if (state == shill::kStatePortal) { 429 onc_object_->SetBooleanWithoutPathExpansion( 430 ::onc::network_config::kRestrictedConnectivity, true); 431 } 432 } 433 434 std::string profile_path; 435 if (onc_source_ != ::onc::ONC_SOURCE_UNKNOWN && 436 shill_dictionary_->GetStringWithoutPathExpansion(shill::kProfileProperty, 437 &profile_path)) { 438 std::string source; 439 if (onc_source_ == ::onc::ONC_SOURCE_DEVICE_POLICY) 440 source = ::onc::network_config::kSourceDevicePolicy; 441 else if (onc_source_ == ::onc::ONC_SOURCE_USER_POLICY) 442 source = ::onc::network_config::kSourceUserPolicy; 443 else if (profile_path == NetworkProfileHandler::GetSharedProfilePath()) 444 source = ::onc::network_config::kSourceDevice; 445 else if (!profile_path.empty()) 446 source = ::onc::network_config::kSourceUser; 447 else 448 source = ::onc::network_config::kSourceNone; 449 onc_object_->SetStringWithoutPathExpansion( 450 ::onc::network_config::kSource, source); 451 } 452 453 // Use a human-readable aa:bb format for any hardware MAC address. Note: 454 // this property is provided by the caller but is not part of the Shill 455 // Service properties (it is copied from the Device properties). 456 std::string address; 457 if (shill_dictionary_->GetStringWithoutPathExpansion(shill::kAddressProperty, 458 &address)) { 459 onc_object_->SetStringWithoutPathExpansion( 460 ::onc::network_config::kMacAddress, 461 network_util::FormattedMacAddress(address)); 462 } 463 464 // Shill's Service has an IPConfig property (note the singular), not an 465 // IPConfigs property. However, we require the caller of the translation to 466 // patch the Shill dictionary before passing it to the translator. 467 const base::ListValue* shill_ipconfigs = NULL; 468 if (shill_dictionary_->GetListWithoutPathExpansion(shill::kIPConfigsProperty, 469 &shill_ipconfigs)) { 470 TranslateAndAddListOfObjects(::onc::network_config::kIPConfigs, 471 *shill_ipconfigs); 472 } 473 474 TranslateAndAddNestedObject(::onc::network_config::kSavedIPConfig); 475 TranslateAndAddNestedObject(::onc::network_config::kStaticIPConfig); 476 } 477 478 void ShillToONCTranslator::TranslateIPConfig() { 479 CopyPropertiesAccordingToSignature(); 480 std::string shill_ip_method; 481 shill_dictionary_->GetStringWithoutPathExpansion(shill::kMethodProperty, 482 &shill_ip_method); 483 std::string type; 484 if (shill_ip_method == shill::kTypeIPv4 || 485 shill_ip_method == shill::kTypeDHCP) { 486 type = ::onc::ipconfig::kIPv4; 487 } else if (shill_ip_method == shill::kTypeIPv6 || 488 shill_ip_method == shill::kTypeDHCP6) { 489 type = ::onc::ipconfig::kIPv6; 490 } else { 491 return; // Ignore unhandled IPConfig types, e.g. bootp, zeroconf, ppp 492 } 493 494 onc_object_->SetStringWithoutPathExpansion(::onc::ipconfig::kType, type); 495 } 496 497 void ShillToONCTranslator::TranslateSavedOrStaticIPConfig( 498 const std::string& nameserver_property) { 499 CopyPropertiesAccordingToSignature(); 500 // Saved/Static IP config nameservers are stored as a comma separated list. 501 std::string shill_nameservers; 502 shill_dictionary_->GetStringWithoutPathExpansion( 503 nameserver_property, &shill_nameservers); 504 std::vector<std::string> onc_nameserver_vector; 505 if (Tokenize(shill_nameservers, ",", &onc_nameserver_vector) > 0) { 506 scoped_ptr<base::ListValue> onc_nameservers(new base::ListValue); 507 for (std::vector<std::string>::iterator iter = 508 onc_nameserver_vector.begin(); 509 iter != onc_nameserver_vector.end(); ++iter) { 510 onc_nameservers->AppendString(*iter); 511 } 512 onc_object_->SetWithoutPathExpansion(::onc::ipconfig::kNameServers, 513 onc_nameservers.release()); 514 } 515 // Static and Saved IPConfig in Shill are always of type IPv4. Set this type 516 // in ONC, but not if the object would be empty except the type. 517 if (!onc_object_->empty()) { 518 onc_object_->SetStringWithoutPathExpansion(::onc::ipconfig::kType, 519 ::onc::ipconfig::kIPv4); 520 } 521 } 522 523 void ShillToONCTranslator::TranslateSavedIPConfig() { 524 TranslateSavedOrStaticIPConfig(shill::kSavedIPNameServersProperty); 525 } 526 527 void ShillToONCTranslator::TranslateStaticIPConfig() { 528 TranslateSavedOrStaticIPConfig(shill::kStaticIPNameServersProperty); 529 } 530 531 void ShillToONCTranslator::TranslateAndAddNestedObject( 532 const std::string& onc_field_name) { 533 TranslateAndAddNestedObject(onc_field_name, *shill_dictionary_); 534 } 535 536 void ShillToONCTranslator::TranslateAndAddNestedObject( 537 const std::string& onc_field_name, 538 const base::DictionaryValue& dictionary) { 539 const OncFieldSignature* field_signature = 540 GetFieldSignature(*onc_signature_, onc_field_name); 541 if (!field_signature) { 542 NOTREACHED() << "Unable to find signature for field: " << onc_field_name; 543 return; 544 } 545 ShillToONCTranslator nested_translator( 546 dictionary, onc_source_, *field_signature->value_signature); 547 scoped_ptr<base::DictionaryValue> nested_object = 548 nested_translator.CreateTranslatedONCObject(); 549 if (nested_object->empty()) 550 return; 551 onc_object_->SetWithoutPathExpansion(onc_field_name, nested_object.release()); 552 } 553 554 void ShillToONCTranslator::SetNestedOncValue( 555 const std::string& onc_dictionary_name, 556 const std::string& onc_field_name, 557 const base::Value& value) { 558 base::DictionaryValue* nested; 559 if (!onc_object_->GetDictionaryWithoutPathExpansion( 560 onc_dictionary_name, &nested)) { 561 nested = new base::DictionaryValue; 562 onc_object_->SetWithoutPathExpansion(onc_dictionary_name, nested); 563 } 564 nested->SetWithoutPathExpansion(onc_field_name, value.DeepCopy()); 565 } 566 567 void ShillToONCTranslator::TranslateAndAddListOfObjects( 568 const std::string& onc_field_name, 569 const base::ListValue& list) { 570 const OncFieldSignature* field_signature = 571 GetFieldSignature(*onc_signature_, onc_field_name); 572 if (field_signature->value_signature->onc_type != base::Value::TYPE_LIST) { 573 LOG(ERROR) << "ONC Field name: '" << onc_field_name << "' has type '" 574 << field_signature->value_signature->onc_type 575 << "', expected: base::Value::TYPE_LIST: " << GetName(); 576 return; 577 } 578 DCHECK(field_signature->value_signature->onc_array_entry_signature); 579 scoped_ptr<base::ListValue> result(new base::ListValue()); 580 for (base::ListValue::const_iterator it = list.begin(); 581 it != list.end(); ++it) { 582 const base::DictionaryValue* shill_value = NULL; 583 if (!(*it)->GetAsDictionary(&shill_value)) 584 continue; 585 ShillToONCTranslator nested_translator( 586 *shill_value, 587 onc_source_, 588 *field_signature->value_signature->onc_array_entry_signature); 589 scoped_ptr<base::DictionaryValue> nested_object = 590 nested_translator.CreateTranslatedONCObject(); 591 // If the nested object couldn't be parsed, simply omit it. 592 if (nested_object->empty()) 593 continue; 594 result->Append(nested_object.release()); 595 } 596 // If there are no entries in the list, there is no need to expose this field. 597 if (result->empty()) 598 return; 599 onc_object_->SetWithoutPathExpansion(onc_field_name, result.release()); 600 } 601 602 void ShillToONCTranslator::CopyPropertiesAccordingToSignature() { 603 CopyPropertiesAccordingToSignature(onc_signature_); 604 } 605 606 void ShillToONCTranslator::CopyPropertiesAccordingToSignature( 607 const OncValueSignature* value_signature) { 608 if (value_signature->base_signature) 609 CopyPropertiesAccordingToSignature(value_signature->base_signature); 610 if (!value_signature->fields) 611 return; 612 for (const OncFieldSignature* field_signature = value_signature->fields; 613 field_signature->onc_field_name != NULL; ++field_signature) { 614 CopyProperty(field_signature); 615 } 616 } 617 618 void ShillToONCTranslator::CopyProperty( 619 const OncFieldSignature* field_signature) { 620 std::string shill_property_name; 621 const base::Value* shill_value = NULL; 622 if (!field_translation_table_ || 623 !GetShillPropertyName(field_signature->onc_field_name, 624 field_translation_table_, 625 &shill_property_name) || 626 !shill_dictionary_->GetWithoutPathExpansion(shill_property_name, 627 &shill_value)) { 628 return; 629 } 630 631 if (shill_value->GetType() != field_signature->value_signature->onc_type) { 632 LOG(ERROR) << "Shill property '" << shill_property_name 633 << "' with value " << *shill_value 634 << " has base::Value::Type " << shill_value->GetType() 635 << " but ONC field '" << field_signature->onc_field_name 636 << "' requires type " 637 << field_signature->value_signature->onc_type 638 << ": " << GetName(); 639 return; 640 } 641 642 onc_object_->SetWithoutPathExpansion(field_signature->onc_field_name, 643 shill_value->DeepCopy()); 644 } 645 646 void ShillToONCTranslator::TranslateWithTableAndSet( 647 const std::string& shill_property_name, 648 const StringTranslationEntry table[], 649 const std::string& onc_field_name) { 650 std::string shill_value; 651 if (!shill_dictionary_->GetStringWithoutPathExpansion(shill_property_name, 652 &shill_value)) { 653 return; 654 } 655 std::string onc_value; 656 if (TranslateStringToONC(table, shill_value, &onc_value)) { 657 onc_object_->SetStringWithoutPathExpansion(onc_field_name, onc_value); 658 return; 659 } 660 LOG(ERROR) << "Shill property '" << shill_property_name << "' with value " 661 << shill_value << " couldn't be translated to ONC: " << GetName(); 662 } 663 664 std::string ShillToONCTranslator::GetName() { 665 DCHECK(shill_dictionary_); 666 std::string name; 667 shill_dictionary_->GetStringWithoutPathExpansion(shill::kNameProperty, &name); 668 return name; 669 } 670 671 } // namespace 672 673 scoped_ptr<base::DictionaryValue> TranslateShillServiceToONCPart( 674 const base::DictionaryValue& shill_dictionary, 675 ::onc::ONCSource onc_source, 676 const OncValueSignature* onc_signature) { 677 CHECK(onc_signature != NULL); 678 679 ShillToONCTranslator translator(shill_dictionary, onc_source, *onc_signature); 680 return translator.CreateTranslatedONCObject(); 681 } 682 683 } // namespace onc 684 } // namespace chromeos 685