1 //
2 // Copyright (C) 2012 The Android Open Source Project
3 //
4 // Licensed under the Apache License, Version 2.0 (the "License");
5 // you may not use this file except in compliance with the License.
6 // You may obtain a copy of the License at
7 //
8 // http://www.apache.org/licenses/LICENSE-2.0
9 //
10 // Unless required by applicable law or agreed to in writing, software
11 // distributed under the License is distributed on an "AS IS" BASIS,
12 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 // See the License for the specific language governing permissions and
14 // limitations under the License.
15 //
16
17 #include "shill/profile.h"
18
19 #include <set>
20 #include <string>
21 #include <vector>
22
23 #include <base/files/file_util.h>
24 #include <base/stl_util.h>
25 #include <base/strings/string_split.h>
26 #include <base/strings/string_util.h>
27 #include <base/strings/stringprintf.h>
28 #if defined(__ANDROID__)
29 #include <dbus/service_constants.h>
30 #else
31 #include <chromeos/dbus/service_constants.h>
32 #endif // __ANDROID__
33
34 #include "shill/adaptor_interfaces.h"
35 #include "shill/control_interface.h"
36 #include "shill/logging.h"
37 #include "shill/manager.h"
38 #include "shill/property_accessor.h"
39 #include "shill/service.h"
40 #include "shill/store_factory.h"
41 #include "shill/store_interface.h"
42 #include "shill/stub_storage.h"
43
44 using base::FilePath;
45 using std::set;
46 using std::string;
47 using std::vector;
48
49 namespace shill {
50
51 #if defined(ENABLE_JSON_STORE)
52 namespace {
53 const char kFileExtensionJson[] = "json";
54 }
55 #endif
56
57 // static
58 const char Profile::kUserProfileListPathname[] =
59 RUNDIR "/loaded_profile_list";
60
Profile(ControlInterface * control_interface,Metrics * metrics,Manager * manager,const Identifier & name,const base::FilePath & storage_directory,bool connect_to_rpc)61 Profile::Profile(ControlInterface* control_interface,
62 Metrics* metrics,
63 Manager* manager,
64 const Identifier& name,
65 const base::FilePath& storage_directory,
66 bool connect_to_rpc)
67 : metrics_(metrics),
68 manager_(manager),
69 control_interface_(control_interface),
70 name_(name) {
71 if (connect_to_rpc)
72 adaptor_.reset(control_interface->CreateProfileAdaptor(this));
73
74 // kCheckPortalListProperty: Registered in DefaultProfile
75 // kCountryProperty: Registered in DefaultProfile
76 store_.RegisterConstString(kNameProperty, &name_.identifier);
77 store_.RegisterConstString(kUserHashProperty, &name_.user_hash);
78
79 // kOfflineModeProperty: Registered in DefaultProfile
80 // kPortalURLProperty: Registered in DefaultProfile
81
82 HelpRegisterConstDerivedStrings(kServicesProperty,
83 &Profile::EnumerateAvailableServices);
84 HelpRegisterConstDerivedStrings(kEntriesProperty, &Profile::EnumerateEntries);
85
86 if (name.user.empty()) {
87 // Subtle: Profile is only directly instantiated for user
88 // profiles. And user profiles must have a non-empty
89 // |name.user|. So we want to CHECK here. But Profile is also the
90 // base class for DefaultProfile. So a CHECK here would cause us
91 // to abort whenever we attempt to instantiate a DefaultProfile.
92 //
93 // Instead, we leave |persistent_profile_path_| unintialized. One
94 // of two things will happen: a) we become a DefaultProfile, and
95 // the DefaultProfile ctor sets |persistent_profile_path_|, or b)
96 // we really are destined to be a user Profile. In the latter
97 // case, our |name| argument was invalid,
98 // |persistent_profile_path_| is never set, and we CHECK for an
99 // empty |persistent_profile_path_| in InitStorage().
100 //
101 // TODO(quiche): Clean this up. crbug.com/527553
102 } else {
103 persistent_profile_path_ = GetFinalStoragePath(storage_directory, name);
104 }
105 }
106
~Profile()107 Profile::~Profile() {}
108
InitStorage(InitStorageOption storage_option,Error * error)109 bool Profile::InitStorage(InitStorageOption storage_option, Error* error) {
110 CHECK(!persistent_profile_path_.empty());
111 std::unique_ptr<StoreInterface> storage(
112 StoreFactory::GetInstance()->CreateStore(persistent_profile_path_));
113 bool already_exists = storage->IsNonEmpty();
114 if (!already_exists && storage_option != kCreateNew &&
115 storage_option != kCreateOrOpenExisting) {
116 Error::PopulateAndLog(
117 FROM_HERE, error, Error::kNotFound,
118 base::StringPrintf("Profile storage for %s:%s does not already exist",
119 name_.user.c_str(), name_.identifier.c_str()));
120 return false;
121 } else if (already_exists && storage_option != kOpenExisting &&
122 storage_option != kCreateOrOpenExisting) {
123 Error::PopulateAndLog(
124 FROM_HERE, error, Error::kAlreadyExists,
125 base::StringPrintf("Profile storage for %s:%s already exists",
126 name_.user.c_str(), name_.identifier.c_str()));
127 return false;
128 }
129 if (!storage->Open()) {
130 Error::PopulateAndLog(
131 FROM_HERE, error, Error::kInternalError,
132 base::StringPrintf("Could not open profile storage for %s:%s",
133 name_.user.c_str(), name_.identifier.c_str()));
134 if (already_exists) {
135 // The profile contents are corrupt, or we do not have access to
136 // this file. Move this file out of the way so a future open attempt
137 // will succeed, assuming the failure reason was the former.
138 storage->MarkAsCorrupted();
139 metrics_->NotifyCorruptedProfile();
140 }
141 return false;
142 }
143 if (!already_exists) {
144 // Add a descriptive header to the profile so even if nothing is stored
145 // to it, it still has some content. Completely empty keyfiles are not
146 // valid for reading.
147 storage->SetHeader(
148 base::StringPrintf("Profile %s:%s", name_.user.c_str(),
149 name_.identifier.c_str()));
150 }
151 set_storage(storage.release());
152 manager_->OnProfileStorageInitialized(this);
153 return true;
154 }
155
InitStubStorage()156 void Profile::InitStubStorage() {
157 set_storage(new StubStorage());
158 }
159
RemoveStorage(Error * error)160 bool Profile::RemoveStorage(Error* error) {
161 CHECK(!storage_.get());
162 CHECK(!persistent_profile_path_.empty());
163
164 if (!base::DeleteFile(persistent_profile_path_, false)) {
165 Error::PopulateAndLog(
166 FROM_HERE, error, Error::kOperationFailed,
167 base::StringPrintf("Could not remove path %s",
168 persistent_profile_path_.value().c_str()));
169 return false;
170 }
171
172 return true;
173 }
174
GetFriendlyName()175 string Profile::GetFriendlyName() {
176 return (name_.user.empty() ? "" : name_.user + "/") + name_.identifier;
177 }
178
GetRpcIdentifier()179 string Profile::GetRpcIdentifier() {
180 if (!adaptor_.get()) {
181 return string();
182 }
183 return adaptor_->GetRpcIdentifier();
184 }
185
set_storage(StoreInterface * storage)186 void Profile::set_storage(StoreInterface* storage) {
187 storage_.reset(storage);
188 }
189
AdoptService(const ServiceRefPtr & service)190 bool Profile::AdoptService(const ServiceRefPtr& service) {
191 if (service->profile() == this) {
192 return false;
193 }
194 service->SetProfile(this);
195 return service->Save(storage_.get()) && storage_->Flush();
196 }
197
AbandonService(const ServiceRefPtr & service)198 bool Profile::AbandonService(const ServiceRefPtr& service) {
199 if (service->profile() == this)
200 service->SetProfile(nullptr);
201 return storage_->DeleteGroup(service->GetStorageIdentifier()) &&
202 storage_->Flush();
203 }
204
UpdateService(const ServiceRefPtr & service)205 bool Profile::UpdateService(const ServiceRefPtr& service) {
206 return service->Save(storage_.get()) && storage_->Flush();
207 }
208
LoadService(const ServiceRefPtr & service)209 bool Profile::LoadService(const ServiceRefPtr& service) {
210 if (!ContainsService(service))
211 return false;
212 return service->Load(storage_.get());
213 }
214
ConfigureService(const ServiceRefPtr & service)215 bool Profile::ConfigureService(const ServiceRefPtr& service) {
216 if (!LoadService(service))
217 return false;
218 service->SetProfile(this);
219 return true;
220 }
221
ConfigureDevice(const DeviceRefPtr & device)222 bool Profile::ConfigureDevice(const DeviceRefPtr& device) {
223 return device->Load(storage_.get());
224 }
225
ContainsService(const ServiceConstRefPtr & service)226 bool Profile::ContainsService(const ServiceConstRefPtr& service) {
227 return service->IsLoadableFrom(*storage_.get());
228 }
229
DeleteEntry(const std::string & entry_name,Error * error)230 void Profile::DeleteEntry(const std::string& entry_name, Error* error) {
231 if (!storage_->ContainsGroup(entry_name)) {
232 Error::PopulateAndLog(
233 FROM_HERE, error, Error::kNotFound,
234 base::StringPrintf("Entry %s does not exist in profile",
235 entry_name.c_str()));
236 return;
237 }
238 if (!manager_->HandleProfileEntryDeletion(this, entry_name)) {
239 // If HandleProfileEntryDeletion() returns succeeds, DeleteGroup()
240 // has already been called when AbandonService was called.
241 // Otherwise, we need to delete the group ourselves.
242 storage_->DeleteGroup(entry_name);
243 }
244 Save();
245 }
246
GetServiceFromEntry(const std::string & entry_name,Error * error)247 ServiceRefPtr Profile::GetServiceFromEntry(const std::string& entry_name,
248 Error* error) {
249 if (!storage_->ContainsGroup(entry_name)) {
250 Error::PopulateAndLog(
251 FROM_HERE, error, Error::kNotFound,
252 base::StringPrintf("Entry %s does not exist in profile",
253 entry_name.c_str()));
254 return nullptr;
255 }
256
257 // Lookup the service entry from the registered services.
258 ServiceRefPtr service =
259 manager_->GetServiceWithStorageIdentifier(this, entry_name, error);
260 if (service) {
261 return service;
262 }
263
264 // Load the service entry to a temporary service.
265 return manager_->CreateTemporaryServiceFromProfile(this, entry_name, error);
266 }
267
IsValidIdentifierToken(const string & token)268 bool Profile::IsValidIdentifierToken(const string& token) {
269 if (token.empty()) {
270 return false;
271 }
272 for (auto chr : token) {
273 if (!base::IsAsciiAlpha(chr) && !base::IsAsciiDigit(chr)) {
274 return false;
275 }
276 }
277 return true;
278 }
279
280 // static
ParseIdentifier(const string & raw,Identifier * parsed)281 bool Profile::ParseIdentifier(const string& raw, Identifier* parsed) {
282 if (raw.empty()) {
283 return false;
284 }
285 if (raw[0] == '~') {
286 // Format: "~user/identifier".
287 size_t slash = raw.find('/');
288 if (slash == string::npos) {
289 return false;
290 }
291 string user(raw.begin() + 1, raw.begin() + slash);
292 string identifier(raw.begin() + slash + 1, raw.end());
293 if (!IsValidIdentifierToken(user) || !IsValidIdentifierToken(identifier)) {
294 return false;
295 }
296 parsed->user = user;
297 parsed->identifier = identifier;
298 return true;
299 }
300
301 // Format: "identifier".
302 if (!IsValidIdentifierToken(raw)) {
303 return false;
304 }
305 parsed->user = "";
306 parsed->identifier = raw;
307 return true;
308 }
309
310 // static
IdentifierToString(const Identifier & name)311 string Profile::IdentifierToString(const Identifier& name) {
312 if (name.user.empty()) {
313 // Format: "identifier".
314 return name.identifier;
315 }
316
317 // Format: "~user/identifier".
318 return base::StringPrintf(
319 "~%s/%s", name.user.c_str(), name.identifier.c_str());
320 }
321
322 // static
LoadUserProfileList(const FilePath & path)323 vector<Profile::Identifier> Profile::LoadUserProfileList(const FilePath& path) {
324 vector<Identifier> profile_identifiers;
325 string profile_data;
326 if (!base::ReadFileToString(path, &profile_data)) {
327 return profile_identifiers;
328 }
329
330 vector<string> profile_lines =
331 base::SplitString(profile_data, "\n", base::KEEP_WHITESPACE,
332 base::SPLIT_WANT_ALL);
333 for (const auto& line : profile_lines) {
334 if (line.empty()) {
335 // This will be the case on the last line, so let's not complain about it.
336 continue;
337 }
338 size_t space = line.find(' ');
339 if (space == string::npos || space == 0) {
340 LOG(ERROR) << "Invalid line found in " << path.value()
341 << ": " << line;
342 continue;
343 }
344 string name(line.begin(), line.begin() + space);
345 Identifier identifier;
346 if (!ParseIdentifier(name, &identifier) || identifier.user.empty()) {
347 LOG(ERROR) << "Invalid profile name found in " << path.value()
348 << ": " << name;
349 continue;
350 }
351 identifier.user_hash = string(line.begin() + space + 1, line.end());
352 profile_identifiers.push_back(identifier);
353 }
354
355 return profile_identifiers;
356 }
357
358 // static
SaveUserProfileList(const FilePath & path,const vector<ProfileRefPtr> & profiles)359 bool Profile::SaveUserProfileList(const FilePath& path,
360 const vector<ProfileRefPtr>& profiles) {
361 vector<string> lines;
362 for (const auto& profile : profiles) {
363 Identifier& id = profile->name_;
364 if (id.user.empty()) {
365 continue;
366 }
367 lines.push_back(base::StringPrintf("%s %s\n",
368 IdentifierToString(id).c_str(),
369 id.user_hash.c_str()));
370 }
371 string content = base::JoinString(lines, "");
372 size_t ret = base::WriteFile(path, content.c_str(), content.length());
373 return ret == content.length();
374 }
375
MatchesIdentifier(const Identifier & name) const376 bool Profile::MatchesIdentifier(const Identifier& name) const {
377 return name.user == name_.user && name.identifier == name_.identifier;
378 }
379
Save()380 bool Profile::Save() {
381 return storage_->Flush();
382 }
383
EnumerateAvailableServices(Error * error)384 vector<string> Profile::EnumerateAvailableServices(Error* error) {
385 // We should return the Manager's service list if this is the active profile.
386 if (manager_->IsActiveProfile(this)) {
387 return manager_->EnumerateAvailableServices(error);
388 } else {
389 return vector<string>();
390 }
391 }
392
EnumerateEntries(Error *)393 vector<string> Profile::EnumerateEntries(Error* /*error*/) {
394 vector<string> service_groups;
395
396 // Filter this list down to only entries that correspond
397 // to a technology. (wifi_*, etc)
398 for (const auto& group : storage_->GetGroups()) {
399 if (Technology::IdentifierFromStorageGroup(group) != Technology::kUnknown)
400 service_groups.push_back(group);
401 }
402
403 return service_groups;
404 }
405
UpdateDevice(const DeviceRefPtr & device)406 bool Profile::UpdateDevice(const DeviceRefPtr& device) {
407 return false;
408 }
409
410 #if !defined(DISABLE_WIFI)
UpdateWiFiProvider(const WiFiProvider & wifi_provider)411 bool Profile::UpdateWiFiProvider(const WiFiProvider& wifi_provider) {
412 return false;
413 }
414 #endif // DISABLE_WIFI
415
HelpRegisterConstDerivedStrings(const string & name,Strings (Profile::* get)(Error *))416 void Profile::HelpRegisterConstDerivedStrings(
417 const string& name,
418 Strings(Profile::*get)(Error*)) {
419 store_.RegisterDerivedStrings(
420 name, StringsAccessor(
421 new CustomAccessor<Profile, Strings>(this, get, nullptr)));
422 }
423
424 // static
GetFinalStoragePath(const FilePath & storage_dir,const Identifier & profile_name)425 FilePath Profile::GetFinalStoragePath(
426 const FilePath& storage_dir,
427 const Identifier& profile_name) {
428 FilePath base_path;
429 if (profile_name.user.empty()) { // True for DefaultProfiles.
430 base_path = storage_dir.Append(
431 base::StringPrintf("%s.profile", profile_name.identifier.c_str()));
432 } else {
433 base_path = storage_dir.Append(
434 base::StringPrintf("%s/%s.profile",
435 profile_name.user.c_str(),
436 profile_name.identifier.c_str()));
437 }
438
439 // TODO(petkov): Validate the directory permissions, etc.
440
441 #if defined(ENABLE_JSON_STORE)
442 return base_path.AddExtension(kFileExtensionJson);
443 #else
444 return base_path;
445 #endif
446 }
447
448 } // namespace shill
449