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