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 }