onc_translator_shill_to_onc.cc revision eb525c5499e34cc9c4b825d6d9e75bb07cc06ace
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 CopyProperty(field_signature); 135 continue; 136 } 137 138 std::string shill_property_name; 139 const base::Value* shill_value = NULL; 140 if (!field_translation_table_ || 141 !GetShillPropertyName(field_signature->onc_field_name, 142 field_translation_table_, 143 &shill_property_name) || 144 !shill_dictionary_->GetWithoutPathExpansion(shill_property_name, 145 &shill_value)) { 146 continue; 147 } 148 149 scoped_ptr<base::Value> translated; 150 std::string shill_str; 151 if (shill_value->GetAsString(&shill_str)) { 152 // Shill wants all Provider/VPN fields to be strings. Translates these 153 // strings back to the correct ONC type. 154 translated = ConvertStringToValue( 155 shill_str, 156 field_signature->value_signature->onc_type); 157 158 if (translated.get() == NULL) { 159 LOG(ERROR) << "Shill property '" << shill_property_name 160 << "' with value " << *shill_value 161 << " couldn't be converted to base::Value::Type " 162 << field_signature->value_signature->onc_type; 163 } else { 164 onc_object_->SetWithoutPathExpansion(onc_field_name, 165 translated.release()); 166 } 167 } else { 168 LOG(ERROR) << "Shill property '" << shill_property_name 169 << "' has value " << *shill_value 170 << ", but expected a string"; 171 } 172 } 173} 174 175void ShillToONCTranslator::TranslateVPN() { 176 TranslateWithTableAndSet(flimflam::kProviderTypeProperty, kVPNTypeTable, 177 vpn::kType); 178 CopyPropertiesAccordingToSignature(); 179 180 std::string vpn_type; 181 if (onc_object_->GetStringWithoutPathExpansion(vpn::kType, 182 &vpn_type)) { 183 if (vpn_type == vpn::kTypeL2TP_IPsec) { 184 TranslateAndAddNestedObject(vpn::kIPsec); 185 TranslateAndAddNestedObject(vpn::kL2TP); 186 } else { 187 TranslateAndAddNestedObject(vpn_type); 188 } 189 } 190} 191 192void ShillToONCTranslator::TranslateWiFiWithState() { 193 TranslateWithTableAndSet(flimflam::kSecurityProperty, kWiFiSecurityTable, 194 wifi::kSecurity); 195 CopyPropertiesAccordingToSignature(); 196} 197 198void ShillToONCTranslator::TranslateAndAddNestedObject( 199 const std::string& onc_field_name) { 200 const OncFieldSignature* field_signature = 201 GetFieldSignature(*onc_signature_, onc_field_name); 202 ShillToONCTranslator nested_translator(*shill_dictionary_, 203 *field_signature->value_signature); 204 scoped_ptr<base::DictionaryValue> nested_object = 205 nested_translator.CreateTranslatedONCObject(); 206 if (nested_object->empty()) 207 return; 208 onc_object_->SetWithoutPathExpansion(onc_field_name, nested_object.release()); 209} 210 211void ShillToONCTranslator::TranslateNetworkWithState() { 212 TranslateWithTableAndSet(flimflam::kTypeProperty, kNetworkTypeTable, 213 network_config::kType); 214 CopyPropertiesAccordingToSignature(); 215 216 std::string network_type; 217 if (onc_object_->GetStringWithoutPathExpansion(network_config::kType, 218 &network_type)) { 219 TranslateAndAddNestedObject(network_type); 220 } 221 222 // Since Name is a read only field in Shill unless it's a VPN, it is copied 223 // here, but not when going the other direction (if it's not a VPN). 224 std::string name; 225 shill_dictionary_->GetStringWithoutPathExpansion(flimflam::kNameProperty, 226 &name); 227 onc_object_->SetStringWithoutPathExpansion(network_config::kName, name); 228 229 std::string state; 230 if (shill_dictionary_->GetStringWithoutPathExpansion(flimflam::kStateProperty, 231 &state)) { 232 std::string onc_state = connection_state::kNotConnected; 233 if (NetworkState::StateIsConnected(state)) { 234 onc_state = connection_state::kConnected; 235 } else if (NetworkState::StateIsConnecting(state)) { 236 onc_state = connection_state::kConnecting; 237 } 238 onc_object_->SetStringWithoutPathExpansion(network_config::kConnectionState, 239 onc_state); 240 } 241} 242 243void ShillToONCTranslator::CopyPropertiesAccordingToSignature() { 244 CopyPropertiesAccordingToSignature(onc_signature_); 245} 246 247void ShillToONCTranslator::CopyPropertiesAccordingToSignature( 248 const OncValueSignature* value_signature) { 249 if (value_signature->base_signature) 250 CopyPropertiesAccordingToSignature(value_signature->base_signature); 251 for (const OncFieldSignature* field_signature = value_signature->fields; 252 field_signature->onc_field_name != NULL; ++field_signature) { 253 CopyProperty(field_signature); 254 } 255} 256 257void ShillToONCTranslator::CopyProperty( 258 const OncFieldSignature* field_signature) { 259 std::string shill_property_name; 260 const base::Value* shill_value = NULL; 261 if (!field_translation_table_ || 262 !GetShillPropertyName(field_signature->onc_field_name, 263 field_translation_table_, 264 &shill_property_name) || 265 !shill_dictionary_->GetWithoutPathExpansion(shill_property_name, 266 &shill_value)) { 267 return; 268 } 269 270 if (shill_value->GetType() != field_signature->value_signature->onc_type) { 271 LOG(ERROR) << "Shill property '" << shill_property_name 272 << "' with value " << *shill_value 273 << " has base::Value::Type " << shill_value->GetType() 274 << " but ONC field '" << field_signature->onc_field_name 275 << "' requires type " 276 << field_signature->value_signature->onc_type << "."; 277 return; 278 } 279 280 onc_object_->SetWithoutPathExpansion(field_signature->onc_field_name, 281 shill_value->DeepCopy()); 282} 283 284void ShillToONCTranslator::TranslateWithTableAndSet( 285 const std::string& shill_property_name, 286 const StringTranslationEntry table[], 287 const std::string& onc_field_name) { 288 std::string shill_value; 289 if (!shill_dictionary_->GetStringWithoutPathExpansion(shill_property_name, 290 &shill_value)) { 291 return; 292 } 293 std::string onc_value; 294 if (TranslateStringToONC(table, shill_value, &onc_value)) { 295 onc_object_->SetStringWithoutPathExpansion(onc_field_name, onc_value); 296 return; 297 } 298 LOG(ERROR) << "Shill property '" << shill_property_name << "' with value " 299 << shill_value << " couldn't be translated to ONC"; 300} 301 302} // namespace 303 304scoped_ptr<base::DictionaryValue> TranslateShillServiceToONCPart( 305 const base::DictionaryValue& shill_dictionary, 306 const OncValueSignature* onc_signature) { 307 CHECK(onc_signature != NULL); 308 309 ShillToONCTranslator translator(shill_dictionary, *onc_signature); 310 return translator.CreateTranslatedONCObject(); 311} 312 313} // namespace onc 314} // namespace chromeos 315