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.
5 #include "chromeos/network/onc/onc_translator.h"
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/network_util.h"
16 #include "chromeos/network/onc/onc_signature.h"
17 #include "chromeos/network/onc/onc_translation_tables.h"
18 #include "chromeos/network/shill_property_util.h"
19 #include "components/onc/onc_constants.h"
20 #include "third_party/cros_system_api/dbus/service_constants.h"
27 // Converts |str| to a base::Value of the given |type|. If the conversion fails,
29 scoped_ptr<base::Value> ConvertStringToValue(const std::string& str,
30 base::Value::Type type) {
32 if (type == base::Value::TYPE_STRING) {
33 value = new base::StringValue(str);
35 value = base::JSONReader::Read(str);
38 if (value == NULL || value->GetType() != type) {
42 return make_scoped_ptr(value);
45 // This class implements the translation of properties from the given
46 // |shill_dictionary| to a new ONC object of signature |onc_signature|. Using
47 // recursive calls to CreateTranslatedONCObject of new instances, nested objects
49 class ShillToONCTranslator {
51 ShillToONCTranslator(const base::DictionaryValue& shill_dictionary,
52 const OncValueSignature& onc_signature)
53 : shill_dictionary_(&shill_dictionary),
54 onc_signature_(&onc_signature) {
55 field_translation_table_ = GetFieldTranslationTable(onc_signature);
58 ShillToONCTranslator(const base::DictionaryValue& shill_dictionary,
59 const OncValueSignature& onc_signature,
60 const FieldTranslationEntry* field_translation_table)
61 : shill_dictionary_(&shill_dictionary),
62 onc_signature_(&onc_signature),
63 field_translation_table_(field_translation_table) {
66 // Translates the associated Shill dictionary and creates an ONC object of the
68 scoped_ptr<base::DictionaryValue> CreateTranslatedONCObject();
71 void TranslateEthernet();
72 void TranslateOpenVPN();
73 void TranslateIPsec();
75 void TranslateWiFiWithState();
76 void TranslateCellularWithState();
77 void TranslateCellularDevice();
78 void TranslateNetworkWithState();
79 void TranslateIPConfig();
81 // Creates an ONC object from |dictionary| according to the signature
82 // associated to |onc_field_name| and adds it to |onc_object_| at
84 void TranslateAndAddNestedObject(const std::string& onc_field_name,
85 const base::DictionaryValue& dictionary);
87 // Creates an ONC object from |shill_dictionary_| according to the signature
88 // associated to |onc_field_name| and adds it to |onc_object_| at
90 void TranslateAndAddNestedObject(const std::string& onc_field_name);
92 // Translates a list of nested objects and adds the list to |onc_object_| at
93 // |onc_field_name|. If there are errors while parsing individual objects or
94 // if the resulting list contains no entries, the result will not be added to
96 void TranslateAndAddListOfObjects(const std::string& onc_field_name,
97 const base::ListValue& list);
99 // Applies function CopyProperty to each field of |value_signature| and its
101 void CopyPropertiesAccordingToSignature(
102 const OncValueSignature* value_signature);
104 // Applies function CopyProperty to each field of |onc_signature_| and its
106 void CopyPropertiesAccordingToSignature();
108 // If |shill_property_name| is defined in |field_signature|, copies this
109 // entry from |shill_dictionary_| to |onc_object_| if it exists.
110 void CopyProperty(const OncFieldSignature* field_signature);
112 // If existent, translates the entry at |shill_property_name| in
113 // |shill_dictionary_| using |table|. It is an error if no matching table
114 // entry is found. Writes the result as entry at |onc_field_name| in
116 void TranslateWithTableAndSet(const std::string& shill_property_name,
117 const StringTranslationEntry table[],
118 const std::string& onc_field_name);
120 const base::DictionaryValue* shill_dictionary_;
121 const OncValueSignature* onc_signature_;
122 const FieldTranslationEntry* field_translation_table_;
123 scoped_ptr<base::DictionaryValue> onc_object_;
125 DISALLOW_COPY_AND_ASSIGN(ShillToONCTranslator);
128 scoped_ptr<base::DictionaryValue>
129 ShillToONCTranslator::CreateTranslatedONCObject() {
130 onc_object_.reset(new base::DictionaryValue);
131 if (onc_signature_ == &kNetworkWithStateSignature) {
132 TranslateNetworkWithState();
133 } else if (onc_signature_ == &kEthernetSignature) {
135 } else if (onc_signature_ == &kVPNSignature) {
137 } else if (onc_signature_ == &kOpenVPNSignature) {
139 } else if (onc_signature_ == &kIPsecSignature) {
141 } else if (onc_signature_ == &kWiFiWithStateSignature) {
142 TranslateWiFiWithState();
143 } else if (onc_signature_ == &kCellularWithStateSignature) {
144 if (field_translation_table_ == kCellularDeviceTable)
145 TranslateCellularDevice();
147 TranslateCellularWithState();
148 } else if (onc_signature_ == &kIPConfigSignature) {
151 CopyPropertiesAccordingToSignature();
153 return onc_object_.Pass();
156 void ShillToONCTranslator::TranslateEthernet() {
157 std::string shill_network_type;
158 shill_dictionary_->GetStringWithoutPathExpansion(shill::kTypeProperty,
159 &shill_network_type);
160 const char* onc_auth = ::onc::ethernet::kAuthenticationNone;
161 if (shill_network_type == shill::kTypeEthernetEap)
162 onc_auth = ::onc::ethernet::k8021X;
163 onc_object_->SetStringWithoutPathExpansion(::onc::ethernet::kAuthentication,
167 void ShillToONCTranslator::TranslateOpenVPN() {
168 if (shill_dictionary_->HasKey(shill::kOpenVPNVerifyX509NameProperty))
169 TranslateAndAddNestedObject(::onc::openvpn::kVerifyX509);
171 // Shill supports only one RemoteCertKU but ONC requires a list. If existing,
172 // wraps the value into a list.
174 if (shill_dictionary_->GetStringWithoutPathExpansion(
175 shill::kOpenVPNRemoteCertKUProperty, &certKU)) {
176 scoped_ptr<base::ListValue> certKUs(new base::ListValue);
177 certKUs->AppendString(certKU);
178 onc_object_->SetWithoutPathExpansion(::onc::openvpn::kRemoteCertKU,
182 for (const OncFieldSignature* field_signature = onc_signature_->fields;
183 field_signature->onc_field_name != NULL; ++field_signature) {
184 const std::string& onc_field_name = field_signature->onc_field_name;
185 if (onc_field_name == ::onc::vpn::kSaveCredentials ||
186 onc_field_name == ::onc::openvpn::kRemoteCertKU ||
187 onc_field_name == ::onc::openvpn::kServerCAPEMs) {
188 CopyProperty(field_signature);
192 std::string shill_property_name;
193 const base::Value* shill_value = NULL;
194 if (!field_translation_table_ ||
195 !GetShillPropertyName(field_signature->onc_field_name,
196 field_translation_table_,
197 &shill_property_name) ||
198 !shill_dictionary_->GetWithoutPathExpansion(shill_property_name,
203 scoped_ptr<base::Value> translated;
204 std::string shill_str;
205 if (shill_value->GetAsString(&shill_str)) {
206 // Shill wants all Provider/VPN fields to be strings. Translates these
207 // strings back to the correct ONC type.
208 translated = ConvertStringToValue(
210 field_signature->value_signature->onc_type);
212 if (translated.get() == NULL) {
213 LOG(ERROR) << "Shill property '" << shill_property_name
214 << "' with value " << *shill_value
215 << " couldn't be converted to base::Value::Type "
216 << field_signature->value_signature->onc_type;
218 onc_object_->SetWithoutPathExpansion(onc_field_name,
219 translated.release());
222 LOG(ERROR) << "Shill property '" << shill_property_name
223 << "' has value " << *shill_value
224 << ", but expected a string";
229 void ShillToONCTranslator::TranslateIPsec() {
230 CopyPropertiesAccordingToSignature();
231 if (shill_dictionary_->HasKey(shill::kL2tpIpsecXauthUserProperty))
232 TranslateAndAddNestedObject(::onc::ipsec::kXAUTH);
235 void ShillToONCTranslator::TranslateVPN() {
236 TranslateWithTableAndSet(
237 shill::kProviderTypeProperty, kVPNTypeTable, ::onc::vpn::kType);
238 CopyPropertiesAccordingToSignature();
240 std::string vpn_type;
241 if (onc_object_->GetStringWithoutPathExpansion(::onc::vpn::kType,
243 if (vpn_type == ::onc::vpn::kTypeL2TP_IPsec) {
244 TranslateAndAddNestedObject(::onc::vpn::kIPsec);
245 TranslateAndAddNestedObject(::onc::vpn::kL2TP);
247 TranslateAndAddNestedObject(vpn_type);
252 void ShillToONCTranslator::TranslateWiFiWithState() {
253 TranslateWithTableAndSet(
254 shill::kSecurityProperty, kWiFiSecurityTable, ::onc::wifi::kSecurity);
255 std::string ssid = shill_property_util::GetSSIDFromProperties(
256 *shill_dictionary_, NULL /* ignore unknown encoding */);
258 onc_object_->SetStringWithoutPathExpansion(::onc::wifi::kSSID, ssid);
259 CopyPropertiesAccordingToSignature();
262 void ShillToONCTranslator::TranslateCellularWithState() {
263 CopyPropertiesAccordingToSignature();
264 const base::DictionaryValue* dictionary = NULL;
265 if (shill_dictionary_->GetDictionaryWithoutPathExpansion(
266 shill::kServingOperatorProperty, &dictionary)) {
267 TranslateAndAddNestedObject(::onc::cellular::kServingOperator, *dictionary);
269 if (shill_dictionary_->GetDictionaryWithoutPathExpansion(
270 shill::kCellularApnProperty, &dictionary)) {
271 TranslateAndAddNestedObject(::onc::cellular::kAPN, *dictionary);
273 // Merge the Device dictionary with this one (Cellular) using the
274 // CellularDevice signature.
275 const base::DictionaryValue* device_dictionary = NULL;
276 if (!shill_dictionary_->GetDictionaryWithoutPathExpansion(
277 shill::kDeviceProperty, &device_dictionary)) {
280 ShillToONCTranslator nested_translator(*device_dictionary,
281 kCellularWithStateSignature,
282 kCellularDeviceTable);
283 scoped_ptr<base::DictionaryValue> nested_object =
284 nested_translator.CreateTranslatedONCObject();
285 onc_object_->MergeDictionary(nested_object.get());
288 void ShillToONCTranslator::TranslateCellularDevice() {
289 CopyPropertiesAccordingToSignature();
290 const base::DictionaryValue* shill_sim_lock_status = NULL;
291 if (shill_dictionary_->GetDictionaryWithoutPathExpansion(
292 shill::kSIMLockStatusProperty, &shill_sim_lock_status)) {
293 TranslateAndAddNestedObject(::onc::cellular::kSIMLockStatus,
294 *shill_sim_lock_status);
296 const base::ListValue* shill_apns = NULL;
297 if (shill_dictionary_->GetListWithoutPathExpansion(
298 shill::kCellularApnListProperty, &shill_apns)) {
299 TranslateAndAddListOfObjects(::onc::cellular::kAPNList, *shill_apns);
301 const base::ListValue* shill_found_networks = NULL;
302 if (shill_dictionary_->GetListWithoutPathExpansion(
303 shill::kFoundNetworksProperty, &shill_found_networks)) {
304 TranslateAndAddListOfObjects(::onc::cellular::kFoundNetworks,
305 *shill_found_networks);
309 void ShillToONCTranslator::TranslateNetworkWithState() {
310 CopyPropertiesAccordingToSignature();
312 std::string shill_network_type;
313 shill_dictionary_->GetStringWithoutPathExpansion(shill::kTypeProperty,
314 &shill_network_type);
315 std::string onc_network_type = ::onc::network_type::kEthernet;
316 if (shill_network_type != shill::kTypeEthernet &&
317 shill_network_type != shill::kTypeEthernetEap) {
318 TranslateStringToONC(
319 kNetworkTypeTable, shill_network_type, &onc_network_type);
321 // Translate nested Cellular, WiFi, etc. properties.
322 if (!onc_network_type.empty()) {
323 onc_object_->SetStringWithoutPathExpansion(::onc::network_config::kType,
325 TranslateAndAddNestedObject(onc_network_type);
328 // Since Name is a read only field in Shill unless it's a VPN, it is copied
329 // here, but not when going the other direction (if it's not a VPN).
331 shill_dictionary_->GetStringWithoutPathExpansion(shill::kNameProperty,
333 onc_object_->SetStringWithoutPathExpansion(::onc::network_config::kName,
336 // Limit ONC state to "NotConnected", "Connected", or "Connecting".
338 if (shill_dictionary_->GetStringWithoutPathExpansion(shill::kStateProperty,
340 std::string onc_state = ::onc::connection_state::kNotConnected;
341 if (NetworkState::StateIsConnected(state)) {
342 onc_state = ::onc::connection_state::kConnected;
343 } else if (NetworkState::StateIsConnecting(state)) {
344 onc_state = ::onc::connection_state::kConnecting;
346 onc_object_->SetStringWithoutPathExpansion(
347 ::onc::network_config::kConnectionState, onc_state);
350 // Use a human-readable aa:bb format for any hardware MAC address. Note:
351 // this property is provided by the caller but is not part of the Shill
352 // Service properties (it is copied from the Device properties).
354 if (shill_dictionary_->GetStringWithoutPathExpansion(shill::kAddressProperty,
356 onc_object_->SetStringWithoutPathExpansion(
357 ::onc::network_config::kMacAddress,
358 network_util::FormattedMacAddress(address));
361 // Shill's Service has an IPConfig property (note the singular), not an
362 // IPConfigs property. However, we require the caller of the translation to
363 // patch the Shill dictionary before passing it to the translator.
364 const base::ListValue* shill_ipconfigs = NULL;
365 if (shill_dictionary_->GetListWithoutPathExpansion(shill::kIPConfigsProperty,
367 TranslateAndAddListOfObjects(::onc::network_config::kIPConfigs,
372 void ShillToONCTranslator::TranslateIPConfig() {
373 CopyPropertiesAccordingToSignature();
374 std::string shill_ip_method;
375 shill_dictionary_->GetStringWithoutPathExpansion(shill::kMethodProperty,
378 if (shill_ip_method == shill::kTypeIPv4 ||
379 shill_ip_method == shill::kTypeDHCP) {
380 type = ::onc::ipconfig::kIPv4;
381 } else if (shill_ip_method == shill::kTypeIPv6 ||
382 shill_ip_method == shill::kTypeDHCP6) {
383 type = ::onc::ipconfig::kIPv6;
385 return; // Ignore unhandled IPConfig types, e.g. bootp, zeroconf, ppp
388 onc_object_->SetStringWithoutPathExpansion(::onc::ipconfig::kType, type);
391 void ShillToONCTranslator::TranslateAndAddNestedObject(
392 const std::string& onc_field_name) {
393 TranslateAndAddNestedObject(onc_field_name, *shill_dictionary_);
396 void ShillToONCTranslator::TranslateAndAddNestedObject(
397 const std::string& onc_field_name,
398 const base::DictionaryValue& dictionary) {
399 const OncFieldSignature* field_signature =
400 GetFieldSignature(*onc_signature_, onc_field_name);
401 if (!field_signature) {
402 NOTREACHED() << "Unable to find signature for field: " << onc_field_name;
405 ShillToONCTranslator nested_translator(dictionary,
406 *field_signature->value_signature);
407 scoped_ptr<base::DictionaryValue> nested_object =
408 nested_translator.CreateTranslatedONCObject();
409 if (nested_object->empty())
411 onc_object_->SetWithoutPathExpansion(onc_field_name, nested_object.release());
414 void ShillToONCTranslator::TranslateAndAddListOfObjects(
415 const std::string& onc_field_name,
416 const base::ListValue& list) {
417 const OncFieldSignature* field_signature =
418 GetFieldSignature(*onc_signature_, onc_field_name);
419 if (field_signature->value_signature->onc_type != base::Value::TYPE_LIST) {
420 LOG(ERROR) << "ONC Field name: '" << onc_field_name << "' has type '"
421 << field_signature->value_signature->onc_type
422 << "', expected: base::Value::TYPE_LIST.";
425 DCHECK(field_signature->value_signature->onc_array_entry_signature);
426 scoped_ptr<base::ListValue> result(new base::ListValue());
427 for (base::ListValue::const_iterator it = list.begin();
428 it != list.end(); ++it) {
429 const base::DictionaryValue* shill_value = NULL;
430 if (!(*it)->GetAsDictionary(&shill_value))
432 ShillToONCTranslator nested_translator(
434 *field_signature->value_signature->onc_array_entry_signature);
435 scoped_ptr<base::DictionaryValue> nested_object =
436 nested_translator.CreateTranslatedONCObject();
437 // If the nested object couldn't be parsed, simply omit it.
438 if (nested_object->empty())
440 result->Append(nested_object.release());
442 // If there are no entries in the list, there is no need to expose this field.
445 onc_object_->SetWithoutPathExpansion(onc_field_name, result.release());
448 void ShillToONCTranslator::CopyPropertiesAccordingToSignature() {
449 CopyPropertiesAccordingToSignature(onc_signature_);
452 void ShillToONCTranslator::CopyPropertiesAccordingToSignature(
453 const OncValueSignature* value_signature) {
454 if (value_signature->base_signature)
455 CopyPropertiesAccordingToSignature(value_signature->base_signature);
456 for (const OncFieldSignature* field_signature = value_signature->fields;
457 field_signature->onc_field_name != NULL; ++field_signature) {
458 CopyProperty(field_signature);
462 void ShillToONCTranslator::CopyProperty(
463 const OncFieldSignature* field_signature) {
464 std::string shill_property_name;
465 const base::Value* shill_value = NULL;
466 if (!field_translation_table_ ||
467 !GetShillPropertyName(field_signature->onc_field_name,
468 field_translation_table_,
469 &shill_property_name) ||
470 !shill_dictionary_->GetWithoutPathExpansion(shill_property_name,
475 if (shill_value->GetType() != field_signature->value_signature->onc_type) {
476 LOG(ERROR) << "Shill property '" << shill_property_name
477 << "' with value " << *shill_value
478 << " has base::Value::Type " << shill_value->GetType()
479 << " but ONC field '" << field_signature->onc_field_name
480 << "' requires type "
481 << field_signature->value_signature->onc_type << ".";
485 onc_object_->SetWithoutPathExpansion(field_signature->onc_field_name,
486 shill_value->DeepCopy());
489 void ShillToONCTranslator::TranslateWithTableAndSet(
490 const std::string& shill_property_name,
491 const StringTranslationEntry table[],
492 const std::string& onc_field_name) {
493 std::string shill_value;
494 if (!shill_dictionary_->GetStringWithoutPathExpansion(shill_property_name,
498 std::string onc_value;
499 if (TranslateStringToONC(table, shill_value, &onc_value)) {
500 onc_object_->SetStringWithoutPathExpansion(onc_field_name, onc_value);
503 LOG(ERROR) << "Shill property '" << shill_property_name << "' with value "
504 << shill_value << " couldn't be translated to ONC";
509 scoped_ptr<base::DictionaryValue> TranslateShillServiceToONCPart(
510 const base::DictionaryValue& shill_dictionary,
511 const OncValueSignature* onc_signature) {
512 CHECK(onc_signature != NULL);
514 ShillToONCTranslator translator(shill_dictionary, *onc_signature);
515 return translator.CreateTranslatedONCObject();
519 } // namespace chromeos