network_state.cc revision 868fa2fe829687343ffae624259930155e16dbd8
1// Copyright (c) 2012 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/network_state.h" 6 7#include "base/i18n/icu_encoding_detection.h" 8#include "base/i18n/icu_string_conversions.h" 9#include "base/strings/string_number_conversions.h" 10#include "base/strings/string_util.h" 11#include "base/strings/stringprintf.h" 12#include "base/strings/utf_string_conversion_utils.h" 13#include "chromeos/network/network_event_log.h" 14#include "chromeos/network/network_ui_data.h" 15#include "chromeos/network/onc/onc_utils.h" 16#include "third_party/cros_system_api/dbus/service_constants.h" 17 18namespace { 19 20bool ConvertListValueToStringVector(const base::ListValue& string_list, 21 std::vector<std::string>* result) { 22 for (size_t i = 0; i < string_list.GetSize(); ++i) { 23 std::string str; 24 if (!string_list.GetString(i, &str)) 25 return false; 26 result->push_back(str); 27 } 28 return true; 29} 30 31// Replace non UTF8 characters in |str| with a replacement character. 32std::string ValidateUTF8(const std::string& str) { 33 std::string result; 34 for (int32 index = 0; index < static_cast<int32>(str.size()); ++index) { 35 uint32 code_point_out; 36 bool is_unicode_char = base::ReadUnicodeCharacter(str.c_str(), str.size(), 37 &index, &code_point_out); 38 const uint32 kFirstNonControlChar = 0x20; 39 if (is_unicode_char && (code_point_out >= kFirstNonControlChar)) { 40 base::WriteUnicodeCharacter(code_point_out, &result); 41 } else { 42 const uint32 kReplacementChar = 0xFFFD; 43 // Puts kReplacementChar if character is a control character [0,0x20) 44 // or is not readable UTF8. 45 base::WriteUnicodeCharacter(kReplacementChar, &result); 46 } 47 } 48 return result; 49} 50 51} // namespace 52 53namespace chromeos { 54 55NetworkState::NetworkState(const std::string& path) 56 : ManagedState(MANAGED_TYPE_NETWORK, path), 57 auto_connect_(false), 58 favorite_(false), 59 priority_(0), 60 onc_source_(onc::ONC_SOURCE_NONE), 61 signal_strength_(0), 62 connectable_(false), 63 passphrase_required_(false), 64 activate_over_non_cellular_networks_(false), 65 cellular_out_of_credits_(false) { 66} 67 68NetworkState::~NetworkState() { 69} 70 71bool NetworkState::PropertyChanged(const std::string& key, 72 const base::Value& value) { 73 // Keep care that these properties are the same as in |GetProperties|. 74 if (ManagedStatePropertyChanged(key, value)) 75 return true; 76 if (key == flimflam::kSignalStrengthProperty) { 77 return GetIntegerValue(key, value, &signal_strength_); 78 } else if (key == flimflam::kStateProperty) { 79 return GetStringValue(key, value, &connection_state_); 80 } else if (key == flimflam::kConnectableProperty) { 81 return GetBooleanValue(key, value, &connectable_); 82 } else if (key == flimflam::kPassphraseRequiredProperty) { 83 return GetBooleanValue(key, value, &passphrase_required_); 84 } else if (key == flimflam::kErrorProperty) { 85 return GetStringValue(key, value, &error_); 86 } else if (key == shill::kErrorDetailsProperty) { 87 return GetStringValue(key, value, &error_details_); 88 } else if (key == IPConfigProperty(flimflam::kAddressProperty)) { 89 return GetStringValue(key, value, &ip_address_); 90 } else if (key == IPConfigProperty(flimflam::kNameServersProperty)) { 91 dns_servers_.clear(); 92 const base::ListValue* dns_servers; 93 if (value.GetAsList(&dns_servers)) 94 ConvertListValueToStringVector(*dns_servers, &dns_servers_); 95 return true; 96 } else if (key == flimflam::kActivationStateProperty) { 97 return GetStringValue(key, value, &activation_state_); 98 } else if (key == flimflam::kRoamingStateProperty) { 99 return GetStringValue(key, value, &roaming_); 100 } else if (key == flimflam::kSecurityProperty) { 101 return GetStringValue(key, value, &security_); 102 } else if (key == flimflam::kAutoConnectProperty) { 103 return GetBooleanValue(key, value, &auto_connect_); 104 } else if (key == flimflam::kFavoriteProperty) { 105 return GetBooleanValue(key, value, &favorite_); 106 } else if (key == flimflam::kPriorityProperty) { 107 return GetIntegerValue(key, value, &priority_); 108 } else if (key == flimflam::kProxyConfigProperty) { 109 std::string proxy_config_str; 110 if (!value.GetAsString(&proxy_config_str)) { 111 LOG(WARNING) << "Failed to parse string value for:" << key; 112 return false; 113 } 114 115 proxy_config_.Clear(); 116 if (proxy_config_str.empty()) 117 return true; 118 119 scoped_ptr<base::DictionaryValue> proxy_config_dict( 120 onc::ReadDictionaryFromJson(proxy_config_str)); 121 if (proxy_config_dict) { 122 // Warning: The DictionaryValue returned from 123 // ReadDictionaryFromJson/JSONParser is an optimized derived class that 124 // doesn't allow releasing ownership of nested values. A Swap in the wrong 125 // order leads to memory access errors. 126 proxy_config_.MergeDictionary(proxy_config_dict.get()); 127 } else { 128 LOG(WARNING) << "Failed to parse dictionary value for: " << key; 129 } 130 return true; 131 } else if (key == flimflam::kUIDataProperty) { 132 std::string ui_data_str; 133 if (!value.GetAsString(&ui_data_str)) { 134 LOG(WARNING) << "Failed to parse string value for:" << key; 135 return false; 136 } 137 138 onc_source_ = onc::ONC_SOURCE_NONE; 139 if (ui_data_str.empty()) 140 return true; 141 142 scoped_ptr<base::DictionaryValue> ui_data_dict( 143 onc::ReadDictionaryFromJson(ui_data_str)); 144 if (ui_data_dict) 145 onc_source_ = NetworkUIData(*ui_data_dict).onc_source(); 146 else 147 LOG(WARNING) << "Failed to parse dictionary value for: " << key; 148 return true; 149 } else if (key == flimflam::kNetworkTechnologyProperty) { 150 return GetStringValue(key, value, &technology_); 151 } else if (key == flimflam::kDeviceProperty) { 152 return GetStringValue(key, value, &device_path_); 153 } else if (key == flimflam::kGuidProperty) { 154 return GetStringValue(key, value, &guid_); 155 } else if (key == flimflam::kProfileProperty) { 156 return GetStringValue(key, value, &profile_path_); 157 } else if (key == shill::kActivateOverNonCellularNetworkProperty) { 158 return GetBooleanValue(key, value, &activate_over_non_cellular_networks_); 159 } else if (key == shill::kOutOfCreditsProperty) { 160 return GetBooleanValue(key, value, &cellular_out_of_credits_); 161 } else if (key == flimflam::kWifiHexSsid) { 162 return GetStringValue(key, value, &hex_ssid_); 163 } else if (key == flimflam::kCountryProperty) { 164 // TODO(stevenjb): This is currently experimental. If we find a case where 165 // base::DetectEncoding() fails in UpdateName(), where country_code_ is 166 // set, figure out whether we can use country_code_ with ConvertToUtf8(). 167 // crbug.com/233267. 168 return GetStringValue(key, value, &country_code_); 169 } 170 return false; 171} 172 173void NetworkState::InitialPropertiesReceived() { 174 UpdateName(); 175} 176 177void NetworkState::GetProperties(base::DictionaryValue* dictionary) const { 178 // Keep care that these properties are the same as in |PropertyChanged|. 179 dictionary->SetStringWithoutPathExpansion(flimflam::kNameProperty, name()); 180 dictionary->SetStringWithoutPathExpansion(flimflam::kTypeProperty, type()); 181 dictionary->SetIntegerWithoutPathExpansion(flimflam::kSignalStrengthProperty, 182 signal_strength_); 183 dictionary->SetStringWithoutPathExpansion(flimflam::kStateProperty, 184 connection_state_); 185 dictionary->SetBooleanWithoutPathExpansion(flimflam::kConnectableProperty, 186 connectable_); 187 dictionary->SetBooleanWithoutPathExpansion( 188 flimflam::kPassphraseRequiredProperty, passphrase_required_); 189 dictionary->SetStringWithoutPathExpansion(flimflam::kErrorProperty, 190 error_); 191 dictionary->SetStringWithoutPathExpansion(shill::kErrorDetailsProperty, 192 error_details_); 193 base::DictionaryValue* ipconfig_properties = new DictionaryValue; 194 ipconfig_properties->SetStringWithoutPathExpansion(flimflam::kAddressProperty, 195 ip_address_); 196 base::ListValue* name_servers = new ListValue; 197 name_servers->AppendStrings(dns_servers_); 198 ipconfig_properties->SetWithoutPathExpansion(flimflam::kNameServersProperty, 199 name_servers); 200 dictionary->SetWithoutPathExpansion(shill::kIPConfigProperty, 201 ipconfig_properties); 202 203 dictionary->SetStringWithoutPathExpansion(flimflam::kActivationStateProperty, 204 activation_state_); 205 dictionary->SetStringWithoutPathExpansion(flimflam::kRoamingStateProperty, 206 roaming_); 207 dictionary->SetStringWithoutPathExpansion(flimflam::kSecurityProperty, 208 security_); 209 dictionary->SetBooleanWithoutPathExpansion(flimflam::kAutoConnectProperty, 210 auto_connect_); 211 dictionary->SetBooleanWithoutPathExpansion(flimflam::kFavoriteProperty, 212 favorite_); 213 dictionary->SetIntegerWithoutPathExpansion(flimflam::kPriorityProperty, 214 priority_); 215 // Proxy config and ONC source are intentionally omitted: These properties are 216 // placed in NetworkState to transition ProxyConfigServiceImpl from 217 // NetworkLibrary to the new network stack. The networking extension API 218 // shouldn't depend on this member. Once ManagedNetworkConfigurationHandler 219 // is used instead of NetworkLibrary, we can remove them again. 220 dictionary->SetStringWithoutPathExpansion( 221 flimflam::kNetworkTechnologyProperty, 222 technology_); 223 dictionary->SetStringWithoutPathExpansion(flimflam::kDeviceProperty, 224 device_path_); 225 dictionary->SetStringWithoutPathExpansion(flimflam::kGuidProperty, guid_); 226 dictionary->SetStringWithoutPathExpansion(flimflam::kProfileProperty, 227 profile_path_); 228 dictionary->SetBooleanWithoutPathExpansion( 229 shill::kActivateOverNonCellularNetworkProperty, 230 activate_over_non_cellular_networks_); 231 dictionary->SetBooleanWithoutPathExpansion(shill::kOutOfCreditsProperty, 232 cellular_out_of_credits_); 233} 234 235bool NetworkState::IsConnectedState() const { 236 return StateIsConnected(connection_state_); 237} 238 239bool NetworkState::IsConnectingState() const { 240 return StateIsConnecting(connection_state_); 241} 242 243bool NetworkState::HasAuthenticationError() const { 244 return (error_ == flimflam::kErrorBadPassphrase || 245 error_ == flimflam::kErrorBadWEPKey || 246 error_ == flimflam::kErrorPppAuthFailed || 247 error_ == shill::kErrorEapLocalTlsFailed || 248 error_ == shill::kErrorEapRemoteTlsFailed || 249 error_ == shill::kErrorEapAuthenticationFailed); 250} 251 252void NetworkState::UpdateName() { 253 if (hex_ssid_.empty()) { 254 // Validate name for UTF8. 255 std::string valid_ssid = ValidateUTF8(name()); 256 if (valid_ssid != name()) { 257 set_name(valid_ssid); 258 NET_LOG_DEBUG("UpdateName", base::StringPrintf( 259 "%s: UTF8: %s", path().c_str(), name().c_str())); 260 } 261 return; 262 } 263 264 std::string ssid; 265 std::vector<uint8> raw_ssid_bytes; 266 if (base::HexStringToBytes(hex_ssid_, &raw_ssid_bytes)) { 267 ssid = std::string(raw_ssid_bytes.begin(), raw_ssid_bytes.end()); 268 } else { 269 std::string desc = base::StringPrintf("%s: Error processing: %s", 270 path().c_str(), hex_ssid_.c_str()); 271 NET_LOG_DEBUG("UpdateName", desc); 272 LOG(ERROR) << desc; 273 ssid = name(); 274 } 275 276 if (IsStringUTF8(ssid)) { 277 if (ssid != name()) { 278 set_name(ssid); 279 NET_LOG_DEBUG("UpdateName", base::StringPrintf( 280 "%s: UTF8: %s", path().c_str(), name().c_str())); 281 } 282 return; 283 } 284 285 // Detect encoding and convert to UTF-8. 286 std::string encoding; 287 if (!base::DetectEncoding(ssid, &encoding)) { 288 // TODO(stevenjb): Test this. See comment in PropertyChanged() under 289 // flimflam::kCountryProperty. 290 encoding = country_code_; 291 } 292 if (!encoding.empty()) { 293 std::string utf8_ssid; 294 if (base::ConvertToUtf8AndNormalize(ssid, encoding, &utf8_ssid)) { 295 set_name(utf8_ssid); 296 NET_LOG_DEBUG("UpdateName", base::StringPrintf( 297 "%s: Encoding=%s: %s", path().c_str(), 298 encoding.c_str(), name().c_str())); 299 return; 300 } 301 } 302 303 // Unrecognized encoding. Only use raw bytes if name_ is empty. 304 if (name().empty()) 305 set_name(ssid); 306 NET_LOG_DEBUG("UpdateName", base::StringPrintf( 307 "%s: Unrecognized Encoding=%s: %s", path().c_str(), 308 encoding.c_str(), name().c_str())); 309} 310 311// static 312bool NetworkState::StateIsConnected(const std::string& connection_state) { 313 return (connection_state == flimflam::kStateReady || 314 connection_state == flimflam::kStateOnline || 315 connection_state == flimflam::kStatePortal); 316} 317 318// static 319bool NetworkState::StateIsConnecting(const std::string& connection_state) { 320 return (connection_state == flimflam::kStateAssociation || 321 connection_state == flimflam::kStateConfiguration || 322 connection_state == flimflam::kStateCarrier); 323} 324 325// static 326std::string NetworkState::IPConfigProperty(const char* key) { 327 return base::StringPrintf("%s.%s", shill::kIPConfigProperty, key); 328} 329 330} // namespace chromeos 331