shill_property_util.cc revision 58537e28ecd584eab876aee8be7156509866d23a
1// Copyright 2013 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/shill_property_util.h"
6
7#include "base/i18n/icu_encoding_detection.h"
8#include "base/i18n/icu_string_conversions.h"
9#include "base/json/json_writer.h"
10#include "base/strings/string_number_conversions.h"
11#include "base/strings/string_util.h"
12#include "base/strings/stringprintf.h"
13#include "base/strings/utf_string_conversion_utils.h"
14#include "base/values.h"
15#include "chromeos/network/network_event_log.h"
16#include "chromeos/network/network_ui_data.h"
17#include "chromeos/network/onc/onc_utils.h"
18#include "third_party/cros_system_api/dbus/service_constants.h"
19
20namespace chromeos {
21
22namespace shill_property_util {
23
24namespace {
25
26// Replace non UTF8 characters in |str| with a replacement character.
27std::string ValidateUTF8(const std::string& str) {
28  std::string result;
29  for (int32 index = 0; index < static_cast<int32>(str.size()); ++index) {
30    uint32 code_point_out;
31    bool is_unicode_char = base::ReadUnicodeCharacter(
32        str.c_str(), str.size(), &index, &code_point_out);
33    const uint32 kFirstNonControlChar = 0x20;
34    if (is_unicode_char && (code_point_out >= kFirstNonControlChar)) {
35      base::WriteUnicodeCharacter(code_point_out, &result);
36    } else {
37      const uint32 kReplacementChar = 0xFFFD;
38      // Puts kReplacementChar if character is a control character [0,0x20)
39      // or is not readable UTF8.
40      base::WriteUnicodeCharacter(kReplacementChar, &result);
41    }
42  }
43  return result;
44}
45
46// If existent and non-empty, copies the string at |key| from |source| to
47// |dest|. Returns true if the string was copied.
48bool CopyStringFromDictionary(const base::DictionaryValue& source,
49                              const std::string& key,
50                              base::DictionaryValue* dest) {
51  std::string string_value;
52  if (!source.GetStringWithoutPathExpansion(key, &string_value) ||
53      string_value.empty())
54    return false;
55  dest->SetStringWithoutPathExpansion(key, string_value);
56  return true;
57}
58
59}  // namespace
60
61std::string GetNameFromProperties(const std::string& service_path,
62                                  const base::DictionaryValue& properties) {
63  std::string name, hex_ssid;
64  properties.GetStringWithoutPathExpansion(flimflam::kNameProperty, &name);
65  properties.GetStringWithoutPathExpansion(flimflam::kWifiHexSsid, &hex_ssid);
66
67  if (hex_ssid.empty()) {
68    if (name.empty())
69      return name;
70    // Validate name for UTF8.
71    std::string valid_ssid = ValidateUTF8(name);
72    if (valid_ssid != name) {
73      NET_LOG_DEBUG(
74          "GetNameFromProperties",
75          base::StringPrintf(
76              "%s: UTF8: %s", service_path.c_str(), valid_ssid.c_str()));
77    }
78    return valid_ssid;
79  }
80
81  std::string ssid;
82  std::vector<uint8> raw_ssid_bytes;
83  if (base::HexStringToBytes(hex_ssid, &raw_ssid_bytes)) {
84    ssid = std::string(raw_ssid_bytes.begin(), raw_ssid_bytes.end());
85    NET_LOG_DEBUG("GetNameFromProperties",
86                  base::StringPrintf("%s: %s, SSID: %s",
87                                     service_path.c_str(),
88                                     hex_ssid.c_str(),
89                                     ssid.c_str()));
90  } else {
91    NET_LOG_ERROR("GetNameFromProperties",
92                  base::StringPrintf("%s: Error processing: %s",
93                                     service_path.c_str(),
94                                     hex_ssid.c_str()));
95    return name;
96  }
97
98  if (IsStringUTF8(ssid)) {
99    if (ssid != name) {
100      NET_LOG_DEBUG("GetNameFromProperties",
101                    base::StringPrintf(
102                        "%s: UTF8: %s", service_path.c_str(), ssid.c_str()));
103    }
104    return ssid;
105  }
106
107  // Detect encoding and convert to UTF-8.
108  std::string country_code;
109  properties.GetStringWithoutPathExpansion(flimflam::kCountryProperty,
110                                           &country_code);
111  std::string encoding;
112  if (!base::DetectEncoding(ssid, &encoding)) {
113    // TODO(stevenjb): This is currently experimental. If we find a case where
114    // base::DetectEncoding() fails, we need to figure out whether we can use
115    // country_code with ConvertToUtf8(). crbug.com/233267.
116    encoding = country_code;
117  }
118  if (!encoding.empty()) {
119    std::string utf8_ssid;
120    if (base::ConvertToUtf8AndNormalize(ssid, encoding, &utf8_ssid)) {
121      if (utf8_ssid != name) {
122        NET_LOG_DEBUG("GetNameFromProperties",
123                      base::StringPrintf("%s: Encoding=%s: %s",
124                                         service_path.c_str(),
125                                         encoding.c_str(),
126                                         utf8_ssid.c_str()));
127      }
128      return utf8_ssid;
129    }
130  }
131
132  // Unrecognized encoding. Only use raw bytes if name_ is empty.
133  NET_LOG_DEBUG("GetNameFromProperties",
134                base::StringPrintf("%s: Unrecognized Encoding=%s: %s",
135                                   service_path.c_str(),
136                                   encoding.c_str(),
137                                   ssid.c_str()));
138  if (name.empty() && !ssid.empty())
139    return ssid;
140  return name;
141}
142
143scoped_ptr<NetworkUIData> GetUIDataFromValue(const base::Value& ui_data_value) {
144  std::string ui_data_str;
145  if (!ui_data_value.GetAsString(&ui_data_str))
146    return scoped_ptr<NetworkUIData>();
147  if (ui_data_str.empty())
148    return make_scoped_ptr(new NetworkUIData());
149  scoped_ptr<base::DictionaryValue> ui_data_dict(
150      chromeos::onc::ReadDictionaryFromJson(ui_data_str));
151  if (!ui_data_dict)
152    return scoped_ptr<NetworkUIData>();
153  return make_scoped_ptr(new NetworkUIData(*ui_data_dict));
154}
155
156scoped_ptr<NetworkUIData> GetUIDataFromProperties(
157    const base::DictionaryValue& shill_dictionary) {
158  const base::Value* ui_data_value = NULL;
159  shill_dictionary.GetWithoutPathExpansion(flimflam::kUIDataProperty,
160                                           &ui_data_value);
161  if (!ui_data_value) {
162    VLOG(2) << "Dictionary has no UIData entry.";
163    return scoped_ptr<NetworkUIData>();
164  }
165  scoped_ptr<NetworkUIData> ui_data = GetUIDataFromValue(*ui_data_value);
166  if (!ui_data)
167    LOG(ERROR) << "UIData is not a valid JSON dictionary.";
168  return ui_data.Pass();
169}
170
171void SetUIData(const NetworkUIData& ui_data,
172               base::DictionaryValue* shill_dictionary) {
173  base::DictionaryValue ui_data_dict;
174  ui_data.FillDictionary(&ui_data_dict);
175  std::string ui_data_blob;
176  base::JSONWriter::Write(&ui_data_dict, &ui_data_blob);
177  shill_dictionary->SetStringWithoutPathExpansion(flimflam::kUIDataProperty,
178                                                  ui_data_blob);
179}
180
181bool CopyIdentifyingProperties(const base::DictionaryValue& service_properties,
182                               base::DictionaryValue* dest) {
183  bool success = true;
184
185  // GUID is optional.
186  CopyStringFromDictionary(service_properties, flimflam::kGuidProperty, dest);
187
188  std::string type;
189  service_properties.GetStringWithoutPathExpansion(flimflam::kTypeProperty,
190                                                   &type);
191  success &= !type.empty();
192  dest->SetStringWithoutPathExpansion(flimflam::kTypeProperty, type);
193  if (type == flimflam::kTypeWifi) {
194    success &= CopyStringFromDictionary(
195        service_properties, flimflam::kSecurityProperty, dest);
196    success &= CopyStringFromDictionary(
197        service_properties, flimflam::kSSIDProperty, dest);
198    success &= CopyStringFromDictionary(
199        service_properties, flimflam::kModeProperty, dest);
200  } else if (type == flimflam::kTypeVPN) {
201    success &= CopyStringFromDictionary(
202        service_properties, flimflam::kNameProperty, dest);
203    // VPN Provider values are read from the "Provider" dictionary, but written
204    // with the keys "Provider.Type" and "Provider.Host".
205    const base::DictionaryValue* provider_properties = NULL;
206    if (!service_properties.GetDictionaryWithoutPathExpansion(
207             flimflam::kProviderProperty, &provider_properties)) {
208      NET_LOG_ERROR("CopyIdentifyingProperties", "Missing VPN provider dict");
209      return false;
210    }
211    std::string vpn_provider_type;
212    provider_properties->GetStringWithoutPathExpansion(flimflam::kTypeProperty,
213                                                       &vpn_provider_type);
214    success &= !vpn_provider_type.empty();
215    dest->SetStringWithoutPathExpansion(flimflam::kProviderTypeProperty,
216                                        vpn_provider_type);
217
218    std::string vpn_provider_host;
219    provider_properties->GetStringWithoutPathExpansion(flimflam::kHostProperty,
220                                                       &vpn_provider_host);
221    success &= !vpn_provider_host.empty();
222    dest->SetStringWithoutPathExpansion(flimflam::kProviderHostProperty,
223                                        vpn_provider_host);
224  } else if (type == flimflam::kTypeEthernet ||
225             type == shill::kTypeEthernetEap) {
226    // Ethernet and EthernetEAP don't have any additional identifying
227    // properties.
228  } else {
229    NOTREACHED() << "Unsupported network type " << type;
230    success = false;
231  }
232  if (!success)
233    NET_LOG_ERROR("CopyIdentifyingProperties", "Missing required properties");
234  return success;
235}
236
237}  // namespace shill_property_util
238
239namespace {
240
241const char kPatternDefault[] = "PatternDefault";
242const char kPatternEthernet[] = "PatternEthernet";
243const char kPatternWireless[] = "PatternWireless";
244const char kPatternMobile[] = "PatternMobile";
245const char kPatternNonVirtual[] = "PatternNonVirtual";
246
247enum NetworkTypeBitFlag {
248  kNetworkTypeNone = 0,
249  kNetworkTypeEthernet = 1 << 0,
250  kNetworkTypeWifi = 1 << 1,
251  kNetworkTypeWimax = 1 << 2,
252  kNetworkTypeCellular = 1 << 3,
253  kNetworkTypeVPN = 1 << 4,
254  kNetworkTypeEthernetEap = 1 << 5
255};
256
257struct ShillToBitFlagEntry {
258  const char* shill_network_type;
259  NetworkTypeBitFlag bit_flag;
260} shill_type_to_flag[] = {
261  { flimflam::kTypeEthernet, kNetworkTypeEthernet },
262  { shill::kTypeEthernetEap, kNetworkTypeEthernetEap },
263  { flimflam::kTypeWifi, kNetworkTypeWifi },
264  { flimflam::kTypeWimax, kNetworkTypeWimax },
265  { flimflam::kTypeCellular, kNetworkTypeCellular },
266  { flimflam::kTypeVPN, kNetworkTypeVPN }
267};
268
269NetworkTypeBitFlag ShillNetworkTypeToFlag(const std::string& shill_type) {
270  for (size_t i = 0; i < arraysize(shill_type_to_flag); ++i) {
271    if (shill_type_to_flag[i].shill_network_type == shill_type)
272      return shill_type_to_flag[i].bit_flag;
273  }
274  NET_LOG_ERROR("ShillNetworkTypeToFlag", "Unknown type: " + shill_type);
275  return kNetworkTypeNone;
276}
277
278}  // namespace
279
280// static
281NetworkTypePattern NetworkTypePattern::Default() {
282  return NetworkTypePattern(~0);
283}
284
285// static
286NetworkTypePattern NetworkTypePattern::Wireless() {
287  return NetworkTypePattern(kNetworkTypeWifi | kNetworkTypeWimax |
288                            kNetworkTypeCellular);
289}
290
291// static
292NetworkTypePattern NetworkTypePattern::Mobile() {
293  return NetworkTypePattern(kNetworkTypeCellular | kNetworkTypeWimax);
294}
295
296// static
297NetworkTypePattern NetworkTypePattern::NonVirtual() {
298  return NetworkTypePattern(~kNetworkTypeVPN);
299}
300
301// static
302NetworkTypePattern NetworkTypePattern::Ethernet() {
303  return NetworkTypePattern(kNetworkTypeEthernet);
304}
305
306// static
307NetworkTypePattern NetworkTypePattern::WiFi() {
308  return NetworkTypePattern(kNetworkTypeWifi);
309}
310
311// static
312NetworkTypePattern NetworkTypePattern::Cellular() {
313  return NetworkTypePattern(kNetworkTypeCellular);
314}
315
316// static
317NetworkTypePattern NetworkTypePattern::VPN() {
318  return NetworkTypePattern(kNetworkTypeVPN);
319}
320
321// static
322NetworkTypePattern NetworkTypePattern::Wimax() {
323  return NetworkTypePattern(kNetworkTypeWimax);
324}
325
326// static
327NetworkTypePattern NetworkTypePattern::Primitive(
328    const std::string& shill_network_type) {
329  return NetworkTypePattern(ShillNetworkTypeToFlag(shill_network_type));
330}
331
332bool NetworkTypePattern::Equals(const NetworkTypePattern& other) const {
333  return pattern_ == other.pattern_;
334}
335
336bool NetworkTypePattern::MatchesType(
337    const std::string& shill_network_type) const {
338  return MatchesPattern(Primitive(shill_network_type));
339}
340
341bool NetworkTypePattern::MatchesPattern(
342    const NetworkTypePattern& other_pattern) const {
343  if (Equals(other_pattern))
344    return true;
345
346  return pattern_ & other_pattern.pattern_;
347}
348
349std::string NetworkTypePattern::ToDebugString() const {
350  if (Equals(Default()))
351    return kPatternDefault;
352  if (Equals(Ethernet()))
353    return kPatternEthernet;
354  if (Equals(Wireless()))
355    return kPatternWireless;
356  if (Equals(Mobile()))
357    return kPatternMobile;
358  if (Equals(NonVirtual()))
359    return kPatternNonVirtual;
360
361  std::string str;
362  for (size_t i = 0; i < arraysize(shill_type_to_flag); ++i) {
363    if (!(pattern_ & shill_type_to_flag[i].bit_flag))
364      continue;
365    if (!str.empty())
366      str += "|";
367    str += shill_type_to_flag[i].shill_network_type;
368  }
369  return str;
370}
371
372NetworkTypePattern::NetworkTypePattern(int pattern) : pattern_(pattern) {}
373
374}  // namespace chromeos
375