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 20 namespace chromeos { 21 22 namespace shill_property_util { 23 24 namespace { 25 26 // Replace non UTF8 characters in |str| with a replacement character. 27 std::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. 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()) 54 return false; 55 dest->SetStringWithoutPathExpansion(key, string_value); 56 return true; 57 } 58 59 } // namespace 60 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); 64 } 65 66 std::string GetSSIDFromProperties(const base::DictionaryValue& properties, 67 bool* unknown_encoding) { 68 if (unknown_encoding) 69 *unknown_encoding = false; 70 std::string hex_ssid; 71 properties.GetStringWithoutPathExpansion(shill::kWifiHexSsid, &hex_ssid); 72 73 if (hex_ssid.empty()) { 74 NET_LOG_ERROR("GetSSIDFromProperties", "No HexSSID set."); 75 return std::string(); 76 } 77 78 std::string ssid; 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()); 82 NET_LOG_DEBUG( 83 "GetSSIDFromProperties", 84 base::StringPrintf("%s, SSID: %s", hex_ssid.c_str(), ssid.c_str())); 85 } else { 86 NET_LOG_ERROR("GetSSIDFromProperties", 87 base::StringPrintf("Error processing: %s", hex_ssid.c_str())); 88 return std::string(); 89 } 90 91 if (IsStringUTF8(ssid)) 92 return ssid; 93 94 // Detect encoding and convert to UTF-8. 95 std::string encoding; 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, 101 &encoding); 102 } 103 std::string utf8_ssid; 104 if (!encoding.empty() && 105 base::ConvertToUtf8AndNormalize(ssid, encoding, &utf8_ssid)) { 106 if (utf8_ssid != ssid) { 107 NET_LOG_DEBUG( 108 "GetSSIDFromProperties", 109 base::StringPrintf( 110 "Encoding=%s: %s", encoding.c_str(), utf8_ssid.c_str())); 111 } 112 return utf8_ssid; 113 } 114 115 if (unknown_encoding) 116 *unknown_encoding = true; 117 NET_LOG_DEBUG( 118 "GetSSIDFromProperties", 119 base::StringPrintf("Unrecognized Encoding=%s", encoding.c_str())); 120 return ssid; 121 } 122 123 std::string GetNameFromProperties(const std::string& service_path, 124 const base::DictionaryValue& properties) { 125 std::string name; 126 properties.GetStringWithoutPathExpansion(shill::kNameProperty, &name); 127 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())); 134 } 135 136 std::string type; 137 properties.GetStringWithoutPathExpansion(shill::kTypeProperty, &type); 138 if (!NetworkTypePattern::WiFi().MatchesType(type)) 139 return validated_name; 140 141 bool unknown_ssid_encoding = false; 142 std::string ssid = GetSSIDFromProperties(properties, &unknown_ssid_encoding); 143 if (ssid.empty()) 144 NET_LOG_ERROR("GetNameFromProperties", "No SSID set: " + service_path); 145 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; 151 152 if (ssid != validated_name) { 153 NET_LOG_DEBUG("GetNameFromProperties", 154 base::StringPrintf("%s: SSID: %s, Name: %s", 155 service_path.c_str(), 156 ssid.c_str(), 157 validated_name.c_str())); 158 } 159 return ssid; 160 } 161 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)); 170 if (!ui_data_dict) 171 return scoped_ptr<NetworkUIData>(); 172 return make_scoped_ptr(new NetworkUIData(*ui_data_dict)); 173 } 174 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, 179 &ui_data_value); 180 if (!ui_data_value) { 181 VLOG(2) << "Dictionary has no UIData entry."; 182 return scoped_ptr<NetworkUIData>(); 183 } 184 scoped_ptr<NetworkUIData> ui_data = GetUIDataFromValue(*ui_data_value); 185 if (!ui_data) 186 LOG(ERROR) << "UIData is not a valid JSON dictionary."; 187 return ui_data.Pass(); 188 } 189 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, 197 ui_data_blob); 198 } 199 200 bool CopyIdentifyingProperties(const base::DictionaryValue& service_properties, 201 base::DictionaryValue* dest) { 202 bool success = true; 203 204 // GUID is optional. 205 CopyStringFromDictionary(service_properties, shill::kGuidProperty, dest); 206 207 std::string type; 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); 214 success &= 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"); 227 return false; 228 } 229 std::string vpn_provider_type; 230 provider_properties->GetStringWithoutPathExpansion(shill::kTypeProperty, 231 &vpn_provider_type); 232 success &= !vpn_provider_type.empty(); 233 dest->SetStringWithoutPathExpansion(shill::kProviderTypeProperty, 234 vpn_provider_type); 235 236 std::string vpn_provider_host; 237 provider_properties->GetStringWithoutPathExpansion(shill::kHostProperty, 238 &vpn_provider_host); 239 success &= !vpn_provider_host.empty(); 240 dest->SetStringWithoutPathExpansion(shill::kProviderHostProperty, 241 vpn_provider_host); 242 } else if (type == shill::kTypeEthernet || type == shill::kTypeEthernetEap) { 243 // Ethernet and EthernetEAP don't have any additional identifying 244 // properties. 245 } else { 246 NOTREACHED() << "Unsupported network type " << type; 247 success = false; 248 } 249 if (!success) 250 NET_LOG_ERROR("CopyIdentifyingProperties", "Missing required properties"); 251 return success; 252 } 253 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)) 258 return false; 259 base::DictionaryValue identifying_b; 260 if (!CopyIdentifyingProperties(properties_b, &identifying_b)) 261 return false; 262 263 return identifying_a.Equals(&identifying_b); 264 } 265 266 } // namespace shill_property_util 267 268 namespace { 269 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"; 275 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 285 }; 286 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 } 298 }; 299 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; 304 } 305 NET_LOG_ERROR("ShillNetworkTypeToFlag", "Unknown type: " + shill_type); 306 return kNetworkTypeNone; 307 } 308 309 } // namespace 310 311 // static 312 NetworkTypePattern NetworkTypePattern::Default() { 313 return NetworkTypePattern(~0); 314 } 315 316 // static 317 NetworkTypePattern NetworkTypePattern::Wireless() { 318 return NetworkTypePattern(kNetworkTypeWifi | kNetworkTypeWimax | 319 kNetworkTypeCellular); 320 } 321 322 // static 323 NetworkTypePattern NetworkTypePattern::Mobile() { 324 return NetworkTypePattern(kNetworkTypeCellular | kNetworkTypeWimax); 325 } 326 327 // static 328 NetworkTypePattern NetworkTypePattern::NonVirtual() { 329 return NetworkTypePattern(~kNetworkTypeVPN); 330 } 331 332 // static 333 NetworkTypePattern NetworkTypePattern::Ethernet() { 334 return NetworkTypePattern(kNetworkTypeEthernet); 335 } 336 337 // static 338 NetworkTypePattern NetworkTypePattern::WiFi() { 339 return NetworkTypePattern(kNetworkTypeWifi); 340 } 341 342 // static 343 NetworkTypePattern NetworkTypePattern::Cellular() { 344 return NetworkTypePattern(kNetworkTypeCellular); 345 } 346 347 // static 348 NetworkTypePattern NetworkTypePattern::VPN() { 349 return NetworkTypePattern(kNetworkTypeVPN); 350 } 351 352 // static 353 NetworkTypePattern NetworkTypePattern::Wimax() { 354 return NetworkTypePattern(kNetworkTypeWimax); 355 } 356 357 // static 358 NetworkTypePattern NetworkTypePattern::Primitive( 359 const std::string& shill_network_type) { 360 return NetworkTypePattern(ShillNetworkTypeToFlag(shill_network_type)); 361 } 362 363 bool NetworkTypePattern::Equals(const NetworkTypePattern& other) const { 364 return pattern_ == other.pattern_; 365 } 366 367 bool NetworkTypePattern::MatchesType( 368 const std::string& shill_network_type) const { 369 return MatchesPattern(Primitive(shill_network_type)); 370 } 371 372 bool NetworkTypePattern::MatchesPattern( 373 const NetworkTypePattern& other_pattern) const { 374 if (Equals(other_pattern)) 375 return true; 376 377 return pattern_ & other_pattern.pattern_; 378 } 379 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; 391 392 std::string str; 393 for (size_t i = 0; i < arraysize(shill_type_to_flag); ++i) { 394 if (!(pattern_ & shill_type_to_flag[i].bit_flag)) 395 continue; 396 if (!str.empty()) 397 str += "|"; 398 str += shill_type_to_flag[i].shill_network_type; 399 } 400 return str; 401 } 402 403 NetworkTypePattern::NetworkTypePattern(int pattern) : pattern_(pattern) {} 404 405 } // namespace chromeos 406