blob: 6fbb21f8eb47aef33cedb850ea00500dd0d1668c [file] [log] [blame]
Darin Petkove7c6ad32012-06-29 10:22:09 +02001// Copyright (c) 2012 The Chromium OS Authors. All rights reserved.
Chris Masone52cd19b2011-06-29 17:23:04 -07002// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
5#include "shill/profile.h"
6
Chris Masone6515aab2011-10-12 16:19:09 -07007#include <set>
Chris Masone52cd19b2011-06-29 17:23:04 -07008#include <string>
Chris Masone6791a432011-07-12 13:23:19 -07009#include <vector>
Chris Masone52cd19b2011-06-29 17:23:04 -070010
Chris Masoneb9c00592011-10-06 13:10:39 -070011#include <base/file_path.h>
Paul Stewarte73d05c2012-03-29 16:26:05 -070012#include <base/file_util.h>
Chris Masone52cd19b2011-06-29 17:23:04 -070013#include <base/logging.h>
Eric Shienbrood3e20a232012-02-16 11:35:56 -050014#include <base/stl_util.h>
Darin Petkova4766822011-07-07 10:42:22 -070015#include <base/string_util.h>
Chris Masone6791a432011-07-12 13:23:19 -070016#include <base/stringprintf.h>
Chris Masone88cbd5f2011-07-03 14:30:04 -070017#include <chromeos/dbus/service_constants.h>
Chris Masone52cd19b2011-06-29 17:23:04 -070018
19#include "shill/adaptor_interfaces.h"
20#include "shill/control_interface.h"
Chris Masoneb9c00592011-10-06 13:10:39 -070021#include "shill/key_file_store.h"
Chris Masone6791a432011-07-12 13:23:19 -070022#include "shill/manager.h"
Chris Masone52cd19b2011-06-29 17:23:04 -070023#include "shill/property_accessor.h"
Chris Masone7aa5f902011-07-11 11:13:35 -070024#include "shill/service.h"
Chris Masone9d779932011-08-25 16:33:41 -070025#include "shill/store_interface.h"
Chris Masone52cd19b2011-06-29 17:23:04 -070026
Chris Masone6515aab2011-10-12 16:19:09 -070027using std::set;
Chris Masone52cd19b2011-06-29 17:23:04 -070028using std::string;
Chris Masone6791a432011-07-12 13:23:19 -070029using std::vector;
Chris Masone52cd19b2011-06-29 17:23:04 -070030
31namespace shill {
Darin Petkova4766822011-07-07 10:42:22 -070032
Darin Petkova4766822011-07-07 10:42:22 -070033Profile::Profile(ControlInterface *control_interface,
Chris Masone7df0c672011-07-15 10:24:54 -070034 Manager *manager,
35 const Identifier &name,
Chris Masone2ae797d2011-08-23 20:41:00 -070036 const string &user_storage_format,
Chris Masone7df0c672011-07-15 10:24:54 -070037 bool connect_to_rpc)
Chris Masone6791a432011-07-12 13:23:19 -070038 : manager_(manager),
Chris Masone7df0c672011-07-15 10:24:54 -070039 name_(name),
Chris Masone2ae797d2011-08-23 20:41:00 -070040 storage_format_(user_storage_format) {
Chris Masone7df0c672011-07-15 10:24:54 -070041 if (connect_to_rpc)
42 adaptor_.reset(control_interface->CreateProfileAdaptor(this));
43
Chris Masone88cbd5f2011-07-03 14:30:04 -070044 // flimflam::kCheckPortalListProperty: Registered in DefaultProfile
45 // flimflam::kCountryProperty: Registered in DefaultProfile
Chris Masone7df0c672011-07-15 10:24:54 -070046 store_.RegisterConstString(flimflam::kNameProperty, &name_.identifier);
Chris Masone88cbd5f2011-07-03 14:30:04 -070047
48 // flimflam::kOfflineModeProperty: Registered in DefaultProfile
49 // flimflam::kPortalURLProperty: Registered in DefaultProfile
50
Chris Masone6791a432011-07-12 13:23:19 -070051 HelpRegisterDerivedStrings(flimflam::kServicesProperty,
52 &Profile::EnumerateAvailableServices,
53 NULL);
Chris Masone7df0c672011-07-15 10:24:54 -070054 HelpRegisterDerivedStrings(flimflam::kEntriesProperty,
55 &Profile::EnumerateEntries,
56 NULL);
Chris Masone52cd19b2011-06-29 17:23:04 -070057}
58
Chris Masone6515aab2011-10-12 16:19:09 -070059Profile::~Profile() {}
Chris Masone52cd19b2011-06-29 17:23:04 -070060
Paul Stewart5dc40aa2011-10-28 19:43:43 -070061bool Profile::InitStorage(GLib *glib, InitStorageOption storage_option,
62 Error *error) {
Chris Masoneb9c00592011-10-06 13:10:39 -070063 FilePath final_path;
64 if (!GetStoragePath(&final_path)) {
Paul Stewartbe005172011-11-02 18:10:29 -070065 Error::PopulateAndLog(error, Error::kInvalidArguments,
Chris Masoneb9c00592011-10-06 13:10:39 -070066 base::StringPrintf("Could not set up profile storage for %s:%s",
Paul Stewartbe005172011-11-02 18:10:29 -070067 name_.user.c_str(), name_.identifier.c_str()));
Chris Masoneb9c00592011-10-06 13:10:39 -070068 return false;
69 }
70 scoped_ptr<KeyFileStore> storage(new KeyFileStore(glib));
71 storage->set_path(final_path);
Paul Stewart5dc40aa2011-10-28 19:43:43 -070072 bool already_exists = storage->IsNonEmpty();
73 if (!already_exists && storage_option != kCreateNew &&
74 storage_option != kCreateOrOpenExisting) {
Paul Stewartbe005172011-11-02 18:10:29 -070075 Error::PopulateAndLog(error, Error::kNotFound,
Paul Stewart5dc40aa2011-10-28 19:43:43 -070076 base::StringPrintf("Profile storage for %s:%s does not already exist",
Paul Stewartbe005172011-11-02 18:10:29 -070077 name_.user.c_str(), name_.identifier.c_str()));
Paul Stewart5dc40aa2011-10-28 19:43:43 -070078 return false;
79 } else if (already_exists && storage_option != kOpenExisting &&
80 storage_option != kCreateOrOpenExisting) {
Paul Stewartbe005172011-11-02 18:10:29 -070081 Error::PopulateAndLog(error, Error::kAlreadyExists,
Paul Stewart5dc40aa2011-10-28 19:43:43 -070082 base::StringPrintf("Profile storage for %s:%s already exists",
Paul Stewartbe005172011-11-02 18:10:29 -070083 name_.user.c_str(), name_.identifier.c_str()));
Paul Stewart5dc40aa2011-10-28 19:43:43 -070084 return false;
85 }
Chris Masoneb9c00592011-10-06 13:10:39 -070086 if (!storage->Open()) {
Paul Stewartbe005172011-11-02 18:10:29 -070087 Error::PopulateAndLog(error, Error::kInternalError,
Chris Masoneb9c00592011-10-06 13:10:39 -070088 base::StringPrintf("Could not open profile storage for %s:%s",
Paul Stewartbe005172011-11-02 18:10:29 -070089 name_.user.c_str(), name_.identifier.c_str()));
Chris Masoneb9c00592011-10-06 13:10:39 -070090 return false;
91 }
Paul Stewart5dc40aa2011-10-28 19:43:43 -070092 if (!already_exists) {
93 // Add a descriptive header to the profile so even if nothing is stored
94 // to it, it still has some content. Completely empty keyfiles are not
95 // valid for reading.
96 storage->SetHeader(
97 base::StringPrintf("Profile %s:%s", name_.user.c_str(),
98 name_.identifier.c_str()));
99 }
Chris Masoneb9c00592011-10-06 13:10:39 -0700100 set_storage(storage.release());
101 return true;
102}
103
Paul Stewarte73d05c2012-03-29 16:26:05 -0700104bool Profile::RemoveStorage(GLib *glib, Error *error) {
105 FilePath path;
106
107 CHECK(!storage_.get());
108
109 if (!GetStoragePath(&path)) {
110 Error::PopulateAndLog(
111 error, Error::kInvalidArguments,
112 base::StringPrintf("Could get the storage path for %s:%s",
113 name_.user.c_str(), name_.identifier.c_str()));
114 return false;
115 }
116
117 if (!file_util::Delete(path, false)) {
118 Error::PopulateAndLog(
119 error, Error::kOperationFailed,
120 base::StringPrintf("Could not remove path %s", path.value().c_str()));
121 return false;
122 }
123
124 return true;
125}
126
Chris Masone7df0c672011-07-15 10:24:54 -0700127string Profile::GetFriendlyName() {
128 return (name_.user.empty() ? "" : name_.user + "/") + name_.identifier;
129}
130
131string Profile::GetRpcIdentifier() {
Paul Stewart19c871d2011-12-15 16:10:13 -0800132 if (!adaptor_.get()) {
133 // NB: This condition happens in unit tests.
134 return string();
135 }
Chris Masone7df0c672011-07-15 10:24:54 -0700136 return adaptor_->GetRpcIdentifier();
137}
138
Chris Masoneb9c00592011-10-06 13:10:39 -0700139void Profile::set_storage(StoreInterface *storage) {
140 storage_.reset(storage);
141}
142
Chris Masone6791a432011-07-12 13:23:19 -0700143bool Profile::AdoptService(const ServiceRefPtr &service) {
Paul Stewart451aa7f2012-04-11 19:07:58 -0700144 if (service->profile() == this) {
Chris Masone6791a432011-07-12 13:23:19 -0700145 return false;
Paul Stewart451aa7f2012-04-11 19:07:58 -0700146 }
Chris Masone6791a432011-07-12 13:23:19 -0700147 service->set_profile(this);
Chris Masone6515aab2011-10-12 16:19:09 -0700148 return service->Save(storage_.get()) && storage_->Flush();
Chris Masone6791a432011-07-12 13:23:19 -0700149}
150
Chris Masone6515aab2011-10-12 16:19:09 -0700151bool Profile::AbandonService(const ServiceRefPtr &service) {
152 if (service->profile() == this)
153 service->set_profile(NULL);
154 return storage_->DeleteGroup(service->GetStorageIdentifier()) &&
155 storage_->Flush();
Chris Masone6791a432011-07-12 13:23:19 -0700156}
157
Chris Masone6515aab2011-10-12 16:19:09 -0700158bool Profile::UpdateService(const ServiceRefPtr &service) {
159 return service->Save(storage_.get()) && storage_->Flush();
Chris Masone6791a432011-07-12 13:23:19 -0700160}
161
Paul Stewartbba6a5b2011-11-02 18:45:59 -0700162bool Profile::ConfigureService(const ServiceRefPtr &service) {
163 if (!ContainsService(service))
Chris Masone6515aab2011-10-12 16:19:09 -0700164 return false;
165 service->set_profile(this);
166 return service->Load(storage_.get());
Chris Masone6791a432011-07-12 13:23:19 -0700167}
168
Paul Stewarta41e38d2011-11-11 07:47:29 -0800169bool Profile::ConfigureDevice(const DeviceRefPtr &device) {
170 return device->Load(storage_.get());
171}
172
Chris Masone6515aab2011-10-12 16:19:09 -0700173bool Profile::ContainsService(const ServiceConstRefPtr &service) {
Paul Stewartbba6a5b2011-11-02 18:45:59 -0700174 return service->IsLoadableFrom(storage_.get());
Chris Masone7aa5f902011-07-11 11:13:35 -0700175}
176
Paul Stewart75225512012-01-26 22:51:33 -0800177void Profile::DeleteEntry(const std::string &entry_name, Error *error) {
178 if (!storage_->ContainsGroup(entry_name)) {
179 Error::PopulateAndLog(error, Error::kNotFound,
180 base::StringPrintf("Entry %s does not exist in profile",
181 entry_name.c_str()));
182 return;
183 }
184 if (!manager_->HandleProfileEntryDeletion(this, entry_name)) {
185 // If HandleProfileEntryDeletion() returns succeeds, DeleteGroup()
186 // has already been called when AbandonService was called.
187 // Otherwise, we need to delete the group ourselves.
188 storage_->DeleteGroup(entry_name);
189 }
Paul Stewart0756db92012-01-27 08:34:47 -0800190 Save();
191}
192
193ServiceRefPtr Profile::GetServiceFromEntry(const std::string &entry_name,
194 Error *error) {
195 return manager_->GetServiceWithStorageIdentifier(this, entry_name, error);
Paul Stewart75225512012-01-26 22:51:33 -0800196}
197
Paul Stewarta41e38d2011-11-11 07:47:29 -0800198bool Profile::IsValidIdentifierToken(const string &token) {
Darin Petkova4766822011-07-07 10:42:22 -0700199 if (token.empty()) {
200 return false;
201 }
202 for (string::const_iterator it = token.begin(); it != token.end(); ++it) {
203 if (!IsAsciiAlpha(*it) && !IsAsciiDigit(*it)) {
204 return false;
205 }
206 }
207 return true;
208}
209
210bool Profile::ParseIdentifier(const string &raw, Identifier *parsed) {
211 if (raw.empty()) {
212 return false;
213 }
214 if (raw[0] == '~') {
215 // Format: "~user/identifier".
216 size_t slash = raw.find('/');
217 if (slash == string::npos) {
218 return false;
219 }
220 string user(raw.begin() + 1, raw.begin() + slash);
221 string identifier(raw.begin() + slash + 1, raw.end());
222 if (!IsValidIdentifierToken(user) || !IsValidIdentifierToken(identifier)) {
223 return false;
224 }
225 parsed->user = user;
226 parsed->identifier = identifier;
227 return true;
228 }
229
230 // Format: "identifier".
231 if (!IsValidIdentifierToken(raw)) {
232 return false;
233 }
234 parsed->user = "";
235 parsed->identifier = raw;
236 return true;
237}
238
Paul Stewart5dc40aa2011-10-28 19:43:43 -0700239bool Profile::MatchesIdentifier(const Identifier &name) const {
240 return name.user == name_.user && name.identifier == name_.identifier;
241}
242
Chris Masoneb9c00592011-10-06 13:10:39 -0700243bool Profile::Save() {
Chris Masone6515aab2011-10-12 16:19:09 -0700244 return storage_->Flush();
Chris Masone9d779932011-08-25 16:33:41 -0700245}
246
Chris Masone2ae797d2011-08-23 20:41:00 -0700247bool Profile::GetStoragePath(FilePath *path) {
248 if (name_.user.empty()) {
249 LOG(ERROR) << "Non-default profiles cannot be stored globally.";
250 return false;
251 }
252 FilePath dir(base::StringPrintf(storage_format_.c_str(), name_.user.c_str()));
Darin Petkova4766822011-07-07 10:42:22 -0700253 // TODO(petkov): Validate the directory permissions, etc.
254 *path = dir.Append(base::StringPrintf("%s.profile",
Chris Masone2ae797d2011-08-23 20:41:00 -0700255 name_.identifier.c_str()));
Darin Petkova4766822011-07-07 10:42:22 -0700256 return true;
257}
258
Gaurav Shah1b7a6162011-11-09 11:41:01 -0800259vector<string> Profile::EnumerateAvailableServices(Error *error) {
Paul Stewart1b253142012-01-26 14:05:52 -0800260 // We should return the Manager's service list if this is the active profile.
261 if (manager_->IsActiveProfile(this)) {
262 return manager_->EnumerateAvailableServices(error);
263 } else {
264 return vector<string>();
265 }
Chris Masone6791a432011-07-12 13:23:19 -0700266}
267
Gaurav Shah1b7a6162011-11-09 11:41:01 -0800268vector<string> Profile::EnumerateEntries(Error */*error*/) {
Chris Masone6515aab2011-10-12 16:19:09 -0700269 set<string> groups(storage_->GetGroups());
Paul Stewart1b253142012-01-26 14:05:52 -0800270 vector<string> service_groups;
271
272 // Filter this list down to only entries that correspond
273 // to a technology. (wifi_*, etc)
274 for (set<string>::iterator it = groups.begin();
275 it != groups.end(); ++it) {
276 if (Technology::IdentifierFromStorageGroup(*it) != Technology::kUnknown)
277 service_groups.push_back(*it);
278 }
279
280 return service_groups;
Chris Masone6791a432011-07-12 13:23:19 -0700281}
282
Darin Petkove7c6ad32012-06-29 10:22:09 +0200283bool Profile::UpdateDevice(const DeviceRefPtr &device) {
284 return false;
285}
286
mukesh agrawalffa3d042011-10-06 15:26:10 -0700287void Profile::HelpRegisterDerivedStrings(
288 const string &name,
Gaurav Shah1b7a6162011-11-09 11:41:01 -0800289 Strings(Profile::*get)(Error *),
mukesh agrawalffa3d042011-10-06 15:26:10 -0700290 void(Profile::*set)(const Strings&, Error *)) {
Chris Masone6791a432011-07-12 13:23:19 -0700291 store_.RegisterDerivedStrings(
292 name,
293 StringsAccessor(new CustomAccessor<Profile, Strings>(this, get, set)));
294}
295
Chris Masone52cd19b2011-06-29 17:23:04 -0700296} // namespace shill