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