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

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

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

Powered by Google App Engine
This is Rietveld 408576698