network_configuration_handler.cc revision c2db58bd994c04d98e4ee2cd7565b71548655fe3
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_configuration_handler.h"
6
7#include <string>
8#include <vector>
9
10#include "base/bind.h"
11#include "base/format_macros.h"
12#include "base/json/json_writer.h"
13#include "base/logging.h"
14#include "base/memory/ref_counted.h"
15#include "base/memory/scoped_ptr.h"
16#include "base/stl_util.h"
17#include "base/strings/stringprintf.h"
18#include "base/values.h"
19#include "chromeos/dbus/dbus_thread_manager.h"
20#include "chromeos/dbus/shill_manager_client.h"
21#include "chromeos/dbus/shill_profile_client.h"
22#include "chromeos/dbus/shill_service_client.h"
23#include "chromeos/network/network_event_log.h"
24#include "chromeos/network/network_state.h"
25#include "chromeos/network/network_state_handler.h"
26#include "dbus/object_path.h"
27#include "third_party/cros_system_api/dbus/service_constants.h"
28
29namespace chromeos {
30
31namespace {
32
33// Strip surrounding "" from keys (if present).
34std::string StripQuotations(const std::string& in_str) {
35  size_t len = in_str.length();
36  if (len >= 2 && in_str[0] == '"' && in_str[len-1] == '"')
37    return in_str.substr(1, len-2);
38  return in_str;
39}
40
41void InvokeErrorCallback(const std::string& error,
42                         const std::string& path,
43                         const network_handler::ErrorCallback& error_callback) {
44  NET_LOG_ERROR(error, path);
45  if (error_callback.is_null())
46    return;
47  scoped_ptr<base::DictionaryValue> error_data(
48      network_handler::CreateErrorData(path, error, ""));
49  error_callback.Run(error, error_data.Pass());
50}
51
52void GetPropertiesCallback(
53    const network_handler::DictionaryResultCallback& callback,
54    const network_handler::ErrorCallback& error_callback,
55    const std::string& service_path,
56    DBusMethodCallStatus call_status,
57    const base::DictionaryValue& properties) {
58  // Get the correct name from WifiHex if necessary.
59  scoped_ptr<base::DictionaryValue> properties_copy(properties.DeepCopy());
60  std::string name = NetworkState::GetNameFromProperties(
61      service_path, properties);
62  if (!name.empty()) {
63    properties_copy->SetStringWithoutPathExpansion(
64        flimflam::kNameProperty, name);
65  }
66  network_handler::GetPropertiesCallback(
67      callback, error_callback, service_path, call_status,
68      *properties_copy.get());
69}
70
71void SetNetworkProfileErrorCallback(
72    const std::string& service_path,
73    const std::string& profile_path,
74    const network_handler::ErrorCallback& error_callback,
75    const std::string& dbus_error_name,
76    const std::string& dbus_error_message) {
77  network_handler::ShillErrorCallbackFunction(
78      "Config.SetNetworkProfile Failed: " + profile_path,
79      service_path, error_callback,
80      dbus_error_name, dbus_error_message);
81}
82
83bool IsPassphrase(const std::string& key) {
84  return key == flimflam::kEapPrivateKeyPasswordProperty ||
85      key == flimflam::kEapPasswordProperty ||
86      key == flimflam::kL2tpIpsecPasswordProperty ||
87      key == flimflam::kOpenVPNPasswordProperty ||
88      key == flimflam::kPassphraseProperty ||
89      key == flimflam::kOpenVPNOTPProperty ||
90      key == flimflam::kEapPrivateKeyProperty ||
91      key == flimflam::kEapPrivateKeyPasswordProperty ||
92      key == flimflam::kEapPinProperty ||
93      key == flimflam::kApnPasswordProperty;
94}
95
96void LogConfigProperties(const std::string& desc,
97                         const std::string& path,
98                         const base::DictionaryValue& properties) {
99  for (base::DictionaryValue::Iterator iter(properties);
100       !iter.IsAtEnd(); iter.Advance()) {
101    std::string v = "******";
102    if (!IsPassphrase(iter.key()))
103      base::JSONWriter::Write(&iter.value(), &v);
104    NET_LOG_DEBUG(desc,  path + "." + iter.key() + "=" + v);
105  }
106}
107
108}  // namespace
109
110// Helper class to request from Shill the profile entries associated with a
111// Service and delete the service from each profile. Triggers either
112// |callback| on success or |error_callback| on failure, and calls
113// |handler|->ProfileEntryDeleterCompleted() on completion to delete itself.
114class NetworkConfigurationHandler::ProfileEntryDeleter
115    : public base::SupportsWeakPtr<ProfileEntryDeleter> {
116 public:
117  ProfileEntryDeleter(NetworkConfigurationHandler* handler,
118                      const std::string& service_path,
119                      const base::Closure& callback,
120                      const network_handler::ErrorCallback& error_callback)
121      : owner_(handler),
122        service_path_(service_path),
123        callback_(callback),
124        error_callback_(error_callback) {
125  }
126
127  void Run() {
128    DBusThreadManager::Get()->GetShillServiceClient()->
129        GetLoadableProfileEntries(
130            dbus::ObjectPath(service_path_),
131            base::Bind(&ProfileEntryDeleter::GetProfileEntriesToDeleteCallback,
132                       AsWeakPtr()));
133  }
134
135 private:
136  void GetProfileEntriesToDeleteCallback(
137      DBusMethodCallStatus call_status,
138      const base::DictionaryValue& profile_entries) {
139    if (call_status != DBUS_METHOD_CALL_SUCCESS) {
140      InvokeErrorCallback(
141          "GetLoadableProfileEntries Failed", service_path_, error_callback_);
142      owner_->ProfileEntryDeleterCompleted(service_path_);  // Deletes this.
143      return;
144    }
145
146    for (base::DictionaryValue::Iterator iter(profile_entries);
147         !iter.IsAtEnd(); iter.Advance()) {
148      std::string profile_path = StripQuotations(iter.key());
149      std::string entry_path;
150      iter.value().GetAsString(&entry_path);
151      if (profile_path.empty() || entry_path.empty()) {
152        NET_LOG_ERROR("Failed to parse Profile Entry", base::StringPrintf(
153            "%s: %s", profile_path.c_str(), entry_path.c_str()));
154        continue;
155      }
156      if (profile_delete_entries_.count(profile_path) != 0) {
157        NET_LOG_ERROR("Multiple Profile Entries", base::StringPrintf(
158            "%s: %s", profile_path.c_str(), entry_path.c_str()));
159        continue;
160      }
161      NET_LOG_DEBUG("Delete Profile Entry", base::StringPrintf(
162          "%s: %s", profile_path.c_str(), entry_path.c_str()));
163      profile_delete_entries_[profile_path] = entry_path;
164      DBusThreadManager::Get()->GetShillProfileClient()->DeleteEntry(
165          dbus::ObjectPath(profile_path),
166          entry_path,
167          base::Bind(&ProfileEntryDeleter::ProfileEntryDeletedCallback,
168                     AsWeakPtr(), profile_path, entry_path),
169          base::Bind(&ProfileEntryDeleter::ShillErrorCallback,
170                     AsWeakPtr(), profile_path, entry_path));
171    }
172  }
173
174  void ProfileEntryDeletedCallback(const std::string& profile_path,
175                                   const std::string& entry) {
176    NET_LOG_DEBUG("Profile Entry Deleted", base::StringPrintf(
177        "%s: %s", profile_path.c_str(), entry.c_str()));
178    profile_delete_entries_.erase(profile_path);
179    if (!profile_delete_entries_.empty())
180      return;
181    // Run the callback if this is the last pending deletion.
182    if (!callback_.is_null())
183      callback_.Run();
184    // Request NetworkStateHandler manager update to update ServiceCompleteList.
185    owner_->network_state_handler_->UpdateManagerProperties();
186    owner_->ProfileEntryDeleterCompleted(service_path_);  // Deletes this.
187  }
188
189  void ShillErrorCallback(const std::string& profile_path,
190                          const std::string& entry,
191                          const std::string& dbus_error_name,
192                          const std::string& dbus_error_message) {
193    // Any Shill Error triggers a failure / error.
194    network_handler::ShillErrorCallbackFunction(
195        "GetLoadableProfileEntries Failed", profile_path, error_callback_,
196        dbus_error_name, dbus_error_message);
197    // Delete this even if there are pending deletions; any callbacks will
198    // safely become no-ops (by invalidating the WeakPtrs).
199    owner_->ProfileEntryDeleterCompleted(service_path_);  // Deletes this.
200  }
201
202  NetworkConfigurationHandler* owner_;  // Unowned
203  std::string service_path_;
204  base::Closure callback_;
205  network_handler::ErrorCallback error_callback_;
206
207  // Map of pending profile entry deletions, indexed by profile path.
208  std::map<std::string, std::string> profile_delete_entries_;
209
210  DISALLOW_COPY_AND_ASSIGN(ProfileEntryDeleter);
211};
212
213// NetworkConfigurationHandler
214
215void NetworkConfigurationHandler::GetProperties(
216    const std::string& service_path,
217    const network_handler::DictionaryResultCallback& callback,
218    const network_handler::ErrorCallback& error_callback) const {
219  DBusThreadManager::Get()->GetShillServiceClient()->GetProperties(
220      dbus::ObjectPath(service_path),
221      base::Bind(&GetPropertiesCallback,
222                 callback, error_callback, service_path));
223}
224
225void NetworkConfigurationHandler::SetProperties(
226    const std::string& service_path,
227    const base::DictionaryValue& properties,
228    const base::Closure& callback,
229    const network_handler::ErrorCallback& error_callback) {
230  if (properties.empty()) {
231    if (!callback.is_null())
232      callback.Run();
233    return;
234  }
235  NET_LOG_USER("SetProperties", service_path);
236  LogConfigProperties("SetProperty", service_path, properties);
237
238  DBusThreadManager::Get()->GetShillServiceClient()->SetProperties(
239      dbus::ObjectPath(service_path),
240      properties,
241      base::Bind(&NetworkConfigurationHandler::SetPropertiesSuccessCallback,
242                 AsWeakPtr(), service_path, callback),
243      base::Bind(&NetworkConfigurationHandler::SetPropertiesErrorCallback,
244                 AsWeakPtr(), service_path, error_callback));
245}
246
247void NetworkConfigurationHandler::ClearProperties(
248    const std::string& service_path,
249    const std::vector<std::string>& names,
250    const base::Closure& callback,
251    const network_handler::ErrorCallback& error_callback) {
252  if (names.empty()) {
253    if (!callback.is_null())
254      callback.Run();
255    return;
256  }
257  NET_LOG_USER("ClearProperties", service_path);
258  for (std::vector<std::string>::const_iterator iter = names.begin();
259       iter != names.end(); ++iter) {
260    NET_LOG_DEBUG("ClearProperty", service_path + "." + *iter);
261  }
262  DBusThreadManager::Get()->GetShillServiceClient()->ClearProperties(
263      dbus::ObjectPath(service_path),
264      names,
265      base::Bind(&NetworkConfigurationHandler::ClearPropertiesSuccessCallback,
266                 AsWeakPtr(), service_path, names, callback, error_callback),
267      base::Bind(&NetworkConfigurationHandler::ClearPropertiesErrorCallback,
268                 AsWeakPtr(), service_path, error_callback));
269}
270
271void NetworkConfigurationHandler::CreateConfiguration(
272    const base::DictionaryValue& properties,
273    const network_handler::StringResultCallback& callback,
274    const network_handler::ErrorCallback& error_callback) {
275  ShillManagerClient* manager =
276      DBusThreadManager::Get()->GetShillManagerClient();
277  std::string type;
278  properties.GetStringWithoutPathExpansion(flimflam::kTypeProperty, &type);
279
280  NET_LOG_USER("CreateConfiguration", type);
281  LogConfigProperties("Configure", type, properties);
282
283  std::string profile;
284  properties.GetStringWithoutPathExpansion(flimflam::kProfileProperty,
285                                           &profile);
286  DCHECK(!profile.empty());
287  manager->ConfigureServiceForProfile(
288      dbus::ObjectPath(profile),
289      properties,
290      base::Bind(&NetworkConfigurationHandler::RunCreateNetworkCallback,
291                 AsWeakPtr(),
292                 callback),
293      base::Bind(&network_handler::ShillErrorCallbackFunction,
294                 "Config.CreateConfiguration Failed",
295                 "",
296                 error_callback));
297}
298
299void NetworkConfigurationHandler::RemoveConfiguration(
300    const std::string& service_path,
301    const base::Closure& callback,
302    const network_handler::ErrorCallback& error_callback) {
303  // Service.Remove is not reliable. Instead, request the profile entries
304  // for the service and remove each entry.
305  if (profile_entry_deleters_.count(service_path)) {
306    InvokeErrorCallback(
307        "RemoveConfiguration In-Progress", service_path, error_callback);
308    return;
309  }
310  NET_LOG_USER("Remove Configuration", service_path);
311  ProfileEntryDeleter* deleter =
312      new ProfileEntryDeleter(this, service_path, callback, error_callback);
313  profile_entry_deleters_[service_path] = deleter;
314  deleter->Run();
315}
316
317void NetworkConfigurationHandler::SetNetworkProfile(
318    const std::string& service_path,
319    const std::string& profile_path,
320    const base::Closure& callback,
321    const network_handler::ErrorCallback& error_callback) {
322  NET_LOG_USER("SetNetworkProfile", service_path + ": " + profile_path);
323  base::StringValue profile_path_value(profile_path);
324  DBusThreadManager::Get()->GetShillServiceClient()->SetProperty(
325      dbus::ObjectPath(service_path),
326      flimflam::kProfileProperty,
327      profile_path_value,
328      callback,
329      base::Bind(&SetNetworkProfileErrorCallback,
330                 service_path, profile_path, error_callback));
331}
332
333// NetworkConfigurationHandler Private methods
334
335NetworkConfigurationHandler::NetworkConfigurationHandler()
336    : network_state_handler_(NULL) {
337}
338
339NetworkConfigurationHandler::~NetworkConfigurationHandler() {
340  STLDeleteContainerPairSecondPointers(
341      profile_entry_deleters_.begin(), profile_entry_deleters_.end());
342}
343
344void NetworkConfigurationHandler::Init(
345    NetworkStateHandler* network_state_handler) {
346  network_state_handler_ = network_state_handler;
347}
348
349void NetworkConfigurationHandler::RunCreateNetworkCallback(
350    const network_handler::StringResultCallback& callback,
351    const dbus::ObjectPath& service_path) {
352  if (!callback.is_null())
353    callback.Run(service_path.value());
354  // This may also get called when CreateConfiguration is used to update an
355  // existing configuration, so request a service update just in case.
356  // TODO(pneubeck): Separate 'Create' and 'Update' calls and only trigger
357  // this on an update.
358  network_state_handler_->RequestUpdateForNetwork(service_path.value());
359}
360
361void NetworkConfigurationHandler::ProfileEntryDeleterCompleted(
362    const std::string& service_path) {
363  std::map<std::string, ProfileEntryDeleter*>::iterator iter =
364      profile_entry_deleters_.find(service_path);
365  DCHECK(iter != profile_entry_deleters_.end());
366  delete iter->second;
367  profile_entry_deleters_.erase(iter);
368}
369
370void NetworkConfigurationHandler::SetPropertiesSuccessCallback(
371    const std::string& service_path,
372    const base::Closure& callback) {
373  if (!callback.is_null())
374    callback.Run();
375  network_state_handler_->RequestUpdateForNetwork(service_path);
376}
377
378void NetworkConfigurationHandler::SetPropertiesErrorCallback(
379    const std::string& service_path,
380    const network_handler::ErrorCallback& error_callback,
381    const std::string& dbus_error_name,
382    const std::string& dbus_error_message) {
383  network_handler::ShillErrorCallbackFunction(
384      "Config.SetProperties Failed",
385      service_path, error_callback,
386      dbus_error_name, dbus_error_message);
387  // Some properties may have changed so request an update regardless.
388  network_state_handler_->RequestUpdateForNetwork(service_path);
389}
390
391void NetworkConfigurationHandler::ClearPropertiesSuccessCallback(
392    const std::string& service_path,
393    const std::vector<std::string>& names,
394    const base::Closure& callback,
395    const network_handler::ErrorCallback& error_callback,
396    const base::ListValue& result) {
397  const std::string kClearPropertiesFailedError("Error.ClearPropertiesFailed");
398  DCHECK(names.size() == result.GetSize())
399      << "Incorrect result size from ClearProperties.";
400
401  bool some_failed = false;
402  for (size_t i = 0; i < result.GetSize(); ++i) {
403    bool success = false;
404    result.GetBoolean(i, &success);
405    if (!success) {
406      NET_LOG_ERROR("ClearProperties Failed: " + names[i], service_path);
407      some_failed = true;
408    }
409  }
410
411  if (some_failed) {
412    if (!error_callback.is_null()) {
413      scoped_ptr<base::DictionaryValue> error_data(
414          network_handler::CreateErrorData(
415              service_path, kClearPropertiesFailedError,
416              base::StringPrintf("Errors: %" PRIuS, result.GetSize())));
417      error_data->Set("errors", result.DeepCopy());
418      scoped_ptr<base::ListValue> name_list(new base::ListValue);
419      name_list->AppendStrings(names);
420      error_data->Set("names", name_list.release());
421      error_callback.Run(kClearPropertiesFailedError, error_data.Pass());
422    }
423  } else if (!callback.is_null()) {
424    callback.Run();
425  }
426  network_state_handler_->RequestUpdateForNetwork(service_path);
427}
428
429void NetworkConfigurationHandler::ClearPropertiesErrorCallback(
430    const std::string& service_path,
431    const network_handler::ErrorCallback& error_callback,
432    const std::string& dbus_error_name,
433    const std::string& dbus_error_message) {
434  network_handler::ShillErrorCallbackFunction(
435      "Config.ClearProperties Failed",
436      service_path, error_callback,
437      dbus_error_name, dbus_error_message);
438  // Some properties may have changed so request an update regardless.
439  network_state_handler_->RequestUpdateForNetwork(service_path);
440}
441
442// static
443NetworkConfigurationHandler* NetworkConfigurationHandler::InitializeForTest(
444    NetworkStateHandler* network_state_handler) {
445  NetworkConfigurationHandler* handler = new NetworkConfigurationHandler();
446  handler->Init(network_state_handler);
447  return handler;
448}
449
450}  // namespace chromeos
451