Index: chrome/browser/password_manager/password_store_kwallet.cc |
=================================================================== |
--- chrome/browser/password_manager/password_store_kwallet.cc (revision 0) |
+++ chrome/browser/password_manager/password_store_kwallet.cc (revision 0) |
@@ -0,0 +1,387 @@ |
+// Copyright (c) 2006-2009 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_kwallet.h" |
+ |
+#include <sstream> |
+ |
+#include "base/logging.h" |
+#include "base/md5.h" |
+#include "base/pickle.h" |
+#include "base/string_util.h" |
+#include "base/task.h" |
+ |
+using std::string; |
+using std::vector; |
+ |
+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() |
+ : error_(NULL), |
+ connection_(NULL), |
+ proxy_(NULL) { |
+} |
+ |
+PasswordStoreKWallet::~PasswordStoreKWallet() { |
+ if (proxy_) { |
+ g_object_unref(proxy_); |
+ } |
+} |
+ |
+bool PasswordStoreKWallet::Init() { |
+ thread_.reset(new base::Thread("Chrome_KeyringThread")); |
+ |
+ if (!thread_->Start()) { |
+ thread_.reset(NULL); |
+ return false; |
+ } |
+ |
+ // Initialize threading in dbus-glib - it should be fine for |
+ // dbus_g_thread_init to be called multiple times. |
+ if (!g_thread_supported()) |
+ g_thread_init(NULL); |
+ dbus_g_thread_init(); |
+ |
+ // Get a connection to the session bus. |
+ connection_ = dbus_g_bus_get(DBUS_BUS_SESSION, &error_); |
+ if (CheckError()) |
+ return false; |
+ |
+ if (!StartKWalletd()) return false; |
+ if (!InitWallet()) return false; |
+ |
+ return true; |
+} |
+ |
+bool PasswordStoreKWallet::StartKWalletd() { |
+ // Sadly kwalletd doesn't use DBUS activation, so we have to make a call to |
+ // klauncher to start it. |
+ DBusGProxy* klauncher_proxy = |
+ dbus_g_proxy_new_for_name(connection_, kKLauncherServiceName, |
+ kKLauncherPath, kKLauncherInterface); |
+ |
+ char* empty_string_list = NULL; |
+ int ret = 1; |
+ char* error = NULL; |
+ dbus_g_proxy_call(klauncher_proxy, "start_service_by_desktop_name", &error_, |
+ G_TYPE_STRING, "kwalletd", // serviceName |
+ G_TYPE_STRV, &empty_string_list, // urls |
+ G_TYPE_STRV, &empty_string_list, // envs |
+ G_TYPE_STRING, "", // startup_id |
+ G_TYPE_BOOLEAN, (gboolean) false, // blind |
+ G_TYPE_INVALID, |
+ G_TYPE_INT, &ret, // result |
+ G_TYPE_STRING, NULL, // dubsName |
+ G_TYPE_STRING, &error, // error |
+ G_TYPE_INT, NULL, // pid |
+ G_TYPE_INVALID); |
+ |
+ if (error && *error) { |
+ LOG(ERROR) << "Error launching kwalletd: " << error; |
+ ret = 1; // Make sure we return false after freeing. |
+ } |
+ |
+ g_free(error); |
+ g_object_unref(klauncher_proxy); |
+ |
+ if (CheckError() || ret != 0) |
+ return false; |
+ return true; |
+} |
+ |
+bool PasswordStoreKWallet::InitWallet() { |
+ // Make a proxy to KWallet. |
+ proxy_ = dbus_g_proxy_new_for_name(connection_, kKWalletServiceName, |
+ kKWalletPath, kKWalletInterface); |
+ |
+ // Check KWallet is enabled. |
+ gboolean is_enabled = false; |
+ dbus_g_proxy_call(proxy_, "isEnabled", &error_, |
+ G_TYPE_INVALID, |
+ G_TYPE_BOOLEAN, &is_enabled, |
+ G_TYPE_INVALID); |
+ if (CheckError() || !is_enabled) |
+ return false; |
+ |
+ // Get the wallet name. |
+ char* wallet_name = NULL; |
+ dbus_g_proxy_call(proxy_, "networkWallet", &error_, |
+ G_TYPE_INVALID, |
+ G_TYPE_STRING, &wallet_name, |
+ G_TYPE_INVALID); |
+ if (CheckError() || !wallet_name) |
+ return false; |
+ |
+ wallet_name_.assign(wallet_name); |
+ g_free(wallet_name); |
+ |
+ return true; |
+} |
+ |
+void PasswordStoreKWallet::AddLoginImpl(const PasswordForm& form) { |
+ AutoLock l(kwallet_lock_); |
+ int wallet_handle = WalletHandle(); |
+ if (wallet_handle == kInvalidKWalletHandle) |
+ return; |
+ |
+ PasswordFormList forms; |
+ GetLoginsList(&forms, form, wallet_handle); |
+ |
+ forms.push_back(const_cast<PasswordForm*>(&form)); |
+ |
+ SetLoginsList(forms, form, wallet_handle); |
+} |
+ |
+void PasswordStoreKWallet::UpdateLoginImpl(const PasswordForm& form) { |
+ AutoLock l(kwallet_lock_); |
+ int wallet_handle = WalletHandle(); |
+ if (wallet_handle == kInvalidKWalletHandle) |
+ return; |
+ |
+ PasswordFormList forms; |
+ GetLoginsList(&forms, form, wallet_handle); |
+ |
+ for (uint i = 0; i < forms.size(); ++i) { |
+ if (CompareForms(form, *forms[i])) { |
+ forms.erase(forms.begin() + i); |
+ forms.insert(forms.begin() + i, const_cast<PasswordForm*>(&form)); |
+ } |
+ } |
+ |
+ SetLoginsList(forms, form, wallet_handle); |
+} |
+ |
+void PasswordStoreKWallet::RemoveLoginImpl(const PasswordForm& form) { |
+ AutoLock l(kwallet_lock_); |
+ int wallet_handle = WalletHandle(); |
+ if (wallet_handle == kInvalidKWalletHandle) |
+ return; |
+ |
+ PasswordFormList forms; |
+ GetLoginsList(&forms, form, wallet_handle); |
+ |
+ for (uint i = 0; i < forms.size(); ++i) { |
+ if (CompareForms(form, *forms[i])) { |
+ forms.erase(forms.begin() + i); |
+ --i; |
+ } |
+ } |
+ |
+ if (forms.empty()) { |
+ // No items left? Remove the entry from the wallet. |
+ int ret = 0; |
+ dbus_g_proxy_call(proxy_, "removeEntry", &error_, |
+ G_TYPE_INT, wallet_handle, // handle |
+ G_TYPE_STRING, kKWalletFolder, // folder |
+ G_TYPE_STRING, form.signon_realm.c_str(), // key |
+ G_TYPE_STRING, kAppId, // appid |
+ G_TYPE_INVALID, |
+ G_TYPE_INT, &ret, |
+ G_TYPE_INVALID); |
+ CheckError(); |
+ if (ret) |
+ LOG(ERROR) << "Bad return code " << ret << " from kwallet removeEntry"; |
+ } else { |
+ // Otherwise update the entry in the wallet. |
+ SetLoginsList(forms, form, wallet_handle); |
+ } |
+} |
+ |
+void PasswordStoreKWallet::GetLoginsImpl(GetLoginsRequest* request) { |
+ PasswordFormList forms; |
+ |
+ AutoLock l(kwallet_lock_); |
+ int wallet_handle = WalletHandle(); |
+ if (wallet_handle != kInvalidKWalletHandle) |
+ GetLoginsList(&forms, request->form, wallet_handle); |
+ |
+ NotifyConsumer(request, forms); |
+} |
+ |
+void PasswordStoreKWallet::GetLoginsList(PasswordFormList* forms, |
+ const PasswordForm& key, |
+ int wallet_handle) { |
+ // Is there an entry in the wallet? |
+ gboolean has_entry = false; |
+ dbus_g_proxy_call(proxy_, "hasEntry", &error_, |
+ G_TYPE_INT, wallet_handle, // handle |
+ G_TYPE_STRING, kKWalletFolder, // folder |
+ G_TYPE_STRING, key.signon_realm.c_str(), // key |
+ G_TYPE_STRING, kAppId, // appid |
+ G_TYPE_INVALID, |
+ G_TYPE_BOOLEAN, &has_entry, |
+ G_TYPE_INVALID); |
+ |
+ if (CheckError() || !has_entry) |
+ return; |
+ |
+ 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, key.signon_realm.c_str(), // 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) |
+ return; |
+ |
+ Pickle pickle(byte_array->data, byte_array->len); |
+ DeserializeValue(key, pickle, forms); |
+} |
+ |
+void PasswordStoreKWallet::SetLoginsList(const PasswordFormList& forms, |
+ const PasswordForm& key, |
+ int wallet_handle) { |
+ Pickle value; |
+ SerializeValue(forms, &value); |
+ |
+ // Convert the pickled bytes to a GByteArray. |
+ GArray* byte_array = g_array_sized_new(false, false, sizeof(char), |
+ value.size()); |
+ g_array_append_vals(byte_array, value.data(), value.size()); |
+ |
+ // Make the call. |
+ int ret = 0; |
+ dbus_g_proxy_call(proxy_, "writeEntry", &error_, |
+ G_TYPE_INT, wallet_handle, // handle |
+ G_TYPE_STRING, kKWalletFolder, // folder |
+ G_TYPE_STRING, key.signon_realm.c_str(), // key |
+ DBUS_TYPE_G_UCHAR_ARRAY, byte_array, // value |
+ G_TYPE_STRING, kAppId, // appid |
+ G_TYPE_INVALID, |
+ G_TYPE_INT, &ret, |
+ G_TYPE_INVALID); |
+ g_array_free(byte_array, true); |
+ |
+ CheckError(); |
+ if (ret) |
+ LOG(ERROR) << "Bad return code " << ret << " from kwallet writeEntry"; |
+} |
+ |
+bool PasswordStoreKWallet::CompareForms(const PasswordForm& a, |
+ const PasswordForm& b) { |
+ return a.origin == b.origin && |
+ a.password_element == b.password_element && |
+ a.signon_realm == b.signon_realm && |
+ a.submit_element == b.submit_element && |
+ a.username_element == b.username_element && |
+ a.username_value == b.username_value; |
+} |
+ |
+void PasswordStoreKWallet::SerializeValue(const PasswordFormList& forms, |
+ Pickle* pickle) { |
+ pickle->WriteInt(forms.size()); |
+ for (PasswordFormList::const_iterator it = forms.begin() ; |
+ it != forms.end() ; ++it) { |
+ const PasswordForm* form = *it; |
+ pickle->WriteInt(form->scheme); |
+ pickle->WriteString(form->origin.spec()); |
+ pickle->WriteString(form->action.spec()); |
+ pickle->WriteWString(form->username_element); |
+ pickle->WriteWString(form->username_value); |
+ pickle->WriteWString(form->password_element); |
+ pickle->WriteWString(form->password_value); |
+ pickle->WriteWString(form->submit_element); |
+ pickle->WriteBool(form->ssl_valid); |
+ pickle->WriteBool(form->preferred); |
+ pickle->WriteBool(form->blacklisted_by_user); |
+ } |
+} |
+ |
+void PasswordStoreKWallet::DeserializeValue(const PasswordForm& key, |
+ const Pickle& pickle, |
+ PasswordFormList* forms) { |
+ void* iter = NULL; |
+ |
+ int count = 0; |
+ pickle.ReadInt(&iter, &count); |
+ |
+ for (int i = 0; i < count; ++i) { |
+ PasswordForm* form = new PasswordForm(); |
+ form->signon_realm.assign(key.signon_realm); |
+ |
+ pickle.ReadInt(&iter, reinterpret_cast<int*>(&form->scheme)); |
+ ReadGURL(pickle, &iter, &form->origin); |
+ ReadGURL(pickle, &iter, &form->action); |
+ pickle.ReadWString(&iter, &form->username_element); |
+ pickle.ReadWString(&iter, &form->username_value); |
+ pickle.ReadWString(&iter, &form->password_element); |
+ pickle.ReadWString(&iter, &form->password_value); |
+ pickle.ReadWString(&iter, &form->submit_element); |
+ pickle.ReadBool(&iter, &form->ssl_valid); |
+ pickle.ReadBool(&iter, &form->preferred); |
+ pickle.ReadBool(&iter, &form->blacklisted_by_user); |
+ forms->push_back(form); |
+ } |
+} |
+ |
+void PasswordStoreKWallet::ReadGURL(const Pickle& pickle, void** iter, |
+ GURL* url) { |
+ string url_string; |
+ pickle.ReadString(iter, &url_string); |
+ *url = GURL(url_string); |
+} |
+ |
+bool PasswordStoreKWallet::CheckError() { |
+ if (error_) { |
+ LOG(ERROR) << "Failed to complete KWallet call: " << error_->message; |
+ g_error_free(error_); |
+ error_ = NULL; |
+ return true; |
+ } |
+ return false; |
+} |
+ |
+int PasswordStoreKWallet::WalletHandle() { |
+ // Open the wallet. |
+ int handle = kInvalidKWalletHandle; |
+ dbus_g_proxy_call(proxy_, "open", &error_, |
+ G_TYPE_STRING, wallet_name_.c_str(), // wallet |
+ G_TYPE_INT64, 0LL, // wid |
+ G_TYPE_STRING, kAppId, // appid |
+ G_TYPE_INVALID, |
+ G_TYPE_INT, &handle, |
+ G_TYPE_INVALID); |
+ if (CheckError() || handle == kInvalidKWalletHandle) |
+ return kInvalidKWalletHandle; |
+ |
+ // Check if our folder exists. |
+ gboolean has_folder = false; |
+ dbus_g_proxy_call(proxy_, "hasFolder", &error_, |
+ G_TYPE_INT, handle, // handle |
+ G_TYPE_STRING, kKWalletFolder, // folder |
+ G_TYPE_STRING, kAppId, // appid |
+ G_TYPE_INVALID, |
+ G_TYPE_BOOLEAN, &has_folder, |
+ G_TYPE_INVALID); |
+ if (CheckError()) |
+ return kInvalidKWalletHandle; |
+ |
+ // Create it if it didn't. |
+ if (!has_folder) { |
+ gboolean success = false; |
+ dbus_g_proxy_call(proxy_, "createFolder", &error_, |
+ G_TYPE_INT, handle, // handle |
+ G_TYPE_STRING, kKWalletFolder, // folder |
+ G_TYPE_STRING, kAppId, // appid |
+ G_TYPE_INVALID, |
+ G_TYPE_BOOLEAN, &success, |
+ G_TYPE_INVALID); |
+ if (CheckError() || !success) |
+ return kInvalidKWalletHandle; |
+ } |
+ |
+ return handle; |
+} |