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