Index: chrome/browser/sync/util/user_settings.cc |
diff --git a/chrome/browser/sync/util/user_settings.cc b/chrome/browser/sync/util/user_settings.cc |
deleted file mode 100644 |
index 2ab090e639f276da855e34889c6a407efcdd45da..0000000000000000000000000000000000000000 |
--- a/chrome/browser/sync/util/user_settings.cc |
+++ /dev/null |
@@ -1,465 +0,0 @@ |
-// Copyright (c) 2011 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. |
-// |
-// This class isn't pretty. It's just a step better than globals, which is what |
-// these were previously. |
- |
-#include "chrome/browser/sync/util/user_settings.h" |
- |
-#include "build/build_config.h" |
- |
-#if defined(OS_WIN) |
-#include <windows.h> |
-#endif |
- |
-#include <limits> |
-#include <string> |
-#include <vector> |
- |
-#include "base/file_util.h" |
-#include "base/md5.h" |
-#include "base/rand_util.h" |
-#include "base/string_util.h" |
-#include "chrome/browser/sync/syncable/directory_manager.h" // For migration. |
-#include "chrome/browser/sync/util/data_encryption.h" |
-#include "chrome/browser/sync/util/sqlite_utils.h" |
-#include "chrome/common/random.h" |
- |
-using std::numeric_limits; |
-using std::string; |
-using std::vector; |
- |
-using syncable::DirectoryManager; |
- |
-namespace browser_sync { |
- |
-void ExecOrDie(sqlite3* dbhandle, const char *query) { |
- sqlite_utils::SQLStatement statement; |
- statement.prepare(dbhandle, query); |
- if (SQLITE_DONE != statement.step()) { |
- LOG(FATAL) << query << "\n" << sqlite3_errmsg(dbhandle); |
- } |
-} |
- |
-// Useful for encoding any sequence of bytes into a string that can be used in |
-// a table name. Kind of like hex encoding, except that A is zero and P is 15. |
-string APEncode(const string& in) { |
- string result; |
- result.reserve(in.size() * 2); |
- for (string::const_iterator i = in.begin(); i != in.end(); ++i) { |
- unsigned int c = static_cast<unsigned char>(*i); |
- result.push_back((c & 0x0F) + 'A'); |
- result.push_back(((c >> 4) & 0x0F) + 'A'); |
- } |
- return result; |
-} |
- |
-string APDecode(const string& in) { |
- string result; |
- result.reserve(in.size() / 2); |
- for (string::const_iterator i = in.begin(); i != in.end(); ++i) { |
- unsigned int c = *i - 'A'; |
- if (++i != in.end()) |
- c = c | (static_cast<unsigned char>(*i - 'A') << 4); |
- result.push_back(c); |
- } |
- return result; |
-} |
- |
-static const char PASSWORD_HASH[] = "password_hash2"; |
-static const char SALT[] = "salt2"; |
- |
-static const int kSaltSize = 20; |
-static const int kCurrentDBVersion = 12; |
- |
-UserSettings::ScopedDBHandle::ScopedDBHandle(UserSettings* settings) |
- : mutex_lock_(settings->dbhandle_mutex_), handle_(&settings->dbhandle_) { |
-} |
- |
-UserSettings::UserSettings() : dbhandle_(NULL) { |
-} |
- |
-string UserSettings::email() const { |
- base::AutoLock lock(mutex_); |
- return email_; |
-} |
- |
-static void MakeSigninsTable(sqlite3* const dbhandle) { |
- // Multiple email addresses can map to the same Google Account. This table |
- // keeps a map of sign-in email addresses to primary Google Account email |
- // addresses. |
- ExecOrDie(dbhandle, |
- "CREATE TABLE signins" |
- " (signin, primary_email, " |
- " PRIMARY KEY(signin, primary_email) ON CONFLICT REPLACE)"); |
-} |
- |
-void UserSettings::MigrateOldVersionsAsNeeded(sqlite3* const handle, |
- int current_version) { |
- switch (current_version) { |
- // Versions 1-9 are unhandled. Version numbers greater than |
- // kCurrentDBVersion should have already been weeded out by the caller. |
- default: |
- // When the version is too old, we just try to continue anyway. There |
- // should not be a released product that makes a database too old for us |
- // to handle. |
- LOG(WARNING) << "UserSettings database version " << current_version << |
- " is too old to handle."; |
- return; |
- case 10: |
- { |
- // Scrape the 'shares' table to find the syncable DB. 'shares' had a |
- // pair of string columns that mapped the username to the filename of |
- // the sync data sqlite3 file. Version 11 switched to a constant |
- // filename, so here we read the string, copy the file to the new name, |
- // delete the old one, and then drop the unused shares table. |
- sqlite_utils::SQLStatement share_query; |
- share_query.prepare(handle, "SELECT share_name, file_name FROM shares"); |
- int query_result = share_query.step(); |
- CHECK(SQLITE_ROW == query_result); |
- FilePath::StringType share_name, file_name; |
-#if defined(OS_POSIX) |
- share_name = share_query.column_string(0); |
- file_name = share_query.column_string(1); |
-#else |
- share_name = share_query.column_wstring(0); |
- file_name = share_query.column_wstring(1); |
-#endif |
- |
- const FilePath& src_syncdata_path = FilePath(file_name); |
- FilePath dst_syncdata_path(src_syncdata_path.DirName()); |
- file_util::AbsolutePath(&dst_syncdata_path); |
- dst_syncdata_path = dst_syncdata_path.Append( |
- DirectoryManager::GetSyncDataDatabaseFilename()); |
- if (!file_util::Move(src_syncdata_path, dst_syncdata_path)) { |
- LOG(WARNING) << "Unable to upgrade UserSettings from v10"; |
- return; |
- } |
- } |
- ExecOrDie(handle, "DROP TABLE shares"); |
- ExecOrDie(handle, "UPDATE db_version SET version = 11"); |
- // FALL THROUGH |
- case 11: |
- ExecOrDie(handle, "DROP TABLE signin_types"); |
- ExecOrDie(handle, "UPDATE db_version SET version = 12"); |
- // FALL THROUGH |
- case kCurrentDBVersion: |
- // Nothing to migrate. |
- return; |
- } |
-} |
- |
-static void MakeCookiesTable(sqlite3* const dbhandle) { |
- // This table keeps a list of auth tokens for each signed in account. There |
- // will be as many rows as there are auth tokens per sign in. |
- // The service_token column will store encrypted values. |
- ExecOrDie(dbhandle, |
- "CREATE TABLE cookies" |
- " (email, service_name, service_token, " |
- " PRIMARY KEY(email, service_name) ON CONFLICT REPLACE)"); |
-} |
- |
-static void MakeClientIDTable(sqlite3* const dbhandle) { |
- // Stores a single client ID value that can be used as the client id, if |
- // there's not another such ID provided on the install. |
- ExecOrDie(dbhandle, "CREATE TABLE client_id (id) "); |
- { |
- sqlite_utils::SQLStatement statement; |
- statement.prepare(dbhandle, |
- "INSERT INTO client_id values ( ? )"); |
- statement.bind_string(0, Generate128BitRandomBase64String()); |
- if (SQLITE_DONE != statement.step()) { |
- LOG(FATAL) << "INSERT INTO client_id\n" << sqlite3_errmsg(dbhandle); |
- } |
- } |
-} |
- |
-bool UserSettings::Init(const FilePath& settings_path) { |
- { // Scope the handle. |
- ScopedDBHandle dbhandle(this); |
- if (dbhandle_) |
- sqlite3_close(dbhandle_); |
- |
- if (SQLITE_OK != sqlite_utils::OpenSqliteDb(settings_path, &dbhandle_)) |
- return false; |
- |
- // In the worst case scenario, the user may hibernate his computer during |
- // one of our transactions. |
- sqlite3_busy_timeout(dbhandle_, numeric_limits<int>::max()); |
- ExecOrDie(dbhandle.get(), "PRAGMA fullfsync = 1"); |
- ExecOrDie(dbhandle.get(), "PRAGMA synchronous = 2"); |
- |
- sqlite_utils::SQLTransaction transaction(dbhandle.get()); |
- transaction.BeginExclusive(); |
- sqlite_utils::SQLStatement table_query; |
- table_query.prepare(dbhandle.get(), |
- "select count(*) from sqlite_master" |
- " where type = 'table' and name = 'db_version'"); |
- int query_result = table_query.step(); |
- CHECK(SQLITE_ROW == query_result); |
- int table_count = table_query.column_int(0); |
- table_query.reset(); |
- if (table_count > 0) { |
- sqlite_utils::SQLStatement version_query; |
- version_query.prepare(dbhandle.get(), |
- "SELECT version FROM db_version"); |
- query_result = version_query.step(); |
- CHECK(SQLITE_ROW == query_result); |
- const int version = version_query.column_int(0); |
- version_query.reset(); |
- if (version > kCurrentDBVersion) { |
- LOG(WARNING) << "UserSettings database is too new."; |
- return false; |
- } |
- |
- MigrateOldVersionsAsNeeded(dbhandle.get(), version); |
- } else { |
- // Create settings table. |
- { |
- sqlite_utils::SQLStatement statement; |
- statement.prepare(dbhandle.get(), |
- "CREATE TABLE settings" |
- " (email, key, value, " |
- " PRIMARY KEY(email, key) ON CONFLICT REPLACE)"); |
- if (SQLITE_DONE != statement.step()) { |
- return false; |
- } |
- } |
- // Create and populate version table. |
- { |
- sqlite_utils::SQLStatement statement; |
- statement.prepare(dbhandle.get(), |
- "CREATE TABLE db_version ( version )"); |
- if (SQLITE_DONE != statement.step()) { |
- return false; |
- } |
- } |
- { |
- sqlite_utils::SQLStatement statement; |
- statement.prepare(dbhandle.get(), |
- "INSERT INTO db_version values ( ? )"); |
- statement.bind_int(0, kCurrentDBVersion); |
- if (SQLITE_DONE != statement.step()) { |
- return false; |
- } |
- } |
- |
- MakeSigninsTable(dbhandle.get()); |
- MakeCookiesTable(dbhandle.get()); |
- MakeClientIDTable(dbhandle.get()); |
- } |
- transaction.Commit(); |
- } |
-#if defined(OS_WIN) |
- // Do not index this file. Scanning can occur every time we close the file, |
- // which causes long delays in SQLite's file locking. |
- const DWORD attrs = GetFileAttributes(settings_path.value().c_str()); |
- const BOOL attrs_set = |
- SetFileAttributes(settings_path.value().c_str(), |
- attrs | FILE_ATTRIBUTE_NOT_CONTENT_INDEXED); |
-#endif |
- return true; |
-} |
- |
-UserSettings::~UserSettings() { |
- if (dbhandle_) |
- sqlite3_close(dbhandle_); |
-} |
- |
-const int32 kInvalidHash = 0xFFFFFFFF; |
- |
-// We use 10 bits of data from the MD5 digest as the hash. |
-const int32 kHashMask = 0x3FF; |
- |
-int32 GetHashFromDigest(base::MD5Digest& digest) { |
- int32 hash = 0; |
- int32 mask = kHashMask; |
- for (size_t i = 0; i < sizeof(digest.a); ++i) { |
- hash = hash << 8; |
- hash = hash | (digest.a[i] & kHashMask); |
- mask = mask >> 8; |
- if (0 == mask) |
- break; |
- } |
- return hash; |
-} |
- |
-void UserSettings::StoreEmailForSignin(const string& signin, |
- const string& primary_email) { |
- ScopedDBHandle dbhandle(this); |
- sqlite_utils::SQLTransaction transaction(dbhandle.get()); |
- int sqlite_result = transaction.BeginExclusive(); |
- CHECK(SQLITE_OK == sqlite_result); |
- sqlite_utils::SQLStatement query; |
- query.prepare(dbhandle.get(), |
- "SELECT COUNT(*) FROM signins" |
- " WHERE signin = ? AND primary_email = ?"); |
- query.bind_string(0, signin); |
- query.bind_string(1, primary_email); |
- int query_result = query.step(); |
- CHECK(SQLITE_ROW == query_result); |
- int32 count = query.column_int(0); |
- query.reset(); |
- if (0 == count) { |
- // Migrate any settings the user might have from earlier versions. |
- { |
- sqlite_utils::SQLStatement statement; |
- statement.prepare(dbhandle.get(), |
- "UPDATE settings SET email = ? WHERE email = ?"); |
- statement.bind_string(0, signin); |
- statement.bind_string(1, primary_email); |
- if (SQLITE_DONE != statement.step()) { |
- LOG(FATAL) << sqlite3_errmsg(dbhandle.get()); |
- } |
- } |
- // Store this signin:email mapping. |
- { |
- sqlite_utils::SQLStatement statement; |
- statement.prepare(dbhandle.get(), |
- "INSERT INTO signins(signin, primary_email)" |
- " values ( ?, ? )"); |
- statement.bind_string(0, signin); |
- statement.bind_string(1, primary_email); |
- if (SQLITE_DONE != statement.step()) { |
- LOG(FATAL) << sqlite3_errmsg(dbhandle.get()); |
- } |
- } |
- } |
- transaction.Commit(); |
-} |
- |
-// string* signin is both the input and the output of this function. |
-bool UserSettings::GetEmailForSignin(string* signin) { |
- ScopedDBHandle dbhandle(this); |
- string result; |
- sqlite_utils::SQLStatement query; |
- query.prepare(dbhandle.get(), |
- "SELECT primary_email FROM signins WHERE signin = ?"); |
- query.bind_string(0, *signin); |
- int query_result = query.step(); |
- if (SQLITE_ROW == query_result) { |
- query.column_string(0, &result); |
- if (!result.empty()) { |
- swap(result, *signin); |
- return true; |
- } |
- } |
- return false; |
-} |
- |
-void UserSettings::StoreHashedPassword(const string& email, |
- const string& password) { |
- // Save one-way hashed password: |
- char binary_salt[kSaltSize]; |
- base::RandBytes(binary_salt, sizeof(binary_salt)); |
- |
- const string salt = APEncode(string(binary_salt, sizeof(binary_salt))); |
- base::MD5Context md5_context; |
- base::MD5Init(&md5_context); |
- base::MD5Update(&md5_context, salt); |
- base::MD5Update(&md5_context, password); |
- base::MD5Digest md5_digest; |
- base::MD5Final(&md5_digest, &md5_context); |
- |
- ScopedDBHandle dbhandle(this); |
- sqlite_utils::SQLTransaction transaction(dbhandle.get()); |
- transaction.BeginExclusive(); |
- { |
- sqlite_utils::SQLStatement statement; |
- statement.prepare(dbhandle.get(), |
- "INSERT INTO settings(email, key, value)" |
- " values ( ?, ?, ? )"); |
- statement.bind_string(0, email); |
- statement.bind_string(1, PASSWORD_HASH); |
- statement.bind_int(2, GetHashFromDigest(md5_digest)); |
- if (SQLITE_DONE != statement.step()) { |
- LOG(FATAL) << sqlite3_errmsg(dbhandle.get()); |
- } |
- } |
- { |
- sqlite_utils::SQLStatement statement; |
- statement.prepare(dbhandle.get(), |
- "INSERT INTO settings(email, key, value)" |
- " values ( ?, ?, ? )"); |
- statement.bind_string(0, email); |
- statement.bind_string(1, SALT); |
- statement.bind_string(2, salt); |
- if (SQLITE_DONE != statement.step()) { |
- LOG(FATAL) << sqlite3_errmsg(dbhandle.get()); |
- } |
- } |
- transaction.Commit(); |
-} |
- |
-bool UserSettings::VerifyAgainstStoredHash(const string& email, |
- const string& password) { |
- ScopedDBHandle dbhandle(this); |
- string salt_and_digest; |
- |
- sqlite_utils::SQLStatement query; |
- query.prepare(dbhandle.get(), |
- "SELECT key, value FROM settings" |
- " WHERE email = ? AND (key = ? OR key = ?)"); |
- query.bind_string(0, email); |
- query.bind_string(1, PASSWORD_HASH); |
- query.bind_string(2, SALT); |
- int query_result = query.step(); |
- string salt; |
- int32 hash = kInvalidHash; |
- while (SQLITE_ROW == query_result) { |
- string key(query.column_string(0)); |
- if (key == SALT) |
- salt = query.column_string(1); |
- else |
- hash = query.column_int(1); |
- query_result = query.step(); |
- } |
- CHECK(SQLITE_DONE == query_result); |
- if (salt.empty() || hash == kInvalidHash) |
- return false; |
- base::MD5Context md5_context; |
- base::MD5Init(&md5_context); |
- base::MD5Update(&md5_context, salt); |
- base::MD5Update(&md5_context, password); |
- base::MD5Digest md5_digest; |
- base::MD5Final(&md5_digest, &md5_context); |
- return hash == GetHashFromDigest(md5_digest); |
-} |
- |
-void UserSettings::SwitchUser(const string& username) { |
- { |
- base::AutoLock lock(mutex_); |
- email_ = username; |
- } |
-} |
- |
-string UserSettings::GetClientId() { |
- ScopedDBHandle dbhandle(this); |
- sqlite_utils::SQLStatement statement; |
- statement.prepare(dbhandle.get(), "SELECT id FROM client_id"); |
- int query_result = statement.step(); |
- string client_id; |
- if (query_result == SQLITE_ROW) |
- client_id = statement.column_string(0); |
- return client_id; |
-} |
- |
-void UserSettings::ClearAllServiceTokens() { |
- ScopedDBHandle dbhandle(this); |
- ExecOrDie(dbhandle.get(), "DELETE FROM cookies"); |
-} |
- |
-bool UserSettings::GetLastUser(string* username) { |
- ScopedDBHandle dbhandle(this); |
- sqlite_utils::SQLStatement query; |
- query.prepare(dbhandle.get(), "SELECT email FROM cookies"); |
- if (SQLITE_ROW == query.step()) { |
- *username = query.column_string(0); |
- return true; |
- } else { |
- return false; |
- } |
-} |
- |
-} // namespace browser_sync |