blob: 953416b0f42270ed3fb801d7a3b40a853be7f987 [file] [log] [blame]
// 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/password_manager/password_store.h"
#include "base/bind.h"
#include "base/memory/scoped_ptr.h"
#include "base/message_loop/message_loop.h"
#include "base/message_loop/message_loop_proxy.h"
#include "base/stl_util.h"
#include "chrome/browser/password_manager/password_store_consumer.h"
#include "components/autofill/core/common/password_form.h"
#include "content/public/browser/browser_thread.h"
using autofill::PasswordForm;
using content::BrowserThread;
using std::vector;
namespace {
// PasswordStoreConsumer callback requires vector const reference.
void RunConsumerCallbackIfNotCanceled(
const CancelableTaskTracker::IsCanceledCallback& is_canceled_cb,
PasswordStoreConsumer* consumer,
const vector<PasswordForm*>* matched_forms) {
if (is_canceled_cb.Run()) {
STLDeleteContainerPointers(matched_forms->begin(), matched_forms->end());
return;
}
// OnGetPasswordStoreResults owns PasswordForms in the vector.
consumer->OnGetPasswordStoreResults(*matched_forms);
}
void PostConsumerCallback(
base::TaskRunner* task_runner,
const CancelableTaskTracker::IsCanceledCallback& is_canceled_cb,
PasswordStoreConsumer* consumer,
const base::Time& ignore_logins_cutoff,
const vector<PasswordForm*>& matched_forms) {
vector<PasswordForm*>* matched_forms_copy = new vector<PasswordForm*>();
if (ignore_logins_cutoff.is_null()) {
*matched_forms_copy = matched_forms;
} else {
// Apply |ignore_logins_cutoff| and delete old ones.
for (size_t i = 0; i < matched_forms.size(); i++) {
if (matched_forms[i]->date_created < ignore_logins_cutoff)
delete matched_forms[i];
else
matched_forms_copy->push_back(matched_forms[i]);
}
}
task_runner->PostTask(
FROM_HERE,
base::Bind(&RunConsumerCallbackIfNotCanceled,
is_canceled_cb, consumer, base::Owned(matched_forms_copy)));
}
} // namespace
PasswordStore::GetLoginsRequest::GetLoginsRequest(
const GetLoginsCallback& callback)
: CancelableRequest1<GetLoginsCallback, vector<PasswordForm*> >(callback) {
}
void PasswordStore::GetLoginsRequest::ApplyIgnoreLoginsCutoff() {
if (!ignore_logins_cutoff_.is_null()) {
// Count down rather than up since we may be deleting elements.
// Note that in principle it could be more efficient to copy the whole array
// since that's worst-case linear time, but we expect that elements will be
// deleted rarely and lists will be small, so this avoids the copies.
for (size_t i = value.size(); i > 0; --i) {
if (value[i - 1]->date_created < ignore_logins_cutoff_) {
delete value[i - 1];
value.erase(value.begin() + (i - 1));
}
}
}
}
PasswordStore::GetLoginsRequest::~GetLoginsRequest() {
if (canceled()) {
STLDeleteElements(&value);
}
}
PasswordStore::PasswordStore() {
}
bool PasswordStore::Init() {
ReportMetrics();
return true;
}
void PasswordStore::AddLogin(const PasswordForm& form) {
ScheduleTask(base::Bind(&PasswordStore::WrapModificationTask, this,
base::Closure(base::Bind(&PasswordStore::AddLoginImpl, this, form))));
}
void PasswordStore::UpdateLogin(const PasswordForm& form) {
ScheduleTask(base::Bind(&PasswordStore::WrapModificationTask, this,
base::Closure(base::Bind(&PasswordStore::UpdateLoginImpl, this, form))));
}
void PasswordStore::RemoveLogin(const PasswordForm& form) {
ScheduleTask(base::Bind(&PasswordStore::WrapModificationTask, this,
base::Closure(base::Bind(&PasswordStore::RemoveLoginImpl, this, form))));
}
void PasswordStore::RemoveLoginsCreatedBetween(const base::Time& delete_begin,
const base::Time& delete_end) {
ScheduleTask(base::Bind(&PasswordStore::WrapModificationTask, this,
base::Closure(
base::Bind(&PasswordStore::RemoveLoginsCreatedBetweenImpl, this,
delete_begin, delete_end))));
}
CancelableTaskTracker::TaskId PasswordStore::GetLogins(
const PasswordForm& form,
PasswordStoreConsumer* consumer) {
// Per http://crbug.com/121738, we deliberately ignore saved logins for
// http*://www.google.com/ that were stored prior to 2012. (Google now uses
// https://accounts.google.com/ for all login forms, so these should be
// unused.) We don't delete them just yet, and they'll still be visible in the
// password manager, but we won't use them to autofill any forms. This is a
// security feature to help minimize damage that can be done by XSS attacks.
// TODO(mdm): actually delete them at some point, say M24 or so.
base::Time ignore_logins_cutoff; // the null time
if (form.scheme == PasswordForm::SCHEME_HTML &&
(form.signon_realm == "http://www.google.com" ||
form.signon_realm == "http://www.google.com/" ||
form.signon_realm == "https://www.google.com" ||
form.signon_realm == "https://www.google.com/")) {
static const base::Time::Exploded exploded_cutoff =
{ 2012, 1, 0, 1, 0, 0, 0, 0 }; // 00:00 Jan 1 2012
ignore_logins_cutoff = base::Time::FromUTCExploded(exploded_cutoff);
}
CancelableTaskTracker::IsCanceledCallback is_canceled_cb;
CancelableTaskTracker::TaskId id =
consumer->cancelable_task_tracker()->NewTrackedTaskId(&is_canceled_cb);
ConsumerCallbackRunner callback_runner =
base::Bind(&PostConsumerCallback,
base::MessageLoopProxy::current(),
is_canceled_cb,
consumer,
ignore_logins_cutoff);
ScheduleTask(
base::Bind(&PasswordStore::GetLoginsImpl, this, form, callback_runner));
return id;
}
CancelableRequestProvider::Handle PasswordStore::GetAutofillableLogins(
PasswordStoreConsumer* consumer) {
return Schedule(&PasswordStore::GetAutofillableLoginsImpl, consumer);
}
CancelableRequestProvider::Handle PasswordStore::GetBlacklistLogins(
PasswordStoreConsumer* consumer) {
return Schedule(&PasswordStore::GetBlacklistLoginsImpl, consumer);
}
void PasswordStore::ReportMetrics() {
ScheduleTask(base::Bind(&PasswordStore::ReportMetricsImpl, this));
}
void PasswordStore::AddObserver(Observer* observer) {
observers_.AddObserver(observer);
}
void PasswordStore::RemoveObserver(Observer* observer) {
observers_.RemoveObserver(observer);
}
PasswordStore::~PasswordStore() {}
PasswordStore::GetLoginsRequest* PasswordStore::NewGetLoginsRequest(
const GetLoginsCallback& callback) {
return new GetLoginsRequest(callback);
}
bool PasswordStore::ScheduleTask(const base::Closure& task) {
return BrowserThread::PostTask(BrowserThread::DB, FROM_HERE, task);
}
void PasswordStore::ForwardLoginsResult(GetLoginsRequest* request) {
request->ApplyIgnoreLoginsCutoff();
request->ForwardResult(request->handle(), request->value);
}
template<typename BackendFunc>
CancelableRequestProvider::Handle PasswordStore::Schedule(
BackendFunc func,
PasswordStoreConsumer* consumer) {
scoped_refptr<GetLoginsRequest> request(
NewGetLoginsRequest(
base::Bind(&PasswordStoreConsumer::OnPasswordStoreRequestDone,
base::Unretained(consumer))));
AddRequest(request.get(), consumer->cancelable_consumer());
ScheduleTask(base::Bind(func, this, request));
return request->handle();
}
template<typename BackendFunc>
CancelableRequestProvider::Handle PasswordStore::Schedule(
BackendFunc func,
PasswordStoreConsumer* consumer,
const PasswordForm& form,
const base::Time& ignore_logins_cutoff) {
scoped_refptr<GetLoginsRequest> request(
NewGetLoginsRequest(
base::Bind(&PasswordStoreConsumer::OnPasswordStoreRequestDone,
base::Unretained(consumer))));
request->set_ignore_logins_cutoff(ignore_logins_cutoff);
AddRequest(request.get(), consumer->cancelable_consumer());
ScheduleTask(base::Bind(func, this, request, form));
return request->handle();
}
void PasswordStore::WrapModificationTask(base::Closure task) {
#if !defined(OS_MACOSX)
DCHECK(BrowserThread::CurrentlyOn(BrowserThread::DB));
#endif // !defined(OS_MACOSX)
task.Run();
PostNotifyLoginsChanged();
}
void PasswordStore::PostNotifyLoginsChanged() {
#if !defined(OS_MACOSX)
DCHECK(BrowserThread::CurrentlyOn(BrowserThread::DB));
#endif // !defined(OS_MACOSX)
BrowserThread::PostTask(
BrowserThread::UI, FROM_HERE,
base::Bind(&PasswordStore::NotifyLoginsChanged, this));
}
void PasswordStore::NotifyLoginsChanged() {
DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
FOR_EACH_OBSERVER(Observer, observers_, OnLoginsChanged());
}