onc_translator_shill_to_onc.cc revision c2db58bd994c04d98e4ee2cd7565b71548655fe3
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 TranslateOpenVPN(); 62 void TranslateVPN(); 63 void TranslateWiFiWithState(); 64 void TranslateNetworkWithState(); 65 66 // Creates an ONC object from |shill_dictionary| according to the signature 67 // associated to |onc_field_name| and adds it to |onc_object_| at 68 // |onc_field_name|. 69 void TranslateAndAddNestedObject(const std::string& onc_field_name); 70 71 // Applies function CopyProperty to each field of |value_signature| and its 72 // base signatures. 73 void CopyPropertiesAccordingToSignature( 74 const OncValueSignature* value_signature); 75 76 // Applies function CopyProperty to each field of |onc_signature_| and its 77 // base signatures. 78 void CopyPropertiesAccordingToSignature(); 79 80 // If |shill_property_name| is defined in |field_signature|, copies this 81 // entry from |shill_dictionary_| to |onc_object_| if it exists. 82 void CopyProperty(const OncFieldSignature* field_signature); 83 84 // If existent, translates the entry at |shill_property_name| in 85 // |shill_dictionary_| using |table|. It is an error if no matching table 86 // entry is found. Writes the result as entry at |onc_field_name| in 87 // |onc_object_|. 88 void TranslateWithTableAndSet(const std::string& shill_property_name, 89 const StringTranslationEntry table[], 90 const std::string& onc_field_name); 91 92 const base::DictionaryValue* shill_dictionary_; 93 const OncValueSignature* onc_signature_; 94 const FieldTranslationEntry* field_translation_table_; 95 scoped_ptr<base::DictionaryValue> onc_object_; 96 97 DISALLOW_COPY_AND_ASSIGN(ShillToONCTranslator); 98}; 99 100scoped_ptr<base::DictionaryValue> 101ShillToONCTranslator::CreateTranslatedONCObject() { 102 onc_object_.reset(new base::DictionaryValue); 103 if (onc_signature_ == &kNetworkWithStateSignature) { 104 TranslateNetworkWithState(); 105 } else if (onc_signature_ == &kVPNSignature) { 106 TranslateVPN(); 107 } else if (onc_signature_ == &kOpenVPNSignature) { 108 TranslateOpenVPN(); 109 } else if (onc_signature_ == &kWiFiWithStateSignature) { 110 TranslateWiFiWithState(); 111 } else { 112 CopyPropertiesAccordingToSignature(); 113 } 114 return onc_object_.Pass(); 115} 116 117void ShillToONCTranslator::TranslateOpenVPN() { 118 // Shill supports only one RemoteCertKU but ONC requires a list. If existing, 119 // wraps the value into a list. 120 std::string certKU; 121 if (shill_dictionary_->GetStringWithoutPathExpansion( 122 flimflam::kOpenVPNRemoteCertKUProperty, &certKU)) { 123 scoped_ptr<base::ListValue> certKUs(new base::ListValue); 124 certKUs->AppendString(certKU); 125 onc_object_->SetWithoutPathExpansion(openvpn::kRemoteCertKU, 126 certKUs.release()); 127 } 128 129 for (const OncFieldSignature* field_signature = onc_signature_->fields; 130 field_signature->onc_field_name != NULL; ++field_signature) { 131 const std::string& onc_field_name = field_signature->onc_field_name; 132 if (onc_field_name == vpn::kSaveCredentials || 133 onc_field_name == openvpn::kRemoteCertKU || 134 onc_field_name == openvpn::kServerCAPEMs) { 135 CopyProperty(field_signature); 136 continue; 137 } 138 139 std::string shill_property_name; 140 const base::Value* shill_value = NULL; 141 if (!field_translation_table_ || 142 !GetShillPropertyName(field_signature->onc_field_name, 143 field_translation_table_, 144 &shill_property_name) || 145 !shill_dictionary_->GetWithoutPathExpansion(shill_property_name, 146 &shill_value)) { 147 continue; 148 } 149 150 scoped_ptr<base::Value> translated; 151 std::string shill_str; 152 if (shill_value->GetAsString(&shill_str)) { 153 // Shill wants all Provider/VPN fields to be strings. Translates these 154 // strings back to the correct ONC type. 155 translated = ConvertStringToValue( 156 shill_str, 157 field_signature->value_signature->onc_type); 158 159 if (translated.get() == NULL) { 160 LOG(ERROR) << "Shill property '" << shill_property_name 161 << "' with value " << *shill_value 162 << " couldn't be converted to base::Value::Type " 163 << field_signature->value_signature->onc_type; 164 } else { 165 onc_object_->SetWithoutPathExpansion(onc_field_name, 166 translated.release()); 167 } 168 } else { 169 LOG(ERROR) << "Shill property '" << shill_property_name 170 << "' has value " << *shill_value 171 << ", but expected a string"; 172 } 173 } 174} 175 176void ShillToONCTranslator::TranslateVPN() { 177 TranslateWithTableAndSet(flimflam::kProviderTypeProperty, kVPNTypeTable, 178 vpn::kType); 179 CopyPropertiesAccordingToSignature(); 180 181 std::string vpn_type; 182 if (onc_object_->GetStringWithoutPathExpansion(vpn::kType, 183 &vpn_type)) { 184 if (vpn_type == vpn::kTypeL2TP_IPsec) { 185 TranslateAndAddNestedObject(vpn::kIPsec); 186 TranslateAndAddNestedObject(vpn::kL2TP); 187 } else { 188 TranslateAndAddNestedObject(vpn_type); 189 } 190 } 191} 192 193void ShillToONCTranslator::TranslateWiFiWithState() { 194 TranslateWithTableAndSet(flimflam::kSecurityProperty, kWiFiSecurityTable, 195 wifi::kSecurity); 196 CopyPropertiesAccordingToSignature(); 197} 198 199void ShillToONCTranslator::TranslateAndAddNestedObject( 200 const std::string& onc_field_name) { 201 const OncFieldSignature* field_signature = 202 GetFieldSignature(*onc_signature_, onc_field_name); 203 ShillToONCTranslator nested_translator(*shill_dictionary_, 204 *field_signature->value_signature); 205 scoped_ptr<base::DictionaryValue> nested_object = 206 nested_translator.CreateTranslatedONCObject(); 207 if (nested_object->empty()) 208 return; 209 onc_object_->SetWithoutPathExpansion(onc_field_name, nested_object.release()); 210} 211 212void ShillToONCTranslator::TranslateNetworkWithState() { 213 TranslateWithTableAndSet(flimflam::kTypeProperty, kNetworkTypeTable, 214 network_config::kType); 215 CopyPropertiesAccordingToSignature(); 216 217 std::string network_type; 218 if (onc_object_->GetStringWithoutPathExpansion(network_config::kType, 219 &network_type)) { 220 TranslateAndAddNestedObject(network_type); 221 } 222 223 // Since Name is a read only field in Shill unless it's a VPN, it is copied 224 // here, but not when going the other direction (if it's not a VPN). 225 std::string name; 226 shill_dictionary_->GetStringWithoutPathExpansion(flimflam::kNameProperty, 227 &name); 228 onc_object_->SetStringWithoutPathExpansion(network_config::kName, name); 229 230 std::string state; 231 if (shill_dictionary_->GetStringWithoutPathExpansion(flimflam::kStateProperty, 232 &state)) { 233 std::string onc_state = connection_state::kNotConnected; 234 if (NetworkState::StateIsConnected(state)) { 235 onc_state = connection_state::kConnected; 236 } else if (NetworkState::StateIsConnecting(state)) { 237 onc_state = connection_state::kConnecting; 238 } 239 onc_object_->SetStringWithoutPathExpansion(network_config::kConnectionState, 240 onc_state); 241 } 242} 243 244void ShillToONCTranslator::CopyPropertiesAccordingToSignature() { 245 CopyPropertiesAccordingToSignature(onc_signature_); 246} 247 248void ShillToONCTranslator::CopyPropertiesAccordingToSignature( 249 const OncValueSignature* value_signature) { 250 if (value_signature->base_signature) 251 CopyPropertiesAccordingToSignature(value_signature->base_signature); 252 for (const OncFieldSignature* field_signature = value_signature->fields; 253 field_signature->onc_field_name != NULL; ++field_signature) { 254 CopyProperty(field_signature); 255 } 256} 257 258void ShillToONCTranslator::CopyProperty( 259 const OncFieldSignature* field_signature) { 260 std::string shill_property_name; 261 const base::Value* shill_value = NULL; 262 if (!field_translation_table_ || 263 !GetShillPropertyName(field_signature->onc_field_name, 264 field_translation_table_, 265 &shill_property_name) || 266 !shill_dictionary_->GetWithoutPathExpansion(shill_property_name, 267 &shill_value)) { 268 return; 269 } 270 271 if (shill_value->GetType() != field_signature->value_signature->onc_type) { 272 LOG(ERROR) << "Shill property '" << shill_property_name 273 << "' with value " << *shill_value 274 << " has base::Value::Type " << shill_value->GetType() 275 << " but ONC field '" << field_signature->onc_field_name 276 << "' requires type " 277 << field_signature->value_signature->onc_type << "."; 278 return; 279 } 280 281 onc_object_->SetWithoutPathExpansion(field_signature->onc_field_name, 282 shill_value->DeepCopy()); 283} 284 285void ShillToONCTranslator::TranslateWithTableAndSet( 286 const std::string& shill_property_name, 287 const StringTranslationEntry table[], 288 const std::string& onc_field_name) { 289 std::string shill_value; 290 if (!shill_dictionary_->GetStringWithoutPathExpansion(shill_property_name, 291 &shill_value)) { 292 return; 293 } 294 std::string onc_value; 295 if (TranslateStringToONC(table, shill_value, &onc_value)) { 296 onc_object_->SetStringWithoutPathExpansion(onc_field_name, onc_value); 297 return; 298 } 299 LOG(ERROR) << "Shill property '" << shill_property_name << "' with value " 300 << shill_value << " couldn't be translated to ONC"; 301} 302 303} // namespace 304 305scoped_ptr<base::DictionaryValue> TranslateShillServiceToONCPart( 306 const base::DictionaryValue& shill_dictionary, 307 const OncValueSignature* onc_signature) { 308 CHECK(onc_signature != NULL); 309 310 ShillToONCTranslator translator(shill_dictionary, *onc_signature); 311 return translator.CreateTranslatedONCObject(); 312} 313 314} // namespace onc 315} // namespace chromeos 316