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..8fa692ee9249ec1a98db8dfdbacf2a4048f3955a 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/strings/string_number_conversions.h" |
+#include "base/strings/string_util.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,9 +49,45 @@ enum LoginTableColumns { |
COLUMN_TIMES_USED |
}; |
+std::string GetRegistryControlledDomain(const GURL& signon_realm) { |
+ return net::registry_controlled_domains::GetDomainAndRegistry( |
+ signon_realm, |
+ net::registry_controlled_domains::INCLUDE_PRIVATE_REGISTRIES); |
+} |
+ |
+std::string GetRegistryControlledDomain(const std::string& signon_realm_str) { |
+ GURL signon_realm(signon_realm_str); |
+ return net::registry_controlled_domains::GetDomainAndRegistry( |
+ signon_realm, |
+ net::registry_controlled_domains::INCLUDE_PRIVATE_REGISTRIES); |
+} |
+ |
+bool RegistryControlledDomainMatches(const scoped_ptr<PasswordForm>& found, |
+ const PasswordForm current) { |
+ const std::string found_registry_controlled_domain = |
+ GetRegistryControlledDomain(found->signon_realm); |
+ const std::string form_registry_controlled_domain = |
+ GetRegistryControlledDomain(current.signon_realm); |
+ return found_registry_controlled_domain == form_registry_controlled_domain; |
+} |
+ |
+bool SchemeMatches(const scoped_ptr<PasswordForm>& found, |
+ const PasswordForm current) { |
+ const std::string found_scheme = GURL(found->signon_realm).scheme(); |
+ const std::string form_scheme = GURL(current.signon_realm).scheme(); |
+ return found_scheme == form_scheme; |
+} |
+ |
+bool PortMatches(const scoped_ptr<PasswordForm>& found, |
+ const PasswordForm current) { |
+ const std::string found_port = GURL(found->signon_realm).port(); |
+ const std::string form_port = GURL(current.signon_realm).port(); |
+ return found_port == form_port; |
+} |
+ |
} // namespace |
-LoginDatabase::LoginDatabase() { |
+LoginDatabase::LoginDatabase() : public_suffix_domain_matching_(false) { |
} |
LoginDatabase::~LoginDatabase() { |
@@ -100,6 +140,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 +401,69 @@ 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 ? "; |
Mike Mammarella
2013/06/20 20:20:23
I might include a comment here that notes that the
nyquist
2013/06/21 21:01:27
Done.
|
+ // TODO(nyquist) Re-enable usage of GetCachedStatement when |
+ // http://crbug.com/248608 is fixed. |
+ s.Assign(db_.GetUniqueStatement(extended_sql_query.c_str())); |
+ const GURL signon_realm(form.signon_realm); |
+ std::string domain = GetRegistryControlledDomain(signon_realm); |
+ // We need to escape . and - in the domain. Since the domain has already |
Mike Mammarella
2013/06/20 20:20:23
Why do you need to escape - ? Does it have some sp
nyquist
2013/06/21 21:01:27
The - is used when using things like A-Z. However,
Ilya Sherman
2013/06/21 21:16:24
Hmm, this "presumably" worries me. Isn't the URL
|
+ // been sanitized using GURL, we do not need to escape any other characters. |
+ ReplaceChars(domain, ".", "\\.", &domain); |
+ ReplaceChars(domain, "-", "\\-", &domain); |
+ std::string scheme = signon_realm.scheme(); |
+ // We need to escape . and - in the scheme. Since the scheme has already |
+ // been sanitized using GURL, we do not need to escape any other characters. |
+ // view-source is an example with '-' and soap.beep is an example with '.'. |
+ ReplaceChars(scheme, ".", "\\.", &scheme); |
+ ReplaceChars(scheme, "-", "\\-", &scheme); |
+ const std::string port = signon_realm.port(); |
+ // For a signon realm such as http://foo.bar/, this regexp will match |
+ // domains on the form http://foo.bar/, http://www.foo.bar/, |
+ // http://www.mobile.foo.bar/. It will not match http://notfoo.bar/. |
+ // The scheme and port has to be the same as the observed form. |
+ std::string regexp = "^(" + scheme + ":\\/\\/)([\\w\\-_]+\\.)*" + |
Mike Mammarella
2013/06/20 20:20:23
_ is not actually a valid character for DNS names,
nyquist
2013/06/21 21:01:27
Removed _. Since - is now the last character in th
|
+ domain + "(:" + port + ")?\\/$"; |
+ 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_) { |
+ if (!SchemeMatches(new_form, form) || |
+ !RegistryControlledDomainMatches(new_form, form) || |
+ !PortMatches(new_form, form)) { |
+ // 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(); |