1c0beca55d290fe0b1c96d78cbbcf94b05c23f5a5Peter Qiu//
2c0beca55d290fe0b1c96d78cbbcf94b05c23f5a5Peter Qiu// Copyright (C) 2012 The Android Open Source Project
3c0beca55d290fe0b1c96d78cbbcf94b05c23f5a5Peter Qiu//
4c0beca55d290fe0b1c96d78cbbcf94b05c23f5a5Peter Qiu// Licensed under the Apache License, Version 2.0 (the "License");
5c0beca55d290fe0b1c96d78cbbcf94b05c23f5a5Peter Qiu// you may not use this file except in compliance with the License.
6c0beca55d290fe0b1c96d78cbbcf94b05c23f5a5Peter Qiu// You may obtain a copy of the License at
7c0beca55d290fe0b1c96d78cbbcf94b05c23f5a5Peter Qiu//
8c0beca55d290fe0b1c96d78cbbcf94b05c23f5a5Peter Qiu//      http://www.apache.org/licenses/LICENSE-2.0
9c0beca55d290fe0b1c96d78cbbcf94b05c23f5a5Peter Qiu//
10c0beca55d290fe0b1c96d78cbbcf94b05c23f5a5Peter Qiu// Unless required by applicable law or agreed to in writing, software
11c0beca55d290fe0b1c96d78cbbcf94b05c23f5a5Peter Qiu// distributed under the License is distributed on an "AS IS" BASIS,
12c0beca55d290fe0b1c96d78cbbcf94b05c23f5a5Peter Qiu// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13c0beca55d290fe0b1c96d78cbbcf94b05c23f5a5Peter Qiu// See the License for the specific language governing permissions and
14c0beca55d290fe0b1c96d78cbbcf94b05c23f5a5Peter Qiu// limitations under the License.
15c0beca55d290fe0b1c96d78cbbcf94b05c23f5a5Peter Qiu//
1652cd19bfd52226611d924930f1178307f79d5fdcChris Masone
1752cd19bfd52226611d924930f1178307f79d5fdcChris Masone#include "shill/profile.h"
1852cd19bfd52226611d924930f1178307f79d5fdcChris Masone
196515aabcd1fbbb9ff2090c9675c17eeef64e7474Chris Masone#include <set>
2052cd19bfd52226611d924930f1178307f79d5fdcChris Masone#include <string>
216791a43add38926966bd420e89541717fb2cb2b5Chris Masone#include <vector>
2252cd19bfd52226611d924930f1178307f79d5fdcChris Masone
2311c213f3cf64f27a0e42ee6da95e98bd1d4b3202Ben Chan#include <base/files/file_util.h>
243e20a2341d0aeb7681e4ee0f89eae6817ade2b3bEric Shienbrood#include <base/stl_util.h>
25a0ddf46e466bd4ba3d20952f0a6988c680c1af14Ben Chan#include <base/strings/string_split.h>
26a0ddf46e466bd4ba3d20952f0a6988c680c1af14Ben Chan#include <base/strings/string_util.h>
27a0ddf46e466bd4ba3d20952f0a6988c680c1af14Ben Chan#include <base/strings/stringprintf.h>
28289a5a5e18bb1a676b3dfce111af4c2c00c7776eSamuel Tan#if defined(__ANDROID__)
29289a5a5e18bb1a676b3dfce111af4c2c00c7776eSamuel Tan#include <dbus/service_constants.h>
30289a5a5e18bb1a676b3dfce111af4c2c00c7776eSamuel Tan#else
3188cbd5f509862f746abbfe99df0605dcc0de40b0Chris Masone#include <chromeos/dbus/service_constants.h>
32289a5a5e18bb1a676b3dfce111af4c2c00c7776eSamuel Tan#endif  // __ANDROID__
3352cd19bfd52226611d924930f1178307f79d5fdcChris Masone
3452cd19bfd52226611d924930f1178307f79d5fdcChris Masone#include "shill/adaptor_interfaces.h"
3552cd19bfd52226611d924930f1178307f79d5fdcChris Masone#include "shill/control_interface.h"
36b691efd71561246065eae3cdd73a96ca1b8a528dChristopher Wiley#include "shill/logging.h"
376791a43add38926966bd420e89541717fb2cb2b5Chris Masone#include "shill/manager.h"
3852cd19bfd52226611d924930f1178307f79d5fdcChris Masone#include "shill/property_accessor.h"
397aa5f90848b530999d8b6788ecb40aa30871c7aeChris Masone#include "shill/service.h"
4004147470fdeaaf9ab7588bd3a7988d74aa990068mukesh agrawal#include "shill/store_factory.h"
419d779936d8f8c2d74b30883e2a2622c4207fe797Chris Masone#include "shill/store_interface.h"
4292496a43b023b912f7f251d0167be360b3335690mukesh agrawal#include "shill/stub_storage.h"
4352cd19bfd52226611d924930f1178307f79d5fdcChris Masone
440e1cdeae24dd678a5fe27c840802582c0ca45ec0Albert Chaulkusing base::FilePath;
456515aabcd1fbbb9ff2090c9675c17eeef64e7474Chris Masoneusing std::set;
4652cd19bfd52226611d924930f1178307f79d5fdcChris Masoneusing std::string;
476791a43add38926966bd420e89541717fb2cb2b5Chris Masoneusing std::vector;
4852cd19bfd52226611d924930f1178307f79d5fdcChris Masone
4952cd19bfd52226611d924930f1178307f79d5fdcChris Masonenamespace shill {
50a4766822880815bafdc69778ccaccaf661536181Darin Petkov
51e48186532acb04b9e11efc9b9334dd555ffcae29mukesh agrawal#if defined(ENABLE_JSON_STORE)
52e48186532acb04b9e11efc9b9334dd555ffcae29mukesh agrawalnamespace {
53e48186532acb04b9e11efc9b9334dd555ffcae29mukesh agrawalconst char kFileExtensionJson[] = "json";
54e48186532acb04b9e11efc9b9334dd555ffcae29mukesh agrawal}
55e48186532acb04b9e11efc9b9334dd555ffcae29mukesh agrawal#endif
56e48186532acb04b9e11efc9b9334dd555ffcae29mukesh agrawal
5778af94ce5ff7c3f22bda6148c6115ec8591129a5Paul Stewart// static
5878af94ce5ff7c3f22bda6148c6115ec8591129a5Paul Stewartconst char Profile::kUserProfileListPathname[] =
5978af94ce5ff7c3f22bda6148c6115ec8591129a5Paul Stewart    RUNDIR "/loaded_profile_list";
6078af94ce5ff7c3f22bda6148c6115ec8591129a5Paul Stewart
611a212a6b98b22ad1d69652bb26a9e94138635476Paul StewartProfile::Profile(ControlInterface* control_interface,
621a212a6b98b22ad1d69652bb26a9e94138635476Paul Stewart                 Metrics* metrics,
631a212a6b98b22ad1d69652bb26a9e94138635476Paul Stewart                 Manager* manager,
641a212a6b98b22ad1d69652bb26a9e94138635476Paul Stewart                 const Identifier& name,
65e48186532acb04b9e11efc9b9334dd555ffcae29mukesh agrawal                 const base::FilePath& storage_directory,
667df0c672458bee8f4ff33004103351d59a9f4b50Chris Masone                 bool connect_to_rpc)
675133b71eaa00535739ebbddd8a8f31cd8fcf33a8Thieu Le    : metrics_(metrics),
685133b71eaa00535739ebbddd8a8f31cd8fcf33a8Thieu Le      manager_(manager),
69a92b0a0438803ebb3432d7b42672fbb7fb6bccf4Peter Qiu      control_interface_(control_interface),
7044b9934a9528c0c19c82e0c6a79642eefeb54ea2mukesh agrawal      name_(name) {
717df0c672458bee8f4ff33004103351d59a9f4b50Chris Masone  if (connect_to_rpc)
727df0c672458bee8f4ff33004103351d59a9f4b50Chris Masone    adaptor_.reset(control_interface->CreateProfileAdaptor(this));
737df0c672458bee8f4ff33004103351d59a9f4b50Chris Masone
745dc58814ae6b1a40f77255986188e43817361ae0Ben Chan  // kCheckPortalListProperty: Registered in DefaultProfile
755dc58814ae6b1a40f77255986188e43817361ae0Ben Chan  // kCountryProperty: Registered in DefaultProfile
765dc58814ae6b1a40f77255986188e43817361ae0Ben Chan  store_.RegisterConstString(kNameProperty, &name_.identifier);
77f3eced90466010ac4b067a565ba371c476e6de67Paul Stewart  store_.RegisterConstString(kUserHashProperty, &name_.user_hash);
7888cbd5f509862f746abbfe99df0605dcc0de40b0Chris Masone
795dc58814ae6b1a40f77255986188e43817361ae0Ben Chan  // kOfflineModeProperty: Registered in DefaultProfile
805dc58814ae6b1a40f77255986188e43817361ae0Ben Chan  // kPortalURLProperty: Registered in DefaultProfile
8188cbd5f509862f746abbfe99df0605dcc0de40b0Chris Masone
825dc58814ae6b1a40f77255986188e43817361ae0Ben Chan  HelpRegisterConstDerivedStrings(kServicesProperty,
83bebf1b8bce52b88c2cc2d93200b9405f9c19cf21mukesh agrawal                                  &Profile::EnumerateAvailableServices);
845dc58814ae6b1a40f77255986188e43817361ae0Ben Chan  HelpRegisterConstDerivedStrings(kEntriesProperty, &Profile::EnumerateEntries);
85e48186532acb04b9e11efc9b9334dd555ffcae29mukesh agrawal
86e48186532acb04b9e11efc9b9334dd555ffcae29mukesh agrawal  if (name.user.empty()) {
87e48186532acb04b9e11efc9b9334dd555ffcae29mukesh agrawal    // Subtle: Profile is only directly instantiated for user
88e48186532acb04b9e11efc9b9334dd555ffcae29mukesh agrawal    // profiles. And user profiles must have a non-empty
89e48186532acb04b9e11efc9b9334dd555ffcae29mukesh agrawal    // |name.user|. So we want to CHECK here. But Profile is also the
90e48186532acb04b9e11efc9b9334dd555ffcae29mukesh agrawal    // base class for DefaultProfile. So a CHECK here would cause us
91e48186532acb04b9e11efc9b9334dd555ffcae29mukesh agrawal    // to abort whenever we attempt to instantiate a DefaultProfile.
92e48186532acb04b9e11efc9b9334dd555ffcae29mukesh agrawal    //
93e48186532acb04b9e11efc9b9334dd555ffcae29mukesh agrawal    // Instead, we leave |persistent_profile_path_| unintialized. One
94e48186532acb04b9e11efc9b9334dd555ffcae29mukesh agrawal    // of two things will happen: a) we become a DefaultProfile, and
95e48186532acb04b9e11efc9b9334dd555ffcae29mukesh agrawal    // the DefaultProfile ctor sets |persistent_profile_path_|, or b)
96e48186532acb04b9e11efc9b9334dd555ffcae29mukesh agrawal    // we really are destined to be a user Profile. In the latter
97e48186532acb04b9e11efc9b9334dd555ffcae29mukesh agrawal    // case, our |name| argument was invalid,
98e48186532acb04b9e11efc9b9334dd555ffcae29mukesh agrawal    // |persistent_profile_path_| is never set, and we CHECK for an
99e48186532acb04b9e11efc9b9334dd555ffcae29mukesh agrawal    // empty |persistent_profile_path_| in InitStorage().
100e48186532acb04b9e11efc9b9334dd555ffcae29mukesh agrawal    //
101e48186532acb04b9e11efc9b9334dd555ffcae29mukesh agrawal    // TODO(quiche): Clean this up. crbug.com/527553
102e48186532acb04b9e11efc9b9334dd555ffcae29mukesh agrawal  } else {
103e48186532acb04b9e11efc9b9334dd555ffcae29mukesh agrawal    persistent_profile_path_ = GetFinalStoragePath(storage_directory, name);
104e48186532acb04b9e11efc9b9334dd555ffcae29mukesh agrawal  }
10552cd19bfd52226611d924930f1178307f79d5fdcChris Masone}
10652cd19bfd52226611d924930f1178307f79d5fdcChris Masone
1076515aabcd1fbbb9ff2090c9675c17eeef64e7474Chris MasoneProfile::~Profile() {}
10852cd19bfd52226611d924930f1178307f79d5fdcChris Masone
109c80c460b90378124cc945b0b1f619d86949000a7mukesh agrawalbool Profile::InitStorage(InitStorageOption storage_option, Error* error) {
110e48186532acb04b9e11efc9b9334dd555ffcae29mukesh agrawal  CHECK(!persistent_profile_path_.empty());
11104147470fdeaaf9ab7588bd3a7988d74aa990068mukesh agrawal  std::unique_ptr<StoreInterface> storage(
112e48186532acb04b9e11efc9b9334dd555ffcae29mukesh agrawal      StoreFactory::GetInstance()->CreateStore(persistent_profile_path_));
1135dc40aad05908dc435e623e56392780ef6d1079ePaul Stewart  bool already_exists = storage->IsNonEmpty();
1145dc40aad05908dc435e623e56392780ef6d1079ePaul Stewart  if (!already_exists && storage_option != kCreateNew &&
1155dc40aad05908dc435e623e56392780ef6d1079ePaul Stewart      storage_option != kCreateOrOpenExisting) {
11634f424e672439bdf237a755f85245ebd7b66e8e2Paul Stewart    Error::PopulateAndLog(
11734f424e672439bdf237a755f85245ebd7b66e8e2Paul Stewart        FROM_HERE, error, Error::kNotFound,
1185dc40aad05908dc435e623e56392780ef6d1079ePaul Stewart        base::StringPrintf("Profile storage for %s:%s does not already exist",
119be005174bce39ddad185c2eb6808117484d522adPaul Stewart                           name_.user.c_str(), name_.identifier.c_str()));
1205dc40aad05908dc435e623e56392780ef6d1079ePaul Stewart    return false;
1215dc40aad05908dc435e623e56392780ef6d1079ePaul Stewart  } else if (already_exists && storage_option != kOpenExisting &&
1225dc40aad05908dc435e623e56392780ef6d1079ePaul Stewart             storage_option != kCreateOrOpenExisting) {
12334f424e672439bdf237a755f85245ebd7b66e8e2Paul Stewart    Error::PopulateAndLog(
12434f424e672439bdf237a755f85245ebd7b66e8e2Paul Stewart        FROM_HERE, error, Error::kAlreadyExists,
1255dc40aad05908dc435e623e56392780ef6d1079ePaul Stewart        base::StringPrintf("Profile storage for %s:%s already exists",
126be005174bce39ddad185c2eb6808117484d522adPaul Stewart                           name_.user.c_str(), name_.identifier.c_str()));
1275dc40aad05908dc435e623e56392780ef6d1079ePaul Stewart    return false;
1285dc40aad05908dc435e623e56392780ef6d1079ePaul Stewart  }
129b9c00597eb1f6d2560f7e88cb0d1f627228840d4Chris Masone  if (!storage->Open()) {
13034f424e672439bdf237a755f85245ebd7b66e8e2Paul Stewart    Error::PopulateAndLog(
13134f424e672439bdf237a755f85245ebd7b66e8e2Paul Stewart        FROM_HERE, error, Error::kInternalError,
132b9c00597eb1f6d2560f7e88cb0d1f627228840d4Chris Masone        base::StringPrintf("Could not open profile storage for %s:%s",
133be005174bce39ddad185c2eb6808117484d522adPaul Stewart                           name_.user.c_str(), name_.identifier.c_str()));
1342ebc16da6068c8c61658b39cf70d157d7d0911ebPaul Stewart    if (already_exists) {
13592496a43b023b912f7f251d0167be360b3335690mukesh agrawal      // The profile contents are corrupt, or we do not have access to
1362ebc16da6068c8c61658b39cf70d157d7d0911ebPaul Stewart      // this file.  Move this file out of the way so a future open attempt
1372ebc16da6068c8c61658b39cf70d157d7d0911ebPaul Stewart      // will succeed, assuming the failure reason was the former.
1382ebc16da6068c8c61658b39cf70d157d7d0911ebPaul Stewart      storage->MarkAsCorrupted();
1395133b71eaa00535739ebbddd8a8f31cd8fcf33a8Thieu Le      metrics_->NotifyCorruptedProfile();
1402ebc16da6068c8c61658b39cf70d157d7d0911ebPaul Stewart    }
141b9c00597eb1f6d2560f7e88cb0d1f627228840d4Chris Masone    return false;
142b9c00597eb1f6d2560f7e88cb0d1f627228840d4Chris Masone  }
1435dc40aad05908dc435e623e56392780ef6d1079ePaul Stewart  if (!already_exists) {
1445dc40aad05908dc435e623e56392780ef6d1079ePaul Stewart    // Add a descriptive header to the profile so even if nothing is stored
1455dc40aad05908dc435e623e56392780ef6d1079ePaul Stewart    // to it, it still has some content.  Completely empty keyfiles are not
1465dc40aad05908dc435e623e56392780ef6d1079ePaul Stewart    // valid for reading.
1475dc40aad05908dc435e623e56392780ef6d1079ePaul Stewart    storage->SetHeader(
1485dc40aad05908dc435e623e56392780ef6d1079ePaul Stewart        base::StringPrintf("Profile %s:%s", name_.user.c_str(),
1495dc40aad05908dc435e623e56392780ef6d1079ePaul Stewart                           name_.identifier.c_str()));
1505dc40aad05908dc435e623e56392780ef6d1079ePaul Stewart  }
151b9c00597eb1f6d2560f7e88cb0d1f627228840d4Chris Masone  set_storage(storage.release());
1529842441bbfae81053a6c08045f1d7b831e5101b2mukesh agrawal  manager_->OnProfileStorageInitialized(this);
153b9c00597eb1f6d2560f7e88cb0d1f627228840d4Chris Masone  return true;
154b9c00597eb1f6d2560f7e88cb0d1f627228840d4Chris Masone}
155b9c00597eb1f6d2560f7e88cb0d1f627228840d4Chris Masone
15692496a43b023b912f7f251d0167be360b3335690mukesh agrawalvoid Profile::InitStubStorage() {
15792496a43b023b912f7f251d0167be360b3335690mukesh agrawal  set_storage(new StubStorage());
15892496a43b023b912f7f251d0167be360b3335690mukesh agrawal}
15992496a43b023b912f7f251d0167be360b3335690mukesh agrawal
160c80c460b90378124cc945b0b1f619d86949000a7mukesh agrawalbool Profile::RemoveStorage(Error* error) {
161e73d05ca690cc847f512072f4709c92cbd7b0651Paul Stewart  CHECK(!storage_.get());
162e48186532acb04b9e11efc9b9334dd555ffcae29mukesh agrawal  CHECK(!persistent_profile_path_.empty());
163e73d05ca690cc847f512072f4709c92cbd7b0651Paul Stewart
164e48186532acb04b9e11efc9b9334dd555ffcae29mukesh agrawal  if (!base::DeleteFile(persistent_profile_path_, false)) {
165e73d05ca690cc847f512072f4709c92cbd7b0651Paul Stewart    Error::PopulateAndLog(
16634f424e672439bdf237a755f85245ebd7b66e8e2Paul Stewart        FROM_HERE, error, Error::kOperationFailed,
167e48186532acb04b9e11efc9b9334dd555ffcae29mukesh agrawal        base::StringPrintf("Could not remove path %s",
168e48186532acb04b9e11efc9b9334dd555ffcae29mukesh agrawal                           persistent_profile_path_.value().c_str()));
169e73d05ca690cc847f512072f4709c92cbd7b0651Paul Stewart    return false;
170e73d05ca690cc847f512072f4709c92cbd7b0651Paul Stewart  }
171e73d05ca690cc847f512072f4709c92cbd7b0651Paul Stewart
172e73d05ca690cc847f512072f4709c92cbd7b0651Paul Stewart  return true;
173e73d05ca690cc847f512072f4709c92cbd7b0651Paul Stewart}
174e73d05ca690cc847f512072f4709c92cbd7b0651Paul Stewart
1757df0c672458bee8f4ff33004103351d59a9f4b50Chris Masonestring Profile::GetFriendlyName() {
1767df0c672458bee8f4ff33004103351d59a9f4b50Chris Masone  return (name_.user.empty() ? "" : name_.user + "/") + name_.identifier;
1777df0c672458bee8f4ff33004103351d59a9f4b50Chris Masone}
1787df0c672458bee8f4ff33004103351d59a9f4b50Chris Masone
1797df0c672458bee8f4ff33004103351d59a9f4b50Chris Masonestring Profile::GetRpcIdentifier() {
18019c871da666a307fdf4e5bced0d745abefe9a04aPaul Stewart  if (!adaptor_.get()) {
18191a5fecf9301440e22bd4a144acdb22558b7ec5ePeter Qiu    return string();
18219c871da666a307fdf4e5bced0d745abefe9a04aPaul Stewart  }
1837df0c672458bee8f4ff33004103351d59a9f4b50Chris Masone  return adaptor_->GetRpcIdentifier();
1847df0c672458bee8f4ff33004103351d59a9f4b50Chris Masone}
1857df0c672458bee8f4ff33004103351d59a9f4b50Chris Masone
1861a212a6b98b22ad1d69652bb26a9e94138635476Paul Stewartvoid Profile::set_storage(StoreInterface* storage) {
187b9c00597eb1f6d2560f7e88cb0d1f627228840d4Chris Masone  storage_.reset(storage);
188b9c00597eb1f6d2560f7e88cb0d1f627228840d4Chris Masone}
189b9c00597eb1f6d2560f7e88cb0d1f627228840d4Chris Masone
1901a212a6b98b22ad1d69652bb26a9e94138635476Paul Stewartbool Profile::AdoptService(const ServiceRefPtr& service) {
191451aa7f3e44642da0598c526030b41c6963c55d3Paul Stewart  if (service->profile() == this) {
1926791a43add38926966bd420e89541717fb2cb2b5Chris Masone    return false;
193451aa7f3e44642da0598c526030b41c6963c55d3Paul Stewart  }
1947917360b71c6680de60b487b3630dc1a0f8cdca2Philipp Neubeck  service->SetProfile(this);
1956515aabcd1fbbb9ff2090c9675c17eeef64e7474Chris Masone  return service->Save(storage_.get()) && storage_->Flush();
1966791a43add38926966bd420e89541717fb2cb2b5Chris Masone}
1976791a43add38926966bd420e89541717fb2cb2b5Chris Masone
1981a212a6b98b22ad1d69652bb26a9e94138635476Paul Stewartbool Profile::AbandonService(const ServiceRefPtr& service) {
1996515aabcd1fbbb9ff2090c9675c17eeef64e7474Chris Masone  if (service->profile() == this)
200cc225ef3b77b5e098cc12c661a947e1737480777Ben Chan    service->SetProfile(nullptr);
2016515aabcd1fbbb9ff2090c9675c17eeef64e7474Chris Masone  return storage_->DeleteGroup(service->GetStorageIdentifier()) &&
2026515aabcd1fbbb9ff2090c9675c17eeef64e7474Chris Masone      storage_->Flush();
2036791a43add38926966bd420e89541717fb2cb2b5Chris Masone}
2046791a43add38926966bd420e89541717fb2cb2b5Chris Masone
2051a212a6b98b22ad1d69652bb26a9e94138635476Paul Stewartbool Profile::UpdateService(const ServiceRefPtr& service) {
2066515aabcd1fbbb9ff2090c9675c17eeef64e7474Chris Masone  return service->Save(storage_.get()) && storage_->Flush();
2076791a43add38926966bd420e89541717fb2cb2b5Chris Masone}
2086791a43add38926966bd420e89541717fb2cb2b5Chris Masone
2091a212a6b98b22ad1d69652bb26a9e94138635476Paul Stewartbool Profile::LoadService(const ServiceRefPtr& service) {
210bba6a5b87cb21424770228cd73e10574a2151568Paul Stewart  if (!ContainsService(service))
2116515aabcd1fbbb9ff2090c9675c17eeef64e7474Chris Masone    return false;
2126515aabcd1fbbb9ff2090c9675c17eeef64e7474Chris Masone  return service->Load(storage_.get());
2136791a43add38926966bd420e89541717fb2cb2b5Chris Masone}
2146791a43add38926966bd420e89541717fb2cb2b5Chris Masone
2151a212a6b98b22ad1d69652bb26a9e94138635476Paul Stewartbool Profile::ConfigureService(const ServiceRefPtr& service) {
2162c575d24aae064efe7c52566a8c07da72e6d2a7bPaul Stewart  if (!LoadService(service))
2172c575d24aae064efe7c52566a8c07da72e6d2a7bPaul Stewart    return false;
2182c575d24aae064efe7c52566a8c07da72e6d2a7bPaul Stewart  service->SetProfile(this);
2192c575d24aae064efe7c52566a8c07da72e6d2a7bPaul Stewart  return true;
2202c575d24aae064efe7c52566a8c07da72e6d2a7bPaul Stewart}
2212c575d24aae064efe7c52566a8c07da72e6d2a7bPaul Stewart
2221a212a6b98b22ad1d69652bb26a9e94138635476Paul Stewartbool Profile::ConfigureDevice(const DeviceRefPtr& device) {
223a41e38d57e4fa37bd2c8de6447890b4e1951acb7Paul Stewart  return device->Load(storage_.get());
224a41e38d57e4fa37bd2c8de6447890b4e1951acb7Paul Stewart}
225a41e38d57e4fa37bd2c8de6447890b4e1951acb7Paul Stewart
2261a212a6b98b22ad1d69652bb26a9e94138635476Paul Stewartbool Profile::ContainsService(const ServiceConstRefPtr& service) {
227e7de2942df36080268c1f296b40e28480479187cPaul Stewart  return service->IsLoadableFrom(*storage_.get());
2287aa5f90848b530999d8b6788ecb40aa30871c7aeChris Masone}
2297aa5f90848b530999d8b6788ecb40aa30871c7aeChris Masone
2301a212a6b98b22ad1d69652bb26a9e94138635476Paul Stewartvoid Profile::DeleteEntry(const std::string& entry_name, Error* error) {
2317522551440b9a287bccf2d95a42357d04ab79814Paul Stewart  if (!storage_->ContainsGroup(entry_name)) {
23234f424e672439bdf237a755f85245ebd7b66e8e2Paul Stewart    Error::PopulateAndLog(
23334f424e672439bdf237a755f85245ebd7b66e8e2Paul Stewart        FROM_HERE, error, Error::kNotFound,
2347522551440b9a287bccf2d95a42357d04ab79814Paul Stewart        base::StringPrintf("Entry %s does not exist in profile",
2357522551440b9a287bccf2d95a42357d04ab79814Paul Stewart                           entry_name.c_str()));
2367522551440b9a287bccf2d95a42357d04ab79814Paul Stewart    return;
2377522551440b9a287bccf2d95a42357d04ab79814Paul Stewart  }
2387522551440b9a287bccf2d95a42357d04ab79814Paul Stewart  if (!manager_->HandleProfileEntryDeletion(this, entry_name)) {
2397522551440b9a287bccf2d95a42357d04ab79814Paul Stewart    // If HandleProfileEntryDeletion() returns succeeds, DeleteGroup()
2407522551440b9a287bccf2d95a42357d04ab79814Paul Stewart    // has already been called when AbandonService was called.
2417522551440b9a287bccf2d95a42357d04ab79814Paul Stewart    // Otherwise, we need to delete the group ourselves.
2427522551440b9a287bccf2d95a42357d04ab79814Paul Stewart    storage_->DeleteGroup(entry_name);
2437522551440b9a287bccf2d95a42357d04ab79814Paul Stewart  }
2440756db95c9d39d6e6aa9a754e54b441eff46ff34Paul Stewart  Save();
2450756db95c9d39d6e6aa9a754e54b441eff46ff34Paul Stewart}
2460756db95c9d39d6e6aa9a754e54b441eff46ff34Paul Stewart
2471a212a6b98b22ad1d69652bb26a9e94138635476Paul StewartServiceRefPtr Profile::GetServiceFromEntry(const std::string& entry_name,
2481a212a6b98b22ad1d69652bb26a9e94138635476Paul Stewart                                           Error* error) {
24918213652e2ce4c6e4354951e5b665097b7a7aa6bPeter Qiu  if (!storage_->ContainsGroup(entry_name)) {
25018213652e2ce4c6e4354951e5b665097b7a7aa6bPeter Qiu    Error::PopulateAndLog(
25118213652e2ce4c6e4354951e5b665097b7a7aa6bPeter Qiu        FROM_HERE, error, Error::kNotFound,
25218213652e2ce4c6e4354951e5b665097b7a7aa6bPeter Qiu        base::StringPrintf("Entry %s does not exist in profile",
25318213652e2ce4c6e4354951e5b665097b7a7aa6bPeter Qiu                           entry_name.c_str()));
25418213652e2ce4c6e4354951e5b665097b7a7aa6bPeter Qiu    return nullptr;
25518213652e2ce4c6e4354951e5b665097b7a7aa6bPeter Qiu  }
25618213652e2ce4c6e4354951e5b665097b7a7aa6bPeter Qiu
25718213652e2ce4c6e4354951e5b665097b7a7aa6bPeter Qiu  // Lookup the service entry from the registered services.
25818213652e2ce4c6e4354951e5b665097b7a7aa6bPeter Qiu  ServiceRefPtr service =
25918213652e2ce4c6e4354951e5b665097b7a7aa6bPeter Qiu      manager_->GetServiceWithStorageIdentifier(this, entry_name, error);
26018213652e2ce4c6e4354951e5b665097b7a7aa6bPeter Qiu  if (service) {
26118213652e2ce4c6e4354951e5b665097b7a7aa6bPeter Qiu    return service;
26218213652e2ce4c6e4354951e5b665097b7a7aa6bPeter Qiu  }
26318213652e2ce4c6e4354951e5b665097b7a7aa6bPeter Qiu
26418213652e2ce4c6e4354951e5b665097b7a7aa6bPeter Qiu  // Load the service entry to a temporary service.
26518213652e2ce4c6e4354951e5b665097b7a7aa6bPeter Qiu  return manager_->CreateTemporaryServiceFromProfile(this, entry_name, error);
2667522551440b9a287bccf2d95a42357d04ab79814Paul Stewart}
2677522551440b9a287bccf2d95a42357d04ab79814Paul Stewart
2681a212a6b98b22ad1d69652bb26a9e94138635476Paul Stewartbool Profile::IsValidIdentifierToken(const string& token) {
269a4766822880815bafdc69778ccaccaf661536181Darin Petkov  if (token.empty()) {
270a4766822880815bafdc69778ccaccaf661536181Darin Petkov    return false;
271a4766822880815bafdc69778ccaccaf661536181Darin Petkov  }
2726db7b24348e69639e19cd6c408388b10d6ee54fePaul Stewart  for (auto chr : token) {
2733a62e235646ec19bee71e8dbee5208282dcd13b5Alex Vakulenko    if (!base::IsAsciiAlpha(chr) && !base::IsAsciiDigit(chr)) {
274a4766822880815bafdc69778ccaccaf661536181Darin Petkov      return false;
275a4766822880815bafdc69778ccaccaf661536181Darin Petkov    }
276a4766822880815bafdc69778ccaccaf661536181Darin Petkov  }
277a4766822880815bafdc69778ccaccaf661536181Darin Petkov  return true;
278a4766822880815bafdc69778ccaccaf661536181Darin Petkov}
279a4766822880815bafdc69778ccaccaf661536181Darin Petkov
280f3eced90466010ac4b067a565ba371c476e6de67Paul Stewart// static
2811a212a6b98b22ad1d69652bb26a9e94138635476Paul Stewartbool Profile::ParseIdentifier(const string& raw, Identifier* parsed) {
282a4766822880815bafdc69778ccaccaf661536181Darin Petkov  if (raw.empty()) {
283a4766822880815bafdc69778ccaccaf661536181Darin Petkov    return false;
284a4766822880815bafdc69778ccaccaf661536181Darin Petkov  }
285a4766822880815bafdc69778ccaccaf661536181Darin Petkov  if (raw[0] == '~') {
286a4766822880815bafdc69778ccaccaf661536181Darin Petkov    // Format: "~user/identifier".
287a4766822880815bafdc69778ccaccaf661536181Darin Petkov    size_t slash = raw.find('/');
288a4766822880815bafdc69778ccaccaf661536181Darin Petkov    if (slash == string::npos) {
289a4766822880815bafdc69778ccaccaf661536181Darin Petkov      return false;
290a4766822880815bafdc69778ccaccaf661536181Darin Petkov    }
291a4766822880815bafdc69778ccaccaf661536181Darin Petkov    string user(raw.begin() + 1, raw.begin() + slash);
292a4766822880815bafdc69778ccaccaf661536181Darin Petkov    string identifier(raw.begin() + slash + 1, raw.end());
293a4766822880815bafdc69778ccaccaf661536181Darin Petkov    if (!IsValidIdentifierToken(user) || !IsValidIdentifierToken(identifier)) {
294a4766822880815bafdc69778ccaccaf661536181Darin Petkov      return false;
295a4766822880815bafdc69778ccaccaf661536181Darin Petkov    }
296a4766822880815bafdc69778ccaccaf661536181Darin Petkov    parsed->user = user;
297a4766822880815bafdc69778ccaccaf661536181Darin Petkov    parsed->identifier = identifier;
298a4766822880815bafdc69778ccaccaf661536181Darin Petkov    return true;
299a4766822880815bafdc69778ccaccaf661536181Darin Petkov  }
300a4766822880815bafdc69778ccaccaf661536181Darin Petkov
301a4766822880815bafdc69778ccaccaf661536181Darin Petkov  // Format: "identifier".
302a4766822880815bafdc69778ccaccaf661536181Darin Petkov  if (!IsValidIdentifierToken(raw)) {
303a4766822880815bafdc69778ccaccaf661536181Darin Petkov    return false;
304a4766822880815bafdc69778ccaccaf661536181Darin Petkov  }
305a4766822880815bafdc69778ccaccaf661536181Darin Petkov  parsed->user = "";
306a4766822880815bafdc69778ccaccaf661536181Darin Petkov  parsed->identifier = raw;
307a4766822880815bafdc69778ccaccaf661536181Darin Petkov  return true;
308a4766822880815bafdc69778ccaccaf661536181Darin Petkov}
309a4766822880815bafdc69778ccaccaf661536181Darin Petkov
310f3eced90466010ac4b067a565ba371c476e6de67Paul Stewart// static
3111a212a6b98b22ad1d69652bb26a9e94138635476Paul Stewartstring Profile::IdentifierToString(const Identifier& name) {
312f3eced90466010ac4b067a565ba371c476e6de67Paul Stewart  if (name.user.empty()) {
313f3eced90466010ac4b067a565ba371c476e6de67Paul Stewart    // Format: "identifier".
314f3eced90466010ac4b067a565ba371c476e6de67Paul Stewart    return name.identifier;
315f3eced90466010ac4b067a565ba371c476e6de67Paul Stewart  }
316f3eced90466010ac4b067a565ba371c476e6de67Paul Stewart
317f3eced90466010ac4b067a565ba371c476e6de67Paul Stewart  // Format: "~user/identifier".
318f3eced90466010ac4b067a565ba371c476e6de67Paul Stewart  return base::StringPrintf(
319f3eced90466010ac4b067a565ba371c476e6de67Paul Stewart      "~%s/%s", name.user.c_str(), name.identifier.c_str());
320f3eced90466010ac4b067a565ba371c476e6de67Paul Stewart}
321f3eced90466010ac4b067a565ba371c476e6de67Paul Stewart
32278af94ce5ff7c3f22bda6148c6115ec8591129a5Paul Stewart// static
3231a212a6b98b22ad1d69652bb26a9e94138635476Paul Stewartvector<Profile::Identifier> Profile::LoadUserProfileList(const FilePath& path) {
32478af94ce5ff7c3f22bda6148c6115ec8591129a5Paul Stewart  vector<Identifier> profile_identifiers;
32578af94ce5ff7c3f22bda6148c6115ec8591129a5Paul Stewart  string profile_data;
326a0ddf46e466bd4ba3d20952f0a6988c680c1af14Ben Chan  if (!base::ReadFileToString(path, &profile_data)) {
32778af94ce5ff7c3f22bda6148c6115ec8591129a5Paul Stewart    return profile_identifiers;
32878af94ce5ff7c3f22bda6148c6115ec8591129a5Paul Stewart  }
32978af94ce5ff7c3f22bda6148c6115ec8591129a5Paul Stewart
3303a62e235646ec19bee71e8dbee5208282dcd13b5Alex Vakulenko  vector<string> profile_lines =
3313a62e235646ec19bee71e8dbee5208282dcd13b5Alex Vakulenko      base::SplitString(profile_data, "\n", base::KEEP_WHITESPACE,
3323a62e235646ec19bee71e8dbee5208282dcd13b5Alex Vakulenko                        base::SPLIT_WANT_ALL);
3331a212a6b98b22ad1d69652bb26a9e94138635476Paul Stewart  for (const auto& line : profile_lines) {
33478af94ce5ff7c3f22bda6148c6115ec8591129a5Paul Stewart    if (line.empty()) {
33578af94ce5ff7c3f22bda6148c6115ec8591129a5Paul Stewart      // This will be the case on the last line, so let's not complain about it.
33678af94ce5ff7c3f22bda6148c6115ec8591129a5Paul Stewart      continue;
33778af94ce5ff7c3f22bda6148c6115ec8591129a5Paul Stewart    }
33878af94ce5ff7c3f22bda6148c6115ec8591129a5Paul Stewart    size_t space = line.find(' ');
33978af94ce5ff7c3f22bda6148c6115ec8591129a5Paul Stewart    if (space == string::npos || space == 0) {
34078af94ce5ff7c3f22bda6148c6115ec8591129a5Paul Stewart      LOG(ERROR) << "Invalid line found in " << path.value()
34178af94ce5ff7c3f22bda6148c6115ec8591129a5Paul Stewart                 << ": " << line;
34278af94ce5ff7c3f22bda6148c6115ec8591129a5Paul Stewart      continue;
34378af94ce5ff7c3f22bda6148c6115ec8591129a5Paul Stewart    }
34478af94ce5ff7c3f22bda6148c6115ec8591129a5Paul Stewart    string name(line.begin(), line.begin() + space);
34578af94ce5ff7c3f22bda6148c6115ec8591129a5Paul Stewart    Identifier identifier;
34678af94ce5ff7c3f22bda6148c6115ec8591129a5Paul Stewart    if (!ParseIdentifier(name, &identifier) || identifier.user.empty()) {
34778af94ce5ff7c3f22bda6148c6115ec8591129a5Paul Stewart      LOG(ERROR) << "Invalid profile name found in " << path.value()
34878af94ce5ff7c3f22bda6148c6115ec8591129a5Paul Stewart                 << ": " << name;
34978af94ce5ff7c3f22bda6148c6115ec8591129a5Paul Stewart      continue;
35078af94ce5ff7c3f22bda6148c6115ec8591129a5Paul Stewart    }
35178af94ce5ff7c3f22bda6148c6115ec8591129a5Paul Stewart    identifier.user_hash = string(line.begin() + space + 1, line.end());
35278af94ce5ff7c3f22bda6148c6115ec8591129a5Paul Stewart    profile_identifiers.push_back(identifier);
35378af94ce5ff7c3f22bda6148c6115ec8591129a5Paul Stewart  }
35478af94ce5ff7c3f22bda6148c6115ec8591129a5Paul Stewart
35578af94ce5ff7c3f22bda6148c6115ec8591129a5Paul Stewart  return profile_identifiers;
35678af94ce5ff7c3f22bda6148c6115ec8591129a5Paul Stewart}
35778af94ce5ff7c3f22bda6148c6115ec8591129a5Paul Stewart
35878af94ce5ff7c3f22bda6148c6115ec8591129a5Paul Stewart// static
3591a212a6b98b22ad1d69652bb26a9e94138635476Paul Stewartbool Profile::SaveUserProfileList(const FilePath& path,
3601a212a6b98b22ad1d69652bb26a9e94138635476Paul Stewart                                  const vector<ProfileRefPtr>& profiles) {
36178af94ce5ff7c3f22bda6148c6115ec8591129a5Paul Stewart  vector<string> lines;
3621a212a6b98b22ad1d69652bb26a9e94138635476Paul Stewart  for (const auto& profile : profiles) {
3631a212a6b98b22ad1d69652bb26a9e94138635476Paul Stewart    Identifier& id = profile->name_;
36478af94ce5ff7c3f22bda6148c6115ec8591129a5Paul Stewart    if (id.user.empty()) {
36578af94ce5ff7c3f22bda6148c6115ec8591129a5Paul Stewart      continue;
36678af94ce5ff7c3f22bda6148c6115ec8591129a5Paul Stewart    }
36778af94ce5ff7c3f22bda6148c6115ec8591129a5Paul Stewart    lines.push_back(base::StringPrintf("%s %s\n",
36878af94ce5ff7c3f22bda6148c6115ec8591129a5Paul Stewart                                       IdentifierToString(id).c_str(),
36978af94ce5ff7c3f22bda6148c6115ec8591129a5Paul Stewart                                       id.user_hash.c_str()));
37078af94ce5ff7c3f22bda6148c6115ec8591129a5Paul Stewart  }
3713a62e235646ec19bee71e8dbee5208282dcd13b5Alex Vakulenko  string content = base::JoinString(lines, "");
3726fbf64f493a9aae7d743888039c61a57386203dbBen Chan  size_t ret = base::WriteFile(path, content.c_str(), content.length());
37378af94ce5ff7c3f22bda6148c6115ec8591129a5Paul Stewart  return ret == content.length();
37478af94ce5ff7c3f22bda6148c6115ec8591129a5Paul Stewart}
37578af94ce5ff7c3f22bda6148c6115ec8591129a5Paul Stewart
3761a212a6b98b22ad1d69652bb26a9e94138635476Paul Stewartbool Profile::MatchesIdentifier(const Identifier& name) const {
3775dc40aad05908dc435e623e56392780ef6d1079ePaul Stewart  return name.user == name_.user && name.identifier == name_.identifier;
3785dc40aad05908dc435e623e56392780ef6d1079ePaul Stewart}
3795dc40aad05908dc435e623e56392780ef6d1079ePaul Stewart
380b9c00597eb1f6d2560f7e88cb0d1f627228840d4Chris Masonebool Profile::Save() {
3816515aabcd1fbbb9ff2090c9675c17eeef64e7474Chris Masone  return storage_->Flush();
3829d779936d8f8c2d74b30883e2a2622c4207fe797Chris Masone}
3839d779936d8f8c2d74b30883e2a2622c4207fe797Chris Masone
3841a212a6b98b22ad1d69652bb26a9e94138635476Paul Stewartvector<string> Profile::EnumerateAvailableServices(Error* error) {
3851b2531423a15ccc5ac5acaf09edac9a687902dfaPaul Stewart  // We should return the Manager's service list if this is the active profile.
3861b2531423a15ccc5ac5acaf09edac9a687902dfaPaul Stewart  if (manager_->IsActiveProfile(this)) {
3871b2531423a15ccc5ac5acaf09edac9a687902dfaPaul Stewart    return manager_->EnumerateAvailableServices(error);
3881b2531423a15ccc5ac5acaf09edac9a687902dfaPaul Stewart  } else {
3891b2531423a15ccc5ac5acaf09edac9a687902dfaPaul Stewart    return vector<string>();
3901b2531423a15ccc5ac5acaf09edac9a687902dfaPaul Stewart  }
3916791a43add38926966bd420e89541717fb2cb2b5Chris Masone}
3926791a43add38926966bd420e89541717fb2cb2b5Chris Masone
3931a212a6b98b22ad1d69652bb26a9e94138635476Paul Stewartvector<string> Profile::EnumerateEntries(Error* /*error*/) {
3941b2531423a15ccc5ac5acaf09edac9a687902dfaPaul Stewart  vector<string> service_groups;
3951b2531423a15ccc5ac5acaf09edac9a687902dfaPaul Stewart
3961b2531423a15ccc5ac5acaf09edac9a687902dfaPaul Stewart  // Filter this list down to only entries that correspond
3971b2531423a15ccc5ac5acaf09edac9a687902dfaPaul Stewart  // to a technology.  (wifi_*, etc)
3981a212a6b98b22ad1d69652bb26a9e94138635476Paul Stewart  for (const auto& group : storage_->GetGroups()) {
3996db7b24348e69639e19cd6c408388b10d6ee54fePaul Stewart    if (Technology::IdentifierFromStorageGroup(group) != Technology::kUnknown)
4006db7b24348e69639e19cd6c408388b10d6ee54fePaul Stewart      service_groups.push_back(group);
4011b2531423a15ccc5ac5acaf09edac9a687902dfaPaul Stewart  }
4021b2531423a15ccc5ac5acaf09edac9a687902dfaPaul Stewart
4031b2531423a15ccc5ac5acaf09edac9a687902dfaPaul Stewart  return service_groups;
4046791a43add38926966bd420e89541717fb2cb2b5Chris Masone}
4056791a43add38926966bd420e89541717fb2cb2b5Chris Masone
4061a212a6b98b22ad1d69652bb26a9e94138635476Paul Stewartbool Profile::UpdateDevice(const DeviceRefPtr& device) {
407e7c6ad35cafed32a0fd1390487c74e66eae1961dDarin Petkov  return false;
408e7c6ad35cafed32a0fd1390487c74e66eae1961dDarin Petkov}
409e7c6ad35cafed32a0fd1390487c74e66eae1961dDarin Petkov
4101a72f5444e077ed21b8e085b17d7f9f1bc44fa5ePeter Qiu#if !defined(DISABLE_WIFI)
4111a212a6b98b22ad1d69652bb26a9e94138635476Paul Stewartbool Profile::UpdateWiFiProvider(const WiFiProvider& wifi_provider) {
41260a3706bee5686ae2322a774b9e7d5e8e63fcaf7Wade Guthrie  return false;
41360a3706bee5686ae2322a774b9e7d5e8e63fcaf7Wade Guthrie}
4141a72f5444e077ed21b8e085b17d7f9f1bc44fa5ePeter Qiu#endif  // DISABLE_WIFI
41560a3706bee5686ae2322a774b9e7d5e8e63fcaf7Wade Guthrie
416bebf1b8bce52b88c2cc2d93200b9405f9c19cf21mukesh agrawalvoid Profile::HelpRegisterConstDerivedStrings(
4171a212a6b98b22ad1d69652bb26a9e94138635476Paul Stewart    const string& name,
4181a212a6b98b22ad1d69652bb26a9e94138635476Paul Stewart    Strings(Profile::*get)(Error*)) {
4196791a43add38926966bd420e89541717fb2cb2b5Chris Masone  store_.RegisterDerivedStrings(
420cc225ef3b77b5e098cc12c661a947e1737480777Ben Chan      name, StringsAccessor(
421cc225ef3b77b5e098cc12c661a947e1737480777Ben Chan                new CustomAccessor<Profile, Strings>(this, get, nullptr)));
4226791a43add38926966bd420e89541717fb2cb2b5Chris Masone}
4236791a43add38926966bd420e89541717fb2cb2b5Chris Masone
424e48186532acb04b9e11efc9b9334dd555ffcae29mukesh agrawal// static
425e48186532acb04b9e11efc9b9334dd555ffcae29mukesh agrawalFilePath Profile::GetFinalStoragePath(
426e48186532acb04b9e11efc9b9334dd555ffcae29mukesh agrawal    const FilePath& storage_dir,
427e48186532acb04b9e11efc9b9334dd555ffcae29mukesh agrawal    const Identifier& profile_name) {
428e48186532acb04b9e11efc9b9334dd555ffcae29mukesh agrawal  FilePath base_path;
429e48186532acb04b9e11efc9b9334dd555ffcae29mukesh agrawal  if (profile_name.user.empty()) {  // True for DefaultProfiles.
430e48186532acb04b9e11efc9b9334dd555ffcae29mukesh agrawal    base_path = storage_dir.Append(
431e48186532acb04b9e11efc9b9334dd555ffcae29mukesh agrawal        base::StringPrintf("%s.profile", profile_name.identifier.c_str()));
432e48186532acb04b9e11efc9b9334dd555ffcae29mukesh agrawal  } else {
433e48186532acb04b9e11efc9b9334dd555ffcae29mukesh agrawal    base_path = storage_dir.Append(
434e48186532acb04b9e11efc9b9334dd555ffcae29mukesh agrawal        base::StringPrintf("%s/%s.profile",
435e48186532acb04b9e11efc9b9334dd555ffcae29mukesh agrawal                           profile_name.user.c_str(),
436e48186532acb04b9e11efc9b9334dd555ffcae29mukesh agrawal                           profile_name.identifier.c_str()));
437e48186532acb04b9e11efc9b9334dd555ffcae29mukesh agrawal  }
438e48186532acb04b9e11efc9b9334dd555ffcae29mukesh agrawal
439e48186532acb04b9e11efc9b9334dd555ffcae29mukesh agrawal  // TODO(petkov): Validate the directory permissions, etc.
440e48186532acb04b9e11efc9b9334dd555ffcae29mukesh agrawal
441e48186532acb04b9e11efc9b9334dd555ffcae29mukesh agrawal#if defined(ENABLE_JSON_STORE)
442e48186532acb04b9e11efc9b9334dd555ffcae29mukesh agrawal  return base_path.AddExtension(kFileExtensionJson);
443e48186532acb04b9e11efc9b9334dd555ffcae29mukesh agrawal#else
444e48186532acb04b9e11efc9b9334dd555ffcae29mukesh agrawal  return base_path;
445e48186532acb04b9e11efc9b9334dd555ffcae29mukesh agrawal#endif
446e48186532acb04b9e11efc9b9334dd555ffcae29mukesh agrawal}
447e48186532acb04b9e11efc9b9334dd555ffcae29mukesh agrawal
44852cd19bfd52226611d924930f1178307f79d5fdcChris Masone}  // namespace shill
449