blob: 911be124dde464c9c95cc6e93b4e80f2ca7cbcd5 [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>
Paul Stewart78af94c2013-04-17 16:02:06 -070014#include <base/string_split.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"
Christopher Wileyb691efd2012-08-09 13:51:51 -070022#include "shill/logging.h"
Chris Masone6791a432011-07-12 13:23:19 -070023#include "shill/manager.h"
Chris Masone52cd19b2011-06-29 17:23:04 -070024#include "shill/property_accessor.h"
Chris Masone7aa5f902011-07-11 11:13:35 -070025#include "shill/service.h"
Chris Masone9d779932011-08-25 16:33:41 -070026#include "shill/store_interface.h"
mukesh agrawal92496a42014-04-08 16:04:43 -070027#include "shill/stub_storage.h"
Chris Masone52cd19b2011-06-29 17:23:04 -070028
Albert Chaulk0e1cdea2013-02-27 15:32:55 -080029using base::FilePath;
Chris Masone6515aab2011-10-12 16:19:09 -070030using std::set;
Chris Masone52cd19b2011-06-29 17:23:04 -070031using std::string;
Chris Masone6791a432011-07-12 13:23:19 -070032using std::vector;
Chris Masone52cd19b2011-06-29 17:23:04 -070033
34namespace shill {
Darin Petkova4766822011-07-07 10:42:22 -070035
Paul Stewart78af94c2013-04-17 16:02:06 -070036// static
37const char Profile::kUserProfileListPathname[] =
38 RUNDIR "/loaded_profile_list";
39
Darin Petkova4766822011-07-07 10:42:22 -070040Profile::Profile(ControlInterface *control_interface,
Thieu Le5133b712013-02-19 14:47:21 -080041 Metrics *metrics,
Chris Masone7df0c672011-07-15 10:24:54 -070042 Manager *manager,
43 const Identifier &name,
Chris Masone2ae797d2011-08-23 20:41:00 -070044 const string &user_storage_format,
Chris Masone7df0c672011-07-15 10:24:54 -070045 bool connect_to_rpc)
Thieu Le5133b712013-02-19 14:47:21 -080046 : metrics_(metrics),
47 manager_(manager),
Chris Masone7df0c672011-07-15 10:24:54 -070048 name_(name),
Chris Masone2ae797d2011-08-23 20:41:00 -070049 storage_format_(user_storage_format) {
Chris Masone7df0c672011-07-15 10:24:54 -070050 if (connect_to_rpc)
51 adaptor_.reset(control_interface->CreateProfileAdaptor(this));
52
Ben Chan5dc58812013-09-20 12:53:25 -070053 // kCheckPortalListProperty: Registered in DefaultProfile
54 // kCountryProperty: Registered in DefaultProfile
55 store_.RegisterConstString(kNameProperty, &name_.identifier);
Paul Stewartf3eced92013-04-17 12:18:22 -070056 store_.RegisterConstString(kUserHashProperty, &name_.user_hash);
Chris Masone88cbd5f2011-07-03 14:30:04 -070057
Ben Chan5dc58812013-09-20 12:53:25 -070058 // kOfflineModeProperty: Registered in DefaultProfile
59 // kPortalURLProperty: Registered in DefaultProfile
Chris Masone88cbd5f2011-07-03 14:30:04 -070060
Ben Chan5dc58812013-09-20 12:53:25 -070061 HelpRegisterConstDerivedStrings(kServicesProperty,
mukesh agrawalbebf1b82013-04-23 15:06:33 -070062 &Profile::EnumerateAvailableServices);
Ben Chan5dc58812013-09-20 12:53:25 -070063 HelpRegisterConstDerivedStrings(kEntriesProperty, &Profile::EnumerateEntries);
Chris Masone52cd19b2011-06-29 17:23:04 -070064}
65
Chris Masone6515aab2011-10-12 16:19:09 -070066Profile::~Profile() {}
Chris Masone52cd19b2011-06-29 17:23:04 -070067
Paul Stewart5dc40aa2011-10-28 19:43:43 -070068bool Profile::InitStorage(GLib *glib, InitStorageOption storage_option,
69 Error *error) {
Chris Masoneb9c00592011-10-06 13:10:39 -070070 FilePath final_path;
71 if (!GetStoragePath(&final_path)) {
Paul Stewartbe005172011-11-02 18:10:29 -070072 Error::PopulateAndLog(error, Error::kInvalidArguments,
Chris Masoneb9c00592011-10-06 13:10:39 -070073 base::StringPrintf("Could not set up profile storage for %s:%s",
Paul Stewartbe005172011-11-02 18:10:29 -070074 name_.user.c_str(), name_.identifier.c_str()));
Chris Masoneb9c00592011-10-06 13:10:39 -070075 return false;
76 }
77 scoped_ptr<KeyFileStore> storage(new KeyFileStore(glib));
78 storage->set_path(final_path);
Paul Stewart5dc40aa2011-10-28 19:43:43 -070079 bool already_exists = storage->IsNonEmpty();
80 if (!already_exists && storage_option != kCreateNew &&
81 storage_option != kCreateOrOpenExisting) {
Paul Stewartbe005172011-11-02 18:10:29 -070082 Error::PopulateAndLog(error, Error::kNotFound,
Paul Stewart5dc40aa2011-10-28 19:43:43 -070083 base::StringPrintf("Profile storage for %s:%s does not already exist",
Paul Stewartbe005172011-11-02 18:10:29 -070084 name_.user.c_str(), name_.identifier.c_str()));
Paul Stewart5dc40aa2011-10-28 19:43:43 -070085 return false;
86 } else if (already_exists && storage_option != kOpenExisting &&
87 storage_option != kCreateOrOpenExisting) {
Paul Stewartbe005172011-11-02 18:10:29 -070088 Error::PopulateAndLog(error, Error::kAlreadyExists,
Paul Stewart5dc40aa2011-10-28 19:43:43 -070089 base::StringPrintf("Profile storage for %s:%s already exists",
Paul Stewartbe005172011-11-02 18:10:29 -070090 name_.user.c_str(), name_.identifier.c_str()));
Paul Stewart5dc40aa2011-10-28 19:43:43 -070091 return false;
92 }
Chris Masoneb9c00592011-10-06 13:10:39 -070093 if (!storage->Open()) {
Paul Stewartbe005172011-11-02 18:10:29 -070094 Error::PopulateAndLog(error, Error::kInternalError,
Chris Masoneb9c00592011-10-06 13:10:39 -070095 base::StringPrintf("Could not open profile storage for %s:%s",
Paul Stewartbe005172011-11-02 18:10:29 -070096 name_.user.c_str(), name_.identifier.c_str()));
Paul Stewart2ebc16d2012-08-23 10:38:39 -070097 if (already_exists) {
mukesh agrawal92496a42014-04-08 16:04:43 -070098 // The profile contents are corrupt, or we do not have access to
Paul Stewart2ebc16d2012-08-23 10:38:39 -070099 // this file. Move this file out of the way so a future open attempt
100 // will succeed, assuming the failure reason was the former.
101 storage->MarkAsCorrupted();
Thieu Le5133b712013-02-19 14:47:21 -0800102 metrics_->NotifyCorruptedProfile();
Paul Stewart2ebc16d2012-08-23 10:38:39 -0700103 }
Chris Masoneb9c00592011-10-06 13:10:39 -0700104 return false;
105 }
Paul Stewart5dc40aa2011-10-28 19:43:43 -0700106 if (!already_exists) {
107 // Add a descriptive header to the profile so even if nothing is stored
108 // to it, it still has some content. Completely empty keyfiles are not
109 // valid for reading.
110 storage->SetHeader(
111 base::StringPrintf("Profile %s:%s", name_.user.c_str(),
112 name_.identifier.c_str()));
113 }
Chris Masoneb9c00592011-10-06 13:10:39 -0700114 set_storage(storage.release());
Paul Stewart3c504012013-01-17 17:49:58 -0800115 manager_->OnProfileStorageInitialized(storage_.get());
Chris Masoneb9c00592011-10-06 13:10:39 -0700116 return true;
117}
118
mukesh agrawal92496a42014-04-08 16:04:43 -0700119void Profile::InitStubStorage() {
120 set_storage(new StubStorage());
121}
122
Paul Stewarte73d05c2012-03-29 16:26:05 -0700123bool Profile::RemoveStorage(GLib *glib, Error *error) {
124 FilePath path;
125
126 CHECK(!storage_.get());
127
128 if (!GetStoragePath(&path)) {
129 Error::PopulateAndLog(
130 error, Error::kInvalidArguments,
131 base::StringPrintf("Could get the storage path for %s:%s",
132 name_.user.c_str(), name_.identifier.c_str()));
133 return false;
134 }
135
136 if (!file_util::Delete(path, false)) {
137 Error::PopulateAndLog(
138 error, Error::kOperationFailed,
139 base::StringPrintf("Could not remove path %s", path.value().c_str()));
140 return false;
141 }
142
143 return true;
144}
145
Chris Masone7df0c672011-07-15 10:24:54 -0700146string Profile::GetFriendlyName() {
147 return (name_.user.empty() ? "" : name_.user + "/") + name_.identifier;
148}
149
150string Profile::GetRpcIdentifier() {
Paul Stewart19c871d2011-12-15 16:10:13 -0800151 if (!adaptor_.get()) {
152 // NB: This condition happens in unit tests.
153 return string();
154 }
Chris Masone7df0c672011-07-15 10:24:54 -0700155 return adaptor_->GetRpcIdentifier();
156}
157
Chris Masoneb9c00592011-10-06 13:10:39 -0700158void Profile::set_storage(StoreInterface *storage) {
159 storage_.reset(storage);
160}
161
Chris Masone6791a432011-07-12 13:23:19 -0700162bool Profile::AdoptService(const ServiceRefPtr &service) {
Paul Stewart451aa7f2012-04-11 19:07:58 -0700163 if (service->profile() == this) {
Chris Masone6791a432011-07-12 13:23:19 -0700164 return false;
Paul Stewart451aa7f2012-04-11 19:07:58 -0700165 }
Philipp Neubeck79173602012-11-13 21:10:09 +0100166 service->SetProfile(this);
Chris Masone6515aab2011-10-12 16:19:09 -0700167 return service->Save(storage_.get()) && storage_->Flush();
Chris Masone6791a432011-07-12 13:23:19 -0700168}
169
Chris Masone6515aab2011-10-12 16:19:09 -0700170bool Profile::AbandonService(const ServiceRefPtr &service) {
171 if (service->profile() == this)
Philipp Neubeck79173602012-11-13 21:10:09 +0100172 service->SetProfile(NULL);
Chris Masone6515aab2011-10-12 16:19:09 -0700173 return storage_->DeleteGroup(service->GetStorageIdentifier()) &&
174 storage_->Flush();
Chris Masone6791a432011-07-12 13:23:19 -0700175}
176
Chris Masone6515aab2011-10-12 16:19:09 -0700177bool Profile::UpdateService(const ServiceRefPtr &service) {
178 return service->Save(storage_.get()) && storage_->Flush();
Chris Masone6791a432011-07-12 13:23:19 -0700179}
180
Paul Stewart2c575d22012-12-07 12:28:57 -0800181bool Profile::LoadService(const ServiceRefPtr &service) {
Paul Stewartbba6a5b2011-11-02 18:45:59 -0700182 if (!ContainsService(service))
Chris Masone6515aab2011-10-12 16:19:09 -0700183 return false;
Chris Masone6515aab2011-10-12 16:19:09 -0700184 return service->Load(storage_.get());
Chris Masone6791a432011-07-12 13:23:19 -0700185}
186
Paul Stewart2c575d22012-12-07 12:28:57 -0800187bool Profile::ConfigureService(const ServiceRefPtr &service) {
188 if (!LoadService(service))
189 return false;
190 service->SetProfile(this);
191 return true;
192}
193
Paul Stewarta41e38d2011-11-11 07:47:29 -0800194bool Profile::ConfigureDevice(const DeviceRefPtr &device) {
195 return device->Load(storage_.get());
196}
197
Chris Masone6515aab2011-10-12 16:19:09 -0700198bool Profile::ContainsService(const ServiceConstRefPtr &service) {
Paul Stewarte7de2942013-04-25 17:07:31 -0700199 return service->IsLoadableFrom(*storage_.get());
Chris Masone7aa5f902011-07-11 11:13:35 -0700200}
201
Paul Stewart75225512012-01-26 22:51:33 -0800202void Profile::DeleteEntry(const std::string &entry_name, Error *error) {
203 if (!storage_->ContainsGroup(entry_name)) {
204 Error::PopulateAndLog(error, Error::kNotFound,
205 base::StringPrintf("Entry %s does not exist in profile",
206 entry_name.c_str()));
207 return;
208 }
209 if (!manager_->HandleProfileEntryDeletion(this, entry_name)) {
210 // If HandleProfileEntryDeletion() returns succeeds, DeleteGroup()
211 // has already been called when AbandonService was called.
212 // Otherwise, we need to delete the group ourselves.
213 storage_->DeleteGroup(entry_name);
214 }
Paul Stewart0756db92012-01-27 08:34:47 -0800215 Save();
216}
217
218ServiceRefPtr Profile::GetServiceFromEntry(const std::string &entry_name,
219 Error *error) {
220 return manager_->GetServiceWithStorageIdentifier(this, entry_name, error);
Paul Stewart75225512012-01-26 22:51:33 -0800221}
222
Paul Stewarta41e38d2011-11-11 07:47:29 -0800223bool Profile::IsValidIdentifierToken(const string &token) {
Darin Petkova4766822011-07-07 10:42:22 -0700224 if (token.empty()) {
225 return false;
226 }
227 for (string::const_iterator it = token.begin(); it != token.end(); ++it) {
228 if (!IsAsciiAlpha(*it) && !IsAsciiDigit(*it)) {
229 return false;
230 }
231 }
232 return true;
233}
234
Paul Stewartf3eced92013-04-17 12:18:22 -0700235// static
Darin Petkova4766822011-07-07 10:42:22 -0700236bool Profile::ParseIdentifier(const string &raw, Identifier *parsed) {
237 if (raw.empty()) {
238 return false;
239 }
240 if (raw[0] == '~') {
241 // Format: "~user/identifier".
242 size_t slash = raw.find('/');
243 if (slash == string::npos) {
244 return false;
245 }
246 string user(raw.begin() + 1, raw.begin() + slash);
247 string identifier(raw.begin() + slash + 1, raw.end());
248 if (!IsValidIdentifierToken(user) || !IsValidIdentifierToken(identifier)) {
249 return false;
250 }
251 parsed->user = user;
252 parsed->identifier = identifier;
253 return true;
254 }
255
256 // Format: "identifier".
257 if (!IsValidIdentifierToken(raw)) {
258 return false;
259 }
260 parsed->user = "";
261 parsed->identifier = raw;
262 return true;
263}
264
Paul Stewartf3eced92013-04-17 12:18:22 -0700265// static
266string Profile::IdentifierToString(const Identifier &name) {
267 if (name.user.empty()) {
268 // Format: "identifier".
269 return name.identifier;
270 }
271
272 // Format: "~user/identifier".
273 return base::StringPrintf(
274 "~%s/%s", name.user.c_str(), name.identifier.c_str());
275}
276
Paul Stewart78af94c2013-04-17 16:02:06 -0700277// static
278vector<Profile::Identifier> Profile::LoadUserProfileList(const FilePath &path) {
279 vector<Identifier> profile_identifiers;
280 string profile_data;
281 if (!file_util::ReadFileToString(path, &profile_data)) {
282 return profile_identifiers;
283 }
284
285 vector<string> profile_lines;
286 base::SplitStringDontTrim(profile_data, '\n', &profile_lines);
287 vector<string>::const_iterator it;
288 for (it = profile_lines.begin(); it != profile_lines.end(); ++it) {
289 const string &line = *it;
290 if (line.empty()) {
291 // This will be the case on the last line, so let's not complain about it.
292 continue;
293 }
294 size_t space = line.find(' ');
295 if (space == string::npos || space == 0) {
296 LOG(ERROR) << "Invalid line found in " << path.value()
297 << ": " << line;
298 continue;
299 }
300 string name(line.begin(), line.begin() + space);
301 Identifier identifier;
302 if (!ParseIdentifier(name, &identifier) || identifier.user.empty()) {
303 LOG(ERROR) << "Invalid profile name found in " << path.value()
304 << ": " << name;
305 continue;
306 }
307 identifier.user_hash = string(line.begin() + space + 1, line.end());
308 profile_identifiers.push_back(identifier);
309 }
310
311 return profile_identifiers;
312}
313
314// static
315bool Profile::SaveUserProfileList(const FilePath &path,
316 const vector<ProfileRefPtr> &profiles) {
317 vector<string> lines;
318 vector<ProfileRefPtr>::const_iterator it;
319 for (it = profiles.begin(); it != profiles.end(); ++it) {
320 Identifier &id = (*it)->name_;
321 if (id.user.empty()) {
322 continue;
323 }
324 lines.push_back(base::StringPrintf("%s %s\n",
325 IdentifierToString(id).c_str(),
326 id.user_hash.c_str()));
327 }
328 string content = JoinString(lines, "");
329 size_t ret = file_util::WriteFile(path, content.c_str(), content.length());
330 return ret == content.length();
331}
332
Paul Stewart5dc40aa2011-10-28 19:43:43 -0700333bool Profile::MatchesIdentifier(const Identifier &name) const {
334 return name.user == name_.user && name.identifier == name_.identifier;
335}
336
Chris Masoneb9c00592011-10-06 13:10:39 -0700337bool Profile::Save() {
Chris Masone6515aab2011-10-12 16:19:09 -0700338 return storage_->Flush();
Chris Masone9d779932011-08-25 16:33:41 -0700339}
340
Chris Masone2ae797d2011-08-23 20:41:00 -0700341bool Profile::GetStoragePath(FilePath *path) {
342 if (name_.user.empty()) {
343 LOG(ERROR) << "Non-default profiles cannot be stored globally.";
344 return false;
345 }
346 FilePath dir(base::StringPrintf(storage_format_.c_str(), name_.user.c_str()));
Darin Petkova4766822011-07-07 10:42:22 -0700347 // TODO(petkov): Validate the directory permissions, etc.
348 *path = dir.Append(base::StringPrintf("%s.profile",
Chris Masone2ae797d2011-08-23 20:41:00 -0700349 name_.identifier.c_str()));
Darin Petkova4766822011-07-07 10:42:22 -0700350 return true;
351}
352
Gaurav Shah1b7a6162011-11-09 11:41:01 -0800353vector<string> Profile::EnumerateAvailableServices(Error *error) {
Paul Stewart1b253142012-01-26 14:05:52 -0800354 // We should return the Manager's service list if this is the active profile.
355 if (manager_->IsActiveProfile(this)) {
356 return manager_->EnumerateAvailableServices(error);
357 } else {
358 return vector<string>();
359 }
Chris Masone6791a432011-07-12 13:23:19 -0700360}
361
Gaurav Shah1b7a6162011-11-09 11:41:01 -0800362vector<string> Profile::EnumerateEntries(Error */*error*/) {
Chris Masone6515aab2011-10-12 16:19:09 -0700363 set<string> groups(storage_->GetGroups());
Paul Stewart1b253142012-01-26 14:05:52 -0800364 vector<string> service_groups;
365
366 // Filter this list down to only entries that correspond
367 // to a technology. (wifi_*, etc)
368 for (set<string>::iterator it = groups.begin();
369 it != groups.end(); ++it) {
370 if (Technology::IdentifierFromStorageGroup(*it) != Technology::kUnknown)
371 service_groups.push_back(*it);
372 }
373
374 return service_groups;
Chris Masone6791a432011-07-12 13:23:19 -0700375}
376
Darin Petkove7c6ad32012-06-29 10:22:09 +0200377bool Profile::UpdateDevice(const DeviceRefPtr &device) {
378 return false;
379}
380
Wade Guthrie60a37062013-04-02 11:39:09 -0700381bool Profile::UpdateWiFiProvider(const WiFiProvider &wifi_provider) {
382 return false;
383}
384
mukesh agrawalbebf1b82013-04-23 15:06:33 -0700385void Profile::HelpRegisterConstDerivedStrings(
mukesh agrawalffa3d042011-10-06 15:26:10 -0700386 const string &name,
mukesh agrawalbebf1b82013-04-23 15:06:33 -0700387 Strings(Profile::*get)(Error *)) {
Chris Masone6791a432011-07-12 13:23:19 -0700388 store_.RegisterDerivedStrings(
389 name,
mukesh agrawalbebf1b82013-04-23 15:06:33 -0700390 StringsAccessor(new CustomAccessor<Profile, Strings>(this, get, NULL)));
Chris Masone6791a432011-07-12 13:23:19 -0700391}
392
Chris Masone52cd19b2011-06-29 17:23:04 -0700393} // namespace shill