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
= base::Value::CreateStringValue(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 // Translates the associated Shill dictionary and creates an ONC object of the
60 scoped_ptr
<base::DictionaryValue
> CreateTranslatedONCObject();
63 void TranslateEthernet();
64 void TranslateOpenVPN();
65 void TranslateIPsec();
67 void TranslateWiFiWithState();
68 void TranslateCellularWithState();
69 void TranslateNetworkWithState();
70 void TranslateIPConfig();
72 // Creates an ONC object from |dictionary| according to the signature
73 // associated to |onc_field_name| and adds it to |onc_object_| at
75 void TranslateAndAddNestedObject(const std::string
& onc_field_name
,
76 const base::DictionaryValue
& dictionary
);
78 // Creates an ONC object from |shill_dictionary_| according to the signature
79 // associated to |onc_field_name| and adds it to |onc_object_| at
81 void TranslateAndAddNestedObject(const std::string
& onc_field_name
);
83 // Translates a list of nested objects and adds the list to |onc_object_| at
84 // |onc_field_name|. If there are errors while parsing individual objects or
85 // if the resulting list contains no entries, the result will not be added to
87 void TranslateAndAddListOfObjects(const std::string
& onc_field_name
,
88 const base::ListValue
& list
);
90 // Applies function CopyProperty to each field of |value_signature| and its
92 void CopyPropertiesAccordingToSignature(
93 const OncValueSignature
* value_signature
);
95 // Applies function CopyProperty to each field of |onc_signature_| and its
97 void CopyPropertiesAccordingToSignature();
99 // If |shill_property_name| is defined in |field_signature|, copies this
100 // entry from |shill_dictionary_| to |onc_object_| if it exists.
101 void CopyProperty(const OncFieldSignature
* field_signature
);
103 // If existent, translates the entry at |shill_property_name| in
104 // |shill_dictionary_| using |table|. It is an error if no matching table
105 // entry is found. Writes the result as entry at |onc_field_name| in
107 void TranslateWithTableAndSet(const std::string
& shill_property_name
,
108 const StringTranslationEntry table
[],
109 const std::string
& onc_field_name
);
111 const base::DictionaryValue
* shill_dictionary_
;
112 const OncValueSignature
* onc_signature_
;
113 const FieldTranslationEntry
* field_translation_table_
;
114 scoped_ptr
<base::DictionaryValue
> onc_object_
;
116 DISALLOW_COPY_AND_ASSIGN(ShillToONCTranslator
);
119 scoped_ptr
<base::DictionaryValue
>
120 ShillToONCTranslator::CreateTranslatedONCObject() {
121 onc_object_
.reset(new base::DictionaryValue
);
122 if (onc_signature_
== &kNetworkWithStateSignature
) {
123 TranslateNetworkWithState();
124 } else if (onc_signature_
== &kEthernetSignature
) {
126 } else if (onc_signature_
== &kVPNSignature
) {
128 } else if (onc_signature_
== &kOpenVPNSignature
) {
130 } else if (onc_signature_
== &kIPsecSignature
) {
132 } else if (onc_signature_
== &kWiFiWithStateSignature
) {
133 TranslateWiFiWithState();
134 } else if (onc_signature_
== &kCellularWithStateSignature
) {
135 TranslateCellularWithState();
136 } else if (onc_signature_
== &kIPConfigSignature
) {
139 CopyPropertiesAccordingToSignature();
141 return onc_object_
.Pass();
144 void ShillToONCTranslator::TranslateEthernet() {
145 std::string shill_network_type
;
146 shill_dictionary_
->GetStringWithoutPathExpansion(shill::kTypeProperty
,
147 &shill_network_type
);
148 const char* onc_auth
= ::onc::ethernet::kAuthenticationNone
;
149 if (shill_network_type
== shill::kTypeEthernetEap
)
150 onc_auth
= ::onc::ethernet::k8021X
;
151 onc_object_
->SetStringWithoutPathExpansion(::onc::ethernet::kAuthentication
,
155 void ShillToONCTranslator::TranslateOpenVPN() {
156 if (shill_dictionary_
->HasKey(shill::kOpenVPNVerifyX509NameProperty
))
157 TranslateAndAddNestedObject(::onc::openvpn::kVerifyX509
);
159 // Shill supports only one RemoteCertKU but ONC requires a list. If existing,
160 // wraps the value into a list.
162 if (shill_dictionary_
->GetStringWithoutPathExpansion(
163 shill::kOpenVPNRemoteCertKUProperty
, &certKU
)) {
164 scoped_ptr
<base::ListValue
> certKUs(new base::ListValue
);
165 certKUs
->AppendString(certKU
);
166 onc_object_
->SetWithoutPathExpansion(::onc::openvpn::kRemoteCertKU
,
170 for (const OncFieldSignature
* field_signature
= onc_signature_
->fields
;
171 field_signature
->onc_field_name
!= NULL
; ++field_signature
) {
172 const std::string
& onc_field_name
= field_signature
->onc_field_name
;
173 if (onc_field_name
== ::onc::vpn::kSaveCredentials
||
174 onc_field_name
== ::onc::openvpn::kRemoteCertKU
||
175 onc_field_name
== ::onc::openvpn::kServerCAPEMs
) {
176 CopyProperty(field_signature
);
180 std::string shill_property_name
;
181 const base::Value
* shill_value
= NULL
;
182 if (!field_translation_table_
||
183 !GetShillPropertyName(field_signature
->onc_field_name
,
184 field_translation_table_
,
185 &shill_property_name
) ||
186 !shill_dictionary_
->GetWithoutPathExpansion(shill_property_name
,
191 scoped_ptr
<base::Value
> translated
;
192 std::string shill_str
;
193 if (shill_value
->GetAsString(&shill_str
)) {
194 // Shill wants all Provider/VPN fields to be strings. Translates these
195 // strings back to the correct ONC type.
196 translated
= ConvertStringToValue(
198 field_signature
->value_signature
->onc_type
);
200 if (translated
.get() == NULL
) {
201 LOG(ERROR
) << "Shill property '" << shill_property_name
202 << "' with value " << *shill_value
203 << " couldn't be converted to base::Value::Type "
204 << field_signature
->value_signature
->onc_type
;
206 onc_object_
->SetWithoutPathExpansion(onc_field_name
,
207 translated
.release());
210 LOG(ERROR
) << "Shill property '" << shill_property_name
211 << "' has value " << *shill_value
212 << ", but expected a string";
217 void ShillToONCTranslator::TranslateIPsec() {
218 CopyPropertiesAccordingToSignature();
219 if (shill_dictionary_
->HasKey(shill::kL2tpIpsecXauthUserProperty
))
220 TranslateAndAddNestedObject(::onc::ipsec::kXAUTH
);
223 void ShillToONCTranslator::TranslateVPN() {
224 TranslateWithTableAndSet(
225 shill::kProviderTypeProperty
, kVPNTypeTable
, ::onc::vpn::kType
);
226 CopyPropertiesAccordingToSignature();
228 std::string vpn_type
;
229 if (onc_object_
->GetStringWithoutPathExpansion(::onc::vpn::kType
,
231 if (vpn_type
== ::onc::vpn::kTypeL2TP_IPsec
) {
232 TranslateAndAddNestedObject(::onc::vpn::kIPsec
);
233 TranslateAndAddNestedObject(::onc::vpn::kL2TP
);
235 TranslateAndAddNestedObject(vpn_type
);
240 void ShillToONCTranslator::TranslateWiFiWithState() {
241 TranslateWithTableAndSet(
242 shill::kSecurityProperty
, kWiFiSecurityTable
, ::onc::wifi::kSecurity
);
243 std::string ssid
= shill_property_util::GetSSIDFromProperties(
244 *shill_dictionary_
, NULL
/* ignore unknown encoding */);
246 onc_object_
->SetStringWithoutPathExpansion(::onc::wifi::kSSID
, ssid
);
247 CopyPropertiesAccordingToSignature();
250 void ShillToONCTranslator::TranslateCellularWithState() {
251 CopyPropertiesAccordingToSignature();
252 const base::DictionaryValue
* dictionary
= NULL
;
253 if (shill_dictionary_
->GetDictionaryWithoutPathExpansion(
254 shill::kServingOperatorProperty
, &dictionary
)) {
255 TranslateAndAddNestedObject(::onc::cellular::kServingOperator
, *dictionary
);
257 if (shill_dictionary_
->GetDictionaryWithoutPathExpansion(
258 shill::kCellularApnProperty
, &dictionary
)) {
259 TranslateAndAddNestedObject(::onc::cellular::kAPN
, *dictionary
);
261 const base::ListValue
* shill_apns
= NULL
;
262 if (shill_dictionary_
->GetListWithoutPathExpansion(
263 shill::kCellularApnListProperty
, &shill_apns
)) {
264 TranslateAndAddListOfObjects(::onc::cellular::kAPNList
, *shill_apns
);
267 const base::DictionaryValue
* device_dictionary
= NULL
;
268 if (!shill_dictionary_
->GetDictionaryWithoutPathExpansion(
269 shill::kDeviceProperty
, &device_dictionary
)) {
273 // Iterate through all fields of the CellularWithState signature and copy
274 // values from the device properties according to the separate
275 // CellularDeviceTable.
276 for (const OncFieldSignature
* field_signature
= onc_signature_
->fields
;
277 field_signature
->onc_field_name
!= NULL
; ++field_signature
) {
278 const std::string
& onc_field_name
= field_signature
->onc_field_name
;
280 std::string shill_property_name
;
281 const base::Value
* shill_value
= NULL
;
282 if (!GetShillPropertyName(field_signature
->onc_field_name
,
283 kCellularDeviceTable
,
284 &shill_property_name
) ||
285 !device_dictionary
->GetWithoutPathExpansion(shill_property_name
,
289 onc_object_
->SetWithoutPathExpansion(onc_field_name
,
290 shill_value
->DeepCopy());
294 void ShillToONCTranslator::TranslateNetworkWithState() {
295 CopyPropertiesAccordingToSignature();
297 std::string shill_network_type
;
298 shill_dictionary_
->GetStringWithoutPathExpansion(shill::kTypeProperty
,
299 &shill_network_type
);
300 std::string onc_network_type
= ::onc::network_type::kEthernet
;
301 if (shill_network_type
!= shill::kTypeEthernet
&&
302 shill_network_type
!= shill::kTypeEthernetEap
) {
303 TranslateStringToONC(
304 kNetworkTypeTable
, shill_network_type
, &onc_network_type
);
306 // Translate nested Cellular, WiFi, etc. properties.
307 if (!onc_network_type
.empty()) {
308 onc_object_
->SetStringWithoutPathExpansion(::onc::network_config::kType
,
310 TranslateAndAddNestedObject(onc_network_type
);
313 // Since Name is a read only field in Shill unless it's a VPN, it is copied
314 // here, but not when going the other direction (if it's not a VPN).
316 shill_dictionary_
->GetStringWithoutPathExpansion(shill::kNameProperty
,
318 onc_object_
->SetStringWithoutPathExpansion(::onc::network_config::kName
,
321 // Limit ONC state to "NotConnected", "Connected", or "Connecting".
323 if (shill_dictionary_
->GetStringWithoutPathExpansion(shill::kStateProperty
,
325 std::string onc_state
= ::onc::connection_state::kNotConnected
;
326 if (NetworkState::StateIsConnected(state
)) {
327 onc_state
= ::onc::connection_state::kConnected
;
328 } else if (NetworkState::StateIsConnecting(state
)) {
329 onc_state
= ::onc::connection_state::kConnecting
;
331 onc_object_
->SetStringWithoutPathExpansion(
332 ::onc::network_config::kConnectionState
, onc_state
);
335 // Use a human-readable aa:bb format for any hardware MAC address. Note:
336 // this property is provided by the caller but is not part of the Shill
337 // Service properties (it is copied from the Device properties).
339 if (shill_dictionary_
->GetStringWithoutPathExpansion(shill::kAddressProperty
,
341 onc_object_
->SetStringWithoutPathExpansion(
342 ::onc::network_config::kMacAddress
,
343 network_util::FormattedMacAddress(address
));
346 // Shill's Service has an IPConfig property (note the singular), not an
347 // IPConfigs property. However, we require the caller of the translation to
348 // patch the Shill dictionary before passing it to the translator.
349 const base::ListValue
* shill_ipconfigs
= NULL
;
350 if (shill_dictionary_
->GetListWithoutPathExpansion(shill::kIPConfigsProperty
,
352 TranslateAndAddListOfObjects(::onc::network_config::kIPConfigs
,
357 void ShillToONCTranslator::TranslateIPConfig() {
358 CopyPropertiesAccordingToSignature();
359 std::string shill_ip_method
;
360 shill_dictionary_
->GetStringWithoutPathExpansion(shill::kMethodProperty
,
363 if (shill_ip_method
== shill::kTypeIPv4
||
364 shill_ip_method
== shill::kTypeDHCP
) {
365 type
= ::onc::ipconfig::kIPv4
;
366 } else if (shill_ip_method
== shill::kTypeIPv6
||
367 shill_ip_method
== shill::kTypeDHCP6
) {
368 type
= ::onc::ipconfig::kIPv6
;
370 return; // Ignore unhandled IPConfig types, e.g. bootp, zeroconf, ppp
373 onc_object_
->SetStringWithoutPathExpansion(::onc::ipconfig::kType
, type
);
376 void ShillToONCTranslator::TranslateAndAddNestedObject(
377 const std::string
& onc_field_name
) {
378 TranslateAndAddNestedObject(onc_field_name
, *shill_dictionary_
);
381 void ShillToONCTranslator::TranslateAndAddNestedObject(
382 const std::string
& onc_field_name
,
383 const base::DictionaryValue
& dictionary
) {
384 const OncFieldSignature
* field_signature
=
385 GetFieldSignature(*onc_signature_
, onc_field_name
);
386 DCHECK(field_signature
) << "Unable to find signature for field "
387 << onc_field_name
<< ".";
388 ShillToONCTranslator
nested_translator(dictionary
,
389 *field_signature
->value_signature
);
390 scoped_ptr
<base::DictionaryValue
> nested_object
=
391 nested_translator
.CreateTranslatedONCObject();
392 if (nested_object
->empty())
394 onc_object_
->SetWithoutPathExpansion(onc_field_name
, nested_object
.release());
397 void ShillToONCTranslator::TranslateAndAddListOfObjects(
398 const std::string
& onc_field_name
,
399 const base::ListValue
& list
) {
400 const OncFieldSignature
* field_signature
=
401 GetFieldSignature(*onc_signature_
, onc_field_name
);
402 if (field_signature
->value_signature
->onc_type
!= base::Value::TYPE_LIST
) {
403 LOG(ERROR
) << "ONC Field name: '" << onc_field_name
<< "' has type '"
404 << field_signature
->value_signature
->onc_type
405 << "', expected: base::Value::TYPE_LIST.";
408 DCHECK(field_signature
->value_signature
->onc_array_entry_signature
);
409 scoped_ptr
<base::ListValue
> result(new base::ListValue());
410 for (base::ListValue::const_iterator it
= list
.begin();
411 it
!= list
.end(); ++it
) {
412 const base::DictionaryValue
* shill_value
= NULL
;
413 if (!(*it
)->GetAsDictionary(&shill_value
))
415 ShillToONCTranslator
nested_translator(
417 *field_signature
->value_signature
->onc_array_entry_signature
);
418 scoped_ptr
<base::DictionaryValue
> nested_object
=
419 nested_translator
.CreateTranslatedONCObject();
420 // If the nested object couldn't be parsed, simply omit it.
421 if (nested_object
->empty())
423 result
->Append(nested_object
.release());
425 // If there are no entries in the list, there is no need to expose this field.
428 onc_object_
->SetWithoutPathExpansion(onc_field_name
, result
.release());
431 void ShillToONCTranslator::CopyPropertiesAccordingToSignature() {
432 CopyPropertiesAccordingToSignature(onc_signature_
);
435 void ShillToONCTranslator::CopyPropertiesAccordingToSignature(
436 const OncValueSignature
* value_signature
) {
437 if (value_signature
->base_signature
)
438 CopyPropertiesAccordingToSignature(value_signature
->base_signature
);
439 for (const OncFieldSignature
* field_signature
= value_signature
->fields
;
440 field_signature
->onc_field_name
!= NULL
; ++field_signature
) {
441 CopyProperty(field_signature
);
445 void ShillToONCTranslator::CopyProperty(
446 const OncFieldSignature
* field_signature
) {
447 std::string shill_property_name
;
448 const base::Value
* shill_value
= NULL
;
449 if (!field_translation_table_
||
450 !GetShillPropertyName(field_signature
->onc_field_name
,
451 field_translation_table_
,
452 &shill_property_name
) ||
453 !shill_dictionary_
->GetWithoutPathExpansion(shill_property_name
,
458 if (shill_value
->GetType() != field_signature
->value_signature
->onc_type
) {
459 LOG(ERROR
) << "Shill property '" << shill_property_name
460 << "' with value " << *shill_value
461 << " has base::Value::Type " << shill_value
->GetType()
462 << " but ONC field '" << field_signature
->onc_field_name
463 << "' requires type "
464 << field_signature
->value_signature
->onc_type
<< ".";
468 onc_object_
->SetWithoutPathExpansion(field_signature
->onc_field_name
,
469 shill_value
->DeepCopy());
472 void ShillToONCTranslator::TranslateWithTableAndSet(
473 const std::string
& shill_property_name
,
474 const StringTranslationEntry table
[],
475 const std::string
& onc_field_name
) {
476 std::string shill_value
;
477 if (!shill_dictionary_
->GetStringWithoutPathExpansion(shill_property_name
,
481 std::string onc_value
;
482 if (TranslateStringToONC(table
, shill_value
, &onc_value
)) {
483 onc_object_
->SetStringWithoutPathExpansion(onc_field_name
, onc_value
);
486 LOG(ERROR
) << "Shill property '" << shill_property_name
<< "' with value "
487 << shill_value
<< " couldn't be translated to ONC";
492 scoped_ptr
<base::DictionaryValue
> TranslateShillServiceToONCPart(
493 const base::DictionaryValue
& shill_dictionary
,
494 const OncValueSignature
* onc_signature
) {
495 CHECK(onc_signature
!= NULL
);
497 ShillToONCTranslator
translator(shill_dictionary
, *onc_signature
);
498 return translator
.CreateTranslatedONCObject();
502 } // namespace chromeos