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/ui/webui/options/password_manager_handler.h"
6
7#include "base/callback.h"
8#include "base/string_number_conversions.h"
9#include "base/utf_string_conversions.h"
10#include "base/values.h"
11#include "chrome/browser/google/google_util.h"
12#include "chrome/browser/prefs/pref_service.h"
13#include "chrome/browser/profiles/profile.h"
14#include "chrome/common/pref_names.h"
15#include "chrome/common/url_constants.h"
16#include "content/common/notification_details.h"
17#include "content/common/notification_source.h"
18#include "grit/chromium_strings.h"
19#include "grit/generated_resources.h"
20#include "net/base/net_util.h"
21#include "ui/base/l10n/l10n_util.h"
22#include "webkit/glue/password_form.h"
23
24PasswordManagerHandler::PasswordManagerHandler()
25    : ALLOW_THIS_IN_INITIALIZER_LIST(populater_(this)),
26      ALLOW_THIS_IN_INITIALIZER_LIST(exception_populater_(this)) {
27}
28
29PasswordManagerHandler::~PasswordManagerHandler() {
30  GetPasswordStore()->RemoveObserver(this);
31}
32
33void PasswordManagerHandler::GetLocalizedValues(
34    DictionaryValue* localized_strings) {
35  DCHECK(localized_strings);
36
37  static const OptionsStringResource resources[] = {
38    { "savedPasswordsTitle",
39      IDS_PASSWORDS_SHOW_PASSWORDS_TAB_TITLE },
40    { "passwordExceptionsTitle",
41      IDS_PASSWORDS_EXCEPTIONS_TAB_TITLE },
42    { "passwordShowButton",
43      IDS_PASSWORDS_PAGE_VIEW_SHOW_BUTTON },
44    { "passwordHideButton",
45      IDS_PASSWORDS_PAGE_VIEW_HIDE_BUTTON },
46    { "passwordsSiteColumn",
47      IDS_PASSWORDS_PAGE_VIEW_SITE_COLUMN },
48    { "passwordsUsernameColumn",
49      IDS_PASSWORDS_PAGE_VIEW_USERNAME_COLUMN },
50    { "passwordsRemoveButton",
51      IDS_PASSWORDS_PAGE_VIEW_REMOVE_BUTTON },
52    { "passwordsNoPasswordsDescription",
53     IDS_PASSWORDS_PAGE_VIEW_NO_PASSWORDS_DESCRIPTION },
54    { "passwordsNoExceptionsDescription",
55     IDS_PASSWORDS_PAGE_VIEW_NO_EXCEPTIONS_DESCRIPTION },
56    { "passwordsRemoveAllButton",
57      IDS_PASSWORDS_PAGE_VIEW_REMOVE_ALL_BUTTON },
58    { "passwordsRemoveAllTitle",
59      IDS_PASSWORDS_PAGE_VIEW_CAPTION_DELETE_ALL_PASSWORDS },
60    { "passwordsRemoveAllWarning",
61      IDS_PASSWORDS_PAGE_VIEW_TEXT_DELETE_ALL_PASSWORDS },
62  };
63
64  RegisterStrings(localized_strings, resources, arraysize(resources));
65  RegisterTitle(localized_strings, "passwordsPage",
66                IDS_PASSWORDS_EXCEPTIONS_WINDOW_TITLE);
67
68  localized_strings->SetString("passwordManagerLearnMoreURL",
69      google_util::AppendGoogleLocaleParam(
70          GURL(chrome::kPasswordManagerLearnMoreURL)).spec());
71}
72
73void PasswordManagerHandler::Initialize() {
74  show_passwords_.Init(prefs::kPasswordManagerAllowShowPasswords,
75                       web_ui_->GetProfile()->GetPrefs(), this);
76  // We should not cache web_ui_->GetProfile(). See crosbug.com/6304.
77  GetPasswordStore()->AddObserver(this);
78}
79
80void PasswordManagerHandler::RegisterMessages() {
81  DCHECK(web_ui_);
82
83  web_ui_->RegisterMessageCallback("updatePasswordLists",
84      NewCallback(this, &PasswordManagerHandler::UpdatePasswordLists));
85  web_ui_->RegisterMessageCallback("removeSavedPassword",
86      NewCallback(this, &PasswordManagerHandler::RemoveSavedPassword));
87  web_ui_->RegisterMessageCallback("removePasswordException",
88      NewCallback(this, &PasswordManagerHandler::RemovePasswordException));
89  web_ui_->RegisterMessageCallback("removeAllSavedPasswords",
90      NewCallback(this, &PasswordManagerHandler::RemoveAllSavedPasswords));
91  web_ui_->RegisterMessageCallback("removeAllPasswordExceptions", NewCallback(
92      this, &PasswordManagerHandler::RemoveAllPasswordExceptions));
93}
94
95void PasswordManagerHandler::OnLoginsChanged() {
96  UpdatePasswordLists(NULL);
97}
98
99PasswordStore* PasswordManagerHandler::GetPasswordStore() {
100  return web_ui_->GetProfile()->GetPasswordStore(Profile::EXPLICIT_ACCESS);
101}
102
103void PasswordManagerHandler::Observe(NotificationType type,
104                                     const NotificationSource& source,
105                                     const NotificationDetails& details) {
106  if (type.value == NotificationType::PREF_CHANGED) {
107    std::string* pref_name = Details<std::string>(details).ptr();
108    if (*pref_name == prefs::kPasswordManagerAllowShowPasswords) {
109      UpdatePasswordLists(NULL);
110    }
111  }
112
113  OptionsPageUIHandler::Observe(type, source, details);
114}
115
116void PasswordManagerHandler::UpdatePasswordLists(const ListValue* args) {
117  // Reset the current lists.
118  password_list_.reset();
119  password_exception_list_.reset();
120
121  languages_ =
122      web_ui_->GetProfile()->GetPrefs()->GetString(prefs::kAcceptLanguages);
123  populater_.Populate();
124  exception_populater_.Populate();
125}
126
127void PasswordManagerHandler::RemoveSavedPassword(const ListValue* args) {
128  std::string string_value = UTF16ToUTF8(ExtractStringValue(args));
129  int index;
130  base::StringToInt(string_value, &index);
131  GetPasswordStore()->RemoveLogin(*password_list_[index]);
132}
133
134void PasswordManagerHandler::RemovePasswordException(
135    const ListValue* args) {
136  std::string string_value = UTF16ToUTF8(ExtractStringValue(args));
137  int index;
138  base::StringToInt(string_value, &index);
139
140  GetPasswordStore()->RemoveLogin(*password_exception_list_[index]);
141}
142
143void PasswordManagerHandler::RemoveAllSavedPasswords(
144    const ListValue* args) {
145  // TODO(jhawkins): This will cause a list refresh for every password in the
146  // list. Add PasswordStore::RemoveAllLogins().
147  PasswordStore* store = GetPasswordStore();
148  for (size_t i = 0; i < password_list_.size(); ++i)
149    store->RemoveLogin(*password_list_[i]);
150}
151
152void PasswordManagerHandler::RemoveAllPasswordExceptions(
153    const ListValue* args) {
154  PasswordStore* store = GetPasswordStore();
155  for (size_t i = 0; i < password_exception_list_.size(); ++i)
156    store->RemoveLogin(*password_exception_list_[i]);
157}
158
159void PasswordManagerHandler::SetPasswordList() {
160  ListValue entries;
161  bool show_passwords = *show_passwords_;
162  string16 empty;
163  for (size_t i = 0; i < password_list_.size(); ++i) {
164    ListValue* entry = new ListValue();
165    entry->Append(new StringValue(net::FormatUrl(password_list_[i]->origin,
166                                                 languages_)));
167    entry->Append(new StringValue(password_list_[i]->username_value));
168    entry->Append(new StringValue(
169        show_passwords ? password_list_[i]->password_value : empty));
170    entries.Append(entry);
171  }
172
173  web_ui_->CallJavascriptFunction("PasswordManager.setSavedPasswordsList",
174                                  entries);
175}
176
177void PasswordManagerHandler::SetPasswordExceptionList() {
178  ListValue entries;
179  for (size_t i = 0; i < password_exception_list_.size(); ++i) {
180    entries.Append(new StringValue(
181        net::FormatUrl(password_exception_list_[i]->origin, languages_)));
182  }
183
184  web_ui_->CallJavascriptFunction("PasswordManager.setPasswordExceptionsList",
185                                  entries);
186}
187
188PasswordManagerHandler::ListPopulater::ListPopulater(
189    PasswordManagerHandler* page)
190    : page_(page),
191      pending_login_query_(0) {
192}
193
194PasswordManagerHandler::ListPopulater::~ListPopulater() {
195}
196
197PasswordManagerHandler::PasswordListPopulater::PasswordListPopulater(
198    PasswordManagerHandler* page) : ListPopulater(page) {
199}
200
201void PasswordManagerHandler::PasswordListPopulater::Populate() {
202  PasswordStore* store = page_->GetPasswordStore();
203  if (store != NULL) {
204    if (pending_login_query_)
205      store->CancelRequest(pending_login_query_);
206
207    pending_login_query_ = store->GetAutofillableLogins(this);
208  } else {
209    LOG(ERROR) << "No password store! Cannot display passwords.";
210  }
211}
212
213void PasswordManagerHandler::PasswordListPopulater::
214    OnPasswordStoreRequestDone(
215        CancelableRequestProvider::Handle handle,
216        const std::vector<webkit_glue::PasswordForm*>& result) {
217  DCHECK_EQ(pending_login_query_, handle);
218  pending_login_query_ = 0;
219  page_->password_list_.reset();
220  page_->password_list_.insert(page_->password_list_.end(),
221                               result.begin(), result.end());
222  page_->SetPasswordList();
223}
224
225PasswordManagerHandler::PasswordExceptionListPopulater::
226    PasswordExceptionListPopulater(PasswordManagerHandler* page)
227        : ListPopulater(page) {
228}
229
230void PasswordManagerHandler::PasswordExceptionListPopulater::Populate() {
231  PasswordStore* store = page_->GetPasswordStore();
232  if (store != NULL) {
233    if (pending_login_query_)
234      store->CancelRequest(pending_login_query_);
235
236    pending_login_query_ = store->GetBlacklistLogins(this);
237  } else {
238    LOG(ERROR) << "No password store! Cannot display exceptions.";
239  }
240}
241
242void PasswordManagerHandler::PasswordExceptionListPopulater::
243    OnPasswordStoreRequestDone(
244        CancelableRequestProvider::Handle handle,
245        const std::vector<webkit_glue::PasswordForm*>& result) {
246  DCHECK_EQ(pending_login_query_, handle);
247  pending_login_query_ = 0;
248  page_->password_exception_list_.reset();
249  page_->password_exception_list_.insert(page_->password_exception_list_.end(),
250                                         result.begin(), result.end());
251  page_->SetPasswordExceptionList();
252}
253