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