shill_property_util.cc revision 68043e1e95eeb07d5cae7aca370b26518b0867d6
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(shill::kNameProperty, &name);
65  properties.GetStringWithoutPathExpansion(shill::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(shill::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(shill::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(shill::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, shill::kGuidProperty, dest);
187
188  std::string type;
189  service_properties.GetStringWithoutPathExpansion(shill::kTypeProperty, &type);
190  success &= !type.empty();
191  dest->SetStringWithoutPathExpansion(shill::kTypeProperty, type);
192  if (type == shill::kTypeWifi) {
193    success &= CopyStringFromDictionary(
194        service_properties, shill::kSecurityProperty, dest);
195    success &= CopyStringFromDictionary(
196        service_properties, shill::kSSIDProperty, dest);
197    success &= CopyStringFromDictionary(
198        service_properties, shill::kModeProperty, dest);
199  } else if (type == shill::kTypeVPN) {
200    success &= CopyStringFromDictionary(
201        service_properties, shill::kNameProperty, dest);
202    // VPN Provider values are read from the "Provider" dictionary, but written
203    // with the keys "Provider.Type" and "Provider.Host".
204    const base::DictionaryValue* provider_properties = NULL;
205    if (!service_properties.GetDictionaryWithoutPathExpansion(
206             shill::kProviderProperty, &provider_properties)) {
207      NET_LOG_ERROR("CopyIdentifyingProperties", "Missing VPN provider dict");
208      return false;
209    }
210    std::string vpn_provider_type;
211    provider_properties->GetStringWithoutPathExpansion(shill::kTypeProperty,
212                                                       &vpn_provider_type);
213    success &= !vpn_provider_type.empty();
214    dest->SetStringWithoutPathExpansion(shill::kProviderTypeProperty,
215                                        vpn_provider_type);
216
217    std::string vpn_provider_host;
218    provider_properties->GetStringWithoutPathExpansion(shill::kHostProperty,
219                                                       &vpn_provider_host);
220    success &= !vpn_provider_host.empty();
221    dest->SetStringWithoutPathExpansion(shill::kProviderHostProperty,
222                                        vpn_provider_host);
223  } else if (type == shill::kTypeEthernet || type == shill::kTypeEthernetEap) {
224    // Ethernet and EthernetEAP don't have any additional identifying
225    // properties.
226  } else {
227    NOTREACHED() << "Unsupported network type " << type;
228    success = false;
229  }
230  if (!success)
231    NET_LOG_ERROR("CopyIdentifyingProperties", "Missing required properties");
232  return success;
233}
234
235}  // namespace shill_property_util
236
237namespace {
238
239const char kPatternDefault[] = "PatternDefault";
240const char kPatternEthernet[] = "PatternEthernet";
241const char kPatternWireless[] = "PatternWireless";
242const char kPatternMobile[] = "PatternMobile";
243const char kPatternNonVirtual[] = "PatternNonVirtual";
244
245enum NetworkTypeBitFlag {
246  kNetworkTypeNone = 0,
247  kNetworkTypeEthernet = 1 << 0,
248  kNetworkTypeWifi = 1 << 1,
249  kNetworkTypeWimax = 1 << 2,
250  kNetworkTypeCellular = 1 << 3,
251  kNetworkTypeVPN = 1 << 4,
252  kNetworkTypeEthernetEap = 1 << 5
253};
254
255struct ShillToBitFlagEntry {
256  const char* shill_network_type;
257  NetworkTypeBitFlag bit_flag;
258} shill_type_to_flag[] = {
259  { shill::kTypeEthernet, kNetworkTypeEthernet },
260  { shill::kTypeEthernetEap, kNetworkTypeEthernetEap },
261  { shill::kTypeWifi, kNetworkTypeWifi },
262  { shill::kTypeWimax, kNetworkTypeWimax },
263  { shill::kTypeCellular, kNetworkTypeCellular },
264  { shill::kTypeVPN, kNetworkTypeVPN }
265};
266
267NetworkTypeBitFlag ShillNetworkTypeToFlag(const std::string& shill_type) {
268  for (size_t i = 0; i < arraysize(shill_type_to_flag); ++i) {
269    if (shill_type_to_flag[i].shill_network_type == shill_type)
270      return shill_type_to_flag[i].bit_flag;
271  }
272  NET_LOG_ERROR("ShillNetworkTypeToFlag", "Unknown type: " + shill_type);
273  return kNetworkTypeNone;
274}
275
276}  // namespace
277
278// static
279NetworkTypePattern NetworkTypePattern::Default() {
280  return NetworkTypePattern(~0);
281}
282
283// static
284NetworkTypePattern NetworkTypePattern::Wireless() {
285  return NetworkTypePattern(kNetworkTypeWifi | kNetworkTypeWimax |
286                            kNetworkTypeCellular);
287}
288
289// static
290NetworkTypePattern NetworkTypePattern::Mobile() {
291  return NetworkTypePattern(kNetworkTypeCellular | kNetworkTypeWimax);
292}
293
294// static
295NetworkTypePattern NetworkTypePattern::NonVirtual() {
296  return NetworkTypePattern(~kNetworkTypeVPN);
297}
298
299// static
300NetworkTypePattern NetworkTypePattern::Ethernet() {
301  return NetworkTypePattern(kNetworkTypeEthernet);
302}
303
304// static
305NetworkTypePattern NetworkTypePattern::WiFi() {
306  return NetworkTypePattern(kNetworkTypeWifi);
307}
308
309// static
310NetworkTypePattern NetworkTypePattern::Cellular() {
311  return NetworkTypePattern(kNetworkTypeCellular);
312}
313
314// static
315NetworkTypePattern NetworkTypePattern::VPN() {
316  return NetworkTypePattern(kNetworkTypeVPN);
317}
318
319// static
320NetworkTypePattern NetworkTypePattern::Wimax() {
321  return NetworkTypePattern(kNetworkTypeWimax);
322}
323
324// static
325NetworkTypePattern NetworkTypePattern::Primitive(
326    const std::string& shill_network_type) {
327  return NetworkTypePattern(ShillNetworkTypeToFlag(shill_network_type));
328}
329
330bool NetworkTypePattern::Equals(const NetworkTypePattern& other) const {
331  return pattern_ == other.pattern_;
332}
333
334bool NetworkTypePattern::MatchesType(
335    const std::string& shill_network_type) const {
336  return MatchesPattern(Primitive(shill_network_type));
337}
338
339bool NetworkTypePattern::MatchesPattern(
340    const NetworkTypePattern& other_pattern) const {
341  if (Equals(other_pattern))
342    return true;
343
344  return pattern_ & other_pattern.pattern_;
345}
346
347std::string NetworkTypePattern::ToDebugString() const {
348  if (Equals(Default()))
349    return kPatternDefault;
350  if (Equals(Ethernet()))
351    return kPatternEthernet;
352  if (Equals(Wireless()))
353    return kPatternWireless;
354  if (Equals(Mobile()))
355    return kPatternMobile;
356  if (Equals(NonVirtual()))
357    return kPatternNonVirtual;
358
359  std::string str;
360  for (size_t i = 0; i < arraysize(shill_type_to_flag); ++i) {
361    if (!(pattern_ & shill_type_to_flag[i].bit_flag))
362      continue;
363    if (!str.empty())
364      str += "|";
365    str += shill_type_to_flag[i].shill_network_type;
366  }
367  return str;
368}
369
370NetworkTypePattern::NetworkTypePattern(int pattern) : pattern_(pattern) {}
371
372}  // namespace chromeos
373