blob: 7d84243d8481503e7c3cf453527fec89ab697bd4 [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>
Eric Shienbrood3e20a232012-02-16 11:35:56 -050013#include <base/stl_util.h>
Darin Petkova4766822011-07-07 10:42:22 -070014#include <base/string_util.h>
Chris Masone6791a432011-07-12 13:23:19 -070015#include <base/stringprintf.h>
Chris Masone88cbd5f2011-07-03 14:30:04 -070016#include <chromeos/dbus/service_constants.h>
Chris Masone52cd19b2011-06-29 17:23:04 -070017
18#include "shill/adaptor_interfaces.h"
19#include "shill/control_interface.h"
Chris Masoneb9c00592011-10-06 13:10:39 -070020#include "shill/key_file_store.h"
Christopher Wileyb691efd2012-08-09 13:51:51 -070021#include "shill/logging.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
Albert Chaulk0e1cdea2013-02-27 15:32:55 -080027using base::FilePath;
Chris Masone6515aab2011-10-12 16:19:09 -070028using std::set;
Chris Masone52cd19b2011-06-29 17:23:04 -070029using std::string;
Chris Masone6791a432011-07-12 13:23:19 -070030using std::vector;
Chris Masone52cd19b2011-06-29 17:23:04 -070031
32namespace shill {
Darin Petkova4766822011-07-07 10:42:22 -070033
Darin Petkova4766822011-07-07 10:42:22 -070034Profile::Profile(ControlInterface *control_interface,
Thieu Le5133b712013-02-19 14:47:21 -080035 Metrics *metrics,
Chris Masone7df0c672011-07-15 10:24:54 -070036 Manager *manager,
37 const Identifier &name,
Chris Masone2ae797d2011-08-23 20:41:00 -070038 const string &user_storage_format,
Chris Masone7df0c672011-07-15 10:24:54 -070039 bool connect_to_rpc)
Thieu Le5133b712013-02-19 14:47:21 -080040 : metrics_(metrics),
41 manager_(manager),
Chris Masone7df0c672011-07-15 10:24:54 -070042 name_(name),
Chris Masone2ae797d2011-08-23 20:41:00 -070043 storage_format_(user_storage_format) {
Chris Masone7df0c672011-07-15 10:24:54 -070044 if (connect_to_rpc)
45 adaptor_.reset(control_interface->CreateProfileAdaptor(this));
46
Chris Masone88cbd5f2011-07-03 14:30:04 -070047 // flimflam::kCheckPortalListProperty: Registered in DefaultProfile
48 // flimflam::kCountryProperty: Registered in DefaultProfile
Chris Masone7df0c672011-07-15 10:24:54 -070049 store_.RegisterConstString(flimflam::kNameProperty, &name_.identifier);
Paul Stewartf3eced92013-04-17 12:18:22 -070050 store_.RegisterConstString(kUserHashProperty, &name_.user_hash);
Chris Masone88cbd5f2011-07-03 14:30:04 -070051
52 // flimflam::kOfflineModeProperty: Registered in DefaultProfile
53 // flimflam::kPortalURLProperty: Registered in DefaultProfile
54
Chris Masone6791a432011-07-12 13:23:19 -070055 HelpRegisterDerivedStrings(flimflam::kServicesProperty,
56 &Profile::EnumerateAvailableServices,
57 NULL);
Chris Masone7df0c672011-07-15 10:24:54 -070058 HelpRegisterDerivedStrings(flimflam::kEntriesProperty,
59 &Profile::EnumerateEntries,
60 NULL);
Chris Masone52cd19b2011-06-29 17:23:04 -070061}
62
Chris Masone6515aab2011-10-12 16:19:09 -070063Profile::~Profile() {}
Chris Masone52cd19b2011-06-29 17:23:04 -070064
Paul Stewart5dc40aa2011-10-28 19:43:43 -070065bool Profile::InitStorage(GLib *glib, InitStorageOption storage_option,
66 Error *error) {
Chris Masoneb9c00592011-10-06 13:10:39 -070067 FilePath final_path;
68 if (!GetStoragePath(&final_path)) {
Paul Stewartbe005172011-11-02 18:10:29 -070069 Error::PopulateAndLog(error, Error::kInvalidArguments,
Chris Masoneb9c00592011-10-06 13:10:39 -070070 base::StringPrintf("Could not set up profile storage for %s:%s",
Paul Stewartbe005172011-11-02 18:10:29 -070071 name_.user.c_str(), name_.identifier.c_str()));
Chris Masoneb9c00592011-10-06 13:10:39 -070072 return false;
73 }
74 scoped_ptr<KeyFileStore> storage(new KeyFileStore(glib));
75 storage->set_path(final_path);
Paul Stewart5dc40aa2011-10-28 19:43:43 -070076 bool already_exists = storage->IsNonEmpty();
77 if (!already_exists && storage_option != kCreateNew &&
78 storage_option != kCreateOrOpenExisting) {
Paul Stewartbe005172011-11-02 18:10:29 -070079 Error::PopulateAndLog(error, Error::kNotFound,
Paul Stewart5dc40aa2011-10-28 19:43:43 -070080 base::StringPrintf("Profile storage for %s:%s does not already exist",
Paul Stewartbe005172011-11-02 18:10:29 -070081 name_.user.c_str(), name_.identifier.c_str()));
Paul Stewart5dc40aa2011-10-28 19:43:43 -070082 return false;
83 } else if (already_exists && storage_option != kOpenExisting &&
84 storage_option != kCreateOrOpenExisting) {
Paul Stewartbe005172011-11-02 18:10:29 -070085 Error::PopulateAndLog(error, Error::kAlreadyExists,
Paul Stewart5dc40aa2011-10-28 19:43:43 -070086 base::StringPrintf("Profile storage for %s:%s already exists",
Paul Stewartbe005172011-11-02 18:10:29 -070087 name_.user.c_str(), name_.identifier.c_str()));
Paul Stewart5dc40aa2011-10-28 19:43:43 -070088 return false;
89 }
Chris Masoneb9c00592011-10-06 13:10:39 -070090 if (!storage->Open()) {
Paul Stewartbe005172011-11-02 18:10:29 -070091 Error::PopulateAndLog(error, Error::kInternalError,
Chris Masoneb9c00592011-10-06 13:10:39 -070092 base::StringPrintf("Could not open profile storage for %s:%s",
Paul Stewartbe005172011-11-02 18:10:29 -070093 name_.user.c_str(), name_.identifier.c_str()));
Paul Stewart2ebc16d2012-08-23 10:38:39 -070094 if (already_exists) {
95 // The profile contents is corrupt, or we do not have access to
96 // this file. Move this file out of the way so a future open attempt
97 // will succeed, assuming the failure reason was the former.
98 storage->MarkAsCorrupted();
Thieu Le5133b712013-02-19 14:47:21 -080099 metrics_->NotifyCorruptedProfile();
Paul Stewart2ebc16d2012-08-23 10:38:39 -0700100 }
Chris Masoneb9c00592011-10-06 13:10:39 -0700101 return false;
102 }
Paul Stewart5dc40aa2011-10-28 19:43:43 -0700103 if (!already_exists) {
104 // Add a descriptive header to the profile so even if nothing is stored
105 // to it, it still has some content. Completely empty keyfiles are not
106 // valid for reading.
107 storage->SetHeader(
108 base::StringPrintf("Profile %s:%s", name_.user.c_str(),
109 name_.identifier.c_str()));
110 }
Chris Masoneb9c00592011-10-06 13:10:39 -0700111 set_storage(storage.release());
Paul Stewart3c504012013-01-17 17:49:58 -0800112 manager_->OnProfileStorageInitialized(storage_.get());
Chris Masoneb9c00592011-10-06 13:10:39 -0700113 return true;
114}
115
Paul Stewarte73d05c2012-03-29 16:26:05 -0700116bool Profile::RemoveStorage(GLib *glib, Error *error) {
117 FilePath path;
118
119 CHECK(!storage_.get());
120
121 if (!GetStoragePath(&path)) {
122 Error::PopulateAndLog(
123 error, Error::kInvalidArguments,
124 base::StringPrintf("Could get the storage path for %s:%s",
125 name_.user.c_str(), name_.identifier.c_str()));
126 return false;
127 }
128
129 if (!file_util::Delete(path, false)) {
130 Error::PopulateAndLog(
131 error, Error::kOperationFailed,
132 base::StringPrintf("Could not remove path %s", path.value().c_str()));
133 return false;
134 }
135
136 return true;
137}
138
Chris Masone7df0c672011-07-15 10:24:54 -0700139string Profile::GetFriendlyName() {
140 return (name_.user.empty() ? "" : name_.user + "/") + name_.identifier;
141}
142
143string Profile::GetRpcIdentifier() {
Paul Stewart19c871d2011-12-15 16:10:13 -0800144 if (!adaptor_.get()) {
145 // NB: This condition happens in unit tests.
146 return string();
147 }
Chris Masone7df0c672011-07-15 10:24:54 -0700148 return adaptor_->GetRpcIdentifier();
149}
150
Chris Masoneb9c00592011-10-06 13:10:39 -0700151void Profile::set_storage(StoreInterface *storage) {
152 storage_.reset(storage);
153}
154
Chris Masone6791a432011-07-12 13:23:19 -0700155bool Profile::AdoptService(const ServiceRefPtr &service) {
Paul Stewart451aa7f2012-04-11 19:07:58 -0700156 if (service->profile() == this) {
Chris Masone6791a432011-07-12 13:23:19 -0700157 return false;
Paul Stewart451aa7f2012-04-11 19:07:58 -0700158 }
Philipp Neubeck79173602012-11-13 21:10:09 +0100159 service->SetProfile(this);
Chris Masone6515aab2011-10-12 16:19:09 -0700160 return service->Save(storage_.get()) && storage_->Flush();
Chris Masone6791a432011-07-12 13:23:19 -0700161}
162
Chris Masone6515aab2011-10-12 16:19:09 -0700163bool Profile::AbandonService(const ServiceRefPtr &service) {
164 if (service->profile() == this)
Philipp Neubeck79173602012-11-13 21:10:09 +0100165 service->SetProfile(NULL);
Chris Masone6515aab2011-10-12 16:19:09 -0700166 return storage_->DeleteGroup(service->GetStorageIdentifier()) &&
167 storage_->Flush();
Chris Masone6791a432011-07-12 13:23:19 -0700168}
169
Chris Masone6515aab2011-10-12 16:19:09 -0700170bool Profile::UpdateService(const ServiceRefPtr &service) {
171 return service->Save(storage_.get()) && storage_->Flush();
Chris Masone6791a432011-07-12 13:23:19 -0700172}
173
Paul Stewart2c575d22012-12-07 12:28:57 -0800174bool Profile::LoadService(const ServiceRefPtr &service) {
Paul Stewartbba6a5b2011-11-02 18:45:59 -0700175 if (!ContainsService(service))
Chris Masone6515aab2011-10-12 16:19:09 -0700176 return false;
Chris Masone6515aab2011-10-12 16:19:09 -0700177 return service->Load(storage_.get());
Chris Masone6791a432011-07-12 13:23:19 -0700178}
179
Paul Stewart2c575d22012-12-07 12:28:57 -0800180bool Profile::ConfigureService(const ServiceRefPtr &service) {
181 if (!LoadService(service))
182 return false;
183 service->SetProfile(this);
184 return true;
185}
186
Paul Stewarta41e38d2011-11-11 07:47:29 -0800187bool Profile::ConfigureDevice(const DeviceRefPtr &device) {
188 return device->Load(storage_.get());
189}
190
Chris Masone6515aab2011-10-12 16:19:09 -0700191bool Profile::ContainsService(const ServiceConstRefPtr &service) {
Paul Stewartbba6a5b2011-11-02 18:45:59 -0700192 return service->IsLoadableFrom(storage_.get());
Chris Masone7aa5f902011-07-11 11:13:35 -0700193}
194
Paul Stewart75225512012-01-26 22:51:33 -0800195void Profile::DeleteEntry(const std::string &entry_name, Error *error) {
196 if (!storage_->ContainsGroup(entry_name)) {
197 Error::PopulateAndLog(error, Error::kNotFound,
198 base::StringPrintf("Entry %s does not exist in profile",
199 entry_name.c_str()));
200 return;
201 }
202 if (!manager_->HandleProfileEntryDeletion(this, entry_name)) {
203 // If HandleProfileEntryDeletion() returns succeeds, DeleteGroup()
204 // has already been called when AbandonService was called.
205 // Otherwise, we need to delete the group ourselves.
206 storage_->DeleteGroup(entry_name);
207 }
Paul Stewart0756db92012-01-27 08:34:47 -0800208 Save();
209}
210
211ServiceRefPtr Profile::GetServiceFromEntry(const std::string &entry_name,
212 Error *error) {
213 return manager_->GetServiceWithStorageIdentifier(this, entry_name, error);
Paul Stewart75225512012-01-26 22:51:33 -0800214}
215
Paul Stewarta41e38d2011-11-11 07:47:29 -0800216bool Profile::IsValidIdentifierToken(const string &token) {
Darin Petkova4766822011-07-07 10:42:22 -0700217 if (token.empty()) {
218 return false;
219 }
220 for (string::const_iterator it = token.begin(); it != token.end(); ++it) {
221 if (!IsAsciiAlpha(*it) && !IsAsciiDigit(*it)) {
222 return false;
223 }
224 }
225 return true;
226}
227
Paul Stewartf3eced92013-04-17 12:18:22 -0700228// static
Darin Petkova4766822011-07-07 10:42:22 -0700229bool Profile::ParseIdentifier(const string &raw, Identifier *parsed) {
230 if (raw.empty()) {
231 return false;
232 }
233 if (raw[0] == '~') {
234 // Format: "~user/identifier".
235 size_t slash = raw.find('/');
236 if (slash == string::npos) {
237 return false;
238 }
239 string user(raw.begin() + 1, raw.begin() + slash);
240 string identifier(raw.begin() + slash + 1, raw.end());
241 if (!IsValidIdentifierToken(user) || !IsValidIdentifierToken(identifier)) {
242 return false;
243 }
244 parsed->user = user;
245 parsed->identifier = identifier;
246 return true;
247 }
248
249 // Format: "identifier".
250 if (!IsValidIdentifierToken(raw)) {
251 return false;
252 }
253 parsed->user = "";
254 parsed->identifier = raw;
255 return true;
256}
257
Paul Stewartf3eced92013-04-17 12:18:22 -0700258// static
259string Profile::IdentifierToString(const Identifier &name) {
260 if (name.user.empty()) {
261 // Format: "identifier".
262 return name.identifier;
263 }
264
265 // Format: "~user/identifier".
266 return base::StringPrintf(
267 "~%s/%s", name.user.c_str(), name.identifier.c_str());
268}
269
Paul Stewart5dc40aa2011-10-28 19:43:43 -0700270bool Profile::MatchesIdentifier(const Identifier &name) const {
271 return name.user == name_.user && name.identifier == name_.identifier;
272}
273
Chris Masoneb9c00592011-10-06 13:10:39 -0700274bool Profile::Save() {
Chris Masone6515aab2011-10-12 16:19:09 -0700275 return storage_->Flush();
Chris Masone9d779932011-08-25 16:33:41 -0700276}
277
Chris Masone2ae797d2011-08-23 20:41:00 -0700278bool Profile::GetStoragePath(FilePath *path) {
279 if (name_.user.empty()) {
280 LOG(ERROR) << "Non-default profiles cannot be stored globally.";
281 return false;
282 }
283 FilePath dir(base::StringPrintf(storage_format_.c_str(), name_.user.c_str()));
Darin Petkova4766822011-07-07 10:42:22 -0700284 // TODO(petkov): Validate the directory permissions, etc.
285 *path = dir.Append(base::StringPrintf("%s.profile",
Chris Masone2ae797d2011-08-23 20:41:00 -0700286 name_.identifier.c_str()));
Darin Petkova4766822011-07-07 10:42:22 -0700287 return true;
288}
289
Gaurav Shah1b7a6162011-11-09 11:41:01 -0800290vector<string> Profile::EnumerateAvailableServices(Error *error) {
Paul Stewart1b253142012-01-26 14:05:52 -0800291 // We should return the Manager's service list if this is the active profile.
292 if (manager_->IsActiveProfile(this)) {
293 return manager_->EnumerateAvailableServices(error);
294 } else {
295 return vector<string>();
296 }
Chris Masone6791a432011-07-12 13:23:19 -0700297}
298
Gaurav Shah1b7a6162011-11-09 11:41:01 -0800299vector<string> Profile::EnumerateEntries(Error */*error*/) {
Chris Masone6515aab2011-10-12 16:19:09 -0700300 set<string> groups(storage_->GetGroups());
Paul Stewart1b253142012-01-26 14:05:52 -0800301 vector<string> service_groups;
302
303 // Filter this list down to only entries that correspond
304 // to a technology. (wifi_*, etc)
305 for (set<string>::iterator it = groups.begin();
306 it != groups.end(); ++it) {
307 if (Technology::IdentifierFromStorageGroup(*it) != Technology::kUnknown)
308 service_groups.push_back(*it);
309 }
310
311 return service_groups;
Chris Masone6791a432011-07-12 13:23:19 -0700312}
313
Darin Petkove7c6ad32012-06-29 10:22:09 +0200314bool Profile::UpdateDevice(const DeviceRefPtr &device) {
315 return false;
316}
317
Wade Guthrie60a37062013-04-02 11:39:09 -0700318bool Profile::UpdateWiFiProvider(const WiFiProvider &wifi_provider) {
319 return false;
320}
321
mukesh agrawalffa3d042011-10-06 15:26:10 -0700322void Profile::HelpRegisterDerivedStrings(
323 const string &name,
Gaurav Shah1b7a6162011-11-09 11:41:01 -0800324 Strings(Profile::*get)(Error *),
mukesh agrawalffa3d042011-10-06 15:26:10 -0700325 void(Profile::*set)(const Strings&, Error *)) {
Chris Masone6791a432011-07-12 13:23:19 -0700326 store_.RegisterDerivedStrings(
327 name,
328 StringsAccessor(new CustomAccessor<Profile, Strings>(this, get, set)));
329}
330
Chris Masone52cd19b2011-06-29 17:23:04 -0700331} // namespace shill