Use Managed properties for Preferred and Provider.
[chromium-blink-merge.git] / chromeos / network / onc / onc_translator_shill_to_onc.cc
blob224e26c870738d8185fc6fe4006b8323d9546b88
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"
7 #include <string>
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"
22 namespace chromeos {
23 namespace onc {
25 namespace {
27 // Converts |str| to a base::Value of the given |type|. If the conversion fails,
28 // returns NULL.
29 scoped_ptr<base::Value> ConvertStringToValue(const std::string& str,
30 base::Value::Type type) {
31 base::Value* value;
32 if (type == base::Value::TYPE_STRING) {
33 value = new base::StringValue(str);
34 } else {
35 value = base::JSONReader::Read(str);
38 if (value == NULL || value->GetType() != type) {
39 delete value;
40 value = NULL;
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
48 // are translated.
49 class ShillToONCTranslator {
50 public:
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
67 // given signature.
68 scoped_ptr<base::DictionaryValue> CreateTranslatedONCObject();
70 private:
71 void TranslateEthernet();
72 void TranslateOpenVPN();
73 void TranslateIPsec();
74 void TranslateVPN();
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
83 // |onc_field_name|.
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
89 // |onc_field_name|.
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
101 // |onc_object_|.
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
106 // base signatures.
107 void CopyPropertiesAccordingToSignature(
108 const OncValueSignature* value_signature);
110 // Applies function CopyProperty to each field of |onc_signature_| and its
111 // base signatures.
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
121 // |onc_object_|.
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_|
127 // for debugging.
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) {
144 TranslateEthernet();
145 } else if (onc_signature_ == &kVPNSignature) {
146 TranslateVPN();
147 } else if (onc_signature_ == &kOpenVPNSignature) {
148 TranslateOpenVPN();
149 } else if (onc_signature_ == &kIPsecSignature) {
150 TranslateIPsec();
151 } else if (onc_signature_ == &kWiFiWithStateSignature) {
152 TranslateWiFiWithState();
153 } else if (onc_signature_ == &kCellularWithStateSignature) {
154 if (field_translation_table_ == kCellularDeviceTable)
155 TranslateCellularDevice();
156 else
157 TranslateCellularWithState();
158 } else if (onc_signature_ == &kIPConfigSignature) {
159 TranslateIPConfig();
160 } else {
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,
174 onc_auth);
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.
183 std::string certKU;
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,
189 certKUs.release());
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);
198 continue;
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,
208 &shill_value)) {
209 continue;
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(
218 shill_str,
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();
227 } else {
228 onc_object_->SetWithoutPathExpansion(onc_field_name,
229 translated.release());
231 } else {
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)) {
253 return;
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)) {
260 return;
262 onc_object_->SetStringWithoutPathExpansion(::onc::vpn::kType,
263 onc_provider_type);
264 std::string provider_host;
265 if (provider->GetStringWithoutPathExpansion(shill::kHostProperty,
266 &provider_host)) {
267 onc_object_->SetStringWithoutPathExpansion(::onc::vpn::kHost,
268 provider_host);
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;
277 } else {
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 */);
296 if (!ssid.empty())
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)) {
317 return;
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,
363 onc_network_type);
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).
369 std::string name;
370 shill_dictionary_->GetStringWithoutPathExpansion(shill::kNameProperty,
371 &name);
372 onc_object_->SetStringWithoutPathExpansion(::onc::network_config::kName,
373 name);
375 // Limit ONC state to "NotConnected", "Connected", or "Connecting".
376 std::string state;
377 if (shill_dictionary_->GetStringWithoutPathExpansion(shill::kStateProperty,
378 &state)) {
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).
392 std::string address;
393 if (shill_dictionary_->GetStringWithoutPathExpansion(shill::kAddressProperty,
394 &address)) {
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,
405 &shill_ipconfigs)) {
406 TranslateAndAddListOfObjects(::onc::network_config::kIPConfigs,
407 *shill_ipconfigs);
411 void ShillToONCTranslator::TranslateIPConfig() {
412 CopyPropertiesAccordingToSignature();
413 std::string shill_ip_method;
414 shill_dictionary_->GetStringWithoutPathExpansion(shill::kMethodProperty,
415 &shill_ip_method);
416 std::string type;
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;
423 } else {
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;
442 return;
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())
449 return;
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();
475 return;
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))
483 continue;
484 ShillToONCTranslator nested_translator(
485 *shill_value,
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())
491 continue;
492 result->Append(nested_object.release());
494 // If there are no entries in the list, there is no need to expose this field.
495 if (result->empty())
496 return;
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,
523 &shill_value)) {
524 return;
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();
535 return;
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,
548 &shill_value)) {
549 return;
551 std::string onc_value;
552 if (TranslateStringToONC(table, shill_value, &onc_value)) {
553 onc_object_->SetStringWithoutPathExpansion(onc_field_name, onc_value);
554 return;
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_);
562 std::string name;
563 shill_dictionary_->GetStringWithoutPathExpansion(shill::kNameProperty, &name);
564 return name;
567 } // namespace
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();
578 } // namespace onc
579 } // namespace chromeos