blob: ab9b97b65d9967db0aba965608a2ec6bfe3c0980 [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
Paul Stewarte73d05c2012-03-29 16:26:05 -070011#include <base/file_util.h>
Eric Shienbrood3e20a232012-02-16 11:35:56 -050012#include <base/stl_util.h>
Ben Chana0ddf462014-02-06 11:32:42 -080013#include <base/strings/string_split.h>
14#include <base/strings/string_util.h>
15#include <base/strings/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"
mukesh agrawal92496a42014-04-08 16:04:43 -070026#include "shill/stub_storage.h"
Chris Masone52cd19b2011-06-29 17:23:04 -070027
Albert Chaulk0e1cdea2013-02-27 15:32:55 -080028using base::FilePath;
Chris Masone6515aab2011-10-12 16:19:09 -070029using std::set;
Chris Masone52cd19b2011-06-29 17:23:04 -070030using std::string;
Chris Masone6791a432011-07-12 13:23:19 -070031using std::vector;
Chris Masone52cd19b2011-06-29 17:23:04 -070032
33namespace shill {
Darin Petkova4766822011-07-07 10:42:22 -070034
Paul Stewart78af94c2013-04-17 16:02:06 -070035// static
36const char Profile::kUserProfileListPathname[] =
37 RUNDIR "/loaded_profile_list";
38
Darin Petkova4766822011-07-07 10:42:22 -070039Profile::Profile(ControlInterface *control_interface,
Thieu Le5133b712013-02-19 14:47:21 -080040 Metrics *metrics,
Chris Masone7df0c672011-07-15 10:24:54 -070041 Manager *manager,
42 const Identifier &name,
mukesh agrawal0a59a5a2014-04-24 19:10:46 -070043 const string &user_storage_directory,
Chris Masone7df0c672011-07-15 10:24:54 -070044 bool connect_to_rpc)
Thieu Le5133b712013-02-19 14:47:21 -080045 : metrics_(metrics),
46 manager_(manager),
Chris Masone7df0c672011-07-15 10:24:54 -070047 name_(name),
mukesh agrawal0a59a5a2014-04-24 19:10:46 -070048 storage_path_(user_storage_directory) {
Chris Masone7df0c672011-07-15 10:24:54 -070049 if (connect_to_rpc)
50 adaptor_.reset(control_interface->CreateProfileAdaptor(this));
51
Ben Chan5dc58812013-09-20 12:53:25 -070052 // kCheckPortalListProperty: Registered in DefaultProfile
53 // kCountryProperty: Registered in DefaultProfile
54 store_.RegisterConstString(kNameProperty, &name_.identifier);
Paul Stewartf3eced92013-04-17 12:18:22 -070055 store_.RegisterConstString(kUserHashProperty, &name_.user_hash);
Chris Masone88cbd5f2011-07-03 14:30:04 -070056
Ben Chan5dc58812013-09-20 12:53:25 -070057 // kOfflineModeProperty: Registered in DefaultProfile
58 // kPortalURLProperty: Registered in DefaultProfile
Chris Masone88cbd5f2011-07-03 14:30:04 -070059
Ben Chan5dc58812013-09-20 12:53:25 -070060 HelpRegisterConstDerivedStrings(kServicesProperty,
mukesh agrawalbebf1b82013-04-23 15:06:33 -070061 &Profile::EnumerateAvailableServices);
Ben Chan5dc58812013-09-20 12:53:25 -070062 HelpRegisterConstDerivedStrings(kEntriesProperty, &Profile::EnumerateEntries);
Chris Masone52cd19b2011-06-29 17:23:04 -070063}
64
Chris Masone6515aab2011-10-12 16:19:09 -070065Profile::~Profile() {}
Chris Masone52cd19b2011-06-29 17:23:04 -070066
Paul Stewart5dc40aa2011-10-28 19:43:43 -070067bool Profile::InitStorage(GLib *glib, InitStorageOption storage_option,
68 Error *error) {
Chris Masoneb9c00592011-10-06 13:10:39 -070069 FilePath final_path;
70 if (!GetStoragePath(&final_path)) {
Paul Stewartbe005172011-11-02 18:10:29 -070071 Error::PopulateAndLog(error, Error::kInvalidArguments,
Chris Masoneb9c00592011-10-06 13:10:39 -070072 base::StringPrintf("Could not set up profile storage for %s:%s",
Paul Stewartbe005172011-11-02 18:10:29 -070073 name_.user.c_str(), name_.identifier.c_str()));
Chris Masoneb9c00592011-10-06 13:10:39 -070074 return false;
75 }
76 scoped_ptr<KeyFileStore> storage(new KeyFileStore(glib));
77 storage->set_path(final_path);
Paul Stewart5dc40aa2011-10-28 19:43:43 -070078 bool already_exists = storage->IsNonEmpty();
79 if (!already_exists && storage_option != kCreateNew &&
80 storage_option != kCreateOrOpenExisting) {
Paul Stewartbe005172011-11-02 18:10:29 -070081 Error::PopulateAndLog(error, Error::kNotFound,
Paul Stewart5dc40aa2011-10-28 19:43:43 -070082 base::StringPrintf("Profile storage for %s:%s does not already exist",
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 } else if (already_exists && storage_option != kOpenExisting &&
86 storage_option != kCreateOrOpenExisting) {
Paul Stewartbe005172011-11-02 18:10:29 -070087 Error::PopulateAndLog(error, Error::kAlreadyExists,
Paul Stewart5dc40aa2011-10-28 19:43:43 -070088 base::StringPrintf("Profile storage for %s:%s already exists",
Paul Stewartbe005172011-11-02 18:10:29 -070089 name_.user.c_str(), name_.identifier.c_str()));
Paul Stewart5dc40aa2011-10-28 19:43:43 -070090 return false;
91 }
Chris Masoneb9c00592011-10-06 13:10:39 -070092 if (!storage->Open()) {
Paul Stewartbe005172011-11-02 18:10:29 -070093 Error::PopulateAndLog(error, Error::kInternalError,
Chris Masoneb9c00592011-10-06 13:10:39 -070094 base::StringPrintf("Could not open profile storage for %s:%s",
Paul Stewartbe005172011-11-02 18:10:29 -070095 name_.user.c_str(), name_.identifier.c_str()));
Paul Stewart2ebc16d2012-08-23 10:38:39 -070096 if (already_exists) {
mukesh agrawal92496a42014-04-08 16:04:43 -070097 // The profile contents are corrupt, or we do not have access to
Paul Stewart2ebc16d2012-08-23 10:38:39 -070098 // this file. Move this file out of the way so a future open attempt
99 // will succeed, assuming the failure reason was the former.
100 storage->MarkAsCorrupted();
Thieu Le5133b712013-02-19 14:47:21 -0800101 metrics_->NotifyCorruptedProfile();
Paul Stewart2ebc16d2012-08-23 10:38:39 -0700102 }
Chris Masoneb9c00592011-10-06 13:10:39 -0700103 return false;
104 }
Paul Stewart5dc40aa2011-10-28 19:43:43 -0700105 if (!already_exists) {
106 // Add a descriptive header to the profile so even if nothing is stored
107 // to it, it still has some content. Completely empty keyfiles are not
108 // valid for reading.
109 storage->SetHeader(
110 base::StringPrintf("Profile %s:%s", name_.user.c_str(),
111 name_.identifier.c_str()));
112 }
Chris Masoneb9c00592011-10-06 13:10:39 -0700113 set_storage(storage.release());
Paul Stewart3c504012013-01-17 17:49:58 -0800114 manager_->OnProfileStorageInitialized(storage_.get());
Chris Masoneb9c00592011-10-06 13:10:39 -0700115 return true;
116}
117
mukesh agrawal92496a42014-04-08 16:04:43 -0700118void Profile::InitStubStorage() {
119 set_storage(new StubStorage());
120}
121
Paul Stewarte73d05c2012-03-29 16:26:05 -0700122bool Profile::RemoveStorage(GLib *glib, Error *error) {
123 FilePath path;
124
125 CHECK(!storage_.get());
126
127 if (!GetStoragePath(&path)) {
128 Error::PopulateAndLog(
129 error, Error::kInvalidArguments,
130 base::StringPrintf("Could get the storage path for %s:%s",
131 name_.user.c_str(), name_.identifier.c_str()));
132 return false;
133 }
134
Ben Chana0ddf462014-02-06 11:32:42 -0800135 if (!base::DeleteFile(path, false)) {
Paul Stewarte73d05c2012-03-29 16:26:05 -0700136 Error::PopulateAndLog(
137 error, Error::kOperationFailed,
138 base::StringPrintf("Could not remove path %s", path.value().c_str()));
139 return false;
140 }
141
142 return true;
143}
144
Chris Masone7df0c672011-07-15 10:24:54 -0700145string Profile::GetFriendlyName() {
146 return (name_.user.empty() ? "" : name_.user + "/") + name_.identifier;
147}
148
149string Profile::GetRpcIdentifier() {
Paul Stewart19c871d2011-12-15 16:10:13 -0800150 if (!adaptor_.get()) {
151 // NB: This condition happens in unit tests.
152 return string();
153 }
Chris Masone7df0c672011-07-15 10:24:54 -0700154 return adaptor_->GetRpcIdentifier();
155}
156
Chris Masoneb9c00592011-10-06 13:10:39 -0700157void Profile::set_storage(StoreInterface *storage) {
158 storage_.reset(storage);
159}
160
Chris Masone6791a432011-07-12 13:23:19 -0700161bool Profile::AdoptService(const ServiceRefPtr &service) {
Paul Stewart451aa7f2012-04-11 19:07:58 -0700162 if (service->profile() == this) {
Chris Masone6791a432011-07-12 13:23:19 -0700163 return false;
Paul Stewart451aa7f2012-04-11 19:07:58 -0700164 }
Philipp Neubeck79173602012-11-13 21:10:09 +0100165 service->SetProfile(this);
Chris Masone6515aab2011-10-12 16:19:09 -0700166 return service->Save(storage_.get()) && storage_->Flush();
Chris Masone6791a432011-07-12 13:23:19 -0700167}
168
Chris Masone6515aab2011-10-12 16:19:09 -0700169bool Profile::AbandonService(const ServiceRefPtr &service) {
170 if (service->profile() == this)
Philipp Neubeck79173602012-11-13 21:10:09 +0100171 service->SetProfile(NULL);
Chris Masone6515aab2011-10-12 16:19:09 -0700172 return storage_->DeleteGroup(service->GetStorageIdentifier()) &&
173 storage_->Flush();
Chris Masone6791a432011-07-12 13:23:19 -0700174}
175
Chris Masone6515aab2011-10-12 16:19:09 -0700176bool Profile::UpdateService(const ServiceRefPtr &service) {
177 return service->Save(storage_.get()) && storage_->Flush();
Chris Masone6791a432011-07-12 13:23:19 -0700178}
179
Paul Stewart2c575d22012-12-07 12:28:57 -0800180bool Profile::LoadService(const ServiceRefPtr &service) {
Paul Stewartbba6a5b2011-11-02 18:45:59 -0700181 if (!ContainsService(service))
Chris Masone6515aab2011-10-12 16:19:09 -0700182 return false;
Chris Masone6515aab2011-10-12 16:19:09 -0700183 return service->Load(storage_.get());
Chris Masone6791a432011-07-12 13:23:19 -0700184}
185
Paul Stewart2c575d22012-12-07 12:28:57 -0800186bool Profile::ConfigureService(const ServiceRefPtr &service) {
187 if (!LoadService(service))
188 return false;
189 service->SetProfile(this);
190 return true;
191}
192
Paul Stewarta41e38d2011-11-11 07:47:29 -0800193bool Profile::ConfigureDevice(const DeviceRefPtr &device) {
194 return device->Load(storage_.get());
195}
196
Chris Masone6515aab2011-10-12 16:19:09 -0700197bool Profile::ContainsService(const ServiceConstRefPtr &service) {
Paul Stewarte7de2942013-04-25 17:07:31 -0700198 return service->IsLoadableFrom(*storage_.get());
Chris Masone7aa5f902011-07-11 11:13:35 -0700199}
200
Paul Stewart75225512012-01-26 22:51:33 -0800201void Profile::DeleteEntry(const std::string &entry_name, Error *error) {
202 if (!storage_->ContainsGroup(entry_name)) {
203 Error::PopulateAndLog(error, Error::kNotFound,
204 base::StringPrintf("Entry %s does not exist in profile",
205 entry_name.c_str()));
206 return;
207 }
208 if (!manager_->HandleProfileEntryDeletion(this, entry_name)) {
209 // If HandleProfileEntryDeletion() returns succeeds, DeleteGroup()
210 // has already been called when AbandonService was called.
211 // Otherwise, we need to delete the group ourselves.
212 storage_->DeleteGroup(entry_name);
213 }
Paul Stewart0756db92012-01-27 08:34:47 -0800214 Save();
215}
216
217ServiceRefPtr Profile::GetServiceFromEntry(const std::string &entry_name,
218 Error *error) {
219 return manager_->GetServiceWithStorageIdentifier(this, entry_name, error);
Paul Stewart75225512012-01-26 22:51:33 -0800220}
221
Paul Stewarta41e38d2011-11-11 07:47:29 -0800222bool Profile::IsValidIdentifierToken(const string &token) {
Darin Petkova4766822011-07-07 10:42:22 -0700223 if (token.empty()) {
224 return false;
225 }
Paul Stewart6db7b242014-05-02 15:34:21 -0700226 for (auto chr : token) {
227 if (!IsAsciiAlpha(chr) && !IsAsciiDigit(chr)) {
Darin Petkova4766822011-07-07 10:42:22 -0700228 return false;
229 }
230 }
231 return true;
232}
233
Paul Stewartf3eced92013-04-17 12:18:22 -0700234// static
Darin Petkova4766822011-07-07 10:42:22 -0700235bool Profile::ParseIdentifier(const string &raw, Identifier *parsed) {
236 if (raw.empty()) {
237 return false;
238 }
239 if (raw[0] == '~') {
240 // Format: "~user/identifier".
241 size_t slash = raw.find('/');
242 if (slash == string::npos) {
243 return false;
244 }
245 string user(raw.begin() + 1, raw.begin() + slash);
246 string identifier(raw.begin() + slash + 1, raw.end());
247 if (!IsValidIdentifierToken(user) || !IsValidIdentifierToken(identifier)) {
248 return false;
249 }
250 parsed->user = user;
251 parsed->identifier = identifier;
252 return true;
253 }
254
255 // Format: "identifier".
256 if (!IsValidIdentifierToken(raw)) {
257 return false;
258 }
259 parsed->user = "";
260 parsed->identifier = raw;
261 return true;
262}
263
Paul Stewartf3eced92013-04-17 12:18:22 -0700264// static
265string Profile::IdentifierToString(const Identifier &name) {
266 if (name.user.empty()) {
267 // Format: "identifier".
268 return name.identifier;
269 }
270
271 // Format: "~user/identifier".
272 return base::StringPrintf(
273 "~%s/%s", name.user.c_str(), name.identifier.c_str());
274}
275
Paul Stewart78af94c2013-04-17 16:02:06 -0700276// static
277vector<Profile::Identifier> Profile::LoadUserProfileList(const FilePath &path) {
278 vector<Identifier> profile_identifiers;
279 string profile_data;
Ben Chana0ddf462014-02-06 11:32:42 -0800280 if (!base::ReadFileToString(path, &profile_data)) {
Paul Stewart78af94c2013-04-17 16:02:06 -0700281 return profile_identifiers;
282 }
283
284 vector<string> profile_lines;
285 base::SplitStringDontTrim(profile_data, '\n', &profile_lines);
Paul Stewart6db7b242014-05-02 15:34:21 -0700286 for (const auto &line : profile_lines) {
Paul Stewart78af94c2013-04-17 16:02:06 -0700287 if (line.empty()) {
288 // This will be the case on the last line, so let's not complain about it.
289 continue;
290 }
291 size_t space = line.find(' ');
292 if (space == string::npos || space == 0) {
293 LOG(ERROR) << "Invalid line found in " << path.value()
294 << ": " << line;
295 continue;
296 }
297 string name(line.begin(), line.begin() + space);
298 Identifier identifier;
299 if (!ParseIdentifier(name, &identifier) || identifier.user.empty()) {
300 LOG(ERROR) << "Invalid profile name found in " << path.value()
301 << ": " << name;
302 continue;
303 }
304 identifier.user_hash = string(line.begin() + space + 1, line.end());
305 profile_identifiers.push_back(identifier);
306 }
307
308 return profile_identifiers;
309}
310
311// static
312bool Profile::SaveUserProfileList(const FilePath &path,
313 const vector<ProfileRefPtr> &profiles) {
314 vector<string> lines;
Paul Stewart6db7b242014-05-02 15:34:21 -0700315 for (const auto &profile : profiles) {
316 Identifier &id = profile->name_;
Paul Stewart78af94c2013-04-17 16:02:06 -0700317 if (id.user.empty()) {
318 continue;
319 }
320 lines.push_back(base::StringPrintf("%s %s\n",
321 IdentifierToString(id).c_str(),
322 id.user_hash.c_str()));
323 }
324 string content = JoinString(lines, "");
Ben Chan6fbf64f2014-05-21 18:07:01 -0700325 size_t ret = base::WriteFile(path, content.c_str(), content.length());
Paul Stewart78af94c2013-04-17 16:02:06 -0700326 return ret == content.length();
327}
328
Paul Stewart5dc40aa2011-10-28 19:43:43 -0700329bool Profile::MatchesIdentifier(const Identifier &name) const {
330 return name.user == name_.user && name.identifier == name_.identifier;
331}
332
Chris Masoneb9c00592011-10-06 13:10:39 -0700333bool Profile::Save() {
Chris Masone6515aab2011-10-12 16:19:09 -0700334 return storage_->Flush();
Chris Masone9d779932011-08-25 16:33:41 -0700335}
336
Chris Masone2ae797d2011-08-23 20:41:00 -0700337bool Profile::GetStoragePath(FilePath *path) {
338 if (name_.user.empty()) {
339 LOG(ERROR) << "Non-default profiles cannot be stored globally.";
340 return false;
341 }
Darin Petkova4766822011-07-07 10:42:22 -0700342 // TODO(petkov): Validate the directory permissions, etc.
mukesh agrawal0a59a5a2014-04-24 19:10:46 -0700343 *path = storage_path_.Append(
344 base::StringPrintf("%s/%s.profile", name_.user.c_str(),
345 name_.identifier.c_str()));
Darin Petkova4766822011-07-07 10:42:22 -0700346 return true;
347}
348
Gaurav Shah1b7a6162011-11-09 11:41:01 -0800349vector<string> Profile::EnumerateAvailableServices(Error *error) {
Paul Stewart1b253142012-01-26 14:05:52 -0800350 // We should return the Manager's service list if this is the active profile.
351 if (manager_->IsActiveProfile(this)) {
352 return manager_->EnumerateAvailableServices(error);
353 } else {
354 return vector<string>();
355 }
Chris Masone6791a432011-07-12 13:23:19 -0700356}
357
Gaurav Shah1b7a6162011-11-09 11:41:01 -0800358vector<string> Profile::EnumerateEntries(Error */*error*/) {
Paul Stewart1b253142012-01-26 14:05:52 -0800359 vector<string> service_groups;
360
361 // Filter this list down to only entries that correspond
362 // to a technology. (wifi_*, etc)
Paul Stewart6db7b242014-05-02 15:34:21 -0700363 for (const auto &group : storage_->GetGroups()) {
364 if (Technology::IdentifierFromStorageGroup(group) != Technology::kUnknown)
365 service_groups.push_back(group);
Paul Stewart1b253142012-01-26 14:05:52 -0800366 }
367
368 return service_groups;
Chris Masone6791a432011-07-12 13:23:19 -0700369}
370
Darin Petkove7c6ad32012-06-29 10:22:09 +0200371bool Profile::UpdateDevice(const DeviceRefPtr &device) {
372 return false;
373}
374
Wade Guthrie60a37062013-04-02 11:39:09 -0700375bool Profile::UpdateWiFiProvider(const WiFiProvider &wifi_provider) {
376 return false;
377}
378
mukesh agrawalbebf1b82013-04-23 15:06:33 -0700379void Profile::HelpRegisterConstDerivedStrings(
mukesh agrawalffa3d042011-10-06 15:26:10 -0700380 const string &name,
mukesh agrawalbebf1b82013-04-23 15:06:33 -0700381 Strings(Profile::*get)(Error *)) {
Chris Masone6791a432011-07-12 13:23:19 -0700382 store_.RegisterDerivedStrings(
383 name,
mukesh agrawalbebf1b82013-04-23 15:06:33 -0700384 StringsAccessor(new CustomAccessor<Profile, Strings>(this, get, NULL)));
Chris Masone6791a432011-07-12 13:23:19 -0700385}
386
Chris Masone52cd19b2011-06-29 17:23:04 -0700387} // namespace shill