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"
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"
22 namespace shill_property_util
{
26 // Replace non UTF8 characters in |str| with a replacement character.
27 std::string
ValidateUTF8(const std::string
& str
) {
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
);
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
);
46 // If existent and non-empty, copies the string at |key| from |source| to
47 // |dest|. Returns true if the string was copied.
48 bool 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()) {
56 dest
->SetStringWithoutPathExpansion(key
, string_value
);
60 // This is the same normalization that Shill applies to security types for the
61 // sake of comparing/identifying WiFi networks. See Shill's
62 // WiFiService::GetSecurityClass.
63 std::string
GetSecurityClass(const std::string
& security
) {
64 if (security
== shill::kSecurityRsn
|| security
== shill::kSecurityWpa
)
65 return shill::kSecurityPsk
;
72 void SetSSID(const std::string ssid
, base::DictionaryValue
* properties
) {
73 std::string hex_ssid
= base::HexEncode(ssid
.c_str(), ssid
.size());
74 properties
->SetStringWithoutPathExpansion(shill::kWifiHexSsid
, hex_ssid
);
77 std::string
GetSSIDFromProperties(const base::DictionaryValue
& properties
,
78 bool* unknown_encoding
) {
79 bool verbose_logging
= false;
80 if (unknown_encoding
) {
81 *unknown_encoding
= false;
82 verbose_logging
= true;
85 // Get name for debugging.
87 properties
.GetStringWithoutPathExpansion(shill::kNameProperty
, &name
);
90 properties
.GetStringWithoutPathExpansion(shill::kWifiHexSsid
, &hex_ssid
);
92 if (hex_ssid
.empty()) {
94 NET_LOG_DEBUG("GetSSIDFromProperties: No HexSSID set.", name
);
99 std::vector
<uint8
> raw_ssid_bytes
;
100 if (base::HexStringToBytes(hex_ssid
, &raw_ssid_bytes
)) {
101 ssid
= std::string(raw_ssid_bytes
.begin(), raw_ssid_bytes
.end());
102 if (verbose_logging
) {
103 NET_LOG_DEBUG(base::StringPrintf("GetSSIDFromProperties: %s, SSID: %s",
104 hex_ssid
.c_str(), ssid
.c_str()), name
);
108 base::StringPrintf("GetSSIDFromProperties: Error processing: %s",
109 hex_ssid
.c_str()), name
);
110 return std::string();
113 if (base::IsStringUTF8(ssid
))
116 // Detect encoding and convert to UTF-8.
117 std::string encoding
;
118 if (!base::DetectEncoding(ssid
, &encoding
)) {
119 // TODO(stevenjb): This is currently experimental. If we find a case where
120 // base::DetectEncoding() fails, we need to figure out whether we can use
121 // country_code with ConvertToUtf8(). crbug.com/233267.
122 properties
.GetStringWithoutPathExpansion(shill::kCountryProperty
,
125 std::string utf8_ssid
;
126 if (!encoding
.empty() &&
127 base::ConvertToUtf8AndNormalize(ssid
, encoding
, &utf8_ssid
)) {
128 if (utf8_ssid
!= ssid
) {
129 if (verbose_logging
) {
131 base::StringPrintf("GetSSIDFromProperties: Encoding=%s: %s",
132 encoding
.c_str(), utf8_ssid
.c_str()), name
);
138 if (unknown_encoding
)
139 *unknown_encoding
= true;
140 if (verbose_logging
) {
142 base::StringPrintf("GetSSIDFromProperties: Unrecognized Encoding=%s",
143 encoding
.c_str()), name
);
148 std::string
GetNetworkIdFromProperties(
149 const base::DictionaryValue
& properties
) {
150 if (properties
.empty())
151 return "EmptyProperties";
153 if (properties
.GetStringWithoutPathExpansion(shill::kGuidProperty
, &result
))
155 if (properties
.GetStringWithoutPathExpansion(shill::kSSIDProperty
, &result
))
157 if (properties
.GetStringWithoutPathExpansion(shill::kNameProperty
, &result
))
159 std::string type
= "UnknownType";
160 properties
.GetStringWithoutPathExpansion(shill::kTypeProperty
, &type
);
161 return "Unidentified " + type
;
164 std::string
GetNameFromProperties(const std::string
& service_path
,
165 const base::DictionaryValue
& properties
) {
167 properties
.GetStringWithoutPathExpansion(shill::kNameProperty
, &name
);
169 std::string validated_name
= ValidateUTF8(name
);
170 if (validated_name
!= name
) {
171 NET_LOG_DEBUG("GetNameFromProperties",
172 base::StringPrintf("Validated name %s: UTF8: %s",
173 service_path
.c_str(),
174 validated_name
.c_str()));
178 properties
.GetStringWithoutPathExpansion(shill::kTypeProperty
, &type
);
180 NET_LOG_ERROR("GetNameFromProperties: No type", service_path
);
181 return validated_name
;
183 if (!NetworkTypePattern::WiFi().MatchesType(type
))
184 return validated_name
;
186 bool unknown_ssid_encoding
= false;
187 std::string ssid
= GetSSIDFromProperties(properties
, &unknown_ssid_encoding
);
189 NET_LOG_ERROR("GetNameFromProperties", "No SSID set: " + service_path
);
191 // Use |validated_name| if |ssid| is empty.
192 // And if the encoding of the SSID is unknown, use |ssid|, which contains raw
193 // bytes in that case, only if |validated_name| is empty.
194 if (ssid
.empty() || (unknown_ssid_encoding
&& !validated_name
.empty()))
195 return validated_name
;
197 if (ssid
!= validated_name
) {
198 NET_LOG_DEBUG("GetNameFromProperties",
199 base::StringPrintf("%s: SSID: %s, Name: %s",
200 service_path
.c_str(),
202 validated_name
.c_str()));
207 scoped_ptr
<NetworkUIData
> GetUIDataFromValue(const base::Value
& ui_data_value
) {
208 std::string ui_data_str
;
209 if (!ui_data_value
.GetAsString(&ui_data_str
))
210 return scoped_ptr
<NetworkUIData
>();
211 if (ui_data_str
.empty())
212 return make_scoped_ptr(new NetworkUIData());
213 scoped_ptr
<base::DictionaryValue
> ui_data_dict(
214 chromeos::onc::ReadDictionaryFromJson(ui_data_str
));
216 return scoped_ptr
<NetworkUIData
>();
217 return make_scoped_ptr(new NetworkUIData(*ui_data_dict
));
220 scoped_ptr
<NetworkUIData
> GetUIDataFromProperties(
221 const base::DictionaryValue
& shill_dictionary
) {
222 const base::Value
* ui_data_value
= NULL
;
223 shill_dictionary
.GetWithoutPathExpansion(shill::kUIDataProperty
,
225 if (!ui_data_value
) {
226 VLOG(2) << "Dictionary has no UIData entry.";
227 return scoped_ptr
<NetworkUIData
>();
229 scoped_ptr
<NetworkUIData
> ui_data
= GetUIDataFromValue(*ui_data_value
);
231 LOG(ERROR
) << "UIData is not a valid JSON dictionary.";
232 return ui_data
.Pass();
235 void SetUIData(const NetworkUIData
& ui_data
,
236 base::DictionaryValue
* shill_dictionary
) {
237 base::DictionaryValue ui_data_dict
;
238 ui_data
.FillDictionary(&ui_data_dict
);
239 std::string ui_data_blob
;
240 base::JSONWriter::Write(&ui_data_dict
, &ui_data_blob
);
241 shill_dictionary
->SetStringWithoutPathExpansion(shill::kUIDataProperty
,
245 bool CopyIdentifyingProperties(const base::DictionaryValue
& service_properties
,
246 const bool properties_read_from_shill
,
247 base::DictionaryValue
* dest
) {
251 CopyStringFromDictionary(service_properties
, shill::kGuidProperty
, dest
);
254 service_properties
.GetStringWithoutPathExpansion(shill::kTypeProperty
, &type
);
255 success
&= !type
.empty();
256 dest
->SetStringWithoutPathExpansion(shill::kTypeProperty
, type
);
257 if (type
== shill::kTypeWifi
) {
258 std::string security
;
259 service_properties
.GetStringWithoutPathExpansion(shill::kSecurityProperty
,
261 if (security
.empty()) {
264 dest
->SetStringWithoutPathExpansion(shill::kSecurityProperty
,
265 GetSecurityClass(security
));
268 CopyStringFromDictionary(service_properties
, shill::kWifiHexSsid
, dest
);
269 success
&= CopyStringFromDictionary(
270 service_properties
, shill::kModeProperty
, dest
);
271 } else if (type
== shill::kTypeVPN
) {
272 success
&= CopyStringFromDictionary(
273 service_properties
, shill::kNameProperty
, dest
);
275 // VPN Provider values are read from the "Provider" dictionary, but written
276 // with the keys "Provider.Type" and "Provider.Host".
277 // TODO(pneubeck): Simplify this once http://crbug.com/381135 is fixed.
278 std::string vpn_provider_type
;
279 std::string vpn_provider_host
;
280 if (properties_read_from_shill
) {
281 const base::DictionaryValue
* provider_properties
= NULL
;
282 if (!service_properties
.GetDictionaryWithoutPathExpansion(
283 shill::kProviderProperty
, &provider_properties
)) {
284 NET_LOG_ERROR("Missing VPN provider dict",
285 GetNetworkIdFromProperties(service_properties
));
287 provider_properties
->GetStringWithoutPathExpansion(shill::kTypeProperty
,
289 provider_properties
->GetStringWithoutPathExpansion(shill::kHostProperty
,
292 service_properties
.GetStringWithoutPathExpansion(
293 shill::kProviderTypeProperty
, &vpn_provider_type
);
294 service_properties
.GetStringWithoutPathExpansion(
295 shill::kProviderHostProperty
, &vpn_provider_host
);
297 success
&= !vpn_provider_type
.empty();
298 dest
->SetStringWithoutPathExpansion(shill::kProviderTypeProperty
,
301 success
&= !vpn_provider_host
.empty();
302 dest
->SetStringWithoutPathExpansion(shill::kProviderHostProperty
,
304 } else if (type
== shill::kTypeEthernet
|| type
== shill::kTypeEthernetEap
) {
305 // Ethernet and EthernetEAP don't have any additional identifying
308 NOTREACHED() << "Unsupported network type " << type
;
312 NET_LOG_ERROR("Missing required properties",
313 GetNetworkIdFromProperties(service_properties
));
318 bool DoIdentifyingPropertiesMatch(const base::DictionaryValue
& new_properties
,
319 const base::DictionaryValue
& old_properties
) {
320 base::DictionaryValue new_identifying
;
321 if (!CopyIdentifyingProperties(
323 false /* properties were not read from Shill */,
327 base::DictionaryValue old_identifying
;
328 if (!CopyIdentifyingProperties(old_properties
,
329 true /* properties were read from Shill */,
334 return new_identifying
.Equals(&old_identifying
);
337 bool IsPassphraseKey(const std::string
& key
) {
338 return key
== shill::kEapPrivateKeyPasswordProperty
||
339 key
== shill::kEapPasswordProperty
||
340 key
== shill::kL2tpIpsecPasswordProperty
||
341 key
== shill::kOpenVPNPasswordProperty
||
342 key
== shill::kOpenVPNAuthUserPassProperty
||
343 key
== shill::kOpenVPNTLSAuthContentsProperty
||
344 key
== shill::kPassphraseProperty
||
345 key
== shill::kOpenVPNOTPProperty
||
346 key
== shill::kEapPrivateKeyProperty
||
347 key
== shill::kEapPinProperty
||
348 key
== shill::kApnPasswordProperty
;
351 bool GetHomeProviderFromProperty(const base::Value
& value
,
352 std::string
* home_provider_id
) {
353 const base::DictionaryValue
* dict
= NULL
;
354 if (!value
.GetAsDictionary(&dict
))
356 std::string home_provider_country
;
357 std::string home_provider_name
;
358 dict
->GetStringWithoutPathExpansion(shill::kOperatorCountryKey
,
359 &home_provider_country
);
360 dict
->GetStringWithoutPathExpansion(shill::kOperatorNameKey
,
361 &home_provider_name
);
362 // Set home_provider_id
363 if (!home_provider_name
.empty() && !home_provider_country
.empty()) {
364 *home_provider_id
= base::StringPrintf(
365 "%s (%s)", home_provider_name
.c_str(), home_provider_country
.c_str());
367 if (!dict
->GetStringWithoutPathExpansion(shill::kOperatorCodeKey
,
372 << "Provider name and country not defined, using code instead: "
373 << *home_provider_id
;
378 } // namespace shill_property_util
380 } // namespace chromeos