Home | History | Annotate | Download | only in shill
      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