diff options
Diffstat (limited to 'chrome/browser/webdata/web_database.cc')
-rw-r--r-- | chrome/browser/webdata/web_database.cc | 873 |
1 files changed, 873 insertions, 0 deletions
diff --git a/chrome/browser/webdata/web_database.cc b/chrome/browser/webdata/web_database.cc new file mode 100644 index 0000000..60b6b82 --- /dev/null +++ b/chrome/browser/webdata/web_database.cc @@ -0,0 +1,873 @@ +// Copyright 2008, Google Inc. +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +#include <algorithm> +#include <limits> + +#include "chrome/browser/webdata/web_database.h" + +#include "base/gfx/png_decoder.h" +#include "base/gfx/png_encoder.h" +#include "base/string_util.h" +#include "base/time.h" +#include "base/values.h" +#include "chrome/browser/history/history_database.h" +#include "chrome/browser/ie7_password.h" +#include "chrome/browser/template_url.h" +#include "chrome/browser/encryptor.h" +#include "chrome/common/scoped_vector.h" +#include "webkit/glue/password_form.h" + +//////////////////////////////////////////////////////////////////////////////// +// +// Schema +// +// keywords Most of the columns mirror that of a field in +// TemplateURL. See TemplateURL for more details. +// id +// short_name +// keyword +// favicon_url +// url +// show_in_default_list +// safe_for_autoreplace +// originating_url +// date_created This column was added after we allowed keywords. +// Keywords created before we started tracking +// creation date have a value of 0 for this. +// usage_count +// input_encodings Semicolon separated list of supported input +// encodings, may be empty. +// suggest_url +// prepopulate_id See TemplateURL::prepoulate_id. +// +// logins +// origin_url +// action_url +// username_element +// username_value +// password_element +// password_value +// submit_element +// signon_realm The authority (scheme, host, port). +// ssl_valid SSL status of page containing the form at first +// impression. +// preferred MRU bit. +// date_created This column was added after logins support. "Legacy" +// entries have a value of 0. +// blacklisted_by_user Tracks whether or not the user opted to 'never +// remember' +// passwords for this site. +// +// web_app_icons +// url URL of the web app. +// width Width of the image. +// height Height of the image. +// image PNG encoded image data. +// +// web_apps +// url URL of the web app. +// has_all_images Do we have all the images? +// +//////////////////////////////////////////////////////////////////////////////// + +// Current version number. +static const int kCurrentVersionNumber = 20; + +// Keys used in the meta table. +static const char* kDefaultSearchProviderKey = "Default Search Provider ID"; +static const char* kBuiltinKeywordVersion = "Builtin Keyword Version"; + +std::string JoinStrings(const std::string& separator, + const std::vector<std::string>& strings) { + if (strings.empty()) + return std::string(); + std::vector<std::string>::const_iterator i(strings.begin()); + std::string result(*i); + while(++i != strings.end()) + result += separator + *i; + return result; +} + +WebDatabase::WebDatabase() : db_(NULL), transaction_nesting_(0) { +} + +WebDatabase::~WebDatabase() { + if (db_) { + DCHECK(transaction_nesting_ == 0) << + "Forgot to close the transaction on shutdown"; + sqlite3_close(db_); + db_ = NULL; + } +} + +void WebDatabase::BeginTransaction() { + DCHECK(db_); + if (transaction_nesting_ == 0) { + int rv = sqlite3_exec(db_, "BEGIN TRANSACTION", NULL, NULL, NULL); + DCHECK(rv == SQLITE_OK) << "Failed to begin transaction"; + } + transaction_nesting_++; +} + +void WebDatabase::CommitTransaction() { + DCHECK(db_); + DCHECK(transaction_nesting_ > 0) << "Committing too many transaction"; + transaction_nesting_--; + if (transaction_nesting_ == 0) { + int rv = sqlite3_exec(db_, "COMMIT", NULL, NULL, NULL); + DCHECK(rv == SQLITE_OK) << "Failed to commit transaction"; + } +} + +bool WebDatabase::Init(const std::wstring& db_name) { + // Open the database, using the narrow version of open so that + // the DB is in UTF-8. + if (sqlite3_open(WideToUTF8(db_name).c_str(), &db_) != SQLITE_OK) { + LOG(WARNING) << "Unable to open the web database."; + return false; + } + + // We don't store that much data in the tables so use a small page size. + // This provides a large benefit for empty tables (which is very likely with + // the tables we create). + sqlite3_exec(db_, "PRAGMA page_size=2048", NULL, NULL, NULL); + + // We shouldn't have much data and what access we currently have is quite + // infrequent. So we go with a small cache size. + sqlite3_exec(db_, "PRAGMA cache_size=32", NULL, NULL, NULL); + + // Run the database in exclusive mode. Nobody else should be accessing the + // database while we're running, and this will give somewhat improved perf. + sqlite3_exec(db_, "PRAGMA locking_mode=EXCLUSIVE", NULL, NULL, NULL); + + // Initialize various tables + SQLTransaction transaction(db_); + transaction.Begin(); + + // Version check. + if (!meta_table_.Init(std::string(), kCurrentVersionNumber, db_)) + return false; + if (meta_table_.GetCompatibleVersionNumber() > kCurrentVersionNumber) { + LOG(WARNING) << "Web database is too new"; + return false; + } + + // Initialize the tables. + if (!InitKeywordsTable() || !InitLoginsTable() || !InitWebAppIconsTable() || + !InitWebAppsTable()) { + LOG(WARNING) << "Unable to initialize the web database."; + return false; + } + int cur_version = meta_table_.GetVersionNumber(); + + // Put migration code here. + + // 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_IF(WARNING, cur_version < kCurrentVersionNumber) << + "Web database version " << cur_version << " is too old to handle."; + + return (transaction.Commit() == SQLITE_OK); +} + +bool WebDatabase::SetWebAppImage(const GURL& url, + const SkBitmap& image) { + SQLStatement s; + if (s.prepare(db_, + "INSERT OR REPLACE INTO web_app_icons " + "(url, width, height, image) VALUES (?, ?, ?, ?)") + != SQLITE_OK) { + NOTREACHED() << "Statement prepare failed"; + return false; + } + + std::vector<unsigned char> image_data; + + SkAutoLockPixels pixel_lock(image); + PNGEncoder::Encode(reinterpret_cast<unsigned char*>(image.getPixels()), + PNGEncoder::FORMAT_BGRA, image.width(), + image.height(), image.width() * 4, false, &image_data); + + s.bind_string(0, history::HistoryDatabase::GURLToDatabaseURL(url)); + s.bind_int(1, image.width()); + s.bind_int(2, image.height()); + s.bind_blob(3, &image_data.front(), static_cast<int>(image_data.size())); + return s.step() == SQLITE_DONE; +} + +bool WebDatabase::GetWebAppImages(const GURL& url, + std::vector<SkBitmap>* images) { + SQLStatement s; + if (s.prepare(db_, "SELECT image FROM web_app_icons WHERE url=?") != + SQLITE_OK) { + NOTREACHED() << "Statement prepare failed"; + return false; + } + s.bind_string(0, history::HistoryDatabase::GURLToDatabaseURL(url)); + while (s.step() == SQLITE_ROW) { + SkBitmap image; + std::vector<unsigned char> image_data; + s.column_blob_as_vector(0, &image_data); + if (PNGDecoder::Decode(&image_data, &image)) { + images->push_back(image); + } else { + // Should only have valid image data in the db. + NOTREACHED(); + } + } + return true; +} + +bool WebDatabase::SetWebAppHasAllImages(const GURL& url, + bool has_all_images) { + SQLStatement s; + if (s.prepare(db_, "INSERT OR REPLACE INTO web_apps (url, has_all_images) " + "VALUES (?, ?)") != SQLITE_OK) { + NOTREACHED() << "Statement prepare failed"; + return false; + } + s.bind_string(0, history::HistoryDatabase::GURLToDatabaseURL(url)); + s.bind_int(1, has_all_images ? 1 : 0); + return (s.step() == SQLITE_DONE); +} + +bool WebDatabase::GetWebAppHasAllImages(const GURL& url) { + SQLStatement s; + if (s.prepare(db_, "SELECT has_all_images FROM web_apps " + "WHERE url=?") != SQLITE_OK) { + NOTREACHED() << "Statement prepare failed"; + return false; + } + s.bind_string(0, history::HistoryDatabase::GURLToDatabaseURL(url)); + return (s.step() == SQLITE_ROW && s.column_int(0) == 1); +} + +bool WebDatabase::RemoveWebApp(const GURL& url) { + SQLStatement delete_s; + if (delete_s.prepare(db_, "DELETE FROM web_app_icons WHERE url = ?") != + SQLITE_OK) { + NOTREACHED() << "Statement prepare failed"; + return false; + } + delete_s.bind_string(0, history::HistoryDatabase::GURLToDatabaseURL(url)); + if (delete_s.step() != SQLITE_DONE) + return false; + + SQLStatement delete_s2; + if (delete_s2.prepare(db_, "DELETE FROM web_apps WHERE url = ?") != + SQLITE_OK) { + NOTREACHED() << "Statement prepare failed"; + return false; + } + delete_s2.bind_string(0, history::HistoryDatabase::GURLToDatabaseURL(url)); + return (delete_s2.step() == SQLITE_DONE); +} + +bool WebDatabase::InitKeywordsTable() { + if (!DoesSqliteTableExist(db_, "keywords")) { + if (sqlite3_exec(db_, "CREATE TABLE keywords (" + "id INTEGER PRIMARY KEY," + "short_name VARCHAR NOT NULL," + "keyword VARCHAR NOT NULL," + "favicon_url VARCHAR NOT NULL," + "url VARCHAR NOT NULL," + "show_in_default_list INTEGER," + "safe_for_autoreplace INTEGER," + "originating_url VARCHAR," + "date_created INTEGER DEFAULT 0," + "usage_count INTEGER DEFAULT 0," + "input_encodings VARCHAR," + "suggest_url VARCHAR," + "prepopulate_id INTEGER DEFAULT 0)", + NULL, NULL, NULL) != SQLITE_OK) { + NOTREACHED(); + return false; + } + } + return true; +} + +bool WebDatabase::InitLoginsTable() { + if (!DoesSqliteTableExist(db_, "logins")) { + // First time + if (sqlite3_exec(db_, "CREATE TABLE logins (" + "origin_url VARCHAR NOT NULL, " + "action_url VARCHAR, " + "username_element VARCHAR, " + "username_value VARCHAR, " + "password_element VARCHAR, " + "password_value BLOB, " + "submit_element VARCHAR, " + "signon_realm VARCHAR NOT NULL," + "ssl_valid INTEGER NOT NULL," + "preferred INTEGER NOT NULL," + "date_created INTEGER NOT NULL," + "blacklisted_by_user INTEGER NOT NULL," + "scheme INTEGER NOT NULL," + "UNIQUE " + "(origin_url, username_element, " + "username_value, password_element, " + "submit_element, signon_realm))", + NULL, NULL, NULL) != SQLITE_OK) { + NOTREACHED(); + return false; + } + if (sqlite3_exec(db_, "CREATE INDEX logins_signon ON " + "logins (signon_realm)", + NULL, NULL, NULL) != SQLITE_OK) { + NOTREACHED(); + return false; + } + } + + if (!DoesSqliteTableExist(db_, "ie7_logins")) { + // First time + if (sqlite3_exec(db_, "CREATE TABLE ie7_logins (" + "url_hash VARCHAR NOT NULL, " + "password_value BLOB, " + "date_created INTEGER NOT NULL," + "UNIQUE " + "(url_hash))", + NULL, NULL, NULL) != SQLITE_OK) { + NOTREACHED(); + return false; + } + if (sqlite3_exec(db_, "CREATE INDEX ie7_logins_hash ON " + "ie7_logins (url_hash)", + NULL, NULL, NULL) != SQLITE_OK) { + NOTREACHED(); + return false; + } + } + return true; +} + +bool WebDatabase::InitWebAppIconsTable() { + if (!DoesSqliteTableExist(db_, "web_app_icons")) { + if (sqlite3_exec(db_, "CREATE TABLE web_app_icons (" + "url LONGVARCHAR," + "width int," + "height int," + "image BLOB, UNIQUE (url, width, height))", + NULL, NULL, NULL) != SQLITE_OK) { + NOTREACHED(); + return false; + } + } + return true; +} + +bool WebDatabase::InitWebAppsTable() { + if (!DoesSqliteTableExist(db_, "web_apps")) { + if (sqlite3_exec(db_, "CREATE TABLE web_apps (" + "url LONGVARCHAR UNIQUE," + "has_all_images INTEGER NOT NULL)", + NULL, NULL, NULL) != SQLITE_OK) { + NOTREACHED(); + return false; + } + if (sqlite3_exec(db_, "CREATE INDEX web_apps_url_index ON " + "web_apps (url)", NULL, NULL, NULL) != SQLITE_OK) { + NOTREACHED(); + return false; + } + } + return true; +} + +static void BindURLToStatement(const TemplateURL& url, SQLStatement* s) { + s->bind_wstring(0, url.short_name()); + s->bind_wstring(1, url.keyword()); + GURL favicon_url = url.GetFavIconURL(); + if (!favicon_url.is_valid()) { + s->bind_string(2, ""); + } else { + s->bind_string(2, history::HistoryDatabase::GURLToDatabaseURL( + url.GetFavIconURL())); + } + if (url.url()) + s->bind_wstring(3, url.url()->url()); + else + s->bind_wstring(3, std::wstring()); + s->bind_int(4, url.safe_for_autoreplace() ? 1 : 0); + if (!url.originating_url().is_valid()) { + s->bind_string(5, std::string()); + } else { + s->bind_string(5, history::HistoryDatabase::GURLToDatabaseURL( + url.originating_url())); + } + s->bind_int64(6, url.date_created().ToTimeT()); + s->bind_int(7, url.usage_count()); + s->bind_string(8, JoinStrings(";", url.input_encodings())); + s->bind_int(9, url.show_in_default_list() ? 1 : 0); + if (url.suggestions_url()) + s->bind_wstring(10, url.suggestions_url()->url()); + else + s->bind_wstring(10, std::wstring()); + s->bind_int(11, url.prepopulate_id()); +} + +bool WebDatabase::AddKeyword(const TemplateURL& url) { + DCHECK(url.id()); + SQLStatement s; + if (s.prepare(db_, + "INSERT INTO keywords " + "(short_name, keyword, favicon_url, url, safe_for_autoreplace, " + "originating_url, date_created, usage_count, input_encodings, " + "show_in_default_list, suggest_url, prepopulate_id, id) " + "VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)") + != SQLITE_OK) { + NOTREACHED() << "Statement prepare failed"; + return false; + } + BindURLToStatement(url, &s); + s.bind_int64(12, url.id()); + if (s.step() != SQLITE_DONE) { + NOTREACHED(); + return false; + } + return true; +} + +bool WebDatabase::RemoveKeyword(TemplateURL::IDType id) { + DCHECK(id); + SQLStatement s; + if (s.prepare(db_, + "DELETE FROM keywords WHERE id = ?") != SQLITE_OK) { + NOTREACHED() << "Statement prepare failed"; + return false; + } + s.bind_int64(0, id); + return s.step() == SQLITE_DONE; +} + +bool WebDatabase::GetKeywords(std::vector<TemplateURL*>* urls) { + SQLStatement s; + if (s.prepare(db_, + "SELECT id, short_name, keyword, favicon_url, url, " + "safe_for_autoreplace, originating_url, date_created, " + "usage_count, input_encodings, show_in_default_list, " + "suggest_url, prepopulate_id " + "FROM keywords ORDER BY id ASC") != SQLITE_OK) { + NOTREACHED() << "Statement prepare failed"; + return false; + } + int64 result; + while ((result = s.step()) == SQLITE_ROW) { + TemplateURL* template_url = new TemplateURL(); + std::wstring tmp; + template_url->set_id(s.column_int64(0)); + + s.column_string16(1, &tmp); + DCHECK(!tmp.empty()); + template_url->set_short_name(tmp); + + s.column_string16(2, &tmp); + template_url->set_keyword(tmp); + + s.column_string16(3, &tmp); + if (!tmp.empty()) + template_url->SetFavIconURL(GURL(tmp)); + + s.column_string16(4, &tmp); + template_url->SetURL(tmp, 0, 0); + + template_url->set_safe_for_autoreplace(s.column_int(5) == 1); + + s.column_string16(6, &tmp); + if (!tmp.empty()) + template_url->set_originating_url(GURL(tmp)); + + template_url->set_date_created(Time::FromTimeT(s.column_int64(7))); + + template_url->set_usage_count(s.column_int(8)); + + std::vector<std::string> encodings; + SplitString(s.column_string(9), ';', &encodings); + template_url->set_input_encodings(encodings); + + template_url->set_show_in_default_list(s.column_int(10) == 1); + + s.column_string16(11, &tmp); + template_url->SetSuggestionsURL(tmp, 0, 0); + + template_url->set_prepopulate_id(s.column_int(12)); + + urls->push_back(template_url); + } + return result == SQLITE_DONE; +} + +bool WebDatabase::UpdateKeyword(const TemplateURL& url) { + DCHECK(url.id()); + SQLStatement s; + if (s.prepare(db_, + "UPDATE keywords " + "SET short_name=?, keyword=?, favicon_url=?, url=?," + "safe_for_autoreplace=?, originating_url=?, " + "date_created=?, usage_count=?, input_encodings=?, " + "show_in_default_list=?, suggest_url=?, prepopulate_id=? " + "WHERE id=?") + != SQLITE_OK) { + NOTREACHED() << "Statement prepare failed"; + return false; + } + BindURLToStatement(url, &s); + s.bind_int64(12, url.id()); + return s.step() == SQLITE_DONE; +} + +bool WebDatabase::SetDefaultSearchProviderID(int64 id) { + return meta_table_.SetValue(kDefaultSearchProviderKey, id); +} + +int64 WebDatabase::GetDefaulSearchProviderID() { + int64 value = 0; + meta_table_.GetValue(kDefaultSearchProviderKey, &value); + return value; +} + +bool WebDatabase::SetBuitinKeywordVersion(int version) { + return meta_table_.SetValue(kBuiltinKeywordVersion, version); +} + +int WebDatabase::GetBuitinKeywordVersion() { + int version = 0; + meta_table_.GetValue(kBuiltinKeywordVersion, &version); + return version; +} + +// Return a new GURL like url, but without any "#foo" bit on the end. +static GURL GURLWithoutRef(const GURL& url) { + url_canon::Replacements<char> replacements; + replacements.ClearRef(); + return url.ReplaceComponents(replacements); +} + +// Convert a list of GUIDs from the in-memory form to the form we keep in +// the database (tab-separated string). +static std::string SerializeGUIDs(const std::vector<std::string>& guids) { + std::string result; + for (size_t i = 0; i < guids.size(); ++i) { + if (!result.empty()) + result.push_back('\t'); + const std::string& guid = guids[i]; + for (size_t j = 0; j < guid.size(); ++j) { + char ch = guid[j]; + // If we have any embedded tabs in the GUID (a pathological case), + // we normalize them to spaces. + if (ch == '\t') + ch = ' '; + result.push_back(ch); + } + } + return result; +} + +// The partner of SerializeGUIDs. Converts a serialized GUIDs string +// back to a vector. +static void DeserializeGUIDs(const std::string& str, + std::vector<std::string>* guids) { + SplitString(str, '\t', guids); +} + +bool WebDatabase::AddLogin(const PasswordForm& form) { + SQLStatement s; + std::string encrypted_password; + if (s.prepare(db_, + "INSERT OR REPLACE INTO logins " + "(origin_url, action_url, username_element, username_value, " + " password_element, password_value, submit_element, " + " signon_realm, ssl_valid, preferred, date_created, " + " blacklisted_by_user, scheme) " + "VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)") != SQLITE_OK) { + NOTREACHED() << "Statement prepare failed"; + return false; + } + + s.bind_string(0, form.origin.spec()); + s.bind_string(1, form.action.spec()); + s.bind_wstring(2, form.username_element); + s.bind_wstring(3, form.username_value); + s.bind_wstring(4, form.password_element); + Encryptor::EncryptWideString(form.password_value, &encrypted_password); + s.bind_blob(5, encrypted_password.data(), + static_cast<int>(encrypted_password.length())); + s.bind_wstring(6, form.submit_element); + s.bind_string(7, form.signon_realm); + s.bind_int(8, form.ssl_valid); + s.bind_int(9, form.preferred); + s.bind_int64(10, form.date_created.ToTimeT()); + s.bind_int(11, form.blacklisted_by_user); + s.bind_int(12, form.scheme); + if (s.step() != SQLITE_DONE) { + NOTREACHED(); + return false; + } + return true; +} + +bool WebDatabase::AddIE7Login(const IE7PasswordInfo& info) { + SQLStatement s; + if (s.prepare(db_, + "INSERT OR REPLACE INTO ie7_logins " + "(url_hash, password_value, date_created) " + "VALUES (?, ?, ?)") != SQLITE_OK) { + NOTREACHED() << "Statement prepare failed"; + return false; + } + + s.bind_wstring(0, info.url_hash); + s.bind_blob(1, &info.encrypted_data.front(), + static_cast<int>(info.encrypted_data.size())); + s.bind_int64(2, info.date_created.ToTimeT()); + if (s.step() != SQLITE_DONE) { + NOTREACHED(); + return false; + } + return true; +} + +bool WebDatabase::UpdateLogin(const PasswordForm& form) { + SQLStatement s; + std::string encrypted_password; + if (s.prepare(db_, "UPDATE logins SET " + "action_url = ?, " + "password_value = ?, " + "ssl_valid = ?, " + "preferred = ? " + "WHERE origin_url = ? AND " + "username_element = ? AND " + "username_value = ? AND " + "password_element = ? AND " + "signon_realm = ?") != SQLITE_OK) { + NOTREACHED() << "Statement prepare failed"; + return false; + } + + s.bind_string(0, form.action.spec()); + Encryptor::EncryptWideString(form.password_value, &encrypted_password); + s.bind_blob(1, encrypted_password.data(), + static_cast<int>(encrypted_password.length())); + s.bind_int(2, form.ssl_valid); + s.bind_int(3, form.preferred); + s.bind_string(4, form.origin.spec()); + s.bind_wstring(5, form.username_element); + s.bind_wstring(6, form.username_value); + s.bind_wstring(7, form.password_element); + s.bind_string(8, form.signon_realm); + + if (s.step() != SQLITE_DONE) { + NOTREACHED(); + return false; + } + return true; +} + +bool WebDatabase::RemoveLogin(const PasswordForm& form) { + SQLStatement s; + // Remove a login by UNIQUE-constrained fields. + if (s.prepare(db_, + "DELETE FROM logins WHERE " + "origin_url = ? AND " + "username_element = ? AND " + "username_value = ? AND " + "password_element = ? AND " + "submit_element = ? AND " + "signon_realm = ? ") != SQLITE_OK) { + NOTREACHED() << "Statement prepare failed"; + return false; + } + s.bind_string(0, form.origin.spec()); + s.bind_wstring(1, form.username_element); + s.bind_wstring(2, form.username_value); + s.bind_wstring(3, form.password_element); + s.bind_wstring(4, form.submit_element); + s.bind_string(5, form.signon_realm); + + if (s.step() != SQLITE_DONE) { + NOTREACHED(); + return false; + } + return true; +} + +bool WebDatabase::RemoveIE7Login(const IE7PasswordInfo& info) { + SQLStatement s; + // Remove a login by UNIQUE-constrained fields. + if (s.prepare(db_, + "DELETE FROM ie7_logins WHERE " + "url_hash = ?") != SQLITE_OK) { + NOTREACHED() << "Statement prepare failed"; + return false; + } + s.bind_wstring(0, info.url_hash); + + if (s.step() != SQLITE_DONE) { + NOTREACHED(); + return false; + } + return true; +} + +bool WebDatabase::RemoveLoginsCreatedBetween(const Time delete_begin, + const Time delete_end) { + SQLStatement s1; + if (s1.prepare(db_, + "DELETE FROM logins WHERE " + "date_created >= ? AND date_created < ?") != SQLITE_OK) { + NOTREACHED() << "Statement 1 prepare failed"; + return false; + } + s1.bind_int64(0, delete_begin.ToTimeT()); + s1.bind_int64(1, + delete_end.is_null() ? + std::numeric_limits<int64>::max() : + delete_end.ToTimeT()); + + SQLStatement s2; + if (s2.prepare(db_, + "DELETE FROM ie7_logins WHERE " + "date_created >= ? AND date_created < ?") != SQLITE_OK) { + NOTREACHED() << "Statement 2 prepare failed"; + return false; + } + s2.bind_int64(0, delete_begin.ToTimeT()); + s2.bind_int64(1, + delete_end.is_null() ? + std::numeric_limits<int64>::max() : + delete_end.ToTimeT()); + + return s1.step() == SQLITE_DONE && s2.step() == SQLITE_DONE; +} + +static void InitPasswordFormFromStatement(PasswordForm* form, + SQLStatement* s) { + std::string encrypted_password; + std::string tmp; + s->column_string(0, &tmp); + form->origin = GURL(tmp); + s->column_string(1, &tmp); + form->action = GURL(tmp); + s->column_string16(2, &form->username_element); + s->column_string16(3, &form->username_value); + s->column_string16(4, &form->password_element); + s->column_blob_as_string(5, &encrypted_password); + Encryptor::DecryptWideString(encrypted_password, &form->password_value); + s->column_string16(6, &form->submit_element); + s->column_string(7, &tmp); + form->signon_realm = tmp; + form->ssl_valid = (s->column_int(8) > 0); + form->preferred = (s->column_int(9) > 0); + form->date_created = Time::FromTimeT(s->column_int64(10)); + form->blacklisted_by_user = (s->column_int(11) > 0); + int scheme_int = s->column_int(12); + DCHECK((scheme_int >= 0) && (scheme_int <= PasswordForm::SCHEME_OTHER)); + form->scheme = static_cast<PasswordForm::Scheme>(scheme_int); +} + +bool WebDatabase::GetLogins(const PasswordForm& form, + std::vector<PasswordForm*>* forms) { + DCHECK(forms); + SQLStatement s; + if (s.prepare(db_, + "SELECT origin_url, action_url, " + "username_element, username_value, " + "password_element, password_value, " + "submit_element, signon_realm, " + "ssl_valid, preferred, " + "date_created, blacklisted_by_user, scheme FROM logins " + "WHERE signon_realm == ? ") != SQLITE_OK) { + NOTREACHED() << "Statement prepare failed"; + return false; + } + + s.bind_string(0, form.signon_realm); + + int64 result; + while ((result = s.step()) == SQLITE_ROW) { + PasswordForm* new_form = new PasswordForm(); + InitPasswordFormFromStatement(new_form, &s); + + forms->push_back(new_form); + } + return result == SQLITE_DONE; +} + +bool WebDatabase::GetIE7Login(const IE7PasswordInfo& info, + IE7PasswordInfo* result) { + DCHECK(result); + SQLStatement s; + if (s.prepare(db_, + "SELECT password_value, date_created FROM ie7_logins " + "WHERE url_hash == ? ") != SQLITE_OK) { + NOTREACHED() << "Statement prepare failed"; + return false; + } + + s.bind_wstring(0, info.url_hash); + + int64 query_result = s.step(); + if (query_result == SQLITE_ROW) { + s.column_blob_as_vector(0, &result->encrypted_data); + result->date_created = Time::FromTimeT(s.column_int64(1)); + result->url_hash = info.url_hash; + s.step(); + } + return query_result == SQLITE_DONE; +} + +bool WebDatabase::GetAllLogins(std::vector<PasswordForm*>* forms, + bool include_blacklisted) { + DCHECK(forms); + SQLStatement s; + std::string stmt = "SELECT origin_url, action_url, " + "username_element, username_value, " + "password_element, password_value, " + "submit_element, signon_realm, ssl_valid, preferred, " + "date_created, blacklisted_by_user, scheme FROM logins "; + if (!include_blacklisted) + stmt.append("WHERE blacklisted_by_user == 0 "); + stmt.append("ORDER BY origin_url"); + + if (s.prepare(db_, stmt.c_str()) != SQLITE_OK) { + NOTREACHED() << "Statement prepare failed"; + return false; + } + + int64 result; + while ((result = s.step()) == SQLITE_ROW) { + PasswordForm* new_form = new PasswordForm(); + InitPasswordFormFromStatement(new_form, &s); + + forms->push_back(new_form); + } + return result == SQLITE_DONE; +} |