onc_translator_shill_to_onc.cc revision eb525c5499e34cc9c4b825d6d9e75bb07cc06ace
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      CopyProperty(field_signature);
135      continue;
136    }
137
138    std::string shill_property_name;
139    const base::Value* shill_value = NULL;
140    if (!field_translation_table_ ||
141        !GetShillPropertyName(field_signature->onc_field_name,
142                              field_translation_table_,
143                              &shill_property_name) ||
144        !shill_dictionary_->GetWithoutPathExpansion(shill_property_name,
145                                                    &shill_value)) {
146      continue;
147    }
148
149    scoped_ptr<base::Value> translated;
150    std::string shill_str;
151    if (shill_value->GetAsString(&shill_str)) {
152      // Shill wants all Provider/VPN fields to be strings. Translates these
153      // strings back to the correct ONC type.
154      translated = ConvertStringToValue(
155          shill_str,
156          field_signature->value_signature->onc_type);
157
158      if (translated.get() == NULL) {
159        LOG(ERROR) << "Shill property '" << shill_property_name
160                   << "' with value " << *shill_value
161                   << " couldn't be converted to base::Value::Type "
162                   << field_signature->value_signature->onc_type;
163      } else {
164        onc_object_->SetWithoutPathExpansion(onc_field_name,
165                                             translated.release());
166      }
167    } else {
168      LOG(ERROR) << "Shill property '" << shill_property_name
169                 << "' has value " << *shill_value
170                 << ", but expected a string";
171    }
172  }
173}
174
175void ShillToONCTranslator::TranslateVPN() {
176  TranslateWithTableAndSet(flimflam::kProviderTypeProperty, kVPNTypeTable,
177                           vpn::kType);
178  CopyPropertiesAccordingToSignature();
179
180  std::string vpn_type;
181  if (onc_object_->GetStringWithoutPathExpansion(vpn::kType,
182                                                 &vpn_type)) {
183    if (vpn_type == vpn::kTypeL2TP_IPsec) {
184      TranslateAndAddNestedObject(vpn::kIPsec);
185      TranslateAndAddNestedObject(vpn::kL2TP);
186    } else {
187      TranslateAndAddNestedObject(vpn_type);
188    }
189  }
190}
191
192void ShillToONCTranslator::TranslateWiFiWithState() {
193  TranslateWithTableAndSet(flimflam::kSecurityProperty, kWiFiSecurityTable,
194                           wifi::kSecurity);
195  CopyPropertiesAccordingToSignature();
196}
197
198void ShillToONCTranslator::TranslateAndAddNestedObject(
199    const std::string& onc_field_name) {
200  const OncFieldSignature* field_signature =
201      GetFieldSignature(*onc_signature_, onc_field_name);
202  ShillToONCTranslator nested_translator(*shill_dictionary_,
203                                         *field_signature->value_signature);
204  scoped_ptr<base::DictionaryValue> nested_object =
205      nested_translator.CreateTranslatedONCObject();
206  if (nested_object->empty())
207    return;
208  onc_object_->SetWithoutPathExpansion(onc_field_name, nested_object.release());
209}
210
211void ShillToONCTranslator::TranslateNetworkWithState() {
212  TranslateWithTableAndSet(flimflam::kTypeProperty, kNetworkTypeTable,
213                           network_config::kType);
214  CopyPropertiesAccordingToSignature();
215
216  std::string network_type;
217  if (onc_object_->GetStringWithoutPathExpansion(network_config::kType,
218                                                 &network_type)) {
219    TranslateAndAddNestedObject(network_type);
220  }
221
222  // Since Name is a read only field in Shill unless it's a VPN, it is copied
223  // here, but not when going the other direction (if it's not a VPN).
224  std::string name;
225  shill_dictionary_->GetStringWithoutPathExpansion(flimflam::kNameProperty,
226                                                   &name);
227  onc_object_->SetStringWithoutPathExpansion(network_config::kName, name);
228
229  std::string state;
230  if (shill_dictionary_->GetStringWithoutPathExpansion(flimflam::kStateProperty,
231                                                       &state)) {
232    std::string onc_state = connection_state::kNotConnected;
233    if (NetworkState::StateIsConnected(state)) {
234      onc_state = connection_state::kConnected;
235    } else if (NetworkState::StateIsConnecting(state)) {
236      onc_state = connection_state::kConnecting;
237    }
238    onc_object_->SetStringWithoutPathExpansion(network_config::kConnectionState,
239                                               onc_state);
240  }
241}
242
243void ShillToONCTranslator::CopyPropertiesAccordingToSignature() {
244  CopyPropertiesAccordingToSignature(onc_signature_);
245}
246
247void ShillToONCTranslator::CopyPropertiesAccordingToSignature(
248    const OncValueSignature* value_signature) {
249  if (value_signature->base_signature)
250    CopyPropertiesAccordingToSignature(value_signature->base_signature);
251  for (const OncFieldSignature* field_signature = value_signature->fields;
252       field_signature->onc_field_name != NULL; ++field_signature) {
253    CopyProperty(field_signature);
254  }
255}
256
257void ShillToONCTranslator::CopyProperty(
258    const OncFieldSignature* field_signature) {
259  std::string shill_property_name;
260  const base::Value* shill_value = NULL;
261  if (!field_translation_table_ ||
262      !GetShillPropertyName(field_signature->onc_field_name,
263                            field_translation_table_,
264                            &shill_property_name) ||
265      !shill_dictionary_->GetWithoutPathExpansion(shill_property_name,
266                                                  &shill_value)) {
267    return;
268  }
269
270  if (shill_value->GetType() != field_signature->value_signature->onc_type) {
271    LOG(ERROR) << "Shill property '" << shill_property_name
272               << "' with value " << *shill_value
273               << " has base::Value::Type " << shill_value->GetType()
274               << " but ONC field '" << field_signature->onc_field_name
275               << "' requires type "
276               << field_signature->value_signature->onc_type << ".";
277    return;
278  }
279
280 onc_object_->SetWithoutPathExpansion(field_signature->onc_field_name,
281                                      shill_value->DeepCopy());
282}
283
284void ShillToONCTranslator::TranslateWithTableAndSet(
285    const std::string& shill_property_name,
286    const StringTranslationEntry table[],
287    const std::string& onc_field_name) {
288  std::string shill_value;
289  if (!shill_dictionary_->GetStringWithoutPathExpansion(shill_property_name,
290                                                        &shill_value)) {
291    return;
292  }
293  std::string onc_value;
294  if (TranslateStringToONC(table, shill_value, &onc_value)) {
295    onc_object_->SetStringWithoutPathExpansion(onc_field_name, onc_value);
296    return;
297  }
298  LOG(ERROR) << "Shill property '" << shill_property_name << "' with value "
299             << shill_value << " couldn't be translated to ONC";
300}
301
302}  // namespace
303
304scoped_ptr<base::DictionaryValue> TranslateShillServiceToONCPart(
305    const base::DictionaryValue& shill_dictionary,
306    const OncValueSignature* onc_signature) {
307  CHECK(onc_signature != NULL);
308
309  ShillToONCTranslator translator(shill_dictionary, *onc_signature);
310  return translator.CreateTranslatedONCObject();
311}
312
313}  // namespace onc
314}  // namespace chromeos
315