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 21 namespace { 22 23 class 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. 81 class 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 103 CryptoModulePasswordDialog::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 185 void CryptoModulePasswordDialog::Show() { 186 gtk_util::ShowDialog(dialog_); 187 } 188 189 void 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 203 void 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. 210 DISABLE_RUNNABLE_METHOD_REFCOUNT(CryptoModuleBlockingDialogDelegate); 211 212 namespace browser { 213 214 void 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 223 crypto::CryptoModuleBlockingPasswordDelegate* 224 NewCryptoModuleBlockingDialogDelegate( 225 CryptoModulePasswordReason reason, 226 const std::string& server) { 227 return new CryptoModuleBlockingDialogDelegate(reason, server); 228 } 229 230 } // namespace browser 231