- add sources.
[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 TranslateVPN();
65   void TranslateWiFiWithState();
66   void TranslateCellularWithState();
67   void TranslateNetworkWithState();
68
69   // Creates an ONC object from |dictionary| according to the signature
70   // associated to |onc_field_name| and adds it to |onc_object_| at
71   // |onc_field_name|.
72   void TranslateAndAddNestedObject(const std::string& onc_field_name,
73                                    const base::DictionaryValue& dictionary);
74
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
77   // |onc_field_name|.
78   void TranslateAndAddNestedObject(const std::string& onc_field_name);
79
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
83   // |onc_object_|.
84   void TranslateAndAddListOfObjects(const std::string& onc_field_name,
85                                     const base::ListValue& list);
86
87   // Applies function CopyProperty to each field of |value_signature| and its
88   // base signatures.
89   void CopyPropertiesAccordingToSignature(
90       const OncValueSignature* value_signature);
91
92   // Applies function CopyProperty to each field of |onc_signature_| and its
93   // base signatures.
94   void CopyPropertiesAccordingToSignature();
95
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);
99
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
103   // |onc_object_|.
104   void TranslateWithTableAndSet(const std::string& shill_property_name,
105                                 const StringTranslationEntry table[],
106                                 const std::string& onc_field_name);
107
108   const base::DictionaryValue* shill_dictionary_;
109   const OncValueSignature* onc_signature_;
110   const FieldTranslationEntry* field_translation_table_;
111   scoped_ptr<base::DictionaryValue> onc_object_;
112
113   DISALLOW_COPY_AND_ASSIGN(ShillToONCTranslator);
114 };
115
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) {
122     TranslateEthernet();
123   } else if (onc_signature_ == &kVPNSignature) {
124     TranslateVPN();
125   } else if (onc_signature_ == &kOpenVPNSignature) {
126     TranslateOpenVPN();
127   } else if (onc_signature_ == &kWiFiWithStateSignature) {
128     TranslateWiFiWithState();
129   } else if (onc_signature_ == &kCellularWithStateSignature) {
130     TranslateCellularWithState();
131   } else {
132     CopyPropertiesAccordingToSignature();
133   }
134   return onc_object_.Pass();
135 }
136
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,
145                                              onc_auth);
146 }
147
148 void ShillToONCTranslator::TranslateOpenVPN() {
149   // Shill supports only one RemoteCertKU but ONC requires a list. If existing,
150   // wraps the value into a list.
151   std::string certKU;
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,
157                                          certKUs.release());
158   }
159
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);
167       continue;
168     }
169
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,
177                                                     &shill_value)) {
178       continue;
179     }
180
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(
187           shill_str,
188           field_signature->value_signature->onc_type);
189
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;
195       } else {
196         onc_object_->SetWithoutPathExpansion(onc_field_name,
197                                              translated.release());
198       }
199     } else {
200       LOG(ERROR) << "Shill property '" << shill_property_name
201                  << "' has value " << *shill_value
202                  << ", but expected a string";
203     }
204   }
205 }
206
207 void ShillToONCTranslator::TranslateVPN() {
208   TranslateWithTableAndSet(
209       shill::kProviderTypeProperty, kVPNTypeTable, ::onc::vpn::kType);
210   CopyPropertiesAccordingToSignature();
211
212   std::string vpn_type;
213   if (onc_object_->GetStringWithoutPathExpansion(::onc::vpn::kType,
214                                                  &vpn_type)) {
215     if (vpn_type == ::onc::vpn::kTypeL2TP_IPsec) {
216       TranslateAndAddNestedObject(::onc::vpn::kIPsec);
217       TranslateAndAddNestedObject(::onc::vpn::kL2TP);
218     } else {
219       TranslateAndAddNestedObject(vpn_type);
220     }
221   }
222 }
223
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 */);
229   if (!ssid.empty())
230     onc_object_->SetStringWithoutPathExpansion(::onc::wifi::kSSID, ssid);
231   CopyPropertiesAccordingToSignature();
232 }
233
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);
240   }
241   if (shill_dictionary_->GetDictionaryWithoutPathExpansion(
242         shill::kCellularApnProperty, &dictionary)) {
243     TranslateAndAddNestedObject(::onc::cellular::kAPN, *dictionary);
244   }
245   const base::ListValue* list = NULL;
246   if (shill_dictionary_->GetListWithoutPathExpansion(
247           shill::kCellularApnListProperty, &list)) {
248     TranslateAndAddListOfObjects(::onc::cellular::kAPNList, *list);
249   }
250 }
251
252 void ShillToONCTranslator::TranslateNetworkWithState() {
253   CopyPropertiesAccordingToSignature();
254
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);
263   }
264   if (!onc_network_type.empty()) {
265     onc_object_->SetStringWithoutPathExpansion(::onc::network_config::kType,
266                                                onc_network_type);
267     TranslateAndAddNestedObject(onc_network_type);
268   }
269
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).
272   std::string name;
273   shill_dictionary_->GetStringWithoutPathExpansion(shill::kNameProperty,
274                                                    &name);
275   onc_object_->SetStringWithoutPathExpansion(::onc::network_config::kName,
276                                              name);
277
278   std::string state;
279   if (shill_dictionary_->GetStringWithoutPathExpansion(shill::kStateProperty,
280                                                        &state)) {
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;
286     }
287     onc_object_->SetStringWithoutPathExpansion(
288         ::onc::network_config::kConnectionState, onc_state);
289   }
290 }
291
292 void ShillToONCTranslator::TranslateAndAddNestedObject(
293     const std::string& onc_field_name) {
294   TranslateAndAddNestedObject(onc_field_name, *shill_dictionary_);
295 }
296
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())
307     return;
308   onc_object_->SetWithoutPathExpansion(onc_field_name, nested_object.release());
309 }
310
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.";
320     return;
321   }
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))
328       continue;
329     ShillToONCTranslator nested_translator(
330         *shill_value,
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.
336       continue;
337     result->Append(nested_object.release());
338   }
339   if (result->empty())
340     // There are no entries in the list, so there is no need to expose this
341     // field.
342     return;
343   onc_object_->SetWithoutPathExpansion(onc_field_name, result.release());
344 }
345
346 void ShillToONCTranslator::CopyPropertiesAccordingToSignature() {
347   CopyPropertiesAccordingToSignature(onc_signature_);
348 }
349
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);
357   }
358 }
359
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,
369                                                   &shill_value)) {
370     return;
371   }
372
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 << ".";
380     return;
381   }
382
383  onc_object_->SetWithoutPathExpansion(field_signature->onc_field_name,
384                                       shill_value->DeepCopy());
385 }
386
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,
393                                                         &shill_value)) {
394     return;
395   }
396   std::string onc_value;
397   if (TranslateStringToONC(table, shill_value, &onc_value)) {
398     onc_object_->SetStringWithoutPathExpansion(onc_field_name, onc_value);
399     return;
400   }
401   LOG(ERROR) << "Shill property '" << shill_property_name << "' with value "
402              << shill_value << " couldn't be translated to ONC";
403 }
404
405 }  // namespace
406
407 scoped_ptr<base::DictionaryValue> TranslateShillServiceToONCPart(
408     const base::DictionaryValue& shill_dictionary,
409     const OncValueSignature* onc_signature) {
410   CHECK(onc_signature != NULL);
411
412   ShillToONCTranslator translator(shill_dictionary, *onc_signature);
413   return translator.CreateTranslatedONCObject();
414 }
415
416 }  // namespace onc
417 }  // namespace chromeos