Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(178)

Side by Side Diff: chrome/browser/sync/util/user_settings.cc

Issue 194065: Initial commit of sync engine code to browser/sync.... (Closed) Base URL: svn://chrome-svn/chrome/trunk/src/
Patch Set: Fixes to gtest include path, reverted syncapi. Created 11 years, 3 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch | Annotate | Revision Log
Property Changes:
Added: svn:eol-style
+ LF
OLDNEW
(Empty)
1 // Copyright (c) 2009 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE entry.
4
5 // This class isn't pretty. It's just a step better than globals, which is what
6 // these were previously.
7
8 #include "chrome/browser/sync/util/user_settings.h"
9
10 #if defined(OS_WINDOWS)
11 #include <windows.h>
12 #endif
13
14 #include <string>
15 #include <limits>
16 #include <vector>
17
18 #include "base/file_util.h"
19 #include "base/string_util.h"
20 #include "chrome/browser/sync/syncable/directory_manager.h" // For migration.
21 #include "chrome/browser/sync/util/crypto_helpers.h"
22 #include "chrome/browser/sync/util/data_encryption.h"
23 #include "chrome/browser/sync/util/path_helpers.h"
24 #include "chrome/browser/sync/util/query_helpers.h"
25
26 using std::numeric_limits;
27 using std::string;
28 using std::vector;
29
30 using syncable::DirectoryManager;
31
32 namespace browser_sync {
33
34 static const char PASSWORD_HASH[] = "password_hash2";
35 static const char SALT[] = "salt2";
36
37 static const int kSaltSize = 20;
38 static const int kCurrentDBVersion = 11;
39
40 UserSettings::ScopedDBHandle::ScopedDBHandle(UserSettings* settings) :
41 mutex_lock_(&settings->dbhandle_mutex_), handle_(&settings->dbhandle_) {
42 }
43
44 UserSettings::UserSettings() :
45 dbhandle_(NULL) {
46 }
47
48 string UserSettings::email() const {
49 ScopedLock lock(&mutex_);
50 return email_;
51 }
52
53 static void MakeSigninsTable(sqlite3* const dbhandle) {
54 // Multiple email addresses can map to the same Google Account.
55 // This table keeps a map of sign-in email addresses to primary
56 // Google Account email addresses.
57 ExecOrDie(dbhandle, "CREATE TABLE signins"
58 " (signin, primary_email, "
59 " PRIMARY KEY(signin, primary_email) ON CONFLICT REPLACE)");
60 }
61
62 void UserSettings::MigrateOldVersionsAsNeeded(sqlite3* const handle,
63 int current_version) {
64 switch (current_version) {
65 // Versions 1-9 are unhandled. Version numbers greater than
66 // kCurrentDBVersion should have already been weeded out by the caller.
67 default:
68 // When the version is too old, we just try to continue anyway. There
69 // should not be a released product that makes a database too old for us
70 // to handle.
71 LOG(WARNING) << "UserSettings database version " << current_version <<
72 " is too old to handle.";
73 return;
74 case 10:
75 {
76 // Scrape the 'shares' table to find the syncable DB. 'shares'
77 // had a pair of string columns that mapped the username to the filename
78 // of the sync data sqlite3 file. Version 11 switched to a constant
79 // filename, so here we read the string, copy the file to the new name,
80 // delete the old one, and then drop the unused shares table.
81 ScopedStatement share_query(PrepareQuery(handle,
82 "SELECT share_name, file_name FROM shares"));
83 int query_result = sqlite3_step(share_query.get());
84 CHECK(SQLITE_ROW == query_result);
85 PathString share_name, file_name;
86 GetColumn(share_query.get(), 0, &share_name);
87 GetColumn(share_query.get(), 1, &file_name);
88
89 if (!file_util::Move(file_name,
90 DirectoryManager::GetSyncDataDatabaseFilename())) {
91 LOG(WARNING) << "Unable to upgrade UserSettings from v10";
92 return;
93 }
94 }
95 ExecOrDie(handle, "DROP TABLE shares");
96 ExecOrDie(handle, "UPDATE db_version SET version = 11");
97 // FALL THROUGH
98 case kCurrentDBVersion:
99 // Nothing to migrate.
100 return;
101 }
102 }
103
104 static void MakeCookiesTable(sqlite3* const dbhandle) {
105 // This table keeps a list of auth tokens for each signed in account. There
106 // will be as many rows as there are auth tokens per sign in.
107 // The service_token column will store encrypted values.
108 ExecOrDie(dbhandle, "CREATE TABLE cookies"
109 " (email, service_name, service_token, "
110 " PRIMARY KEY(email, service_name) ON CONFLICT REPLACE)");
111 }
112
113 static void MakeSigninTypesTable(sqlite3* const dbhandle) {
114 // With every successful gaia authentication, remember if it was
115 // a hosted domain or not.
116 ExecOrDie(dbhandle, "CREATE TABLE signin_types"
117 " (signin, signin_type, "
118 " PRIMARY KEY(signin, signin_type) ON CONFLICT REPLACE)");
119 }
120
121 static void MakeClientIDTable(sqlite3* const dbhandle) {
122 // Stores a single client ID value that can be used as the client id,
123 // if there's not another such ID provided on the install.
124 ExecOrDie(dbhandle, "CREATE TABLE client_id (id) ");
125 ExecOrDie(dbhandle, "INSERT INTO client_id values ( ? )",
126 Generate128BitRandomHexString());
127 }
128
129 bool UserSettings::Init(const PathString& settings_path) {
130 { // Scope the handle
131 ScopedDBHandle dbhandle(this);
132 if (dbhandle_)
133 sqlite3_close(dbhandle_);
134 CHECK(SQLITE_OK == SqliteOpen(settings_path.c_str(), &dbhandle_));
135 // In the worst case scenario, the user may hibernate his computer during
136 // one of our transactions.
137 sqlite3_busy_timeout(dbhandle_, numeric_limits<int>::max());
138
139 int sqlite_result = Exec(dbhandle.get(), "BEGIN EXCLUSIVE TRANSACTION");
140 CHECK(SQLITE_DONE == sqlite_result);
141 ScopedStatement table_query(PrepareQuery(dbhandle.get(),
142 "select count(*) from sqlite_master where type = 'table'"
143 " and name = 'db_version'"));
144 int query_result = sqlite3_step(table_query.get());
145 CHECK(SQLITE_ROW == query_result);
146 int table_count = 0;
147 GetColumn(table_query.get(), 0, &table_count);
148 table_query.reset(NULL);
149 if (table_count > 0) {
150 ScopedStatement version_query(PrepareQuery(dbhandle.get(),
151 "SELECT version FROM db_version"));
152 query_result = sqlite3_step(version_query.get());
153 CHECK(SQLITE_ROW == query_result);
154 const int version = sqlite3_column_int(version_query.get(), 0);
155 version_query.reset(NULL);
156 if (version > kCurrentDBVersion) {
157 LOG(WARNING) << "UserSettings database is too new.";
158 return false;
159 }
160
161 MigrateOldVersionsAsNeeded(dbhandle.get(), version);
162 } else {
163 // Create settings table.
164 ExecOrDie(dbhandle.get(), "CREATE TABLE settings"
165 " (email, key, value, "
166 " PRIMARY KEY(email, key) ON CONFLICT REPLACE)");
167
168 // Create and populate version table.
169 ExecOrDie(dbhandle.get(), "CREATE TABLE db_version ( version )");
170 ExecOrDie(dbhandle.get(), "INSERT INTO db_version values ( ? )",
171 kCurrentDBVersion);
172
173 MakeSigninsTable(dbhandle.get());
174 MakeCookiesTable(dbhandle.get());
175 MakeSigninTypesTable(dbhandle.get());
176 MakeClientIDTable(dbhandle.get());
177 }
178 ExecOrDie(dbhandle.get(), "COMMIT TRANSACTION");
179 }
180 #ifdef OS_WINDOWS
181 // Do not index this file. Scanning can occur every time we close the file,
182 // which causes long delays in SQLite's file locking.
183 const DWORD attrs = GetFileAttributes(settings_path.c_str());
184 const BOOL attrs_set =
185 SetFileAttributes(settings_path.c_str(),
186 attrs | FILE_ATTRIBUTE_NOT_CONTENT_INDEXED);
187 #endif
188 return true;
189 }
190
191
192 UserSettings::~UserSettings() {
193 if (dbhandle_)
194 sqlite3_close(dbhandle_);
195 }
196
197 const int32 kInvalidHash = 0xFFFFFFFF;
198
199 // We use 10 bits of data from the MD5 digest as the hash.
200 const int32 kHashMask = 0x3FF;
201
202 int32 GetHashFromDigest(const vector<uint8>& digest) {
203 int32 hash = 0;
204 int32 mask = kHashMask;
205 for (vector<uint8>::const_iterator i = digest.begin(); i != digest.end();
206 ++i) {
207 hash = hash << 8;
208 hash = hash | (*i & kHashMask);
209 mask = mask >> 8;
210 if (0 == mask)
211 break;
212 }
213 return hash;
214 }
215
216 void UserSettings::StoreEmailForSignin(const string& signin,
217 const string& primary_email) {
218 ScopedDBHandle dbhandle(this);
219 ExecOrDie(dbhandle.get(), "BEGIN TRANSACTION");
220 ScopedStatement query(PrepareQuery(dbhandle.get(),
221 "SELECT COUNT(*) FROM signins"
222 " WHERE signin = ? AND primary_email = ?",
223 signin, primary_email));
224 int query_result = sqlite3_step(query.get());
225 CHECK(SQLITE_ROW == query_result);
226 int32 count = 0;
227 GetColumn(query.get(), 0, &count);
228 query.reset(NULL);
229 if (0 == count) {
230 // Migrate any settings the user might have from earlier versions.
231 ExecOrDie(dbhandle.get(), "UPDATE settings SET email = ? WHERE email = ?",
232 primary_email, signin);
233 // Store this signin:email mapping.
234 ExecOrDie(dbhandle.get(), "INSERT INTO signins(signin, primary_email)"
235 " values ( ?, ? )", signin, primary_email);
236 }
237 ExecOrDie(dbhandle.get(), "COMMIT TRANSACTION");
238 }
239
240 bool UserSettings::GetEmailForSignin(/*in, out*/string* signin) {
241 ScopedDBHandle dbhandle(this);
242 string result;
243 ScopedStatement query(PrepareQuery(dbhandle.get(),
244 "SELECT primary_email FROM signins"
245 " WHERE signin = ?", *signin));
246 int query_result = sqlite3_step(query.get());
247 if (SQLITE_ROW == query_result) {
248 GetColumn(query.get(), 0, &result);
249 if (!result.empty()) {
250 swap(result, *signin);
251 return true;
252 }
253 }
254 return false;
255 }
256
257 void UserSettings::StoreHashedPassword(const string& email,
258 const string& password) {
259 // Save one-way hashed password:
260 char binary_salt[kSaltSize];
261 {
262 ScopedLock lock(&mutex_);
263 GetRandomBytes(binary_salt, sizeof(binary_salt));
264 }
265 const string salt = APEncode(string(binary_salt, sizeof(binary_salt)));
266 MD5Calculator md5;
267 md5.AddData(salt.data(), salt.size());
268 md5.AddData(password.data(), password.size());
269 ScopedDBHandle dbhandle(this);
270 ExecOrDie(dbhandle.get(), "BEGIN TRANSACTION");
271 ExecOrDie(dbhandle.get(), "INSERT INTO settings(email, key, value )"
272 " values ( ?, ?, ? )", email, PASSWORD_HASH,
273 GetHashFromDigest(md5.GetDigest()));
274 ExecOrDie(dbhandle.get(), "INSERT INTO settings(email, key, value )"
275 " values ( ?, ?, ? )", email, SALT, salt);
276 ExecOrDie(dbhandle.get(), "COMMIT TRANSACTION");
277 }
278
279 bool UserSettings::VerifyAgainstStoredHash(const string& email,
280 const string& password) {
281 ScopedDBHandle dbhandle(this);
282 string salt_and_digest;
283
284 ScopedStatement query(PrepareQuery(dbhandle.get(),
285 "SELECT key, value FROM settings"
286 " WHERE email = ? AND"
287 " (key = ? OR key = ?)",
288 email, PASSWORD_HASH, SALT));
289 int query_result = sqlite3_step(query.get());
290 string salt;
291 int32 hash = kInvalidHash;
292 while (SQLITE_ROW == query_result) {
293 string key;
294 GetColumn(query.get(), 0, &key);
295 if (key == SALT)
296 GetColumn(query.get(), 1, &salt);
297 else
298 GetColumn(query.get(), 1, &hash);
299 query_result = sqlite3_step(query.get());
300 }
301 CHECK(SQLITE_DONE == query_result);
302 if (salt.empty() || hash == kInvalidHash)
303 return false;
304 MD5Calculator md5;
305 md5.AddData(salt.data(), salt.size());
306 md5.AddData(password.data(), password.size());
307 return hash == GetHashFromDigest(md5.GetDigest());
308 }
309
310 void UserSettings::SwitchUser(const string& username) {
311 {
312 ScopedLock lock(&mutex_);
313 email_ = username;
314 }
315 }
316
317 void UserSettings::RememberSigninType(const string& signin, SignIn signin_type)
318 {
319 ScopedDBHandle dbhandle(this);
320 ExecOrDie(dbhandle.get(), "INSERT INTO signin_types(signin, signin_type)"
321 " values ( ?, ? )", signin, static_cast<int>(signin_type));
322 }
323
324 SignIn UserSettings::RecallSigninType(const string& signin, SignIn default_type)
325 {
326 ScopedDBHandle dbhandle(this);
327 ScopedStatement query(PrepareQuery(dbhandle.get(),
328 "SELECT signin_type from signin_types"
329 " WHERE signin = ?", signin));
330 int query_result = sqlite3_step(query.get());
331 if (SQLITE_ROW == query_result) {
332 int signin_type;
333 GetColumn(query.get(), 0, &signin_type);
334 return static_cast<SignIn>(signin_type);
335 }
336 return default_type;
337 }
338
339 string UserSettings::GetClientId() {
340 ScopedDBHandle dbhandle(this);
341 ScopedStatement query(PrepareQuery(dbhandle.get(),
342 "SELECT id FROM client_id"));
343 int query_result = sqlite3_step(query.get());
344 string client_id;
345 if (query_result == SQLITE_ROW)
346 GetColumn(query.get(), 0, &client_id);
347 return client_id;
348 }
349
350 } // namespace browser_sync
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698