shill: Support for profile identifiers, and creating persistent storage.
BUG=chromium-os:17252
TEST=unit tests
Change-Id: Iaec7b6b5737a997fde3d5215196fdcbf72eefe09
Reviewed-on: http://gerrit.chromium.org/gerrit/3749
Tested-by: Darin Petkov <petkov@chromium.org>
Reviewed-by: Chris Masone <cmasone@chromium.org>
diff --git a/Makefile b/Makefile
index 110ad26..463cff9 100644
--- a/Makefile
+++ b/Makefile
@@ -106,6 +106,7 @@
mock_control.o \
mock_device.o \
mock_service.o \
+ profile_unittest.o \
property_accessor_unittest.o \
property_store_unittest.o \
service_unittest.o \
diff --git a/default_profile.cc b/default_profile.cc
index 3f0da6d..74facf3 100644
--- a/default_profile.cc
+++ b/default_profile.cc
@@ -13,8 +13,9 @@
namespace shill {
DefaultProfile::DefaultProfile(ControlInterface *control_interface,
+ GLib *glib,
const Manager::Properties &manager_props)
- : Profile(control_interface) {
+ : Profile(control_interface, glib) {
store_.RegisterConstString(flimflam::kCheckPortalListProperty,
&manager_props.check_portal_list);
store_.RegisterConstString(flimflam::kCountryProperty,
diff --git a/default_profile.h b/default_profile.h
index db65487..746860c 100644
--- a/default_profile.h
+++ b/default_profile.h
@@ -23,6 +23,7 @@
class DefaultProfile : public Profile {
public:
DefaultProfile(ControlInterface *control_interface,
+ GLib *glib,
const Manager::Properties &manager_props);
virtual ~DefaultProfile();
diff --git a/default_profile_unittest.cc b/default_profile_unittest.cc
index 6e3e712..8567143 100644
--- a/default_profile_unittest.cc
+++ b/default_profile_unittest.cc
@@ -23,12 +23,13 @@
class DefaultProfileTest : public PropertyStoreTest {
public:
- DefaultProfileTest() : profile_(&control_interface_, properties_) {}
+ DefaultProfileTest() : profile_(&control_interface_, &glib_, properties_) {}
virtual ~DefaultProfileTest() {}
protected:
DefaultProfile profile_;
+ GLib glib_;
Manager::Properties properties_;
};
diff --git a/profile.cc b/profile.cc
index 2593e56..c1e92a4 100644
--- a/profile.cc
+++ b/profile.cc
@@ -7,6 +7,7 @@
#include <string>
#include <base/logging.h>
+#include <base/string_util.h>
#include <chromeos/dbus/service_constants.h>
#include "shill/adaptor_interfaces.h"
@@ -16,8 +17,14 @@
using std::string;
namespace shill {
-Profile::Profile(ControlInterface *control_interface)
- : adaptor_(control_interface->CreateProfileAdaptor(this)) {
+
+const char Profile::kGlobalStorageDir[] = "/var/cache/flimflam";
+const char Profile::kUserStorageDirFormat[] = "/home/%s/user/flimflam";
+
+Profile::Profile(ControlInterface *control_interface,
+ GLib *glib)
+ : adaptor_(control_interface->CreateProfileAdaptor(this)),
+ storage_(glib) {
// flimflam::kCheckPortalListProperty: Registered in DefaultProfile
// flimflam::kCountryProperty: Registered in DefaultProfile
store_.RegisterConstString(flimflam::kNameProperty, &name_);
@@ -36,4 +43,61 @@
Profile::~Profile() {}
+bool Profile::IsValidIdentifierToken(const std::string &token) {
+ if (token.empty()) {
+ return false;
+ }
+ for (string::const_iterator it = token.begin(); it != token.end(); ++it) {
+ if (!IsAsciiAlpha(*it) && !IsAsciiDigit(*it)) {
+ return false;
+ }
+ }
+ return true;
+}
+
+bool Profile::ParseIdentifier(const string &raw, Identifier *parsed) {
+ if (raw.empty()) {
+ return false;
+ }
+ if (raw[0] == '~') {
+ // Format: "~user/identifier".
+ size_t slash = raw.find('/');
+ if (slash == string::npos) {
+ return false;
+ }
+ string user(raw.begin() + 1, raw.begin() + slash);
+ string identifier(raw.begin() + slash + 1, raw.end());
+ if (!IsValidIdentifierToken(user) || !IsValidIdentifierToken(identifier)) {
+ return false;
+ }
+ parsed->user = user;
+ parsed->identifier = identifier;
+ return true;
+ }
+
+ // Format: "identifier".
+ if (!IsValidIdentifierToken(raw)) {
+ return false;
+ }
+ parsed->user = "";
+ parsed->identifier = raw;
+ return true;
+}
+
+string Profile::GetRpcPath(const Identifier &identifier) {
+ string user = identifier.user.empty() ? "" : identifier.user + "/";
+ return "/profile/" + user + identifier.identifier;
+}
+
+bool Profile::GetStoragePath(const Identifier &identifier, FilePath *path) {
+ FilePath dir(
+ identifier.user.empty() ?
+ kGlobalStorageDir :
+ base::StringPrintf(kUserStorageDirFormat, identifier.user.c_str()));
+ // TODO(petkov): Validate the directory permissions, etc.
+ *path = dir.Append(base::StringPrintf("%s.profile",
+ identifier.identifier.c_str()));
+ return true;
+}
+
} // namespace shill
diff --git a/profile.h b/profile.h
index 11b9b80..9621d4a 100644
--- a/profile.h
+++ b/profile.h
@@ -9,24 +9,54 @@
#include <vector>
#include <base/memory/scoped_ptr.h>
+#include <gtest/gtest_prod.h> // for FRIEND_TEST
+#include "shill/key_file_store.h"
#include "shill/property_store.h"
#include "shill/refptr_types.h"
#include "shill/shill_event.h"
+class FilePath;
+
namespace shill {
class ControlInterface;
class Error;
+class GLib;
class ProfileAdaptorInterface;
class Profile {
public:
- explicit Profile(ControlInterface *control_interface);
+ struct Identifier {
+ std::string user; // Empty for global.
+ std::string identifier;
+ };
+
+ static const char kGlobalStorageDir[];
+ static const char kUserStorageDirFormat[];
+
+ Profile(ControlInterface *control_interface, GLib *glib);
virtual ~Profile();
PropertyStore *store() { return &store_; }
+ // Parses a profile identifier. There're two acceptable forms of the |raw|
+ // identifier: "identifier" and "~user/identifier". Both "user" and
+ // "identifier" must be suitable for use in a D-Bus object path. Returns true
+ // on success.
+ static bool ParseIdentifier(const std::string &raw, Identifier *parsed);
+
+ // Returns the RPC object path for a profile identified by
+ // |identifier|. |identifier| must be a valid identifier, possibly parsed and
+ // validated through Profile::ParseIdentifier.
+ static std::string GetRpcPath(const Identifier &identifier);
+
+ // Sets |path| to the persistent store file path for a profile identified by
+ // |identifier|. Returns true on success, and false if unable to determine an
+ // appropriate file location. |identifier| must be a valid identifier,
+ // possibly parsed and validated through Profile::ParseIdentifier.
+ static bool GetStoragePath(const Identifier &identifier, FilePath *path);
+
protected:
// Properties to be get/set via PropertyStore calls that must also be visible
// in subclasses.
@@ -34,9 +64,15 @@
private:
friend class ProfileAdaptorInterface;
+ FRIEND_TEST(ProfileTest, IsValidIdentifierToken);
+
+ static bool IsValidIdentifierToken(const std::string &token);
scoped_ptr<ProfileAdaptorInterface> adaptor_;
+ // Persistent store associated with the profile.
+ KeyFileStore storage_;
+
// Properties to be get/set via PropertyStore calls.
std::string name_;
diff --git a/profile_unittest.cc b/profile_unittest.cc
new file mode 100644
index 0000000..d3a03a9
--- /dev/null
+++ b/profile_unittest.cc
@@ -0,0 +1,85 @@
+// Copyright (c) 2011 The Chromium OS Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include <base/string_util.h>
+#include <gtest/gtest.h>
+
+#include "shill/profile.h"
+
+using std::string;
+using testing::Test;
+
+namespace shill {
+
+class ProfileTest : public Test {
+};
+
+TEST_F(ProfileTest, IsValidIdentifierToken) {
+ EXPECT_FALSE(Profile::IsValidIdentifierToken(""));
+ EXPECT_FALSE(Profile::IsValidIdentifierToken(" "));
+ EXPECT_FALSE(Profile::IsValidIdentifierToken("-"));
+ EXPECT_FALSE(Profile::IsValidIdentifierToken("~"));
+ EXPECT_FALSE(Profile::IsValidIdentifierToken("_"));
+ EXPECT_TRUE(Profile::IsValidIdentifierToken("a"));
+ EXPECT_TRUE(Profile::IsValidIdentifierToken("ABCDEFGHIJKLMNOPQRSTUVWXYZ"));
+ EXPECT_TRUE(Profile::IsValidIdentifierToken("abcdefghijklmnopqrstuvwxyz"));
+ EXPECT_TRUE(Profile::IsValidIdentifierToken("0123456789"));
+}
+
+TEST_F(ProfileTest, ParseIdentifier) {
+ Profile::Identifier identifier;
+ EXPECT_FALSE(Profile::ParseIdentifier("", &identifier));
+ EXPECT_FALSE(Profile::ParseIdentifier("~", &identifier));
+ EXPECT_FALSE(Profile::ParseIdentifier("~foo", &identifier));
+ EXPECT_FALSE(Profile::ParseIdentifier("~/", &identifier));
+ EXPECT_FALSE(Profile::ParseIdentifier("~bar/", &identifier));
+ EXPECT_FALSE(Profile::ParseIdentifier("~/zoo", &identifier));
+ EXPECT_FALSE(Profile::ParseIdentifier("~./moo", &identifier));
+ EXPECT_FALSE(Profile::ParseIdentifier("~valid/?", &identifier));
+ EXPECT_FALSE(Profile::ParseIdentifier("~no//no", &identifier));
+ EXPECT_FALSE(Profile::ParseIdentifier("~no~no", &identifier));
+
+ static const char kUser[] = "user";
+ static const char kIdentifier[] = "identifier";
+ EXPECT_TRUE(Profile::ParseIdentifier(
+ base::StringPrintf("~%s/%s", kUser, kIdentifier),
+ &identifier));
+ EXPECT_EQ(kUser, identifier.user);
+ EXPECT_EQ(kIdentifier, identifier.identifier);
+
+ EXPECT_FALSE(Profile::ParseIdentifier("!", &identifier));
+ EXPECT_FALSE(Profile::ParseIdentifier("/nope", &identifier));
+
+ static const char kIdentifier2[] = "something";
+ EXPECT_TRUE(Profile::ParseIdentifier(kIdentifier2, &identifier));
+ EXPECT_EQ("", identifier.user);
+ EXPECT_EQ(kIdentifier2, identifier.identifier);
+}
+
+TEST_F(ProfileTest, GetRpcPath) {
+ static const char kUser[] = "theUser";
+ static const char kIdentifier[] = "theIdentifier";
+ static const char kPathPrefix[] = "/profile/";
+ Profile::Identifier identifier;
+ identifier.identifier = kIdentifier;
+ EXPECT_EQ(string(kPathPrefix) + kIdentifier, Profile::GetRpcPath(identifier));
+ identifier.user = kUser;
+ EXPECT_EQ(string(kPathPrefix) + kUser + "/" + kIdentifier,
+ Profile::GetRpcPath(identifier));
+}
+
+TEST_F(ProfileTest, GetStoragePath) {
+ static const char kUser[] = "chronos";
+ static const char kIdentifier[] = "someprofile";
+ FilePath path;
+ Profile::Identifier identifier;
+ identifier.identifier = kIdentifier;
+ EXPECT_TRUE(Profile::GetStoragePath(identifier, &path));
+ EXPECT_EQ("/var/cache/flimflam/someprofile.profile", path.value());
+ identifier.user = kUser;
+ EXPECT_TRUE(Profile::GetStoragePath(identifier, &path));
+ EXPECT_EQ("/home/chronos/user/flimflam/someprofile.profile", path.value());
+}
+
+} // namespace shill