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 // Sets |onc_field_name| in dictionary |onc_dictionary_name| in |onc_object_|
93 // to |value| if the dictionary exists.
94 void SetNestedOncValue(const std::string
& onc_dictionary_name
,
95 const std::string
& onc_field_name
,
96 const base::Value
& value
);
98 // Translates a list of nested objects and adds the list to |onc_object_| at
99 // |onc_field_name|. If there are errors while parsing individual objects or
100 // if the resulting list contains no entries, the result will not be added to
102 void TranslateAndAddListOfObjects(const std::string
& onc_field_name
,
103 const base::ListValue
& list
);
105 // Applies function CopyProperty to each field of |value_signature| and its
107 void CopyPropertiesAccordingToSignature(
108 const OncValueSignature
* value_signature
);
110 // Applies function CopyProperty to each field of |onc_signature_| and its
112 void CopyPropertiesAccordingToSignature();
114 // If |shill_property_name| is defined in |field_signature|, copies this
115 // entry from |shill_dictionary_| to |onc_object_| if it exists.
116 void CopyProperty(const OncFieldSignature
* field_signature
);
118 // If existent, translates the entry at |shill_property_name| in
119 // |shill_dictionary_| using |table|. It is an error if no matching table
120 // entry is found. Writes the result as entry at |onc_field_name| in
122 void TranslateWithTableAndSet(const std::string
& shill_property_name
,
123 const StringTranslationEntry table
[],
124 const std::string
& onc_field_name
);
126 // Returns the name of the Shill service provided in |shill_dictionary_|
128 std::string
GetName();
130 const base::DictionaryValue
* shill_dictionary_
;
131 const OncValueSignature
* onc_signature_
;
132 const FieldTranslationEntry
* field_translation_table_
;
133 scoped_ptr
<base::DictionaryValue
> onc_object_
;
135 DISALLOW_COPY_AND_ASSIGN(ShillToONCTranslator
);
138 scoped_ptr
<base::DictionaryValue
>
139 ShillToONCTranslator::CreateTranslatedONCObject() {
140 onc_object_
.reset(new base::DictionaryValue
);
141 if (onc_signature_
== &kNetworkWithStateSignature
) {
142 TranslateNetworkWithState();
143 } else if (onc_signature_
== &kEthernetSignature
) {
145 } else if (onc_signature_
== &kVPNSignature
) {
147 } else if (onc_signature_
== &kOpenVPNSignature
) {
149 } else if (onc_signature_
== &kIPsecSignature
) {
151 } else if (onc_signature_
== &kWiFiWithStateSignature
) {
152 TranslateWiFiWithState();
153 } else if (onc_signature_
== &kCellularWithStateSignature
) {
154 if (field_translation_table_
== kCellularDeviceTable
)
155 TranslateCellularDevice();
157 TranslateCellularWithState();
158 } else if (onc_signature_
== &kIPConfigSignature
) {
161 CopyPropertiesAccordingToSignature();
163 return onc_object_
.Pass();
166 void ShillToONCTranslator::TranslateEthernet() {
167 std::string shill_network_type
;
168 shill_dictionary_
->GetStringWithoutPathExpansion(shill::kTypeProperty
,
169 &shill_network_type
);
170 const char* onc_auth
= ::onc::ethernet::kAuthenticationNone
;
171 if (shill_network_type
== shill::kTypeEthernetEap
)
172 onc_auth
= ::onc::ethernet::k8021X
;
173 onc_object_
->SetStringWithoutPathExpansion(::onc::ethernet::kAuthentication
,
177 void ShillToONCTranslator::TranslateOpenVPN() {
178 if (shill_dictionary_
->HasKey(shill::kOpenVPNVerifyX509NameProperty
))
179 TranslateAndAddNestedObject(::onc::openvpn::kVerifyX509
);
181 // Shill supports only one RemoteCertKU but ONC requires a list. If existing,
182 // wraps the value into a list.
184 if (shill_dictionary_
->GetStringWithoutPathExpansion(
185 shill::kOpenVPNRemoteCertKUProperty
, &certKU
)) {
186 scoped_ptr
<base::ListValue
> certKUs(new base::ListValue
);
187 certKUs
->AppendString(certKU
);
188 onc_object_
->SetWithoutPathExpansion(::onc::openvpn::kRemoteCertKU
,
192 for (const OncFieldSignature
* field_signature
= onc_signature_
->fields
;
193 field_signature
->onc_field_name
!= NULL
; ++field_signature
) {
194 const std::string
& onc_field_name
= field_signature
->onc_field_name
;
195 if (onc_field_name
== ::onc::openvpn::kRemoteCertKU
||
196 onc_field_name
== ::onc::openvpn::kServerCAPEMs
) {
197 CopyProperty(field_signature
);
201 std::string shill_property_name
;
202 const base::Value
* shill_value
= NULL
;
203 if (!field_translation_table_
||
204 !GetShillPropertyName(field_signature
->onc_field_name
,
205 field_translation_table_
,
206 &shill_property_name
) ||
207 !shill_dictionary_
->GetWithoutPathExpansion(shill_property_name
,
212 scoped_ptr
<base::Value
> translated
;
213 std::string shill_str
;
214 if (shill_value
->GetAsString(&shill_str
)) {
215 // Shill wants all Provider/VPN fields to be strings. Translates these
216 // strings back to the correct ONC type.
217 translated
= ConvertStringToValue(
219 field_signature
->value_signature
->onc_type
);
221 if (translated
.get() == NULL
) {
222 LOG(ERROR
) << "Shill property '" << shill_property_name
223 << "' with value " << *shill_value
224 << " couldn't be converted to base::Value::Type "
225 << field_signature
->value_signature
->onc_type
226 << ": " << GetName();
228 onc_object_
->SetWithoutPathExpansion(onc_field_name
,
229 translated
.release());
232 LOG(ERROR
) << "Shill property '" << shill_property_name
233 << "' has value " << *shill_value
234 << ", but expected a string: " << GetName();
239 void ShillToONCTranslator::TranslateIPsec() {
240 CopyPropertiesAccordingToSignature();
241 if (shill_dictionary_
->HasKey(shill::kL2tpIpsecXauthUserProperty
))
242 TranslateAndAddNestedObject(::onc::ipsec::kXAUTH
);
245 void ShillToONCTranslator::TranslateVPN() {
246 CopyPropertiesAccordingToSignature();
248 // Parse Shill Provider dictionary. Note, this may not exist, e.g. if we are
249 // just translating network state in network_util::TranslateNetworkStateToONC.
250 const base::DictionaryValue
* provider
= NULL
;
251 if (!shill_dictionary_
->GetDictionaryWithoutPathExpansion(
252 shill::kProviderProperty
, &provider
)) {
255 std::string shill_provider_type
, onc_provider_type
;
256 provider
->GetStringWithoutPathExpansion(shill::kTypeProperty
,
257 &shill_provider_type
);
258 if (!TranslateStringToONC(
259 kVPNTypeTable
, shill_provider_type
, &onc_provider_type
)) {
262 onc_object_
->SetStringWithoutPathExpansion(::onc::vpn::kType
,
264 std::string provider_host
;
265 if (provider
->GetStringWithoutPathExpansion(shill::kHostProperty
,
267 onc_object_
->SetStringWithoutPathExpansion(::onc::vpn::kHost
,
271 // Translate the nested dictionary.
272 std::string provider_type_dictionary
;
273 if (onc_provider_type
== ::onc::vpn::kTypeL2TP_IPsec
) {
274 TranslateAndAddNestedObject(::onc::vpn::kIPsec
, *provider
);
275 TranslateAndAddNestedObject(::onc::vpn::kL2TP
, *provider
);
276 provider_type_dictionary
= ::onc::vpn::kIPsec
;
278 TranslateAndAddNestedObject(onc_provider_type
, *provider
);
279 provider_type_dictionary
= onc_provider_type
;
282 bool save_credentials
;
283 if (shill_dictionary_
->GetBooleanWithoutPathExpansion(
284 shill::kSaveCredentialsProperty
, &save_credentials
)) {
285 SetNestedOncValue(provider_type_dictionary
,
286 ::onc::vpn::kSaveCredentials
,
287 base::FundamentalValue(save_credentials
));
291 void ShillToONCTranslator::TranslateWiFiWithState() {
292 TranslateWithTableAndSet(
293 shill::kSecurityProperty
, kWiFiSecurityTable
, ::onc::wifi::kSecurity
);
294 std::string ssid
= shill_property_util::GetSSIDFromProperties(
295 *shill_dictionary_
, NULL
/* ignore unknown encoding */);
297 onc_object_
->SetStringWithoutPathExpansion(::onc::wifi::kSSID
, ssid
);
298 CopyPropertiesAccordingToSignature();
301 void ShillToONCTranslator::TranslateCellularWithState() {
302 CopyPropertiesAccordingToSignature();
303 const base::DictionaryValue
* dictionary
= NULL
;
304 if (shill_dictionary_
->GetDictionaryWithoutPathExpansion(
305 shill::kServingOperatorProperty
, &dictionary
)) {
306 TranslateAndAddNestedObject(::onc::cellular::kServingOperator
, *dictionary
);
308 if (shill_dictionary_
->GetDictionaryWithoutPathExpansion(
309 shill::kCellularApnProperty
, &dictionary
)) {
310 TranslateAndAddNestedObject(::onc::cellular::kAPN
, *dictionary
);
312 // Merge the Device dictionary with this one (Cellular) using the
313 // CellularDevice signature.
314 const base::DictionaryValue
* device_dictionary
= NULL
;
315 if (!shill_dictionary_
->GetDictionaryWithoutPathExpansion(
316 shill::kDeviceProperty
, &device_dictionary
)) {
319 ShillToONCTranslator
nested_translator(*device_dictionary
,
320 kCellularWithStateSignature
,
321 kCellularDeviceTable
);
322 scoped_ptr
<base::DictionaryValue
> nested_object
=
323 nested_translator
.CreateTranslatedONCObject();
324 onc_object_
->MergeDictionary(nested_object
.get());
327 void ShillToONCTranslator::TranslateCellularDevice() {
328 CopyPropertiesAccordingToSignature();
329 const base::DictionaryValue
* shill_sim_lock_status
= NULL
;
330 if (shill_dictionary_
->GetDictionaryWithoutPathExpansion(
331 shill::kSIMLockStatusProperty
, &shill_sim_lock_status
)) {
332 TranslateAndAddNestedObject(::onc::cellular::kSIMLockStatus
,
333 *shill_sim_lock_status
);
335 const base::ListValue
* shill_apns
= NULL
;
336 if (shill_dictionary_
->GetListWithoutPathExpansion(
337 shill::kCellularApnListProperty
, &shill_apns
)) {
338 TranslateAndAddListOfObjects(::onc::cellular::kAPNList
, *shill_apns
);
340 const base::ListValue
* shill_found_networks
= NULL
;
341 if (shill_dictionary_
->GetListWithoutPathExpansion(
342 shill::kFoundNetworksProperty
, &shill_found_networks
)) {
343 TranslateAndAddListOfObjects(::onc::cellular::kFoundNetworks
,
344 *shill_found_networks
);
348 void ShillToONCTranslator::TranslateNetworkWithState() {
349 CopyPropertiesAccordingToSignature();
351 std::string shill_network_type
;
352 shill_dictionary_
->GetStringWithoutPathExpansion(shill::kTypeProperty
,
353 &shill_network_type
);
354 std::string onc_network_type
= ::onc::network_type::kEthernet
;
355 if (shill_network_type
!= shill::kTypeEthernet
&&
356 shill_network_type
!= shill::kTypeEthernetEap
) {
357 TranslateStringToONC(
358 kNetworkTypeTable
, shill_network_type
, &onc_network_type
);
360 // Translate nested Cellular, WiFi, etc. properties.
361 if (!onc_network_type
.empty()) {
362 onc_object_
->SetStringWithoutPathExpansion(::onc::network_config::kType
,
364 TranslateAndAddNestedObject(onc_network_type
);
367 // Since Name is a read only field in Shill unless it's a VPN, it is copied
368 // here, but not when going the other direction (if it's not a VPN).
370 shill_dictionary_
->GetStringWithoutPathExpansion(shill::kNameProperty
,
372 onc_object_
->SetStringWithoutPathExpansion(::onc::network_config::kName
,
375 // Limit ONC state to "NotConnected", "Connected", or "Connecting".
377 if (shill_dictionary_
->GetStringWithoutPathExpansion(shill::kStateProperty
,
379 std::string onc_state
= ::onc::connection_state::kNotConnected
;
380 if (NetworkState::StateIsConnected(state
)) {
381 onc_state
= ::onc::connection_state::kConnected
;
382 } else if (NetworkState::StateIsConnecting(state
)) {
383 onc_state
= ::onc::connection_state::kConnecting
;
385 onc_object_
->SetStringWithoutPathExpansion(
386 ::onc::network_config::kConnectionState
, onc_state
);
389 // Use a human-readable aa:bb format for any hardware MAC address. Note:
390 // this property is provided by the caller but is not part of the Shill
391 // Service properties (it is copied from the Device properties).
393 if (shill_dictionary_
->GetStringWithoutPathExpansion(shill::kAddressProperty
,
395 onc_object_
->SetStringWithoutPathExpansion(
396 ::onc::network_config::kMacAddress
,
397 network_util::FormattedMacAddress(address
));
400 // Shill's Service has an IPConfig property (note the singular), not an
401 // IPConfigs property. However, we require the caller of the translation to
402 // patch the Shill dictionary before passing it to the translator.
403 const base::ListValue
* shill_ipconfigs
= NULL
;
404 if (shill_dictionary_
->GetListWithoutPathExpansion(shill::kIPConfigsProperty
,
406 TranslateAndAddListOfObjects(::onc::network_config::kIPConfigs
,
411 void ShillToONCTranslator::TranslateIPConfig() {
412 CopyPropertiesAccordingToSignature();
413 std::string shill_ip_method
;
414 shill_dictionary_
->GetStringWithoutPathExpansion(shill::kMethodProperty
,
417 if (shill_ip_method
== shill::kTypeIPv4
||
418 shill_ip_method
== shill::kTypeDHCP
) {
419 type
= ::onc::ipconfig::kIPv4
;
420 } else if (shill_ip_method
== shill::kTypeIPv6
||
421 shill_ip_method
== shill::kTypeDHCP6
) {
422 type
= ::onc::ipconfig::kIPv6
;
424 return; // Ignore unhandled IPConfig types, e.g. bootp, zeroconf, ppp
427 onc_object_
->SetStringWithoutPathExpansion(::onc::ipconfig::kType
, type
);
430 void ShillToONCTranslator::TranslateAndAddNestedObject(
431 const std::string
& onc_field_name
) {
432 TranslateAndAddNestedObject(onc_field_name
, *shill_dictionary_
);
435 void ShillToONCTranslator::TranslateAndAddNestedObject(
436 const std::string
& onc_field_name
,
437 const base::DictionaryValue
& dictionary
) {
438 const OncFieldSignature
* field_signature
=
439 GetFieldSignature(*onc_signature_
, onc_field_name
);
440 if (!field_signature
) {
441 NOTREACHED() << "Unable to find signature for field: " << onc_field_name
;
444 ShillToONCTranslator
nested_translator(dictionary
,
445 *field_signature
->value_signature
);
446 scoped_ptr
<base::DictionaryValue
> nested_object
=
447 nested_translator
.CreateTranslatedONCObject();
448 if (nested_object
->empty())
450 onc_object_
->SetWithoutPathExpansion(onc_field_name
, nested_object
.release());
453 void ShillToONCTranslator::SetNestedOncValue(
454 const std::string
& onc_dictionary_name
,
455 const std::string
& onc_field_name
,
456 const base::Value
& value
) {
457 base::DictionaryValue
* nested
;
458 if (!onc_object_
->GetDictionaryWithoutPathExpansion(
459 onc_dictionary_name
, &nested
)) {
460 nested
= new base::DictionaryValue
;
461 onc_object_
->SetWithoutPathExpansion(onc_dictionary_name
, nested
);
463 nested
->SetWithoutPathExpansion(onc_field_name
, value
.DeepCopy());
466 void ShillToONCTranslator::TranslateAndAddListOfObjects(
467 const std::string
& onc_field_name
,
468 const base::ListValue
& list
) {
469 const OncFieldSignature
* field_signature
=
470 GetFieldSignature(*onc_signature_
, onc_field_name
);
471 if (field_signature
->value_signature
->onc_type
!= base::Value::TYPE_LIST
) {
472 LOG(ERROR
) << "ONC Field name: '" << onc_field_name
<< "' has type '"
473 << field_signature
->value_signature
->onc_type
474 << "', expected: base::Value::TYPE_LIST: " << GetName();
477 DCHECK(field_signature
->value_signature
->onc_array_entry_signature
);
478 scoped_ptr
<base::ListValue
> result(new base::ListValue());
479 for (base::ListValue::const_iterator it
= list
.begin();
480 it
!= list
.end(); ++it
) {
481 const base::DictionaryValue
* shill_value
= NULL
;
482 if (!(*it
)->GetAsDictionary(&shill_value
))
484 ShillToONCTranslator
nested_translator(
486 *field_signature
->value_signature
->onc_array_entry_signature
);
487 scoped_ptr
<base::DictionaryValue
> nested_object
=
488 nested_translator
.CreateTranslatedONCObject();
489 // If the nested object couldn't be parsed, simply omit it.
490 if (nested_object
->empty())
492 result
->Append(nested_object
.release());
494 // If there are no entries in the list, there is no need to expose this field.
497 onc_object_
->SetWithoutPathExpansion(onc_field_name
, result
.release());
500 void ShillToONCTranslator::CopyPropertiesAccordingToSignature() {
501 CopyPropertiesAccordingToSignature(onc_signature_
);
504 void ShillToONCTranslator::CopyPropertiesAccordingToSignature(
505 const OncValueSignature
* value_signature
) {
506 if (value_signature
->base_signature
)
507 CopyPropertiesAccordingToSignature(value_signature
->base_signature
);
508 for (const OncFieldSignature
* field_signature
= value_signature
->fields
;
509 field_signature
->onc_field_name
!= NULL
; ++field_signature
) {
510 CopyProperty(field_signature
);
514 void ShillToONCTranslator::CopyProperty(
515 const OncFieldSignature
* field_signature
) {
516 std::string shill_property_name
;
517 const base::Value
* shill_value
= NULL
;
518 if (!field_translation_table_
||
519 !GetShillPropertyName(field_signature
->onc_field_name
,
520 field_translation_table_
,
521 &shill_property_name
) ||
522 !shill_dictionary_
->GetWithoutPathExpansion(shill_property_name
,
527 if (shill_value
->GetType() != field_signature
->value_signature
->onc_type
) {
528 LOG(ERROR
) << "Shill property '" << shill_property_name
529 << "' with value " << *shill_value
530 << " has base::Value::Type " << shill_value
->GetType()
531 << " but ONC field '" << field_signature
->onc_field_name
532 << "' requires type "
533 << field_signature
->value_signature
->onc_type
534 << ": " << GetName();
538 onc_object_
->SetWithoutPathExpansion(field_signature
->onc_field_name
,
539 shill_value
->DeepCopy());
542 void ShillToONCTranslator::TranslateWithTableAndSet(
543 const std::string
& shill_property_name
,
544 const StringTranslationEntry table
[],
545 const std::string
& onc_field_name
) {
546 std::string shill_value
;
547 if (!shill_dictionary_
->GetStringWithoutPathExpansion(shill_property_name
,
551 std::string onc_value
;
552 if (TranslateStringToONC(table
, shill_value
, &onc_value
)) {
553 onc_object_
->SetStringWithoutPathExpansion(onc_field_name
, onc_value
);
556 LOG(ERROR
) << "Shill property '" << shill_property_name
<< "' with value "
557 << shill_value
<< " couldn't be translated to ONC: " << GetName();
560 std::string
ShillToONCTranslator::GetName() {
561 DCHECK(shill_dictionary_
);
563 shill_dictionary_
->GetStringWithoutPathExpansion(shill::kNameProperty
, &name
);
569 scoped_ptr
<base::DictionaryValue
> TranslateShillServiceToONCPart(
570 const base::DictionaryValue
& shill_dictionary
,
571 const OncValueSignature
* onc_signature
) {
572 CHECK(onc_signature
!= NULL
);
574 ShillToONCTranslator
translator(shill_dictionary
, *onc_signature
);
575 return translator
.CreateTranslatedONCObject();
579 } // namespace chromeos