summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authormdm@chromium.org <mdm@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2010-06-16 21:33:16 +0000
committermdm@chromium.org <mdm@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2010-06-16 21:33:16 +0000
commit7f58b02555f53658403cc2639a944a5b25b28fb8 (patch)
tree2d8e637dcd64064edb4a0ce2f410d47c9db108bc
parent2307f147f598b97e3aa615738c7c74228d21efed (diff)
downloadchromium_src-7f58b02555f53658403cc2639a944a5b25b28fb8.zip
chromium_src-7f58b02555f53658403cc2639a944a5b25b28fb8.tar.gz
chromium_src-7f58b02555f53658403cc2639a944a5b25b28fb8.tar.bz2
Linux: refactor GNOME Keyring and KWallet integration to allow migration from the default store, and add unit tests. Still disabled.
BUG=12351, 25404 TEST=unit tests work Review URL: http://codereview.chromium.org/2806002 git-svn-id: svn://svn.chromium.org/chrome/trunk/src@50034 0039d316-1c4b-4281-b951-d872f2087c98
-rw-r--r--chrome/browser/password_manager/native_backend_gnome_x.cc (renamed from chrome/browser/password_manager/password_store_gnome.cc)253
-rw-r--r--chrome/browser/password_manager/native_backend_gnome_x.h62
-rw-r--r--chrome/browser/password_manager/native_backend_kwallet_x.cc (renamed from chrome/browser/password_manager/password_store_kwallet.cc)283
-rw-r--r--chrome/browser/password_manager/native_backend_kwallet_x.h (renamed from chrome/browser/password_manager/password_store_kwallet.h)92
-rw-r--r--chrome/browser/password_manager/password_store_default.cc4
-rw-r--r--chrome/browser/password_manager/password_store_gnome.h69
-rw-r--r--chrome/browser/password_manager/password_store_x.cc231
-rw-r--r--chrome/browser/password_manager/password_store_x.h107
-rw-r--r--chrome/browser/password_manager/password_store_x_unittest.cc771
-rwxr-xr-xchrome/chrome_browser.gypi40
-rwxr-xr-xchrome/chrome_tests.gypi1
11 files changed, 1524 insertions, 389 deletions
diff --git a/chrome/browser/password_manager/password_store_gnome.cc b/chrome/browser/password_manager/native_backend_gnome_x.cc
index 7495815..29febc0 100644
--- a/chrome/browser/password_manager/password_store_gnome.cc
+++ b/chrome/browser/password_manager/native_backend_gnome_x.cc
@@ -2,7 +2,7 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
-#include "chrome/browser/password_manager/password_store_gnome.h"
+#include "chrome/browser/password_manager/native_backend_gnome_x.h"
#if defined(DLOPEN_GNOME_KEYRING)
#include <dlfcn.h>
@@ -13,13 +13,12 @@
#include "base/logging.h"
#include "base/string_util.h"
-#include "base/task.h"
#include "base/time.h"
#include "base/utf_string_conversions.h"
+#include "chrome/browser/chrome_thread.h"
using std::map;
using std::string;
-using std::vector;
using webkit_glue::PasswordForm;
/* Many of the gnome_keyring_* functions use variable arguments, which makes
@@ -136,7 +135,7 @@ bool LoadGnomeKeyring() {
#define GNOME_KEYRING_APPLICATION_CHROME "chrome"
// Schema is analagous to the fields in PasswordForm.
-const GnomeKeyringPasswordSchema PasswordStoreGnome::kGnomeSchema = {
+const GnomeKeyringPasswordSchema NativeBackendGnome::kGnomeSchema = {
GNOME_KEYRING_ITEM_GENERIC_SECRET, {
{ "origin_url", GNOME_KEYRING_ATTRIBUTE_TYPE_STRING },
{ "action_url", GNOME_KEYRING_ATTRIBUTE_TYPE_STRING },
@@ -156,26 +155,47 @@ const GnomeKeyringPasswordSchema PasswordStoreGnome::kGnomeSchema = {
}
};
-PasswordStoreGnome::PasswordStoreGnome(LoginDatabase* login_db,
- Profile* profile,
- WebDataService* web_data_service) {
+NativeBackendGnome::NativeBackendGnome() {
}
-PasswordStoreGnome::~PasswordStoreGnome() {
+NativeBackendGnome::~NativeBackendGnome() {
}
-bool PasswordStoreGnome::Init() {
- return PasswordStore::Init() &&
- LoadGnomeKeyring() &&
- gnome_keyring_is_available();
+bool NativeBackendGnome::Init() {
+ return LoadGnomeKeyring() && gnome_keyring_is_available();
}
-void PasswordStoreGnome::AddLoginImpl(const PasswordForm& form) {
+bool NativeBackendGnome::AddLogin(const PasswordForm& form) {
DCHECK(ChromeThread::CurrentlyOn(ChromeThread::DB));
- AddLoginHelper(form, base::Time::Now());
+ GnomeKeyringResult result = gnome_keyring_store_password_sync(
+ &kGnomeSchema,
+ NULL, // Default keyring.
+ form.origin.spec().c_str(), // Display name.
+ UTF16ToUTF8(form.password_value).c_str(),
+ "origin_url", form.origin.spec().c_str(),
+ "action_url", form.action.spec().c_str(),
+ "username_element", UTF16ToUTF8(form.username_element).c_str(),
+ "username_value", UTF16ToUTF8(form.username_value).c_str(),
+ "password_element", UTF16ToUTF8(form.password_element).c_str(),
+ "submit_element", UTF16ToUTF8(form.submit_element).c_str(),
+ "signon_realm", form.signon_realm.c_str(),
+ "ssl_valid", form.ssl_valid,
+ "preferred", form.preferred,
+ "date_created", Int64ToString(form.date_created.ToTimeT()).c_str(),
+ "blacklisted_by_user", form.blacklisted_by_user,
+ "scheme", form.scheme,
+ "application", GNOME_KEYRING_APPLICATION_CHROME,
+ NULL);
+
+ if (result != GNOME_KEYRING_RESULT_OK) {
+ LOG(ERROR) << "Keyring save failed: "
+ << gnome_keyring_result_to_message(result);
+ return false;
+ }
+ return true;
}
-void PasswordStoreGnome::UpdateLoginImpl(const PasswordForm& form) {
+bool NativeBackendGnome::UpdateLogin(const PasswordForm& form) {
// Based on LoginDatabase::UpdateLogin(), we search for forms to update by
// origin_url, username_element, username_value, password_element, and
// signon_realm. We then compare the result to the updated form. If they
@@ -201,31 +221,35 @@ void PasswordStoreGnome::UpdateLoginImpl(const PasswordForm& form) {
"application", GNOME_KEYRING_ATTRIBUTE_TYPE_STRING,
GNOME_KEYRING_APPLICATION_CHROME,
NULL);
- vector<PasswordForm*> forms;
- if (result == GNOME_KEYRING_RESULT_OK) {
- FillFormVector(found, &forms);
- for (size_t i = 0; i < forms.size(); ++i) {
- if (forms[i]->action != form.action ||
- forms[i]->password_value != form.password_value ||
- forms[i]->ssl_valid != form.ssl_valid ||
- forms[i]->preferred != form.preferred) {
- PasswordForm updated = *forms[i];
- updated.action = form.action;
- updated.password_value = form.password_value;
- updated.ssl_valid = form.ssl_valid;
- updated.preferred = form.preferred;
- if (AddLoginHelper(updated, updated.date_created))
- RemoveLoginImpl(*forms[i]);
- }
- delete forms[i];
- }
- } else {
+ if (result != GNOME_KEYRING_RESULT_OK) {
LOG(ERROR) << "Keyring find failed: "
<< gnome_keyring_result_to_message(result);
+ return false;
+ }
+ bool ok = true;
+ PasswordFormList forms;
+ ConvertFormList(found, &forms);
+ for (size_t i = 0; i < forms.size(); ++i) {
+ if (forms[i]->action != form.action ||
+ forms[i]->password_value != form.password_value ||
+ forms[i]->ssl_valid != form.ssl_valid ||
+ forms[i]->preferred != form.preferred) {
+ PasswordForm updated = *forms[i];
+ updated.action = form.action;
+ updated.password_value = form.password_value;
+ updated.ssl_valid = form.ssl_valid;
+ updated.preferred = form.preferred;
+ if (AddLogin(updated))
+ RemoveLogin(*forms[i]);
+ else
+ ok = false;
+ }
+ delete forms[i];
}
+ return ok;
}
-void PasswordStoreGnome::RemoveLoginImpl(const PasswordForm& form) {
+bool NativeBackendGnome::RemoveLogin(const PasswordForm& form) {
DCHECK(ChromeThread::CurrentlyOn(ChromeThread::DB));
// We find forms using the same fields as LoginDatabase::RemoveLogin().
GnomeKeyringResult result = gnome_keyring_delete_password_sync(
@@ -241,43 +265,35 @@ void PasswordStoreGnome::RemoveLoginImpl(const PasswordForm& form) {
if (result != GNOME_KEYRING_RESULT_OK) {
LOG(ERROR) << "Keyring delete failed: "
<< gnome_keyring_result_to_message(result);
+ return false;
}
+ return true;
}
-void PasswordStoreGnome::RemoveLoginsCreatedBetweenImpl(
+bool NativeBackendGnome::RemoveLoginsCreatedBetween(
const base::Time& delete_begin,
const base::Time& delete_end) {
DCHECK(ChromeThread::CurrentlyOn(ChromeThread::DB));
- GList* found = NULL;
- // Search GNOME keyring for all passwords, then delete the ones in the range.
- // We need to search for something, otherwise we get no results - so we search
- // for the fixed application string.
- GnomeKeyringResult result = gnome_keyring_find_itemsv_sync(
- GNOME_KEYRING_ITEM_GENERIC_SECRET,
- &found,
- "application", GNOME_KEYRING_ATTRIBUTE_TYPE_STRING,
- GNOME_KEYRING_APPLICATION_CHROME,
- NULL);
- if (result == GNOME_KEYRING_RESULT_OK) {
- // We could walk the list and delete items as we find them, but it is much
- // easier to build the vector and use RemoveLoginImpl() to delete them.
- vector<PasswordForm*> forms;
- FillFormVector(found, &forms);
- for (size_t i = 0; i < forms.size(); ++i) {
- if (delete_begin <= forms[i]->date_created &&
- (delete_end.is_null() || forms[i]->date_created < delete_end)) {
- RemoveLoginImpl(*forms[i]);
- }
- delete forms[i];
+ bool ok = true;
+ // We could walk the list and delete items as we find them, but it is much
+ // easier to build the list and use RemoveLogin() to delete them.
+ PasswordFormList forms;
+ if (!GetAllLogins(&forms))
+ return false;
+
+ for (size_t i = 0; i < forms.size(); ++i) {
+ if (delete_begin <= forms[i]->date_created &&
+ (delete_end.is_null() || forms[i]->date_created < delete_end)) {
+ if (!RemoveLogin(*forms[i]))
+ ok = false;
}
- } else if (result != GNOME_KEYRING_RESULT_NO_MATCH) {
- LOG(ERROR) << "Keyring find failed: "
- << gnome_keyring_result_to_message(result);
+ delete forms[i];
}
+ return ok;
}
-void PasswordStoreGnome::GetLoginsImpl(GetLoginsRequest* request,
- const PasswordForm& form) {
+bool NativeBackendGnome::GetLogins(const PasswordForm& form,
+ PasswordFormList* forms) {
DCHECK(ChromeThread::CurrentlyOn(ChromeThread::DB));
GList* found = NULL;
// Search gnome keyring for matching passwords.
@@ -289,97 +305,96 @@ void PasswordStoreGnome::GetLoginsImpl(GetLoginsRequest* request,
"application", GNOME_KEYRING_ATTRIBUTE_TYPE_STRING,
GNOME_KEYRING_APPLICATION_CHROME,
NULL);
- vector<PasswordForm*> forms;
- if (result == GNOME_KEYRING_RESULT_OK) {
- FillFormVector(found, &forms);
- } else if (result != GNOME_KEYRING_RESULT_NO_MATCH) {
+ if (result == GNOME_KEYRING_RESULT_NO_MATCH)
+ return true;
+ if (result != GNOME_KEYRING_RESULT_OK) {
LOG(ERROR) << "Keyring find failed: "
<< gnome_keyring_result_to_message(result);
+ return false;
}
- NotifyConsumer(request, forms);
+ ConvertFormList(found, forms);
+ return true;
}
-void PasswordStoreGnome::GetAutofillableLoginsImpl(
- GetLoginsRequest* request) {
- std::vector<PasswordForm*> forms;
- FillAutofillableLogins(&forms);
- NotifyConsumer(request, forms);
-}
+bool NativeBackendGnome::GetLoginsCreatedBetween(const base::Time& get_begin,
+ const base::Time& get_end,
+ PasswordFormList* forms) {
+ DCHECK(ChromeThread::CurrentlyOn(ChromeThread::DB));
+ // We could walk the list and add items as we find them, but it is much
+ // easier to build the list and then filter the results.
+ PasswordFormList all_forms;
+ if (!GetAllLogins(&all_forms))
+ return false;
+
+ forms->reserve(forms->size() + all_forms.size());
+ for (size_t i = 0; i < all_forms.size(); ++i) {
+ if (get_begin <= all_forms[i]->date_created &&
+ (get_end.is_null() || all_forms[i]->date_created < get_end)) {
+ forms->push_back(all_forms[i]);
+ } else {
+ delete all_forms[i];
+ }
+ }
-void PasswordStoreGnome::GetBlacklistLoginsImpl(
- GetLoginsRequest* request) {
- std::vector<PasswordForm*> forms;
- FillBlacklistLogins(&forms);
- NotifyConsumer(request, forms);
+ return true;
}
-bool PasswordStoreGnome::FillAutofillableLogins(
- std::vector<PasswordForm*>* forms) {
- return FillSomeLogins(true, forms);
+bool NativeBackendGnome::GetAutofillableLogins(PasswordFormList* forms) {
+ return GetLoginsList(forms, true);
}
-bool PasswordStoreGnome::FillBlacklistLogins(
- std::vector<PasswordForm*>* forms) {
- return FillSomeLogins(false, forms);
+bool NativeBackendGnome::GetBlacklistLogins(PasswordFormList* forms) {
+ return GetLoginsList(forms, false);
}
-bool PasswordStoreGnome::AddLoginHelper(const PasswordForm& form,
- const base::Time& date_created) {
- GnomeKeyringResult result = gnome_keyring_store_password_sync(
- &kGnomeSchema,
- NULL, // Default keyring.
- form.origin.spec().c_str(), // Display name.
- UTF16ToUTF8(form.password_value).c_str(),
- "origin_url", form.origin.spec().c_str(),
- "action_url", form.action.spec().c_str(),
- "username_element", UTF16ToUTF8(form.username_element).c_str(),
- "username_value", UTF16ToUTF8(form.username_value).c_str(),
- "password_element", UTF16ToUTF8(form.password_element).c_str(),
- "submit_element", UTF16ToUTF8(form.submit_element).c_str(),
- "signon_realm", form.signon_realm.c_str(),
- "ssl_valid", form.ssl_valid,
- "preferred", form.preferred,
- "date_created", Int64ToString(date_created.ToTimeT()).c_str(),
- "blacklisted_by_user", form.blacklisted_by_user,
- "scheme", form.scheme,
- "application", GNOME_KEYRING_APPLICATION_CHROME,
+bool NativeBackendGnome::GetLoginsList(PasswordFormList* forms,
+ bool autofillable) {
+ DCHECK(ChromeThread::CurrentlyOn(ChromeThread::DB));
+ GList* found = NULL;
+ uint32_t blacklisted_by_user = !autofillable;
+ // Search gnome keyring for matching passwords.
+ GnomeKeyringResult result = gnome_keyring_find_itemsv_sync(
+ GNOME_KEYRING_ITEM_GENERIC_SECRET,
+ &found,
+ "blacklisted_by_user", GNOME_KEYRING_ATTRIBUTE_TYPE_UINT32,
+ blacklisted_by_user,
+ "application", GNOME_KEYRING_ATTRIBUTE_TYPE_STRING,
+ GNOME_KEYRING_APPLICATION_CHROME,
NULL);
-
+ if (result == GNOME_KEYRING_RESULT_NO_MATCH)
+ return true;
if (result != GNOME_KEYRING_RESULT_OK) {
- LOG(ERROR) << "Keyring save failed: "
+ LOG(ERROR) << "Keyring find failed: "
<< gnome_keyring_result_to_message(result);
return false;
}
+ ConvertFormList(found, forms);
return true;
}
-bool PasswordStoreGnome::FillSomeLogins(
- bool autofillable,
- std::vector<PasswordForm*>* forms) {
- DCHECK(ChromeThread::CurrentlyOn(ChromeThread::DB));
+bool NativeBackendGnome::GetAllLogins(PasswordFormList* forms) {
GList* found = NULL;
- uint32_t blacklisted_by_user = !autofillable;
- // Search gnome keyring for matching passwords.
+ // We need to search for something, otherwise we get no results - so we search
+ // for the fixed application string.
GnomeKeyringResult result = gnome_keyring_find_itemsv_sync(
GNOME_KEYRING_ITEM_GENERIC_SECRET,
&found,
- "blacklisted_by_user", GNOME_KEYRING_ATTRIBUTE_TYPE_UINT32,
- blacklisted_by_user,
"application", GNOME_KEYRING_ATTRIBUTE_TYPE_STRING,
GNOME_KEYRING_APPLICATION_CHROME,
NULL);
- if (result == GNOME_KEYRING_RESULT_OK) {
- FillFormVector(found, forms);
- } else if (result != GNOME_KEYRING_RESULT_NO_MATCH) {
+ if (result == GNOME_KEYRING_RESULT_NO_MATCH)
+ return true;
+ if (result != GNOME_KEYRING_RESULT_OK) {
LOG(ERROR) << "Keyring find failed: "
<< gnome_keyring_result_to_message(result);
return false;
}
+ ConvertFormList(found, forms);
return true;
}
-void PasswordStoreGnome::FillFormVector(GList* found,
- std::vector<PasswordForm*>* forms) {
+void NativeBackendGnome::ConvertFormList(GList* found,
+ PasswordFormList* forms) {
GList* element = g_list_first(found);
while (element != NULL) {
GnomeKeyringFound* data = static_cast<GnomeKeyringFound*>(element->data);
diff --git a/chrome/browser/password_manager/native_backend_gnome_x.h b/chrome/browser/password_manager/native_backend_gnome_x.h
new file mode 100644
index 0000000..aa554be
--- /dev/null
+++ b/chrome/browser/password_manager/native_backend_gnome_x.h
@@ -0,0 +1,62 @@
+// Copyright (c) 2010 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef CHROME_BROWSER_PASSWORD_MANAGER_NATIVE_BACKEND_GNOME_X_H_
+#define CHROME_BROWSER_PASSWORD_MANAGER_NATIVE_BACKEND_GNOME_X_H_
+
+extern "C" {
+#include <gnome-keyring.h>
+}
+
+#include <vector>
+
+#include "base/basictypes.h"
+#include "base/time.h"
+#include "chrome/browser/password_manager/password_store_x.h"
+
+namespace webkit_glue {
+struct PasswordForm;
+}
+
+// NativeBackend implementation using GNOME Keyring.
+class NativeBackendGnome : public PasswordStoreX::NativeBackend {
+ public:
+ NativeBackendGnome();
+
+ virtual ~NativeBackendGnome();
+
+ virtual bool Init();
+
+ // Implements NativeBackend interface.
+ virtual bool AddLogin(const webkit_glue::PasswordForm& form);
+ virtual bool UpdateLogin(const webkit_glue::PasswordForm& form);
+ virtual bool RemoveLogin(const webkit_glue::PasswordForm& form);
+ virtual bool RemoveLoginsCreatedBetween(const base::Time& delete_begin,
+ const base::Time& delete_end);
+ virtual bool GetLogins(const webkit_glue::PasswordForm& form,
+ PasswordFormList* forms);
+ virtual bool GetLoginsCreatedBetween(const base::Time& get_begin,
+ const base::Time& get_end,
+ PasswordFormList* forms);
+ virtual bool GetAutofillableLogins(PasswordFormList* forms);
+ virtual bool GetBlacklistLogins(PasswordFormList* forms);
+
+ private:
+ // Reads PasswordForms from the keyring with the given autofillability state.
+ bool GetLoginsList(PasswordFormList* forms, bool autofillable);
+
+ // Helper for GetLoginsCreatedBetween().
+ bool GetAllLogins(PasswordFormList* forms);
+
+ // Parse all the results from the given GList into a PasswordFormList, and
+ // free the GList. PasswordForms are allocated on the heap, and should be
+ // deleted by the consumer.
+ void ConvertFormList(GList* found, PasswordFormList* forms);
+
+ static const GnomeKeyringPasswordSchema kGnomeSchema;
+
+ DISALLOW_COPY_AND_ASSIGN(NativeBackendGnome);
+};
+
+#endif // CHROME_BROWSER_PASSWORD_MANAGER_NATIVE_BACKEND_GNOME_X_H_
diff --git a/chrome/browser/password_manager/password_store_kwallet.cc b/chrome/browser/password_manager/native_backend_kwallet_x.cc
index 26f0ab9..501ce1f 100644
--- a/chrome/browser/password_manager/password_store_kwallet.cc
+++ b/chrome/browser/password_manager/native_backend_kwallet_x.cc
@@ -2,16 +2,15 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
-#include "chrome/browser/password_manager/password_store_kwallet.h"
+#include "chrome/browser/password_manager/native_backend_kwallet_x.h"
#include <sstream>
#include "base/logging.h"
-#include "base/md5.h"
#include "base/pickle.h"
#include "base/stl_util-inl.h"
#include "base/string_util.h"
-#include "base/task.h"
+#include "chrome/browser/chrome_thread.h"
using std::string;
using std::vector;
@@ -19,31 +18,28 @@ using webkit_glue::PasswordForm;
// We could localize these strings, but then changing your locale would cause
// you to lose access to all your stored passwords. Maybe best not to do that.
-const char* PasswordStoreKWallet::kAppId = "Chrome";
-const char* PasswordStoreKWallet::kKWalletFolder = "Chrome Form Data";
-
-const char* PasswordStoreKWallet::kKWalletServiceName = "org.kde.kwalletd";
-const char* PasswordStoreKWallet::kKWalletPath = "/modules/kwalletd";
-const char* PasswordStoreKWallet::kKWalletInterface = "org.kde.KWallet";
-const char* PasswordStoreKWallet::kKLauncherServiceName = "org.kde.klauncher";
-const char* PasswordStoreKWallet::kKLauncherPath = "/KLauncher";
-const char* PasswordStoreKWallet::kKLauncherInterface = "org.kde.KLauncher";
-
-PasswordStoreKWallet::PasswordStoreKWallet(LoginDatabase* login_db,
- Profile* profile,
- WebDataService* web_data_service)
+const char* NativeBackendKWallet::kAppId = "Chrome";
+const char* NativeBackendKWallet::kKWalletFolder = "Chrome Form Data";
+
+const char* NativeBackendKWallet::kKWalletServiceName = "org.kde.kwalletd";
+const char* NativeBackendKWallet::kKWalletPath = "/modules/kwalletd";
+const char* NativeBackendKWallet::kKWalletInterface = "org.kde.KWallet";
+const char* NativeBackendKWallet::kKLauncherServiceName = "org.kde.klauncher";
+const char* NativeBackendKWallet::kKLauncherPath = "/KLauncher";
+const char* NativeBackendKWallet::kKLauncherInterface = "org.kde.KLauncher";
+
+NativeBackendKWallet::NativeBackendKWallet()
: error_(NULL),
connection_(NULL),
proxy_(NULL) {
}
-PasswordStoreKWallet::~PasswordStoreKWallet() {
- if (proxy_) {
+NativeBackendKWallet::~NativeBackendKWallet() {
+ if (proxy_)
g_object_unref(proxy_);
- }
}
-bool PasswordStoreKWallet::Init() {
+bool NativeBackendKWallet::Init() {
// Initialize threading in dbus-glib - it should be fine for
// dbus_g_thread_init to be called multiple times.
if (!g_thread_supported())
@@ -64,7 +60,7 @@ bool PasswordStoreKWallet::Init() {
return true;
}
-bool PasswordStoreKWallet::StartKWalletd() {
+bool NativeBackendKWallet::StartKWalletd() {
// Sadly kwalletd doesn't use DBUS activation, so we have to make a call to
// klauncher to start it.
DBusGProxy* klauncher_proxy =
@@ -100,7 +96,7 @@ bool PasswordStoreKWallet::StartKWalletd() {
return true;
}
-bool PasswordStoreKWallet::InitWallet() {
+bool NativeBackendKWallet::InitWallet() {
// Make a proxy to KWallet.
proxy_ = dbus_g_proxy_new_for_name(connection_, kKWalletServiceName,
kKWalletPath, kKWalletInterface);
@@ -129,26 +125,25 @@ bool PasswordStoreKWallet::InitWallet() {
return true;
}
-void PasswordStoreKWallet::AddLoginImpl(const PasswordForm& form) {
- AutoLock l(kwallet_lock_);
+bool NativeBackendKWallet::AddLogin(const PasswordForm& form) {
int wallet_handle = WalletHandle();
if (wallet_handle == kInvalidKWalletHandle)
- return;
+ return false;
PasswordFormList forms;
GetLoginsList(&forms, form.signon_realm, wallet_handle);
forms.push_back(new PasswordForm(form));
- SetLoginsList(forms, form.signon_realm, wallet_handle);
+ bool ok = SetLoginsList(forms, form.signon_realm, wallet_handle);
STLDeleteElements(&forms);
+ return ok;
}
-void PasswordStoreKWallet::UpdateLoginImpl(const PasswordForm& form) {
- AutoLock l(kwallet_lock_);
+bool NativeBackendKWallet::UpdateLogin(const PasswordForm& form) {
int wallet_handle = WalletHandle();
if (wallet_handle == kInvalidKWalletHandle)
- return;
+ return false;
PasswordFormList forms;
GetLoginsList(&forms, form.signon_realm, wallet_handle);
@@ -158,16 +153,16 @@ void PasswordStoreKWallet::UpdateLoginImpl(const PasswordForm& form) {
*forms[i] = form;
}
- SetLoginsList(forms, form.signon_realm, wallet_handle);
+ bool ok = SetLoginsList(forms, form.signon_realm, wallet_handle);
STLDeleteElements(&forms);
+ return ok;
}
-void PasswordStoreKWallet::RemoveLoginImpl(const PasswordForm& form) {
- AutoLock l(kwallet_lock_);
+bool NativeBackendKWallet::RemoveLogin(const PasswordForm& form) {
int wallet_handle = WalletHandle();
if (wallet_handle == kInvalidKWalletHandle)
- return;
+ return false;
PasswordFormList all_forms;
GetLoginsList(&all_forms, form.signon_realm, wallet_handle);
@@ -181,18 +176,19 @@ void PasswordStoreKWallet::RemoveLoginImpl(const PasswordForm& form) {
kept_forms.push_back(all_forms[i]);
}
- // Update the entry in the wallet.
- SetLoginsList(kept_forms, form.signon_realm, wallet_handle);
+ // Update the entry in the wallet, possibly deleting it.
+ bool ok = SetLoginsList(kept_forms, form.signon_realm, wallet_handle);
+
STLDeleteElements(&kept_forms);
+ return ok;
}
-void PasswordStoreKWallet::RemoveLoginsCreatedBetweenImpl(
+bool NativeBackendKWallet::RemoveLoginsCreatedBetween(
const base::Time& delete_begin,
const base::Time& delete_end) {
- AutoLock l(kwallet_lock_);
int wallet_handle = WalletHandle();
if (wallet_handle == kInvalidKWalletHandle)
- return;
+ return false;
// We could probably also use readEntryList here.
char** realm_list = NULL;
@@ -204,8 +200,9 @@ void PasswordStoreKWallet::RemoveLoginsCreatedBetweenImpl(
G_TYPE_STRV, &realm_list,
G_TYPE_INVALID);
if (CheckError())
- return;
+ return false;
+ bool ok = true;
for (char** realm = realm_list; *realm; ++realm) {
GArray* byte_array = NULL;
dbus_g_proxy_call(proxy_, "readEntry", &error_,
@@ -237,49 +234,46 @@ void PasswordStoreKWallet::RemoveLoginsCreatedBetweenImpl(
}
}
- SetLoginsList(kept_forms, signon_realm, wallet_handle);
+ if (!SetLoginsList(kept_forms, signon_realm, wallet_handle))
+ ok = false;
STLDeleteElements(&kept_forms);
}
g_strfreev(realm_list);
+ return ok;
}
-void PasswordStoreKWallet::GetLoginsImpl(GetLoginsRequest* request,
- const PasswordForm& form) {
- PasswordFormList forms;
-
- AutoLock l(kwallet_lock_);
+bool NativeBackendKWallet::GetLogins(const PasswordForm& form,
+ PasswordFormList* forms) {
int wallet_handle = WalletHandle();
- if (wallet_handle != kInvalidKWalletHandle)
- GetLoginsList(&forms, form.signon_realm, wallet_handle);
-
- NotifyConsumer(request, forms);
-}
-
-void PasswordStoreKWallet::GetAutofillableLoginsImpl(
- GetLoginsRequest* request) {
- std::vector<PasswordForm*> forms;
- FillAutofillableLogins(&forms);
- NotifyConsumer(request, forms);
+ if (wallet_handle == kInvalidKWalletHandle)
+ return false;
+ return GetLoginsList(forms, form.signon_realm, wallet_handle);
}
-void PasswordStoreKWallet::GetBlacklistLoginsImpl(
- GetLoginsRequest* request) {
- std::vector<PasswordForm*> forms;
- FillBlacklistLogins(&forms);
- NotifyConsumer(request, forms);
+bool NativeBackendKWallet::GetLoginsCreatedBetween(const base::Time& get_begin,
+ const base::Time& get_end,
+ PasswordFormList* forms) {
+ int wallet_handle = WalletHandle();
+ if (wallet_handle == kInvalidKWalletHandle)
+ return false;
+ return GetLoginsList(forms, get_begin, get_end, wallet_handle);
}
-bool PasswordStoreKWallet::FillAutofillableLogins(
- std::vector<PasswordForm*>* forms) {
- return FillSomeLogins(true, forms);
+bool NativeBackendKWallet::GetAutofillableLogins(PasswordFormList* forms) {
+ int wallet_handle = WalletHandle();
+ if (wallet_handle == kInvalidKWalletHandle)
+ return false;
+ return GetLoginsList(forms, true, wallet_handle);
}
-bool PasswordStoreKWallet::FillBlacklistLogins(
- std::vector<PasswordForm*>* forms) {
- return FillSomeLogins(false, forms);
+bool NativeBackendKWallet::GetBlacklistLogins(PasswordFormList* forms) {
+ int wallet_handle = WalletHandle();
+ if (wallet_handle == kInvalidKWalletHandle)
+ return false;
+ return GetLoginsList(forms, false, wallet_handle);
}
-void PasswordStoreKWallet::GetLoginsList(PasswordFormList* forms,
+bool NativeBackendKWallet::GetLoginsList(PasswordFormList* forms,
const string& signon_realm,
int wallet_handle) {
// Is there an entry in the wallet?
@@ -294,7 +288,7 @@ void PasswordStoreKWallet::GetLoginsList(PasswordFormList* forms,
G_TYPE_INVALID);
if (CheckError() || !has_entry)
- return;
+ return false;
GArray* byte_array = NULL;
dbus_g_proxy_call(proxy_, "readEntry", &error_,
@@ -307,14 +301,93 @@ void PasswordStoreKWallet::GetLoginsList(PasswordFormList* forms,
G_TYPE_INVALID);
if (CheckError() || !byte_array || !byte_array->len)
- return;
+ return false;
Pickle pickle(byte_array->data, byte_array->len);
DeserializeValue(signon_realm, pickle, forms);
g_array_free(byte_array, true);
+
+ return true;
}
-void PasswordStoreKWallet::SetLoginsList(const PasswordFormList& forms,
+bool NativeBackendKWallet::GetLoginsList(PasswordFormList* forms,
+ bool autofillable,
+ int wallet_handle) {
+ PasswordFormList all_forms;
+ if (!GetAllLogins(&all_forms, wallet_handle))
+ return false;
+
+ // We have to read all the entries, and then filter them here.
+ forms->reserve(forms->size() + all_forms.size());
+ for (size_t i = 0; i < all_forms.size(); ++i) {
+ if (all_forms[i]->blacklisted_by_user == !autofillable)
+ forms->push_back(all_forms[i]);
+ else
+ delete all_forms[i];
+ }
+
+ return true;
+}
+
+bool NativeBackendKWallet::GetLoginsList(PasswordFormList* forms,
+ const base::Time& begin,
+ const base::Time& end,
+ int wallet_handle) {
+ PasswordFormList all_forms;
+ if (!GetAllLogins(&all_forms, wallet_handle))
+ return false;
+
+ // We have to read all the entries, and then filter them here.
+ forms->reserve(forms->size() + all_forms.size());
+ for (size_t i = 0; i < all_forms.size(); ++i) {
+ if (begin <= all_forms[i]->date_created &&
+ (end.is_null() || all_forms[i]->date_created < end)) {
+ forms->push_back(all_forms[i]);
+ } else {
+ delete all_forms[i];
+ }
+ }
+
+ return true;
+}
+
+bool NativeBackendKWallet::GetAllLogins(PasswordFormList* forms,
+ int wallet_handle) {
+ // We could probably also use readEntryList here.
+ char** realm_list = NULL;
+ dbus_g_proxy_call(proxy_, "entryList", &error_,
+ G_TYPE_INT, wallet_handle, // handle
+ G_TYPE_STRING, kKWalletFolder, // folder
+ G_TYPE_STRING, kAppId, // appid
+ G_TYPE_INVALID,
+ G_TYPE_STRV, &realm_list,
+ G_TYPE_INVALID);
+ if (CheckError())
+ return false;
+
+ for (char** realm = realm_list; *realm; ++realm) {
+ GArray* byte_array = NULL;
+ dbus_g_proxy_call(proxy_, "readEntry", &error_,
+ G_TYPE_INT, wallet_handle, // handle
+ G_TYPE_STRING, kKWalletFolder, // folder
+ G_TYPE_STRING, *realm, // key
+ G_TYPE_STRING, kAppId, // appid
+ G_TYPE_INVALID,
+ DBUS_TYPE_G_UCHAR_ARRAY, &byte_array,
+ G_TYPE_INVALID);
+
+ if (CheckError() || !byte_array || !byte_array->len)
+ continue;
+
+ Pickle pickle(byte_array->data, byte_array->len);
+ DeserializeValue(*realm, pickle, forms);
+ g_array_free(byte_array, true);
+ }
+ g_strfreev(realm_list);
+ return true;
+}
+
+bool NativeBackendKWallet::SetLoginsList(const PasswordFormList& forms,
const string& signon_realm,
int wallet_handle) {
if (forms.empty()) {
@@ -331,7 +404,7 @@ void PasswordStoreKWallet::SetLoginsList(const PasswordFormList& forms,
CheckError();
if (ret != 0)
LOG(ERROR) << "Bad return code " << ret << " from kwallet removeEntry";
- return;
+ return ret == 0;
}
Pickle value;
@@ -358,61 +431,10 @@ void PasswordStoreKWallet::SetLoginsList(const PasswordFormList& forms,
CheckError();
if (ret != 0)
LOG(ERROR) << "Bad return code " << ret << " from kwallet writeEntry";
+ return ret == 0;
}
-bool PasswordStoreKWallet::FillSomeLogins(bool autofillable,
- PasswordFormList* forms) {
- AutoLock l(kwallet_lock_);
- int wallet_handle = WalletHandle();
- if (wallet_handle == kInvalidKWalletHandle)
- return false;
-
- // We could probably also use readEntryList here.
- char** realm_list = NULL;
- dbus_g_proxy_call(proxy_, "entryList", &error_,
- G_TYPE_INT, wallet_handle, // handle
- G_TYPE_STRING, kKWalletFolder, // folder
- G_TYPE_STRING, kAppId, // appid
- G_TYPE_INVALID,
- G_TYPE_STRV, &realm_list,
- G_TYPE_INVALID);
- if (CheckError())
- return false;
-
- PasswordFormList all_forms;
- for (char** realm = realm_list; *realm; ++realm) {
- GArray* byte_array = NULL;
- dbus_g_proxy_call(proxy_, "readEntry", &error_,
- G_TYPE_INT, wallet_handle, // handle
- G_TYPE_STRING, kKWalletFolder, // folder
- G_TYPE_STRING, *realm, // key
- G_TYPE_STRING, kAppId, // appid
- G_TYPE_INVALID,
- DBUS_TYPE_G_UCHAR_ARRAY, &byte_array,
- G_TYPE_INVALID);
-
- if (CheckError() || !byte_array || !byte_array->len)
- continue;
-
- Pickle pickle(byte_array->data, byte_array->len);
- DeserializeValue(*realm, pickle, &all_forms);
- g_array_free(byte_array, true);
- }
- g_strfreev(realm_list);
-
- // We have to read all the entries, and then filter them here.
- forms->reserve(forms->size() + all_forms.size());
- for (size_t i = 0; i < all_forms.size(); ++i) {
- if (all_forms[i]->blacklisted_by_user == !autofillable)
- forms->push_back(all_forms[i]);
- else
- delete all_forms[i];
- }
-
- return true;
-}
-
-bool PasswordStoreKWallet::CompareForms(const PasswordForm& a,
+bool NativeBackendKWallet::CompareForms(const PasswordForm& a,
const PasswordForm& b,
bool update_check) {
// An update check doesn't care about the submit element.
@@ -425,7 +447,7 @@ bool PasswordStoreKWallet::CompareForms(const PasswordForm& a,
a.username_value == b.username_value;
}
-void PasswordStoreKWallet::SerializeValue(const PasswordFormList& forms,
+void NativeBackendKWallet::SerializeValue(const PasswordFormList& forms,
Pickle* pickle) {
pickle->WriteInt(kPickleVersion);
pickle->WriteSize(forms.size());
@@ -447,7 +469,7 @@ void PasswordStoreKWallet::SerializeValue(const PasswordFormList& forms,
}
}
-void PasswordStoreKWallet::DeserializeValue(const string& signon_realm,
+void NativeBackendKWallet::DeserializeValue(const string& signon_realm,
const Pickle& pickle,
PasswordFormList* forms) {
void* iter = NULL;
@@ -487,14 +509,14 @@ void PasswordStoreKWallet::DeserializeValue(const string& signon_realm,
}
}
-void PasswordStoreKWallet::ReadGURL(const Pickle& pickle, void** iter,
+void NativeBackendKWallet::ReadGURL(const Pickle& pickle, void** iter,
GURL* url) {
string url_string;
pickle.ReadString(iter, &url_string);
*url = GURL(url_string);
}
-bool PasswordStoreKWallet::CheckError() {
+bool NativeBackendKWallet::CheckError() {
if (error_) {
LOG(ERROR) << "Failed to complete KWallet call: " << error_->message;
g_error_free(error_);
@@ -504,7 +526,8 @@ bool PasswordStoreKWallet::CheckError() {
return false;
}
-int PasswordStoreKWallet::WalletHandle() {
+int NativeBackendKWallet::WalletHandle() {
+ DCHECK(ChromeThread::CurrentlyOn(ChromeThread::DB));
// Open the wallet.
int handle = kInvalidKWalletHandle;
dbus_g_proxy_call(proxy_, "open", &error_,
diff --git a/chrome/browser/password_manager/password_store_kwallet.h b/chrome/browser/password_manager/native_backend_kwallet_x.h
index f7835fe..ad2b367 100644
--- a/chrome/browser/password_manager/password_store_kwallet.h
+++ b/chrome/browser/password_manager/native_backend_kwallet_x.h
@@ -2,72 +2,75 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
-#ifndef CHROME_BROWSER_PASSWORD_MANAGER_PASSWORD_STORE_KWALLET_H_
-#define CHROME_BROWSER_PASSWORD_MANAGER_PASSWORD_STORE_KWALLET_H_
+#ifndef CHROME_BROWSER_PASSWORD_MANAGER_NATIVE_BACKEND_KWALLET_X_H_
+#define CHROME_BROWSER_PASSWORD_MANAGER_NATIVE_BACKEND_KWALLET_X_H_
#include <dbus/dbus-glib.h>
#include <glib.h>
#include <string>
-#include <vector>
-#include "base/lock.h"
-#include "chrome/browser/password_manager/login_database.h"
-#include "chrome/browser/password_manager/password_store.h"
-#include "chrome/browser/webdata/web_data_service.h"
+#include "base/basictypes.h"
+#include "base/time.h"
+#include "chrome/browser/password_manager/password_store_x.h"
#include "webkit/glue/password_form.h"
class Pickle;
-class Profile;
-class Task;
-class PasswordStoreKWallet : public PasswordStore {
+// NativeBackend implementation using KWallet.
+class NativeBackendKWallet : public PasswordStoreX::NativeBackend {
public:
- PasswordStoreKWallet(LoginDatabase* login_db,
- Profile* profile,
- WebDataService* web_data_service);
-
- bool Init();
+ NativeBackendKWallet();
+
+ virtual ~NativeBackendKWallet();
+
+ virtual bool Init();
+
+ // Implements NativeBackend interface.
+ virtual bool AddLogin(const webkit_glue::PasswordForm& form);
+ virtual bool UpdateLogin(const webkit_glue::PasswordForm& form);
+ virtual bool RemoveLogin(const webkit_glue::PasswordForm& form);
+ virtual bool RemoveLoginsCreatedBetween(const base::Time& delete_begin,
+ const base::Time& delete_end);
+ virtual bool GetLogins(const webkit_glue::PasswordForm& form,
+ PasswordFormList* forms);
+ virtual bool GetLoginsCreatedBetween(const base::Time& delete_begin,
+ const base::Time& delete_end,
+ PasswordFormList* forms);
+ virtual bool GetAutofillableLogins(PasswordFormList* forms);
+ virtual bool GetBlacklistLogins(PasswordFormList* forms);
private:
- typedef std::vector<webkit_glue::PasswordForm*> PasswordFormList;
-
- virtual ~PasswordStoreKWallet();
-
- // Implements PasswordStore interface.
- virtual void AddLoginImpl(const webkit_glue::PasswordForm& form);
- virtual void UpdateLoginImpl(const webkit_glue::PasswordForm& form);
- virtual void RemoveLoginImpl(const webkit_glue::PasswordForm& form);
- virtual void RemoveLoginsCreatedBetweenImpl(const base::Time& delete_begin,
- const base::Time& delete_end);
- virtual void GetLoginsImpl(GetLoginsRequest* request,
- const webkit_glue::PasswordForm& form);
- virtual void GetAutofillableLoginsImpl(GetLoginsRequest* request);
- virtual void GetBlacklistLoginsImpl(GetLoginsRequest* request);
- virtual bool FillAutofillableLogins(
- std::vector<webkit_glue::PasswordForm*>* forms);
- virtual bool FillBlacklistLogins(
- std::vector<webkit_glue::PasswordForm*>* forms);
-
// Initialization.
bool StartKWalletd();
bool InitWallet();
- // Reads a list of PasswordForms from the wallet that match the signon_realm.
- void GetLoginsList(PasswordFormList* forms,
+ // Reads PasswordForms from the wallet that match the given signon_realm.
+ bool GetLoginsList(PasswordFormList* forms,
const std::string& signon_realm,
int wallet_handle);
+ // Reads PasswordForms from the wallet with the given autofillability state.
+ bool GetLoginsList(PasswordFormList* forms,
+ bool autofillable,
+ int wallet_handle);
+
+ // Reads PasswordForms from the wallet created in the given time range.
+ bool GetLoginsList(PasswordFormList* forms,
+ const base::Time& begin,
+ const base::Time& end,
+ int wallet_handle);
+
+ // Helper for some of the above GetLoginsList() methods.
+ bool GetAllLogins(PasswordFormList* forms, int wallet_handle);
+
// Writes a list of PasswordForms to the wallet with the given signon_realm.
// Overwrites any existing list for this signon_realm. Removes the entry if
- // |forms| is empty.
- void SetLoginsList(const PasswordFormList& forms,
+ // |forms| is empty. Returns true on success.
+ bool SetLoginsList(const PasswordFormList& forms,
const std::string& signon_realm,
int wallet_handle);
- // Helper for FillAutofillableLogins() and FillBlacklistLogins().
- bool FillSomeLogins(bool autofillable, PasswordFormList* forms);
-
// Checks if the last DBus call returned an error. If it did, logs the error
// message, frees it and returns true.
// This must be called after every DBus call.
@@ -117,9 +120,6 @@ class PasswordStoreKWallet : public PasswordStore {
// Invalid handle returned by WalletHandle().
static const int kInvalidKWalletHandle = -1;
- // Controls all access to kwallet DBus calls.
- Lock kwallet_lock_;
-
// Error from the last DBus call. NULL when there's no error. Freed and
// cleared by CheckError().
GError* error_;
@@ -131,7 +131,7 @@ class PasswordStoreKWallet : public PasswordStore {
// The name of the wallet we've opened. Set during Init().
std::string wallet_name_;
- DISALLOW_COPY_AND_ASSIGN(PasswordStoreKWallet);
+ DISALLOW_COPY_AND_ASSIGN(NativeBackendKWallet);
};
-#endif // CHROME_BROWSER_PASSWORD_MANAGER_PASSWORD_STORE_KWALLET_H_
+#endif // CHROME_BROWSER_PASSWORD_MANAGER_NATIVE_BACKEND_KWALLET_X_H_
diff --git a/chrome/browser/password_manager/password_store_default.cc b/chrome/browser/password_manager/password_store_default.cc
index 8d3ff96..dc58f56 100644
--- a/chrome/browser/password_manager/password_store_default.cc
+++ b/chrome/browser/password_manager/password_store_default.cc
@@ -4,6 +4,8 @@
#include "chrome/browser/password_manager/password_store_default.h"
+#include <vector>
+
#include "chrome/browser/chrome_thread.h"
#include "chrome/browser/password_manager/password_store_change.h"
#include "chrome/browser/pref_service.h"
@@ -145,7 +147,7 @@ void PasswordStoreDefault::OnWebDataServiceRequestDone(
static_cast<const WDResult<PasswordForms>*>(result)->GetValue();
for (PasswordForms::const_iterator it = forms.begin();
it != forms.end(); ++it) {
- AddLoginImpl(**it);
+ AddLogin(**it);
web_data_service_->RemoveLogin(**it);
delete *it;
}
diff --git a/chrome/browser/password_manager/password_store_gnome.h b/chrome/browser/password_manager/password_store_gnome.h
deleted file mode 100644
index 8927ba3..0000000
--- a/chrome/browser/password_manager/password_store_gnome.h
+++ /dev/null
@@ -1,69 +0,0 @@
-// Copyright (c) 2010 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#ifndef CHROME_BROWSER_PASSWORD_MANAGER_PASSWORD_STORE_GNOME_H_
-#define CHROME_BROWSER_PASSWORD_MANAGER_PASSWORD_STORE_GNOME_H_
-
-extern "C" {
-#include <gnome-keyring.h>
-}
-
-#include <vector>
-
-#include "base/lock.h"
-#include "chrome/browser/password_manager/login_database.h"
-#include "chrome/browser/password_manager/password_store.h"
-#include "chrome/browser/profile.h"
-#include "chrome/browser/webdata/web_data_service.h"
-
-class Profile;
-class Task;
-
-// PasswordStore implementation using GNOME Keyring.
-class PasswordStoreGnome : public PasswordStore {
- public:
- PasswordStoreGnome(LoginDatabase* login_db,
- Profile* profile,
- WebDataService* web_data_service);
-
- virtual bool Init();
-
- private:
- virtual ~PasswordStoreGnome();
-
- // Implements PasswordStore interface.
- virtual void AddLoginImpl(const webkit_glue::PasswordForm& form);
- virtual void UpdateLoginImpl(const webkit_glue::PasswordForm& form);
- virtual void RemoveLoginImpl(const webkit_glue::PasswordForm& form);
- virtual void RemoveLoginsCreatedBetweenImpl(const base::Time& delete_begin,
- const base::Time& delete_end);
- virtual void GetLoginsImpl(GetLoginsRequest* request,
- const webkit_glue::PasswordForm& form);
- virtual void GetAutofillableLoginsImpl(GetLoginsRequest* request);
- virtual void GetBlacklistLoginsImpl(GetLoginsRequest* request);
- virtual bool FillAutofillableLogins(
- std::vector<webkit_glue::PasswordForm*>* forms);
- virtual bool FillBlacklistLogins(
- std::vector<webkit_glue::PasswordForm*>* forms);
-
- // Helper for AddLoginImpl() and UpdateLoginImpl() with a success status.
- bool AddLoginHelper(const webkit_glue::PasswordForm& form,
- const base::Time& date_created);
-
- // Helper for FillAutofillableLogins() and FillBlacklistLogins().
- bool FillSomeLogins(bool autofillable,
- std::vector<webkit_glue::PasswordForm*>* forms);
-
- // Parse all the results from the given GList into a
- // vector<PasswordForm*>, and free the GList. PasswordForms are
- // allocated on the heap, and should be deleted by the consumer.
- void FillFormVector(GList* found,
- std::vector<webkit_glue::PasswordForm*>* forms);
-
- static const GnomeKeyringPasswordSchema kGnomeSchema;
-
- DISALLOW_COPY_AND_ASSIGN(PasswordStoreGnome);
-};
-
-#endif // CHROME_BROWSER_PASSWORD_MANAGER_PASSWORD_STORE_GNOME_H_
diff --git a/chrome/browser/password_manager/password_store_x.cc b/chrome/browser/password_manager/password_store_x.cc
new file mode 100644
index 0000000..a9f7021
--- /dev/null
+++ b/chrome/browser/password_manager/password_store_x.cc
@@ -0,0 +1,231 @@
+// Copyright (c) 2010 The Chromium 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 "chrome/browser/password_manager/password_store_x.h"
+
+#include <map>
+#include <vector>
+
+#include "base/logging.h"
+#include "base/stl_util-inl.h"
+#include "chrome/browser/chrome_thread.h"
+#include "chrome/browser/password_manager/password_store_change.h"
+#include "chrome/common/notification_service.h"
+
+using std::vector;
+using webkit_glue::PasswordForm;
+
+PasswordStoreX::PasswordStoreX(LoginDatabase* login_db,
+ Profile* profile,
+ WebDataService* web_data_service,
+ NativeBackend* backend)
+ : PasswordStoreDefault(login_db, profile, web_data_service),
+ backend_(backend), migration_checked_(!backend), allow_fallback_(false) {
+}
+
+PasswordStoreX::~PasswordStoreX() {
+}
+
+void PasswordStoreX::AddLoginImpl(const PasswordForm& form) {
+ CheckMigration();
+ if (use_native_backend() && backend_->AddLogin(form)) {
+ PasswordStoreChangeList changes;
+ changes.push_back(PasswordStoreChange(PasswordStoreChange::ADD, form));
+ NotificationService::current()->Notify(
+ NotificationType::LOGINS_CHANGED,
+ NotificationService::AllSources(),
+ Details<PasswordStoreChangeList>(&changes));
+ allow_fallback_ = false;
+ } else if (allow_default_store()) {
+ PasswordStoreDefault::AddLoginImpl(form);
+ }
+}
+
+void PasswordStoreX::UpdateLoginImpl(const PasswordForm& form) {
+ CheckMigration();
+ if (use_native_backend() && backend_->UpdateLogin(form)) {
+ PasswordStoreChangeList changes;
+ changes.push_back(PasswordStoreChange(PasswordStoreChange::UPDATE, form));
+ NotificationService::current()->Notify(
+ NotificationType::LOGINS_CHANGED,
+ NotificationService::AllSources(),
+ Details<PasswordStoreChangeList>(&changes));
+ allow_fallback_ = false;
+ } else if (allow_default_store()) {
+ PasswordStoreDefault::UpdateLoginImpl(form);
+ }
+}
+
+void PasswordStoreX::RemoveLoginImpl(const PasswordForm& form) {
+ CheckMigration();
+ if (use_native_backend() && backend_->RemoveLogin(form)) {
+ PasswordStoreChangeList changes;
+ changes.push_back(PasswordStoreChange(PasswordStoreChange::REMOVE, form));
+ NotificationService::current()->Notify(
+ NotificationType::LOGINS_CHANGED,
+ NotificationService::AllSources(),
+ Details<PasswordStoreChangeList>(&changes));
+ allow_fallback_ = false;
+ } else if (allow_default_store()) {
+ PasswordStoreDefault::RemoveLoginImpl(form);
+ }
+}
+
+void PasswordStoreX::RemoveLoginsCreatedBetweenImpl(
+ const base::Time& delete_begin,
+ const base::Time& delete_end) {
+ CheckMigration();
+ vector<PasswordForm*> forms;
+ if (use_native_backend() &&
+ backend_->GetLoginsCreatedBetween(delete_begin, delete_end, &forms) &&
+ backend_->RemoveLoginsCreatedBetween(delete_begin, delete_end)) {
+ PasswordStoreChangeList changes;
+ for (vector<PasswordForm*>::const_iterator it = forms.begin();
+ it != forms.end(); ++it) {
+ changes.push_back(PasswordStoreChange(PasswordStoreChange::REMOVE,
+ **it));
+ }
+ NotificationService::current()->Notify(
+ NotificationType::LOGINS_CHANGED,
+ NotificationService::AllSources(),
+ Details<PasswordStoreChangeList>(&changes));
+ allow_fallback_ = false;
+ } else if (allow_default_store()) {
+ PasswordStoreDefault::RemoveLoginsCreatedBetweenImpl(delete_begin,
+ delete_end);
+ }
+ STLDeleteElements(&forms);
+}
+
+void PasswordStoreX::GetLoginsImpl(GetLoginsRequest* request,
+ const PasswordForm& form) {
+ CheckMigration();
+ vector<PasswordForm*> forms;
+ if (use_native_backend() && backend_->GetLogins(form, &forms)) {
+ NotifyConsumer(request, forms);
+ allow_fallback_ = false;
+ } else if (allow_default_store()) {
+ PasswordStoreDefault::GetLoginsImpl(request, form);
+ } else {
+ // The consumer will be left hanging unless we reply.
+ NotifyConsumer(request, forms);
+ }
+}
+
+void PasswordStoreX::GetAutofillableLoginsImpl(GetLoginsRequest* request) {
+ CheckMigration();
+ vector<PasswordForm*> forms;
+ if (use_native_backend() && backend_->GetAutofillableLogins(&forms)) {
+ NotifyConsumer(request, forms);
+ allow_fallback_ = false;
+ } else if (allow_default_store()) {
+ PasswordStoreDefault::GetAutofillableLoginsImpl(request);
+ } else {
+ // The consumer will be left hanging unless we reply.
+ NotifyConsumer(request, forms);
+ }
+}
+
+void PasswordStoreX::GetBlacklistLoginsImpl(GetLoginsRequest* request) {
+ CheckMigration();
+ vector<PasswordForm*> forms;
+ if (use_native_backend() && backend_->GetBlacklistLogins(&forms)) {
+ NotifyConsumer(request, forms);
+ allow_fallback_ = false;
+ } else if (allow_default_store()) {
+ PasswordStoreDefault::GetBlacklistLoginsImpl(request);
+ } else {
+ // The consumer will be left hanging unless we reply.
+ NotifyConsumer(request, forms);
+ }
+}
+
+bool PasswordStoreX::FillAutofillableLogins(vector<PasswordForm*>* forms) {
+ CheckMigration();
+ if (use_native_backend() && backend_->GetAutofillableLogins(forms)) {
+ allow_fallback_ = false;
+ return true;
+ }
+ if (allow_default_store())
+ return PasswordStoreDefault::FillAutofillableLogins(forms);
+ return false;
+}
+
+bool PasswordStoreX::FillBlacklistLogins(vector<PasswordForm*>* forms) {
+ CheckMigration();
+ if (use_native_backend() && backend_->GetBlacklistLogins(forms)) {
+ allow_fallback_ = false;
+ return true;
+ }
+ if (allow_default_store())
+ return PasswordStoreDefault::FillBlacklistLogins(forms);
+ return false;
+}
+
+void PasswordStoreX::CheckMigration() {
+ DCHECK(ChromeThread::CurrentlyOn(ChromeThread::DB));
+ if (migration_checked_ || !backend_.get())
+ return;
+ migration_checked_ = true;
+ ssize_t migrated = MigrateLogins();
+ if (migrated > 0) {
+ LOG(INFO) << "Migrated " << migrated << " passwords to native store.";
+ } else if (migrated == 0) {
+ // As long as we are able to migrate some passwords, we know the native
+ // store is working. But if there is nothing to migrate, the "migration"
+ // can succeed even when the native store would fail. In this case we
+ // allow a later fallback to the default store. Once any later operation
+ // succeeds on the native store, we will no longer allow it.
+ allow_fallback_ = true;
+ } else {
+ LOG(WARNING) << "Native password store migration failed! " <<
+ "Falling back on default (unencrypted) store.";
+ backend_.reset(NULL);
+ }
+}
+
+bool PasswordStoreX::allow_default_store() {
+ if (allow_fallback_) {
+ LOG(WARNING) << "Native password store failed! " <<
+ "Falling back on default (unencrypted) store.";
+ backend_.reset(NULL);
+ // Don't warn again. We'll use the default store because backend_ is NULL.
+ allow_fallback_ = false;
+ }
+ return !backend_.get();
+}
+
+ssize_t PasswordStoreX::MigrateLogins() {
+ DCHECK(backend_.get());
+ vector<PasswordForm*> forms;
+ bool ok = PasswordStoreDefault::FillAutofillableLogins(&forms) &&
+ PasswordStoreDefault::FillBlacklistLogins(&forms);
+ if (ok) {
+ // We add all the passwords (and blacklist entries) to the native backend
+ // before attempting to remove any from the login database, to make sure we
+ // don't somehow end up with some of the passwords in one store and some in
+ // another. We'll always have at least one intact store this way.
+ for (size_t i = 0; i < forms.size(); ++i) {
+ if (!backend_->AddLogin(*forms[i])) {
+ ok = false;
+ break;
+ }
+ }
+ if (ok) {
+ for (size_t i = 0; i < forms.size(); ++i) {
+ // If even one of these calls to RemoveLoginImpl() succeeds, then we
+ // should prefer the native backend to the now-incomplete login
+ // database. Thus we want to return a success status even in the case
+ // where some fail. The only real problem with this is that we might
+ // leave passwords in the login database and never come back to clean
+ // them out if any of these calls do fail.
+ // TODO(mdm): Really we should just delete the login database file.
+ PasswordStoreDefault::RemoveLoginImpl(*forms[i]);
+ }
+ }
+ }
+ ssize_t result = ok ? forms.size() : -1;
+ STLDeleteElements(&forms);
+ return result;
+}
diff --git a/chrome/browser/password_manager/password_store_x.h b/chrome/browser/password_manager/password_store_x.h
new file mode 100644
index 0000000..fd3fe42
--- /dev/null
+++ b/chrome/browser/password_manager/password_store_x.h
@@ -0,0 +1,107 @@
+// Copyright (c) 2010 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef CHROME_BROWSER_PASSWORD_MANAGER_PASSWORD_STORE_X_H_
+#define CHROME_BROWSER_PASSWORD_MANAGER_PASSWORD_STORE_X_H_
+
+#include <vector>
+
+#include "base/scoped_ptr.h"
+#include "base/time.h"
+#include "chrome/browser/password_manager/password_store_default.h"
+
+class LoginDatabase;
+class Profile;
+class WebDataService;
+
+// PasswordStoreX is used on Linux and other non-Windows, non-Mac OS X
+// operating systems. It uses a "native backend" to actually store the password
+// data when such a backend is available, and otherwise falls back to using the
+// login database like PasswordStoreDefault. It also handles automatically
+// migrating password data to a native backend from the login database.
+//
+// There are currently native backends for GNOME Keyring and KWallet.
+class PasswordStoreX : public PasswordStoreDefault {
+ public:
+ // NativeBackends more or less implement the PaswordStore interface, but
+ // with return values rather than implicit consumer notification.
+ class NativeBackend {
+ public:
+ typedef std::vector<webkit_glue::PasswordForm*> PasswordFormList;
+
+ virtual ~NativeBackend() {}
+
+ virtual bool Init() = 0;
+
+ virtual bool AddLogin(const webkit_glue::PasswordForm& form) = 0;
+ virtual bool UpdateLogin(const webkit_glue::PasswordForm& form) = 0;
+ virtual bool RemoveLogin(const webkit_glue::PasswordForm& form) = 0;
+ virtual bool RemoveLoginsCreatedBetween(const base::Time& delete_begin,
+ const base::Time& delete_end) = 0;
+ virtual bool GetLogins(const webkit_glue::PasswordForm& form,
+ PasswordFormList* forms) = 0;
+ virtual bool GetLoginsCreatedBetween(const base::Time& get_begin,
+ const base::Time& get_end,
+ PasswordFormList* forms) = 0;
+ virtual bool GetAutofillableLogins(PasswordFormList* forms) = 0;
+ virtual bool GetBlacklistLogins(PasswordFormList* forms) = 0;
+ };
+
+ // Takes ownership of |login_db| and |backend|. |backend| may be NULL in which
+ // case this PasswordStoreX will act the same as PasswordStoreDefault.
+ PasswordStoreX(LoginDatabase* login_db,
+ Profile* profile,
+ WebDataService* web_data_service,
+ NativeBackend* backend);
+
+ private:
+ friend class PasswordStoreXTest;
+
+ virtual ~PasswordStoreX();
+
+ // Implements PasswordStore interface.
+ virtual void AddLoginImpl(const webkit_glue::PasswordForm& form);
+ virtual void UpdateLoginImpl(const webkit_glue::PasswordForm& form);
+ virtual void RemoveLoginImpl(const webkit_glue::PasswordForm& form);
+ virtual void RemoveLoginsCreatedBetweenImpl(const base::Time& delete_begin,
+ const base::Time& delete_end);
+ virtual void GetLoginsImpl(GetLoginsRequest* request,
+ const webkit_glue::PasswordForm& form);
+ virtual void GetAutofillableLoginsImpl(GetLoginsRequest* request);
+ virtual void GetBlacklistLoginsImpl(GetLoginsRequest* request);
+ virtual bool FillAutofillableLogins(
+ std::vector<webkit_glue::PasswordForm*>* forms);
+ virtual bool FillBlacklistLogins(
+ std::vector<webkit_glue::PasswordForm*>* forms);
+
+ // Check to see whether migration is necessary, and perform it if so.
+ void CheckMigration();
+
+ // Return true if we should try using the native backend.
+ bool use_native_backend() { return !!backend_.get(); }
+
+ // Return true if we can fall back on the default store, warning the first
+ // time we call it when falling back is necessary. See |allow_fallback_|.
+ bool allow_default_store();
+
+ // Synchronously migrates all the passwords stored in the login database to
+ // the native backend. If successful, the login database will be left with no
+ // stored passwords, and the number of passwords migrated will be returned.
+ // (This might be 0 if migration was not necessary.) Returns < 0 on failure.
+ ssize_t MigrateLogins();
+
+ // The native backend in use, or NULL if none.
+ scoped_ptr<NativeBackend> backend_;
+ // Whether we have already attempted migration to the native store.
+ bool migration_checked_;
+ // Whether we should allow falling back to the default store. If there is
+ // nothing to migrate, then the first attempt to use the native store will
+ // be the first time we try to use it and we should allow falling back. If
+ // we have migrated successfully, then we do not allow falling back.
+ bool allow_fallback_;
+
+ DISALLOW_COPY_AND_ASSIGN(PasswordStoreX);
+};
+
+#endif // CHROME_BROWSER_PASSWORD_MANAGER_PASSWORD_STORE_X_H_
diff --git a/chrome/browser/password_manager/password_store_x_unittest.cc b/chrome/browser/password_manager/password_store_x_unittest.cc
new file mode 100644
index 0000000..1b4708c
--- /dev/null
+++ b/chrome/browser/password_manager/password_store_x_unittest.cc
@@ -0,0 +1,771 @@
+// Copyright (c) 2010 The Chromium 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/basictypes.h"
+#include "base/scoped_temp_dir.h"
+#include "base/stl_util-inl.h"
+#include "base/string_util.h"
+#include "base/time.h"
+#include "base/waitable_event.h"
+#include "chrome/browser/password_manager/password_form_data.h"
+#include "chrome/browser/password_manager/password_store_change.h"
+#include "chrome/browser/password_manager/password_store_x.h"
+#include "chrome/browser/webdata/web_data_service.h"
+#include "chrome/common/notification_service.h"
+#include "chrome/common/pref_names.h"
+#include "chrome/test/testing_profile.h"
+#include "testing/gmock/include/gmock/gmock.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+using base::WaitableEvent;
+using testing::_;
+using testing::DoAll;
+using testing::ElementsAreArray;
+using testing::Pointee;
+using testing::Property;
+using testing::WithArg;
+using webkit_glue::PasswordForm;
+
+namespace {
+
+class MockPasswordStoreConsumer : public PasswordStoreConsumer {
+ public:
+ MOCK_METHOD2(OnPasswordStoreRequestDone,
+ void(int, const std::vector<PasswordForm*>&));
+};
+
+class MockWebDataServiceConsumer : public WebDataServiceConsumer {
+ public:
+ MOCK_METHOD2(OnWebDataServiceRequestDone, void(WebDataService::Handle,
+ const WDTypedResult*));
+};
+
+class SignalingTask : public Task {
+ public:
+ explicit SignalingTask(WaitableEvent* event) : event_(event) {
+ }
+ virtual void Run() {
+ event_->Signal();
+ }
+ private:
+ WaitableEvent* event_;
+};
+
+class MockNotificationObserver : public NotificationObserver {
+ public:
+ MOCK_METHOD3(Observe, void(NotificationType,
+ const NotificationSource&,
+ const NotificationDetails&));
+};
+
+// This class will add and remove a mock notification observer from
+// the DB thread.
+class DBThreadObserverHelper
+ : public base::RefCountedThreadSafe<DBThreadObserverHelper,
+ ChromeThread::DeleteOnDBThread> {
+ public:
+ DBThreadObserverHelper() : done_event_(true, false) {}
+
+ void Init() {
+ DCHECK(ChromeThread::CurrentlyOn(ChromeThread::UI));
+ ChromeThread::PostTask(
+ ChromeThread::DB,
+ FROM_HERE,
+ NewRunnableMethod(this, &DBThreadObserverHelper::AddObserverTask));
+ done_event_.Wait();
+ }
+
+ virtual ~DBThreadObserverHelper() {
+ DCHECK(ChromeThread::CurrentlyOn(ChromeThread::DB));
+ registrar_.RemoveAll();
+ }
+
+ MockNotificationObserver& observer() {
+ return observer_;
+ }
+
+ protected:
+ friend class base::RefCountedThreadSafe<DBThreadObserverHelper>;
+
+ void AddObserverTask() {
+ DCHECK(ChromeThread::CurrentlyOn(ChromeThread::DB));
+ registrar_.Add(&observer_,
+ NotificationType::LOGINS_CHANGED,
+ NotificationService::AllSources());
+ done_event_.Signal();
+ }
+
+ WaitableEvent done_event_;
+ NotificationRegistrar registrar_;
+ MockNotificationObserver observer_;
+};
+
+class FailingBackend : public PasswordStoreX::NativeBackend {
+ public:
+ virtual bool Init() { return true; }
+
+ virtual bool AddLogin(const PasswordForm& form) { return false; }
+ virtual bool UpdateLogin(const PasswordForm& form) { return false; }
+ virtual bool RemoveLogin(const PasswordForm& form) { return false; }
+
+ virtual bool RemoveLoginsCreatedBetween(const base::Time& delete_begin,
+ const base::Time& delete_end) {
+ return false;
+ }
+
+ virtual bool GetLogins(const PasswordForm& form, PasswordFormList* forms) {
+ return false;
+ }
+
+ virtual bool GetLoginsCreatedBetween(const base::Time& get_begin,
+ const base::Time& get_end,
+ PasswordFormList* forms) {
+ return false;
+ }
+
+ virtual bool GetAutofillableLogins(PasswordFormList* forms) { return false; }
+ virtual bool GetBlacklistLogins(PasswordFormList* forms) { return false; }
+};
+
+class MockBackend : public PasswordStoreX::NativeBackend {
+ public:
+ virtual bool Init() { return true; }
+
+ virtual bool AddLogin(const PasswordForm& form) {
+ all_forms_.push_back(form);
+ return true;
+ }
+
+ virtual bool UpdateLogin(const PasswordForm& form) {
+ for (size_t i = 0; i < all_forms_.size(); ++i)
+ if (CompareForms(all_forms_[i], form, true))
+ all_forms_[i] = form;
+ return true;
+ }
+
+ virtual bool RemoveLogin(const PasswordForm& form) {
+ for (size_t i = 0; i < all_forms_.size(); ++i)
+ if (CompareForms(all_forms_[i], form, false))
+ erase(i--);
+ return true;
+ }
+
+ virtual bool RemoveLoginsCreatedBetween(const base::Time& delete_begin,
+ const base::Time& delete_end) {
+ for (size_t i = 0; i < all_forms_.size(); ++i) {
+ if (delete_begin <= all_forms_[i].date_created &&
+ (delete_end.is_null() || all_forms_[i].date_created < delete_end))
+ erase(i--);
+ }
+ return true;
+ }
+
+ virtual bool GetLogins(const PasswordForm& form, PasswordFormList* forms) {
+ for (size_t i = 0; i < all_forms_.size(); ++i)
+ if (all_forms_[i].signon_realm == form.signon_realm)
+ forms->push_back(new PasswordForm(all_forms_[i]));
+ return true;
+ }
+
+ virtual bool GetLoginsCreatedBetween(const base::Time& get_begin,
+ const base::Time& get_end,
+ PasswordFormList* forms) {
+ for (size_t i = 0; i < all_forms_.size(); ++i)
+ if (get_begin <= all_forms_[i].date_created &&
+ (get_end.is_null() || all_forms_[i].date_created < get_end))
+ forms->push_back(new PasswordForm(all_forms_[i]));
+ return true;
+ }
+
+ virtual bool GetAutofillableLogins(PasswordFormList* forms) {
+ for (size_t i = 0; i < all_forms_.size(); ++i)
+ if (!all_forms_[i].blacklisted_by_user)
+ forms->push_back(new PasswordForm(all_forms_[i]));
+ return true;
+ }
+
+ virtual bool GetBlacklistLogins(PasswordFormList* forms) {
+ for (size_t i = 0; i < all_forms_.size(); ++i)
+ if (all_forms_[i].blacklisted_by_user)
+ forms->push_back(new PasswordForm(all_forms_[i]));
+ return true;
+ }
+
+ private:
+ void erase(size_t index) {
+ if (index < all_forms_.size() - 1)
+ all_forms_[index] = all_forms_[all_forms_.size() - 1];
+ all_forms_.pop_back();
+ }
+
+ bool CompareForms(const PasswordForm& a, const PasswordForm& b, bool update) {
+ // An update check doesn't care about the submit element.
+ if (!update && a.submit_element != b.submit_element)
+ return false;
+ return a.origin == b.origin &&
+ a.password_element == b.password_element &&
+ a.signon_realm == b.signon_realm &&
+ a.username_element == b.username_element &&
+ a.username_value == b.username_value;
+ }
+
+ std::vector<PasswordForm> all_forms_;
+};
+
+class MockLoginDatabaseReturn {
+ public:
+ MOCK_METHOD1(OnLoginDatabaseQueryDone,
+ void(const std::vector<PasswordForm*>&));
+};
+
+class LoginDatabaseQueryTask : public Task {
+ public:
+ LoginDatabaseQueryTask(LoginDatabase* login_db,
+ bool autofillable,
+ MockLoginDatabaseReturn* mock_return)
+ : login_db_(login_db), autofillable_(autofillable),
+ mock_return_(mock_return) {
+ }
+
+ virtual void Run() {
+ std::vector<PasswordForm*> forms;
+ if (autofillable_)
+ login_db_->GetAutofillableLogins(&forms);
+ else
+ login_db_->GetBlacklistLogins(&forms);
+ mock_return_->OnLoginDatabaseQueryDone(forms);
+ }
+
+ private:
+ LoginDatabase* login_db_;
+ bool autofillable_;
+ MockLoginDatabaseReturn* mock_return_;
+};
+
+const PasswordFormData g_autofillable_data[] = {
+ { PasswordForm::SCHEME_HTML,
+ "http://foo.example.com",
+ "http://foo.example.com/origin",
+ "http://foo.example.com/action",
+ L"submit_element",
+ L"username_element",
+ L"password_element",
+ L"username_value",
+ L"password_value",
+ true, false, 1 },
+ { PasswordForm::SCHEME_HTML,
+ "http://bar.example.com",
+ "http://bar.example.com/origin",
+ "http://bar.example.com/action",
+ L"submit_element",
+ L"username_element",
+ L"password_element",
+ L"username_value",
+ L"password_value",
+ true, false, 2 },
+ { PasswordForm::SCHEME_HTML,
+ "http://baz.example.com",
+ "http://baz.example.com/origin",
+ "http://baz.example.com/action",
+ L"submit_element",
+ L"username_element",
+ L"password_element",
+ L"username_value",
+ L"password_value",
+ true, false, 3 },
+};
+const PasswordFormData g_blacklisted_data[] = {
+ { PasswordForm::SCHEME_HTML,
+ "http://blacklisted.example.com",
+ "http://blacklisted.example.com/origin",
+ "http://blacklisted.example.com/action",
+ L"submit_element",
+ L"username_element",
+ L"password_element",
+ NULL,
+ NULL,
+ false, false, 1 },
+ { PasswordForm::SCHEME_HTML,
+ "http://blacklisted2.example.com",
+ "http://blacklisted2.example.com/origin",
+ "http://blacklisted2.example.com/action",
+ L"submit_element",
+ L"username_element",
+ L"password_element",
+ NULL,
+ NULL,
+ false, false, 2 },
+};
+
+} // anonymous namespace
+
+typedef std::vector<PasswordForm*> VectorOfForms;
+
+// LoginDatabase isn't reference counted, but in these unit tests that won't be
+// a problem as it always outlives the threads we post tasks to.
+template<>
+struct RunnableMethodTraits<LoginDatabase> {
+ void RetainCallee(LoginDatabase*) {}
+ void ReleaseCallee(LoginDatabase*) {}
+};
+
+enum BackendType {
+ NO_BACKEND,
+ FAILING_BACKEND,
+ WORKING_BACKEND
+};
+
+class PasswordStoreXTest : public testing::TestWithParam<BackendType> {
+ protected:
+ PasswordStoreXTest()
+ : ui_thread_(ChromeThread::UI, &message_loop_),
+ db_thread_(ChromeThread::DB) {
+ }
+
+ virtual void SetUp() {
+ ASSERT_TRUE(db_thread_.Start());
+ ASSERT_TRUE(temp_dir_.CreateUniqueTempDir());
+
+ profile_.reset(new TestingProfile());
+
+ login_db_.reset(new LoginDatabase());
+ ASSERT_TRUE(login_db_->Init(temp_dir_.path().Append(
+ FILE_PATH_LITERAL("login_test"))));
+
+ wds_ = new WebDataService();
+ ASSERT_TRUE(wds_->Init(temp_dir_.path()));
+ }
+
+ virtual void TearDown() {
+ wds_->Shutdown();
+ MessageLoop::current()->PostTask(FROM_HERE, new MessageLoop::QuitTask);
+ MessageLoop::current()->Run();
+ db_thread_.Stop();
+ }
+
+ PasswordStoreX::NativeBackend* GetBackend() {
+ switch (GetParam()) {
+ case FAILING_BACKEND:
+ return new FailingBackend();
+ case WORKING_BACKEND:
+ return new MockBackend();
+ default:
+ return NULL;
+ }
+ }
+
+ MessageLoopForUI message_loop_;
+ ChromeThread ui_thread_;
+ ChromeThread db_thread_; // PasswordStore, WDS schedule work on this thread.
+
+ scoped_ptr<LoginDatabase> login_db_;
+ scoped_ptr<TestingProfile> profile_;
+ scoped_refptr<WebDataService> wds_;
+ ScopedTempDir temp_dir_;
+};
+
+ACTION(STLDeleteElements0) {
+ STLDeleteContainerPointers(arg0.begin(), arg0.end());
+}
+
+ACTION(QuitUIMessageLoop) {
+ DCHECK(ChromeThread::CurrentlyOn(ChromeThread::UI));
+ MessageLoop::current()->Quit();
+}
+
+MATCHER(EmptyWDResult, "") {
+ return static_cast<const WDResult<std::vector<PasswordForm*> >*>(
+ arg)->GetValue().empty();
+}
+
+TEST_P(PasswordStoreXTest, WDSMigration) {
+ VectorOfForms expected_autofillable;
+ for (unsigned int i = 0; i < ARRAYSIZE_UNSAFE(g_autofillable_data); ++i) {
+ expected_autofillable.push_back(
+ CreatePasswordFormFromData(g_autofillable_data[i]));
+ }
+
+ VectorOfForms expected_blacklisted;
+ for (unsigned int i = 0; i < ARRAYSIZE_UNSAFE(g_blacklisted_data); ++i) {
+ expected_blacklisted.push_back(
+ CreatePasswordFormFromData(g_blacklisted_data[i]));
+ }
+
+ // Populate the WDS with logins that should be migrated.
+ for (VectorOfForms::iterator it = expected_autofillable.begin();
+ it != expected_autofillable.end(); ++it) {
+ wds_->AddLogin(**it);
+ }
+ for (VectorOfForms::iterator it = expected_blacklisted.begin();
+ it != expected_blacklisted.end(); ++it) {
+ wds_->AddLogin(**it);
+ }
+
+ // The WDS schedules tasks to run on the DB thread so we schedule yet another
+ // task to notify us that it's safe to carry on with the test.
+ WaitableEvent done(false, false);
+ ChromeThread::PostTask(ChromeThread::DB, FROM_HERE, new SignalingTask(&done));
+ done.Wait();
+
+ // Initializing the PasswordStore should trigger a migration.
+ scoped_refptr<PasswordStoreX> store(
+ new PasswordStoreX(login_db_.release(),
+ profile_.get(),
+ wds_.get(),
+ GetBackend()));
+ store->Init();
+
+ // Check that the migration preference has not been initialized.
+ ASSERT_TRUE(NULL == profile_->GetPrefs()->FindPreference(
+ prefs::kLoginDatabaseMigrated));
+
+ // Again, the WDS schedules tasks to run on the DB thread, so schedule a task
+ // to signal us when it is safe to continue.
+ ChromeThread::PostTask(ChromeThread::DB, FROM_HERE, new SignalingTask(&done));
+ done.Wait();
+
+ // Let the WDS callbacks proceed so the logins can be migrated.
+ MessageLoop::current()->RunAllPending();
+
+ MockPasswordStoreConsumer consumer;
+
+ // Make sure we quit the MessageLoop even if the test fails.
+ ON_CALL(consumer, OnPasswordStoreRequestDone(_, _))
+ .WillByDefault(QuitUIMessageLoop());
+
+ // The autofillable forms should have been migrated from the WDS to the login
+ // database.
+ EXPECT_CALL(consumer,
+ OnPasswordStoreRequestDone(_,
+ ContainsAllPasswordForms(expected_autofillable)))
+ .WillOnce(DoAll(WithArg<1>(STLDeleteElements0()), QuitUIMessageLoop()));
+
+ store->GetAutofillableLogins(&consumer);
+ MessageLoop::current()->Run();
+
+ // The blacklisted forms should have been migrated from the WDS to the login
+ // database.
+ EXPECT_CALL(consumer,
+ OnPasswordStoreRequestDone(_,
+ ContainsAllPasswordForms(expected_blacklisted)))
+ .WillOnce(DoAll(WithArg<1>(STLDeleteElements0()), QuitUIMessageLoop()));
+
+ store->GetBlacklistLogins(&consumer);
+ MessageLoop::current()->Run();
+
+ // Check that the migration updated the migrated preference.
+ ASSERT_TRUE(profile_->GetPrefs()->GetBoolean(prefs::kLoginDatabaseMigrated));
+
+ MockWebDataServiceConsumer wds_consumer;
+
+ // No autofillable logins should be left in the WDS.
+ EXPECT_CALL(wds_consumer,
+ OnWebDataServiceRequestDone(_, EmptyWDResult()));
+
+ wds_->GetAutofillableLogins(&wds_consumer);
+
+ // Wait for the WDS methods to execute on the DB thread.
+ ChromeThread::PostTask(ChromeThread::DB, FROM_HERE, new SignalingTask(&done));
+ done.Wait();
+
+ // Handle the callback from the WDS.
+ MessageLoop::current()->RunAllPending();
+
+ // Likewise, no blacklisted logins should be left in the WDS.
+ EXPECT_CALL(wds_consumer,
+ OnWebDataServiceRequestDone(_, EmptyWDResult()));
+
+ wds_->GetBlacklistLogins(&wds_consumer);
+
+ // Wait for the WDS methods to execute on the DB thread.
+ ChromeThread::PostTask(ChromeThread::DB, FROM_HERE, new SignalingTask(&done));
+ done.Wait();
+
+ // Handle the callback from the WDS.
+ MessageLoop::current()->RunAllPending();
+
+ STLDeleteElements(&expected_autofillable);
+ STLDeleteElements(&expected_blacklisted);
+}
+
+TEST_P(PasswordStoreXTest, WDSMigrationAlreadyDone) {
+ PasswordFormData wds_data[] = {
+ { PasswordForm::SCHEME_HTML,
+ "http://bar.example.com",
+ "http://bar.example.com/origin",
+ "http://bar.example.com/action",
+ L"submit_element",
+ L"username_element",
+ L"password_element",
+ L"username_value",
+ L"password_value",
+ true, false, 1 },
+ };
+
+ VectorOfForms unexpected_autofillable;
+ for (unsigned int i = 0; i < ARRAYSIZE_UNSAFE(wds_data); ++i) {
+ unexpected_autofillable.push_back(
+ CreatePasswordFormFromData(wds_data[i]));
+ }
+
+ // Populate the WDS with logins that should be migrated.
+ for (VectorOfForms::iterator it = unexpected_autofillable.begin();
+ it != unexpected_autofillable.end(); ++it) {
+ wds_->AddLogin(**it);
+ }
+
+ // The WDS schedules tasks to run on the DB thread so we schedule yet another
+ // task to notify us that it's safe to carry on with the test.
+ WaitableEvent done(false, false);
+ ChromeThread::PostTask(ChromeThread::DB, FROM_HERE, new SignalingTask(&done));
+ done.Wait();
+
+ // Prentend that the migration has already taken place.
+ profile_->GetPrefs()->RegisterBooleanPref(prefs::kLoginDatabaseMigrated,
+ true);
+
+ // Initializing the PasswordStore shouldn't trigger a migration.
+ scoped_refptr<PasswordStoreX> store(
+ new PasswordStoreX(login_db_.release(),
+ profile_.get(),
+ wds_.get(),
+ GetBackend()));
+ store->Init();
+
+ MockPasswordStoreConsumer consumer;
+ // Make sure we quit the MessageLoop even if the test fails.
+ ON_CALL(consumer, OnPasswordStoreRequestDone(_, _))
+ .WillByDefault(QuitUIMessageLoop());
+
+ // No forms should be migrated.
+ VectorOfForms empty;
+ EXPECT_CALL(consumer,
+ OnPasswordStoreRequestDone(_,
+ ContainsAllPasswordForms(empty)))
+ .WillOnce(QuitUIMessageLoop());
+
+ store->GetAutofillableLogins(&consumer);
+ MessageLoop::current()->Run();
+
+ STLDeleteElements(&unexpected_autofillable);
+}
+
+TEST_P(PasswordStoreXTest, Notifications) {
+ // Pretend that the migration has already taken place.
+ profile_->GetPrefs()->RegisterBooleanPref(prefs::kLoginDatabaseMigrated,
+ true);
+
+ // Initializing the PasswordStore shouldn't trigger a migration.
+ scoped_refptr<PasswordStoreX> store(
+ new PasswordStoreX(login_db_.release(),
+ profile_.get(),
+ wds_.get(),
+ GetBackend()));
+ store->Init();
+
+ PasswordFormData form_data =
+ { PasswordForm::SCHEME_HTML,
+ "http://bar.example.com",
+ "http://bar.example.com/origin",
+ "http://bar.example.com/action",
+ L"submit_element",
+ L"username_element",
+ L"password_element",
+ L"username_value",
+ L"password_value",
+ true, false, 1 };
+ scoped_ptr<PasswordForm> form(CreatePasswordFormFromData(form_data));
+
+ scoped_refptr<DBThreadObserverHelper> helper = new DBThreadObserverHelper;
+ helper->Init();
+
+ const PasswordStoreChange expected_add_changes[] = {
+ PasswordStoreChange(PasswordStoreChange::ADD, *form),
+ };
+
+ EXPECT_CALL(helper->observer(),
+ Observe(NotificationType(NotificationType::LOGINS_CHANGED),
+ NotificationService::AllSources(),
+ Property(&Details<const PasswordStoreChangeList>::ptr,
+ Pointee(ElementsAreArray(
+ expected_add_changes)))));
+
+ // Adding a login should trigger a notification.
+ store->AddLogin(*form);
+
+ // The PasswordStore schedules tasks to run on the DB thread so we schedule
+ // yet another task to notify us that it's safe to carry on with the test.
+ WaitableEvent done(false, false);
+ ChromeThread::PostTask(ChromeThread::DB, FROM_HERE, new SignalingTask(&done));
+ done.Wait();
+
+ // Change the password.
+ form->password_value = WideToUTF16(L"a different password");
+
+ const PasswordStoreChange expected_update_changes[] = {
+ PasswordStoreChange(PasswordStoreChange::UPDATE, *form),
+ };
+
+ EXPECT_CALL(helper->observer(),
+ Observe(NotificationType(NotificationType::LOGINS_CHANGED),
+ NotificationService::AllSources(),
+ Property(&Details<const PasswordStoreChangeList>::ptr,
+ Pointee(ElementsAreArray(
+ expected_update_changes)))));
+
+ // Updating the login with the new password should trigger a notification.
+ store->UpdateLogin(*form);
+
+ // Wait for PasswordStore to send the notification.
+ ChromeThread::PostTask(ChromeThread::DB, FROM_HERE, new SignalingTask(&done));
+ done.Wait();
+
+ const PasswordStoreChange expected_delete_changes[] = {
+ PasswordStoreChange(PasswordStoreChange::REMOVE, *form),
+ };
+
+ EXPECT_CALL(helper->observer(),
+ Observe(NotificationType(NotificationType::LOGINS_CHANGED),
+ NotificationService::AllSources(),
+ Property(&Details<const PasswordStoreChangeList>::ptr,
+ Pointee(ElementsAreArray(
+ expected_delete_changes)))));
+
+ // Deleting the login should trigger a notification.
+ store->RemoveLogin(*form);
+
+ // Wait for PasswordStore to send the notification.
+ ChromeThread::PostTask(ChromeThread::DB, FROM_HERE, new SignalingTask(&done));
+ done.Wait();
+}
+
+TEST_P(PasswordStoreXTest, NativeMigration) {
+ VectorOfForms expected_autofillable;
+ for (unsigned int i = 0; i < ARRAYSIZE_UNSAFE(g_autofillable_data); ++i) {
+ expected_autofillable.push_back(
+ CreatePasswordFormFromData(g_autofillable_data[i]));
+ }
+
+ VectorOfForms expected_blacklisted;
+ for (unsigned int i = 0; i < ARRAYSIZE_UNSAFE(g_blacklisted_data); ++i) {
+ expected_blacklisted.push_back(
+ CreatePasswordFormFromData(g_blacklisted_data[i]));
+ }
+
+ LoginDatabase* login_db = login_db_.get();
+
+ // Populate the login DB with logins that should be migrated.
+ for (VectorOfForms::iterator it = expected_autofillable.begin();
+ it != expected_autofillable.end(); ++it) {
+ ChromeThread::PostTask(ChromeThread::DB,
+ FROM_HERE,
+ NewRunnableMethod(login_db,
+ &LoginDatabase::AddLogin,
+ **it));
+ }
+ for (VectorOfForms::iterator it = expected_blacklisted.begin();
+ it != expected_blacklisted.end(); ++it) {
+ ChromeThread::PostTask(ChromeThread::DB,
+ FROM_HERE,
+ NewRunnableMethod(login_db,
+ &LoginDatabase::AddLogin,
+ **it));
+ }
+
+ // Schedule another task on the DB thread to notify us that it's safe to
+ // carry on with the test.
+ WaitableEvent done(false, false);
+ ChromeThread::PostTask(ChromeThread::DB, FROM_HERE, new SignalingTask(&done));
+ done.Wait();
+
+ // Pretend that the WDS migration has already taken place.
+ profile_->GetPrefs()->RegisterBooleanPref(prefs::kLoginDatabaseMigrated,
+ true);
+
+ // Initializing the PasswordStore shouldn't trigger a native migration (yet).
+ scoped_refptr<PasswordStoreX> store(
+ new PasswordStoreX(login_db_.release(),
+ profile_.get(),
+ wds_.get(),
+ GetBackend()));
+ store->Init();
+
+ MockPasswordStoreConsumer consumer;
+
+ // Make sure we quit the MessageLoop even if the test fails.
+ ON_CALL(consumer, OnPasswordStoreRequestDone(_, _))
+ .WillByDefault(QuitUIMessageLoop());
+
+ // The autofillable forms should have been migrated to the native backend.
+ EXPECT_CALL(consumer,
+ OnPasswordStoreRequestDone(_,
+ ContainsAllPasswordForms(expected_autofillable)))
+ .WillOnce(DoAll(WithArg<1>(STLDeleteElements0()), QuitUIMessageLoop()));
+
+ store->GetAutofillableLogins(&consumer);
+ MessageLoop::current()->Run();
+
+ // The blacklisted forms should have been migrated to the native backend.
+ EXPECT_CALL(consumer,
+ OnPasswordStoreRequestDone(_,
+ ContainsAllPasswordForms(expected_blacklisted)))
+ .WillOnce(DoAll(WithArg<1>(STLDeleteElements0()), QuitUIMessageLoop()));
+
+ store->GetBlacklistLogins(&consumer);
+ MessageLoop::current()->Run();
+
+ VectorOfForms empty;
+ MockLoginDatabaseReturn ld_return;
+
+ if (GetParam() == WORKING_BACKEND) {
+ // No autofillable logins should be left in the login DB.
+ EXPECT_CALL(ld_return,
+ OnLoginDatabaseQueryDone(ContainsAllPasswordForms(empty)));
+ } else {
+ // The autofillable logins should still be in the login DB.
+ EXPECT_CALL(ld_return,
+ OnLoginDatabaseQueryDone(
+ ContainsAllPasswordForms(expected_autofillable)))
+ .WillOnce(WithArg<0>(STLDeleteElements0()));
+ }
+
+ ChromeThread::PostTask(ChromeThread::DB, FROM_HERE,
+ new LoginDatabaseQueryTask(login_db, true, &ld_return));
+
+ // Wait for the login DB methods to execute on the DB thread.
+ ChromeThread::PostTask(ChromeThread::DB, FROM_HERE, new SignalingTask(&done));
+ done.Wait();
+
+ if (GetParam() == WORKING_BACKEND) {
+ // Likewise, no blacklisted logins should be left in the login DB.
+ EXPECT_CALL(ld_return,
+ OnLoginDatabaseQueryDone(ContainsAllPasswordForms(empty)));
+ } else {
+ // The blacklisted logins should still be in the login DB.
+ EXPECT_CALL(ld_return,
+ OnLoginDatabaseQueryDone(
+ ContainsAllPasswordForms(expected_blacklisted)))
+ .WillOnce(WithArg<0>(STLDeleteElements0()));
+ }
+
+ ChromeThread::PostTask(ChromeThread::DB, FROM_HERE,
+ new LoginDatabaseQueryTask(login_db, false, &ld_return));
+
+ // Wait for the login DB methods to execute on the DB thread.
+ ChromeThread::PostTask(ChromeThread::DB, FROM_HERE, new SignalingTask(&done));
+ done.Wait();
+
+ STLDeleteElements(&expected_autofillable);
+ STLDeleteElements(&expected_blacklisted);
+}
+
+INSTANTIATE_TEST_CASE_P(NoBackend,
+ PasswordStoreXTest,
+ testing::Values(NO_BACKEND));
+INSTANTIATE_TEST_CASE_P(FailingBackend,
+ PasswordStoreXTest,
+ testing::Values(FAILING_BACKEND));
+INSTANTIATE_TEST_CASE_P(WorkingBackend,
+ PasswordStoreXTest,
+ testing::Values(WORKING_BACKEND));
diff --git a/chrome/chrome_browser.gypi b/chrome/chrome_browser.gypi
index f297108..0e0ad22 100755
--- a/chrome/chrome_browser.gypi
+++ b/chrome/chrome_browser.gypi
@@ -1805,17 +1805,21 @@
'browser/parsers/metadata_parser_manager.cc',
'browser/parsers/metadata_parser_manager.h',
'browser/parsers/metadata_parser.cc',
+ 'browser/password_manager/encryptor.h',
'browser/password_manager/encryptor_linux.cc',
'browser/password_manager/encryptor_mac.mm',
'browser/password_manager/encryptor_win.cc',
- 'browser/password_manager/encryptor.h',
'browser/password_manager/ie7_password.cc',
'browser/password_manager/ie7_password.h',
+ 'browser/password_manager/login_database.cc',
+ 'browser/password_manager/login_database.h',
'browser/password_manager/login_database_mac.cc',
'browser/password_manager/login_database_posix.cc',
'browser/password_manager/login_database_win.cc',
- 'browser/password_manager/login_database.cc',
- 'browser/password_manager/login_database.h',
+ 'browser/password_manager/native_backend_gnome_x.cc',
+ 'browser/password_manager/native_backend_gnome_x.h',
+ 'browser/password_manager/native_backend_kwallet_x.cc',
+ 'browser/password_manager/native_backend_kwallet_x.h',
'browser/password_manager/password_form_manager.cc',
'browser/password_manager/password_form_manager.h',
'browser/password_manager/password_manager.cc',
@@ -1824,15 +1828,13 @@
'browser/password_manager/password_store.h',
'browser/password_manager/password_store_default.cc',
'browser/password_manager/password_store_default.h',
- 'browser/password_manager/password_store_gnome.h',
- 'browser/password_manager/password_store_gnome.cc',
- 'browser/password_manager/password_store_kwallet.h',
- 'browser/password_manager/password_store_kwallet.cc',
- 'browser/password_manager/password_store_mac_internal.h',
- 'browser/password_manager/password_store_mac.h',
'browser/password_manager/password_store_mac.cc',
- 'browser/password_manager/password_store_win.h',
+ 'browser/password_manager/password_store_mac.h',
+ 'browser/password_manager/password_store_mac_internal.h',
'browser/password_manager/password_store_win.cc',
+ 'browser/password_manager/password_store_win.h',
+ 'browser/password_manager/password_store_x.cc',
+ 'browser/password_manager/password_store_x.h',
'browser/platform_util.h',
'browser/platform_util_linux.cc',
'browser/platform_util_chromeos.cc',
@@ -2686,10 +2688,10 @@
}],
['chromeos==1', {
'sources!': [
- 'browser/password_manager/password_store_gnome.h',
- 'browser/password_manager/password_store_gnome.cc',
- 'browser/password_manager/password_store_kwallet.h',
- 'browser/password_manager/password_store_kwallet.cc',
+ 'browser/password_manager/native_backend_gnome.h',
+ 'browser/password_manager/native_backend_gnome.cc',
+ 'browser/password_manager/native_backend_kwallet.h',
+ 'browser/password_manager/native_backend_kwallet.cc',
'browser/platform_util_linux.cc',
],
'link_settings': {
@@ -2782,12 +2784,6 @@
'browser/importer/nss_decryptor_system_nss.cc',
'browser/importer/nss_decryptor_system_nss.h',
'browser/jankometer.cc',
- 'browser/password_manager/password_store_gnome.h',
- 'browser/password_manager/password_store_gnome.cc',
- 'browser/password_manager/password_store_kwallet.h',
- 'browser/password_manager/password_store_kwallet.cc',
- 'browser/password_manager/password_store_win.cc',
- 'browser/password_manager/password_store_win.h',
'browser/renderer_host/backing_store_proxy.cc',
'browser/renderer_host/backing_store_proxy.h',
'browser/renderer_host/gpu_view_host.cc',
@@ -2960,10 +2956,6 @@
'browser/history/history_publisher_none.cc',
'browser/importer/nss_decryptor_system_nss.cc',
'browser/importer/nss_decryptor_system_nss.h',
- 'browser/password_manager/password_store_gnome.h',
- 'browser/password_manager/password_store_gnome.cc',
- 'browser/password_manager/password_store_kwallet.h',
- 'browser/password_manager/password_store_kwallet.cc',
'browser/power_save_blocker_stub.cc',
'browser/views/select_file_dialog.cc',
],
diff --git a/chrome/chrome_tests.gypi b/chrome/chrome_tests.gypi
index 282b234..c9a60d9 100755
--- a/chrome/chrome_tests.gypi
+++ b/chrome/chrome_tests.gypi
@@ -873,6 +873,7 @@
'browser/password_manager/password_store_default_unittest.cc',
'browser/password_manager/password_store_mac_unittest.cc',
'browser/password_manager/password_store_win_unittest.cc',
+ 'browser/password_manager/password_store_x_unittest.cc',
'browser/pref_member_unittest.cc',
'browser/pref_service_unittest.cc',
'browser/preferences_mock_mac.cc',