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/memory/scoped_ptr.h"
12#include "base/strings/string_util.h"
13#include "base/strings/utf_string_conversions.h"
14#include "components/os_crypt/ie7_password_win.h"
15#include "components/password_manager/core/browser/password_manager.h"
16#include "components/password_manager/core/browser/webdata/password_web_data_service_win.h"
17#include "content/public/browser/browser_thread.h"
18
19using autofill::PasswordForm;
20using content::BrowserThread;
21using password_manager::PasswordStoreDefault;
22
23// Handles requests to PasswordWebDataService.
24class PasswordStoreWin::DBHandler : public WebDataServiceConsumer {
25 public:
26  DBHandler(PasswordWebDataService* web_data_service,
27            PasswordStoreWin* password_store)
28      : web_data_service_(web_data_service),
29        password_store_(password_store) {
30  }
31
32  ~DBHandler();
33
34  // Requests the IE7 login for |form|. This is async. |callback_runner| will be
35  // run when complete.
36  void GetIE7Login(
37      const PasswordForm& form,
38      const PasswordStoreWin::ConsumerCallbackRunner& callback_runner);
39
40 private:
41  struct RequestInfo {
42    RequestInfo() {}
43
44    RequestInfo(PasswordForm* request_form,
45                const PasswordStoreWin::ConsumerCallbackRunner& runner)
46        : form(request_form),
47          callback_runner(runner) {}
48
49    PasswordForm* form;
50    PasswordStoreWin::ConsumerCallbackRunner callback_runner;
51  };
52
53  // Holds info associated with in-flight GetIE7Login requests.
54  typedef std::map<PasswordWebDataService::Handle, RequestInfo>
55      PendingRequestMap;
56
57  // Gets logins from IE7 if no others are found. Also copies them into
58  // Chrome's WebDatabase so we don't need to look next time.
59  std::vector<autofill::PasswordForm*> GetIE7Results(
60      const WDTypedResult* result,
61      const PasswordForm& form);
62
63  // WebDataServiceConsumer implementation.
64  virtual void OnWebDataServiceRequestDone(
65      PasswordWebDataService::Handle handle,
66      const WDTypedResult* result) OVERRIDE;
67
68  scoped_refptr<PasswordWebDataService> web_data_service_;
69
70  // This creates a cycle between us and PasswordStore. The cycle is broken
71  // from PasswordStoreWin::Shutdown, which deletes us.
72  scoped_refptr<PasswordStoreWin> password_store_;
73
74  PendingRequestMap pending_requests_;
75
76  DISALLOW_COPY_AND_ASSIGN(DBHandler);
77};
78
79PasswordStoreWin::DBHandler::~DBHandler() {
80  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::DB));
81  for (PendingRequestMap::const_iterator i = pending_requests_.begin();
82       i != pending_requests_.end();
83       ++i) {
84    web_data_service_->CancelRequest(i->first);
85    delete i->second.form;
86  }
87}
88
89void PasswordStoreWin::DBHandler::GetIE7Login(
90    const PasswordForm& form,
91    const PasswordStoreWin::ConsumerCallbackRunner& callback_runner) {
92  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::DB));
93  IE7PasswordInfo info;
94  info.url_hash =
95      ie7_password::GetUrlHash(base::UTF8ToWide(form.origin.spec()));
96  PasswordWebDataService::Handle handle =
97      web_data_service_->GetIE7Login(info, this);
98  pending_requests_[handle] =
99      RequestInfo(new PasswordForm(form), callback_runner);
100}
101
102std::vector<PasswordForm*> PasswordStoreWin::DBHandler::GetIE7Results(
103    const WDTypedResult *result,
104    const PasswordForm& form) {
105  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::DB));
106  std::vector<PasswordForm*> matching_forms;
107
108  const WDResult<IE7PasswordInfo>* r =
109      static_cast<const WDResult<IE7PasswordInfo>*>(result);
110  IE7PasswordInfo info = r->GetValue();
111
112  if (!info.encrypted_data.empty()) {
113    // We got a result.
114    // Delete the entry. If it's good we will add it to the real saved password
115    // table.
116    web_data_service_->RemoveIE7Login(info);
117    std::vector<ie7_password::DecryptedCredentials> credentials;
118    std::wstring url = base::ASCIIToWide(form.origin.spec());
119    if (ie7_password::DecryptPasswords(url,
120                                       info.encrypted_data,
121                                       &credentials)) {
122      for (size_t i = 0; i < credentials.size(); ++i) {
123        PasswordForm* autofill = new PasswordForm();
124        autofill->username_value = credentials[i].username;
125        autofill->password_value = credentials[i].password;
126        autofill->signon_realm = form.signon_realm;
127        autofill->origin = form.origin;
128        autofill->preferred = true;
129        autofill->ssl_valid = form.origin.SchemeIsSecure();
130        autofill->date_created = info.date_created;
131
132        matching_forms.push_back(autofill);
133        // Add this PasswordForm to the saved password table. We're on the DB
134        // thread already, so we use AddLoginImpl.
135        password_store_->AddLoginImpl(*autofill);
136      }
137    }
138  }
139  return matching_forms;
140}
141
142void PasswordStoreWin::DBHandler::OnWebDataServiceRequestDone(
143    PasswordWebDataService::Handle handle,
144    const WDTypedResult* result) {
145  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::DB));
146
147  PendingRequestMap::iterator i = pending_requests_.find(handle);
148  DCHECK(i != pending_requests_.end());
149
150  scoped_ptr<PasswordForm> form(i->second.form);
151  PasswordStoreWin::ConsumerCallbackRunner callback_runner(
152      i->second.callback_runner);
153  pending_requests_.erase(i);
154
155  if (!result) {
156    // The WDS returns NULL if it is shutting down. Run callback with empty
157    // result.
158    callback_runner.Run(std::vector<autofill::PasswordForm*>());
159    return;
160  }
161
162  DCHECK_EQ(PASSWORD_IE7_RESULT, result->GetType());
163  std::vector<autofill::PasswordForm*> matched_forms =
164      GetIE7Results(result, *form);
165
166  callback_runner.Run(matched_forms);
167}
168
169PasswordStoreWin::PasswordStoreWin(
170    scoped_refptr<base::SingleThreadTaskRunner> main_thread_runner,
171    scoped_refptr<base::SingleThreadTaskRunner> db_thread_runner,
172    password_manager::LoginDatabase* login_database,
173    PasswordWebDataService* web_data_service)
174    : PasswordStoreDefault(main_thread_runner,
175                           db_thread_runner,
176                           login_database) {
177  db_handler_.reset(new DBHandler(web_data_service, this));
178}
179
180PasswordStoreWin::~PasswordStoreWin() {
181}
182
183void PasswordStoreWin::ShutdownOnDBThread() {
184  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::DB));
185  db_handler_.reset();
186}
187
188void PasswordStoreWin::Shutdown() {
189  BrowserThread::PostTask(
190      BrowserThread::DB, FROM_HERE,
191      base::Bind(&PasswordStoreWin::ShutdownOnDBThread, this));
192  PasswordStoreDefault::Shutdown();
193}
194
195void PasswordStoreWin::GetIE7LoginIfNecessary(
196    const PasswordForm& form,
197    const ConsumerCallbackRunner& callback_runner,
198    const std::vector<autofill::PasswordForm*>& matched_forms) {
199  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::DB));
200  if (matched_forms.empty() && db_handler_.get()) {
201    db_handler_->GetIE7Login(form, callback_runner);
202  } else {
203    // No need to get IE7 login.
204    callback_runner.Run(matched_forms);
205  }
206}
207
208void PasswordStoreWin::GetLoginsImpl(
209    const PasswordForm& form,
210    AuthorizationPromptPolicy prompt_policy,
211    const ConsumerCallbackRunner& callback_runner) {
212  ConsumerCallbackRunner get_ie7_login =
213      base::Bind(&PasswordStoreWin::GetIE7LoginIfNecessary,
214                 this, form, callback_runner);
215  PasswordStoreDefault::GetLoginsImpl(form, prompt_policy, get_ie7_login);
216}
217