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