mail-notification

Fork of Jean-Yves Lefort's mail-notification, a tray icon to notify of new mail
git clone https://code.djc.id.au/git/mail-notification/

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 }