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 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 107 Profile::~Profile() {} 108 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 156 void Profile::InitStubStorage() { 157 set_storage(new StubStorage()); 158 } 159 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 175 string Profile::GetFriendlyName() { 176 return (name_.user.empty() ? "" : name_.user + "/") + name_.identifier; 177 } 178 179 string Profile::GetRpcIdentifier() { 180 if (!adaptor_.get()) { 181 return string(); 182 } 183 return adaptor_->GetRpcIdentifier(); 184 } 185 186 void Profile::set_storage(StoreInterface* storage) { 187 storage_.reset(storage); 188 } 189 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 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 205 bool Profile::UpdateService(const ServiceRefPtr& service) { 206 return service->Save(storage_.get()) && storage_->Flush(); 207 } 208 209 bool Profile::LoadService(const ServiceRefPtr& service) { 210 if (!ContainsService(service)) 211 return false; 212 return service->Load(storage_.get()); 213 } 214 215 bool Profile::ConfigureService(const ServiceRefPtr& service) { 216 if (!LoadService(service)) 217 return false; 218 service->SetProfile(this); 219 return true; 220 } 221 222 bool Profile::ConfigureDevice(const DeviceRefPtr& device) { 223 return device->Load(storage_.get()); 224 } 225 226 bool Profile::ContainsService(const ServiceConstRefPtr& service) { 227 return service->IsLoadableFrom(*storage_.get()); 228 } 229 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 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 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 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 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 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 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 376 bool Profile::MatchesIdentifier(const Identifier& name) const { 377 return name.user == name_.user && name.identifier == name_.identifier; 378 } 379 380 bool Profile::Save() { 381 return storage_->Flush(); 382 } 383 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 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 406 bool Profile::UpdateDevice(const DeviceRefPtr& device) { 407 return false; 408 } 409 410 #if !defined(DISABLE_WIFI) 411 bool Profile::UpdateWiFiProvider(const WiFiProvider& wifi_provider) { 412 return false; 413 } 414 #endif // DISABLE_WIFI 415 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 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