src/mn-authenticated-mailbox.gob (14001B) - raw
1 /* 2 * Mail Notification 3 * Copyright (C) 2003-2008 Jean-Yves Lefort <jylefort@brutele.be> 4 * 5 * This program is free software; you can redistribute it and/or modify 6 * it under the terms of the GNU General Public License as published by 7 * the Free Software Foundation; either version 3 of the License, or 8 * (at your option) any later version. 9 * 10 * This program is distributed in the hope that it will be useful, 11 * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 * GNU General Public License for more details. 14 * 15 * You should have received a copy of the GNU General Public License along 16 * with this program; if not, write to the Free Software Foundation, Inc., 17 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. 18 */ 19 20 %headertop{ 21 #include <gnome-keyring.h> 22 #include "mn-mailbox.h" 23 %} 24 25 %privateheader{ 26 #include <gtk/gtk.h> 27 %} 28 29 %{ 30 #include <glib/gi18n.h> 31 #include <gnome.h> 32 #include "mn-mailbox-private.h" 33 #include "mn-shell.h" 34 #include "mn-util.h" 35 #include "mn-keyring.h" 36 %} 37 38 class MN:Authenticated:Mailbox from MN:Mailbox (abstract) 39 { 40 public char *username destroywith g_free; 41 property STRING username (link, flags = MN_MAILBOX_PARAM_LOAD_SAVE | MN_MAILBOX_PARAM_REQUIRED); 42 43 /* 44 * Set when: 45 * 46 * - the password is loaded from a mailboxes.xml file produced by 47 * an older version of MN 48 * - a mailbox has just been created by the properties dialog 49 * (where the user has entered a password) 50 * 51 * When the password is set here, it is automatically saved to the 52 * keyring in added(). Is is otherwise unused (when getting the 53 * password, this field is not consulted, the keyring is used 54 * instead). 55 */ 56 public char *password destroywith g_free; 57 property STRING password (link, flags = MN_MAILBOX_PARAM_LOAD); 58 59 /* last moment for subclasses to set these attributes is during seal() */ 60 protected char *keyring_username destroywith g_free; 61 protected char *keyring_domain destroywith g_free; 62 protected char *keyring_server destroywith g_free; 63 protected char *keyring_protocol destroywith g_free; 64 protected char *keyring_authtype destroywith g_free; 65 protected int keyring_port; 66 67 private gpointer get_password_request; 68 private gpointer set_password_request; 69 70 property BOOLEAN setting_password (export) 71 get { 72 g_value_set_boolean(VAL, selfp->set_password_request != NULL); 73 }; 74 75 private gboolean checking; 76 77 public char *runtime_password destroywith g_free; 78 79 private GtkWidget *auth_dialog; 80 protected gboolean auth_prompted; 81 protected gboolean auth_cancelled; /* user pressed Cancel or mailbox removed */ 82 protected gboolean auth_failed; 83 84 private char *prompted_password_keyring destroywith g_free; 85 private guint32 prompted_password_item_id; 86 87 class_init (class) 88 { 89 /* 90 * We might need to save the password to the keyring before the 91 * first check, so we will call mn_mailbox_enable_checking() 92 * ourselves. 93 */ 94 MN_MAILBOX_CLASS(class)->enable_checking_when_added = FALSE; 95 } 96 97 override (MN:Mailbox) void 98 seal (MNMailbox *mailbox) 99 { 100 Self *self = SELF(mailbox); 101 102 PARENT_HANDLER(mailbox); 103 104 if (self->password) 105 mn_shell->mailboxes->must_save_after_load = TRUE; 106 107 self_parse_username(self, &self->keyring_username, &self->keyring_domain); 108 } 109 110 override (MN:Mailbox) void 111 added (MNMailbox *mailbox) 112 { 113 Self *self = SELF(mailbox); 114 115 PARENT_HANDLER(mailbox); 116 117 /* 118 * If self->password is set, the password must be saved to the 119 * keyring (either because the user has just added the mailbox or 120 * because a mailboxes.xml produced by an older version of MN was 121 * loaded). Checking will be enabled after the password has been 122 * saved. Otherwise we must enable checking immediately. 123 */ 124 if (self->password) 125 self_set_password(self); 126 else 127 mn_mailbox_enable_checking(mailbox); 128 } 129 130 override (MN:Mailbox) void 131 removed (MNMailbox *mailbox) 132 { 133 Self *self = SELF(mailbox); 134 135 PARENT_HANDLER(mailbox); 136 137 if (selfp->auth_dialog) 138 gtk_dialog_response(GTK_DIALOG(selfp->auth_dialog), GTK_RESPONSE_CANCEL); 139 140 if (selfp->get_password_request) 141 gnome_keyring_cancel_request(selfp->get_password_request); 142 143 if (selfp->set_password_request) 144 gnome_keyring_cancel_request(selfp->set_password_request); 145 146 /* 147 * We do not remove the password from the keyring, since it is 148 * meant to be shared with other applications 149 */ 150 } 151 152 private void 153 set_password (self) 154 { 155 g_assert(self->password != NULL); 156 g_assert(selfp->set_password_request == NULL); 157 158 g_object_ref(self); 159 self_set_set_password_request(self, 160 gnome_keyring_set_network_password(NULL, 161 self->keyring_username, 162 self->keyring_domain, 163 self->keyring_server, 164 NULL, 165 self->keyring_protocol, 166 self->keyring_authtype, 167 self->keyring_port, 168 self->password, 169 self_set_password_cb, 170 self, 171 g_object_unref)); 172 } 173 174 private void 175 set_password_cb (GnomeKeyringResult result, 176 guint32 item_id, 177 gpointer data) 178 { 179 Self *self = data; 180 181 GDK_THREADS_ENTER(); 182 183 self_set_set_password_request(self, NULL); 184 185 /* 186 * If the mailbox has not been removed, display an error dialog if 187 * the password could not be saved and enable checking. 188 */ 189 if (mn_mailbox_get_active(MN_MAILBOX(self))) 190 { 191 self_handle_set_password_result(self, result); 192 193 mn_mailbox_enable_checking(MN_MAILBOX(self)); 194 } 195 196 /* do not call gdk_flush(), we're normally in the main thread */ 197 GDK_THREADS_LEAVE(); 198 } 199 200 private void 201 set_set_password_request (self, gpointer request) 202 { 203 selfp->set_password_request = request; 204 g_object_notify(G_OBJECT(self), "setting-password"); 205 } 206 207 override (MN:Mailbox) void 208 check (MNMailbox *mailbox) 209 { 210 Self *self = SELF(mailbox); 211 212 if (selfp->checking) 213 return; 214 215 PARENT_HANDLER(mailbox); 216 217 selfp->checking = TRUE; 218 mn_mailbox_set_error(mailbox, NULL); 219 220 g_object_ref(self); 221 mn_thread_create((GThreadFunc) self_check_thread_cb, self); 222 } 223 224 private void 225 check_thread_cb (self) 226 { 227 self_authenticated_check(self); 228 229 GDK_THREADS_ENTER(); 230 231 selfp->checking = FALSE; 232 g_object_unref(self); 233 234 gdk_flush(); 235 GDK_THREADS_LEAVE(); 236 } 237 238 virtual private void 239 authenticated_check (self) 240 { 241 self->auth_prompted = FALSE; 242 self->auth_cancelled = FALSE; 243 self->auth_failed = FALSE; 244 245 g_free(selfp->prompted_password_keyring); 246 selfp->prompted_password_keyring = NULL; 247 248 selfp->prompted_password_item_id = 0; 249 } 250 251 protected void 252 parse_username (self, char **username, char **domain) 253 { 254 char *at; 255 256 at = strrchr(self->username, '@'); 257 if (at) 258 { 259 if (username) 260 *username = g_strndup(self->username, at - self->username); 261 if (domain) 262 *domain = g_strdup(at + 1); 263 } 264 else 265 { 266 if (username) 267 *username = g_strdup(self->username); 268 if (domain) 269 *domain = NULL; 270 } 271 } 272 273 /* 274 * Returns FALSE if the authentication was cancelled (either because 275 * the user pressed Cancel in the password prompt dialog or because 276 * the mailbox was removed). 277 */ 278 protected gboolean 279 fill_password (self, gboolean may_prompt) 280 { 281 self->auth_cancelled = FALSE; 282 283 g_free(self->runtime_password); 284 self->runtime_password = self_get_password_sync(self); 285 286 /* 287 * Check if the user removed the mailbox while we were waiting for 288 * the keyring password. 289 */ 290 if (! mn_mailbox_get_active(MN_MAILBOX(self))) 291 { 292 self->auth_cancelled = TRUE; 293 return FALSE; 294 } 295 296 if (! self->runtime_password && may_prompt) 297 { 298 GDK_THREADS_ENTER(); 299 300 self->auth_prompted = TRUE; 301 302 if (! self_prompt_for_password(self, 303 &self->runtime_password, 304 self->auth_failed 305 /* translators: the first %s is the mailbox format (eg: IMAP) and the second %s is the mailbox name (eg: john@imapserver.org) */ 306 ? _("Mail Notification was unable to log into %s mailbox %s, possibly because the password you have entered is invalid.\n\nPlease re-enter your password.") 307 /* translators: the first %s is the mailbox format (eg: IMAP) and the second %s is the mailbox name (eg: john@imapserver.org) */ 308 : _("Enter your password for %s mailbox %s."), 309 MN_MAILBOX(self)->format, 310 MN_MAILBOX(self)->runtime_name)) 311 self->auth_cancelled = TRUE; 312 313 gdk_flush(); 314 GDK_THREADS_LEAVE(); 315 } 316 317 return ! self->auth_cancelled; 318 } 319 320 private char * 321 get_password_sync (self) 322 { 323 return mn_keyring_get_password_sync(self->keyring_username, 324 self->keyring_domain, 325 self->keyring_server, 326 self->keyring_protocol, 327 self->keyring_authtype, 328 self->keyring_port, 329 self_get_password_sync_request_cb, 330 self); 331 } 332 333 private void 334 get_password_sync_request_cb (gpointer request, gpointer data) 335 { 336 Self *self = data; 337 338 selfp->get_password_request = request; 339 } 340 341 protected void 342 auth_failed (self) 343 { 344 /* 345 * If the password had been entered at the password prompt, we 346 * must remove it from the keyring since the authentication has 347 * failed. 348 */ 349 if (self->auth_prompted && selfp->prompted_password_item_id) 350 { 351 self_delete_password(self, 352 selfp->prompted_password_keyring, 353 selfp->prompted_password_item_id); 354 355 g_free(selfp->prompted_password_keyring); 356 selfp->prompted_password_keyring = NULL; 357 358 selfp->prompted_password_item_id = 0; 359 } 360 361 self->auth_failed = TRUE; 362 } 363 364 private gboolean 365 prompt_for_password (self, 366 char **password (check null), 367 const char *format (check null), 368 ...) 369 attr {G_GNUC_PRINTF(3, 4)} 370 { 371 char *message; 372 gboolean ok; 373 374 g_return_val_if_fail(selfp->auth_dialog == NULL, FALSE); 375 376 MN_STRDUP_VPRINTF(message, format); 377 378 /* keep the title in sync with gnome-authentication-manager */ 379 380 /* translators: header capitalization */ 381 selfp->auth_dialog = gnome_password_dialog_new(_("Authentication Required"), 382 message, 383 self->username, 384 NULL, 385 FALSE); 386 g_free(message); 387 388 mn_add_weak_pointer(&selfp->auth_dialog); 389 390 gnome_password_dialog_set_show_userpass_buttons(GNOME_PASSWORD_DIALOG(selfp->auth_dialog), FALSE); 391 gnome_password_dialog_set_readonly_username(GNOME_PASSWORD_DIALOG(selfp->auth_dialog), TRUE); 392 gnome_password_dialog_set_show_remember(GNOME_PASSWORD_DIALOG(selfp->auth_dialog), TRUE); 393 gnome_password_dialog_set_remember(GNOME_PASSWORD_DIALOG(selfp->auth_dialog), GNOME_PASSWORD_DIALOG_REMEMBER_SESSION); 394 395 ok = gnome_password_dialog_run_and_block(GNOME_PASSWORD_DIALOG(selfp->auth_dialog)); 396 if (ok) 397 { 398 GnomePasswordDialogRemember remember; 399 400 *password = gnome_password_dialog_get_password(GNOME_PASSWORD_DIALOG(selfp->auth_dialog)); 401 402 remember = gnome_password_dialog_get_remember(GNOME_PASSWORD_DIALOG(selfp->auth_dialog)); 403 if (remember == GNOME_PASSWORD_DIALOG_REMEMBER_SESSION) 404 self_set_prompted_password(self, "session", *password); 405 else if (remember == GNOME_PASSWORD_DIALOG_REMEMBER_FOREVER) 406 self_set_prompted_password(self, NULL, *password); 407 } 408 409 gtk_widget_destroy(selfp->auth_dialog); 410 411 return ok; 412 } 413 414 private void 415 set_prompted_password (self, 416 const char *keyring, 417 const char *password (check null)) 418 { 419 GnomeKeyringResult result; 420 421 /* do not block the main loop */ 422 gdk_flush(); 423 GDK_THREADS_LEAVE(); 424 425 g_free(selfp->prompted_password_keyring); 426 selfp->prompted_password_keyring = g_strdup(keyring); 427 428 result = mn_keyring_set_password_sync(keyring, 429 self->keyring_username, 430 self->keyring_domain, 431 self->keyring_server, 432 self->keyring_protocol, 433 self->keyring_authtype, 434 self->keyring_port, 435 password, 436 &selfp->prompted_password_item_id, 437 self_set_prompted_password_request_cb, 438 self); 439 440 GDK_THREADS_ENTER(); 441 442 self_handle_set_password_result(self, result); 443 } 444 445 private void 446 set_prompted_password_request_cb (gpointer request, gpointer data) 447 { 448 Self *self = data; 449 450 GDK_THREADS_ENTER(); 451 452 self_set_set_password_request(self, request); 453 454 /* do not call gdk_flush(), we're normally in the main thread */ 455 GDK_THREADS_LEAVE(); 456 } 457 458 private void 459 handle_set_password_result (self, GnomeKeyringResult result) 460 { 461 if (result != GNOME_KEYRING_RESULT_OK && result != GNOME_KEYRING_RESULT_CANCELLED) 462 mn_show_error_dialog(NULL, 463 _("Unable to save the mailbox password"), 464 /* translators: the first %s is the mailbox format (eg: IMAP) and the second %s is the mailbox name (eg: john@imapserver.org) */ 465 _("The password of %s mailbox %s could not be saved to the keyring."), 466 MN_MAILBOX(self)->format, 467 MN_MAILBOX(self)->runtime_name); 468 } 469 470 public gpointer 471 get_password (self, 472 GnomeKeyringOperationGetListCallback cb, 473 gpointer data) 474 { 475 return gnome_keyring_find_network_password(self->keyring_username, 476 self->keyring_domain, 477 self->keyring_server, 478 NULL, 479 self->keyring_protocol, 480 self->keyring_authtype, 481 self->keyring_port, 482 cb, 483 data, 484 NULL); 485 } 486 487 private void 488 delete_password (self, 489 const char *keyring, 490 guint32 item_id (check != 0)) 491 { 492 char *default_keyring = NULL; 493 494 if (! keyring) 495 { 496 /* 497 * gnome_keyring_item_delete_sync() does not work if the 498 * passed keyring is NULL, so we must get the default keyring. 499 */ 500 if (gnome_keyring_get_default_keyring_sync(&default_keyring) != GNOME_KEYRING_RESULT_OK) 501 return; 502 503 keyring = default_keyring; 504 } 505 506 gnome_keyring_item_delete_sync(keyring, item_id); 507 508 g_free(default_keyring); 509 } 510 }