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
) ||
55 dest
->SetStringWithoutPathExpansion(key
, string_value
);
61 void SetSSID(const std::string ssid
, base::DictionaryValue
* properties
) {
62 std::string hex_ssid
= base::HexEncode(ssid
.c_str(), ssid
.size());
63 properties
->SetStringWithoutPathExpansion(shill::kWifiHexSsid
, hex_ssid
);
66 std::string
GetSSIDFromProperties(const base::DictionaryValue
& properties
,
67 bool* unknown_encoding
) {
69 *unknown_encoding
= false;
71 properties
.GetStringWithoutPathExpansion(shill::kWifiHexSsid
, &hex_ssid
);
73 if (hex_ssid
.empty()) {
74 NET_LOG_ERROR("GetSSIDFromProperties", "No HexSSID set.");
79 std::vector
<uint8
> raw_ssid_bytes
;
80 if (base::HexStringToBytes(hex_ssid
, &raw_ssid_bytes
)) {
81 ssid
= std::string(raw_ssid_bytes
.begin(), raw_ssid_bytes
.end());
83 "GetSSIDFromProperties",
84 base::StringPrintf("%s, SSID: %s", hex_ssid
.c_str(), ssid
.c_str()));
86 NET_LOG_ERROR("GetSSIDFromProperties",
87 base::StringPrintf("Error processing: %s", hex_ssid
.c_str()));
91 if (IsStringUTF8(ssid
))
94 // Detect encoding and convert to UTF-8.
96 if (!base::DetectEncoding(ssid
, &encoding
)) {
97 // TODO(stevenjb): This is currently experimental. If we find a case where
98 // base::DetectEncoding() fails, we need to figure out whether we can use
99 // country_code with ConvertToUtf8(). crbug.com/233267.
100 properties
.GetStringWithoutPathExpansion(shill::kCountryProperty
,
103 std::string utf8_ssid
;
104 if (!encoding
.empty() &&
105 base::ConvertToUtf8AndNormalize(ssid
, encoding
, &utf8_ssid
)) {
106 if (utf8_ssid
!= ssid
) {
108 "GetSSIDFromProperties",
110 "Encoding=%s: %s", encoding
.c_str(), utf8_ssid
.c_str()));
115 if (unknown_encoding
)
116 *unknown_encoding
= true;
118 "GetSSIDFromProperties",
119 base::StringPrintf("Unrecognized Encoding=%s", encoding
.c_str()));
123 std::string
GetNameFromProperties(const std::string
& service_path
,
124 const base::DictionaryValue
& properties
) {
126 properties
.GetStringWithoutPathExpansion(shill::kNameProperty
, &name
);
128 std::string validated_name
= ValidateUTF8(name
);
129 if (validated_name
!= name
) {
130 NET_LOG_DEBUG("GetNameFromProperties",
131 base::StringPrintf("Validated name %s: UTF8: %s",
132 service_path
.c_str(),
133 validated_name
.c_str()));
137 properties
.GetStringWithoutPathExpansion(shill::kTypeProperty
, &type
);
138 if (!NetworkTypePattern::WiFi().MatchesType(type
))
139 return validated_name
;
141 bool unknown_ssid_encoding
= false;
142 std::string ssid
= GetSSIDFromProperties(properties
, &unknown_ssid_encoding
);
144 NET_LOG_ERROR("GetNameFromProperties", "No SSID set: " + service_path
);
146 // Use |validated_name| if |ssid| is empty.
147 // And if the encoding of the SSID is unknown, use |ssid|, which contains raw
148 // bytes in that case, only if |validated_name| is empty.
149 if (ssid
.empty() || (unknown_ssid_encoding
&& !validated_name
.empty()))
150 return validated_name
;
152 if (ssid
!= validated_name
) {
153 NET_LOG_DEBUG("GetNameFromProperties",
154 base::StringPrintf("%s: SSID: %s, Name: %s",
155 service_path
.c_str(),
157 validated_name
.c_str()));
162 scoped_ptr
<NetworkUIData
> GetUIDataFromValue(const base::Value
& ui_data_value
) {
163 std::string ui_data_str
;
164 if (!ui_data_value
.GetAsString(&ui_data_str
))
165 return scoped_ptr
<NetworkUIData
>();
166 if (ui_data_str
.empty())
167 return make_scoped_ptr(new NetworkUIData());
168 scoped_ptr
<base::DictionaryValue
> ui_data_dict(
169 chromeos::onc::ReadDictionaryFromJson(ui_data_str
));
171 return scoped_ptr
<NetworkUIData
>();
172 return make_scoped_ptr(new NetworkUIData(*ui_data_dict
));
175 scoped_ptr
<NetworkUIData
> GetUIDataFromProperties(
176 const base::DictionaryValue
& shill_dictionary
) {
177 const base::Value
* ui_data_value
= NULL
;
178 shill_dictionary
.GetWithoutPathExpansion(shill::kUIDataProperty
,
180 if (!ui_data_value
) {
181 VLOG(2) << "Dictionary has no UIData entry.";
182 return scoped_ptr
<NetworkUIData
>();
184 scoped_ptr
<NetworkUIData
> ui_data
= GetUIDataFromValue(*ui_data_value
);
186 LOG(ERROR
) << "UIData is not a valid JSON dictionary.";
187 return ui_data
.Pass();
190 void SetUIData(const NetworkUIData
& ui_data
,
191 base::DictionaryValue
* shill_dictionary
) {
192 base::DictionaryValue ui_data_dict
;
193 ui_data
.FillDictionary(&ui_data_dict
);
194 std::string ui_data_blob
;
195 base::JSONWriter::Write(&ui_data_dict
, &ui_data_blob
);
196 shill_dictionary
->SetStringWithoutPathExpansion(shill::kUIDataProperty
,
200 bool CopyIdentifyingProperties(const base::DictionaryValue
& service_properties
,
201 base::DictionaryValue
* dest
) {
205 CopyStringFromDictionary(service_properties
, shill::kGuidProperty
, dest
);
208 service_properties
.GetStringWithoutPathExpansion(shill::kTypeProperty
, &type
);
209 success
&= !type
.empty();
210 dest
->SetStringWithoutPathExpansion(shill::kTypeProperty
, type
);
211 if (type
== shill::kTypeWifi
) {
212 success
&= CopyStringFromDictionary(
213 service_properties
, shill::kSecurityProperty
, dest
);
215 CopyStringFromDictionary(service_properties
, shill::kWifiHexSsid
, dest
);
216 success
&= CopyStringFromDictionary(
217 service_properties
, shill::kModeProperty
, dest
);
218 } else if (type
== shill::kTypeVPN
) {
219 success
&= CopyStringFromDictionary(
220 service_properties
, shill::kNameProperty
, dest
);
221 // VPN Provider values are read from the "Provider" dictionary, but written
222 // with the keys "Provider.Type" and "Provider.Host".
223 const base::DictionaryValue
* provider_properties
= NULL
;
224 if (!service_properties
.GetDictionaryWithoutPathExpansion(
225 shill::kProviderProperty
, &provider_properties
)) {
226 NET_LOG_ERROR("CopyIdentifyingProperties", "Missing VPN provider dict");
229 std::string vpn_provider_type
;
230 provider_properties
->GetStringWithoutPathExpansion(shill::kTypeProperty
,
232 success
&= !vpn_provider_type
.empty();
233 dest
->SetStringWithoutPathExpansion(shill::kProviderTypeProperty
,
236 std::string vpn_provider_host
;
237 provider_properties
->GetStringWithoutPathExpansion(shill::kHostProperty
,
239 success
&= !vpn_provider_host
.empty();
240 dest
->SetStringWithoutPathExpansion(shill::kProviderHostProperty
,
242 } else if (type
== shill::kTypeEthernet
|| type
== shill::kTypeEthernetEap
) {
243 // Ethernet and EthernetEAP don't have any additional identifying
246 NOTREACHED() << "Unsupported network type " << type
;
250 NET_LOG_ERROR("CopyIdentifyingProperties", "Missing required properties");
254 bool DoIdentifyingPropertiesMatch(const base::DictionaryValue
& properties_a
,
255 const base::DictionaryValue
& properties_b
) {
256 base::DictionaryValue identifying_a
;
257 if (!CopyIdentifyingProperties(properties_a
, &identifying_a
))
259 base::DictionaryValue identifying_b
;
260 if (!CopyIdentifyingProperties(properties_b
, &identifying_b
))
263 return identifying_a
.Equals(&identifying_b
);
266 } // namespace shill_property_util
270 const char kPatternDefault
[] = "PatternDefault";
271 const char kPatternEthernet
[] = "PatternEthernet";
272 const char kPatternWireless
[] = "PatternWireless";
273 const char kPatternMobile
[] = "PatternMobile";
274 const char kPatternNonVirtual
[] = "PatternNonVirtual";
276 enum NetworkTypeBitFlag
{
277 kNetworkTypeNone
= 0,
278 kNetworkTypeEthernet
= 1 << 0,
279 kNetworkTypeWifi
= 1 << 1,
280 kNetworkTypeWimax
= 1 << 2,
281 kNetworkTypeCellular
= 1 << 3,
282 kNetworkTypeVPN
= 1 << 4,
283 kNetworkTypeEthernetEap
= 1 << 5,
284 kNetworkTypeBluetooth
= 1 << 6
287 struct ShillToBitFlagEntry
{
288 const char* shill_network_type
;
289 NetworkTypeBitFlag bit_flag
;
290 } shill_type_to_flag
[] = {
291 { shill::kTypeEthernet
, kNetworkTypeEthernet
},
292 { shill::kTypeEthernetEap
, kNetworkTypeEthernetEap
},
293 { shill::kTypeWifi
, kNetworkTypeWifi
},
294 { shill::kTypeWimax
, kNetworkTypeWimax
},
295 { shill::kTypeCellular
, kNetworkTypeCellular
},
296 { shill::kTypeVPN
, kNetworkTypeVPN
},
297 { shill::kTypeBluetooth
, kNetworkTypeBluetooth
}
300 NetworkTypeBitFlag
ShillNetworkTypeToFlag(const std::string
& shill_type
) {
301 for (size_t i
= 0; i
< arraysize(shill_type_to_flag
); ++i
) {
302 if (shill_type_to_flag
[i
].shill_network_type
== shill_type
)
303 return shill_type_to_flag
[i
].bit_flag
;
305 NET_LOG_ERROR("ShillNetworkTypeToFlag", "Unknown type: " + shill_type
);
306 return kNetworkTypeNone
;
312 NetworkTypePattern
NetworkTypePattern::Default() {
313 return NetworkTypePattern(~0);
317 NetworkTypePattern
NetworkTypePattern::Wireless() {
318 return NetworkTypePattern(kNetworkTypeWifi
| kNetworkTypeWimax
|
319 kNetworkTypeCellular
);
323 NetworkTypePattern
NetworkTypePattern::Mobile() {
324 return NetworkTypePattern(kNetworkTypeCellular
| kNetworkTypeWimax
);
328 NetworkTypePattern
NetworkTypePattern::NonVirtual() {
329 return NetworkTypePattern(~kNetworkTypeVPN
);
333 NetworkTypePattern
NetworkTypePattern::Ethernet() {
334 return NetworkTypePattern(kNetworkTypeEthernet
);
338 NetworkTypePattern
NetworkTypePattern::WiFi() {
339 return NetworkTypePattern(kNetworkTypeWifi
);
343 NetworkTypePattern
NetworkTypePattern::Cellular() {
344 return NetworkTypePattern(kNetworkTypeCellular
);
348 NetworkTypePattern
NetworkTypePattern::VPN() {
349 return NetworkTypePattern(kNetworkTypeVPN
);
353 NetworkTypePattern
NetworkTypePattern::Wimax() {
354 return NetworkTypePattern(kNetworkTypeWimax
);
358 NetworkTypePattern
NetworkTypePattern::Primitive(
359 const std::string
& shill_network_type
) {
360 return NetworkTypePattern(ShillNetworkTypeToFlag(shill_network_type
));
363 bool NetworkTypePattern::Equals(const NetworkTypePattern
& other
) const {
364 return pattern_
== other
.pattern_
;
367 bool NetworkTypePattern::MatchesType(
368 const std::string
& shill_network_type
) const {
369 return MatchesPattern(Primitive(shill_network_type
));
372 bool NetworkTypePattern::MatchesPattern(
373 const NetworkTypePattern
& other_pattern
) const {
374 if (Equals(other_pattern
))
377 return pattern_
& other_pattern
.pattern_
;
380 std::string
NetworkTypePattern::ToDebugString() const {
381 if (Equals(Default()))
382 return kPatternDefault
;
383 if (Equals(Ethernet()))
384 return kPatternEthernet
;
385 if (Equals(Wireless()))
386 return kPatternWireless
;
387 if (Equals(Mobile()))
388 return kPatternMobile
;
389 if (Equals(NonVirtual()))
390 return kPatternNonVirtual
;
393 for (size_t i
= 0; i
< arraysize(shill_type_to_flag
); ++i
) {
394 if (!(pattern_
& shill_type_to_flag
[i
].bit_flag
))
398 str
+= shill_type_to_flag
[i
].shill_network_type
;
403 NetworkTypePattern::NetworkTypePattern(int pattern
) : pattern_(pattern
) {}
405 } // namespace chromeos