onc_translator_onc_to_shill.cc revision 2a99a7e74a7f215066514fe81d2bfa6639d9eddd
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 TranslateOpenVPN();
56  void TranslateVPN();
57  void TranslateWiFi();
58  void TranslateEAP();
59  void TranslateNetworkConfiguration();
60
61  // Copies all entries from |onc_object_| to |shill_dictionary_| for which a
62  // translation (shill_property_name) is defined by |onc_signature_|.
63  void CopyFieldsAccordingToSignature();
64
65  // Adds |value| to |shill_dictionary| at the field shill_property_name given
66  // by the associated signature. Takes ownership of |value|. Does nothing if
67  // |value| is NULL or the property name cannot be read from the signature.
68  void AddValueAccordingToSignature(const std::string& onc_field_name,
69                                    scoped_ptr<base::Value> value);
70
71  // If existent, translates the entry at |onc_field_name| in |onc_object_|
72  // using |table|. It is an error if no matching table entry is found. Writes
73  // the result as entry at |shill_property_name| in |shill_dictionary_|.
74  void TranslateWithTableAndSet(const std::string& onc_field_name,
75                                const StringTranslationEntry table[],
76                                const std::string& shill_property_name);
77
78  const OncValueSignature* onc_signature_;
79  const FieldTranslationEntry* field_translation_table_;
80  const base::DictionaryValue* onc_object_;
81  base::DictionaryValue* shill_dictionary_;
82
83  DISALLOW_COPY_AND_ASSIGN(LocalTranslator);
84};
85
86void LocalTranslator::TranslateFields() {
87  if (onc_signature_ == &kNetworkConfigurationSignature)
88    TranslateNetworkConfiguration();
89  else if (onc_signature_ == &kVPNSignature)
90    TranslateVPN();
91  else if (onc_signature_ == &kOpenVPNSignature)
92    TranslateOpenVPN();
93  else if (onc_signature_ == &kWiFiSignature)
94    TranslateWiFi();
95  else if (onc_signature_ == &kEAPSignature)
96    TranslateEAP();
97  else
98    CopyFieldsAccordingToSignature();
99}
100
101void LocalTranslator::TranslateOpenVPN() {
102  // Shill supports only one RemoteCertKU but ONC a list.
103  // Copy only the first entry if existing.
104  const base::ListValue* certKUs = NULL;
105  std::string certKU;
106  if (onc_object_->GetListWithoutPathExpansion(vpn::kRemoteCertKU, &certKUs) &&
107      certKUs->GetString(0, &certKU)) {
108    shill_dictionary_->SetStringWithoutPathExpansion(
109        flimflam::kOpenVPNRemoteCertKUProperty, certKU);
110  }
111
112  for (base::DictionaryValue::Iterator it(*onc_object_); it.HasNext();
113       it.Advance()) {
114    scoped_ptr<base::Value> translated;
115    if (it.key() == vpn::kSaveCredentials || it.key() == vpn::kRemoteCertKU) {
116      translated.reset(it.value().DeepCopy());
117    } else {
118      // Shill wants all Provider/VPN fields to be strings.
119      translated = ConvertValueToString(it.value());
120    }
121    AddValueAccordingToSignature(it.key(), translated.Pass());
122  }
123}
124
125void LocalTranslator::TranslateVPN() {
126  std::string type;
127  onc_object_->GetStringWithoutPathExpansion(vpn::kType, &type);
128  TranslateWithTableAndSet(type, kVPNTypeTable,
129                           flimflam::kProviderTypeProperty);
130
131  CopyFieldsAccordingToSignature();
132}
133
134void LocalTranslator::TranslateWiFi() {
135  std::string security;
136  onc_object_->GetStringWithoutPathExpansion(wifi::kSecurity, &security);
137  TranslateWithTableAndSet(security, kWiFiSecurityTable,
138                           flimflam::kSecurityProperty);
139
140  // We currently only support managed and no adhoc networks.
141  shill_dictionary_->SetStringWithoutPathExpansion(flimflam::kModeProperty,
142                                                   flimflam::kModeManaged);
143  CopyFieldsAccordingToSignature();
144}
145
146void LocalTranslator::TranslateEAP() {
147  std::string outer;
148  onc_object_->GetStringWithoutPathExpansion(eap::kOuter, &outer);
149  TranslateWithTableAndSet(outer, kEAPOuterTable, flimflam::kEapMethodProperty);
150
151  // Translate the inner protocol only for outer tunneling protocols.
152  if (outer == eap::kPEAP || outer == eap::kEAP_TTLS) {
153    // In ONC the Inner protocol defaults to "Automatic".
154    std::string inner = eap::kAutomatic;
155    // ONC's Inner == "Automatic" translates to omitting the Phase2 property in
156    // Shill.
157    onc_object_->GetStringWithoutPathExpansion(eap::kInner, &inner);
158    if (inner != eap::kAutomatic) {
159      const StringTranslationEntry* table =
160          outer == eap::kPEAP ? kEAP_PEAP_InnerTable : kEAP_TTLS_InnerTable;
161      TranslateWithTableAndSet(inner, table, flimflam::kEapPhase2AuthProperty);
162    }
163  }
164
165  CopyFieldsAccordingToSignature();
166}
167
168void LocalTranslator::TranslateNetworkConfiguration() {
169  std::string type;
170  onc_object_->GetStringWithoutPathExpansion(network_config::kType, &type);
171  TranslateWithTableAndSet(type, kNetworkTypeTable, flimflam::kTypeProperty);
172
173  // Shill doesn't allow setting the name for non-VPN networks.
174  if (type == network_type::kVPN) {
175    std::string name;
176    onc_object_->GetStringWithoutPathExpansion(network_config::kName, &name);
177    shill_dictionary_->SetStringWithoutPathExpansion(
178        flimflam::kNameProperty, name);
179  }
180
181  CopyFieldsAccordingToSignature();
182}
183
184void LocalTranslator::CopyFieldsAccordingToSignature() {
185  for (base::DictionaryValue::Iterator it(*onc_object_); it.HasNext();
186       it.Advance()) {
187    AddValueAccordingToSignature(it.key(),
188                                 make_scoped_ptr(it.value().DeepCopy()));
189  }
190}
191
192void LocalTranslator::AddValueAccordingToSignature(
193    const std::string& onc_name,
194    scoped_ptr<base::Value> value) {
195  if (!value || !field_translation_table_)
196    return;
197
198  std::string shill_property_name;
199  if (!GetShillPropertyName(onc_name,
200                            field_translation_table_,
201                            &shill_property_name))
202    return;
203
204  shill_dictionary_->SetWithoutPathExpansion(shill_property_name,
205                                             value.release());
206}
207
208void LocalTranslator::TranslateWithTableAndSet(
209    const std::string& onc_value,
210    const StringTranslationEntry table[],
211    const std::string& shill_property_name) {
212  std::string shill_value;
213  if (TranslateStringToShill(table, onc_value, &shill_value)) {
214    shill_dictionary_->SetStringWithoutPathExpansion(shill_property_name,
215                                                     shill_value);
216    return;
217  }
218  // As we previously validate ONC, this case should never occur. If it still
219  // occurs, we should check here. Otherwise the failure will only show up much
220  // later in Shill.
221  LOG(ERROR) << "Value '" << onc_value
222             << "' cannot be translated to Shill property "
223             << shill_property_name;
224}
225
226// Iterates recursively over |onc_object| and its |signature|. At each object
227// applies the local translation using LocalTranslator::TranslateFields. The
228// results are written to |shill_dictionary|.
229void TranslateONCHierarchy(const OncValueSignature& signature,
230                           const base::DictionaryValue& onc_object,
231                           base::DictionaryValue* shill_dictionary) {
232  // Translates fields of |onc_object| and writes them to |shill_dictionary_|.
233  LocalTranslator translator(signature, onc_object, shill_dictionary);
234  translator.TranslateFields();
235
236  // Recurse into nested objects.
237  for (base::DictionaryValue::Iterator it(onc_object); it.HasNext();
238       it.Advance()) {
239    const base::DictionaryValue* inner_object = NULL;
240    if (!it.value().GetAsDictionary(&inner_object))
241      continue;
242
243    const OncFieldSignature* field_signature =
244        GetFieldSignature(signature, it.key());
245
246    TranslateONCHierarchy(*field_signature->value_signature, *inner_object,
247                          shill_dictionary);
248  }
249}
250
251}  // namespace
252
253scoped_ptr<base::DictionaryValue> TranslateONCObjectToShill(
254    const OncValueSignature* onc_signature,
255    const base::DictionaryValue& onc_object) {
256  CHECK(onc_signature != NULL);
257  scoped_ptr<base::DictionaryValue> shill_dictionary(new base::DictionaryValue);
258  TranslateONCHierarchy(*onc_signature, onc_object, shill_dictionary.get());
259  return shill_dictionary.Pass();
260}
261
262}  // namespace onc
263}  // namespace chromeos
264