shill_property_util.cc revision 58537e28ecd584eab876aee8be7156509866d23a
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(flimflam::kNameProperty, &name); 65 properties.GetStringWithoutPathExpansion(flimflam::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(flimflam::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(flimflam::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(flimflam::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, flimflam::kGuidProperty, dest); 187 188 std::string type; 189 service_properties.GetStringWithoutPathExpansion(flimflam::kTypeProperty, 190 &type); 191 success &= !type.empty(); 192 dest->SetStringWithoutPathExpansion(flimflam::kTypeProperty, type); 193 if (type == flimflam::kTypeWifi) { 194 success &= CopyStringFromDictionary( 195 service_properties, flimflam::kSecurityProperty, dest); 196 success &= CopyStringFromDictionary( 197 service_properties, flimflam::kSSIDProperty, dest); 198 success &= CopyStringFromDictionary( 199 service_properties, flimflam::kModeProperty, dest); 200 } else if (type == flimflam::kTypeVPN) { 201 success &= CopyStringFromDictionary( 202 service_properties, flimflam::kNameProperty, dest); 203 // VPN Provider values are read from the "Provider" dictionary, but written 204 // with the keys "Provider.Type" and "Provider.Host". 205 const base::DictionaryValue* provider_properties = NULL; 206 if (!service_properties.GetDictionaryWithoutPathExpansion( 207 flimflam::kProviderProperty, &provider_properties)) { 208 NET_LOG_ERROR("CopyIdentifyingProperties", "Missing VPN provider dict"); 209 return false; 210 } 211 std::string vpn_provider_type; 212 provider_properties->GetStringWithoutPathExpansion(flimflam::kTypeProperty, 213 &vpn_provider_type); 214 success &= !vpn_provider_type.empty(); 215 dest->SetStringWithoutPathExpansion(flimflam::kProviderTypeProperty, 216 vpn_provider_type); 217 218 std::string vpn_provider_host; 219 provider_properties->GetStringWithoutPathExpansion(flimflam::kHostProperty, 220 &vpn_provider_host); 221 success &= !vpn_provider_host.empty(); 222 dest->SetStringWithoutPathExpansion(flimflam::kProviderHostProperty, 223 vpn_provider_host); 224 } else if (type == flimflam::kTypeEthernet || 225 type == shill::kTypeEthernetEap) { 226 // Ethernet and EthernetEAP don't have any additional identifying 227 // properties. 228 } else { 229 NOTREACHED() << "Unsupported network type " << type; 230 success = false; 231 } 232 if (!success) 233 NET_LOG_ERROR("CopyIdentifyingProperties", "Missing required properties"); 234 return success; 235} 236 237} // namespace shill_property_util 238 239namespace { 240 241const char kPatternDefault[] = "PatternDefault"; 242const char kPatternEthernet[] = "PatternEthernet"; 243const char kPatternWireless[] = "PatternWireless"; 244const char kPatternMobile[] = "PatternMobile"; 245const char kPatternNonVirtual[] = "PatternNonVirtual"; 246 247enum NetworkTypeBitFlag { 248 kNetworkTypeNone = 0, 249 kNetworkTypeEthernet = 1 << 0, 250 kNetworkTypeWifi = 1 << 1, 251 kNetworkTypeWimax = 1 << 2, 252 kNetworkTypeCellular = 1 << 3, 253 kNetworkTypeVPN = 1 << 4, 254 kNetworkTypeEthernetEap = 1 << 5 255}; 256 257struct ShillToBitFlagEntry { 258 const char* shill_network_type; 259 NetworkTypeBitFlag bit_flag; 260} shill_type_to_flag[] = { 261 { flimflam::kTypeEthernet, kNetworkTypeEthernet }, 262 { shill::kTypeEthernetEap, kNetworkTypeEthernetEap }, 263 { flimflam::kTypeWifi, kNetworkTypeWifi }, 264 { flimflam::kTypeWimax, kNetworkTypeWimax }, 265 { flimflam::kTypeCellular, kNetworkTypeCellular }, 266 { flimflam::kTypeVPN, kNetworkTypeVPN } 267}; 268 269NetworkTypeBitFlag ShillNetworkTypeToFlag(const std::string& shill_type) { 270 for (size_t i = 0; i < arraysize(shill_type_to_flag); ++i) { 271 if (shill_type_to_flag[i].shill_network_type == shill_type) 272 return shill_type_to_flag[i].bit_flag; 273 } 274 NET_LOG_ERROR("ShillNetworkTypeToFlag", "Unknown type: " + shill_type); 275 return kNetworkTypeNone; 276} 277 278} // namespace 279 280// static 281NetworkTypePattern NetworkTypePattern::Default() { 282 return NetworkTypePattern(~0); 283} 284 285// static 286NetworkTypePattern NetworkTypePattern::Wireless() { 287 return NetworkTypePattern(kNetworkTypeWifi | kNetworkTypeWimax | 288 kNetworkTypeCellular); 289} 290 291// static 292NetworkTypePattern NetworkTypePattern::Mobile() { 293 return NetworkTypePattern(kNetworkTypeCellular | kNetworkTypeWimax); 294} 295 296// static 297NetworkTypePattern NetworkTypePattern::NonVirtual() { 298 return NetworkTypePattern(~kNetworkTypeVPN); 299} 300 301// static 302NetworkTypePattern NetworkTypePattern::Ethernet() { 303 return NetworkTypePattern(kNetworkTypeEthernet); 304} 305 306// static 307NetworkTypePattern NetworkTypePattern::WiFi() { 308 return NetworkTypePattern(kNetworkTypeWifi); 309} 310 311// static 312NetworkTypePattern NetworkTypePattern::Cellular() { 313 return NetworkTypePattern(kNetworkTypeCellular); 314} 315 316// static 317NetworkTypePattern NetworkTypePattern::VPN() { 318 return NetworkTypePattern(kNetworkTypeVPN); 319} 320 321// static 322NetworkTypePattern NetworkTypePattern::Wimax() { 323 return NetworkTypePattern(kNetworkTypeWimax); 324} 325 326// static 327NetworkTypePattern NetworkTypePattern::Primitive( 328 const std::string& shill_network_type) { 329 return NetworkTypePattern(ShillNetworkTypeToFlag(shill_network_type)); 330} 331 332bool NetworkTypePattern::Equals(const NetworkTypePattern& other) const { 333 return pattern_ == other.pattern_; 334} 335 336bool NetworkTypePattern::MatchesType( 337 const std::string& shill_network_type) const { 338 return MatchesPattern(Primitive(shill_network_type)); 339} 340 341bool NetworkTypePattern::MatchesPattern( 342 const NetworkTypePattern& other_pattern) const { 343 if (Equals(other_pattern)) 344 return true; 345 346 return pattern_ & other_pattern.pattern_; 347} 348 349std::string NetworkTypePattern::ToDebugString() const { 350 if (Equals(Default())) 351 return kPatternDefault; 352 if (Equals(Ethernet())) 353 return kPatternEthernet; 354 if (Equals(Wireless())) 355 return kPatternWireless; 356 if (Equals(Mobile())) 357 return kPatternMobile; 358 if (Equals(NonVirtual())) 359 return kPatternNonVirtual; 360 361 std::string str; 362 for (size_t i = 0; i < arraysize(shill_type_to_flag); ++i) { 363 if (!(pattern_ & shill_type_to_flag[i].bit_flag)) 364 continue; 365 if (!str.empty()) 366 str += "|"; 367 str += shill_type_to_flag[i].shill_network_type; 368 } 369 return str; 370} 371 372NetworkTypePattern::NetworkTypePattern(int pattern) : pattern_(pattern) {} 373 374} // namespace chromeos 375