onc_translator_shill_to_onc.cc revision c2db58bd994c04d98e4ee2cd7565b71548655fe3
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 TranslateOpenVPN();
62  void TranslateVPN();
63  void TranslateWiFiWithState();
64  void TranslateNetworkWithState();
65
66  // Creates an ONC object from |shill_dictionary| according to the signature
67  // associated to |onc_field_name| and adds it to |onc_object_| at
68  // |onc_field_name|.
69  void TranslateAndAddNestedObject(const std::string& onc_field_name);
70
71  // Applies function CopyProperty to each field of |value_signature| and its
72  // base signatures.
73  void CopyPropertiesAccordingToSignature(
74      const OncValueSignature* value_signature);
75
76  // Applies function CopyProperty to each field of |onc_signature_| and its
77  // base signatures.
78  void CopyPropertiesAccordingToSignature();
79
80  // If |shill_property_name| is defined in |field_signature|, copies this
81  // entry from |shill_dictionary_| to |onc_object_| if it exists.
82  void CopyProperty(const OncFieldSignature* field_signature);
83
84  // If existent, translates the entry at |shill_property_name| in
85  // |shill_dictionary_| using |table|. It is an error if no matching table
86  // entry is found. Writes the result as entry at |onc_field_name| in
87  // |onc_object_|.
88  void TranslateWithTableAndSet(const std::string& shill_property_name,
89                                const StringTranslationEntry table[],
90                                const std::string& onc_field_name);
91
92  const base::DictionaryValue* shill_dictionary_;
93  const OncValueSignature* onc_signature_;
94  const FieldTranslationEntry* field_translation_table_;
95  scoped_ptr<base::DictionaryValue> onc_object_;
96
97  DISALLOW_COPY_AND_ASSIGN(ShillToONCTranslator);
98};
99
100scoped_ptr<base::DictionaryValue>
101ShillToONCTranslator::CreateTranslatedONCObject() {
102  onc_object_.reset(new base::DictionaryValue);
103  if (onc_signature_ == &kNetworkWithStateSignature) {
104    TranslateNetworkWithState();
105  } else if (onc_signature_ == &kVPNSignature) {
106    TranslateVPN();
107  } else if (onc_signature_ == &kOpenVPNSignature) {
108    TranslateOpenVPN();
109  } else if (onc_signature_ == &kWiFiWithStateSignature) {
110    TranslateWiFiWithState();
111  } else {
112    CopyPropertiesAccordingToSignature();
113  }
114  return onc_object_.Pass();
115}
116
117void ShillToONCTranslator::TranslateOpenVPN() {
118  // Shill supports only one RemoteCertKU but ONC requires a list. If existing,
119  // wraps the value into a list.
120  std::string certKU;
121  if (shill_dictionary_->GetStringWithoutPathExpansion(
122          flimflam::kOpenVPNRemoteCertKUProperty, &certKU)) {
123    scoped_ptr<base::ListValue> certKUs(new base::ListValue);
124    certKUs->AppendString(certKU);
125    onc_object_->SetWithoutPathExpansion(openvpn::kRemoteCertKU,
126                                         certKUs.release());
127  }
128
129  for (const OncFieldSignature* field_signature = onc_signature_->fields;
130       field_signature->onc_field_name != NULL; ++field_signature) {
131    const std::string& onc_field_name = field_signature->onc_field_name;
132    if (onc_field_name == vpn::kSaveCredentials ||
133        onc_field_name == openvpn::kRemoteCertKU ||
134        onc_field_name == openvpn::kServerCAPEMs) {
135      CopyProperty(field_signature);
136      continue;
137    }
138
139    std::string shill_property_name;
140    const base::Value* shill_value = NULL;
141    if (!field_translation_table_ ||
142        !GetShillPropertyName(field_signature->onc_field_name,
143                              field_translation_table_,
144                              &shill_property_name) ||
145        !shill_dictionary_->GetWithoutPathExpansion(shill_property_name,
146                                                    &shill_value)) {
147      continue;
148    }
149
150    scoped_ptr<base::Value> translated;
151    std::string shill_str;
152    if (shill_value->GetAsString(&shill_str)) {
153      // Shill wants all Provider/VPN fields to be strings. Translates these
154      // strings back to the correct ONC type.
155      translated = ConvertStringToValue(
156          shill_str,
157          field_signature->value_signature->onc_type);
158
159      if (translated.get() == NULL) {
160        LOG(ERROR) << "Shill property '" << shill_property_name
161                   << "' with value " << *shill_value
162                   << " couldn't be converted to base::Value::Type "
163                   << field_signature->value_signature->onc_type;
164      } else {
165        onc_object_->SetWithoutPathExpansion(onc_field_name,
166                                             translated.release());
167      }
168    } else {
169      LOG(ERROR) << "Shill property '" << shill_property_name
170                 << "' has value " << *shill_value
171                 << ", but expected a string";
172    }
173  }
174}
175
176void ShillToONCTranslator::TranslateVPN() {
177  TranslateWithTableAndSet(flimflam::kProviderTypeProperty, kVPNTypeTable,
178                           vpn::kType);
179  CopyPropertiesAccordingToSignature();
180
181  std::string vpn_type;
182  if (onc_object_->GetStringWithoutPathExpansion(vpn::kType,
183                                                 &vpn_type)) {
184    if (vpn_type == vpn::kTypeL2TP_IPsec) {
185      TranslateAndAddNestedObject(vpn::kIPsec);
186      TranslateAndAddNestedObject(vpn::kL2TP);
187    } else {
188      TranslateAndAddNestedObject(vpn_type);
189    }
190  }
191}
192
193void ShillToONCTranslator::TranslateWiFiWithState() {
194  TranslateWithTableAndSet(flimflam::kSecurityProperty, kWiFiSecurityTable,
195                           wifi::kSecurity);
196  CopyPropertiesAccordingToSignature();
197}
198
199void ShillToONCTranslator::TranslateAndAddNestedObject(
200    const std::string& onc_field_name) {
201  const OncFieldSignature* field_signature =
202      GetFieldSignature(*onc_signature_, onc_field_name);
203  ShillToONCTranslator nested_translator(*shill_dictionary_,
204                                         *field_signature->value_signature);
205  scoped_ptr<base::DictionaryValue> nested_object =
206      nested_translator.CreateTranslatedONCObject();
207  if (nested_object->empty())
208    return;
209  onc_object_->SetWithoutPathExpansion(onc_field_name, nested_object.release());
210}
211
212void ShillToONCTranslator::TranslateNetworkWithState() {
213  TranslateWithTableAndSet(flimflam::kTypeProperty, kNetworkTypeTable,
214                           network_config::kType);
215  CopyPropertiesAccordingToSignature();
216
217  std::string network_type;
218  if (onc_object_->GetStringWithoutPathExpansion(network_config::kType,
219                                                 &network_type)) {
220    TranslateAndAddNestedObject(network_type);
221  }
222
223  // Since Name is a read only field in Shill unless it's a VPN, it is copied
224  // here, but not when going the other direction (if it's not a VPN).
225  std::string name;
226  shill_dictionary_->GetStringWithoutPathExpansion(flimflam::kNameProperty,
227                                                   &name);
228  onc_object_->SetStringWithoutPathExpansion(network_config::kName, name);
229
230  std::string state;
231  if (shill_dictionary_->GetStringWithoutPathExpansion(flimflam::kStateProperty,
232                                                       &state)) {
233    std::string onc_state = connection_state::kNotConnected;
234    if (NetworkState::StateIsConnected(state)) {
235      onc_state = connection_state::kConnected;
236    } else if (NetworkState::StateIsConnecting(state)) {
237      onc_state = connection_state::kConnecting;
238    }
239    onc_object_->SetStringWithoutPathExpansion(network_config::kConnectionState,
240                                               onc_state);
241  }
242}
243
244void ShillToONCTranslator::CopyPropertiesAccordingToSignature() {
245  CopyPropertiesAccordingToSignature(onc_signature_);
246}
247
248void ShillToONCTranslator::CopyPropertiesAccordingToSignature(
249    const OncValueSignature* value_signature) {
250  if (value_signature->base_signature)
251    CopyPropertiesAccordingToSignature(value_signature->base_signature);
252  for (const OncFieldSignature* field_signature = value_signature->fields;
253       field_signature->onc_field_name != NULL; ++field_signature) {
254    CopyProperty(field_signature);
255  }
256}
257
258void ShillToONCTranslator::CopyProperty(
259    const OncFieldSignature* field_signature) {
260  std::string shill_property_name;
261  const base::Value* shill_value = NULL;
262  if (!field_translation_table_ ||
263      !GetShillPropertyName(field_signature->onc_field_name,
264                            field_translation_table_,
265                            &shill_property_name) ||
266      !shill_dictionary_->GetWithoutPathExpansion(shill_property_name,
267                                                  &shill_value)) {
268    return;
269  }
270
271  if (shill_value->GetType() != field_signature->value_signature->onc_type) {
272    LOG(ERROR) << "Shill property '" << shill_property_name
273               << "' with value " << *shill_value
274               << " has base::Value::Type " << shill_value->GetType()
275               << " but ONC field '" << field_signature->onc_field_name
276               << "' requires type "
277               << field_signature->value_signature->onc_type << ".";
278    return;
279  }
280
281 onc_object_->SetWithoutPathExpansion(field_signature->onc_field_name,
282                                      shill_value->DeepCopy());
283}
284
285void ShillToONCTranslator::TranslateWithTableAndSet(
286    const std::string& shill_property_name,
287    const StringTranslationEntry table[],
288    const std::string& onc_field_name) {
289  std::string shill_value;
290  if (!shill_dictionary_->GetStringWithoutPathExpansion(shill_property_name,
291                                                        &shill_value)) {
292    return;
293  }
294  std::string onc_value;
295  if (TranslateStringToONC(table, shill_value, &onc_value)) {
296    onc_object_->SetStringWithoutPathExpansion(onc_field_name, onc_value);
297    return;
298  }
299  LOG(ERROR) << "Shill property '" << shill_property_name << "' with value "
300             << shill_value << " couldn't be translated to ONC";
301}
302
303}  // namespace
304
305scoped_ptr<base::DictionaryValue> TranslateShillServiceToONCPart(
306    const base::DictionaryValue& shill_dictionary,
307    const OncValueSignature* onc_signature) {
308  CHECK(onc_signature != NULL);
309
310  ShillToONCTranslator translator(shill_dictionary, *onc_signature);
311  return translator.CreateTranslatedONCObject();
312}
313
314}  // namespace onc
315}  // namespace chromeos
316