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_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"
25 // Converts |str| to a base::Value of the given |type|. If the conversion fails,
27 scoped_ptr
<base::Value
> ConvertStringToValue(const std::string
& str
,
28 base::Value::Type type
) {
30 if (type
== base::Value::TYPE_STRING
) {
31 value
= base::Value::CreateStringValue(str
);
33 value
= base::JSONReader::Read(str
);
36 if (value
== NULL
|| value
->GetType() != type
) {
40 return make_scoped_ptr(value
);
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
47 class ShillToONCTranslator
{
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
);
56 // Translates the associated Shill dictionary and creates an ONC object of the
58 scoped_ptr
<base::DictionaryValue
> CreateTranslatedONCObject();
61 void TranslateOpenVPN();
63 void TranslateWiFiWithState();
64 void TranslateCellularWithState();
65 void TranslateNetworkWithState();
67 // Creates an ONC object from |dictionary| according to the signature
68 // associated to |onc_field_name| and adds it to |onc_object_| at
70 void TranslateAndAddNestedObject(const std::string
& onc_field_name
,
71 const base::DictionaryValue
& dictionary
);
73 // Creates an ONC object from |shill_dictionary_| according to the signature
74 // associated to |onc_field_name| and adds it to |onc_object_| at
76 void TranslateAndAddNestedObject(const std::string
& onc_field_name
);
78 // Applies function CopyProperty to each field of |value_signature| and its
80 void CopyPropertiesAccordingToSignature(
81 const OncValueSignature
* value_signature
);
83 // Applies function CopyProperty to each field of |onc_signature_| and its
85 void CopyPropertiesAccordingToSignature();
87 // If |shill_property_name| is defined in |field_signature|, copies this
88 // entry from |shill_dictionary_| to |onc_object_| if it exists.
89 void CopyProperty(const OncFieldSignature
* field_signature
);
91 // If existent, translates the entry at |shill_property_name| in
92 // |shill_dictionary_| using |table|. It is an error if no matching table
93 // entry is found. Writes the result as entry at |onc_field_name| in
95 void TranslateWithTableAndSet(const std::string
& shill_property_name
,
96 const StringTranslationEntry table
[],
97 const std::string
& onc_field_name
);
99 const base::DictionaryValue
* shill_dictionary_
;
100 const OncValueSignature
* onc_signature_
;
101 const FieldTranslationEntry
* field_translation_table_
;
102 scoped_ptr
<base::DictionaryValue
> onc_object_
;
104 DISALLOW_COPY_AND_ASSIGN(ShillToONCTranslator
);
107 scoped_ptr
<base::DictionaryValue
>
108 ShillToONCTranslator::CreateTranslatedONCObject() {
109 onc_object_
.reset(new base::DictionaryValue
);
110 if (onc_signature_
== &kNetworkWithStateSignature
) {
111 TranslateNetworkWithState();
112 } else if (onc_signature_
== &kVPNSignature
) {
114 } else if (onc_signature_
== &kOpenVPNSignature
) {
116 } else if (onc_signature_
== &kWiFiWithStateSignature
) {
117 TranslateWiFiWithState();
118 } else if (onc_signature_
== &kCellularWithStateSignature
) {
119 TranslateCellularWithState();
121 CopyPropertiesAccordingToSignature();
123 return onc_object_
.Pass();
126 void ShillToONCTranslator::TranslateOpenVPN() {
127 // Shill supports only one RemoteCertKU but ONC requires a list. If existing,
128 // wraps the value into a list.
130 if (shill_dictionary_
->GetStringWithoutPathExpansion(
131 flimflam::kOpenVPNRemoteCertKUProperty
, &certKU
)) {
132 scoped_ptr
<base::ListValue
> certKUs(new base::ListValue
);
133 certKUs
->AppendString(certKU
);
134 onc_object_
->SetWithoutPathExpansion(openvpn::kRemoteCertKU
,
138 for (const OncFieldSignature
* field_signature
= onc_signature_
->fields
;
139 field_signature
->onc_field_name
!= NULL
; ++field_signature
) {
140 const std::string
& onc_field_name
= field_signature
->onc_field_name
;
141 if (onc_field_name
== vpn::kSaveCredentials
||
142 onc_field_name
== openvpn::kRemoteCertKU
||
143 onc_field_name
== openvpn::kServerCAPEMs
) {
144 CopyProperty(field_signature
);
148 std::string shill_property_name
;
149 const base::Value
* shill_value
= NULL
;
150 if (!field_translation_table_
||
151 !GetShillPropertyName(field_signature
->onc_field_name
,
152 field_translation_table_
,
153 &shill_property_name
) ||
154 !shill_dictionary_
->GetWithoutPathExpansion(shill_property_name
,
159 scoped_ptr
<base::Value
> translated
;
160 std::string shill_str
;
161 if (shill_value
->GetAsString(&shill_str
)) {
162 // Shill wants all Provider/VPN fields to be strings. Translates these
163 // strings back to the correct ONC type.
164 translated
= ConvertStringToValue(
166 field_signature
->value_signature
->onc_type
);
168 if (translated
.get() == NULL
) {
169 LOG(ERROR
) << "Shill property '" << shill_property_name
170 << "' with value " << *shill_value
171 << " couldn't be converted to base::Value::Type "
172 << field_signature
->value_signature
->onc_type
;
174 onc_object_
->SetWithoutPathExpansion(onc_field_name
,
175 translated
.release());
178 LOG(ERROR
) << "Shill property '" << shill_property_name
179 << "' has value " << *shill_value
180 << ", but expected a string";
185 void ShillToONCTranslator::TranslateVPN() {
186 TranslateWithTableAndSet(flimflam::kProviderTypeProperty
, kVPNTypeTable
,
188 CopyPropertiesAccordingToSignature();
190 std::string vpn_type
;
191 if (onc_object_
->GetStringWithoutPathExpansion(vpn::kType
,
193 if (vpn_type
== vpn::kTypeL2TP_IPsec
) {
194 TranslateAndAddNestedObject(vpn::kIPsec
);
195 TranslateAndAddNestedObject(vpn::kL2TP
);
197 TranslateAndAddNestedObject(vpn_type
);
202 void ShillToONCTranslator::TranslateWiFiWithState() {
203 TranslateWithTableAndSet(flimflam::kSecurityProperty
, kWiFiSecurityTable
,
205 CopyPropertiesAccordingToSignature();
208 void ShillToONCTranslator::TranslateCellularWithState() {
209 CopyPropertiesAccordingToSignature();
210 const base::DictionaryValue
* serving_operator
= NULL
;
211 if (shill_dictionary_
->GetDictionaryWithoutPathExpansion(
212 flimflam::kServingOperatorProperty
, &serving_operator
)) {
213 TranslateAndAddNestedObject(cellular::kServingOperator
, *serving_operator
);
217 void ShillToONCTranslator::TranslateNetworkWithState() {
218 TranslateWithTableAndSet(flimflam::kTypeProperty
, kNetworkTypeTable
,
219 network_config::kType
);
220 CopyPropertiesAccordingToSignature();
222 std::string network_type
;
223 if (onc_object_
->GetStringWithoutPathExpansion(network_config::kType
,
225 TranslateAndAddNestedObject(network_type
);
228 // Since Name is a read only field in Shill unless it's a VPN, it is copied
229 // here, but not when going the other direction (if it's not a VPN).
231 shill_dictionary_
->GetStringWithoutPathExpansion(flimflam::kNameProperty
,
233 onc_object_
->SetStringWithoutPathExpansion(network_config::kName
, name
);
236 if (shill_dictionary_
->GetStringWithoutPathExpansion(flimflam::kStateProperty
,
238 std::string onc_state
= connection_state::kNotConnected
;
239 if (NetworkState::StateIsConnected(state
)) {
240 onc_state
= connection_state::kConnected
;
241 } else if (NetworkState::StateIsConnecting(state
)) {
242 onc_state
= connection_state::kConnecting
;
244 onc_object_
->SetStringWithoutPathExpansion(network_config::kConnectionState
,
249 void ShillToONCTranslator::TranslateAndAddNestedObject(
250 const std::string
& onc_field_name
) {
251 TranslateAndAddNestedObject(onc_field_name
, *shill_dictionary_
);
254 void ShillToONCTranslator::TranslateAndAddNestedObject(
255 const std::string
& onc_field_name
,
256 const base::DictionaryValue
& dictionary
) {
257 const OncFieldSignature
* field_signature
=
258 GetFieldSignature(*onc_signature_
, onc_field_name
);
259 ShillToONCTranslator
nested_translator(dictionary
,
260 *field_signature
->value_signature
);
261 scoped_ptr
<base::DictionaryValue
> nested_object
=
262 nested_translator
.CreateTranslatedONCObject();
263 if (nested_object
->empty())
265 onc_object_
->SetWithoutPathExpansion(onc_field_name
, nested_object
.release());
268 void ShillToONCTranslator::CopyPropertiesAccordingToSignature() {
269 CopyPropertiesAccordingToSignature(onc_signature_
);
272 void ShillToONCTranslator::CopyPropertiesAccordingToSignature(
273 const OncValueSignature
* value_signature
) {
274 if (value_signature
->base_signature
)
275 CopyPropertiesAccordingToSignature(value_signature
->base_signature
);
276 for (const OncFieldSignature
* field_signature
= value_signature
->fields
;
277 field_signature
->onc_field_name
!= NULL
; ++field_signature
) {
278 CopyProperty(field_signature
);
282 void ShillToONCTranslator::CopyProperty(
283 const OncFieldSignature
* field_signature
) {
284 std::string shill_property_name
;
285 const base::Value
* shill_value
= NULL
;
286 if (!field_translation_table_
||
287 !GetShillPropertyName(field_signature
->onc_field_name
,
288 field_translation_table_
,
289 &shill_property_name
) ||
290 !shill_dictionary_
->GetWithoutPathExpansion(shill_property_name
,
295 if (shill_value
->GetType() != field_signature
->value_signature
->onc_type
) {
296 LOG(ERROR
) << "Shill property '" << shill_property_name
297 << "' with value " << *shill_value
298 << " has base::Value::Type " << shill_value
->GetType()
299 << " but ONC field '" << field_signature
->onc_field_name
300 << "' requires type "
301 << field_signature
->value_signature
->onc_type
<< ".";
305 onc_object_
->SetWithoutPathExpansion(field_signature
->onc_field_name
,
306 shill_value
->DeepCopy());
309 void ShillToONCTranslator::TranslateWithTableAndSet(
310 const std::string
& shill_property_name
,
311 const StringTranslationEntry table
[],
312 const std::string
& onc_field_name
) {
313 std::string shill_value
;
314 if (!shill_dictionary_
->GetStringWithoutPathExpansion(shill_property_name
,
318 std::string onc_value
;
319 if (TranslateStringToONC(table
, shill_value
, &onc_value
)) {
320 onc_object_
->SetStringWithoutPathExpansion(onc_field_name
, onc_value
);
323 LOG(ERROR
) << "Shill property '" << shill_property_name
<< "' with value "
324 << shill_value
<< " couldn't be translated to ONC";
329 scoped_ptr
<base::DictionaryValue
> TranslateShillServiceToONCPart(
330 const base::DictionaryValue
& shill_dictionary
,
331 const OncValueSignature
* onc_signature
) {
332 CHECK(onc_signature
!= NULL
);
334 ShillToONCTranslator
translator(shill_dictionary
, *onc_signature
);
335 return translator
.CreateTranslatedONCObject();
339 } // namespace chromeos