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.
5 #include "chromeos/network/shill_property_util.h"
9 #include "base/i18n/icu_encoding_detection.h"
10 #include "base/i18n/icu_string_conversions.h"
11 #include "base/json/json_writer.h"
12 #include "base/strings/string_number_conversions.h"
13 #include "base/strings/string_util.h"
14 #include "base/strings/stringprintf.h"
15 #include "base/strings/utf_string_conversion_utils.h"
16 #include "base/values.h"
17 #include "chromeos/network/network_ui_data.h"
18 #include "chromeos/network/onc/onc_utils.h"
19 #include "components/device_event_log/device_event_log.h"
20 #include "third_party/cros_system_api/dbus/service_constants.h"
24 namespace shill_property_util
{
28 // Replace non UTF8 characters in |str| with a replacement character.
29 std::string
ValidateUTF8(const std::string
& str
) {
31 for (int32 index
= 0; index
< static_cast<int32
>(str
.size()); ++index
) {
32 uint32 code_point_out
;
33 bool is_unicode_char
= base::ReadUnicodeCharacter(
34 str
.c_str(), str
.size(), &index
, &code_point_out
);
35 const uint32 kFirstNonControlChar
= 0x20;
36 if (is_unicode_char
&& (code_point_out
>= kFirstNonControlChar
)) {
37 base::WriteUnicodeCharacter(code_point_out
, &result
);
39 const uint32 kReplacementChar
= 0xFFFD;
40 // Puts kReplacementChar if character is a control character [0,0x20)
41 // or is not readable UTF8.
42 base::WriteUnicodeCharacter(kReplacementChar
, &result
);
48 // If existent and non-empty, copies the string at |key| from |source| to
49 // |dest|. Returns true if the string was copied.
50 bool CopyStringFromDictionary(const base::DictionaryValue
& source
,
51 const std::string
& key
,
52 base::DictionaryValue
* dest
) {
53 std::string string_value
;
54 if (!source
.GetStringWithoutPathExpansion(key
, &string_value
) ||
55 string_value
.empty()) {
58 dest
->SetStringWithoutPathExpansion(key
, string_value
);
64 void SetSSID(const std::string ssid
, base::DictionaryValue
* properties
) {
65 std::string hex_ssid
= base::HexEncode(ssid
.c_str(), ssid
.size());
66 properties
->SetStringWithoutPathExpansion(shill::kWifiHexSsid
, hex_ssid
);
69 std::string
GetSSIDFromProperties(const base::DictionaryValue
& properties
,
71 bool* unknown_encoding
) {
73 *unknown_encoding
= false;
75 // Get name for debugging.
77 properties
.GetStringWithoutPathExpansion(shill::kNameProperty
, &name
);
80 properties
.GetStringWithoutPathExpansion(shill::kWifiHexSsid
, &hex_ssid
);
82 if (hex_ssid
.empty()) {
84 NET_LOG(DEBUG
) << "GetSSIDFromProperties: No HexSSID set: " << name
;
89 std::vector
<uint8
> raw_ssid_bytes
;
90 if (base::HexStringToBytes(hex_ssid
, &raw_ssid_bytes
)) {
91 ssid
= std::string(raw_ssid_bytes
.begin(), raw_ssid_bytes
.end());
92 VLOG(2) << "GetSSIDFromProperties: " << name
<< " HexSsid=" << hex_ssid
95 NET_LOG(ERROR
) << "GetSSIDFromProperties: " << name
96 << " Error processing HexSsid: " << hex_ssid
;
100 if (base::IsStringUTF8(ssid
))
103 // Detect encoding and convert to UTF-8.
104 std::string encoding
;
105 if (!base::DetectEncoding(ssid
, &encoding
)) {
106 // TODO(stevenjb): This is currently experimental. If we find a case where
107 // base::DetectEncoding() fails, we need to figure out whether we can use
108 // country_code with ConvertToUtf8(). crbug.com/233267.
109 properties
.GetStringWithoutPathExpansion(shill::kCountryProperty
,
112 std::string utf8_ssid
;
113 if (!encoding
.empty() &&
114 base::ConvertToUtf8AndNormalize(ssid
, encoding
, &utf8_ssid
)) {
115 if (utf8_ssid
!= ssid
) {
116 if (verbose_logging
) {
117 NET_LOG(DEBUG
) << "GetSSIDFromProperties: " << name
118 << " Encoding=" << encoding
<< " SSID=" << ssid
119 << " UTF8 SSID=" << utf8_ssid
;
125 if (unknown_encoding
)
126 *unknown_encoding
= true;
127 if (verbose_logging
) {
128 NET_LOG(DEBUG
) << "GetSSIDFromProperties: " << name
129 << " Unrecognized Encoding=" << encoding
;
134 std::string
GetNetworkIdFromProperties(
135 const base::DictionaryValue
& properties
) {
136 if (properties
.empty())
137 return "EmptyProperties";
139 if (properties
.GetStringWithoutPathExpansion(shill::kGuidProperty
, &result
))
141 if (properties
.GetStringWithoutPathExpansion(shill::kSSIDProperty
, &result
))
143 if (properties
.GetStringWithoutPathExpansion(shill::kNameProperty
, &result
))
145 std::string type
= "UnknownType";
146 properties
.GetStringWithoutPathExpansion(shill::kTypeProperty
, &type
);
147 return "Unidentified " + type
;
150 std::string
GetNameFromProperties(const std::string
& service_path
,
151 const base::DictionaryValue
& properties
) {
153 properties
.GetStringWithoutPathExpansion(shill::kNameProperty
, &name
);
155 std::string validated_name
= ValidateUTF8(name
);
156 if (validated_name
!= name
) {
157 NET_LOG(DEBUG
) << "GetNameFromProperties: " << service_path
158 << " Validated name=" << validated_name
<< " name=" << name
;
162 properties
.GetStringWithoutPathExpansion(shill::kTypeProperty
, &type
);
164 NET_LOG(ERROR
) << "GetNameFromProperties: " << service_path
<< " No type.";
165 return validated_name
;
167 if (!NetworkTypePattern::WiFi().MatchesType(type
))
168 return validated_name
;
170 bool unknown_ssid_encoding
= false;
171 std::string ssid
= GetSSIDFromProperties(
172 properties
, true /* verbose_logging */, &unknown_ssid_encoding
);
174 NET_LOG(ERROR
) << "GetNameFromProperties: " << service_path
178 // Use |validated_name| if |ssid| is empty.
179 // And if the encoding of the SSID is unknown, use |ssid|, which contains raw
180 // bytes in that case, only if |validated_name| is empty.
181 if (ssid
.empty() || (unknown_ssid_encoding
&& !validated_name
.empty()))
182 return validated_name
;
184 if (ssid
!= validated_name
) {
185 NET_LOG(DEBUG
) << "GetNameFromProperties: " << service_path
186 << " SSID=" << ssid
<< " Validated name=" << validated_name
;
191 scoped_ptr
<NetworkUIData
> GetUIDataFromValue(const base::Value
& ui_data_value
) {
192 std::string ui_data_str
;
193 if (!ui_data_value
.GetAsString(&ui_data_str
))
194 return scoped_ptr
<NetworkUIData
>();
195 if (ui_data_str
.empty())
196 return make_scoped_ptr(new NetworkUIData());
197 scoped_ptr
<base::DictionaryValue
> ui_data_dict(
198 chromeos::onc::ReadDictionaryFromJson(ui_data_str
));
200 return scoped_ptr
<NetworkUIData
>();
201 return make_scoped_ptr(new NetworkUIData(*ui_data_dict
));
204 scoped_ptr
<NetworkUIData
> GetUIDataFromProperties(
205 const base::DictionaryValue
& shill_dictionary
) {
206 const base::Value
* ui_data_value
= NULL
;
207 shill_dictionary
.GetWithoutPathExpansion(shill::kUIDataProperty
,
209 if (!ui_data_value
) {
210 VLOG(2) << "Dictionary has no UIData entry.";
211 return scoped_ptr
<NetworkUIData
>();
213 scoped_ptr
<NetworkUIData
> ui_data
= GetUIDataFromValue(*ui_data_value
);
215 LOG(ERROR
) << "UIData is not a valid JSON dictionary.";
216 return ui_data
.Pass();
219 void SetUIData(const NetworkUIData
& ui_data
,
220 base::DictionaryValue
* shill_dictionary
) {
221 base::DictionaryValue ui_data_dict
;
222 ui_data
.FillDictionary(&ui_data_dict
);
223 std::string ui_data_blob
;
224 base::JSONWriter::Write(&ui_data_dict
, &ui_data_blob
);
225 shill_dictionary
->SetStringWithoutPathExpansion(shill::kUIDataProperty
,
229 bool CopyIdentifyingProperties(const base::DictionaryValue
& service_properties
,
230 const bool properties_read_from_shill
,
231 base::DictionaryValue
* dest
) {
235 CopyStringFromDictionary(service_properties
, shill::kGuidProperty
, dest
);
238 service_properties
.GetStringWithoutPathExpansion(shill::kTypeProperty
, &type
);
239 success
&= !type
.empty();
240 dest
->SetStringWithoutPathExpansion(shill::kTypeProperty
, type
);
241 if (type
== shill::kTypeWifi
) {
243 CopyStringFromDictionary(
244 service_properties
, shill::kSecurityClassProperty
, dest
);
246 CopyStringFromDictionary(service_properties
, shill::kWifiHexSsid
, dest
);
247 success
&= CopyStringFromDictionary(
248 service_properties
, shill::kModeProperty
, dest
);
249 } else if (type
== shill::kTypeCellular
) {
250 success
&= CopyStringFromDictionary(
251 service_properties
, shill::kNetworkTechnologyProperty
, dest
);
252 } else if (type
== shill::kTypeVPN
) {
253 success
&= CopyStringFromDictionary(
254 service_properties
, shill::kNameProperty
, dest
);
256 // VPN Provider values are read from the "Provider" dictionary, but written
257 // with the keys "Provider.Type" and "Provider.Host".
258 // TODO(pneubeck): Simplify this once http://crbug.com/381135 is fixed.
259 std::string vpn_provider_type
;
260 std::string vpn_provider_host
;
261 if (properties_read_from_shill
) {
262 const base::DictionaryValue
* provider_properties
= NULL
;
263 if (!service_properties
.GetDictionaryWithoutPathExpansion(
264 shill::kProviderProperty
, &provider_properties
)) {
265 NET_LOG(ERROR
) << "Missing VPN provider dict: "
266 << GetNetworkIdFromProperties(service_properties
);
268 provider_properties
->GetStringWithoutPathExpansion(shill::kTypeProperty
,
270 provider_properties
->GetStringWithoutPathExpansion(shill::kHostProperty
,
273 service_properties
.GetStringWithoutPathExpansion(
274 shill::kProviderTypeProperty
, &vpn_provider_type
);
275 service_properties
.GetStringWithoutPathExpansion(
276 shill::kProviderHostProperty
, &vpn_provider_host
);
278 success
&= !vpn_provider_type
.empty();
279 dest
->SetStringWithoutPathExpansion(shill::kProviderTypeProperty
,
282 success
&= !vpn_provider_host
.empty();
283 dest
->SetStringWithoutPathExpansion(shill::kProviderHostProperty
,
285 } else if (type
== shill::kTypeEthernet
|| type
== shill::kTypeEthernetEap
) {
286 // Ethernet and EthernetEAP don't have any additional identifying
289 NOTREACHED() << "Unsupported network type " << type
;
293 NET_LOG(ERROR
) << "Missing required properties: "
294 << GetNetworkIdFromProperties(service_properties
);
299 bool DoIdentifyingPropertiesMatch(const base::DictionaryValue
& new_properties
,
300 const base::DictionaryValue
& old_properties
) {
301 base::DictionaryValue new_identifying
;
302 if (!CopyIdentifyingProperties(
304 false /* properties were not read from Shill */,
308 base::DictionaryValue old_identifying
;
309 if (!CopyIdentifyingProperties(old_properties
,
310 true /* properties were read from Shill */,
315 return new_identifying
.Equals(&old_identifying
);
318 bool IsLoggableShillProperty(const std::string
& key
) {
319 static std::set
<std::string
>* s_skip_properties
= nullptr;
320 if (!s_skip_properties
) {
321 s_skip_properties
= new std::set
<std::string
>;
322 s_skip_properties
->insert(shill::kApnPasswordProperty
);
323 s_skip_properties
->insert(shill::kEapCaCertNssProperty
);
324 s_skip_properties
->insert(shill::kEapCaCertPemProperty
);
325 s_skip_properties
->insert(shill::kEapCaCertProperty
);
326 s_skip_properties
->insert(shill::kEapClientCertNssProperty
);
327 s_skip_properties
->insert(shill::kEapClientCertProperty
);
328 s_skip_properties
->insert(shill::kEapPasswordProperty
);
329 s_skip_properties
->insert(shill::kEapPinProperty
);
330 s_skip_properties
->insert(shill::kEapPrivateKeyPasswordProperty
);
331 s_skip_properties
->insert(shill::kEapPrivateKeyProperty
);
332 s_skip_properties
->insert(shill::kL2tpIpsecCaCertPemProperty
);
333 s_skip_properties
->insert(shill::kL2tpIpsecPasswordProperty
);
334 s_skip_properties
->insert(shill::kL2tpIpsecPinProperty
);
335 s_skip_properties
->insert(shill::kL2tpIpsecPskProperty
);
336 s_skip_properties
->insert(shill::kOpenVPNAuthUserPassProperty
);
337 s_skip_properties
->insert(shill::kOpenVPNCaCertNSSProperty
);
338 s_skip_properties
->insert(shill::kOpenVPNCaCertPemProperty
);
339 s_skip_properties
->insert(shill::kOpenVPNCaCertProperty
);
340 s_skip_properties
->insert(shill::kOpenVPNCertProperty
);
341 s_skip_properties
->insert(shill::kOpenVPNExtraCertPemProperty
);
342 s_skip_properties
->insert(shill::kOpenVPNOTPProperty
);
343 s_skip_properties
->insert(shill::kOpenVPNPasswordProperty
);
344 s_skip_properties
->insert(shill::kOpenVPNPinProperty
);
345 s_skip_properties
->insert(shill::kOpenVPNTLSAuthContentsProperty
);
346 s_skip_properties
->insert(shill::kPPPoEPasswordProperty
);
347 s_skip_properties
->insert(shill::kPassphraseProperty
);
349 return s_skip_properties
->count(key
) == 0;
352 bool GetHomeProviderFromProperty(const base::Value
& value
,
353 std::string
* home_provider_id
) {
354 const base::DictionaryValue
* dict
= NULL
;
355 if (!value
.GetAsDictionary(&dict
))
357 std::string home_provider_country
;
358 std::string home_provider_name
;
359 dict
->GetStringWithoutPathExpansion(shill::kOperatorCountryKey
,
360 &home_provider_country
);
361 dict
->GetStringWithoutPathExpansion(shill::kOperatorNameKey
,
362 &home_provider_name
);
363 // Set home_provider_id
364 if (!home_provider_name
.empty() && !home_provider_country
.empty()) {
365 *home_provider_id
= base::StringPrintf(
366 "%s (%s)", home_provider_name
.c_str(), home_provider_country
.c_str());
368 if (!dict
->GetStringWithoutPathExpansion(shill::kOperatorCodeKey
,
373 << "Provider name and country not defined, using code instead: "
374 << *home_provider_id
;
379 } // namespace shill_property_util
381 } // namespace chromeos