Chromium Code Reviews| Index: chrome/browser/password_manager/login_database.cc |
| diff --git a/chrome/browser/password_manager/login_database.cc b/chrome/browser/password_manager/login_database.cc |
| index 43277284086113507ec2f82e0ab5d249c1e825d6..8c01bd59af9dc532e5ce01e59d076493fa3508c2 100644 |
| --- a/chrome/browser/password_manager/login_database.cc |
| +++ b/chrome/browser/password_manager/login_database.cc |
| @@ -7,14 +7,18 @@ |
| #include <algorithm> |
| #include <limits> |
| +#include "base/command_line.h" |
| #include "base/file_util.h" |
| #include "base/files/file_path.h" |
| #include "base/logging.h" |
| #include "base/metrics/histogram.h" |
| #include "base/pickle.h" |
| +#include "base/string_util.h" |
| #include "base/strings/string_number_conversions.h" |
| #include "base/strings/utf_string_conversions.h" |
| #include "base/time.h" |
| +#include "chrome/common/chrome_switches.h" |
| +#include "net/base/registry_controlled_domains/registry_controlled_domain.h" |
| #include "sql/statement.h" |
| #include "sql/transaction.h" |
| @@ -45,6 +49,13 @@ enum LoginTableColumns { |
| COLUMN_TIMES_USED |
| }; |
| +std::string GetRegistryControlledDomain(const std::string& signon_realm_str) { |
| + const GURL signon_realm(signon_realm_str); |
| + return net::registry_controlled_domains::GetDomainAndRegistry( |
| + signon_realm, |
| + net::registry_controlled_domains::INCLUDE_PRIVATE_REGISTRIES); |
| +} |
| + |
| } // namespace |
| LoginDatabase::LoginDatabase() { |
| @@ -100,6 +111,10 @@ bool LoginDatabase::Init(const base::FilePath& db_path) { |
| db_.Close(); |
| return false; |
| } |
| + |
| + public_suffix_domain_matching_ = CommandLine::ForCurrentProcess()->HasSwitch( |
| + switches::kEnablePasswordAutofillPublicSuffixDomainMatching); |
| + |
| return true; |
| } |
| @@ -357,19 +372,63 @@ bool LoginDatabase::GetLogins(const PasswordForm& form, |
| std::vector<PasswordForm*>* forms) const { |
| DCHECK(forms); |
| // You *must* change LoginTableColumns if this query changes. |
| - sql::Statement s(db_.GetCachedStatement(SQL_FROM_HERE, |
| - "SELECT origin_url, action_url, " |
| + const std::string sql_query = "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, password_type, possible_usernames, times_used " |
| - "FROM logins WHERE signon_realm == ? ")); |
| - s.BindString(0, form.signon_realm); |
| + "FROM logins WHERE signon_realm == ? "; |
| + sql::Statement s; |
| + if (public_suffix_domain_matching_) { |
| + const std::string extended_sql_query = |
| + sql_query + "OR signon_realm REGEXP ? "; |
| + // TODO(nyquist) Re-enable usage of cached statements if possible. |
| + // s.Assign(db_.GetCachedStatement(SQL_FROM_HERE, sql_query.c_str())); |
| + s.Assign(db_.GetUniqueStatement(extended_sql_query.c_str())); |
| + std::string domain = GetRegistryControlledDomain(form.signon_realm); |
| + // We need to escape ., - and _ in the domain. Since the domain has already |
|
palmer
2013/06/11 18:55:45
The comment doesn't match the code --- you don't e
nyquist
2013/06/11 23:54:43
I don't think _ has to be escaped in regexp. Updat
|
| + // been sanitized using GURL, we do not need to escape any other characters. |
| + ReplaceChars(domain, ".", "\\.", &domain); |
| + ReplaceChars(domain, "-", "\\-", &domain); |
| + // For a domain such as foo.bar, this regexp will match domains for http |
| + // and https and on the form: http://foo.bar/, http://www.foo.bar/, |
| + // http://www.mobile.foo.bar/. It will not match http://notfoo.bar/. |
| + // It also matches any port, such as http://foo.bar:8080/. |
| + std::string regexp = |
| + "^((http|https):\\/\\/)([\\w\\-_]+\\.)*" + domain + "(:\\d+)?\\/$"; |
|
palmer
2013/06/11 18:55:45
It doesn't feel right to match loosely on the port
nyquist
2013/06/11 23:54:43
Made this more strict. Now both scheme and port ha
|
| + s.BindString(0, form.signon_realm); |
| + s.BindString(1, regexp); |
| + } else { |
| + s.Assign(db_.GetCachedStatement(SQL_FROM_HERE, sql_query.c_str())); |
| + s.BindString(0, form.signon_realm); |
| + } |
| while (s.Step()) { |
| scoped_ptr<PasswordForm> new_form(new PasswordForm()); |
| if (!InitPasswordFormFromStatement(new_form.get(), s)) |
| return false; |
| + if (public_suffix_domain_matching_) { |
| + const std::string found_registry_controlled_domain = |
| + GetRegistryControlledDomain(new_form->signon_realm); |
| + const std::string form_registry_controlled_domain = |
| + GetRegistryControlledDomain(form.signon_realm); |
| + if (found_registry_controlled_domain != form_registry_controlled_domain) { |
| + // The database returned results that should not match. Skipping result. |
| + continue; |
| + } |
| + if (form.signon_realm != new_form->signon_realm) { |
| + // This is not a perfect match, so we need to create a new valid result. |
| + // We do this by copying over origin, signon realm and action from the |
| + // observed form and setting the original signon realm to what we found |
| + // in the database. We use the fact that |original_signon_realm| is |
| + // non-empty to communicate that this match was found using public |
| + // suffix matching. |
| + new_form->original_signon_realm = new_form->signon_realm; |
| + new_form->origin = form.origin; |
| + new_form->signon_realm = form.signon_realm; |
| + new_form->action = form.action; |
| + } |
| + } |
| forms->push_back(new_form.release()); |
| } |
| return s.Succeeded(); |