onc_translator_shill_to_onc.cc revision 68043e1e95eeb07d5cae7aca370b26518b0867d6
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_constants.h"
16#include "chromeos/network/onc/onc_signature.h"
17#include "chromeos/network/onc/onc_translation_tables.h"
18#include "third_party/cros_system_api/dbus/service_constants.h"
19
20namespace chromeos {
21namespace onc {
22
23namespace {
24
25// Converts |str| to a base::Value of the given |type|. If the conversion fails,
26// returns NULL.
27scoped_ptr<base::Value> ConvertStringToValue(const std::string& str,
28                                             base::Value::Type type) {
29  base::Value* value;
30  if (type == base::Value::TYPE_STRING) {
31    value = base::Value::CreateStringValue(str);
32  } else {
33    value = base::JSONReader::Read(str);
34  }
35
36  if (value == NULL || value->GetType() != type) {
37    delete value;
38    value = NULL;
39  }
40  return make_scoped_ptr(value);
41}
42
43// This class implements the translation of properties from the given
44// |shill_dictionary| to a new ONC object of signature |onc_signature|. Using
45// recursive calls to CreateTranslatedONCObject of new instances, nested objects
46// are translated.
47class ShillToONCTranslator {
48 public:
49  ShillToONCTranslator(const base::DictionaryValue& shill_dictionary,
50                       const OncValueSignature& onc_signature)
51      : shill_dictionary_(&shill_dictionary),
52        onc_signature_(&onc_signature) {
53    field_translation_table_ = GetFieldTranslationTable(onc_signature);
54  }
55
56  // Translates the associated Shill dictionary and creates an ONC object of the
57  // given signature.
58  scoped_ptr<base::DictionaryValue> CreateTranslatedONCObject();
59
60 private:
61  void TranslateEthernet();
62  void TranslateOpenVPN();
63  void TranslateVPN();
64  void TranslateWiFiWithState();
65  void TranslateCellularWithState();
66  void TranslateNetworkWithState();
67
68  // Creates an ONC object from |dictionary| according to the signature
69  // associated to |onc_field_name| and adds it to |onc_object_| at
70  // |onc_field_name|.
71  void TranslateAndAddNestedObject(const std::string& onc_field_name,
72                                   const base::DictionaryValue& dictionary);
73
74  // Creates an ONC object from |shill_dictionary_| according to the signature
75  // associated to |onc_field_name| and adds it to |onc_object_| at
76  // |onc_field_name|.
77  void TranslateAndAddNestedObject(const std::string& onc_field_name);
78
79  // Applies function CopyProperty to each field of |value_signature| and its
80  // base signatures.
81  void CopyPropertiesAccordingToSignature(
82      const OncValueSignature* value_signature);
83
84  // Applies function CopyProperty to each field of |onc_signature_| and its
85  // base signatures.
86  void CopyPropertiesAccordingToSignature();
87
88  // If |shill_property_name| is defined in |field_signature|, copies this
89  // entry from |shill_dictionary_| to |onc_object_| if it exists.
90  void CopyProperty(const OncFieldSignature* field_signature);
91
92  // If existent, translates the entry at |shill_property_name| in
93  // |shill_dictionary_| using |table|. It is an error if no matching table
94  // entry is found. Writes the result as entry at |onc_field_name| in
95  // |onc_object_|.
96  void TranslateWithTableAndSet(const std::string& shill_property_name,
97                                const StringTranslationEntry table[],
98                                const std::string& onc_field_name);
99
100  const base::DictionaryValue* shill_dictionary_;
101  const OncValueSignature* onc_signature_;
102  const FieldTranslationEntry* field_translation_table_;
103  scoped_ptr<base::DictionaryValue> onc_object_;
104
105  DISALLOW_COPY_AND_ASSIGN(ShillToONCTranslator);
106};
107
108scoped_ptr<base::DictionaryValue>
109ShillToONCTranslator::CreateTranslatedONCObject() {
110  onc_object_.reset(new base::DictionaryValue);
111  if (onc_signature_ == &kNetworkWithStateSignature) {
112    TranslateNetworkWithState();
113  } else if (onc_signature_ == &kEthernetSignature) {
114    TranslateEthernet();
115  } else if (onc_signature_ == &kVPNSignature) {
116    TranslateVPN();
117  } else if (onc_signature_ == &kOpenVPNSignature) {
118    TranslateOpenVPN();
119  } else if (onc_signature_ == &kWiFiWithStateSignature) {
120    TranslateWiFiWithState();
121  } else if (onc_signature_ == &kCellularWithStateSignature) {
122    TranslateCellularWithState();
123  } else {
124    CopyPropertiesAccordingToSignature();
125  }
126  return onc_object_.Pass();
127}
128
129void ShillToONCTranslator::TranslateEthernet() {
130  std::string shill_network_type;
131  shill_dictionary_->GetStringWithoutPathExpansion(shill::kTypeProperty,
132                                                   &shill_network_type);
133  const char* onc_auth = ethernet::kNone;
134  if (shill_network_type == shill::kTypeEthernetEap)
135    onc_auth = ethernet::k8021X;
136  onc_object_->SetStringWithoutPathExpansion(ethernet::kAuthentication,
137                                             onc_auth);
138}
139
140void ShillToONCTranslator::TranslateOpenVPN() {
141  // Shill supports only one RemoteCertKU but ONC requires a list. If existing,
142  // wraps the value into a list.
143  std::string certKU;
144  if (shill_dictionary_->GetStringWithoutPathExpansion(
145          shill::kOpenVPNRemoteCertKUProperty, &certKU)) {
146    scoped_ptr<base::ListValue> certKUs(new base::ListValue);
147    certKUs->AppendString(certKU);
148    onc_object_->SetWithoutPathExpansion(openvpn::kRemoteCertKU,
149                                         certKUs.release());
150  }
151
152  for (const OncFieldSignature* field_signature = onc_signature_->fields;
153       field_signature->onc_field_name != NULL; ++field_signature) {
154    const std::string& onc_field_name = field_signature->onc_field_name;
155    if (onc_field_name == vpn::kSaveCredentials ||
156        onc_field_name == openvpn::kRemoteCertKU ||
157        onc_field_name == openvpn::kServerCAPEMs) {
158      CopyProperty(field_signature);
159      continue;
160    }
161
162    std::string shill_property_name;
163    const base::Value* shill_value = NULL;
164    if (!field_translation_table_ ||
165        !GetShillPropertyName(field_signature->onc_field_name,
166                              field_translation_table_,
167                              &shill_property_name) ||
168        !shill_dictionary_->GetWithoutPathExpansion(shill_property_name,
169                                                    &shill_value)) {
170      continue;
171    }
172
173    scoped_ptr<base::Value> translated;
174    std::string shill_str;
175    if (shill_value->GetAsString(&shill_str)) {
176      // Shill wants all Provider/VPN fields to be strings. Translates these
177      // strings back to the correct ONC type.
178      translated = ConvertStringToValue(
179          shill_str,
180          field_signature->value_signature->onc_type);
181
182      if (translated.get() == NULL) {
183        LOG(ERROR) << "Shill property '" << shill_property_name
184                   << "' with value " << *shill_value
185                   << " couldn't be converted to base::Value::Type "
186                   << field_signature->value_signature->onc_type;
187      } else {
188        onc_object_->SetWithoutPathExpansion(onc_field_name,
189                                             translated.release());
190      }
191    } else {
192      LOG(ERROR) << "Shill property '" << shill_property_name
193                 << "' has value " << *shill_value
194                 << ", but expected a string";
195    }
196  }
197}
198
199void ShillToONCTranslator::TranslateVPN() {
200  TranslateWithTableAndSet(shill::kProviderTypeProperty, kVPNTypeTable,
201                           vpn::kType);
202  CopyPropertiesAccordingToSignature();
203
204  std::string vpn_type;
205  if (onc_object_->GetStringWithoutPathExpansion(vpn::kType,
206                                                 &vpn_type)) {
207    if (vpn_type == vpn::kTypeL2TP_IPsec) {
208      TranslateAndAddNestedObject(vpn::kIPsec);
209      TranslateAndAddNestedObject(vpn::kL2TP);
210    } else {
211      TranslateAndAddNestedObject(vpn_type);
212    }
213  }
214}
215
216void ShillToONCTranslator::TranslateWiFiWithState() {
217  TranslateWithTableAndSet(shill::kSecurityProperty, kWiFiSecurityTable,
218                           wifi::kSecurity);
219  CopyPropertiesAccordingToSignature();
220}
221
222void ShillToONCTranslator::TranslateCellularWithState() {
223  CopyPropertiesAccordingToSignature();
224  const base::DictionaryValue* dictionary = NULL;
225  if (shill_dictionary_->GetDictionaryWithoutPathExpansion(
226        shill::kServingOperatorProperty, &dictionary)) {
227    TranslateAndAddNestedObject(cellular::kServingOperator, *dictionary);
228  }
229  if (shill_dictionary_->GetDictionaryWithoutPathExpansion(
230        shill::kCellularApnProperty, &dictionary)) {
231    TranslateAndAddNestedObject(cellular::kAPN, *dictionary);
232  }
233}
234
235void ShillToONCTranslator::TranslateNetworkWithState() {
236  CopyPropertiesAccordingToSignature();
237
238  std::string shill_network_type;
239  shill_dictionary_->GetStringWithoutPathExpansion(shill::kTypeProperty,
240                                                   &shill_network_type);
241  std::string onc_network_type = network_type::kEthernet;
242  if (shill_network_type != shill::kTypeEthernet &&
243      shill_network_type != shill::kTypeEthernetEap) {
244    TranslateStringToONC(
245        kNetworkTypeTable, shill_network_type, &onc_network_type);
246  }
247  if (!onc_network_type.empty()) {
248    onc_object_->SetStringWithoutPathExpansion(network_config::kType,
249                                               onc_network_type);
250    TranslateAndAddNestedObject(onc_network_type);
251  }
252
253  // Since Name is a read only field in Shill unless it's a VPN, it is copied
254  // here, but not when going the other direction (if it's not a VPN).
255  std::string name;
256  shill_dictionary_->GetStringWithoutPathExpansion(shill::kNameProperty,
257                                                   &name);
258  onc_object_->SetStringWithoutPathExpansion(network_config::kName, name);
259
260  std::string state;
261  if (shill_dictionary_->GetStringWithoutPathExpansion(shill::kStateProperty,
262                                                       &state)) {
263    std::string onc_state = connection_state::kNotConnected;
264    if (NetworkState::StateIsConnected(state)) {
265      onc_state = connection_state::kConnected;
266    } else if (NetworkState::StateIsConnecting(state)) {
267      onc_state = connection_state::kConnecting;
268    }
269    onc_object_->SetStringWithoutPathExpansion(network_config::kConnectionState,
270                                               onc_state);
271  }
272}
273
274void ShillToONCTranslator::TranslateAndAddNestedObject(
275    const std::string& onc_field_name) {
276  TranslateAndAddNestedObject(onc_field_name, *shill_dictionary_);
277}
278
279void ShillToONCTranslator::TranslateAndAddNestedObject(
280    const std::string& onc_field_name,
281    const base::DictionaryValue& dictionary) {
282  const OncFieldSignature* field_signature =
283      GetFieldSignature(*onc_signature_, onc_field_name);
284  ShillToONCTranslator nested_translator(dictionary,
285                                         *field_signature->value_signature);
286  scoped_ptr<base::DictionaryValue> nested_object =
287      nested_translator.CreateTranslatedONCObject();
288  if (nested_object->empty())
289    return;
290  onc_object_->SetWithoutPathExpansion(onc_field_name, nested_object.release());
291}
292
293void ShillToONCTranslator::CopyPropertiesAccordingToSignature() {
294  CopyPropertiesAccordingToSignature(onc_signature_);
295}
296
297void ShillToONCTranslator::CopyPropertiesAccordingToSignature(
298    const OncValueSignature* value_signature) {
299  if (value_signature->base_signature)
300    CopyPropertiesAccordingToSignature(value_signature->base_signature);
301  for (const OncFieldSignature* field_signature = value_signature->fields;
302       field_signature->onc_field_name != NULL; ++field_signature) {
303    CopyProperty(field_signature);
304  }
305}
306
307void ShillToONCTranslator::CopyProperty(
308    const OncFieldSignature* field_signature) {
309  std::string shill_property_name;
310  const base::Value* shill_value = NULL;
311  if (!field_translation_table_ ||
312      !GetShillPropertyName(field_signature->onc_field_name,
313                            field_translation_table_,
314                            &shill_property_name) ||
315      !shill_dictionary_->GetWithoutPathExpansion(shill_property_name,
316                                                  &shill_value)) {
317    return;
318  }
319
320  if (shill_value->GetType() != field_signature->value_signature->onc_type) {
321    LOG(ERROR) << "Shill property '" << shill_property_name
322               << "' with value " << *shill_value
323               << " has base::Value::Type " << shill_value->GetType()
324               << " but ONC field '" << field_signature->onc_field_name
325               << "' requires type "
326               << field_signature->value_signature->onc_type << ".";
327    return;
328  }
329
330 onc_object_->SetWithoutPathExpansion(field_signature->onc_field_name,
331                                      shill_value->DeepCopy());
332}
333
334void ShillToONCTranslator::TranslateWithTableAndSet(
335    const std::string& shill_property_name,
336    const StringTranslationEntry table[],
337    const std::string& onc_field_name) {
338  std::string shill_value;
339  if (!shill_dictionary_->GetStringWithoutPathExpansion(shill_property_name,
340                                                        &shill_value)) {
341    return;
342  }
343  std::string onc_value;
344  if (TranslateStringToONC(table, shill_value, &onc_value)) {
345    onc_object_->SetStringWithoutPathExpansion(onc_field_name, onc_value);
346    return;
347  }
348  LOG(ERROR) << "Shill property '" << shill_property_name << "' with value "
349             << shill_value << " couldn't be translated to ONC";
350}
351
352}  // namespace
353
354scoped_ptr<base::DictionaryValue> TranslateShillServiceToONCPart(
355    const base::DictionaryValue& shill_dictionary,
356    const OncValueSignature* onc_signature) {
357  CHECK(onc_signature != NULL);
358
359  ShillToONCTranslator translator(shill_dictionary, *onc_signature);
360  return translator.CreateTranslatedONCObject();
361}
362
363}  // namespace onc
364}  // namespace chromeos
365