| 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
|
|
|