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