shill_property_util.cc revision 68043e1e95eeb07d5cae7aca370b26518b0867d6
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 20namespace chromeos { 21 22namespace shill_property_util { 23 24namespace { 25 26// Replace non UTF8 characters in |str| with a replacement character. 27std::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. 48bool 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 61std::string GetNameFromProperties(const std::string& service_path, 62 const base::DictionaryValue& properties) { 63 std::string name, hex_ssid; 64 properties.GetStringWithoutPathExpansion(shill::kNameProperty, &name); 65 properties.GetStringWithoutPathExpansion(shill::kWifiHexSsid, &hex_ssid); 66 67 if (hex_ssid.empty()) { 68 if (name.empty()) 69 return name; 70 // Validate name for UTF8. 71 std::string valid_ssid = ValidateUTF8(name); 72 if (valid_ssid != name) { 73 NET_LOG_DEBUG( 74 "GetNameFromProperties", 75 base::StringPrintf( 76 "%s: UTF8: %s", service_path.c_str(), valid_ssid.c_str())); 77 } 78 return valid_ssid; 79 } 80 81 std::string ssid; 82 std::vector<uint8> raw_ssid_bytes; 83 if (base::HexStringToBytes(hex_ssid, &raw_ssid_bytes)) { 84 ssid = std::string(raw_ssid_bytes.begin(), raw_ssid_bytes.end()); 85 NET_LOG_DEBUG("GetNameFromProperties", 86 base::StringPrintf("%s: %s, SSID: %s", 87 service_path.c_str(), 88 hex_ssid.c_str(), 89 ssid.c_str())); 90 } else { 91 NET_LOG_ERROR("GetNameFromProperties", 92 base::StringPrintf("%s: Error processing: %s", 93 service_path.c_str(), 94 hex_ssid.c_str())); 95 return name; 96 } 97 98 if (IsStringUTF8(ssid)) { 99 if (ssid != name) { 100 NET_LOG_DEBUG("GetNameFromProperties", 101 base::StringPrintf( 102 "%s: UTF8: %s", service_path.c_str(), ssid.c_str())); 103 } 104 return ssid; 105 } 106 107 // Detect encoding and convert to UTF-8. 108 std::string country_code; 109 properties.GetStringWithoutPathExpansion(shill::kCountryProperty, 110 &country_code); 111 std::string encoding; 112 if (!base::DetectEncoding(ssid, &encoding)) { 113 // TODO(stevenjb): This is currently experimental. If we find a case where 114 // base::DetectEncoding() fails, we need to figure out whether we can use 115 // country_code with ConvertToUtf8(). crbug.com/233267. 116 encoding = country_code; 117 } 118 if (!encoding.empty()) { 119 std::string utf8_ssid; 120 if (base::ConvertToUtf8AndNormalize(ssid, encoding, &utf8_ssid)) { 121 if (utf8_ssid != name) { 122 NET_LOG_DEBUG("GetNameFromProperties", 123 base::StringPrintf("%s: Encoding=%s: %s", 124 service_path.c_str(), 125 encoding.c_str(), 126 utf8_ssid.c_str())); 127 } 128 return utf8_ssid; 129 } 130 } 131 132 // Unrecognized encoding. Only use raw bytes if name_ is empty. 133 NET_LOG_DEBUG("GetNameFromProperties", 134 base::StringPrintf("%s: Unrecognized Encoding=%s: %s", 135 service_path.c_str(), 136 encoding.c_str(), 137 ssid.c_str())); 138 if (name.empty() && !ssid.empty()) 139 return ssid; 140 return name; 141} 142 143scoped_ptr<NetworkUIData> GetUIDataFromValue(const base::Value& ui_data_value) { 144 std::string ui_data_str; 145 if (!ui_data_value.GetAsString(&ui_data_str)) 146 return scoped_ptr<NetworkUIData>(); 147 if (ui_data_str.empty()) 148 return make_scoped_ptr(new NetworkUIData()); 149 scoped_ptr<base::DictionaryValue> ui_data_dict( 150 chromeos::onc::ReadDictionaryFromJson(ui_data_str)); 151 if (!ui_data_dict) 152 return scoped_ptr<NetworkUIData>(); 153 return make_scoped_ptr(new NetworkUIData(*ui_data_dict)); 154} 155 156scoped_ptr<NetworkUIData> GetUIDataFromProperties( 157 const base::DictionaryValue& shill_dictionary) { 158 const base::Value* ui_data_value = NULL; 159 shill_dictionary.GetWithoutPathExpansion(shill::kUIDataProperty, 160 &ui_data_value); 161 if (!ui_data_value) { 162 VLOG(2) << "Dictionary has no UIData entry."; 163 return scoped_ptr<NetworkUIData>(); 164 } 165 scoped_ptr<NetworkUIData> ui_data = GetUIDataFromValue(*ui_data_value); 166 if (!ui_data) 167 LOG(ERROR) << "UIData is not a valid JSON dictionary."; 168 return ui_data.Pass(); 169} 170 171void SetUIData(const NetworkUIData& ui_data, 172 base::DictionaryValue* shill_dictionary) { 173 base::DictionaryValue ui_data_dict; 174 ui_data.FillDictionary(&ui_data_dict); 175 std::string ui_data_blob; 176 base::JSONWriter::Write(&ui_data_dict, &ui_data_blob); 177 shill_dictionary->SetStringWithoutPathExpansion(shill::kUIDataProperty, 178 ui_data_blob); 179} 180 181bool CopyIdentifyingProperties(const base::DictionaryValue& service_properties, 182 base::DictionaryValue* dest) { 183 bool success = true; 184 185 // GUID is optional. 186 CopyStringFromDictionary(service_properties, shill::kGuidProperty, dest); 187 188 std::string type; 189 service_properties.GetStringWithoutPathExpansion(shill::kTypeProperty, &type); 190 success &= !type.empty(); 191 dest->SetStringWithoutPathExpansion(shill::kTypeProperty, type); 192 if (type == shill::kTypeWifi) { 193 success &= CopyStringFromDictionary( 194 service_properties, shill::kSecurityProperty, dest); 195 success &= CopyStringFromDictionary( 196 service_properties, shill::kSSIDProperty, dest); 197 success &= CopyStringFromDictionary( 198 service_properties, shill::kModeProperty, dest); 199 } else if (type == shill::kTypeVPN) { 200 success &= CopyStringFromDictionary( 201 service_properties, shill::kNameProperty, dest); 202 // VPN Provider values are read from the "Provider" dictionary, but written 203 // with the keys "Provider.Type" and "Provider.Host". 204 const base::DictionaryValue* provider_properties = NULL; 205 if (!service_properties.GetDictionaryWithoutPathExpansion( 206 shill::kProviderProperty, &provider_properties)) { 207 NET_LOG_ERROR("CopyIdentifyingProperties", "Missing VPN provider dict"); 208 return false; 209 } 210 std::string vpn_provider_type; 211 provider_properties->GetStringWithoutPathExpansion(shill::kTypeProperty, 212 &vpn_provider_type); 213 success &= !vpn_provider_type.empty(); 214 dest->SetStringWithoutPathExpansion(shill::kProviderTypeProperty, 215 vpn_provider_type); 216 217 std::string vpn_provider_host; 218 provider_properties->GetStringWithoutPathExpansion(shill::kHostProperty, 219 &vpn_provider_host); 220 success &= !vpn_provider_host.empty(); 221 dest->SetStringWithoutPathExpansion(shill::kProviderHostProperty, 222 vpn_provider_host); 223 } else if (type == shill::kTypeEthernet || type == shill::kTypeEthernetEap) { 224 // Ethernet and EthernetEAP don't have any additional identifying 225 // properties. 226 } else { 227 NOTREACHED() << "Unsupported network type " << type; 228 success = false; 229 } 230 if (!success) 231 NET_LOG_ERROR("CopyIdentifyingProperties", "Missing required properties"); 232 return success; 233} 234 235} // namespace shill_property_util 236 237namespace { 238 239const char kPatternDefault[] = "PatternDefault"; 240const char kPatternEthernet[] = "PatternEthernet"; 241const char kPatternWireless[] = "PatternWireless"; 242const char kPatternMobile[] = "PatternMobile"; 243const char kPatternNonVirtual[] = "PatternNonVirtual"; 244 245enum NetworkTypeBitFlag { 246 kNetworkTypeNone = 0, 247 kNetworkTypeEthernet = 1 << 0, 248 kNetworkTypeWifi = 1 << 1, 249 kNetworkTypeWimax = 1 << 2, 250 kNetworkTypeCellular = 1 << 3, 251 kNetworkTypeVPN = 1 << 4, 252 kNetworkTypeEthernetEap = 1 << 5 253}; 254 255struct ShillToBitFlagEntry { 256 const char* shill_network_type; 257 NetworkTypeBitFlag bit_flag; 258} shill_type_to_flag[] = { 259 { shill::kTypeEthernet, kNetworkTypeEthernet }, 260 { shill::kTypeEthernetEap, kNetworkTypeEthernetEap }, 261 { shill::kTypeWifi, kNetworkTypeWifi }, 262 { shill::kTypeWimax, kNetworkTypeWimax }, 263 { shill::kTypeCellular, kNetworkTypeCellular }, 264 { shill::kTypeVPN, kNetworkTypeVPN } 265}; 266 267NetworkTypeBitFlag ShillNetworkTypeToFlag(const std::string& shill_type) { 268 for (size_t i = 0; i < arraysize(shill_type_to_flag); ++i) { 269 if (shill_type_to_flag[i].shill_network_type == shill_type) 270 return shill_type_to_flag[i].bit_flag; 271 } 272 NET_LOG_ERROR("ShillNetworkTypeToFlag", "Unknown type: " + shill_type); 273 return kNetworkTypeNone; 274} 275 276} // namespace 277 278// static 279NetworkTypePattern NetworkTypePattern::Default() { 280 return NetworkTypePattern(~0); 281} 282 283// static 284NetworkTypePattern NetworkTypePattern::Wireless() { 285 return NetworkTypePattern(kNetworkTypeWifi | kNetworkTypeWimax | 286 kNetworkTypeCellular); 287} 288 289// static 290NetworkTypePattern NetworkTypePattern::Mobile() { 291 return NetworkTypePattern(kNetworkTypeCellular | kNetworkTypeWimax); 292} 293 294// static 295NetworkTypePattern NetworkTypePattern::NonVirtual() { 296 return NetworkTypePattern(~kNetworkTypeVPN); 297} 298 299// static 300NetworkTypePattern NetworkTypePattern::Ethernet() { 301 return NetworkTypePattern(kNetworkTypeEthernet); 302} 303 304// static 305NetworkTypePattern NetworkTypePattern::WiFi() { 306 return NetworkTypePattern(kNetworkTypeWifi); 307} 308 309// static 310NetworkTypePattern NetworkTypePattern::Cellular() { 311 return NetworkTypePattern(kNetworkTypeCellular); 312} 313 314// static 315NetworkTypePattern NetworkTypePattern::VPN() { 316 return NetworkTypePattern(kNetworkTypeVPN); 317} 318 319// static 320NetworkTypePattern NetworkTypePattern::Wimax() { 321 return NetworkTypePattern(kNetworkTypeWimax); 322} 323 324// static 325NetworkTypePattern NetworkTypePattern::Primitive( 326 const std::string& shill_network_type) { 327 return NetworkTypePattern(ShillNetworkTypeToFlag(shill_network_type)); 328} 329 330bool NetworkTypePattern::Equals(const NetworkTypePattern& other) const { 331 return pattern_ == other.pattern_; 332} 333 334bool NetworkTypePattern::MatchesType( 335 const std::string& shill_network_type) const { 336 return MatchesPattern(Primitive(shill_network_type)); 337} 338 339bool NetworkTypePattern::MatchesPattern( 340 const NetworkTypePattern& other_pattern) const { 341 if (Equals(other_pattern)) 342 return true; 343 344 return pattern_ & other_pattern.pattern_; 345} 346 347std::string NetworkTypePattern::ToDebugString() const { 348 if (Equals(Default())) 349 return kPatternDefault; 350 if (Equals(Ethernet())) 351 return kPatternEthernet; 352 if (Equals(Wireless())) 353 return kPatternWireless; 354 if (Equals(Mobile())) 355 return kPatternMobile; 356 if (Equals(NonVirtual())) 357 return kPatternNonVirtual; 358 359 std::string str; 360 for (size_t i = 0; i < arraysize(shill_type_to_flag); ++i) { 361 if (!(pattern_ & shill_type_to_flag[i].bit_flag)) 362 continue; 363 if (!str.empty()) 364 str += "|"; 365 str += shill_type_to_flag[i].shill_network_type; 366 } 367 return str; 368} 369 370NetworkTypePattern::NetworkTypePattern(int pattern) : pattern_(pattern) {} 371 372} // namespace chromeos 373