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