// Copyright (c) 2012 The Chromium Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. #include "chrome/browser/webdata/web_intents_table.h" #include #include "base/i18n/case_conversion.h" #include "base/logging.h" #include "base/string_util.h" #include "base/utf_string_conversions.h" #include "chrome/browser/intents/default_web_intent_service.h" #include "googleurl/src/gurl.h" #include "net/base/mime_util.h" #include "sql/statement.h" #include "third_party/sqlite/sqlite3.h" using webkit_glue::WebIntentServiceData; namespace { bool ExtractIntents(sql::Statement* s, std::vector* services) { DCHECK(s); if (!s->is_valid()) return false; while (s->Step()) { WebIntentServiceData service; service.action = s->ColumnString16(0); service.type = s->ColumnString16(1); service.scheme = s->ColumnString16(2); service.service_url = GURL(s->ColumnString16(3)); service.title = s->ColumnString16(4); // Default to window disposition. service.disposition = WebIntentServiceData::DISPOSITION_WINDOW; if (s->ColumnString16(5) == ASCIIToUTF16("inline")) service.disposition = WebIntentServiceData::DISPOSITION_INLINE; services->push_back(service); } return s->Succeeded(); } } // namespace WebIntentsTable::WebIntentsTable(sql::Connection* db, sql::MetaTable* meta_table) : WebDatabaseTable(db, meta_table) { } WebIntentsTable::~WebIntentsTable() { } bool WebIntentsTable::Init() { if (!db_->DoesTableExist("web_intents")) { if (!db_->Execute("CREATE TABLE web_intents (" " service_url LONGVARCHAR," " action VARCHAR," " type VARCHAR," " title LONGVARCHAR," " disposition VARCHAR," " scheme VARCHAR," " UNIQUE (service_url, action, scheme, type))")) { return false; } if (!db_->Execute("CREATE INDEX IF NOT EXISTS web_intents_index" " ON web_intents (action)")) return false; if (!db_->Execute("CREATE INDEX IF NOT EXISTS web_intents_index" " ON web_intents (scheme)")) return false; } if (!db_->DoesTableExist("web_intents_defaults")) { if (!db_->Execute("CREATE TABLE web_intents_defaults (" " action VARCHAR," " type VARCHAR," " url_pattern LONGVARCHAR," " user_date INTEGER," " suppression INTEGER," " service_url LONGVARCHAR," " scheme VARCHAR," " UNIQUE (action, scheme, type, url_pattern))")) { return false; } if (!db_->Execute("CREATE INDEX IF NOT EXISTS web_intents_default_index" " ON web_intents_defaults (action)")) return false; if (!db_->Execute("CREATE INDEX IF NOT EXISTS web_intents_default_index" " ON web_intents_defaults (scheme)")) return false; } return true; } // TODO(jhawkins): Figure out Sync story. bool WebIntentsTable::IsSyncable() { return false; } // Updates the table by way of renaming the old tables, rerunning // the Init method, then selecting old values into the new tables. bool WebIntentsTable::MigrateToVersion46AddSchemeColumn() { if (!db_->Execute("ALTER TABLE web_intents RENAME TO old_web_intents")) { DLOG(WARNING) << "Could not backup web_intents table."; return false; } if (!db_->Execute("ALTER TABLE web_intents_defaults" " RENAME TO old_web_intents_defaults")) { DLOG(WARNING) << "Could not backup web_intents_defaults table."; return false; } if (!Init()) return false; int error = db_->ExecuteAndReturnErrorCode( "INSERT INTO web_intents" " (service_url, action, type, title, disposition)" " SELECT " " service_url, action, type, title, disposition" " FROM old_web_intents"); if (error != SQLITE_OK) { DLOG(WARNING) << "Could not copy old intent data to upgraded table." << db_->GetErrorMessage(); } error = db_->ExecuteAndReturnErrorCode( "INSERT INTO web_intents_defaults" " (service_url, action, type, url_pattern, user_date, suppression)" " SELECT " " service_url, action, type, url_pattern, user_date, suppression" " FROM old_web_intents_defaults"); if (error != SQLITE_OK) { DLOG(WARNING) << "Could not copy old intent defaults to upgraded table." << db_->GetErrorMessage(); } if (!db_->Execute("DROP table old_web_intents")) { LOG(WARNING) << "Could not drop backup web_intents table."; return false; } if (!db_->Execute("DROP table old_web_intents_defaults")) { DLOG(WARNING) << "Could not drop backup web_intents_defaults table."; return false; } return true; } bool WebIntentsTable::GetWebIntentServices( const string16& action, std::vector* services) { DCHECK(services); sql::Statement s(db_->GetUniqueStatement( "SELECT action, type, scheme, service_url, title, disposition" " FROM web_intents" " WHERE action=?")); s.BindString16(0, action); return ExtractIntents(&s, services); } bool WebIntentsTable::GetWebIntentServicesForScheme( const string16& scheme, std::vector* services) { DCHECK(services); sql::Statement s(db_->GetUniqueStatement( "SELECT action, type, scheme, service_url, title, disposition" " FROM web_intents" " WHERE scheme=?")); s.BindString16(0, scheme); return ExtractIntents(&s, services); } // TODO(gbillock): This currently does a full-table scan. Eventually we will // store registrations by domain, and so have an indexed origin. At that time, // this should just change to do lookup by origin instead of URL. bool WebIntentsTable::GetWebIntentServicesForURL( const string16& service_url, std::vector* services) { DCHECK(services); sql::Statement s(db_->GetUniqueStatement( "SELECT action, type, scheme, service_url, title, disposition" " FROM web_intents" " WHERE service_url=?")); s.BindString16(0, service_url); return ExtractIntents(&s, services); } bool WebIntentsTable::GetAllWebIntentServices( std::vector* services) { DCHECK(services); sql::Statement s(db_->GetUniqueStatement( "SELECT action, type, scheme, service_url, title, disposition" " FROM web_intents")); return ExtractIntents(&s, services); } bool WebIntentsTable::SetWebIntentService(const WebIntentServiceData& service) { sql::Statement s(db_->GetUniqueStatement( "INSERT OR REPLACE INTO web_intents " "(action, type, scheme, service_url, title, disposition) " "VALUES (?, ?, ?, ?, ?, ?)")); // Default to window disposition. string16 disposition = ASCIIToUTF16("window"); if (service.disposition == WebIntentServiceData::DISPOSITION_INLINE) disposition = ASCIIToUTF16("inline"); s.BindString16(0, service.action); s.BindString16(1, service.type); s.BindString16(2, service.scheme); s.BindString(3, service.service_url.spec()); s.BindString16(4, service.title); s.BindString16(5, disposition); return s.Run(); } // TODO(jhawkins): Investigate the need to remove rows matching only // |service.service_url|. It's unlikely the user will be given the ability to // remove at the granularity of actions or types. bool WebIntentsTable::RemoveWebIntentService( const WebIntentServiceData& service) { sql::Statement s(db_->GetUniqueStatement( "DELETE FROM web_intents " "WHERE action = ? AND type = ? AND scheme = ? AND service_url = ?")); s.BindString16(0, service.action); s.BindString16(1, service.type); s.BindString16(2, service.scheme); s.BindString(3, service.service_url.spec()); return s.Run(); } bool WebIntentsTable::GetDefaultServices( const string16& action, std::vector* default_services) { sql::Statement s(db_->GetUniqueStatement( "SELECT action, type, url_pattern, user_date, suppression, " "service_url FROM web_intents_defaults " "WHERE action=?")); s.BindString16(0, action); while (s.Step()) { DefaultWebIntentService entry; entry.action = s.ColumnString16(0); entry.type = s.ColumnString16(1); if (entry.url_pattern.Parse(s.ColumnString(2)) != URLPattern::PARSE_SUCCESS) { return false; } entry.user_date = s.ColumnInt(3); entry.suppression = s.ColumnInt64(4); entry.service_url = s.ColumnString(5); default_services->push_back(entry); } return s.Succeeded(); } bool WebIntentsTable::GetAllDefaultServices( std::vector* default_services) { sql::Statement s(db_->GetUniqueStatement( "SELECT action, type, url_pattern, user_date, suppression, " "service_url FROM web_intents_defaults")); while (s.Step()) { DefaultWebIntentService entry; entry.action = s.ColumnString16(0); entry.type = s.ColumnString16(1); if (entry.url_pattern.Parse(s.ColumnString(2)) != URLPattern::PARSE_SUCCESS) { return false; } entry.user_date = s.ColumnInt(3); entry.suppression = s.ColumnInt64(4); entry.service_url = s.ColumnString(5); default_services->push_back(entry); } return s.Succeeded(); } bool WebIntentsTable::SetDefaultService( const DefaultWebIntentService& default_service) { sql::Statement s(db_->GetUniqueStatement( "INSERT OR REPLACE INTO web_intents_defaults " "(action, type, url_pattern, user_date, suppression," " service_url, scheme) " "VALUES (?, ?, ?, ?, ?, ?, ?)")); s.BindString16(0, default_service.action); s.BindString16(1, default_service.type); s.BindString(2, default_service.url_pattern.GetAsString()); s.BindInt(3, default_service.user_date); s.BindInt64(4, default_service.suppression); s.BindString(5, default_service.service_url); s.BindString16(6, default_service.scheme); return s.Run(); } bool WebIntentsTable::RemoveDefaultService( const DefaultWebIntentService& default_service) { sql::Statement s(db_->GetUniqueStatement( "DELETE FROM web_intents_defaults " "WHERE action = ? AND type = ? AND url_pattern = ?")); s.BindString16(0, default_service.action); s.BindString16(1, default_service.type); s.BindString(2, default_service.url_pattern.GetAsString()); return s.Run(); } bool WebIntentsTable::RemoveServiceDefaults(const GURL& service_url) { sql::Statement s(db_->GetUniqueStatement( "DELETE FROM web_intents_defaults WHERE service_url = ?")); s.BindString(0, service_url.spec()); return s.Run(); }