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_ui_data.h"
16 #include "chromeos/network/onc/onc_utils.h"
17 #include "components/device_event_log/device_event_log.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
);
62 void SetSSID(const std::string ssid
, base::DictionaryValue
* properties
) {
63 std::string hex_ssid
= base::HexEncode(ssid
.c_str(), ssid
.size());
64 properties
->SetStringWithoutPathExpansion(shill::kWifiHexSsid
, hex_ssid
);
67 std::string
GetSSIDFromProperties(const base::DictionaryValue
& properties
,
69 bool* unknown_encoding
) {
71 *unknown_encoding
= false;
73 // Get name for debugging.
75 properties
.GetStringWithoutPathExpansion(shill::kNameProperty
, &name
);
78 properties
.GetStringWithoutPathExpansion(shill::kWifiHexSsid
, &hex_ssid
);
80 if (hex_ssid
.empty()) {
82 NET_LOG(DEBUG
) << "GetSSIDFromProperties: No HexSSID set: " << name
;
87 std::vector
<uint8
> raw_ssid_bytes
;
88 if (base::HexStringToBytes(hex_ssid
, &raw_ssid_bytes
)) {
89 ssid
= std::string(raw_ssid_bytes
.begin(), raw_ssid_bytes
.end());
90 VLOG(2) << "GetSSIDFromProperties: " << name
<< " HexSsid=" << hex_ssid
93 NET_LOG(ERROR
) << "GetSSIDFromProperties: " << name
94 << " Error processing HexSsid: " << hex_ssid
;
98 if (base::IsStringUTF8(ssid
))
101 // Detect encoding and convert to UTF-8.
102 std::string encoding
;
103 if (!base::DetectEncoding(ssid
, &encoding
)) {
104 // TODO(stevenjb): This is currently experimental. If we find a case where
105 // base::DetectEncoding() fails, we need to figure out whether we can use
106 // country_code with ConvertToUtf8(). crbug.com/233267.
107 properties
.GetStringWithoutPathExpansion(shill::kCountryProperty
,
110 std::string utf8_ssid
;
111 if (!encoding
.empty() &&
112 base::ConvertToUtf8AndNormalize(ssid
, encoding
, &utf8_ssid
)) {
113 if (utf8_ssid
!= ssid
) {
114 if (verbose_logging
) {
115 NET_LOG(DEBUG
) << "GetSSIDFromProperties: " << name
116 << " Encoding=" << encoding
<< " SSID=" << ssid
117 << " UTF8 SSID=" << utf8_ssid
;
123 if (unknown_encoding
)
124 *unknown_encoding
= true;
125 if (verbose_logging
) {
126 NET_LOG(DEBUG
) << "GetSSIDFromProperties: " << name
127 << " Unrecognized Encoding=" << encoding
;
132 std::string
GetNetworkIdFromProperties(
133 const base::DictionaryValue
& properties
) {
134 if (properties
.empty())
135 return "EmptyProperties";
137 if (properties
.GetStringWithoutPathExpansion(shill::kGuidProperty
, &result
))
139 if (properties
.GetStringWithoutPathExpansion(shill::kSSIDProperty
, &result
))
141 if (properties
.GetStringWithoutPathExpansion(shill::kNameProperty
, &result
))
143 std::string type
= "UnknownType";
144 properties
.GetStringWithoutPathExpansion(shill::kTypeProperty
, &type
);
145 return "Unidentified " + type
;
148 std::string
GetNameFromProperties(const std::string
& service_path
,
149 const base::DictionaryValue
& properties
) {
151 properties
.GetStringWithoutPathExpansion(shill::kNameProperty
, &name
);
153 std::string validated_name
= ValidateUTF8(name
);
154 if (validated_name
!= name
) {
155 NET_LOG(DEBUG
) << "GetNameFromProperties: " << service_path
156 << " Validated name=" << validated_name
<< " name=" << name
;
160 properties
.GetStringWithoutPathExpansion(shill::kTypeProperty
, &type
);
162 NET_LOG(ERROR
) << "GetNameFromProperties: " << service_path
<< " No type.";
163 return validated_name
;
165 if (!NetworkTypePattern::WiFi().MatchesType(type
))
166 return validated_name
;
168 bool unknown_ssid_encoding
= false;
169 std::string ssid
= GetSSIDFromProperties(
170 properties
, true /* verbose_logging */, &unknown_ssid_encoding
);
172 NET_LOG(ERROR
) << "GetNameFromProperties: " << service_path
176 // Use |validated_name| if |ssid| is empty.
177 // And if the encoding of the SSID is unknown, use |ssid|, which contains raw
178 // bytes in that case, only if |validated_name| is empty.
179 if (ssid
.empty() || (unknown_ssid_encoding
&& !validated_name
.empty()))
180 return validated_name
;
182 if (ssid
!= validated_name
) {
183 NET_LOG(DEBUG
) << "GetNameFromProperties: " << service_path
184 << " SSID=" << ssid
<< " Validated name=" << validated_name
;
189 scoped_ptr
<NetworkUIData
> GetUIDataFromValue(const base::Value
& ui_data_value
) {
190 std::string ui_data_str
;
191 if (!ui_data_value
.GetAsString(&ui_data_str
))
192 return scoped_ptr
<NetworkUIData
>();
193 if (ui_data_str
.empty())
194 return make_scoped_ptr(new NetworkUIData());
195 scoped_ptr
<base::DictionaryValue
> ui_data_dict(
196 chromeos::onc::ReadDictionaryFromJson(ui_data_str
));
198 return scoped_ptr
<NetworkUIData
>();
199 return make_scoped_ptr(new NetworkUIData(*ui_data_dict
));
202 scoped_ptr
<NetworkUIData
> GetUIDataFromProperties(
203 const base::DictionaryValue
& shill_dictionary
) {
204 const base::Value
* ui_data_value
= NULL
;
205 shill_dictionary
.GetWithoutPathExpansion(shill::kUIDataProperty
,
207 if (!ui_data_value
) {
208 VLOG(2) << "Dictionary has no UIData entry.";
209 return scoped_ptr
<NetworkUIData
>();
211 scoped_ptr
<NetworkUIData
> ui_data
= GetUIDataFromValue(*ui_data_value
);
213 LOG(ERROR
) << "UIData is not a valid JSON dictionary.";
214 return ui_data
.Pass();
217 void SetUIData(const NetworkUIData
& ui_data
,
218 base::DictionaryValue
* shill_dictionary
) {
219 base::DictionaryValue ui_data_dict
;
220 ui_data
.FillDictionary(&ui_data_dict
);
221 std::string ui_data_blob
;
222 base::JSONWriter::Write(&ui_data_dict
, &ui_data_blob
);
223 shill_dictionary
->SetStringWithoutPathExpansion(shill::kUIDataProperty
,
227 bool CopyIdentifyingProperties(const base::DictionaryValue
& service_properties
,
228 const bool properties_read_from_shill
,
229 base::DictionaryValue
* dest
) {
233 CopyStringFromDictionary(service_properties
, shill::kGuidProperty
, dest
);
236 service_properties
.GetStringWithoutPathExpansion(shill::kTypeProperty
, &type
);
237 success
&= !type
.empty();
238 dest
->SetStringWithoutPathExpansion(shill::kTypeProperty
, type
);
239 if (type
== shill::kTypeWifi
) {
241 CopyStringFromDictionary(
242 service_properties
, shill::kSecurityClassProperty
, dest
);
244 CopyStringFromDictionary(service_properties
, shill::kWifiHexSsid
, dest
);
245 success
&= CopyStringFromDictionary(
246 service_properties
, shill::kModeProperty
, dest
);
247 } else if (type
== shill::kTypeCellular
) {
248 success
&= CopyStringFromDictionary(
249 service_properties
, shill::kNetworkTechnologyProperty
, dest
);
250 } else if (type
== shill::kTypeVPN
) {
251 success
&= CopyStringFromDictionary(
252 service_properties
, shill::kNameProperty
, dest
);
254 // VPN Provider values are read from the "Provider" dictionary, but written
255 // with the keys "Provider.Type" and "Provider.Host".
256 // TODO(pneubeck): Simplify this once http://crbug.com/381135 is fixed.
257 std::string vpn_provider_type
;
258 std::string vpn_provider_host
;
259 if (properties_read_from_shill
) {
260 const base::DictionaryValue
* provider_properties
= NULL
;
261 if (!service_properties
.GetDictionaryWithoutPathExpansion(
262 shill::kProviderProperty
, &provider_properties
)) {
263 NET_LOG(ERROR
) << "Missing VPN provider dict: "
264 << GetNetworkIdFromProperties(service_properties
);
266 provider_properties
->GetStringWithoutPathExpansion(shill::kTypeProperty
,
268 provider_properties
->GetStringWithoutPathExpansion(shill::kHostProperty
,
271 service_properties
.GetStringWithoutPathExpansion(
272 shill::kProviderTypeProperty
, &vpn_provider_type
);
273 service_properties
.GetStringWithoutPathExpansion(
274 shill::kProviderHostProperty
, &vpn_provider_host
);
276 success
&= !vpn_provider_type
.empty();
277 dest
->SetStringWithoutPathExpansion(shill::kProviderTypeProperty
,
280 success
&= !vpn_provider_host
.empty();
281 dest
->SetStringWithoutPathExpansion(shill::kProviderHostProperty
,
283 } else if (type
== shill::kTypeEthernet
|| type
== shill::kTypeEthernetEap
) {
284 // Ethernet and EthernetEAP don't have any additional identifying
287 NOTREACHED() << "Unsupported network type " << type
;
291 NET_LOG(ERROR
) << "Missing required properties: "
292 << GetNetworkIdFromProperties(service_properties
);
297 bool DoIdentifyingPropertiesMatch(const base::DictionaryValue
& new_properties
,
298 const base::DictionaryValue
& old_properties
) {
299 base::DictionaryValue new_identifying
;
300 if (!CopyIdentifyingProperties(
302 false /* properties were not read from Shill */,
306 base::DictionaryValue old_identifying
;
307 if (!CopyIdentifyingProperties(old_properties
,
308 true /* properties were read from Shill */,
313 return new_identifying
.Equals(&old_identifying
);
316 bool IsPassphraseKey(const std::string
& key
) {
317 return key
== shill::kEapPrivateKeyPasswordProperty
||
318 key
== shill::kEapPasswordProperty
||
319 key
== shill::kL2tpIpsecPasswordProperty
||
320 key
== shill::kOpenVPNPasswordProperty
||
321 key
== shill::kOpenVPNAuthUserPassProperty
||
322 key
== shill::kOpenVPNTLSAuthContentsProperty
||
323 key
== shill::kPassphraseProperty
||
324 key
== shill::kOpenVPNOTPProperty
||
325 key
== shill::kEapPrivateKeyProperty
||
326 key
== shill::kEapPinProperty
||
327 key
== shill::kApnPasswordProperty
;
330 bool GetHomeProviderFromProperty(const base::Value
& value
,
331 std::string
* home_provider_id
) {
332 const base::DictionaryValue
* dict
= NULL
;
333 if (!value
.GetAsDictionary(&dict
))
335 std::string home_provider_country
;
336 std::string home_provider_name
;
337 dict
->GetStringWithoutPathExpansion(shill::kOperatorCountryKey
,
338 &home_provider_country
);
339 dict
->GetStringWithoutPathExpansion(shill::kOperatorNameKey
,
340 &home_provider_name
);
341 // Set home_provider_id
342 if (!home_provider_name
.empty() && !home_provider_country
.empty()) {
343 *home_provider_id
= base::StringPrintf(
344 "%s (%s)", home_provider_name
.c_str(), home_provider_country
.c_str());
346 if (!dict
->GetStringWithoutPathExpansion(shill::kOperatorCodeKey
,
351 << "Provider name and country not defined, using code instead: "
352 << *home_provider_id
;
357 } // namespace shill_property_util
359 } // namespace chromeos