onc_translator_onc_to_shill.cc revision d0247b1b59f9c528cb6df88b4f2b9afaf80d181e
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// The implementation of TranslateONCObjectToShill is structured in two parts:
6// - The recursion through the existing ONC hierarchy
7//     see TranslateONCHierarchy
8// - The local translation of an object depending on the associated signature
9//     see LocalTranslator::TranslateFields
10
11#include "chromeos/network/onc/onc_translator.h"
12
13#include <string>
14
15#include "base/json/json_reader.h"
16#include "base/json/json_writer.h"
17#include "base/logging.h"
18#include "base/values.h"
19#include "chromeos/network/onc/onc_constants.h"
20#include "chromeos/network/onc/onc_signature.h"
21#include "chromeos/network/onc/onc_translation_tables.h"
22#include "third_party/cros_system_api/dbus/service_constants.h"
23
24namespace chromeos {
25namespace onc {
26
27namespace {
28
29scoped_ptr<base::StringValue> ConvertValueToString(const base::Value& value) {
30  std::string str;
31  if (!value.GetAsString(&str))
32    base::JSONWriter::Write(&value, &str);
33  return make_scoped_ptr(base::Value::CreateStringValue(str));
34}
35
36// This class is responsible to translate the local fields of the given
37// |onc_object| according to |onc_signature| into |shill_dictionary|. This
38// translation should consider (if possible) only fields of this ONC object and
39// not nested objects because recursion is handled by the calling function
40// TranslateONCHierarchy.
41class LocalTranslator {
42 public:
43  LocalTranslator(const OncValueSignature& onc_signature,
44                  const base::DictionaryValue& onc_object,
45                  base::DictionaryValue* shill_dictionary)
46      : onc_signature_(&onc_signature),
47        onc_object_(&onc_object),
48        shill_dictionary_(shill_dictionary) {
49    field_translation_table_ = GetFieldTranslationTable(onc_signature);
50  }
51
52  void TranslateFields();
53
54 private:
55  void TranslateEthernet();
56  void TranslateOpenVPN();
57  void TranslateVPN();
58  void TranslateWiFi();
59  void TranslateEAP();
60  void TranslateNetworkConfiguration();
61
62  // Copies all entries from |onc_object_| to |shill_dictionary_| for which a
63  // translation (shill_property_name) is defined by |onc_signature_|.
64  void CopyFieldsAccordingToSignature();
65
66  // Adds |value| to |shill_dictionary| at the field shill_property_name given
67  // by the associated signature. Takes ownership of |value|. Does nothing if
68  // |value| is NULL or the property name cannot be read from the signature.
69  void AddValueAccordingToSignature(const std::string& onc_field_name,
70                                    scoped_ptr<base::Value> value);
71
72  // If existent, translates the entry at |onc_field_name| in |onc_object_|
73  // using |table|. It is an error if no matching table entry is found. Writes
74  // the result as entry at |shill_property_name| in |shill_dictionary_|.
75  void TranslateWithTableAndSet(const std::string& onc_field_name,
76                                const StringTranslationEntry table[],
77                                const std::string& shill_property_name);
78
79  const OncValueSignature* onc_signature_;
80  const FieldTranslationEntry* field_translation_table_;
81  const base::DictionaryValue* onc_object_;
82  base::DictionaryValue* shill_dictionary_;
83
84  DISALLOW_COPY_AND_ASSIGN(LocalTranslator);
85};
86
87void LocalTranslator::TranslateFields() {
88  if (onc_signature_ == &kNetworkConfigurationSignature)
89    TranslateNetworkConfiguration();
90  else if (onc_signature_ == &kEthernetSignature)
91    TranslateEthernet();
92  else if (onc_signature_ == &kVPNSignature)
93    TranslateVPN();
94  else if (onc_signature_ == &kOpenVPNSignature)
95    TranslateOpenVPN();
96  else if (onc_signature_ == &kWiFiSignature)
97    TranslateWiFi();
98  else if (onc_signature_ == &kEAPSignature)
99    TranslateEAP();
100  else
101    CopyFieldsAccordingToSignature();
102}
103
104void LocalTranslator::TranslateEthernet() {
105  std::string authentication;
106  onc_object_->GetStringWithoutPathExpansion(ethernet::kAuthentication,
107                                             &authentication);
108
109  const char* shill_type = flimflam::kTypeEthernet;
110  if (authentication == ethernet::k8021X)
111    shill_type = shill::kTypeEthernetEap;
112  shill_dictionary_->SetStringWithoutPathExpansion(flimflam::kTypeProperty,
113                                                   shill_type);
114
115  CopyFieldsAccordingToSignature();
116}
117
118void LocalTranslator::TranslateOpenVPN() {
119  // Shill supports only one RemoteCertKU but ONC a list.
120  // Copy only the first entry if existing.
121  const base::ListValue* certKUs = NULL;
122  std::string certKU;
123  if (onc_object_->GetListWithoutPathExpansion(openvpn::kRemoteCertKU,
124                                               &certKUs) &&
125      certKUs->GetString(0, &certKU)) {
126    shill_dictionary_->SetStringWithoutPathExpansion(
127        flimflam::kOpenVPNRemoteCertKUProperty, certKU);
128  }
129
130  for (base::DictionaryValue::Iterator it(*onc_object_); !it.IsAtEnd();
131       it.Advance()) {
132    scoped_ptr<base::Value> translated;
133    if (it.key() == vpn::kSaveCredentials ||
134        it.key() == openvpn::kRemoteCertKU ||
135        it.key() == openvpn::kServerCAPEMs) {
136      translated.reset(it.value().DeepCopy());
137    } else {
138      // Shill wants all Provider/VPN fields to be strings.
139      translated = ConvertValueToString(it.value());
140    }
141    AddValueAccordingToSignature(it.key(), translated.Pass());
142  }
143}
144
145void LocalTranslator::TranslateVPN() {
146  std::string type;
147  onc_object_->GetStringWithoutPathExpansion(vpn::kType, &type);
148  TranslateWithTableAndSet(type, kVPNTypeTable,
149                           flimflam::kProviderTypeProperty);
150
151  CopyFieldsAccordingToSignature();
152}
153
154void LocalTranslator::TranslateWiFi() {
155  std::string security;
156  onc_object_->GetStringWithoutPathExpansion(wifi::kSecurity, &security);
157  TranslateWithTableAndSet(security, kWiFiSecurityTable,
158                           flimflam::kSecurityProperty);
159
160  // We currently only support managed and no adhoc networks.
161  shill_dictionary_->SetStringWithoutPathExpansion(flimflam::kModeProperty,
162                                                   flimflam::kModeManaged);
163  CopyFieldsAccordingToSignature();
164}
165
166void LocalTranslator::TranslateEAP() {
167  std::string outer;
168  onc_object_->GetStringWithoutPathExpansion(eap::kOuter, &outer);
169  TranslateWithTableAndSet(outer, kEAPOuterTable, flimflam::kEapMethodProperty);
170
171  // Translate the inner protocol only for outer tunneling protocols.
172  if (outer == eap::kPEAP || outer == eap::kEAP_TTLS) {
173    // In ONC the Inner protocol defaults to "Automatic".
174    std::string inner = eap::kAutomatic;
175    // ONC's Inner == "Automatic" translates to omitting the Phase2 property in
176    // Shill.
177    onc_object_->GetStringWithoutPathExpansion(eap::kInner, &inner);
178    if (inner != eap::kAutomatic) {
179      const StringTranslationEntry* table =
180          outer == eap::kPEAP ? kEAP_PEAP_InnerTable : kEAP_TTLS_InnerTable;
181      TranslateWithTableAndSet(inner, table, flimflam::kEapPhase2AuthProperty);
182    }
183  }
184
185  CopyFieldsAccordingToSignature();
186}
187
188void LocalTranslator::TranslateNetworkConfiguration() {
189  std::string type;
190  onc_object_->GetStringWithoutPathExpansion(network_config::kType, &type);
191
192  // Set the type except for Ethernet which is set in TranslateEthernet.
193  if (type != network_type::kEthernet)
194    TranslateWithTableAndSet(type, kNetworkTypeTable, flimflam::kTypeProperty);
195
196  // Shill doesn't allow setting the name for non-VPN networks.
197  if (type == network_type::kVPN) {
198    std::string name;
199    onc_object_->GetStringWithoutPathExpansion(network_config::kName, &name);
200    shill_dictionary_->SetStringWithoutPathExpansion(flimflam::kNameProperty,
201                                                     name);
202  }
203
204  CopyFieldsAccordingToSignature();
205}
206
207void LocalTranslator::CopyFieldsAccordingToSignature() {
208  for (base::DictionaryValue::Iterator it(*onc_object_); !it.IsAtEnd();
209       it.Advance()) {
210    AddValueAccordingToSignature(it.key(),
211                                 make_scoped_ptr(it.value().DeepCopy()));
212  }
213}
214
215void LocalTranslator::AddValueAccordingToSignature(
216    const std::string& onc_name,
217    scoped_ptr<base::Value> value) {
218  if (!value || !field_translation_table_)
219    return;
220  std::string shill_property_name;
221  if (!GetShillPropertyName(onc_name,
222                            field_translation_table_,
223                            &shill_property_name))
224    return;
225
226  shill_dictionary_->SetWithoutPathExpansion(shill_property_name,
227                                             value.release());
228}
229
230void LocalTranslator::TranslateWithTableAndSet(
231    const std::string& onc_value,
232    const StringTranslationEntry table[],
233    const std::string& shill_property_name) {
234  std::string shill_value;
235  if (TranslateStringToShill(table, onc_value, &shill_value)) {
236    shill_dictionary_->SetStringWithoutPathExpansion(shill_property_name,
237                                                     shill_value);
238    return;
239  }
240  // As we previously validate ONC, this case should never occur. If it still
241  // occurs, we should check here. Otherwise the failure will only show up much
242  // later in Shill.
243  LOG(ERROR) << "Value '" << onc_value
244             << "' cannot be translated to Shill property "
245             << shill_property_name;
246}
247
248// Iterates recursively over |onc_object| and its |signature|. At each object
249// applies the local translation using LocalTranslator::TranslateFields. The
250// results are written to |shill_dictionary|.
251void TranslateONCHierarchy(const OncValueSignature& signature,
252                           const base::DictionaryValue& onc_object,
253                           base::DictionaryValue* shill_dictionary) {
254  base::DictionaryValue* target_shill_dictionary = shill_dictionary;
255  std::vector<std::string> path_to_shill_dictionary =
256      GetPathToNestedShillDictionary(signature);
257  for (std::vector<std::string>::const_iterator it =
258           path_to_shill_dictionary.begin();
259       it != path_to_shill_dictionary.end();
260       ++it) {
261    base::DictionaryValue* nested_shill_dict = NULL;
262    target_shill_dictionary->GetDictionaryWithoutPathExpansion(
263        *it, &nested_shill_dict);
264    if (!nested_shill_dict)
265      nested_shill_dict = new base::DictionaryValue;
266    target_shill_dictionary->SetWithoutPathExpansion(*it, nested_shill_dict);
267    target_shill_dictionary = nested_shill_dict;
268  }
269  // Translates fields of |onc_object| and writes them to
270  // |target_shill_dictionary_| nested in |shill_dictionary|.
271  LocalTranslator translator(signature, onc_object, target_shill_dictionary);
272  translator.TranslateFields();
273
274  // Recurse into nested objects.
275  for (base::DictionaryValue::Iterator it(onc_object); !it.IsAtEnd();
276       it.Advance()) {
277    const base::DictionaryValue* inner_object = NULL;
278    if (!it.value().GetAsDictionary(&inner_object))
279      continue;
280
281    const OncFieldSignature* field_signature =
282        GetFieldSignature(signature, it.key());
283
284    TranslateONCHierarchy(*field_signature->value_signature, *inner_object,
285                          shill_dictionary);
286  }
287}
288
289}  // namespace
290
291scoped_ptr<base::DictionaryValue> TranslateONCObjectToShill(
292    const OncValueSignature* onc_signature,
293    const base::DictionaryValue& onc_object) {
294  CHECK(onc_signature != NULL);
295  scoped_ptr<base::DictionaryValue> shill_dictionary(new base::DictionaryValue);
296  TranslateONCHierarchy(*onc_signature, onc_object, shill_dictionary.get());
297  return shill_dictionary.Pass();
298}
299
300}  // namespace onc
301}  // namespace chromeos
302