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/onc/onc_signature.h"
16 #include "chromeos/network/onc/onc_translation_tables.h"
17 #include "chromeos/network/shill_property_util.h"
18 #include "components/onc/onc_constants.h"
19 #include "third_party/cros_system_api/dbus/service_constants.h"
26 // Converts |str| to a base::Value of the given |type|. If the conversion fails,
28 scoped_ptr
<base::Value
> ConvertStringToValue(const std::string
& str
,
29 base::Value::Type type
) {
31 if (type
== base::Value::TYPE_STRING
) {
32 value
= base::Value::CreateStringValue(str
);
34 value
= base::JSONReader::Read(str
);
37 if (value
== NULL
|| value
->GetType() != type
) {
41 return make_scoped_ptr(value
);
44 // This class implements the translation of properties from the given
45 // |shill_dictionary| to a new ONC object of signature |onc_signature|. Using
46 // recursive calls to CreateTranslatedONCObject of new instances, nested objects
48 class ShillToONCTranslator
{
50 ShillToONCTranslator(const base::DictionaryValue
& shill_dictionary
,
51 const OncValueSignature
& onc_signature
)
52 : shill_dictionary_(&shill_dictionary
),
53 onc_signature_(&onc_signature
) {
54 field_translation_table_
= GetFieldTranslationTable(onc_signature
);
57 // Translates the associated Shill dictionary and creates an ONC object of the
59 scoped_ptr
<base::DictionaryValue
> CreateTranslatedONCObject();
62 void TranslateEthernet();
63 void TranslateOpenVPN();
64 void TranslateIPsec();
66 void TranslateWiFiWithState();
67 void TranslateCellularWithState();
68 void TranslateNetworkWithState();
69 void TranslateIPConfig();
71 // Creates an ONC object from |dictionary| according to the signature
72 // associated to |onc_field_name| and adds it to |onc_object_| at
74 void TranslateAndAddNestedObject(const std::string
& onc_field_name
,
75 const base::DictionaryValue
& dictionary
);
77 // Creates an ONC object from |shill_dictionary_| according to the signature
78 // associated to |onc_field_name| and adds it to |onc_object_| at
80 void TranslateAndAddNestedObject(const std::string
& onc_field_name
);
82 // Translates a list of nested objects and adds the list to |onc_object_| at
83 // |onc_field_name|. If there are errors while parsing individual objects or
84 // if the resulting list contains no entries, the result will not be added to
86 void TranslateAndAddListOfObjects(const std::string
& onc_field_name
,
87 const base::ListValue
& list
);
89 // Applies function CopyProperty to each field of |value_signature| and its
91 void CopyPropertiesAccordingToSignature(
92 const OncValueSignature
* value_signature
);
94 // Applies function CopyProperty to each field of |onc_signature_| and its
96 void CopyPropertiesAccordingToSignature();
98 // If |shill_property_name| is defined in |field_signature|, copies this
99 // entry from |shill_dictionary_| to |onc_object_| if it exists.
100 void CopyProperty(const OncFieldSignature
* field_signature
);
102 // If existent, translates the entry at |shill_property_name| in
103 // |shill_dictionary_| using |table|. It is an error if no matching table
104 // entry is found. Writes the result as entry at |onc_field_name| in
106 void TranslateWithTableAndSet(const std::string
& shill_property_name
,
107 const StringTranslationEntry table
[],
108 const std::string
& onc_field_name
);
110 const base::DictionaryValue
* shill_dictionary_
;
111 const OncValueSignature
* onc_signature_
;
112 const FieldTranslationEntry
* field_translation_table_
;
113 scoped_ptr
<base::DictionaryValue
> onc_object_
;
115 DISALLOW_COPY_AND_ASSIGN(ShillToONCTranslator
);
118 scoped_ptr
<base::DictionaryValue
>
119 ShillToONCTranslator::CreateTranslatedONCObject() {
120 onc_object_
.reset(new base::DictionaryValue
);
121 if (onc_signature_
== &kNetworkWithStateSignature
) {
122 TranslateNetworkWithState();
123 } else if (onc_signature_
== &kEthernetSignature
) {
125 } else if (onc_signature_
== &kVPNSignature
) {
127 } else if (onc_signature_
== &kOpenVPNSignature
) {
129 } else if (onc_signature_
== &kIPsecSignature
) {
131 } else if (onc_signature_
== &kWiFiWithStateSignature
) {
132 TranslateWiFiWithState();
133 } else if (onc_signature_
== &kCellularWithStateSignature
) {
134 TranslateCellularWithState();
135 } else if (onc_signature_
== &kIPConfigSignature
) {
138 CopyPropertiesAccordingToSignature();
140 return onc_object_
.Pass();
143 void ShillToONCTranslator::TranslateEthernet() {
144 std::string shill_network_type
;
145 shill_dictionary_
->GetStringWithoutPathExpansion(shill::kTypeProperty
,
146 &shill_network_type
);
147 const char* onc_auth
= ::onc::ethernet::kNone
;
148 if (shill_network_type
== shill::kTypeEthernetEap
)
149 onc_auth
= ::onc::ethernet::k8021X
;
150 onc_object_
->SetStringWithoutPathExpansion(::onc::ethernet::kAuthentication
,
154 void ShillToONCTranslator::TranslateOpenVPN() {
155 if (shill_dictionary_
->HasKey(shill::kOpenVPNVerifyX509NameProperty
))
156 TranslateAndAddNestedObject(::onc::openvpn::kVerifyX509
);
158 // Shill supports only one RemoteCertKU but ONC requires a list. If existing,
159 // wraps the value into a list.
161 if (shill_dictionary_
->GetStringWithoutPathExpansion(
162 shill::kOpenVPNRemoteCertKUProperty
, &certKU
)) {
163 scoped_ptr
<base::ListValue
> certKUs(new base::ListValue
);
164 certKUs
->AppendString(certKU
);
165 onc_object_
->SetWithoutPathExpansion(::onc::openvpn::kRemoteCertKU
,
169 for (const OncFieldSignature
* field_signature
= onc_signature_
->fields
;
170 field_signature
->onc_field_name
!= NULL
; ++field_signature
) {
171 const std::string
& onc_field_name
= field_signature
->onc_field_name
;
172 if (onc_field_name
== ::onc::vpn::kSaveCredentials
||
173 onc_field_name
== ::onc::openvpn::kRemoteCertKU
||
174 onc_field_name
== ::onc::openvpn::kServerCAPEMs
) {
175 CopyProperty(field_signature
);
179 std::string shill_property_name
;
180 const base::Value
* shill_value
= NULL
;
181 if (!field_translation_table_
||
182 !GetShillPropertyName(field_signature
->onc_field_name
,
183 field_translation_table_
,
184 &shill_property_name
) ||
185 !shill_dictionary_
->GetWithoutPathExpansion(shill_property_name
,
190 scoped_ptr
<base::Value
> translated
;
191 std::string shill_str
;
192 if (shill_value
->GetAsString(&shill_str
)) {
193 // Shill wants all Provider/VPN fields to be strings. Translates these
194 // strings back to the correct ONC type.
195 translated
= ConvertStringToValue(
197 field_signature
->value_signature
->onc_type
);
199 if (translated
.get() == NULL
) {
200 LOG(ERROR
) << "Shill property '" << shill_property_name
201 << "' with value " << *shill_value
202 << " couldn't be converted to base::Value::Type "
203 << field_signature
->value_signature
->onc_type
;
205 onc_object_
->SetWithoutPathExpansion(onc_field_name
,
206 translated
.release());
209 LOG(ERROR
) << "Shill property '" << shill_property_name
210 << "' has value " << *shill_value
211 << ", but expected a string";
216 void ShillToONCTranslator::TranslateIPsec() {
217 CopyPropertiesAccordingToSignature();
218 if (shill_dictionary_
->HasKey(shill::kL2tpIpsecXauthUserProperty
))
219 TranslateAndAddNestedObject(::onc::ipsec::kXAUTH
);
222 void ShillToONCTranslator::TranslateVPN() {
223 TranslateWithTableAndSet(
224 shill::kProviderTypeProperty
, kVPNTypeTable
, ::onc::vpn::kType
);
225 CopyPropertiesAccordingToSignature();
227 std::string vpn_type
;
228 if (onc_object_
->GetStringWithoutPathExpansion(::onc::vpn::kType
,
230 if (vpn_type
== ::onc::vpn::kTypeL2TP_IPsec
) {
231 TranslateAndAddNestedObject(::onc::vpn::kIPsec
);
232 TranslateAndAddNestedObject(::onc::vpn::kL2TP
);
234 TranslateAndAddNestedObject(vpn_type
);
239 void ShillToONCTranslator::TranslateWiFiWithState() {
240 TranslateWithTableAndSet(
241 shill::kSecurityProperty
, kWiFiSecurityTable
, ::onc::wifi::kSecurity
);
242 std::string ssid
= shill_property_util::GetSSIDFromProperties(
243 *shill_dictionary_
, NULL
/* ignore unknown encoding */);
245 onc_object_
->SetStringWithoutPathExpansion(::onc::wifi::kSSID
, ssid
);
246 CopyPropertiesAccordingToSignature();
249 void ShillToONCTranslator::TranslateCellularWithState() {
250 CopyPropertiesAccordingToSignature();
251 const base::DictionaryValue
* dictionary
= NULL
;
252 if (shill_dictionary_
->GetDictionaryWithoutPathExpansion(
253 shill::kServingOperatorProperty
, &dictionary
)) {
254 TranslateAndAddNestedObject(::onc::cellular::kServingOperator
, *dictionary
);
256 if (shill_dictionary_
->GetDictionaryWithoutPathExpansion(
257 shill::kCellularApnProperty
, &dictionary
)) {
258 TranslateAndAddNestedObject(::onc::cellular::kAPN
, *dictionary
);
260 const base::ListValue
* shill_apns
= NULL
;
261 if (shill_dictionary_
->GetListWithoutPathExpansion(
262 shill::kCellularApnListProperty
, &shill_apns
)) {
263 TranslateAndAddListOfObjects(::onc::cellular::kAPNList
, *shill_apns
);
267 void ShillToONCTranslator::TranslateNetworkWithState() {
268 CopyPropertiesAccordingToSignature();
270 std::string shill_network_type
;
271 shill_dictionary_
->GetStringWithoutPathExpansion(shill::kTypeProperty
,
272 &shill_network_type
);
273 std::string onc_network_type
= ::onc::network_type::kEthernet
;
274 if (shill_network_type
!= shill::kTypeEthernet
&&
275 shill_network_type
!= shill::kTypeEthernetEap
) {
276 TranslateStringToONC(
277 kNetworkTypeTable
, shill_network_type
, &onc_network_type
);
279 if (!onc_network_type
.empty()) {
280 onc_object_
->SetStringWithoutPathExpansion(::onc::network_config::kType
,
282 TranslateAndAddNestedObject(onc_network_type
);
285 // Since Name is a read only field in Shill unless it's a VPN, it is copied
286 // here, but not when going the other direction (if it's not a VPN).
288 shill_dictionary_
->GetStringWithoutPathExpansion(shill::kNameProperty
,
290 onc_object_
->SetStringWithoutPathExpansion(::onc::network_config::kName
,
294 if (shill_dictionary_
->GetStringWithoutPathExpansion(shill::kStateProperty
,
296 std::string onc_state
= ::onc::connection_state::kNotConnected
;
297 if (NetworkState::StateIsConnected(state
)) {
298 onc_state
= ::onc::connection_state::kConnected
;
299 } else if (NetworkState::StateIsConnecting(state
)) {
300 onc_state
= ::onc::connection_state::kConnecting
;
302 onc_object_
->SetStringWithoutPathExpansion(
303 ::onc::network_config::kConnectionState
, onc_state
);
306 // Shill's Service has an IPConfig property (note the singular, and not a
307 // IPConfigs property). However, we require the caller of the translation to
308 // patch the Shill dictionary before passing it to the translator.
309 const base::ListValue
* shill_ipconfigs
= NULL
;
310 if (shill_dictionary_
->GetListWithoutPathExpansion(shill::kIPConfigsProperty
,
312 TranslateAndAddListOfObjects(::onc::network_config::kIPConfigs
,
317 void ShillToONCTranslator::TranslateIPConfig() {
318 CopyPropertiesAccordingToSignature();
319 std::string shill_ip_method
;
320 shill_dictionary_
->GetStringWithoutPathExpansion(shill::kMethodProperty
,
322 if (shill_ip_method
!= shill::kTypeIPv4
&&
323 shill_ip_method
!= shill::kTypeIPv6
) {
324 LOG(ERROR
) << "Unhandled IPConfig Method value " << shill_ip_method
;
328 std::string type
= ::onc::ipconfig::kIPv4
;
329 if (shill_ip_method
== shill::kTypeIPv6
)
330 type
= ::onc::ipconfig::kIPv6
;
331 onc_object_
->SetStringWithoutPathExpansion(::onc::ipconfig::kType
, type
);
334 void ShillToONCTranslator::TranslateAndAddNestedObject(
335 const std::string
& onc_field_name
) {
336 TranslateAndAddNestedObject(onc_field_name
, *shill_dictionary_
);
339 void ShillToONCTranslator::TranslateAndAddNestedObject(
340 const std::string
& onc_field_name
,
341 const base::DictionaryValue
& dictionary
) {
342 const OncFieldSignature
* field_signature
=
343 GetFieldSignature(*onc_signature_
, onc_field_name
);
344 DCHECK(field_signature
) << "Unable to find signature for field "
345 << onc_field_name
<< ".";
346 ShillToONCTranslator
nested_translator(dictionary
,
347 *field_signature
->value_signature
);
348 scoped_ptr
<base::DictionaryValue
> nested_object
=
349 nested_translator
.CreateTranslatedONCObject();
350 if (nested_object
->empty())
352 onc_object_
->SetWithoutPathExpansion(onc_field_name
, nested_object
.release());
355 void ShillToONCTranslator::TranslateAndAddListOfObjects(
356 const std::string
& onc_field_name
,
357 const base::ListValue
& list
) {
358 const OncFieldSignature
* field_signature
=
359 GetFieldSignature(*onc_signature_
, onc_field_name
);
360 if (field_signature
->value_signature
->onc_type
!= base::Value::TYPE_LIST
) {
361 LOG(ERROR
) << "ONC Field name: '" << onc_field_name
<< "' has type '"
362 << field_signature
->value_signature
->onc_type
363 << "', expected: base::Value::TYPE_LIST.";
366 DCHECK(field_signature
->value_signature
->onc_array_entry_signature
);
367 scoped_ptr
<base::ListValue
> result(new base::ListValue());
368 for (base::ListValue::const_iterator it
= list
.begin();
369 it
!= list
.end(); ++it
) {
370 const base::DictionaryValue
* shill_value
= NULL
;
371 if (!(*it
)->GetAsDictionary(&shill_value
))
373 ShillToONCTranslator
nested_translator(
375 *field_signature
->value_signature
->onc_array_entry_signature
);
376 scoped_ptr
<base::DictionaryValue
> nested_object
=
377 nested_translator
.CreateTranslatedONCObject();
378 // If the nested object couldn't be parsed, simply omit it.
379 if (nested_object
->empty())
381 result
->Append(nested_object
.release());
383 // If there are no entries in the list, there is no need to expose this field.
386 onc_object_
->SetWithoutPathExpansion(onc_field_name
, result
.release());
389 void ShillToONCTranslator::CopyPropertiesAccordingToSignature() {
390 CopyPropertiesAccordingToSignature(onc_signature_
);
393 void ShillToONCTranslator::CopyPropertiesAccordingToSignature(
394 const OncValueSignature
* value_signature
) {
395 if (value_signature
->base_signature
)
396 CopyPropertiesAccordingToSignature(value_signature
->base_signature
);
397 for (const OncFieldSignature
* field_signature
= value_signature
->fields
;
398 field_signature
->onc_field_name
!= NULL
; ++field_signature
) {
399 CopyProperty(field_signature
);
403 void ShillToONCTranslator::CopyProperty(
404 const OncFieldSignature
* field_signature
) {
405 std::string shill_property_name
;
406 const base::Value
* shill_value
= NULL
;
407 if (!field_translation_table_
||
408 !GetShillPropertyName(field_signature
->onc_field_name
,
409 field_translation_table_
,
410 &shill_property_name
) ||
411 !shill_dictionary_
->GetWithoutPathExpansion(shill_property_name
,
416 if (shill_value
->GetType() != field_signature
->value_signature
->onc_type
) {
417 LOG(ERROR
) << "Shill property '" << shill_property_name
418 << "' with value " << *shill_value
419 << " has base::Value::Type " << shill_value
->GetType()
420 << " but ONC field '" << field_signature
->onc_field_name
421 << "' requires type "
422 << field_signature
->value_signature
->onc_type
<< ".";
426 onc_object_
->SetWithoutPathExpansion(field_signature
->onc_field_name
,
427 shill_value
->DeepCopy());
430 void ShillToONCTranslator::TranslateWithTableAndSet(
431 const std::string
& shill_property_name
,
432 const StringTranslationEntry table
[],
433 const std::string
& onc_field_name
) {
434 std::string shill_value
;
435 if (!shill_dictionary_
->GetStringWithoutPathExpansion(shill_property_name
,
439 std::string onc_value
;
440 if (TranslateStringToONC(table
, shill_value
, &onc_value
)) {
441 onc_object_
->SetStringWithoutPathExpansion(onc_field_name
, onc_value
);
444 LOG(ERROR
) << "Shill property '" << shill_property_name
<< "' with value "
445 << shill_value
<< " couldn't be translated to ONC";
450 scoped_ptr
<base::DictionaryValue
> TranslateShillServiceToONCPart(
451 const base::DictionaryValue
& shill_dictionary
,
452 const OncValueSignature
* onc_signature
) {
453 CHECK(onc_signature
!= NULL
);
455 ShillToONCTranslator
translator(shill_dictionary
, *onc_signature
);
456 return translator
.CreateTranslatedONCObject();
460 } // namespace chromeos