shill: Basic persistent store interface and key-file implementation.

The current interface and implementation support all functionality required by
the legacy network manager. The implementation uses glib's key file parser for
legacy reasons. The interface-based implementation allows us to switch to a
different format in the future, if necessary.

BUG=chromium-os:16897
TEST=unit test

Change-Id: I8fd54f47e7309c603b66ba86bbecb8d5092e8674
Reviewed-on: http://gerrit.chromium.org/gerrit/3160
Reviewed-by: Darin Petkov <petkov@chromium.org>
Tested-by: Darin Petkov <petkov@chromium.org>
diff --git a/key_file_store.cc b/key_file_store.cc
new file mode 100644
index 0000000..d844e7e
--- /dev/null
+++ b/key_file_store.cc
@@ -0,0 +1,195 @@
+// 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 "shill/key_file_store.h"
+
+#include <base/file_util.h>
+#include <base/logging.h>
+
+using std::set;
+using std::string;
+
+namespace shill {
+
+KeyFileStore::KeyFileStore(GLib *glib) : glib_(glib), key_file_(NULL) {}
+
+KeyFileStore::~KeyFileStore() {
+  ReleaseKeyFile();
+}
+
+void KeyFileStore::ReleaseKeyFile() {
+  if (key_file_) {
+    glib_->KeyFileFree(key_file_);
+    key_file_ = NULL;
+  }
+}
+
+bool KeyFileStore::Open() {
+  CHECK(!path_.empty());
+  CHECK(!key_file_);
+  key_file_ = glib_->KeyFileNew();
+  int64 file_size = 0;
+  if (!file_util::GetFileSize(path_, &file_size) || file_size == 0) {
+    LOG(INFO) << "Creating a new key file at " << path_.value();
+    return true;
+  }
+  GError *error = NULL;
+  if (glib_->KeyFileLoadFromFile(
+          key_file_,
+          path_.value().c_str(),
+          static_cast<GKeyFileFlags>(G_KEY_FILE_KEEP_COMMENTS |
+                                     G_KEY_FILE_KEEP_TRANSLATIONS),
+          &error)) {
+    return true;
+  }
+  LOG(ERROR) << "Failed to load key file from " << path_.value() << ": "
+             << glib_->ConvertErrorToMessage(error);
+  ReleaseKeyFile();
+  return false;
+}
+
+bool KeyFileStore::Close() {
+  CHECK(key_file_);
+  GError *error = NULL;
+  gsize length = 0;
+  gchar *data = glib_->KeyFileToData(key_file_, &length, &error);
+  ReleaseKeyFile();
+
+  bool success = true;
+  if (path_.empty()) {
+    LOG(ERROR) << "Empty key file path.";
+    success = false;
+  }
+  if (success && (!data || error)) {
+    LOG(ERROR) << "Failed to convert key file to string: "
+               << glib_->ConvertErrorToMessage(error);
+    success = false;
+  }
+  if (success && file_util::WriteFile(path_, data, length) != length) {
+    LOG(ERROR) << "Failed to store key file: " << path_.value();
+    success = false;
+  }
+  glib_->Free(data);
+  return success;
+}
+
+set<string> KeyFileStore::GetGroups() {
+  CHECK(key_file_);
+  gsize length = 0;
+  gchar **groups = g_key_file_get_groups(key_file_, &length);
+  if (!groups) {
+    LOG(ERROR) << "Unable to obtain groups.";
+    return set<string>();
+  }
+  set<string> group_set(groups, groups + length);
+  glib_->Strfreev(groups);
+  return group_set;
+}
+
+bool KeyFileStore::ContainsGroup(const string &group) {
+  CHECK(key_file_);
+  return glib_->KeyFileHasGroup(key_file_, group.c_str());
+}
+
+bool KeyFileStore::DeleteKey(const string &group, const string &key) {
+  CHECK(key_file_);
+  GError *error = NULL;
+  glib_->KeyFileRemoveKey(key_file_, group.c_str(), key.c_str(), &error);
+  if (error) {
+    LOG(ERROR) << "Failed to delete (" << group << ":" << key << "): "
+               << glib_->ConvertErrorToMessage(error);
+    return false;
+  }
+  return true;
+}
+
+bool KeyFileStore::DeleteGroup(const string &group) {
+  CHECK(key_file_);
+  GError *error = NULL;
+  glib_->KeyFileRemoveGroup(key_file_, group.c_str(), &error);
+  if (error) {
+    LOG(ERROR) << "Failed to delete group " << group << "): "
+               << glib_->ConvertErrorToMessage(error);
+    return false;
+  }
+  return true;
+}
+
+bool KeyFileStore::GetString(const string &group,
+                             const string &key,
+                             string *value) {
+  CHECK(key_file_);
+  GError *error = NULL;
+  gchar *data =
+      glib_->KeyFileGetString(key_file_, group.c_str(), key.c_str(), &error);
+  if (!data) {
+    LOG(ERROR) << "Failed to lookup (" << group << ":" << key << "): "
+               << glib_->ConvertErrorToMessage(error);
+    return false;
+  }
+  if (value) {
+    *value = data;
+  }
+  glib_->Free(data);
+  return true;
+}
+
+bool KeyFileStore::SetString(const string &group,
+                             const string &key,
+                             const string &value) {
+  CHECK(key_file_);
+  glib_->KeyFileSetString(key_file_, group.c_str(), key.c_str(), value.c_str());
+  return true;
+}
+
+bool KeyFileStore::GetBool(const string &group,
+                           const string &key,
+                           bool *value) {
+  CHECK(key_file_);
+  GError *error = NULL;
+  gboolean data =
+      glib_->KeyFileGetBoolean(key_file_, group.c_str(), key.c_str(), &error);
+  if (error) {
+    LOG(ERROR) << "Failed to lookup (" << group << ":" << key << "): "
+               << glib_->ConvertErrorToMessage(error);
+    return false;
+  }
+  if (value) {
+    *value = data;
+  }
+  return true;
+}
+
+bool KeyFileStore::SetBool(const string &group, const string &key, bool value) {
+  CHECK(key_file_);
+  glib_->KeyFileSetBoolean(key_file_,
+                           group.c_str(),
+                           key.c_str(),
+                           value ? TRUE : FALSE);
+  return true;
+}
+
+bool KeyFileStore::GetInt(const string &group, const string &key, int *value) {
+  CHECK(key_file_);
+  GError *error = NULL;
+  gint data =
+      glib_->KeyFileGetInteger(key_file_, group.c_str(), key.c_str(), &error);
+  if (error) {
+    LOG(ERROR) << "Failed to lookup (" << group << ":" << key << "): "
+               << glib_->ConvertErrorToMessage(error);
+    return false;
+  }
+  if (value) {
+    *value = data;
+  }
+  return true;
+}
+
+bool KeyFileStore::SetInt(const string &group, const string &key, int value) {
+  CHECK(key_file_);
+  glib_->KeyFileSetInteger(key_file_, group.c_str(), key.c_str(), value);
+  return true;
+}
+
+}  // namespace shill