1// Copyright (c) 2013 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_profile_handler.h"
6
7#include <algorithm>
8
9#include "base/bind.h"
10#include "base/strings/string_util.h"
11#include "base/values.h"
12#include "chromeos/dbus/dbus_thread_manager.h"
13#include "chromeos/dbus/shill_manager_client.h"
14#include "chromeos/dbus/shill_profile_client.h"
15#include "chromeos/network/network_profile_observer.h"
16#include "dbus/object_path.h"
17#include "third_party/cros_system_api/dbus/service_constants.h"
18
19namespace chromeos {
20
21namespace {
22
23bool ConvertListValueToStringVector(const base::ListValue& string_list,
24                                    std::vector<std::string>* result) {
25  for (size_t i = 0; i < string_list.GetSize(); ++i) {
26    std::string str;
27    if (!string_list.GetString(i, &str))
28      return false;
29    result->push_back(str);
30  }
31  return true;
32}
33
34void LogProfileRequestError(const std::string& profile_path,
35                            const std::string& error_name,
36                            const std::string& error_message) {
37  LOG(ERROR) << "Error when requesting properties for profile "
38             << profile_path << ": " << error_message;
39}
40
41class ProfilePathEquals {
42 public:
43  explicit ProfilePathEquals(const std::string& path)
44      : path_(path) {
45  }
46
47  bool operator()(const NetworkProfile& profile) {
48    return profile.path == path_;
49  }
50
51 private:
52  std::string path_;
53};
54
55}  // namespace
56
57// static
58std::string NetworkProfileHandler::GetSharedProfilePath() {
59  return ShillProfileClient::GetSharedProfilePath();
60}
61
62void NetworkProfileHandler::AddObserver(NetworkProfileObserver* observer) {
63  observers_.AddObserver(observer);
64}
65
66void NetworkProfileHandler::RemoveObserver(NetworkProfileObserver* observer) {
67  observers_.RemoveObserver(observer);
68}
69
70void NetworkProfileHandler::GetManagerPropertiesCallback(
71    DBusMethodCallStatus call_status,
72    const base::DictionaryValue& properties) {
73  if (DBUS_METHOD_CALL_FAILURE) {
74    LOG(ERROR) << "Error when requesting manager properties.";
75    return;
76  }
77
78  const base::Value* profiles = NULL;
79  properties.GetWithoutPathExpansion(shill::kProfilesProperty, &profiles);
80  if (!profiles) {
81    LOG(ERROR) << "Manager properties returned from Shill don't contain "
82               << "the field " << shill::kProfilesProperty;
83    return;
84  }
85  OnPropertyChanged(shill::kProfilesProperty, *profiles);
86}
87
88void NetworkProfileHandler::OnPropertyChanged(const std::string& name,
89                                              const base::Value& value) {
90  if (name != shill::kProfilesProperty)
91    return;
92
93  const base::ListValue* profiles_value = NULL;
94  value.GetAsList(&profiles_value);
95  DCHECK(profiles_value);
96
97  std::vector<std::string> new_profile_paths;
98  bool result = ConvertListValueToStringVector(*profiles_value,
99                                               &new_profile_paths);
100  DCHECK(result);
101
102  VLOG(2) << "Profiles: " << profiles_.size();
103  // Search for removed profiles.
104  std::vector<std::string> removed_profile_paths;
105  for (ProfileList::const_iterator it = profiles_.begin();
106       it != profiles_.end(); ++it) {
107    if (std::find(new_profile_paths.begin(),
108                  new_profile_paths.end(),
109                  it->path) == new_profile_paths.end()) {
110      removed_profile_paths.push_back(it->path);
111    }
112  }
113
114  for (std::vector<std::string>::const_iterator it =
115           removed_profile_paths.begin();
116       it != removed_profile_paths.end(); ++it) {
117    RemoveProfile(*it);
118  }
119
120  for (std::vector<std::string>::const_iterator it = new_profile_paths.begin();
121       it != new_profile_paths.end(); ++it) {
122    // Skip known profiles. The associated userhash should never change.
123    if (GetProfileForPath(*it))
124      continue;
125
126    VLOG(2) << "Requesting properties of profile path " << *it << ".";
127    DBusThreadManager::Get()->GetShillProfileClient()->GetProperties(
128        dbus::ObjectPath(*it),
129        base::Bind(&NetworkProfileHandler::GetProfilePropertiesCallback,
130                   weak_ptr_factory_.GetWeakPtr(),
131                   *it),
132        base::Bind(&LogProfileRequestError, *it));
133  }
134}
135
136void NetworkProfileHandler::GetProfilePropertiesCallback(
137    const std::string& profile_path,
138    const base::DictionaryValue& properties) {
139  std::string userhash;
140  properties.GetStringWithoutPathExpansion(shill::kUserHashProperty, &userhash);
141
142  AddProfile(NetworkProfile(profile_path, userhash));
143}
144
145void NetworkProfileHandler::AddProfile(const NetworkProfile& profile) {
146  VLOG(2) << "Adding profile " << profile.ToDebugString() << ".";
147  profiles_.push_back(profile);
148  FOR_EACH_OBSERVER(NetworkProfileObserver, observers_,
149                    OnProfileAdded(profiles_.back()));
150}
151
152void NetworkProfileHandler::RemoveProfile(const std::string& profile_path) {
153  VLOG(2) << "Removing profile for path " << profile_path << ".";
154  ProfileList::iterator found = std::find_if(profiles_.begin(), profiles_.end(),
155                                             ProfilePathEquals(profile_path));
156  if (found == profiles_.end())
157    return;
158  NetworkProfile profile = *found;
159  profiles_.erase(found);
160  FOR_EACH_OBSERVER(NetworkProfileObserver, observers_,
161                    OnProfileRemoved(profile));
162}
163
164const NetworkProfile* NetworkProfileHandler::GetProfileForPath(
165    const std::string& profile_path) const {
166  ProfileList::const_iterator found =
167      std::find_if(profiles_.begin(), profiles_.end(),
168                   ProfilePathEquals(profile_path));
169
170  if (found == profiles_.end())
171    return NULL;
172  return &*found;
173}
174
175const NetworkProfile* NetworkProfileHandler::GetProfileForUserhash(
176    const std::string& userhash) const {
177  for (NetworkProfileHandler::ProfileList::const_iterator it =
178           profiles_.begin();
179       it != profiles_.end(); ++it) {
180    if (it->userhash == userhash)
181      return &*it;
182  }
183  return NULL;
184}
185
186const NetworkProfile* NetworkProfileHandler::GetDefaultUserProfile() const {
187  for (NetworkProfileHandler::ProfileList::const_iterator it =
188           profiles_.begin();
189       it != profiles_.end(); ++it) {
190    if (!it->userhash.empty())
191      return &*it;
192  }
193  return NULL;
194}
195
196NetworkProfileHandler::NetworkProfileHandler()
197    : weak_ptr_factory_(this) {
198}
199
200void NetworkProfileHandler::Init() {
201  DBusThreadManager::Get()->GetShillManagerClient()->
202      AddPropertyChangedObserver(this);
203
204  // Request the initial profile list.
205  DBusThreadManager::Get()->GetShillManagerClient()->GetProperties(
206      base::Bind(&NetworkProfileHandler::GetManagerPropertiesCallback,
207                 weak_ptr_factory_.GetWeakPtr()));
208}
209
210NetworkProfileHandler::~NetworkProfileHandler() {
211  DBusThreadManager::Get()->GetShillManagerClient()->
212      RemovePropertyChangedObserver(this);
213}
214
215}  // namespace chromeos
216