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