Upstream version 9.38.198.0
[platform/framework/web/crosswalk.git] / src / chromeos / network / onc / onc_translator_shill_to_onc.cc
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.
4
5 #include "chromeos/network/onc/onc_translator.h"
6
7 #include <string>
8
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"
21
22 namespace chromeos {
23 namespace onc {
24
25 namespace {
26
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);
36   }
37
38   if (value == NULL || value->GetType() != type) {
39     delete value;
40     value = NULL;
41   }
42   return make_scoped_ptr(value);
43 }
44
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);
56   }
57
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) {
64   }
65
66   // Translates the associated Shill dictionary and creates an ONC object of the
67   // given signature.
68   scoped_ptr<base::DictionaryValue> CreateTranslatedONCObject();
69
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();
80
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);
86
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);
91
92   // Translates a list of nested objects and adds the list to |onc_object_| at
93   // |onc_field_name|. If there are errors while parsing individual objects or
94   // if the resulting list contains no entries, the result will not be added to
95   // |onc_object_|.
96   void TranslateAndAddListOfObjects(const std::string& onc_field_name,
97                                     const base::ListValue& list);
98
99   // Applies function CopyProperty to each field of |value_signature| and its
100   // base signatures.
101   void CopyPropertiesAccordingToSignature(
102       const OncValueSignature* value_signature);
103
104   // Applies function CopyProperty to each field of |onc_signature_| and its
105   // base signatures.
106   void CopyPropertiesAccordingToSignature();
107
108   // If |shill_property_name| is defined in |field_signature|, copies this
109   // entry from |shill_dictionary_| to |onc_object_| if it exists.
110   void CopyProperty(const OncFieldSignature* field_signature);
111
112   // If existent, translates the entry at |shill_property_name| in
113   // |shill_dictionary_| using |table|. It is an error if no matching table
114   // entry is found. Writes the result as entry at |onc_field_name| in
115   // |onc_object_|.
116   void TranslateWithTableAndSet(const std::string& shill_property_name,
117                                 const StringTranslationEntry table[],
118                                 const std::string& onc_field_name);
119
120   const base::DictionaryValue* shill_dictionary_;
121   const OncValueSignature* onc_signature_;
122   const FieldTranslationEntry* field_translation_table_;
123   scoped_ptr<base::DictionaryValue> onc_object_;
124
125   DISALLOW_COPY_AND_ASSIGN(ShillToONCTranslator);
126 };
127
128 scoped_ptr<base::DictionaryValue>
129 ShillToONCTranslator::CreateTranslatedONCObject() {
130   onc_object_.reset(new base::DictionaryValue);
131   if (onc_signature_ == &kNetworkWithStateSignature) {
132     TranslateNetworkWithState();
133   } else if (onc_signature_ == &kEthernetSignature) {
134     TranslateEthernet();
135   } else if (onc_signature_ == &kVPNSignature) {
136     TranslateVPN();
137   } else if (onc_signature_ == &kOpenVPNSignature) {
138     TranslateOpenVPN();
139   } else if (onc_signature_ == &kIPsecSignature) {
140     TranslateIPsec();
141   } else if (onc_signature_ == &kWiFiWithStateSignature) {
142     TranslateWiFiWithState();
143   } else if (onc_signature_ == &kCellularWithStateSignature) {
144     if (field_translation_table_ == kCellularDeviceTable)
145       TranslateCellularDevice();
146     else
147       TranslateCellularWithState();
148   } else if (onc_signature_ == &kIPConfigSignature) {
149     TranslateIPConfig();
150   } else {
151     CopyPropertiesAccordingToSignature();
152   }
153   return onc_object_.Pass();
154 }
155
156 void ShillToONCTranslator::TranslateEthernet() {
157   std::string shill_network_type;
158   shill_dictionary_->GetStringWithoutPathExpansion(shill::kTypeProperty,
159                                                    &shill_network_type);
160   const char* onc_auth = ::onc::ethernet::kAuthenticationNone;
161   if (shill_network_type == shill::kTypeEthernetEap)
162     onc_auth = ::onc::ethernet::k8021X;
163   onc_object_->SetStringWithoutPathExpansion(::onc::ethernet::kAuthentication,
164                                              onc_auth);
165 }
166
167 void ShillToONCTranslator::TranslateOpenVPN() {
168   if (shill_dictionary_->HasKey(shill::kOpenVPNVerifyX509NameProperty))
169     TranslateAndAddNestedObject(::onc::openvpn::kVerifyX509);
170
171   // Shill supports only one RemoteCertKU but ONC requires a list. If existing,
172   // wraps the value into a list.
173   std::string certKU;
174   if (shill_dictionary_->GetStringWithoutPathExpansion(
175           shill::kOpenVPNRemoteCertKUProperty, &certKU)) {
176     scoped_ptr<base::ListValue> certKUs(new base::ListValue);
177     certKUs->AppendString(certKU);
178     onc_object_->SetWithoutPathExpansion(::onc::openvpn::kRemoteCertKU,
179                                          certKUs.release());
180   }
181
182   for (const OncFieldSignature* field_signature = onc_signature_->fields;
183        field_signature->onc_field_name != NULL; ++field_signature) {
184     const std::string& onc_field_name = field_signature->onc_field_name;
185     if (onc_field_name == ::onc::vpn::kSaveCredentials ||
186         onc_field_name == ::onc::openvpn::kRemoteCertKU ||
187         onc_field_name == ::onc::openvpn::kServerCAPEMs) {
188       CopyProperty(field_signature);
189       continue;
190     }
191
192     std::string shill_property_name;
193     const base::Value* shill_value = NULL;
194     if (!field_translation_table_ ||
195         !GetShillPropertyName(field_signature->onc_field_name,
196                               field_translation_table_,
197                               &shill_property_name) ||
198         !shill_dictionary_->GetWithoutPathExpansion(shill_property_name,
199                                                     &shill_value)) {
200       continue;
201     }
202
203     scoped_ptr<base::Value> translated;
204     std::string shill_str;
205     if (shill_value->GetAsString(&shill_str)) {
206       // Shill wants all Provider/VPN fields to be strings. Translates these
207       // strings back to the correct ONC type.
208       translated = ConvertStringToValue(
209           shill_str,
210           field_signature->value_signature->onc_type);
211
212       if (translated.get() == NULL) {
213         LOG(ERROR) << "Shill property '" << shill_property_name
214                    << "' with value " << *shill_value
215                    << " couldn't be converted to base::Value::Type "
216                    << field_signature->value_signature->onc_type;
217       } else {
218         onc_object_->SetWithoutPathExpansion(onc_field_name,
219                                              translated.release());
220       }
221     } else {
222       LOG(ERROR) << "Shill property '" << shill_property_name
223                  << "' has value " << *shill_value
224                  << ", but expected a string";
225     }
226   }
227 }
228
229 void ShillToONCTranslator::TranslateIPsec() {
230   CopyPropertiesAccordingToSignature();
231   if (shill_dictionary_->HasKey(shill::kL2tpIpsecXauthUserProperty))
232     TranslateAndAddNestedObject(::onc::ipsec::kXAUTH);
233 }
234
235 void ShillToONCTranslator::TranslateVPN() {
236   TranslateWithTableAndSet(
237       shill::kProviderTypeProperty, kVPNTypeTable, ::onc::vpn::kType);
238   CopyPropertiesAccordingToSignature();
239
240   std::string vpn_type;
241   if (onc_object_->GetStringWithoutPathExpansion(::onc::vpn::kType,
242                                                  &vpn_type)) {
243     if (vpn_type == ::onc::vpn::kTypeL2TP_IPsec) {
244       TranslateAndAddNestedObject(::onc::vpn::kIPsec);
245       TranslateAndAddNestedObject(::onc::vpn::kL2TP);
246     } else {
247       TranslateAndAddNestedObject(vpn_type);
248     }
249   }
250 }
251
252 void ShillToONCTranslator::TranslateWiFiWithState() {
253   TranslateWithTableAndSet(
254       shill::kSecurityProperty, kWiFiSecurityTable, ::onc::wifi::kSecurity);
255   std::string ssid = shill_property_util::GetSSIDFromProperties(
256       *shill_dictionary_, NULL /* ignore unknown encoding */);
257   if (!ssid.empty())
258     onc_object_->SetStringWithoutPathExpansion(::onc::wifi::kSSID, ssid);
259   CopyPropertiesAccordingToSignature();
260 }
261
262 void ShillToONCTranslator::TranslateCellularWithState() {
263   CopyPropertiesAccordingToSignature();
264   const base::DictionaryValue* dictionary = NULL;
265   if (shill_dictionary_->GetDictionaryWithoutPathExpansion(
266         shill::kServingOperatorProperty, &dictionary)) {
267     TranslateAndAddNestedObject(::onc::cellular::kServingOperator, *dictionary);
268   }
269   if (shill_dictionary_->GetDictionaryWithoutPathExpansion(
270         shill::kCellularApnProperty, &dictionary)) {
271     TranslateAndAddNestedObject(::onc::cellular::kAPN, *dictionary);
272   }
273   // Merge the Device dictionary with this one (Cellular) using the
274   // CellularDevice signature.
275   const base::DictionaryValue* device_dictionary = NULL;
276   if (!shill_dictionary_->GetDictionaryWithoutPathExpansion(
277           shill::kDeviceProperty, &device_dictionary)) {
278     return;
279   }
280   ShillToONCTranslator nested_translator(*device_dictionary,
281                                          kCellularWithStateSignature,
282                                          kCellularDeviceTable);
283   scoped_ptr<base::DictionaryValue> nested_object =
284       nested_translator.CreateTranslatedONCObject();
285   onc_object_->MergeDictionary(nested_object.get());
286 }
287
288 void ShillToONCTranslator::TranslateCellularDevice() {
289   CopyPropertiesAccordingToSignature();
290   const base::DictionaryValue* shill_sim_lock_status = NULL;
291   if (shill_dictionary_->GetDictionaryWithoutPathExpansion(
292           shill::kSIMLockStatusProperty, &shill_sim_lock_status)) {
293     TranslateAndAddNestedObject(::onc::cellular::kSIMLockStatus,
294                                 *shill_sim_lock_status);
295   }
296   const base::ListValue* shill_apns = NULL;
297   if (shill_dictionary_->GetListWithoutPathExpansion(
298           shill::kCellularApnListProperty, &shill_apns)) {
299     TranslateAndAddListOfObjects(::onc::cellular::kAPNList, *shill_apns);
300   }
301   const base::ListValue* shill_found_networks = NULL;
302   if (shill_dictionary_->GetListWithoutPathExpansion(
303           shill::kFoundNetworksProperty, &shill_found_networks)) {
304     TranslateAndAddListOfObjects(::onc::cellular::kFoundNetworks,
305                                  *shill_found_networks);
306   }
307 }
308
309 void ShillToONCTranslator::TranslateNetworkWithState() {
310   CopyPropertiesAccordingToSignature();
311
312   std::string shill_network_type;
313   shill_dictionary_->GetStringWithoutPathExpansion(shill::kTypeProperty,
314                                                    &shill_network_type);
315   std::string onc_network_type = ::onc::network_type::kEthernet;
316   if (shill_network_type != shill::kTypeEthernet &&
317       shill_network_type != shill::kTypeEthernetEap) {
318     TranslateStringToONC(
319         kNetworkTypeTable, shill_network_type, &onc_network_type);
320   }
321   // Translate nested Cellular, WiFi, etc. properties.
322   if (!onc_network_type.empty()) {
323     onc_object_->SetStringWithoutPathExpansion(::onc::network_config::kType,
324                                                onc_network_type);
325     TranslateAndAddNestedObject(onc_network_type);
326   }
327
328   // Since Name is a read only field in Shill unless it's a VPN, it is copied
329   // here, but not when going the other direction (if it's not a VPN).
330   std::string name;
331   shill_dictionary_->GetStringWithoutPathExpansion(shill::kNameProperty,
332                                                    &name);
333   onc_object_->SetStringWithoutPathExpansion(::onc::network_config::kName,
334                                              name);
335
336   // Limit ONC state to "NotConnected", "Connected", or "Connecting".
337   std::string state;
338   if (shill_dictionary_->GetStringWithoutPathExpansion(shill::kStateProperty,
339                                                        &state)) {
340     std::string onc_state = ::onc::connection_state::kNotConnected;
341     if (NetworkState::StateIsConnected(state)) {
342       onc_state = ::onc::connection_state::kConnected;
343     } else if (NetworkState::StateIsConnecting(state)) {
344       onc_state = ::onc::connection_state::kConnecting;
345     }
346     onc_object_->SetStringWithoutPathExpansion(
347         ::onc::network_config::kConnectionState, onc_state);
348   }
349
350   // Use a human-readable aa:bb format for any hardware MAC address. Note:
351   // this property is provided by the caller but is not part of the Shill
352   // Service properties (it is copied from the Device properties).
353   std::string address;
354   if (shill_dictionary_->GetStringWithoutPathExpansion(shill::kAddressProperty,
355                                                        &address)) {
356     onc_object_->SetStringWithoutPathExpansion(
357         ::onc::network_config::kMacAddress,
358         network_util::FormattedMacAddress(address));
359   }
360
361   // Shill's Service has an IPConfig property (note the singular), not an
362   // IPConfigs property. However, we require the caller of the translation to
363   // patch the Shill dictionary before passing it to the translator.
364   const base::ListValue* shill_ipconfigs = NULL;
365   if (shill_dictionary_->GetListWithoutPathExpansion(shill::kIPConfigsProperty,
366                                                      &shill_ipconfigs)) {
367     TranslateAndAddListOfObjects(::onc::network_config::kIPConfigs,
368                                  *shill_ipconfigs);
369   }
370 }
371
372 void ShillToONCTranslator::TranslateIPConfig() {
373   CopyPropertiesAccordingToSignature();
374   std::string shill_ip_method;
375   shill_dictionary_->GetStringWithoutPathExpansion(shill::kMethodProperty,
376                                                    &shill_ip_method);
377   std::string type;
378   if (shill_ip_method == shill::kTypeIPv4 ||
379       shill_ip_method == shill::kTypeDHCP) {
380     type = ::onc::ipconfig::kIPv4;
381   } else if (shill_ip_method == shill::kTypeIPv6 ||
382              shill_ip_method == shill::kTypeDHCP6) {
383     type = ::onc::ipconfig::kIPv6;
384   } else {
385     return;  // Ignore unhandled IPConfig types, e.g. bootp, zeroconf, ppp
386   }
387
388   onc_object_->SetStringWithoutPathExpansion(::onc::ipconfig::kType, type);
389 }
390
391 void ShillToONCTranslator::TranslateAndAddNestedObject(
392     const std::string& onc_field_name) {
393   TranslateAndAddNestedObject(onc_field_name, *shill_dictionary_);
394 }
395
396 void ShillToONCTranslator::TranslateAndAddNestedObject(
397     const std::string& onc_field_name,
398     const base::DictionaryValue& dictionary) {
399   const OncFieldSignature* field_signature =
400       GetFieldSignature(*onc_signature_, onc_field_name);
401   if (!field_signature) {
402     NOTREACHED() << "Unable to find signature for field: " << onc_field_name;
403     return;
404   }
405   ShillToONCTranslator nested_translator(dictionary,
406                                          *field_signature->value_signature);
407   scoped_ptr<base::DictionaryValue> nested_object =
408       nested_translator.CreateTranslatedONCObject();
409   if (nested_object->empty())
410     return;
411   onc_object_->SetWithoutPathExpansion(onc_field_name, nested_object.release());
412 }
413
414 void ShillToONCTranslator::TranslateAndAddListOfObjects(
415     const std::string& onc_field_name,
416     const base::ListValue& list) {
417   const OncFieldSignature* field_signature =
418       GetFieldSignature(*onc_signature_, onc_field_name);
419   if (field_signature->value_signature->onc_type != base::Value::TYPE_LIST) {
420     LOG(ERROR) << "ONC Field name: '" << onc_field_name << "' has type '"
421                << field_signature->value_signature->onc_type
422                << "', expected: base::Value::TYPE_LIST.";
423     return;
424   }
425   DCHECK(field_signature->value_signature->onc_array_entry_signature);
426   scoped_ptr<base::ListValue> result(new base::ListValue());
427   for (base::ListValue::const_iterator it = list.begin();
428        it != list.end(); ++it) {
429     const base::DictionaryValue* shill_value = NULL;
430     if (!(*it)->GetAsDictionary(&shill_value))
431       continue;
432     ShillToONCTranslator nested_translator(
433         *shill_value,
434         *field_signature->value_signature->onc_array_entry_signature);
435     scoped_ptr<base::DictionaryValue> nested_object =
436         nested_translator.CreateTranslatedONCObject();
437     // If the nested object couldn't be parsed, simply omit it.
438     if (nested_object->empty())
439       continue;
440     result->Append(nested_object.release());
441   }
442   // If there are no entries in the list, there is no need to expose this field.
443   if (result->empty())
444     return;
445   onc_object_->SetWithoutPathExpansion(onc_field_name, result.release());
446 }
447
448 void ShillToONCTranslator::CopyPropertiesAccordingToSignature() {
449   CopyPropertiesAccordingToSignature(onc_signature_);
450 }
451
452 void ShillToONCTranslator::CopyPropertiesAccordingToSignature(
453     const OncValueSignature* value_signature) {
454   if (value_signature->base_signature)
455     CopyPropertiesAccordingToSignature(value_signature->base_signature);
456   for (const OncFieldSignature* field_signature = value_signature->fields;
457        field_signature->onc_field_name != NULL; ++field_signature) {
458     CopyProperty(field_signature);
459   }
460 }
461
462 void ShillToONCTranslator::CopyProperty(
463     const OncFieldSignature* field_signature) {
464   std::string shill_property_name;
465   const base::Value* shill_value = NULL;
466   if (!field_translation_table_ ||
467       !GetShillPropertyName(field_signature->onc_field_name,
468                             field_translation_table_,
469                             &shill_property_name) ||
470       !shill_dictionary_->GetWithoutPathExpansion(shill_property_name,
471                                                   &shill_value)) {
472     return;
473   }
474
475   if (shill_value->GetType() != field_signature->value_signature->onc_type) {
476     LOG(ERROR) << "Shill property '" << shill_property_name
477                << "' with value " << *shill_value
478                << " has base::Value::Type " << shill_value->GetType()
479                << " but ONC field '" << field_signature->onc_field_name
480                << "' requires type "
481                << field_signature->value_signature->onc_type << ".";
482     return;
483   }
484
485  onc_object_->SetWithoutPathExpansion(field_signature->onc_field_name,
486                                       shill_value->DeepCopy());
487 }
488
489 void ShillToONCTranslator::TranslateWithTableAndSet(
490     const std::string& shill_property_name,
491     const StringTranslationEntry table[],
492     const std::string& onc_field_name) {
493   std::string shill_value;
494   if (!shill_dictionary_->GetStringWithoutPathExpansion(shill_property_name,
495                                                         &shill_value)) {
496     return;
497   }
498   std::string onc_value;
499   if (TranslateStringToONC(table, shill_value, &onc_value)) {
500     onc_object_->SetStringWithoutPathExpansion(onc_field_name, onc_value);
501     return;
502   }
503   LOG(ERROR) << "Shill property '" << shill_property_name << "' with value "
504              << shill_value << " couldn't be translated to ONC";
505 }
506
507 }  // namespace
508
509 scoped_ptr<base::DictionaryValue> TranslateShillServiceToONCPart(
510     const base::DictionaryValue& shill_dictionary,
511     const OncValueSignature* onc_signature) {
512   CHECK(onc_signature != NULL);
513
514   ShillToONCTranslator translator(shill_dictionary, *onc_signature);
515   return translator.CreateTranslatedONCObject();
516 }
517
518 }  // namespace onc
519 }  // namespace chromeos