blob: 0533a03407d89f419119cb678f93c140a2c4317d [file] [log] [blame]
Chris Masone52cd19b2011-06-29 17:23:04 -07001// Copyright (c) 2011 The Chromium OS 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 "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>
Chris Masone52cd19b2011-06-29 17:23:04 -070012#include <base/logging.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"
Chris Masone6791a432011-07-12 13:23:19 -070021#include "shill/manager.h"
Chris Masone52cd19b2011-06-29 17:23:04 -070022#include "shill/property_accessor.h"
Chris Masone7aa5f902011-07-11 11:13:35 -070023#include "shill/service.h"
Chris Masone9d779932011-08-25 16:33:41 -070024#include "shill/store_interface.h"
Chris Masone52cd19b2011-06-29 17:23:04 -070025
Chris Masone6515aab2011-10-12 16:19:09 -070026using std::set;
Chris Masone52cd19b2011-06-29 17:23:04 -070027using std::string;
Chris Masone6791a432011-07-12 13:23:19 -070028using std::vector;
Chris Masone52cd19b2011-06-29 17:23:04 -070029
30namespace shill {
Darin Petkova4766822011-07-07 10:42:22 -070031
Darin Petkova4766822011-07-07 10:42:22 -070032Profile::Profile(ControlInterface *control_interface,
Chris Masone7df0c672011-07-15 10:24:54 -070033 Manager *manager,
34 const Identifier &name,
Chris Masone2ae797d2011-08-23 20:41:00 -070035 const string &user_storage_format,
Chris Masone7df0c672011-07-15 10:24:54 -070036 bool connect_to_rpc)
Chris Masone6791a432011-07-12 13:23:19 -070037 : manager_(manager),
Chris Masone7df0c672011-07-15 10:24:54 -070038 name_(name),
Chris Masone2ae797d2011-08-23 20:41:00 -070039 storage_format_(user_storage_format) {
Chris Masone7df0c672011-07-15 10:24:54 -070040 if (connect_to_rpc)
41 adaptor_.reset(control_interface->CreateProfileAdaptor(this));
42
Chris Masone88cbd5f2011-07-03 14:30:04 -070043 // flimflam::kCheckPortalListProperty: Registered in DefaultProfile
44 // flimflam::kCountryProperty: Registered in DefaultProfile
Chris Masone7df0c672011-07-15 10:24:54 -070045 store_.RegisterConstString(flimflam::kNameProperty, &name_.identifier);
Chris Masone88cbd5f2011-07-03 14:30:04 -070046
47 // flimflam::kOfflineModeProperty: Registered in DefaultProfile
48 // flimflam::kPortalURLProperty: Registered in DefaultProfile
49
Chris Masone6791a432011-07-12 13:23:19 -070050 HelpRegisterDerivedStrings(flimflam::kServicesProperty,
51 &Profile::EnumerateAvailableServices,
52 NULL);
Chris Masone7df0c672011-07-15 10:24:54 -070053 HelpRegisterDerivedStrings(flimflam::kEntriesProperty,
54 &Profile::EnumerateEntries,
55 NULL);
Chris Masone52cd19b2011-06-29 17:23:04 -070056}
57
Chris Masone6515aab2011-10-12 16:19:09 -070058Profile::~Profile() {}
Chris Masone52cd19b2011-06-29 17:23:04 -070059
Paul Stewart5dc40aa2011-10-28 19:43:43 -070060bool Profile::InitStorage(GLib *glib, InitStorageOption storage_option,
61 Error *error) {
Chris Masoneb9c00592011-10-06 13:10:39 -070062 FilePath final_path;
63 if (!GetStoragePath(&final_path)) {
Paul Stewartbe005172011-11-02 18:10:29 -070064 Error::PopulateAndLog(error, Error::kInvalidArguments,
Chris Masoneb9c00592011-10-06 13:10:39 -070065 base::StringPrintf("Could not set up profile storage for %s:%s",
Paul Stewartbe005172011-11-02 18:10:29 -070066 name_.user.c_str(), name_.identifier.c_str()));
Chris Masoneb9c00592011-10-06 13:10:39 -070067 return false;
68 }
69 scoped_ptr<KeyFileStore> storage(new KeyFileStore(glib));
70 storage->set_path(final_path);
Paul Stewart5dc40aa2011-10-28 19:43:43 -070071 bool already_exists = storage->IsNonEmpty();
72 if (!already_exists && storage_option != kCreateNew &&
73 storage_option != kCreateOrOpenExisting) {
Paul Stewartbe005172011-11-02 18:10:29 -070074 Error::PopulateAndLog(error, Error::kNotFound,
Paul Stewart5dc40aa2011-10-28 19:43:43 -070075 base::StringPrintf("Profile storage for %s:%s does not already exist",
Paul Stewartbe005172011-11-02 18:10:29 -070076 name_.user.c_str(), name_.identifier.c_str()));
Paul Stewart5dc40aa2011-10-28 19:43:43 -070077 return false;
78 } else if (already_exists && storage_option != kOpenExisting &&
79 storage_option != kCreateOrOpenExisting) {
Paul Stewartbe005172011-11-02 18:10:29 -070080 Error::PopulateAndLog(error, Error::kAlreadyExists,
Paul Stewart5dc40aa2011-10-28 19:43:43 -070081 base::StringPrintf("Profile storage for %s:%s already exists",
Paul Stewartbe005172011-11-02 18:10:29 -070082 name_.user.c_str(), name_.identifier.c_str()));
Paul Stewart5dc40aa2011-10-28 19:43:43 -070083 return false;
84 }
Chris Masoneb9c00592011-10-06 13:10:39 -070085 if (!storage->Open()) {
Paul Stewartbe005172011-11-02 18:10:29 -070086 Error::PopulateAndLog(error, Error::kInternalError,
Chris Masoneb9c00592011-10-06 13:10:39 -070087 base::StringPrintf("Could not open profile storage for %s:%s",
Paul Stewartbe005172011-11-02 18:10:29 -070088 name_.user.c_str(), name_.identifier.c_str()));
Chris Masoneb9c00592011-10-06 13:10:39 -070089 return false;
90 }
Paul Stewart5dc40aa2011-10-28 19:43:43 -070091 if (!already_exists) {
92 // Add a descriptive header to the profile so even if nothing is stored
93 // to it, it still has some content. Completely empty keyfiles are not
94 // valid for reading.
95 storage->SetHeader(
96 base::StringPrintf("Profile %s:%s", name_.user.c_str(),
97 name_.identifier.c_str()));
98 }
Chris Masoneb9c00592011-10-06 13:10:39 -070099 set_storage(storage.release());
100 return true;
101}
102
Chris Masone7df0c672011-07-15 10:24:54 -0700103string Profile::GetFriendlyName() {
104 return (name_.user.empty() ? "" : name_.user + "/") + name_.identifier;
105}
106
107string Profile::GetRpcIdentifier() {
Paul Stewart19c871d2011-12-15 16:10:13 -0800108 if (!adaptor_.get()) {
109 // NB: This condition happens in unit tests.
110 return string();
111 }
Chris Masone7df0c672011-07-15 10:24:54 -0700112 return adaptor_->GetRpcIdentifier();
113}
114
Chris Masoneb9c00592011-10-06 13:10:39 -0700115void Profile::set_storage(StoreInterface *storage) {
116 storage_.reset(storage);
117}
118
Chris Masone6791a432011-07-12 13:23:19 -0700119bool Profile::AdoptService(const ServiceRefPtr &service) {
Chris Masone6515aab2011-10-12 16:19:09 -0700120 if (storage_->ContainsGroup(service->GetStorageIdentifier()))
Chris Masone6791a432011-07-12 13:23:19 -0700121 return false;
122 service->set_profile(this);
Chris Masone6515aab2011-10-12 16:19:09 -0700123 return service->Save(storage_.get()) && storage_->Flush();
Chris Masone6791a432011-07-12 13:23:19 -0700124}
125
Chris Masone6515aab2011-10-12 16:19:09 -0700126bool Profile::AbandonService(const ServiceRefPtr &service) {
127 if (service->profile() == this)
128 service->set_profile(NULL);
129 return storage_->DeleteGroup(service->GetStorageIdentifier()) &&
130 storage_->Flush();
Chris Masone6791a432011-07-12 13:23:19 -0700131}
132
Chris Masone6515aab2011-10-12 16:19:09 -0700133bool Profile::UpdateService(const ServiceRefPtr &service) {
134 return service->Save(storage_.get()) && storage_->Flush();
Chris Masone6791a432011-07-12 13:23:19 -0700135}
136
Paul Stewartbba6a5b2011-11-02 18:45:59 -0700137bool Profile::ConfigureService(const ServiceRefPtr &service) {
138 if (!ContainsService(service))
Chris Masone6515aab2011-10-12 16:19:09 -0700139 return false;
140 service->set_profile(this);
141 return service->Load(storage_.get());
Chris Masone6791a432011-07-12 13:23:19 -0700142}
143
Paul Stewarta41e38d2011-11-11 07:47:29 -0800144bool Profile::ConfigureDevice(const DeviceRefPtr &device) {
145 return device->Load(storage_.get());
146}
147
Chris Masone6515aab2011-10-12 16:19:09 -0700148bool Profile::ContainsService(const ServiceConstRefPtr &service) {
Paul Stewartbba6a5b2011-11-02 18:45:59 -0700149 return service->IsLoadableFrom(storage_.get());
Chris Masone7aa5f902011-07-11 11:13:35 -0700150}
151
Paul Stewart75225512012-01-26 22:51:33 -0800152void Profile::DeleteEntry(const std::string &entry_name, Error *error) {
153 if (!storage_->ContainsGroup(entry_name)) {
154 Error::PopulateAndLog(error, Error::kNotFound,
155 base::StringPrintf("Entry %s does not exist in profile",
156 entry_name.c_str()));
157 return;
158 }
159 if (!manager_->HandleProfileEntryDeletion(this, entry_name)) {
160 // If HandleProfileEntryDeletion() returns succeeds, DeleteGroup()
161 // has already been called when AbandonService was called.
162 // Otherwise, we need to delete the group ourselves.
163 storage_->DeleteGroup(entry_name);
164 }
Paul Stewart0756db92012-01-27 08:34:47 -0800165 Save();
166}
167
168ServiceRefPtr Profile::GetServiceFromEntry(const std::string &entry_name,
169 Error *error) {
170 return manager_->GetServiceWithStorageIdentifier(this, entry_name, error);
Paul Stewart75225512012-01-26 22:51:33 -0800171}
172
Paul Stewarta41e38d2011-11-11 07:47:29 -0800173bool Profile::IsValidIdentifierToken(const string &token) {
Darin Petkova4766822011-07-07 10:42:22 -0700174 if (token.empty()) {
175 return false;
176 }
177 for (string::const_iterator it = token.begin(); it != token.end(); ++it) {
178 if (!IsAsciiAlpha(*it) && !IsAsciiDigit(*it)) {
179 return false;
180 }
181 }
182 return true;
183}
184
185bool Profile::ParseIdentifier(const string &raw, Identifier *parsed) {
186 if (raw.empty()) {
187 return false;
188 }
189 if (raw[0] == '~') {
190 // Format: "~user/identifier".
191 size_t slash = raw.find('/');
192 if (slash == string::npos) {
193 return false;
194 }
195 string user(raw.begin() + 1, raw.begin() + slash);
196 string identifier(raw.begin() + slash + 1, raw.end());
197 if (!IsValidIdentifierToken(user) || !IsValidIdentifierToken(identifier)) {
198 return false;
199 }
200 parsed->user = user;
201 parsed->identifier = identifier;
202 return true;
203 }
204
205 // Format: "identifier".
206 if (!IsValidIdentifierToken(raw)) {
207 return false;
208 }
209 parsed->user = "";
210 parsed->identifier = raw;
211 return true;
212}
213
Paul Stewart5dc40aa2011-10-28 19:43:43 -0700214bool Profile::MatchesIdentifier(const Identifier &name) const {
215 return name.user == name_.user && name.identifier == name_.identifier;
216}
217
Chris Masoneb9c00592011-10-06 13:10:39 -0700218bool Profile::Save() {
Chris Masone6515aab2011-10-12 16:19:09 -0700219 return storage_->Flush();
Chris Masone9d779932011-08-25 16:33:41 -0700220}
221
Chris Masone2ae797d2011-08-23 20:41:00 -0700222bool Profile::GetStoragePath(FilePath *path) {
223 if (name_.user.empty()) {
224 LOG(ERROR) << "Non-default profiles cannot be stored globally.";
225 return false;
226 }
227 FilePath dir(base::StringPrintf(storage_format_.c_str(), name_.user.c_str()));
Darin Petkova4766822011-07-07 10:42:22 -0700228 // TODO(petkov): Validate the directory permissions, etc.
229 *path = dir.Append(base::StringPrintf("%s.profile",
Chris Masone2ae797d2011-08-23 20:41:00 -0700230 name_.identifier.c_str()));
Darin Petkova4766822011-07-07 10:42:22 -0700231 return true;
232}
233
Gaurav Shah1b7a6162011-11-09 11:41:01 -0800234vector<string> Profile::EnumerateAvailableServices(Error *error) {
Paul Stewart1b253142012-01-26 14:05:52 -0800235 // We should return the Manager's service list if this is the active profile.
236 if (manager_->IsActiveProfile(this)) {
237 return manager_->EnumerateAvailableServices(error);
238 } else {
239 return vector<string>();
240 }
Chris Masone6791a432011-07-12 13:23:19 -0700241}
242
Gaurav Shah1b7a6162011-11-09 11:41:01 -0800243vector<string> Profile::EnumerateEntries(Error */*error*/) {
Chris Masone6515aab2011-10-12 16:19:09 -0700244 set<string> groups(storage_->GetGroups());
Paul Stewart1b253142012-01-26 14:05:52 -0800245 vector<string> service_groups;
246
247 // Filter this list down to only entries that correspond
248 // to a technology. (wifi_*, etc)
249 for (set<string>::iterator it = groups.begin();
250 it != groups.end(); ++it) {
251 if (Technology::IdentifierFromStorageGroup(*it) != Technology::kUnknown)
252 service_groups.push_back(*it);
253 }
254
255 return service_groups;
Chris Masone6791a432011-07-12 13:23:19 -0700256}
257
mukesh agrawalffa3d042011-10-06 15:26:10 -0700258void Profile::HelpRegisterDerivedStrings(
259 const string &name,
Gaurav Shah1b7a6162011-11-09 11:41:01 -0800260 Strings(Profile::*get)(Error *),
mukesh agrawalffa3d042011-10-06 15:26:10 -0700261 void(Profile::*set)(const Strings&, Error *)) {
Chris Masone6791a432011-07-12 13:23:19 -0700262 store_.RegisterDerivedStrings(
263 name,
264 StringsAccessor(new CustomAccessor<Profile, Strings>(this, get, set)));
265}
266
Chris Masone52cd19b2011-06-29 17:23:04 -0700267} // namespace shill