1c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch// Copyright (c) 2010 The Chromium Authors. All rights reserved. 2c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch// Use of this source code is governed by a BSD-style license that can be 3c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch// found in the LICENSE file. 4c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch// 5c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch// This class isn't pretty. It's just a step better than globals, which is what 6c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch// these were previously. 7c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch 8c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch#include "chrome/browser/sync/util/user_settings.h" 9c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch 10c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch#include "build/build_config.h" 11c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch 12c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch#if defined(OS_WIN) 13c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch#include <windows.h> 14c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch#endif 15c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch 16c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch#include <limits> 17c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch#include <string> 18c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch#include <vector> 19c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch 20c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch#include "base/file_util.h" 21c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch#include "base/string_util.h" 22c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch#include "chrome/browser/sync/syncable/directory_manager.h" // For migration. 23c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch#include "chrome/browser/sync/util/crypto_helpers.h" 24c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch#include "chrome/browser/sync/util/data_encryption.h" 25c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch#include "chrome/common/sqlite_utils.h" 26c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch 27c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdochusing std::numeric_limits; 28c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdochusing std::string; 29c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdochusing std::vector; 30c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch 31c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdochusing syncable::DirectoryManager; 32c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch 33c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdochnamespace browser_sync { 34c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch 35c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdochvoid ExecOrDie(sqlite3* dbhandle, const char *query) { 36c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch SQLStatement statement; 37c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch statement.prepare(dbhandle, query); 38c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch if (SQLITE_DONE != statement.step()) { 39c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch LOG(FATAL) << query << "\n" << sqlite3_errmsg(dbhandle); 40c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch } 41c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch} 42c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch 43c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch// Useful for encoding any sequence of bytes into a string that can be used in 44c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch// a table name. Kind of like hex encoding, except that A is zero and P is 15. 45c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdochstring APEncode(const string& in) { 46c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch string result; 47c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch result.reserve(in.size() * 2); 48c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch for (string::const_iterator i = in.begin(); i != in.end(); ++i) { 49c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch unsigned int c = static_cast<unsigned char>(*i); 50c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch result.push_back((c & 0x0F) + 'A'); 51c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch result.push_back(((c >> 4) & 0x0F) + 'A'); 52c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch } 53c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch return result; 54c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch} 55c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch 56c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdochstring APDecode(const string& in) { 57c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch string result; 58c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch result.reserve(in.size() / 2); 59c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch for (string::const_iterator i = in.begin(); i != in.end(); ++i) { 60c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch unsigned int c = *i - 'A'; 61c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch if (++i != in.end()) 62c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch c = c | (static_cast<unsigned char>(*i - 'A') << 4); 63c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch result.push_back(c); 64c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch } 65c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch return result; 66c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch} 67c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch 68c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdochstatic const char PASSWORD_HASH[] = "password_hash2"; 69c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdochstatic const char SALT[] = "salt2"; 70c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch 71c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdochstatic const int kSaltSize = 20; 72c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdochstatic const int kCurrentDBVersion = 12; 73c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch 74c407dc5cd9bdc5668497f21b26b09d988ab439deBen MurdochUserSettings::ScopedDBHandle::ScopedDBHandle(UserSettings* settings) 75c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch : mutex_lock_(settings->dbhandle_mutex_), handle_(&settings->dbhandle_) { 76c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch} 77c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch 78c407dc5cd9bdc5668497f21b26b09d988ab439deBen MurdochUserSettings::UserSettings() : dbhandle_(NULL) { 79c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch} 80c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch 81c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdochstring UserSettings::email() const { 8272a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen base::AutoLock lock(mutex_); 83c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch return email_; 84c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch} 85c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch 86c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdochstatic void MakeSigninsTable(sqlite3* const dbhandle) { 87c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch // Multiple email addresses can map to the same Google Account. This table 88c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch // keeps a map of sign-in email addresses to primary Google Account email 89c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch // addresses. 90c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch ExecOrDie(dbhandle, 91c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch "CREATE TABLE signins" 92c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch " (signin, primary_email, " 93c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch " PRIMARY KEY(signin, primary_email) ON CONFLICT REPLACE)"); 94c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch} 95c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch 96c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdochvoid UserSettings::MigrateOldVersionsAsNeeded(sqlite3* const handle, 97c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch int current_version) { 98c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch switch (current_version) { 99c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch // Versions 1-9 are unhandled. Version numbers greater than 100c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch // kCurrentDBVersion should have already been weeded out by the caller. 101c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch default: 102c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch // When the version is too old, we just try to continue anyway. There 103c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch // should not be a released product that makes a database too old for us 104c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch // to handle. 105c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch LOG(WARNING) << "UserSettings database version " << current_version << 106c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch " is too old to handle."; 107c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch return; 108c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch case 10: 109c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch { 110c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch // Scrape the 'shares' table to find the syncable DB. 'shares' had a 111c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch // pair of string columns that mapped the username to the filename of 112c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch // the sync data sqlite3 file. Version 11 switched to a constant 113c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch // filename, so here we read the string, copy the file to the new name, 114c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch // delete the old one, and then drop the unused shares table. 115c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch SQLStatement share_query; 116c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch share_query.prepare(handle, "SELECT share_name, file_name FROM shares"); 117c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch int query_result = share_query.step(); 118c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch CHECK(SQLITE_ROW == query_result); 119c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch FilePath::StringType share_name, file_name; 120c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch#if defined(OS_POSIX) 121c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch share_name = share_query.column_string(0); 122c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch file_name = share_query.column_string(1); 123c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch#else 124c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch share_name = share_query.column_wstring(0); 125c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch file_name = share_query.column_wstring(1); 126c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch#endif 127c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch 128c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch const FilePath& src_syncdata_path = FilePath(file_name); 129c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch FilePath dst_syncdata_path(src_syncdata_path.DirName()); 130c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch file_util::AbsolutePath(&dst_syncdata_path); 131c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch dst_syncdata_path = dst_syncdata_path.Append( 132c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch DirectoryManager::GetSyncDataDatabaseFilename()); 133c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch if (!file_util::Move(src_syncdata_path, dst_syncdata_path)) { 134c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch LOG(WARNING) << "Unable to upgrade UserSettings from v10"; 135c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch return; 136c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch } 137c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch } 138c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch ExecOrDie(handle, "DROP TABLE shares"); 139c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch ExecOrDie(handle, "UPDATE db_version SET version = 11"); 140c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch // FALL THROUGH 141c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch case 11: 142c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch ExecOrDie(handle, "DROP TABLE signin_types"); 143c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch ExecOrDie(handle, "UPDATE db_version SET version = 12"); 144c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch // FALL THROUGH 145c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch case kCurrentDBVersion: 146c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch // Nothing to migrate. 147c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch return; 148c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch } 149c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch} 150c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch 151c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdochstatic void MakeCookiesTable(sqlite3* const dbhandle) { 152c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch // This table keeps a list of auth tokens for each signed in account. There 153c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch // will be as many rows as there are auth tokens per sign in. 154c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch // The service_token column will store encrypted values. 155c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch ExecOrDie(dbhandle, 156c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch "CREATE TABLE cookies" 157c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch " (email, service_name, service_token, " 158c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch " PRIMARY KEY(email, service_name) ON CONFLICT REPLACE)"); 159c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch} 160c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch 161c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdochstatic void MakeClientIDTable(sqlite3* const dbhandle) { 162c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch // Stores a single client ID value that can be used as the client id, if 163c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch // there's not another such ID provided on the install. 164c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch ExecOrDie(dbhandle, "CREATE TABLE client_id (id) "); 165c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch { 166c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch SQLStatement statement; 167c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch statement.prepare(dbhandle, 168c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch "INSERT INTO client_id values ( ? )"); 169c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch statement.bind_string(0, Generate128BitRandomHexString()); 170c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch if (SQLITE_DONE != statement.step()) { 171c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch LOG(FATAL) << "INSERT INTO client_id\n" << sqlite3_errmsg(dbhandle); 172c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch } 173c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch } 174c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch} 175c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch 176c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdochbool UserSettings::Init(const FilePath& settings_path) { 177c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch { // Scope the handle. 178c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch ScopedDBHandle dbhandle(this); 179c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch if (dbhandle_) 180c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch sqlite3_close(dbhandle_); 1813345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick 1823345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick if (SQLITE_OK != sqlite_utils::OpenSqliteDb(settings_path, &dbhandle_)) 1833345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick return false; 1843345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick 185c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch // In the worst case scenario, the user may hibernate his computer during 186c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch // one of our transactions. 187c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch sqlite3_busy_timeout(dbhandle_, numeric_limits<int>::max()); 188c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch ExecOrDie(dbhandle.get(), "PRAGMA fullfsync = 1"); 189c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch ExecOrDie(dbhandle.get(), "PRAGMA synchronous = 2"); 190c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch 191c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch SQLTransaction transaction(dbhandle.get()); 192c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch transaction.BeginExclusive(); 193c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch SQLStatement table_query; 194c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch table_query.prepare(dbhandle.get(), 195c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch "select count(*) from sqlite_master" 196c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch " where type = 'table' and name = 'db_version'"); 197c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch int query_result = table_query.step(); 198c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch CHECK(SQLITE_ROW == query_result); 199c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch int table_count = table_query.column_int(0); 200c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch table_query.reset(); 201c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch if (table_count > 0) { 202c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch SQLStatement version_query; 203c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch version_query.prepare(dbhandle.get(), 204c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch "SELECT version FROM db_version"); 205c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch query_result = version_query.step(); 206c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch CHECK(SQLITE_ROW == query_result); 207c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch const int version = version_query.column_int(0); 208c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch version_query.reset(); 209c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch if (version > kCurrentDBVersion) { 210c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch LOG(WARNING) << "UserSettings database is too new."; 211c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch return false; 212c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch } 213c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch 214c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch MigrateOldVersionsAsNeeded(dbhandle.get(), version); 215c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch } else { 216c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch // Create settings table. 217c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch { 218c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch SQLStatement statement; 219c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch statement.prepare(dbhandle.get(), 220c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch "CREATE TABLE settings" 221c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch " (email, key, value, " 222c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch " PRIMARY KEY(email, key) ON CONFLICT REPLACE)"); 223c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch if (SQLITE_DONE != statement.step()) { 224c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch return false; 225c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch } 226c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch } 227c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch // Create and populate version table. 228c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch { 229c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch SQLStatement statement; 230c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch statement.prepare(dbhandle.get(), 231c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch "CREATE TABLE db_version ( version )"); 232c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch if (SQLITE_DONE != statement.step()) { 233c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch return false; 234c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch } 235c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch } 236c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch { 237c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch SQLStatement statement; 238c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch statement.prepare(dbhandle.get(), 239c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch "INSERT INTO db_version values ( ? )"); 240c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch statement.bind_int(0, kCurrentDBVersion); 241c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch if (SQLITE_DONE != statement.step()) { 242c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch return false; 243c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch } 244c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch } 245c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch 246c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch MakeSigninsTable(dbhandle.get()); 247c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch MakeCookiesTable(dbhandle.get()); 248c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch MakeClientIDTable(dbhandle.get()); 249c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch } 250c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch transaction.Commit(); 251c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch } 252c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch#if defined(OS_WIN) 253c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch // Do not index this file. Scanning can occur every time we close the file, 254c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch // which causes long delays in SQLite's file locking. 255c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch const DWORD attrs = GetFileAttributes(settings_path.value().c_str()); 256c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch const BOOL attrs_set = 257c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch SetFileAttributes(settings_path.value().c_str(), 258c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch attrs | FILE_ATTRIBUTE_NOT_CONTENT_INDEXED); 259c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch#endif 260c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch return true; 261c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch} 262c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch 263c407dc5cd9bdc5668497f21b26b09d988ab439deBen MurdochUserSettings::~UserSettings() { 264c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch if (dbhandle_) 265c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch sqlite3_close(dbhandle_); 266c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch} 267c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch 268c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdochconst int32 kInvalidHash = 0xFFFFFFFF; 269c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch 270c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch// We use 10 bits of data from the MD5 digest as the hash. 271c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdochconst int32 kHashMask = 0x3FF; 272c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch 273c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdochint32 GetHashFromDigest(const vector<uint8>& digest) { 274c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch int32 hash = 0; 275c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch int32 mask = kHashMask; 276c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch for (vector<uint8>::const_iterator i = digest.begin(); i != digest.end(); 277c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch ++i) { 278c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch hash = hash << 8; 279c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch hash = hash | (*i & kHashMask); 280c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch mask = mask >> 8; 281c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch if (0 == mask) 282c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch break; 283c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch } 284c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch return hash; 285c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch} 286c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch 287c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdochvoid UserSettings::StoreEmailForSignin(const string& signin, 288c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch const string& primary_email) { 289c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch ScopedDBHandle dbhandle(this); 290c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch SQLTransaction transaction(dbhandle.get()); 291c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch int sqlite_result = transaction.BeginExclusive(); 292c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch CHECK(SQLITE_OK == sqlite_result); 293c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch SQLStatement query; 294c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch query.prepare(dbhandle.get(), 295c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch "SELECT COUNT(*) FROM signins" 296c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch " WHERE signin = ? AND primary_email = ?"); 297c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch query.bind_string(0, signin); 298c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch query.bind_string(1, primary_email); 299c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch int query_result = query.step(); 300c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch CHECK(SQLITE_ROW == query_result); 301c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch int32 count = query.column_int(0); 302c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch query.reset(); 303c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch if (0 == count) { 304c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch // Migrate any settings the user might have from earlier versions. 305c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch { 306c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch SQLStatement statement; 307c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch statement.prepare(dbhandle.get(), 308c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch "UPDATE settings SET email = ? WHERE email = ?"); 309c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch statement.bind_string(0, signin); 310c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch statement.bind_string(1, primary_email); 311c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch if (SQLITE_DONE != statement.step()) { 312c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch LOG(FATAL) << sqlite3_errmsg(dbhandle.get()); 313c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch } 314c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch } 315c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch // Store this signin:email mapping. 316c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch { 317c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch SQLStatement statement; 318c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch statement.prepare(dbhandle.get(), 319c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch "INSERT INTO signins(signin, primary_email)" 320c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch " values ( ?, ? )"); 321c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch statement.bind_string(0, signin); 322c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch statement.bind_string(1, primary_email); 323c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch if (SQLITE_DONE != statement.step()) { 324c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch LOG(FATAL) << sqlite3_errmsg(dbhandle.get()); 325c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch } 326c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch } 327c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch } 328c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch transaction.Commit(); 329c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch} 330c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch 331c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch// string* signin is both the input and the output of this function. 332c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdochbool UserSettings::GetEmailForSignin(string* signin) { 333c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch ScopedDBHandle dbhandle(this); 334c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch string result; 335c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch SQLStatement query; 336c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch query.prepare(dbhandle.get(), 337c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch "SELECT primary_email FROM signins WHERE signin = ?"); 338c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch query.bind_string(0, *signin); 339c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch int query_result = query.step(); 340c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch if (SQLITE_ROW == query_result) { 341c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch query.column_string(0, &result); 342c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch if (!result.empty()) { 343c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch swap(result, *signin); 344c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch return true; 345c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch } 346c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch } 347c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch return false; 348c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch} 349c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch 350c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdochvoid UserSettings::StoreHashedPassword(const string& email, 351c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch const string& password) { 352c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch // Save one-way hashed password: 353c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch char binary_salt[kSaltSize]; 354c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch GetRandomBytes(binary_salt, sizeof(binary_salt)); 355c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch 356c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch const string salt = APEncode(string(binary_salt, sizeof(binary_salt))); 357c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch MD5Calculator md5; 358c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch md5.AddData(salt.data(), salt.size()); 359c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch md5.AddData(password.data(), password.size()); 360c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch ScopedDBHandle dbhandle(this); 361c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch SQLTransaction transaction(dbhandle.get()); 362c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch transaction.BeginExclusive(); 363c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch { 364c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch SQLStatement statement; 365c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch statement.prepare(dbhandle.get(), 366c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch "INSERT INTO settings(email, key, value)" 367c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch " values ( ?, ?, ? )"); 368c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch statement.bind_string(0, email); 369c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch statement.bind_string(1, PASSWORD_HASH); 370c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch statement.bind_int(2, GetHashFromDigest(md5.GetDigest())); 371c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch if (SQLITE_DONE != statement.step()) { 372c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch LOG(FATAL) << sqlite3_errmsg(dbhandle.get()); 373c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch } 374c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch } 375c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch { 376c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch SQLStatement statement; 377c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch statement.prepare(dbhandle.get(), 378c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch "INSERT INTO settings(email, key, value)" 379c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch " values ( ?, ?, ? )"); 380c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch statement.bind_string(0, email); 381c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch statement.bind_string(1, SALT); 382c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch statement.bind_string(2, salt); 383c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch if (SQLITE_DONE != statement.step()) { 384c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch LOG(FATAL) << sqlite3_errmsg(dbhandle.get()); 385c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch } 386c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch } 387c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch transaction.Commit(); 388c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch} 389c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch 390c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdochbool UserSettings::VerifyAgainstStoredHash(const string& email, 391c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch const string& password) { 392c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch ScopedDBHandle dbhandle(this); 393c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch string salt_and_digest; 394c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch 395c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch SQLStatement query; 396c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch query.prepare(dbhandle.get(), 397c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch "SELECT key, value FROM settings" 398c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch " WHERE email = ? AND (key = ? OR key = ?)"); 399c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch query.bind_string(0, email); 400c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch query.bind_string(1, PASSWORD_HASH); 401c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch query.bind_string(2, SALT); 402c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch int query_result = query.step(); 403c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch string salt; 404c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch int32 hash = kInvalidHash; 405c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch while (SQLITE_ROW == query_result) { 406c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch string key(query.column_string(0)); 407c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch if (key == SALT) 408c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch salt = query.column_string(1); 409c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch else 410c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch hash = query.column_int(1); 411c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch query_result = query.step(); 412c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch } 413c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch CHECK(SQLITE_DONE == query_result); 414c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch if (salt.empty() || hash == kInvalidHash) 415c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch return false; 416c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch MD5Calculator md5; 417c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch md5.AddData(salt.data(), salt.size()); 418c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch md5.AddData(password.data(), password.size()); 419c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch return hash == GetHashFromDigest(md5.GetDigest()); 420c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch} 421c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch 422c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdochvoid UserSettings::SwitchUser(const string& username) { 423c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch { 42472a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen base::AutoLock lock(mutex_); 425c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch email_ = username; 426c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch } 427c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch} 428c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch 429c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdochstring UserSettings::GetClientId() { 430c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch ScopedDBHandle dbhandle(this); 431c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch SQLStatement statement; 432c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch statement.prepare(dbhandle.get(), "SELECT id FROM client_id"); 433c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch int query_result = statement.step(); 434c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch string client_id; 435c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch if (query_result == SQLITE_ROW) 436c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch client_id = statement.column_string(0); 437c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch return client_id; 438c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch} 439c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch 440c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdochvoid UserSettings::ClearAllServiceTokens() { 441c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch ScopedDBHandle dbhandle(this); 442c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch ExecOrDie(dbhandle.get(), "DELETE FROM cookies"); 443c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch} 444c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch 445c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdochbool UserSettings::GetLastUser(string* username) { 446c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch ScopedDBHandle dbhandle(this); 447c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch SQLStatement query; 448c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch query.prepare(dbhandle.get(), "SELECT email FROM cookies"); 449c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch if (SQLITE_ROW == query.step()) { 450c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch *username = query.column_string(0); 451c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch return true; 452c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch } else { 453c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch return false; 454c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch } 455c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch} 456c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch 457c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch} // namespace browser_sync 458