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();
65 void TranslateWiFiWithState();
66 void TranslateCellularWithState();
67 void TranslateNetworkWithState();
69 // Creates an ONC object from |dictionary| according to the signature
70 // associated to |onc_field_name| and adds it to |onc_object_| at
72 void TranslateAndAddNestedObject(const std::string& onc_field_name,
73 const base::DictionaryValue& dictionary);
75 // Creates an ONC object from |shill_dictionary_| according to the signature
76 // associated to |onc_field_name| and adds it to |onc_object_| at
78 void TranslateAndAddNestedObject(const std::string& onc_field_name);
80 // Translates a list of nested objects and adds the list to |onc_object_| at
81 // |onc_field_name|. If there are errors while parsing individual objects or
82 // if the resulting list contains no entries, the result will not be added to
84 void TranslateAndAddListOfObjects(const std::string& onc_field_name,
85 const base::ListValue& list);
87 // Applies function CopyProperty to each field of |value_signature| and its
89 void CopyPropertiesAccordingToSignature(
90 const OncValueSignature* value_signature);
92 // Applies function CopyProperty to each field of |onc_signature_| and its
94 void CopyPropertiesAccordingToSignature();
96 // If |shill_property_name| is defined in |field_signature|, copies this
97 // entry from |shill_dictionary_| to |onc_object_| if it exists.
98 void CopyProperty(const OncFieldSignature* field_signature);
100 // If existent, translates the entry at |shill_property_name| in
101 // |shill_dictionary_| using |table|. It is an error if no matching table
102 // entry is found. Writes the result as entry at |onc_field_name| in
104 void TranslateWithTableAndSet(const std::string& shill_property_name,
105 const StringTranslationEntry table[],
106 const std::string& onc_field_name);
108 const base::DictionaryValue* shill_dictionary_;
109 const OncValueSignature* onc_signature_;
110 const FieldTranslationEntry* field_translation_table_;
111 scoped_ptr<base::DictionaryValue> onc_object_;
113 DISALLOW_COPY_AND_ASSIGN(ShillToONCTranslator);
116 scoped_ptr<base::DictionaryValue>
117 ShillToONCTranslator::CreateTranslatedONCObject() {
118 onc_object_.reset(new base::DictionaryValue);
119 if (onc_signature_ == &kNetworkWithStateSignature) {
120 TranslateNetworkWithState();
121 } else if (onc_signature_ == &kEthernetSignature) {
123 } else if (onc_signature_ == &kVPNSignature) {
125 } else if (onc_signature_ == &kOpenVPNSignature) {
127 } else if (onc_signature_ == &kWiFiWithStateSignature) {
128 TranslateWiFiWithState();
129 } else if (onc_signature_ == &kCellularWithStateSignature) {
130 TranslateCellularWithState();
132 CopyPropertiesAccordingToSignature();
134 return onc_object_.Pass();
137 void ShillToONCTranslator::TranslateEthernet() {
138 std::string shill_network_type;
139 shill_dictionary_->GetStringWithoutPathExpansion(shill::kTypeProperty,
140 &shill_network_type);
141 const char* onc_auth = ::onc::ethernet::kNone;
142 if (shill_network_type == shill::kTypeEthernetEap)
143 onc_auth = ::onc::ethernet::k8021X;
144 onc_object_->SetStringWithoutPathExpansion(::onc::ethernet::kAuthentication,
148 void ShillToONCTranslator::TranslateOpenVPN() {
149 // Shill supports only one RemoteCertKU but ONC requires a list. If existing,
150 // wraps the value into a list.
152 if (shill_dictionary_->GetStringWithoutPathExpansion(
153 shill::kOpenVPNRemoteCertKUProperty, &certKU)) {
154 scoped_ptr<base::ListValue> certKUs(new base::ListValue);
155 certKUs->AppendString(certKU);
156 onc_object_->SetWithoutPathExpansion(::onc::openvpn::kRemoteCertKU,
160 for (const OncFieldSignature* field_signature = onc_signature_->fields;
161 field_signature->onc_field_name != NULL; ++field_signature) {
162 const std::string& onc_field_name = field_signature->onc_field_name;
163 if (onc_field_name == ::onc::vpn::kSaveCredentials ||
164 onc_field_name == ::onc::openvpn::kRemoteCertKU ||
165 onc_field_name == ::onc::openvpn::kServerCAPEMs) {
166 CopyProperty(field_signature);
170 std::string shill_property_name;
171 const base::Value* shill_value = NULL;
172 if (!field_translation_table_ ||
173 !GetShillPropertyName(field_signature->onc_field_name,
174 field_translation_table_,
175 &shill_property_name) ||
176 !shill_dictionary_->GetWithoutPathExpansion(shill_property_name,
181 scoped_ptr<base::Value> translated;
182 std::string shill_str;
183 if (shill_value->GetAsString(&shill_str)) {
184 // Shill wants all Provider/VPN fields to be strings. Translates these
185 // strings back to the correct ONC type.
186 translated = ConvertStringToValue(
188 field_signature->value_signature->onc_type);
190 if (translated.get() == NULL) {
191 LOG(ERROR) << "Shill property '" << shill_property_name
192 << "' with value " << *shill_value
193 << " couldn't be converted to base::Value::Type "
194 << field_signature->value_signature->onc_type;
196 onc_object_->SetWithoutPathExpansion(onc_field_name,
197 translated.release());
200 LOG(ERROR) << "Shill property '" << shill_property_name
201 << "' has value " << *shill_value
202 << ", but expected a string";
207 void ShillToONCTranslator::TranslateVPN() {
208 TranslateWithTableAndSet(
209 shill::kProviderTypeProperty, kVPNTypeTable, ::onc::vpn::kType);
210 CopyPropertiesAccordingToSignature();
212 std::string vpn_type;
213 if (onc_object_->GetStringWithoutPathExpansion(::onc::vpn::kType,
215 if (vpn_type == ::onc::vpn::kTypeL2TP_IPsec) {
216 TranslateAndAddNestedObject(::onc::vpn::kIPsec);
217 TranslateAndAddNestedObject(::onc::vpn::kL2TP);
219 TranslateAndAddNestedObject(vpn_type);
224 void ShillToONCTranslator::TranslateWiFiWithState() {
225 TranslateWithTableAndSet(
226 shill::kSecurityProperty, kWiFiSecurityTable, ::onc::wifi::kSecurity);
227 std::string ssid = shill_property_util::GetSSIDFromProperties(
228 *shill_dictionary_, NULL /* ignore unknown encoding */);
230 onc_object_->SetStringWithoutPathExpansion(::onc::wifi::kSSID, ssid);
231 CopyPropertiesAccordingToSignature();
234 void ShillToONCTranslator::TranslateCellularWithState() {
235 CopyPropertiesAccordingToSignature();
236 const base::DictionaryValue* dictionary = NULL;
237 if (shill_dictionary_->GetDictionaryWithoutPathExpansion(
238 shill::kServingOperatorProperty, &dictionary)) {
239 TranslateAndAddNestedObject(::onc::cellular::kServingOperator, *dictionary);
241 if (shill_dictionary_->GetDictionaryWithoutPathExpansion(
242 shill::kCellularApnProperty, &dictionary)) {
243 TranslateAndAddNestedObject(::onc::cellular::kAPN, *dictionary);
245 const base::ListValue* list = NULL;
246 if (shill_dictionary_->GetListWithoutPathExpansion(
247 shill::kCellularApnListProperty, &list)) {
248 TranslateAndAddListOfObjects(::onc::cellular::kAPNList, *list);
252 void ShillToONCTranslator::TranslateNetworkWithState() {
253 CopyPropertiesAccordingToSignature();
255 std::string shill_network_type;
256 shill_dictionary_->GetStringWithoutPathExpansion(shill::kTypeProperty,
257 &shill_network_type);
258 std::string onc_network_type = ::onc::network_type::kEthernet;
259 if (shill_network_type != shill::kTypeEthernet &&
260 shill_network_type != shill::kTypeEthernetEap) {
261 TranslateStringToONC(
262 kNetworkTypeTable, shill_network_type, &onc_network_type);
264 if (!onc_network_type.empty()) {
265 onc_object_->SetStringWithoutPathExpansion(::onc::network_config::kType,
267 TranslateAndAddNestedObject(onc_network_type);
270 // Since Name is a read only field in Shill unless it's a VPN, it is copied
271 // here, but not when going the other direction (if it's not a VPN).
273 shill_dictionary_->GetStringWithoutPathExpansion(shill::kNameProperty,
275 onc_object_->SetStringWithoutPathExpansion(::onc::network_config::kName,
279 if (shill_dictionary_->GetStringWithoutPathExpansion(shill::kStateProperty,
281 std::string onc_state = ::onc::connection_state::kNotConnected;
282 if (NetworkState::StateIsConnected(state)) {
283 onc_state = ::onc::connection_state::kConnected;
284 } else if (NetworkState::StateIsConnecting(state)) {
285 onc_state = ::onc::connection_state::kConnecting;
287 onc_object_->SetStringWithoutPathExpansion(
288 ::onc::network_config::kConnectionState, onc_state);
292 void ShillToONCTranslator::TranslateAndAddNestedObject(
293 const std::string& onc_field_name) {
294 TranslateAndAddNestedObject(onc_field_name, *shill_dictionary_);
297 void ShillToONCTranslator::TranslateAndAddNestedObject(
298 const std::string& onc_field_name,
299 const base::DictionaryValue& dictionary) {
300 const OncFieldSignature* field_signature =
301 GetFieldSignature(*onc_signature_, onc_field_name);
302 ShillToONCTranslator nested_translator(dictionary,
303 *field_signature->value_signature);
304 scoped_ptr<base::DictionaryValue> nested_object =
305 nested_translator.CreateTranslatedONCObject();
306 if (nested_object->empty())
308 onc_object_->SetWithoutPathExpansion(onc_field_name, nested_object.release());
311 void ShillToONCTranslator::TranslateAndAddListOfObjects(
312 const std::string& onc_field_name,
313 const base::ListValue& list) {
314 const OncFieldSignature* field_signature =
315 GetFieldSignature(*onc_signature_, onc_field_name);
316 if (field_signature->value_signature->onc_type != Value::TYPE_LIST) {
317 LOG(ERROR) << "ONC Field name: '" << onc_field_name << "' has type '"
318 << field_signature->value_signature->onc_type
319 << "', expected: base::Value::TYPE_LIST.";
322 DCHECK(field_signature->value_signature->onc_array_entry_signature);
323 scoped_ptr<base::ListValue> result(new base::ListValue());
324 for (base::ListValue::const_iterator it = list.begin();
325 it != list.end(); ++it) {
326 const base::DictionaryValue* shill_value = NULL;
327 if (!(*it)->GetAsDictionary(&shill_value))
329 ShillToONCTranslator nested_translator(
331 *field_signature->value_signature->onc_array_entry_signature);
332 scoped_ptr<base::DictionaryValue> nested_object =
333 nested_translator.CreateTranslatedONCObject();
334 if (nested_object->empty())
335 // The nested object couldn't be parsed, so simply omit it.
337 result->Append(nested_object.release());
340 // There are no entries in the list, so there is no need to expose this
343 onc_object_->SetWithoutPathExpansion(onc_field_name, result.release());
346 void ShillToONCTranslator::CopyPropertiesAccordingToSignature() {
347 CopyPropertiesAccordingToSignature(onc_signature_);
350 void ShillToONCTranslator::CopyPropertiesAccordingToSignature(
351 const OncValueSignature* value_signature) {
352 if (value_signature->base_signature)
353 CopyPropertiesAccordingToSignature(value_signature->base_signature);
354 for (const OncFieldSignature* field_signature = value_signature->fields;
355 field_signature->onc_field_name != NULL; ++field_signature) {
356 CopyProperty(field_signature);
360 void ShillToONCTranslator::CopyProperty(
361 const OncFieldSignature* field_signature) {
362 std::string shill_property_name;
363 const base::Value* shill_value = NULL;
364 if (!field_translation_table_ ||
365 !GetShillPropertyName(field_signature->onc_field_name,
366 field_translation_table_,
367 &shill_property_name) ||
368 !shill_dictionary_->GetWithoutPathExpansion(shill_property_name,
373 if (shill_value->GetType() != field_signature->value_signature->onc_type) {
374 LOG(ERROR) << "Shill property '" << shill_property_name
375 << "' with value " << *shill_value
376 << " has base::Value::Type " << shill_value->GetType()
377 << " but ONC field '" << field_signature->onc_field_name
378 << "' requires type "
379 << field_signature->value_signature->onc_type << ".";
383 onc_object_->SetWithoutPathExpansion(field_signature->onc_field_name,
384 shill_value->DeepCopy());
387 void ShillToONCTranslator::TranslateWithTableAndSet(
388 const std::string& shill_property_name,
389 const StringTranslationEntry table[],
390 const std::string& onc_field_name) {
391 std::string shill_value;
392 if (!shill_dictionary_->GetStringWithoutPathExpansion(shill_property_name,
396 std::string onc_value;
397 if (TranslateStringToONC(table, shill_value, &onc_value)) {
398 onc_object_->SetStringWithoutPathExpansion(onc_field_name, onc_value);
401 LOG(ERROR) << "Shill property '" << shill_property_name << "' with value "
402 << shill_value << " couldn't be translated to ONC";
407 scoped_ptr<base::DictionaryValue> TranslateShillServiceToONCPart(
408 const base::DictionaryValue& shill_dictionary,
409 const OncValueSignature* onc_signature) {
410 CHECK(onc_signature != NULL);
412 ShillToONCTranslator translator(shill_dictionary, *onc_signature);
413 return translator.CreateTranslatedONCObject();
417 } // namespace chromeos