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