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/crypto_module_password_dialog.h"
6
7#include <gtk/gtk.h>
8
9#include "base/basictypes.h"
10#include "base/synchronization/waitable_event.h"
11#include "base/task.h"
12#include "base/utf_string_conversions.h"
13#include "crypto/crypto_module_blocking_password_delegate.h"
14#include "chrome/browser/ui/gtk/gtk_util.h"
15#include "content/browser/browser_thread.h"
16#include "googleurl/src/gurl.h"
17#include "grit/generated_resources.h"
18#include "ui/base/gtk/gtk_signal.h"
19#include "ui/base/l10n/l10n_util.h"
20
21namespace {
22
23class CryptoModuleBlockingDialogDelegate
24    : public crypto::CryptoModuleBlockingPasswordDelegate {
25 public:
26  CryptoModuleBlockingDialogDelegate(browser::CryptoModulePasswordReason reason,
27                                     const std::string& server)
28      : event_(false, false),
29        reason_(reason),
30        server_(server),
31        password_(),
32        cancelled_(false) {
33  }
34
35  ~CryptoModuleBlockingDialogDelegate() {
36    password_.replace(0, password_.size(), password_.size(), 0);
37  }
38
39  // crypto::CryptoModuleBlockingDialogDelegate implementation.
40  virtual std::string RequestPassword(const std::string& slot_name, bool retry,
41                                      bool* cancelled) {
42    DCHECK(!BrowserThread::CurrentlyOn(BrowserThread::UI));
43    DCHECK(!event_.IsSignaled());
44    event_.Reset();
45    if (BrowserThread::PostTask(
46            BrowserThread::UI, FROM_HERE,
47            NewRunnableMethod(this,
48                              &CryptoModuleBlockingDialogDelegate::ShowDialog,
49                              slot_name,
50                              retry))) {
51      event_.Wait();
52    }
53    *cancelled = cancelled_;
54    return password_;
55  }
56
57 private:
58  void ShowDialog(const std::string& slot_name, bool retry) {
59    DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
60    ShowCryptoModulePasswordDialog(
61        slot_name, retry, reason_, server_,
62        NewCallback(this, &CryptoModuleBlockingDialogDelegate::GotPassword));
63  }
64  void GotPassword(const char* password) {
65    if (password)
66      password_ = password;
67    else
68      cancelled_ = true;
69    event_.Signal();
70  }
71  base::WaitableEvent event_;
72  browser::CryptoModulePasswordReason reason_;
73  std::string server_;
74  std::string password_;
75  bool cancelled_;
76
77  DISALLOW_COPY_AND_ASSIGN(CryptoModuleBlockingDialogDelegate);
78};
79
80// TODO(mattm): change into a constrained dialog.
81class CryptoModulePasswordDialog {
82 public:
83  CryptoModulePasswordDialog(const std::string& slot_name,
84                             bool retry,
85                             browser::CryptoModulePasswordReason reason,
86                             const std::string& server,
87                             browser::CryptoModulePasswordCallback* callback);
88
89  void Show();
90
91 private:
92  CHROMEGTK_CALLBACK_1(CryptoModulePasswordDialog, void, OnResponse, int);
93  CHROMEGTK_CALLBACK_0(CryptoModulePasswordDialog, void, OnWindowDestroy);
94
95  scoped_ptr<browser::CryptoModulePasswordCallback> callback_;
96
97  GtkWidget* dialog_;
98  GtkWidget* password_entry_;
99
100  DISALLOW_COPY_AND_ASSIGN(CryptoModulePasswordDialog);
101};
102
103CryptoModulePasswordDialog::CryptoModulePasswordDialog(
104    const std::string& slot_name,
105    bool retry,
106    browser::CryptoModulePasswordReason reason,
107    const std::string& server,
108    browser::CryptoModulePasswordCallback* callback)
109    : callback_(callback) {
110  dialog_ = gtk_dialog_new_with_buttons(
111      l10n_util::GetStringUTF8(IDS_CRYPTO_MODULE_AUTH_DIALOG_TITLE).c_str(),
112      NULL,
113      GTK_DIALOG_NO_SEPARATOR,
114      NULL);  // Populate the buttons later, for control over the OK button.
115  gtk_dialog_add_button(GTK_DIALOG(dialog_),
116                        GTK_STOCK_CANCEL, GTK_RESPONSE_REJECT);
117  GtkWidget* ok_button = gtk_util::AddButtonToDialog(
118      dialog_,
119      l10n_util::GetStringUTF8(
120          IDS_CRYPTO_MODULE_AUTH_DIALOG_OK_BUTTON_LABEL).c_str(),
121      GTK_STOCK_OK,
122      GTK_RESPONSE_ACCEPT);
123  GTK_WIDGET_SET_FLAGS(ok_button, GTK_CAN_DEFAULT);
124  gtk_dialog_set_default_response(GTK_DIALOG(dialog_), GTK_RESPONSE_ACCEPT);
125
126  // Select an appropriate text for the reason.
127  std::string text;
128  const string16& server16 = UTF8ToUTF16(server);
129  const string16& slot16 = UTF8ToUTF16(slot_name);
130  switch (reason) {
131    case browser::kCryptoModulePasswordKeygen:
132      text = l10n_util::GetStringFUTF8(
133          IDS_CRYPTO_MODULE_AUTH_DIALOG_TEXT_KEYGEN, slot16, server16);
134      break;
135    case browser::kCryptoModulePasswordCertEnrollment:
136      text = l10n_util::GetStringFUTF8(
137          IDS_CRYPTO_MODULE_AUTH_DIALOG_TEXT_CERT_ENROLLMENT, slot16, server16);
138      break;
139    case browser::kCryptoModulePasswordClientAuth:
140      text = l10n_util::GetStringFUTF8(
141          IDS_CRYPTO_MODULE_AUTH_DIALOG_TEXT_CLIENT_AUTH, slot16, server16);
142      break;
143    case browser::kCryptoModulePasswordListCerts:
144      text = l10n_util::GetStringFUTF8(
145          IDS_CRYPTO_MODULE_AUTH_DIALOG_TEXT_LIST_CERTS, slot16);
146      break;
147    case browser::kCryptoModulePasswordCertImport:
148      text = l10n_util::GetStringFUTF8(
149          IDS_CRYPTO_MODULE_AUTH_DIALOG_TEXT_CERT_IMPORT, slot16);
150      break;
151    case browser::kCryptoModulePasswordCertExport:
152      text = l10n_util::GetStringFUTF8(
153          IDS_CRYPTO_MODULE_AUTH_DIALOG_TEXT_CERT_EXPORT, slot16);
154      break;
155    default:
156      NOTREACHED();
157  }
158  GtkWidget* label = gtk_label_new(text.c_str());
159  gtk_label_set_line_wrap(GTK_LABEL(label), TRUE);
160  gtk_util::LeftAlignMisc(label);
161  gtk_box_pack_start(GTK_BOX(GTK_DIALOG(dialog_)->vbox), label,
162                     FALSE, FALSE, 0);
163
164  password_entry_ = gtk_entry_new();
165  gtk_entry_set_activates_default(GTK_ENTRY(password_entry_), TRUE);
166  gtk_entry_set_visibility(GTK_ENTRY(password_entry_), FALSE);
167
168  GtkWidget* password_box = gtk_hbox_new(FALSE, gtk_util::kLabelSpacing);
169  gtk_box_pack_start(GTK_BOX(password_box),
170                     gtk_label_new(l10n_util::GetStringUTF8(
171                         IDS_CRYPTO_MODULE_AUTH_DIALOG_PASSWORD_FIELD).c_str()),
172                     FALSE, FALSE, 0);
173  gtk_box_pack_start(GTK_BOX(password_box), password_entry_,
174                     TRUE, TRUE, 0);
175
176  gtk_box_pack_start(GTK_BOX(GTK_DIALOG(dialog_)->vbox), password_box,
177                     FALSE, FALSE, 0);
178
179  g_signal_connect(dialog_, "response",
180                   G_CALLBACK(OnResponseThunk), this);
181  g_signal_connect(dialog_, "destroy",
182                   G_CALLBACK(OnWindowDestroyThunk), this);
183}
184
185void CryptoModulePasswordDialog::Show() {
186  gtk_util::ShowDialog(dialog_);
187}
188
189void CryptoModulePasswordDialog::OnResponse(GtkWidget* dialog,
190                                            int response_id) {
191  if (response_id == GTK_RESPONSE_ACCEPT)
192    callback_->Run(gtk_entry_get_text(GTK_ENTRY(password_entry_)));
193  else
194    callback_->Run(static_cast<const char*>(NULL));
195
196  // This will cause gtk to zero out the buffer.  (see
197  // gtk_entry_buffer_normal_delete_text:
198  // http://git.gnome.org/browse/gtk+/tree/gtk/gtkentrybuffer.c#n187)
199  gtk_editable_delete_text(GTK_EDITABLE(password_entry_), 0, -1);
200  gtk_widget_destroy(dialog_);
201}
202
203void CryptoModulePasswordDialog::OnWindowDestroy(GtkWidget* widget) {
204  delete this;
205}
206
207}  // namespace
208
209// Every post-task we do blocks, so there's no need to ref-count.
210DISABLE_RUNNABLE_METHOD_REFCOUNT(CryptoModuleBlockingDialogDelegate);
211
212namespace browser {
213
214void ShowCryptoModulePasswordDialog(const std::string& slot_name,
215                                    bool retry,
216                                    CryptoModulePasswordReason reason,
217                                    const std::string& server,
218                                    CryptoModulePasswordCallback* callback) {
219  (new CryptoModulePasswordDialog(slot_name, retry, reason, server,
220                                  callback))->Show();
221}
222
223crypto::CryptoModuleBlockingPasswordDelegate*
224    NewCryptoModuleBlockingDialogDelegate(
225        CryptoModulePasswordReason reason,
226        const std::string& server) {
227  return new CryptoModuleBlockingDialogDelegate(reason, server);
228}
229
230}  // namespace browser
231