Upstream version 7.36.149.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/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"
20
21 namespace chromeos {
22 namespace onc {
23
24 namespace {
25
26 // Converts |str| to a base::Value of the given |type|. If the conversion fails,
27 // returns NULL.
28 scoped_ptr<base::Value> ConvertStringToValue(const std::string& str,
29                                              base::Value::Type type) {
30   base::Value* value;
31   if (type == base::Value::TYPE_STRING) {
32     value = base::Value::CreateStringValue(str);
33   } else {
34     value = base::JSONReader::Read(str);
35   }
36
37   if (value == NULL || value->GetType() != type) {
38     delete value;
39     value = NULL;
40   }
41   return make_scoped_ptr(value);
42 }
43
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
47 // are translated.
48 class ShillToONCTranslator {
49  public:
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);
55   }
56
57   // Translates the associated Shill dictionary and creates an ONC object of the
58   // given signature.
59   scoped_ptr<base::DictionaryValue> CreateTranslatedONCObject();
60
61  private:
62   void TranslateEthernet();
63   void TranslateOpenVPN();
64   void TranslateIPsec();
65   void TranslateVPN();
66   void TranslateWiFiWithState();
67   void TranslateCellularWithState();
68   void TranslateNetworkWithState();
69   void TranslateIPConfig();
70
71   // Creates an ONC object from |dictionary| according to the signature
72   // associated to |onc_field_name| and adds it to |onc_object_| at
73   // |onc_field_name|.
74   void TranslateAndAddNestedObject(const std::string& onc_field_name,
75                                    const base::DictionaryValue& dictionary);
76
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
79   // |onc_field_name|.
80   void TranslateAndAddNestedObject(const std::string& onc_field_name);
81
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
85   // |onc_object_|.
86   void TranslateAndAddListOfObjects(const std::string& onc_field_name,
87                                     const base::ListValue& list);
88
89   // Applies function CopyProperty to each field of |value_signature| and its
90   // base signatures.
91   void CopyPropertiesAccordingToSignature(
92       const OncValueSignature* value_signature);
93
94   // Applies function CopyProperty to each field of |onc_signature_| and its
95   // base signatures.
96   void CopyPropertiesAccordingToSignature();
97
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);
101
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
105   // |onc_object_|.
106   void TranslateWithTableAndSet(const std::string& shill_property_name,
107                                 const StringTranslationEntry table[],
108                                 const std::string& onc_field_name);
109
110   const base::DictionaryValue* shill_dictionary_;
111   const OncValueSignature* onc_signature_;
112   const FieldTranslationEntry* field_translation_table_;
113   scoped_ptr<base::DictionaryValue> onc_object_;
114
115   DISALLOW_COPY_AND_ASSIGN(ShillToONCTranslator);
116 };
117
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) {
124     TranslateEthernet();
125   } else if (onc_signature_ == &kVPNSignature) {
126     TranslateVPN();
127   } else if (onc_signature_ == &kOpenVPNSignature) {
128     TranslateOpenVPN();
129   } else if (onc_signature_ == &kIPsecSignature) {
130     TranslateIPsec();
131   } else if (onc_signature_ == &kWiFiWithStateSignature) {
132     TranslateWiFiWithState();
133   } else if (onc_signature_ == &kCellularWithStateSignature) {
134     TranslateCellularWithState();
135   } else if (onc_signature_ == &kIPConfigSignature) {
136     TranslateIPConfig();
137   } else {
138     CopyPropertiesAccordingToSignature();
139   }
140   return onc_object_.Pass();
141 }
142
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,
151                                              onc_auth);
152 }
153
154 void ShillToONCTranslator::TranslateOpenVPN() {
155   if (shill_dictionary_->HasKey(shill::kOpenVPNVerifyX509NameProperty))
156     TranslateAndAddNestedObject(::onc::openvpn::kVerifyX509);
157
158   // Shill supports only one RemoteCertKU but ONC requires a list. If existing,
159   // wraps the value into a list.
160   std::string certKU;
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,
166                                          certKUs.release());
167   }
168
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);
176       continue;
177     }
178
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,
186                                                     &shill_value)) {
187       continue;
188     }
189
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(
196           shill_str,
197           field_signature->value_signature->onc_type);
198
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;
204       } else {
205         onc_object_->SetWithoutPathExpansion(onc_field_name,
206                                              translated.release());
207       }
208     } else {
209       LOG(ERROR) << "Shill property '" << shill_property_name
210                  << "' has value " << *shill_value
211                  << ", but expected a string";
212     }
213   }
214 }
215
216 void ShillToONCTranslator::TranslateIPsec() {
217   CopyPropertiesAccordingToSignature();
218   if (shill_dictionary_->HasKey(shill::kL2tpIpsecXauthUserProperty))
219     TranslateAndAddNestedObject(::onc::ipsec::kXAUTH);
220 }
221
222 void ShillToONCTranslator::TranslateVPN() {
223   TranslateWithTableAndSet(
224       shill::kProviderTypeProperty, kVPNTypeTable, ::onc::vpn::kType);
225   CopyPropertiesAccordingToSignature();
226
227   std::string vpn_type;
228   if (onc_object_->GetStringWithoutPathExpansion(::onc::vpn::kType,
229                                                  &vpn_type)) {
230     if (vpn_type == ::onc::vpn::kTypeL2TP_IPsec) {
231       TranslateAndAddNestedObject(::onc::vpn::kIPsec);
232       TranslateAndAddNestedObject(::onc::vpn::kL2TP);
233     } else {
234       TranslateAndAddNestedObject(vpn_type);
235     }
236   }
237 }
238
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 */);
244   if (!ssid.empty())
245     onc_object_->SetStringWithoutPathExpansion(::onc::wifi::kSSID, ssid);
246   CopyPropertiesAccordingToSignature();
247 }
248
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);
255   }
256   if (shill_dictionary_->GetDictionaryWithoutPathExpansion(
257         shill::kCellularApnProperty, &dictionary)) {
258     TranslateAndAddNestedObject(::onc::cellular::kAPN, *dictionary);
259   }
260   const base::ListValue* shill_apns = NULL;
261   if (shill_dictionary_->GetListWithoutPathExpansion(
262           shill::kCellularApnListProperty, &shill_apns)) {
263     TranslateAndAddListOfObjects(::onc::cellular::kAPNList, *shill_apns);
264   }
265 }
266
267 void ShillToONCTranslator::TranslateNetworkWithState() {
268   CopyPropertiesAccordingToSignature();
269
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);
278   }
279   if (!onc_network_type.empty()) {
280     onc_object_->SetStringWithoutPathExpansion(::onc::network_config::kType,
281                                                onc_network_type);
282     TranslateAndAddNestedObject(onc_network_type);
283   }
284
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).
287   std::string name;
288   shill_dictionary_->GetStringWithoutPathExpansion(shill::kNameProperty,
289                                                    &name);
290   onc_object_->SetStringWithoutPathExpansion(::onc::network_config::kName,
291                                              name);
292
293   std::string state;
294   if (shill_dictionary_->GetStringWithoutPathExpansion(shill::kStateProperty,
295                                                        &state)) {
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;
301     }
302     onc_object_->SetStringWithoutPathExpansion(
303         ::onc::network_config::kConnectionState, onc_state);
304   }
305
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,
311                                                      &shill_ipconfigs)) {
312     TranslateAndAddListOfObjects(::onc::network_config::kIPConfigs,
313                                  *shill_ipconfigs);
314   }
315 }
316
317 void ShillToONCTranslator::TranslateIPConfig() {
318   CopyPropertiesAccordingToSignature();
319   std::string shill_ip_method;
320   shill_dictionary_->GetStringWithoutPathExpansion(shill::kMethodProperty,
321                                                    &shill_ip_method);
322   std::string type;
323   if (shill_ip_method == shill::kTypeIPv4 ||
324       shill_ip_method == shill::kTypeDHCP) {
325     type = ::onc::ipconfig::kIPv4;
326   } else if (shill_ip_method == shill::kTypeIPv6 ||
327              shill_ip_method == shill::kTypeDHCP6) {
328     type = ::onc::ipconfig::kIPv6;
329   } else {
330     return;  // Ignore unhandled IPConfig types, e.g. bootp, zeroconf, ppp
331   }
332
333   onc_object_->SetStringWithoutPathExpansion(::onc::ipconfig::kType, type);
334 }
335
336 void ShillToONCTranslator::TranslateAndAddNestedObject(
337     const std::string& onc_field_name) {
338   TranslateAndAddNestedObject(onc_field_name, *shill_dictionary_);
339 }
340
341 void ShillToONCTranslator::TranslateAndAddNestedObject(
342     const std::string& onc_field_name,
343     const base::DictionaryValue& dictionary) {
344   const OncFieldSignature* field_signature =
345       GetFieldSignature(*onc_signature_, onc_field_name);
346   DCHECK(field_signature) << "Unable to find signature for field "
347                           << onc_field_name << ".";
348   ShillToONCTranslator nested_translator(dictionary,
349                                          *field_signature->value_signature);
350   scoped_ptr<base::DictionaryValue> nested_object =
351       nested_translator.CreateTranslatedONCObject();
352   if (nested_object->empty())
353     return;
354   onc_object_->SetWithoutPathExpansion(onc_field_name, nested_object.release());
355 }
356
357 void ShillToONCTranslator::TranslateAndAddListOfObjects(
358     const std::string& onc_field_name,
359     const base::ListValue& list) {
360   const OncFieldSignature* field_signature =
361       GetFieldSignature(*onc_signature_, onc_field_name);
362   if (field_signature->value_signature->onc_type != base::Value::TYPE_LIST) {
363     LOG(ERROR) << "ONC Field name: '" << onc_field_name << "' has type '"
364                << field_signature->value_signature->onc_type
365                << "', expected: base::Value::TYPE_LIST.";
366     return;
367   }
368   DCHECK(field_signature->value_signature->onc_array_entry_signature);
369   scoped_ptr<base::ListValue> result(new base::ListValue());
370   for (base::ListValue::const_iterator it = list.begin();
371        it != list.end(); ++it) {
372     const base::DictionaryValue* shill_value = NULL;
373     if (!(*it)->GetAsDictionary(&shill_value))
374       continue;
375     ShillToONCTranslator nested_translator(
376         *shill_value,
377         *field_signature->value_signature->onc_array_entry_signature);
378     scoped_ptr<base::DictionaryValue> nested_object =
379         nested_translator.CreateTranslatedONCObject();
380     // If the nested object couldn't be parsed, simply omit it.
381     if (nested_object->empty())
382       continue;
383     result->Append(nested_object.release());
384   }
385   // If there are no entries in the list, there is no need to expose this field.
386   if (result->empty())
387     return;
388   onc_object_->SetWithoutPathExpansion(onc_field_name, result.release());
389 }
390
391 void ShillToONCTranslator::CopyPropertiesAccordingToSignature() {
392   CopyPropertiesAccordingToSignature(onc_signature_);
393 }
394
395 void ShillToONCTranslator::CopyPropertiesAccordingToSignature(
396     const OncValueSignature* value_signature) {
397   if (value_signature->base_signature)
398     CopyPropertiesAccordingToSignature(value_signature->base_signature);
399   for (const OncFieldSignature* field_signature = value_signature->fields;
400        field_signature->onc_field_name != NULL; ++field_signature) {
401     CopyProperty(field_signature);
402   }
403 }
404
405 void ShillToONCTranslator::CopyProperty(
406     const OncFieldSignature* field_signature) {
407   std::string shill_property_name;
408   const base::Value* shill_value = NULL;
409   if (!field_translation_table_ ||
410       !GetShillPropertyName(field_signature->onc_field_name,
411                             field_translation_table_,
412                             &shill_property_name) ||
413       !shill_dictionary_->GetWithoutPathExpansion(shill_property_name,
414                                                   &shill_value)) {
415     return;
416   }
417
418   if (shill_value->GetType() != field_signature->value_signature->onc_type) {
419     LOG(ERROR) << "Shill property '" << shill_property_name
420                << "' with value " << *shill_value
421                << " has base::Value::Type " << shill_value->GetType()
422                << " but ONC field '" << field_signature->onc_field_name
423                << "' requires type "
424                << field_signature->value_signature->onc_type << ".";
425     return;
426   }
427
428  onc_object_->SetWithoutPathExpansion(field_signature->onc_field_name,
429                                       shill_value->DeepCopy());
430 }
431
432 void ShillToONCTranslator::TranslateWithTableAndSet(
433     const std::string& shill_property_name,
434     const StringTranslationEntry table[],
435     const std::string& onc_field_name) {
436   std::string shill_value;
437   if (!shill_dictionary_->GetStringWithoutPathExpansion(shill_property_name,
438                                                         &shill_value)) {
439     return;
440   }
441   std::string onc_value;
442   if (TranslateStringToONC(table, shill_value, &onc_value)) {
443     onc_object_->SetStringWithoutPathExpansion(onc_field_name, onc_value);
444     return;
445   }
446   LOG(ERROR) << "Shill property '" << shill_property_name << "' with value "
447              << shill_value << " couldn't be translated to ONC";
448 }
449
450 }  // namespace
451
452 scoped_ptr<base::DictionaryValue> TranslateShillServiceToONCPart(
453     const base::DictionaryValue& shill_dictionary,
454     const OncValueSignature* onc_signature) {
455   CHECK(onc_signature != NULL);
456
457   ShillToONCTranslator translator(shill_dictionary, *onc_signature);
458   return translator.CreateTranslatedONCObject();
459 }
460
461 }  // namespace onc
462 }  // namespace chromeos