1// Copyright (c) 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 "ash/system/chromeos/network/network_connect.h"
6
7#include "ash/shell.h"
8#include "ash/system/chromeos/network/network_observer.h"
9#include "ash/system/chromeos/network/network_state_notifier.h"
10#include "ash/system/tray/system_tray_delegate.h"
11#include "ash/system/tray/system_tray_notifier.h"
12#include "base/bind.h"
13#include "base/memory/scoped_ptr.h"
14#include "base/strings/utf_string_conversions.h"
15#include "base/values.h"
16#include "chromeos/login/login_state.h"
17#include "chromeos/network/device_state.h"
18#include "chromeos/network/network_configuration_handler.h"
19#include "chromeos/network/network_connection_handler.h"
20#include "chromeos/network/network_event_log.h"
21#include "chromeos/network/network_handler_callbacks.h"
22#include "chromeos/network/network_profile.h"
23#include "chromeos/network/network_profile_handler.h"
24#include "chromeos/network/network_state.h"
25#include "chromeos/network/network_state_handler.h"
26#include "grit/ash_strings.h"
27#include "third_party/cros_system_api/dbus/service_constants.h"
28#include "ui/base/l10n/l10n_util.h"
29
30using chromeos::DeviceState;
31using chromeos::NetworkConfigurationHandler;
32using chromeos::NetworkConnectionHandler;
33using chromeos::NetworkHandler;
34using chromeos::NetworkProfile;
35using chromeos::NetworkProfileHandler;
36using chromeos::NetworkState;
37
38namespace ash {
39
40namespace {
41
42// TODO(stevenjb): This should be in service_constants.h
43const char kErrorInProgress[] = "org.chromium.flimflam.Error.InProgress";
44
45// Returns true for carriers that can be activated through Shill instead of
46// through a WebUI dialog.
47bool IsDirectActivatedCarrier(const std::string& carrier) {
48  if (carrier == shill::kCarrierSprint)
49    return true;
50  return false;
51}
52
53void ShowErrorNotification(const std::string& error,
54                           const std::string& service_path) {
55  Shell::GetInstance()->system_tray_notifier()->network_state_notifier()->
56      ShowNetworkConnectError(error, service_path);
57}
58
59void OnConnectFailed(const std::string& service_path,
60                     gfx::NativeWindow owning_window,
61                     const std::string& error_name,
62                     scoped_ptr<base::DictionaryValue> error_data) {
63  NET_LOG_ERROR("Connect Failed: " + error_name, service_path);
64
65  // If a new connect attempt canceled this connect, no need to notify the user.
66  if (error_name == NetworkConnectionHandler::kErrorConnectCanceled)
67    return;
68
69  if (error_name == flimflam::kErrorBadPassphrase ||
70      error_name == NetworkConnectionHandler::kErrorPassphraseRequired ||
71      error_name == NetworkConnectionHandler::kErrorConfigurationRequired ||
72      error_name == NetworkConnectionHandler::kErrorAuthenticationRequired) {
73    ash::Shell::GetInstance()->system_tray_delegate()->ConfigureNetwork(
74        service_path);
75    return;
76  }
77
78  if (error_name == NetworkConnectionHandler::kErrorCertificateRequired) {
79    ash::Shell::GetInstance()->system_tray_delegate()->EnrollOrConfigureNetwork(
80        service_path, owning_window);
81    return;
82  }
83
84  if (error_name == NetworkConnectionHandler::kErrorActivationRequired) {
85    network_connect::ActivateCellular(service_path);
86    return;
87  }
88
89  if (error_name == NetworkConnectionHandler::kErrorConnected ||
90      error_name == NetworkConnectionHandler::kErrorConnecting) {
91    ash::Shell::GetInstance()->system_tray_delegate()->ShowNetworkSettings(
92        service_path);
93    return;
94  }
95
96  // ConnectFailed or unknown error; show a notification.
97  ShowErrorNotification(error_name, service_path);
98
99  // Show a configure dialog for ConnectFailed errors.
100  if (error_name != flimflam::kErrorConnectFailed)
101    return;
102
103  // If Shill reports an InProgress error, don't try to configure the network.
104  std::string dbus_error_name;
105  error_data.get()->GetString(
106      chromeos::network_handler::kDbusErrorName, &dbus_error_name);
107  if (dbus_error_name == kErrorInProgress)
108    return;
109
110  ash::Shell::GetInstance()->system_tray_delegate()->ConfigureNetwork(
111      service_path);
112}
113
114void OnConnectSucceeded(const std::string& service_path) {
115  NET_LOG_USER("Connect Succeeded", service_path);
116  ash::Shell::GetInstance()->system_tray_notifier()->NotifyClearNetworkMessage(
117      NetworkObserver::ERROR_CONNECT_FAILED);
118}
119
120// If |check_error_state| is true, error state for the network is checked,
121// otherwise any current error state is ignored (e.g. for recently configured
122// networks or repeat connect attempts). |owning_window| will be used to parent
123// any configuration UI on failure and may be NULL (in which case the default
124// window will be used).
125void CallConnectToNetwork(const std::string& service_path,
126                          bool check_error_state,
127                          gfx::NativeWindow owning_window) {
128  NET_LOG_USER("ConnectToNetwork", service_path);
129
130  ash::Shell::GetInstance()->system_tray_notifier()->NotifyClearNetworkMessage(
131      NetworkObserver::ERROR_CONNECT_FAILED);
132
133  NetworkHandler::Get()->network_connection_handler()->ConnectToNetwork(
134      service_path,
135      base::Bind(&OnConnectSucceeded, service_path),
136      base::Bind(&OnConnectFailed, service_path, owning_window),
137      check_error_state);
138}
139
140void OnActivateFailed(const std::string& service_path,
141                      const std::string& error_name,
142                      scoped_ptr<base::DictionaryValue> error_data) {
143  NET_LOG_ERROR("Unable to activate network", service_path);
144  ShowErrorNotification(network_connect::kErrorActivateFailed, service_path);
145}
146
147void OnActivateSucceeded(const std::string& service_path) {
148  NET_LOG_USER("Activation Succeeded", service_path);
149}
150
151void OnConfigureFailed(const std::string& error_name,
152                       scoped_ptr<base::DictionaryValue> error_data) {
153  NET_LOG_ERROR("Unable to configure network", "");
154  ShowErrorNotification(NetworkConnectionHandler::kErrorConfigureFailed, "");
155}
156
157void OnConfigureSucceeded(const std::string& service_path) {
158  NET_LOG_USER("Configure Succeeded", service_path);
159  // After configuring a network, ignore any (possibly stale) error state.
160  const bool check_error_state = false;
161  const gfx::NativeWindow owning_window = NULL;
162  CallConnectToNetwork(service_path, check_error_state, owning_window);
163}
164
165void SetPropertiesFailed(const std::string& desc,
166                         const std::string& service_path,
167                         const std::string& config_error_name,
168                         scoped_ptr<base::DictionaryValue> error_data) {
169  NET_LOG_ERROR(desc + ": Failed: " + config_error_name, service_path);
170  ShowErrorNotification(
171      NetworkConnectionHandler::kErrorConfigureFailed, service_path);
172}
173
174void SetPropertiesToClear(base::DictionaryValue* properties_to_set,
175                          std::vector<std::string>* properties_to_clear) {
176  // Move empty string properties to properties_to_clear.
177  for (base::DictionaryValue::Iterator iter(*properties_to_set);
178       !iter.IsAtEnd(); iter.Advance()) {
179    std::string value_str;
180    if (iter.value().GetAsString(&value_str) && value_str.empty())
181      properties_to_clear->push_back(iter.key());
182  }
183  // Remove cleared properties from properties_to_set.
184  for (std::vector<std::string>::iterator iter = properties_to_clear->begin();
185       iter != properties_to_clear->end(); ++iter) {
186    properties_to_set->RemoveWithoutPathExpansion(*iter, NULL);
187  }
188}
189
190void ClearPropertiesAndConnect(
191    const std::string& service_path,
192    const std::vector<std::string>& properties_to_clear) {
193  NET_LOG_USER("ClearPropertiesAndConnect", service_path);
194  // After configuring a network, ignore any (possibly stale) error state.
195  const bool check_error_state = false;
196  const gfx::NativeWindow owning_window = NULL;
197  NetworkHandler::Get()->network_configuration_handler()->ClearProperties(
198      service_path,
199      properties_to_clear,
200      base::Bind(&CallConnectToNetwork,
201                 service_path, check_error_state,
202                 owning_window),
203      base::Bind(&SetPropertiesFailed, "ClearProperties", service_path));
204}
205
206// Returns false if !shared and no valid profile is available, which will
207// trigger an error and abort.
208bool GetNetworkProfilePath(bool shared, std::string* profile_path) {
209  if (shared) {
210    *profile_path = NetworkProfileHandler::kSharedProfilePath;
211    return true;
212  }
213
214  if (!chromeos::LoginState::Get()->IsUserAuthenticated()) {
215    NET_LOG_ERROR("User profile specified before login", "");
216    return false;
217  }
218
219  const NetworkProfile* profile  =
220      NetworkHandler::Get()->network_profile_handler()->
221      GetDefaultUserProfile();
222  if (!profile) {
223    NET_LOG_ERROR("No user profile for unshared network configuration", "");
224    return false;
225  }
226
227  *profile_path = profile->path;
228  return true;
229}
230
231void ConfigureSetProfileSucceeded(
232    const std::string& service_path,
233    scoped_ptr<base::DictionaryValue> properties_to_set) {
234  std::vector<std::string> properties_to_clear;
235  SetPropertiesToClear(properties_to_set.get(), &properties_to_clear);
236  NetworkHandler::Get()->network_configuration_handler()->SetProperties(
237      service_path,
238      *properties_to_set,
239      base::Bind(&ClearPropertiesAndConnect,
240                 service_path,
241                 properties_to_clear),
242      base::Bind(&SetPropertiesFailed, "SetProperties", service_path));
243}
244
245}  // namespace
246
247namespace network_connect {
248
249const char kErrorActivateFailed[] = "activate-failed";
250
251void ConnectToNetwork(const std::string& service_path,
252                      gfx::NativeWindow owning_window) {
253  const bool check_error_state = true;
254  CallConnectToNetwork(service_path, check_error_state, owning_window);
255}
256
257void ActivateCellular(const std::string& service_path) {
258  NET_LOG_USER("ActivateCellular", service_path);
259  const NetworkState* cellular =
260      NetworkHandler::Get()->network_state_handler()->
261      GetNetworkState(service_path);
262  if (!cellular || cellular->type() != flimflam::kTypeCellular) {
263    NET_LOG_ERROR("ActivateCellular with no Service", service_path);
264    return;
265  }
266  const DeviceState* cellular_device =
267      NetworkHandler::Get()->network_state_handler()->
268      GetDeviceState(cellular->device_path());
269  if (!cellular_device) {
270    NET_LOG_ERROR("ActivateCellular with no Device", service_path);
271    return;
272  }
273  if (!IsDirectActivatedCarrier(cellular_device->carrier())) {
274    // For non direct activation, show the mobile setup dialog which can be
275    // used to activate the network. Only show the dialog, if an account
276    // management URL is available.
277    if (!cellular->payment_url().empty())
278      ash::Shell::GetInstance()->system_tray_delegate()->ShowMobileSetup(
279          service_path);
280    return;
281  }
282  if (cellular->activation_state() == flimflam::kActivationStateActivated) {
283    NET_LOG_ERROR("ActivateCellular for activated service", service_path);
284    return;
285  }
286
287  NetworkHandler::Get()->network_connection_handler()->ActivateNetwork(
288      service_path,
289      "",  // carrier
290      base::Bind(&OnActivateSucceeded, service_path),
291      base::Bind(&OnActivateFailed, service_path));
292}
293
294void ConfigureNetworkAndConnect(const std::string& service_path,
295                                const base::DictionaryValue& properties,
296                                bool shared) {
297  NET_LOG_USER("ConfigureNetworkAndConnect", service_path);
298
299  scoped_ptr<base::DictionaryValue> properties_to_set(properties.DeepCopy());
300
301  std::string profile_path;
302  if (!GetNetworkProfilePath(shared, &profile_path)) {
303    ShowErrorNotification(
304        NetworkConnectionHandler::kErrorConfigureFailed, service_path);
305    return;
306  }
307  NetworkHandler::Get()->network_configuration_handler()->SetNetworkProfile(
308      service_path, profile_path,
309      base::Bind(&ConfigureSetProfileSucceeded,
310                 service_path, base::Passed(&properties_to_set)),
311      base::Bind(&SetPropertiesFailed,
312                 "SetProfile: " + profile_path, service_path));
313}
314
315void CreateConfigurationAndConnect(base::DictionaryValue* properties,
316                                   bool shared) {
317  NET_LOG_USER("CreateConfigurationAndConnect", "");
318  std::string profile_path;
319  if (!GetNetworkProfilePath(shared, &profile_path)) {
320    ShowErrorNotification(NetworkConnectionHandler::kErrorConfigureFailed, "");
321    return;
322  }
323  properties->SetStringWithoutPathExpansion(
324      flimflam::kProfileProperty, profile_path);
325  NetworkHandler::Get()->network_configuration_handler()->CreateConfiguration(
326      *properties,
327      base::Bind(&OnConfigureSucceeded),
328      base::Bind(&OnConfigureFailed));
329}
330
331string16 ErrorString(const std::string& error) {
332  if (error.empty())
333    return string16();
334  if (error == flimflam::kErrorOutOfRange)
335    return l10n_util::GetStringUTF16(IDS_CHROMEOS_NETWORK_ERROR_OUT_OF_RANGE);
336  if (error == flimflam::kErrorPinMissing)
337    return l10n_util::GetStringUTF16(IDS_CHROMEOS_NETWORK_ERROR_PIN_MISSING);
338  if (error == flimflam::kErrorDhcpFailed)
339    return l10n_util::GetStringUTF16(IDS_CHROMEOS_NETWORK_ERROR_DHCP_FAILED);
340  if (error == flimflam::kErrorConnectFailed)
341    return l10n_util::GetStringUTF16(IDS_CHROMEOS_NETWORK_ERROR_CONNECT_FAILED);
342  if (error == flimflam::kErrorBadPassphrase)
343    return l10n_util::GetStringUTF16(IDS_CHROMEOS_NETWORK_ERROR_BAD_PASSPHRASE);
344  if (error == flimflam::kErrorBadWEPKey)
345    return l10n_util::GetStringUTF16(IDS_CHROMEOS_NETWORK_ERROR_BAD_WEPKEY);
346  if (error == flimflam::kErrorActivationFailed) {
347    return l10n_util::GetStringUTF16(
348        IDS_CHROMEOS_NETWORK_ERROR_ACTIVATION_FAILED);
349  }
350  if (error == flimflam::kErrorNeedEvdo)
351    return l10n_util::GetStringUTF16(IDS_CHROMEOS_NETWORK_ERROR_NEED_EVDO);
352  if (error == flimflam::kErrorNeedHomeNetwork) {
353    return l10n_util::GetStringUTF16(
354        IDS_CHROMEOS_NETWORK_ERROR_NEED_HOME_NETWORK);
355  }
356  if (error == flimflam::kErrorOtaspFailed)
357    return l10n_util::GetStringUTF16(IDS_CHROMEOS_NETWORK_ERROR_OTASP_FAILED);
358  if (error == flimflam::kErrorAaaFailed)
359    return l10n_util::GetStringUTF16(IDS_CHROMEOS_NETWORK_ERROR_AAA_FAILED);
360  if (error == flimflam::kErrorInternal)
361    return l10n_util::GetStringUTF16(IDS_CHROMEOS_NETWORK_ERROR_INTERNAL);
362  if (error == flimflam::kErrorDNSLookupFailed) {
363    return l10n_util::GetStringUTF16(
364        IDS_CHROMEOS_NETWORK_ERROR_DNS_LOOKUP_FAILED);
365  }
366  if (error == flimflam::kErrorHTTPGetFailed) {
367    return l10n_util::GetStringUTF16(
368        IDS_CHROMEOS_NETWORK_ERROR_HTTP_GET_FAILED);
369  }
370  if (error == flimflam::kErrorIpsecPskAuthFailed) {
371    return l10n_util::GetStringUTF16(
372        IDS_CHROMEOS_NETWORK_ERROR_IPSEC_PSK_AUTH_FAILED);
373  }
374  if (error == flimflam::kErrorIpsecCertAuthFailed ||
375      error == shill::kErrorEapAuthenticationFailed) {
376    return l10n_util::GetStringUTF16(
377        IDS_CHROMEOS_NETWORK_ERROR_CERT_AUTH_FAILED);
378  }
379  if (error == shill::kErrorEapLocalTlsFailed) {
380    return l10n_util::GetStringUTF16(
381        IDS_CHROMEOS_NETWORK_ERROR_EAP_LOCAL_TLS_FAILED);
382  }
383  if (error == shill::kErrorEapRemoteTlsFailed) {
384    return l10n_util::GetStringUTF16(
385        IDS_CHROMEOS_NETWORK_ERROR_EAP_REMOTE_TLS_FAILED);
386  }
387  if (error == flimflam::kErrorPppAuthFailed) {
388    return l10n_util::GetStringUTF16(
389        IDS_CHROMEOS_NETWORK_ERROR_PPP_AUTH_FAILED);
390  }
391
392  if (StringToLowerASCII(error) ==
393      StringToLowerASCII(std::string(flimflam::kUnknownString))) {
394    return l10n_util::GetStringUTF16(IDS_CHROMEOS_NETWORK_ERROR_UNKNOWN);
395  }
396  return l10n_util::GetStringFUTF16(IDS_NETWORK_UNRECOGNIZED_ERROR,
397                                    UTF8ToUTF16(error));
398}
399
400}  // network_connect
401}  // ash
402