password_store_win.cc revision 5821806d5e7f356e8fa4b058a389a808ea183019
1// Copyright (c) 2012 The Chromium Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
5#include "chrome/browser/password_manager/password_store_win.h"
6
7#include <map>
8
9#include "base/bind.h"
10#include "base/logging.h"
11#include "base/string_util.h"
12#include "base/utf_string_conversions.h"
13#include "chrome/browser/password_manager/ie7_password.h"
14#include "chrome/browser/password_manager/password_manager.h"
15#include "chrome/browser/profiles/profile.h"
16#include "chrome/browser/webdata/web_data_service.h"
17
18using content::BrowserThread;
19using content::PasswordForm;
20
21namespace {
22// Subclass GetLoginsRequest in order to hold a copy of the form information
23// from the GetLogins request for the ForwardLoginsResult call. Note that the
24// other calls such as GetBlacklistLogins and GetAutofillableLogins, the form is
25// not set.
26class FormGetLoginsRequest : public PasswordStore::GetLoginsRequest {
27 public:
28  explicit FormGetLoginsRequest(
29      const PasswordStore::GetLoginsCallback& callback)
30      : GetLoginsRequest(callback) {}
31
32  // We hold a copy of the |form| used in GetLoginsImpl as a pointer.  If the
33  // form is not set (is NULL), then we are not a GetLogins request.
34  void SetLoginsRequestForm(const PasswordForm& form) {
35    form_.reset(new PasswordForm(form));
36  }
37  PasswordForm* form() const {
38    return form_.get();
39  }
40  bool IsLoginsRequest() const { return !!form_.get(); }
41
42 protected:
43  virtual ~FormGetLoginsRequest() {}
44
45 private:
46  scoped_ptr<PasswordForm> form_;
47};
48
49}  // namespace
50
51// Handles requests to WebDataService.
52class PasswordStoreWin::DBHandler : public WebDataServiceConsumer {
53 public:
54  DBHandler(WebDataService* web_data_service,
55            PasswordStoreWin* password_store)
56      : web_data_service_(web_data_service),
57        password_store_(password_store) {
58  }
59
60  ~DBHandler();
61
62  // Requests the IE7 login for |url| and |request|. This is async. The request
63  // is processed when complete.
64  void GetIE7Login(const GURL& url, GetLoginsRequest* request);
65
66 private:
67  // Holds requests associated with in-flight GetLogin queries.
68  typedef std::map<WebDataService::Handle,
69                   scoped_refptr<GetLoginsRequest> > PendingRequestMap;
70
71  // Gets logins from IE7 if no others are found. Also copies them into
72  // Chrome's WebDatabase so we don't need to look next time.
73  PasswordForm* GetIE7Result(const WDTypedResult* result,
74                             const PasswordForm& form);
75
76  // WebDataServiceConsumer.
77  virtual void OnWebDataServiceRequestDone(
78      WebDataService::Handle handle,
79      const WDTypedResult* result) OVERRIDE;
80
81  scoped_refptr<WebDataService> web_data_service_;
82
83  // This creates a cycle between us and PasswordStore. The cycle is broken
84  // from PasswordStoreWin::Shutdown, which deletes us.
85  scoped_refptr<PasswordStoreWin> password_store_;
86
87  // Holds requests associated with in-flight GetLogin queries.
88  PendingRequestMap pending_requests_;
89
90  DISALLOW_COPY_AND_ASSIGN(DBHandler);
91};
92
93PasswordStoreWin::DBHandler::~DBHandler() {
94  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::DB));
95  for (PendingRequestMap::const_iterator i = pending_requests_.begin();
96       i != pending_requests_.end(); ++i) {
97    web_data_service_->CancelRequest(i->first);
98  }
99}
100
101void PasswordStoreWin::DBHandler::GetIE7Login(const GURL& url,
102                                              GetLoginsRequest* request) {
103  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::DB));
104  IE7PasswordInfo info;
105  info.url_hash = ie7_password::GetUrlHash(UTF8ToWide(url.spec()));
106  WebDataService::Handle handle = web_data_service_->GetIE7Login(info, this);
107  pending_requests_.insert(PendingRequestMap::value_type(handle, request));
108}
109
110PasswordForm* PasswordStoreWin::DBHandler::GetIE7Result(
111    const WDTypedResult *result,
112    const PasswordForm& form) {
113  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::DB));
114
115  const WDResult<IE7PasswordInfo>* r =
116      static_cast<const WDResult<IE7PasswordInfo>*>(result);
117  IE7PasswordInfo info = r->GetValue();
118
119  if (!info.encrypted_data.empty()) {
120    // We got a result.
121    // Delete the entry. If it's good we will add it to the real saved password
122    // table.
123    web_data_service_->RemoveIE7Login(info);
124    std::wstring username;
125    std::wstring password;
126    std::wstring url = ASCIIToWide(form.origin.spec());
127    if (!ie7_password::DecryptPassword(url, info.encrypted_data,
128                                       &username, &password)) {
129      return NULL;
130    }
131
132    PasswordForm* autofill = new PasswordForm(form);
133    autofill->username_value = username;
134    autofill->password_value = password;
135    autofill->preferred = true;
136    autofill->ssl_valid = form.origin.SchemeIsSecure();
137    autofill->date_created = info.date_created;
138    // Add this PasswordForm to the saved password table. We're on the DB thread
139    // already, so we use AddLoginImpl.
140    password_store_->AddLoginImpl(*autofill);
141    return autofill;
142  }
143  return NULL;
144}
145
146void PasswordStoreWin::DBHandler::OnWebDataServiceRequestDone(
147    WebDataService::Handle handle,
148    const WDTypedResult* result) {
149  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::DB));
150
151  PendingRequestMap::iterator i(pending_requests_.find(handle));
152  DCHECK(i != pending_requests_.end());
153  scoped_refptr<GetLoginsRequest> request(i->second);
154  pending_requests_.erase(i);
155
156  if (!result)
157    return;  // The WDS returns NULL if it is shutting down.
158
159  DCHECK_EQ(PASSWORD_IE7_RESULT, result->GetType());
160  PasswordForm* form =
161      static_cast<FormGetLoginsRequest*>(request.get())->form();
162  DCHECK(form);
163  PasswordForm* ie7_form = GetIE7Result(result, *form);
164
165  if (ie7_form)
166    request->value.push_back(ie7_form);
167
168  // Since we aren't using ForwardLoginsResult(), we must call
169  // ApplyIgnoreLoginsCutoff() manually here as is done in
170  // PasswordStore::ForwardLoginsResult().
171  request->ApplyIgnoreLoginsCutoff();
172  request->ForwardResult(request->handle(), request->value);
173}
174
175PasswordStoreWin::PasswordStoreWin(LoginDatabase* login_database,
176                                   Profile* profile,
177                                   WebDataService* web_data_service)
178    : PasswordStoreDefault(login_database, profile) {
179  db_handler_.reset(new DBHandler(web_data_service, this));
180}
181
182PasswordStoreWin::~PasswordStoreWin() {
183}
184
185void PasswordStoreWin::ShutdownOnDBThread() {
186  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::DB));
187  db_handler_.reset();
188}
189
190PasswordStore::GetLoginsRequest* PasswordStoreWin::NewGetLoginsRequest(
191    const GetLoginsCallback& callback) {
192  return new FormGetLoginsRequest(callback);
193}
194
195void PasswordStoreWin::ShutdownOnUIThread() {
196  BrowserThread::PostTask(
197      BrowserThread::DB, FROM_HERE,
198      base::Bind(&PasswordStoreWin::ShutdownOnDBThread, this));
199  PasswordStoreDefault::ShutdownOnUIThread();
200}
201
202void PasswordStoreWin::ForwardLoginsResult(GetLoginsRequest* request) {
203  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::DB));
204  if (static_cast<FormGetLoginsRequest*>(request)->IsLoginsRequest() &&
205      request->value.empty() && db_handler_.get()) {
206    db_handler_->GetIE7Login(
207        static_cast<FormGetLoginsRequest*>(request)->form()->origin,
208        request);
209  } else {
210    PasswordStore::ForwardLoginsResult(request);
211  }
212}
213
214void PasswordStoreWin::GetLoginsImpl(GetLoginsRequest* request,
215                                     const PasswordForm& form) {
216  static_cast<FormGetLoginsRequest*>(request)->SetLoginsRequestForm(form);
217
218  PasswordStoreDefault::GetLoginsImpl(request, form);
219}
220