f3108d5117bbe16d5adbdb80598b48b018b24939
[modest] / src / modest-tny-account-store.c
1 /* Copyright (c) 2006, Nokia Corporation
2  * All rights reserved.
3  *
4  * Redistribution and use in source and binary forms, with or without
5  * modification, are permitted provided that the following conditions are
6  * met:
7  *
8  * * Redistributions of source code must retain the above copyright
9  *   notice, this list of conditions and the following disclaimer.
10  * * Redistributions in binary form must reproduce the above copyright
11  *   notice, this list of conditions and the following disclaimer in the
12  *   documentation and/or other materials provided with the distribution.
13  * * Neither the name of the Nokia Corporation nor the names of its
14  *   contributors may be used to endorse or promote products derived from
15  *   this software without specific prior written permission.
16  *
17  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
18  * IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
19  * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
20  * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER
21  * OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
22  * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
23  * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
24  * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
25  * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
26  * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
27  * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
28  */
29
30 #include <string.h>
31 #include <glib/gi18n.h>
32
33 #include <tny-error.h>
34 #include <tny-account.h>
35 #include <tny-account-store.h>
36 #include <tny-store-account.h>
37 #include <tny-transport-account.h>
38 #include <tny-simple-list.h>
39 #include <tny-account-store.h>
40 #include <tny-camel-transport-account.h>
41 #include <tny-camel-imap-store-account.h>
42 #include <tny-camel-pop-store-account.h>
43
44 #include <modest-runtime.h>
45 #include <modest-marshal.h>
46 #include <modest-protocol-info.h>
47 #include <modest-local-folder-info.h>
48 #include <modest-tny-account.h>
49 #include <modest-tny-local-folders-account.h>
50 #include <modest-account-mgr.h>
51 #include <modest-account-mgr-helpers.h>
52 #include <widgets/modest-window-mgr.h>
53 #include <modest-signal-mgr.h>
54 #include <modest-debug.h>
55
56 #include "modest-tny-account-store.h"
57 #include "modest-tny-platform-factory.h"
58 #include <tny-gtk-lockable.h>
59 #include <camel/camel.h>
60 #include <modest-platform.h>
61
62 #ifdef MODEST_PLATFORM_MAEMO
63 #include <tny-maemo-conic-device.h>
64 #include <maemo/modest-maemo-utils.h>
65 #include <maemo/modest-account-settings-dialog.h>
66 #endif
67
68 #include <libgnomevfs/gnome-vfs-volume-monitor.h>
69
70 /* 'private'/'protected' functions */
71 static void    modest_tny_account_store_class_init   (ModestTnyAccountStoreClass *klass);
72 static void    modest_tny_account_store_finalize     (GObject *obj);
73 static void    modest_tny_account_store_instance_init (ModestTnyAccountStore *obj);
74 static void    modest_tny_account_store_init          (gpointer g, gpointer iface_data);
75 static void    modest_tny_account_store_base_init     (gpointer g_class);
76
77 static void    on_account_inserted         (ModestAccountMgr *acc_mgr, 
78                                             const gchar *account,
79                                             gpointer user_data);
80
81 static void    add_existing_accounts       (ModestTnyAccountStore *self);
82
83 static void    insert_account              (ModestTnyAccountStore *self,
84                                             const gchar *account,
85                                             gboolean notify);
86
87 static void    on_account_removed          (ModestAccountMgr *acc_mgr, 
88                                             const gchar *account,
89                                             gpointer user_data);
90
91 static gchar*  get_password                (TnyAccount *account, 
92                                             const gchar * prompt_not_used, 
93                                             gboolean *cancel);
94
95 static void    forget_password             (TnyAccount *account);
96
97 static void    on_vfs_volume_mounted       (GnomeVFSVolumeMonitor *volume_monitor, 
98                                             GnomeVFSVolume *volume, 
99                                             gpointer user_data);
100
101 static void    on_vfs_volume_unmounted     (GnomeVFSVolumeMonitor *volume_monitor, 
102                                             GnomeVFSVolume *volume, 
103                                             gpointer user_data);
104
105 static void    modest_tny_account_store_forget_password_in_memory (ModestTnyAccountStore *self, 
106                                                                    const gchar *server_account_name);
107
108 static void    add_connection_specific_transport_accounts         (ModestTnyAccountStore *self);
109
110 /* list my signals */
111 enum {
112         ACCOUNT_CHANGED_SIGNAL,
113         ACCOUNT_INSERTED_SIGNAL,
114         ACCOUNT_REMOVED_SIGNAL,
115
116         PASSWORD_REQUESTED_SIGNAL,
117         LAST_SIGNAL
118 };
119
120 typedef struct _ModestTnyAccountStorePrivate ModestTnyAccountStorePrivate;
121 struct _ModestTnyAccountStorePrivate {
122         gchar              *cache_dir;  
123         GHashTable         *password_hash;
124 #ifdef MODEST_PLATFORM_MAEMO
125         GHashTable         *account_settings_dialog_hash;
126 #endif
127         ModestAccountMgr   *account_mgr;
128         TnySessionCamel    *session;
129         TnyDevice          *device;
130
131         GSList *sighandlers;
132         
133         /* We cache the lists of accounts here */
134         TnyList             *store_accounts;
135         TnyList             *transport_accounts;
136         TnyList             *store_accounts_outboxes;
137         
138         /* Matches transport accounts and outbox folder */
139         GHashTable          *outbox_of_transport;
140 };
141
142 #define MODEST_TNY_ACCOUNT_STORE_GET_PRIVATE(o)      (G_TYPE_INSTANCE_GET_PRIVATE((o), \
143                                                       MODEST_TYPE_TNY_ACCOUNT_STORE, \
144                                                       ModestTnyAccountStorePrivate))
145
146 /* globals */
147 static GObjectClass *parent_class = NULL;
148
149 static guint signals[LAST_SIGNAL] = {0};
150
151 GType
152 modest_tny_account_store_get_type (void)
153 {
154         static GType my_type = 0;
155
156         if (!my_type) {
157                 static const GTypeInfo my_info = {
158                         sizeof(ModestTnyAccountStoreClass),
159                         modest_tny_account_store_base_init,     /* base init */
160                         NULL,           /* base finalize */
161                         (GClassInitFunc) modest_tny_account_store_class_init,
162                         NULL,           /* class finalize */
163                         NULL,           /* class data */
164                         sizeof(ModestTnyAccountStore),
165                         0,              /* n_preallocs */
166                         (GInstanceInitFunc) modest_tny_account_store_instance_init,
167                         NULL
168                 };
169
170                 static const GInterfaceInfo iface_info = {
171                         (GInterfaceInitFunc) modest_tny_account_store_init,
172                         NULL,         /* interface_finalize */
173                         NULL          /* interface_data */
174                 };
175                 /* hack hack */
176                 my_type = g_type_register_static (G_TYPE_OBJECT,
177                                                   "ModestTnyAccountStore",
178                                                   &my_info, 0);
179                 g_type_add_interface_static (my_type, TNY_TYPE_ACCOUNT_STORE,
180                                              &iface_info);
181         }
182         return my_type;
183 }
184
185
186 static void
187 modest_tny_account_store_base_init (gpointer g_class)
188 {
189         static gboolean tny_account_store_initialized = FALSE;
190
191         if (!tny_account_store_initialized) {
192
193                 signals[ACCOUNT_CHANGED_SIGNAL] =
194                         g_signal_new ("account_changed",
195                                       MODEST_TYPE_TNY_ACCOUNT_STORE,
196                                       G_SIGNAL_RUN_FIRST,
197                                       G_STRUCT_OFFSET (ModestTnyAccountStoreClass, account_changed),
198                                       NULL, NULL,
199                                       g_cclosure_marshal_VOID__OBJECT,
200                                       G_TYPE_NONE, 1, TNY_TYPE_ACCOUNT);
201
202                 signals[ACCOUNT_INSERTED_SIGNAL] =
203                         g_signal_new ("account_inserted",
204                                       MODEST_TYPE_TNY_ACCOUNT_STORE,
205                                       G_SIGNAL_RUN_FIRST,
206                                       G_STRUCT_OFFSET (ModestTnyAccountStoreClass, account_inserted),
207                                       NULL, NULL,
208                                       g_cclosure_marshal_VOID__OBJECT,
209                                       G_TYPE_NONE, 1, TNY_TYPE_ACCOUNT);
210                 
211                 signals[ACCOUNT_REMOVED_SIGNAL] =
212                         g_signal_new ("account_removed",
213                                       MODEST_TYPE_TNY_ACCOUNT_STORE,
214                                       G_SIGNAL_RUN_FIRST,
215                                       G_STRUCT_OFFSET (ModestTnyAccountStoreClass, account_removed),
216                                       NULL, NULL,
217                                       g_cclosure_marshal_VOID__OBJECT,
218                                       G_TYPE_NONE, 1, TNY_TYPE_ACCOUNT);
219
220                 signals[PASSWORD_REQUESTED_SIGNAL] =
221                         g_signal_new ("password_requested",
222                                       MODEST_TYPE_TNY_ACCOUNT_STORE,
223                                       G_SIGNAL_RUN_FIRST,
224                                       G_STRUCT_OFFSET(ModestTnyAccountStoreClass, password_requested),
225                                       NULL, NULL,
226                                       modest_marshal_VOID__STRING_POINTER_POINTER_POINTER_POINTER,
227                                       G_TYPE_NONE, 5, G_TYPE_STRING, G_TYPE_POINTER, G_TYPE_POINTER, G_TYPE_POINTER,
228                                       G_TYPE_POINTER);          
229
230                 tny_account_store_initialized = TRUE;
231         }
232 }
233
234
235 static void
236 modest_tny_account_store_class_init (ModestTnyAccountStoreClass *klass)
237 {
238         GObjectClass *gobject_class;
239         gobject_class = (GObjectClass*) klass;
240
241         parent_class            = g_type_class_peek_parent (klass);
242         gobject_class->finalize = modest_tny_account_store_finalize;
243
244         g_type_class_add_private (gobject_class,
245                                   sizeof(ModestTnyAccountStorePrivate));
246 }
247      
248 static void
249 modest_tny_account_store_instance_init (ModestTnyAccountStore *obj)
250 {
251         GnomeVFSVolumeMonitor* monitor = NULL;
252         ModestTnyAccountStorePrivate *priv;
253
254         priv = MODEST_TNY_ACCOUNT_STORE_GET_PRIVATE(obj);
255
256         priv->cache_dir              = NULL;
257         priv->account_mgr            = NULL;
258         priv->session                = NULL;
259         priv->device                 = NULL;
260         priv->sighandlers            = NULL;
261         
262         priv->outbox_of_transport = g_hash_table_new_full (g_direct_hash,
263                                                            g_direct_equal,
264                                                            NULL,
265                                                            NULL);
266
267         /* An in-memory store of passwords, 
268          * for passwords that are not remembered in the configuration,
269          * so they need to be asked for from the user once in each session:
270          */
271         priv->password_hash = g_hash_table_new_full (g_str_hash, g_str_equal,
272                                                      g_free, g_free);
273
274 #ifdef MODEST_PLATFORM_MAEMO                                                         
275         /* A hash-map of modest account names to dialog pointers,
276          * so we can avoid showing the account settings twice for the same modest account: */                                 
277         priv->account_settings_dialog_hash = g_hash_table_new_full (g_str_hash, g_str_equal, 
278                                                                     g_free, NULL);
279 #endif
280         
281         /* Respond to volume mounts and unmounts, such 
282          * as the insertion/removal of the memory card: */
283         /* This is a singleton, so it does not need to be unrefed. */
284         monitor = gnome_vfs_get_volume_monitor();
285
286         priv->sighandlers = modest_signal_mgr_connect (priv->sighandlers, 
287                                                        G_OBJECT(monitor), 
288                                                        "volume-mounted",
289                                                        G_CALLBACK(on_vfs_volume_mounted),
290                                                        obj);
291         priv->sighandlers = modest_signal_mgr_connect (priv->sighandlers, 
292                                                        G_OBJECT(monitor), "volume-unmounted",
293                                                        G_CALLBACK(on_vfs_volume_unmounted),
294                                                        obj);
295 }
296
297 /* disconnect the list of TnyAccounts */
298 static void
299 account_disconnect (TnyAccount *account)
300 {
301         g_return_if_fail (account && TNY_IS_ACCOUNT(account));
302         tny_camel_account_set_online (TNY_CAMEL_ACCOUNT(account), FALSE, NULL, NULL);
303 }
304
305
306 /* disconnect the list of TnyAccounts */
307 static void
308 account_verify_last_ref (TnyAccount *account, const gchar *str)
309 {
310         gchar *txt;
311
312         g_return_if_fail (account && TNY_IS_ACCOUNT(account));
313
314         txt = g_strdup_printf ("%s: %s", str ? str : "?", tny_account_get_name(account));
315         MODEST_DEBUG_VERIFY_OBJECT_LAST_REF(G_OBJECT(account),txt);
316         g_free (txt);
317 }
318
319
320
321
322 static void
323 foreach_account_append_to_list (gpointer data, 
324                                 gpointer user_data)
325 {
326         TnyList *list;
327         
328         list = TNY_LIST (user_data);
329         tny_list_append (list, G_OBJECT (data));
330 }
331
332 /********************************************************************/
333 /*           Control the state of the MMC local account             */
334 /********************************************************************/
335
336 /** Only call this if the memory card is really mounted.
337  */ 
338 static void
339 add_mmc_account(ModestTnyAccountStore *self, gboolean emit_insert_signal)
340 {
341         ModestTnyAccountStorePrivate *priv = MODEST_TNY_ACCOUNT_STORE_GET_PRIVATE(self);
342         g_return_if_fail (priv->session);
343         
344         TnyAccount *mmc_account = modest_tny_account_new_for_local_folders (priv->account_mgr, 
345                                                                         priv->session, 
346                                                                         MODEST_MCC1_VOLUMEPATH);
347
348         /* Add to the list of store accounts */
349         tny_list_append (priv->store_accounts, G_OBJECT (mmc_account));
350
351         if (emit_insert_signal) {
352                 g_signal_emit (G_OBJECT (self), 
353                                signals [ACCOUNT_INSERTED_SIGNAL],
354                                0, mmc_account);
355         }
356
357         /* Free */
358         g_object_unref (mmc_account);
359 }
360
361 static void
362 on_vfs_volume_mounted(GnomeVFSVolumeMonitor *volume_monitor, 
363                       GnomeVFSVolume *volume, 
364                       gpointer user_data)
365 {
366         ModestTnyAccountStore *self;
367         ModestTnyAccountStorePrivate *priv;
368  
369         gchar *uri = NULL;
370
371         self = MODEST_TNY_ACCOUNT_STORE(user_data);
372         priv = MODEST_TNY_ACCOUNT_STORE_GET_PRIVATE(self);
373         
374         /* Check whether this was the external MMC1 card: */
375         uri = gnome_vfs_volume_get_activation_uri (volume);
376
377         if (uri && (!strcmp (uri, MODEST_MCC1_VOLUMEPATH_URI))) {
378                 add_mmc_account (self, TRUE /* emit the insert signal. */);
379         }
380         
381         g_free (uri);
382 }
383
384 static void
385 on_vfs_volume_unmounted(GnomeVFSVolumeMonitor *volume_monitor, 
386                         GnomeVFSVolume *volume, 
387                         gpointer user_data)
388 {
389         ModestTnyAccountStore *self;
390         ModestTnyAccountStorePrivate *priv;
391         gchar *uri = NULL;
392
393         self = MODEST_TNY_ACCOUNT_STORE(user_data);
394         priv = MODEST_TNY_ACCOUNT_STORE_GET_PRIVATE(self);
395         
396         /* Check whether this was the external MMC1 card: */
397         uri = gnome_vfs_volume_get_activation_uri (volume);
398         if (uri && (strcmp (uri, MODEST_MCC1_VOLUMEPATH_URI) == 0)) {
399                 TnyAccount *mmc_account = NULL;
400                 gboolean found = FALSE;
401                 TnyIterator *iter = NULL;
402
403                 iter = tny_list_create_iterator (priv->store_accounts);
404                 while (!tny_iterator_is_done (iter) && !found) {
405                         TnyAccount *account;
406
407                         account = TNY_ACCOUNT (tny_iterator_get_current (iter));
408                         if (modest_tny_account_is_memory_card_account (account)) {
409                                 found = TRUE;
410                                 mmc_account = g_object_ref (account);
411                         }
412                         g_object_unref (account);
413                         tny_iterator_next (iter);
414                 }
415                 g_object_unref (iter);
416
417                 if (found) {
418                         /* Remove from the list */
419                         tny_list_remove (priv->store_accounts, G_OBJECT (mmc_account));
420                         
421                         /* Notify observers */
422                         g_signal_emit (G_OBJECT (self),
423                                        signals [ACCOUNT_REMOVED_SIGNAL],
424                                        0, mmc_account);
425
426                         g_object_unref (mmc_account);
427                 } else {
428                         g_warning ("%s: there was no store account for the unmounted MMC",
429                                    __FUNCTION__);
430                 }
431         }
432         g_free (uri);
433 }
434
435 /**
436  * modest_tny_account_store_forget_password_in_memory
437  * @self: a TnyAccountStore instance
438  * @account: A server account.
439  * 
440  * Forget any password stored in memory for this account.
441  * For instance, this should be called when the user has changed the password in the account settings.
442  */
443 static void
444 modest_tny_account_store_forget_password_in_memory (ModestTnyAccountStore *self, const gchar * server_account_name)
445 {
446         /* printf ("DEBUG: %s\n", __FUNCTION__); */
447         ModestTnyAccountStorePrivate *priv = MODEST_TNY_ACCOUNT_STORE_GET_PRIVATE(self);
448
449         if (server_account_name && priv->password_hash) {
450                 g_hash_table_remove (priv->password_hash, server_account_name);
451         }
452 }
453
454 static void
455 on_account_changed (ModestAccountMgr *acc_mgr, 
456                     const gchar *account_name, 
457                     TnyAccountType account_type,
458                     gpointer user_data)
459 {
460         ModestTnyAccountStore *self = MODEST_TNY_ACCOUNT_STORE(user_data);
461         ModestTnyAccountStorePrivate *priv;
462         TnyList* account_list;
463         gboolean found = FALSE;
464         TnyIterator *iter = NULL;
465
466         priv = MODEST_TNY_ACCOUNT_STORE_GET_PRIVATE(self);
467         account_list = (account_type == TNY_ACCOUNT_TYPE_STORE ? 
468                         priv->store_accounts : 
469                         priv->transport_accounts);
470         
471         iter = tny_list_create_iterator (account_list);
472         while (!tny_iterator_is_done (iter) && !found) {
473                 TnyAccount *tny_account;
474                 tny_account = TNY_ACCOUNT (tny_iterator_get_current (iter));
475                 if (tny_account) {
476                         if (!strcmp (tny_account_get_id (tny_account), account_name)) {
477                                 found = TRUE;
478                                 modest_tny_account_update_from_account (tny_account, get_password, forget_password);
479                                 g_signal_emit (G_OBJECT(self), signals[ACCOUNT_CHANGED_SIGNAL], 0, tny_account);
480                         }
481                         g_object_unref (tny_account);
482                 }
483                 tny_iterator_next (iter);
484         }
485
486         if (iter)
487                 g_object_unref (iter);
488 }
489
490 #ifdef MODEST_PLATFORM_MAEMO
491 static void 
492 on_account_settings_hide (GtkWidget *widget, gpointer user_data)
493 {
494         /* This is easier than using a struct for the user_data: */
495         ModestTnyAccountStore *self = modest_runtime_get_account_store();
496         ModestTnyAccountStorePrivate *priv = MODEST_TNY_ACCOUNT_STORE_GET_PRIVATE(self);
497         
498         gchar *account_name = (gchar *) user_data;
499         if (account_name)
500                 g_hash_table_remove (priv->account_settings_dialog_hash, account_name);
501 }
502 #endif
503
504 static void 
505 show_password_warning_only ()
506 {
507         ModestWindow *main_window = 
508                 modest_window_mgr_get_main_window (modest_runtime_get_window_mgr (), FALSE); /* don't create */
509         
510         /* Show an explanatory temporary banner: */
511         if (main_window) 
512                 modest_platform_information_banner (GTK_WIDGET(main_window), NULL, 
513                                                     _("mcen_ib_username_pw_incorrect"));
514         else
515                 g_warning ("%s: %s", __FUNCTION__, _("mcen_ib_username_pw_incorrect"));
516 }
517
518 #ifdef MODEST_PLATFORM_MAEMO
519 static void 
520 show_wrong_password_dialog (TnyAccount *account)
521
522         /* This is easier than using a struct for the user_data: */
523         ModestTnyAccountStore *self = modest_runtime_get_account_store();
524         const gchar *modest_account_name;
525         GtkWidget *main_window;
526         GtkWidget *dialog;
527
528         main_window = (GtkWidget *) modest_window_mgr_get_main_window (modest_runtime_get_window_mgr (),
529                                                                        FALSE); /* don't create */
530         if (!main_window) {
531                 g_warning ("%s: password was wrong; ignoring because no main window", __FUNCTION__);
532                 return;
533         }
534
535         modest_account_name = modest_tny_account_get_parent_modest_account_name_for_server_account (account);
536         if (!modest_account_name) {
537                 g_warning ("%s: modest_tny_account_get_parent_modest_account_name_for_server_account() failed.\n", 
538                         __FUNCTION__);
539         }
540         
541         dialog = modest_tny_account_store_show_account_settings_dialog (self, modest_account_name);
542         modest_account_settings_dialog_save_password (MODEST_ACCOUNT_SETTINGS_DIALOG (dialog));
543         /* Show an explanatory temporary banner: */
544         modest_platform_information_banner (GTK_WIDGET(dialog), NULL, _("mcen_ib_username_pw_incorrect"));
545 }
546 #endif
547
548 /* This callback will be called by Tinymail when it needs the password
549  * from the user or the account settings.
550  * It can also call forget_password() before calling this,
551  * so that we clear wrong passwords out of our account settings.
552  * Note that TnyAccount here will be the server account. */
553 static gchar*
554 get_password (TnyAccount *account, const gchar * prompt_not_used, gboolean *cancel)
555 {
556         /* TODO: Settting cancel to FALSE does not actually cancel everything.
557          * We still get multiple requests afterwards, so we end up showing the 
558          * same dialogs repeatedly.
559          */       
560         const TnyAccountStore *account_store = NULL;
561         ModestTnyAccountStore *self = NULL;
562         ModestTnyAccountStorePrivate *priv;
563         gchar *username = NULL;
564         gchar *pwd = NULL;
565         gpointer pwd_ptr = NULL;
566         gboolean already_asked = FALSE;
567
568         g_return_val_if_fail (account, NULL);
569         
570         MODEST_DEBUG_BLOCK(
571                 g_debug ("%s: prompt (not shown) = %s\n", __FUNCTION__, prompt_not_used);
572         );
573         
574         /* Initialize the output parameter: */
575         if (cancel)
576                 *cancel = FALSE;
577                 
578         const gchar *server_account_name = tny_account_get_id (account);
579         account_store = TNY_ACCOUNT_STORE(g_object_get_data (G_OBJECT(account),
580                                                              "account_store"));
581
582         if (!server_account_name || !account_store) {
583                 g_warning ("modest: %s: could not retrieve account_store for account %s",
584                            __FUNCTION__, server_account_name ? server_account_name : "<NULL>");
585                 if (cancel)
586                         *cancel = TRUE;
587                 
588                 return NULL;
589         }
590
591         self = MODEST_TNY_ACCOUNT_STORE (account_store);
592         priv = MODEST_TNY_ACCOUNT_STORE_GET_PRIVATE(self);
593         
594         /* This hash map stores passwords, including passwords that are not stored in gconf. */
595         /* Is it in the hash? if it's already there, it must be wrong... */
596         pwd_ptr = (gpointer)&pwd; /* pwd_ptr so the compiler does not complained about
597                                    * type-punned ptrs...*/
598         already_asked = priv->password_hash && 
599                                 g_hash_table_lookup_extended (priv->password_hash,
600                                                       server_account_name,
601                                                       NULL,
602                                                       (gpointer*)&pwd_ptr);
603         MODEST_DEBUG_BLOCK(
604                 g_debug ("%s: Already asked = %d\n", __FUNCTION__, already_asked);
605         );
606                 
607         /* If the password is not already there, try ModestConf */
608         if (!already_asked) {
609                 pwd  = modest_account_mgr_get_server_account_password (priv->account_mgr,
610                                                                        server_account_name);
611                 g_hash_table_insert (priv->password_hash, g_strdup (server_account_name), g_strdup (pwd));
612         }
613
614         /* If it was already asked, it must have been wrong, so ask again */
615         if (already_asked || !pwd || strlen(pwd) == 0) {
616                 /* As per the UI spec, if no password was set in the account settings, 
617                  * ask for it now. But if the password is wrong in the account settings, 
618                  * then show a banner and the account settings dialog so it can be corrected:
619                  */
620                 const gboolean settings_have_password = 
621                         modest_account_mgr_get_server_account_has_password (priv->account_mgr, server_account_name);
622                 MODEST_DEBUG_BLOCK(
623                         printf ("%s: settings_have_password=%d\n",
624                                 __FUNCTION__, settings_have_password);
625                 );
626                 if (settings_have_password) {
627                         /* The password must be wrong, so show the account settings dialog so it can be corrected: */
628 #ifdef MODEST_PLATFORM_MAEMO
629                         show_wrong_password_dialog (account);
630 #endif
631                         
632                         if (cancel)
633                                 *cancel = TRUE;
634                                 
635                         return NULL;
636                 }
637         
638                 /* we don't have it yet. Get the password from the user */
639                 const gchar* account_id = tny_account_get_id (account);
640                 gboolean remember = FALSE;
641                 pwd = NULL;
642                 
643                 if (already_asked) {
644                         /* Show an info banner, before we show the protected password dialog: */
645                         show_password_warning_only();
646                 }
647
648                 /* Request password */
649                 g_signal_emit (G_OBJECT(account_store), signals[PASSWORD_REQUESTED_SIGNAL], 0,
650                                account_id, /* server_account_name */
651                                &username, &pwd, cancel, &remember);
652
653                 
654                 if (!*cancel) {
655                         /* The password will be returned as the result,
656                          * but we need to tell tinymail about the username too: */
657                         tny_account_set_user (account, username);
658                         
659                         /* Do not save the password in gconf, because
660                          * the UI spec says "The password will never
661                          * be saved in the account": */
662
663                         /* We need to dup the string even knowing that
664                            it's already a dup of the contents of an
665                            entry, because it if it's wrong, then camel
666                            will free it */
667                         g_hash_table_insert (priv->password_hash, g_strdup (server_account_name), g_strdup(pwd));
668                 } else {
669                         g_hash_table_remove (priv->password_hash, server_account_name);
670                         
671                         g_free (pwd);
672                         pwd = NULL;
673                 }
674
675                 g_free (username);
676                 username = NULL;
677         } else
678                 if (cancel)
679                         *cancel = FALSE;        
680         return pwd;
681 }
682
683 void 
684 modest_tny_account_store_forget_already_asked (ModestTnyAccountStore *self, TnyAccount *account)
685 {
686         g_return_if_fail (account);
687           
688         ModestTnyAccountStorePrivate *priv;
689         gchar *pwd = NULL;
690         gpointer pwd_ptr = NULL;
691         gboolean already_asked = FALSE;
692                 
693         const gchar *server_account_name = tny_account_get_id (account);
694
695         priv = MODEST_TNY_ACCOUNT_STORE_GET_PRIVATE(self);
696         
697         /* This hash map stores passwords, including passwords that are not stored in gconf. */
698         pwd_ptr = (gpointer)&pwd; /* pwd_ptr so the compiler does not complained about
699                                    * type-punned ptrs...*/
700         already_asked = priv->password_hash && 
701                                 g_hash_table_lookup_extended (priv->password_hash,
702                                                       server_account_name,
703                                                       NULL,
704                                                       (gpointer*)&pwd_ptr);
705
706         if (already_asked) {
707                 g_hash_table_remove (priv->password_hash, server_account_name);         
708                 g_free (pwd);
709                 pwd = NULL;
710         }
711
712         return;
713 }
714
715 /* tinymail calls this if the connection failed due to an incorrect password.
716  * And it seems to call this for any general connection failure. */
717 static void
718 forget_password (TnyAccount *account)
719 {
720         ModestTnyAccountStore *self;
721         ModestTnyAccountStorePrivate *priv;
722         const TnyAccountStore *account_store;
723         gchar *pwd;
724         const gchar *key;
725         
726         account_store = TNY_ACCOUNT_STORE(g_object_get_data (G_OBJECT(account),
727                                                              "account_store"));
728         self = MODEST_TNY_ACCOUNT_STORE (account_store);
729         priv = MODEST_TNY_ACCOUNT_STORE_GET_PRIVATE(self);
730         key  = tny_account_get_id (account);
731
732         /* Do not remove the key, this will allow us to detect that we
733            have already asked for it at least once */
734         pwd = g_hash_table_lookup (priv->password_hash, key);
735         if (pwd) {
736                 memset (pwd, 0, strlen (pwd));
737                 g_hash_table_insert (priv->password_hash, g_strdup (key), NULL);
738         }
739
740         /* Remove from configuration system */
741         /*
742         modest_account_mgr_unset (priv->account_mgr,
743                                   key, MODEST_ACCOUNT_PASSWORD, TRUE);
744         */
745 }
746
747 static void
748 modest_tny_account_store_finalize (GObject *obj)
749 {
750         ModestTnyAccountStore *self        = MODEST_TNY_ACCOUNT_STORE(obj);
751         ModestTnyAccountStorePrivate *priv = MODEST_TNY_ACCOUNT_STORE_GET_PRIVATE(self);
752
753         g_free (priv->cache_dir);
754         priv->cache_dir = NULL;
755         
756         if (priv->password_hash) {
757                 g_hash_table_destroy (priv->password_hash);
758                 priv->password_hash = NULL;
759         }
760
761 #ifdef MODEST_PLATFORM_MAEMO
762         if (priv->account_settings_dialog_hash) {
763                 g_hash_table_destroy (priv->account_settings_dialog_hash);
764                 priv->account_settings_dialog_hash = NULL;
765         }
766 #endif
767
768         if (priv->outbox_of_transport) {
769                 g_hash_table_destroy (priv->outbox_of_transport);
770                 priv->outbox_of_transport = NULL;
771         }
772
773         modest_signal_mgr_disconnect_all_and_destroy (priv->sighandlers);
774         priv->sighandlers = NULL;       
775
776         if (priv->account_mgr) {
777                 g_object_unref (G_OBJECT(priv->account_mgr));
778                 priv->account_mgr = NULL;
779         }
780
781         if (priv->device) {
782                 g_object_unref (G_OBJECT(priv->device));
783                 priv->device = NULL;
784         }
785
786         /* Destroy all accounts. Disconnect all accounts before they are destroyed */
787         if (priv->store_accounts) {
788                 tny_list_foreach (priv->store_accounts, (GFunc)account_disconnect, NULL);
789                 tny_list_foreach (priv->store_accounts, (GFunc)account_verify_last_ref, "store");
790                 g_object_unref (priv->store_accounts);
791                 priv->store_accounts = NULL;
792         }
793         
794         if (priv->transport_accounts) {
795                 tny_list_foreach (priv->transport_accounts, (GFunc)account_disconnect, NULL);
796                 tny_list_foreach (priv->transport_accounts, (GFunc)account_verify_last_ref, "transport");
797                 g_object_unref (priv->transport_accounts);
798                 priv->transport_accounts = NULL;
799         }
800
801         if (priv->store_accounts_outboxes) {
802                 g_object_unref (priv->store_accounts_outboxes);
803                 priv->store_accounts_outboxes = NULL;
804         }
805                 
806         if (priv->session) {
807                 camel_object_unref (CAMEL_OBJECT(priv->session));
808                 priv->session = NULL;
809         }
810
811         camel_shutdown ();
812         
813         G_OBJECT_CLASS(parent_class)->finalize (obj);
814 }
815
816 gboolean 
817 volume_path_is_mounted (const gchar* path)
818 {
819         g_return_val_if_fail (path, FALSE);
820
821         gboolean result = FALSE;
822         gchar * path_as_uri = g_filename_to_uri (path, NULL, NULL);
823         g_return_val_if_fail (path_as_uri, FALSE);
824
825         /* Get the monitor singleton: */
826         GnomeVFSVolumeMonitor *monitor = gnome_vfs_get_volume_monitor();
827
828         /* This seems like a simpler way to do this, but it returns a   
829          * GnomeVFSVolume even if the drive is not mounted: */
830         /*
831         GnomeVFSVolume *volume = gnome_vfs_volume_monitor_get_volume_for_path (monitor, 
832                 MODEST_MCC1_VOLUMEPATH);
833         if (volume) {
834                 gnome_vfs_volume_unref(volume);
835         }
836         */
837
838         /* Get the mounted volumes from the monitor: */
839         GList *list = gnome_vfs_volume_monitor_get_mounted_volumes (monitor);
840         GList *iter = list;
841         for (iter = list; iter; iter = g_list_next (iter)) {
842                 GnomeVFSVolume *volume = (GnomeVFSVolume*)iter->data;
843                 if (volume) {
844                         /*
845                         char *display_name = 
846                                 gnome_vfs_volume_get_display_name (volume);
847                         printf ("volume display name=%s\n", display_name);
848                         g_free (display_name);
849                         */
850                         
851                         char *uri = 
852                                 gnome_vfs_volume_get_activation_uri (volume);
853                         /* printf ("  uri=%s\n", uri); */
854                         if (uri && (strcmp (uri, path_as_uri) == 0))
855                                 result = TRUE;
856
857                         g_free (uri);
858
859                         gnome_vfs_volume_unref (volume);
860                 }
861         }
862
863         g_list_free (list);
864
865         g_free (path_as_uri);
866
867         return result;
868 }
869
870 ModestTnyAccountStore*
871 modest_tny_account_store_new (ModestAccountMgr *account_mgr, 
872                               TnyDevice *device) 
873 {
874         GObject *obj;
875         ModestTnyAccountStorePrivate *priv;
876         TnyAccount *local_account = NULL;
877         
878         g_return_val_if_fail (account_mgr, NULL);
879         g_return_val_if_fail (device, NULL);
880
881         obj  = G_OBJECT(g_object_new(MODEST_TYPE_TNY_ACCOUNT_STORE, NULL));
882         priv = MODEST_TNY_ACCOUNT_STORE_GET_PRIVATE(obj);
883
884         priv->account_mgr = g_object_ref (G_OBJECT(account_mgr));
885         priv->device = g_object_ref (device);
886         
887         priv->session = tny_session_camel_new (TNY_ACCOUNT_STORE(obj));
888         if (!priv->session) {
889                 g_warning ("failed to get TnySessionCamel");
890                 return NULL;
891         }
892
893         /* Set the ui locker */ 
894         tny_session_camel_set_ui_locker (priv->session,  tny_gtk_lockable_new ());
895         
896         /* Connect signals */
897         priv->sighandlers  =  modest_signal_mgr_connect (priv->sighandlers,
898                                                          G_OBJECT(account_mgr), "account_inserted",
899                                                          G_CALLBACK (on_account_inserted), obj);
900         priv->sighandlers  =  modest_signal_mgr_connect (priv->sighandlers,
901                                                          G_OBJECT(account_mgr), "account_changed",
902                                                          G_CALLBACK (on_account_changed), obj);
903         priv->sighandlers  =  modest_signal_mgr_connect (priv->sighandlers,
904                                                          G_OBJECT(account_mgr), "account_removed",
905                                                          G_CALLBACK (on_account_removed), obj);
906
907         /* Create the lists of accounts */
908         priv->store_accounts = tny_simple_list_new ();
909         priv->transport_accounts = tny_simple_list_new ();
910         priv->store_accounts_outboxes = tny_simple_list_new ();
911
912         /* Create the local folders account */
913         local_account = 
914                 modest_tny_account_new_for_local_folders (priv->account_mgr, priv->session, NULL);
915         tny_list_append (priv->store_accounts, G_OBJECT(local_account));
916         g_object_unref (local_account); 
917
918         /* Add the other remote accounts. Do this after adding the
919            local account, because we need to add our outboxes to the
920            global OUTBOX hosted in the local account */
921         add_existing_accounts (MODEST_TNY_ACCOUNT_STORE (obj));
922         
923         /* FIXME: I'm doing this (adding an "if (FALSE)"because this
924            stuff is not working properly and could cause SIGSEVs, for
925            example one send queue will be created for each connection
926            specific SMTP server, so when tinymail asks for the outbox
927            it will return NULL because there is no outbox folder for
928            this specific transport accounts, and it's a must that the
929            send queue returns an outbox */
930         if (TRUE)
931                 /* Add connection-specific transport accounts */
932                 add_connection_specific_transport_accounts (MODEST_TNY_ACCOUNT_STORE(obj));
933         
934         /* This is a singleton, so it does not need to be unrefed. */
935         if (volume_path_is_mounted (MODEST_MCC1_VOLUMEPATH)) {
936                 /* It is mounted: */
937                 add_mmc_account (MODEST_TNY_ACCOUNT_STORE (obj), FALSE /* don't emit the insert signal. */); 
938         }
939         
940         return MODEST_TNY_ACCOUNT_STORE(obj);
941 }
942
943 static void
944 modest_tny_account_store_get_accounts  (TnyAccountStore *self, 
945                                         TnyList *list,
946                                         TnyGetAccountsRequestType request_type)
947 {
948         ModestTnyAccountStorePrivate *priv;
949         
950         g_return_if_fail (self);
951         g_return_if_fail (TNY_IS_LIST(list));
952         
953         priv = MODEST_TNY_ACCOUNT_STORE_GET_PRIVATE(self);
954         
955         switch (request_type) {
956         case TNY_ACCOUNT_STORE_BOTH:
957                 tny_list_foreach (priv->store_accounts, foreach_account_append_to_list, list);
958                 tny_list_foreach (priv->transport_accounts, foreach_account_append_to_list, list);
959                 break;
960         case TNY_ACCOUNT_STORE_STORE_ACCOUNTS:
961                 tny_list_foreach (priv->store_accounts, foreach_account_append_to_list, list);
962                 break;
963         case TNY_ACCOUNT_STORE_TRANSPORT_ACCOUNTS:
964                 tny_list_foreach (priv->transport_accounts, foreach_account_append_to_list, list);
965                 break;
966         default:
967                 g_return_if_reached ();
968         }
969
970         /* Initialize session. Why do we need this ??? */
971         tny_session_camel_set_initialized (priv->session);
972 }
973
974
975 static const gchar*
976 modest_tny_account_store_get_cache_dir (TnyAccountStore *self)
977 {
978         ModestTnyAccountStorePrivate *priv;
979         priv = MODEST_TNY_ACCOUNT_STORE_GET_PRIVATE(self);
980         
981         if (!priv->cache_dir)
982                 priv->cache_dir = g_build_filename (g_get_home_dir(), 
983                                                     MODEST_DIR, MODEST_CACHE_DIR, NULL);
984         return priv->cache_dir;
985 }
986
987
988 /*
989  * callers need to unref
990  */
991 static TnyDevice*
992 modest_tny_account_store_get_device (TnyAccountStore *self)
993 {
994         ModestTnyAccountStorePrivate *priv;
995
996         g_return_val_if_fail (self, NULL);
997         
998         priv = MODEST_TNY_ACCOUNT_STORE_GET_PRIVATE(self);
999         
1000         if (priv->device) 
1001                 return g_object_ref (G_OBJECT(priv->device));
1002         else
1003                 return NULL;
1004 }
1005
1006
1007 static TnyAccount*
1008 modest_tny_account_store_find_account_by_url (TnyAccountStore *self, const gchar* url_string)
1009 {
1010         return modest_tny_account_store_get_tny_account_by (MODEST_TNY_ACCOUNT_STORE (self), 
1011                                                             MODEST_TNY_ACCOUNT_STORE_QUERY_URL,
1012                                                             url_string);
1013 }
1014
1015
1016
1017 static gboolean
1018 modest_tny_account_store_alert (TnyAccountStore *self, 
1019                                 TnyAccount *account, 
1020                                 TnyAlertType type,
1021                                 gboolean question, 
1022                                 GError *error)
1023 {
1024         ModestTransportStoreProtocol proto =
1025                 MODEST_PROTOCOL_TRANSPORT_STORE_UNKNOWN; 
1026         const gchar* server_name = NULL;
1027         gchar *prompt = NULL;
1028         gboolean retval;
1029
1030
1031         g_return_val_if_fail (account, FALSE);
1032         g_return_val_if_fail (error, FALSE);
1033         
1034         /* Get the server name: */
1035         server_name = tny_account_get_hostname (account);
1036
1037         if (account) {
1038                 const gchar *proto_name = tny_account_get_proto (account);
1039                 if (proto_name)
1040                         proto = modest_protocol_info_get_transport_store_protocol (proto_name);
1041                 else {
1042                         g_warning("modest: %s: account with id=%s has no proto.\n", __FUNCTION__, 
1043                                   tny_account_get_id (account));
1044                         return FALSE;
1045                 }
1046         }
1047
1048         switch (error->code) {
1049         case TNY_SYSTEM_ERROR_CANCEL:
1050                 /* Don't show waste the user's time by showing him a dialog telling 
1051                  * him that he has just cancelled something: */
1052                 return TRUE;
1053
1054         case TNY_SERVICE_ERROR_PROTOCOL:
1055                 /* Like a BAD from IMAP (protocol error) */
1056         case TNY_SERVICE_ERROR_LOST_CONNECTION:
1057                 /* Lost the connection with the service */
1058         case TNY_SERVICE_ERROR_UNAVAILABLE:
1059                 /* You must be working online for this operation */
1060         case TNY_SERVICE_ERROR_CONNECT:
1061                 switch (proto) {
1062                 case MODEST_PROTOCOL_STORE_POP:
1063                         prompt = g_strdup_printf (_("emev_ni_ui_pop3_msg_connect_error"),
1064                                                   server_name);
1065                         break;
1066                 case MODEST_PROTOCOL_STORE_IMAP:
1067                         prompt = g_strdup_printf (_("emev_ni_ui_imap_connect_server_error"),
1068                                                   server_name);
1069                         break;
1070                 case MODEST_PROTOCOL_TRANSPORT_SMTP:
1071                         prompt = g_strdup_printf (_("emev_ib_ui_smtp_server_invalid"),
1072                                                   server_name);
1073                         break;
1074                 default:
1075                         g_return_val_if_reached (FALSE);
1076                 }
1077                 break;
1078                 
1079         case TNY_SERVICE_ERROR_AUTHENTICATE:
1080                 /* It seems that there's no better error to show with
1081                  * POP and IMAP because TNY_SERVICE_ERROR_AUTHENTICATE
1082                  * may appear if there's a timeout during auth */
1083                 switch (proto) {
1084                 case MODEST_PROTOCOL_STORE_POP:
1085                         prompt = g_strdup_printf (_("emev_ni_ui_pop3_msg_connect_error"),
1086                                                   server_name);
1087                         break;
1088                 case MODEST_PROTOCOL_STORE_IMAP:
1089                         prompt = g_strdup_printf (_("emev_ni_ui_imap_connect_server_error"),
1090                                                   server_name);
1091                         break;
1092                 case MODEST_PROTOCOL_TRANSPORT_SMTP:
1093                         prompt = g_strdup_printf (_("emev_ni_ui_smtp_authentication_fail_error"),
1094                                                   server_name);
1095                         break;
1096                 default:
1097                         g_return_val_if_reached (FALSE);
1098                 }
1099                 break;
1100                         
1101         case TNY_SERVICE_ERROR_CERTIFICATE:
1102                 /* We'll show the proper dialog later */
1103                 break;
1104
1105         case TNY_SYSTEM_ERROR_MEMORY:
1106                 /* Can't allocate memory for this operation */
1107
1108         case TNY_SERVICE_ERROR_UNKNOWN: 
1109                 return FALSE;                   
1110         default:
1111                 g_return_val_if_reached (FALSE);
1112         }
1113         
1114
1115         if (error->code == TNY_SERVICE_ERROR_CERTIFICATE)
1116                 retval = modest_platform_run_certificate_confirmation_dialog (server_name,
1117                                                                               error->message);
1118         else
1119                 retval = modest_platform_run_alert_dialog (prompt, question);
1120         
1121         if (prompt)
1122                 g_free (prompt);
1123         
1124         return retval;
1125 }
1126
1127
1128 static void
1129 modest_tny_account_store_init (gpointer g, gpointer iface_data)
1130 {
1131         TnyAccountStoreIface *klass;
1132
1133         g_return_if_fail (g);
1134
1135         klass = (TnyAccountStoreIface *)g;
1136
1137         klass->get_accounts =
1138                 modest_tny_account_store_get_accounts;
1139         klass->get_cache_dir =
1140                 modest_tny_account_store_get_cache_dir;
1141         klass->get_device =
1142                 modest_tny_account_store_get_device;
1143         klass->alert =
1144                 modest_tny_account_store_alert;
1145         klass->find_account =
1146                 modest_tny_account_store_find_account_by_url;
1147 }
1148
1149 void
1150 modest_tny_account_store_set_get_pass_func (ModestTnyAccountStore *self,
1151                                             ModestTnyGetPassFunc func)
1152 {
1153         /* not implemented, we use signals */
1154         g_printerr ("modest: set_get_pass_func not implemented\n");
1155 }
1156
1157 TnySessionCamel*
1158 modest_tny_account_store_get_session  (TnyAccountStore *self)
1159 {
1160         g_return_val_if_fail (self, NULL);
1161         return MODEST_TNY_ACCOUNT_STORE_GET_PRIVATE (self)->session;
1162 }
1163
1164 static TnyAccount*
1165 get_tny_account_by (TnyList *accounts,
1166                     ModestTnyAccountStoreQueryType type,
1167                     const gchar *str)
1168 {
1169         TnyIterator *iter = NULL;
1170         gboolean found = FALSE;
1171         TnyAccount *retval = NULL;
1172
1173         g_return_val_if_fail (TNY_IS_LIST(accounts), NULL);
1174
1175         if (tny_list_get_length(accounts) == 0) {
1176                 g_warning ("%s: account list is empty", __FUNCTION__);
1177                 return NULL;
1178         }
1179         
1180         iter = tny_list_create_iterator (accounts);
1181         while (!tny_iterator_is_done (iter) && !found) {
1182                 TnyAccount *tmp_account = NULL;
1183                 const gchar *val = NULL;
1184
1185                 tmp_account = TNY_ACCOUNT (tny_iterator_get_current (iter));
1186                 if (!TNY_IS_ACCOUNT(tmp_account)) {
1187                         g_warning ("%s: not a valid account", __FUNCTION__);
1188                         tmp_account = NULL;
1189                         break;
1190                 }
1191
1192                 switch (type) {
1193                 case MODEST_TNY_ACCOUNT_STORE_QUERY_ID:
1194                         val = tny_account_get_id (tmp_account);
1195                         break;
1196                 case MODEST_TNY_ACCOUNT_STORE_QUERY_URL:
1197                         val = tny_account_get_url_string (tmp_account);
1198                         break;
1199                 }
1200                 
1201                 if (type == MODEST_TNY_ACCOUNT_STORE_QUERY_URL && 
1202                     tny_account_matches_url_string (tmp_account, str)) {
1203                         retval = g_object_ref (tmp_account);
1204                         found = TRUE;
1205                 } else {
1206                         if (val && str && strcmp (val, str) == 0) {
1207                                 retval = g_object_ref (tmp_account);
1208                                 found = TRUE;
1209                         }
1210                 }
1211                 g_object_unref (tmp_account);
1212                 tny_iterator_next (iter);
1213         }
1214         g_object_unref (iter);
1215
1216         return retval;
1217 }
1218
1219 TnyAccount*
1220 modest_tny_account_store_get_tny_account_by (ModestTnyAccountStore *self, 
1221                                              ModestTnyAccountStoreQueryType type,
1222                                              const gchar *str)
1223 {
1224         TnyAccount *account = NULL;
1225         ModestTnyAccountStorePrivate *priv;     
1226         
1227         g_return_val_if_fail (self, NULL);
1228         g_return_val_if_fail (str, NULL);
1229         
1230         priv = MODEST_TNY_ACCOUNT_STORE_GET_PRIVATE(self);
1231         
1232         /* Search in store accounts */
1233         account = get_tny_account_by (priv->store_accounts, type, str);
1234
1235         /* If we already found something, no need to search the transport accounts */
1236         if (!account) {
1237                 account = get_tny_account_by (priv->transport_accounts, type, str);
1238
1239                 /* If we already found something, no need to search the
1240                    per-account outbox accounts */
1241                 if (!account)
1242                         account = get_tny_account_by (priv->store_accounts_outboxes, type, str);
1243         }
1244
1245         /* Warn if nothing was found. This is generally unusual. */
1246         if (!account) {
1247                 g_warning("%s: Failed to find account with %s=%s\n", 
1248                           __FUNCTION__, 
1249                           (type == MODEST_TNY_ACCOUNT_STORE_QUERY_ID) ? "ID" : "URL",                     
1250                           str);
1251         }
1252
1253         /* Returns a new reference to the account if found */   
1254         return account;
1255 }
1256
1257
1258 TnyAccount*
1259 modest_tny_account_store_get_server_account (ModestTnyAccountStore *self,
1260                                              const gchar *account_name,
1261                                              TnyAccountType type)
1262 {
1263         ModestTnyAccountStorePrivate *priv = NULL;
1264         TnyAccount *retval = NULL;
1265         TnyList *account_list = NULL;
1266         TnyIterator *iter = NULL;
1267         gboolean found;
1268
1269         g_return_val_if_fail (self, NULL);
1270         g_return_val_if_fail (account_name, NULL);
1271         g_return_val_if_fail (type == TNY_ACCOUNT_TYPE_STORE || 
1272                               type == TNY_ACCOUNT_TYPE_TRANSPORT,
1273                               NULL);
1274         
1275         priv = MODEST_TNY_ACCOUNT_STORE_GET_PRIVATE(self);
1276
1277         account_list = (type == TNY_ACCOUNT_TYPE_STORE) ? 
1278                 priv->store_accounts : 
1279                 priv->transport_accounts;
1280
1281         if (!account_list) {
1282                 g_printerr ("%s: No server accounts of type %s\n", __FUNCTION__, 
1283                         (type == TNY_ACCOUNT_TYPE_STORE) ? "store" : "transport");
1284                 return NULL;
1285         }
1286         
1287         /* Look for the server account */
1288         found = FALSE;
1289         iter = tny_list_create_iterator (account_list);
1290         while (!tny_iterator_is_done (iter) && !found) {
1291                 const gchar *modest_acc_name;
1292                 TnyAccount *tmp_account;
1293
1294                 tmp_account = TNY_ACCOUNT (tny_iterator_get_current (iter));
1295                 modest_acc_name = 
1296                         modest_tny_account_get_parent_modest_account_name_for_server_account (tmp_account);
1297                 
1298                 if (account_name && modest_acc_name && !strcmp (account_name, modest_acc_name)) {
1299                         found = TRUE;
1300                         retval = g_object_ref (tmp_account);
1301                 }
1302                 /* Free and continue */
1303                 g_object_unref (tmp_account);
1304                 tny_iterator_next (iter);
1305         }
1306         g_object_unref (iter);
1307
1308         if (!found) {
1309                 g_printerr ("modest: %s: could not get tny %s account for %s\n." \
1310                             "Number of server accounts of this type=%d\n", __FUNCTION__,
1311                             (type == TNY_ACCOUNT_TYPE_STORE) ? "store" : "transport",
1312                             account_name, tny_list_get_length (account_list));
1313         }
1314
1315         /* Returns a new reference */
1316         return retval;
1317 }
1318
1319 TnyAccount*
1320 modest_tny_account_store_get_smtp_specific_transport_account_for_open_connection (
1321         ModestTnyAccountStore *self, const gchar *account_name)
1322 {
1323         TnyDevice *device;
1324
1325         g_return_val_if_fail (self && MODEST_IS_TNY_ACCOUNT_STORE(self), NULL);
1326         g_return_val_if_fail (account_name, NULL);
1327
1328         /* Get the current connection: */
1329         device = modest_runtime_get_device ();
1330
1331         if (!device) {
1332                 g_warning ("%s: could not get device", __FUNCTION__);
1333                 return NULL;
1334         }
1335                 
1336         if (!tny_device_is_online (device))
1337                 return NULL;
1338         
1339 #ifdef MODEST_HAVE_CONIC
1340         g_return_val_if_fail (TNY_IS_MAEMO_CONIC_DEVICE (device), NULL);
1341         
1342         TnyMaemoConicDevice *maemo_device = TNY_MAEMO_CONIC_DEVICE (device);    
1343         const gchar* iap_id = tny_maemo_conic_device_get_current_iap_id (maemo_device);
1344         /* printf ("DEBUG: %s: iap_id=%s\n", __FUNCTION__, iap_id); */
1345         if (!iap_id)
1346                 return NULL;
1347                 
1348         ConIcIap* connection = tny_maemo_conic_device_get_iap (maemo_device, iap_id);
1349         if (!connection)
1350                 return NULL;
1351                 
1352         const gchar *connection_id = con_ic_iap_get_id (connection);
1353         /* printf ("DEBUG: %s: connection_id=%s\n", __FUNCTION__, connection_id); */
1354         if (!connection_id)
1355                 return NULL;
1356         
1357         /*  Get the connection-specific transport acccount, if any: */
1358         ModestAccountMgr *account_manager = modest_runtime_get_account_mgr ();
1359
1360         /* Check if this account has connection-specific SMTP enabled */
1361         if (!modest_account_mgr_get_use_connection_specific_smtp (account_manager, account_name)) {
1362                 return NULL;
1363         }
1364
1365         gchar* server_account_name = modest_account_mgr_get_connection_specific_smtp (account_manager, 
1366                 connection_id);
1367
1368         /* printf ("DEBUG: %s: server_account_name=%s\n", __FUNCTION__, server_account_name); */
1369         if (!server_account_name) {
1370                 return NULL; /* No connection-specific SMTP server was specified for this connection. */
1371         }
1372                 
1373         TnyAccount* account = modest_tny_account_store_get_tny_account_by (self, 
1374                                                                            MODEST_TNY_ACCOUNT_STORE_QUERY_ID, 
1375                                                                            server_account_name);
1376
1377         /* printf ("DEBUG: %s: account=%p\n", __FUNCTION__, account); */
1378         g_free (server_account_name);   
1379
1380         /* Unref the get()ed object, as required by the tny_maemo_conic_device_get_iap() documentation. */
1381         g_object_unref (connection);
1382         
1383         return account;
1384 #else
1385         return NULL; /* TODO: Implement this for GNOME, instead of just Maemo? */
1386 #endif /* MODEST_HAVE_CONIC */
1387 }
1388
1389                                                                  
1390 TnyAccount*
1391 modest_tny_account_store_get_transport_account_for_open_connection (ModestTnyAccountStore *self,
1392                                                                     const gchar *account_name)
1393 {
1394         g_return_val_if_fail (self, NULL);
1395         g_return_val_if_fail (account_name, NULL);
1396
1397         if (!account_name || !self)
1398                 return NULL;
1399         
1400         /*  Get the connection-specific transport acccount, if any: */
1401         /* Note: This gives us a reference: */
1402         TnyAccount *account =
1403                 modest_tny_account_store_get_smtp_specific_transport_account_for_open_connection (self, account_name);
1404                         
1405         /* If there is no connection-specific transport account (the common case), 
1406          * just get the regular transport account: */
1407         if (!account) {
1408                 /* The special local folders don't have transport accounts. */
1409                 if (strcmp (account_name, MODEST_LOCAL_FOLDERS_ACCOUNT_ID) == 0)
1410                         account = NULL;
1411                 else {
1412                         /* Note: This gives us a reference: */
1413                         account = modest_tny_account_store_get_server_account (self, account_name, 
1414                                                      TNY_ACCOUNT_TYPE_TRANSPORT);
1415                 }
1416         }
1417                         
1418         /* returns a reference. */     
1419         return account;
1420 }
1421
1422 TnyAccount*
1423 modest_tny_account_store_get_local_folders_account (ModestTnyAccountStore *self)
1424 {
1425         TnyAccount *account = NULL;
1426         ModestTnyAccountStorePrivate *priv;
1427         TnyIterator *iter;
1428         gboolean found;
1429
1430         g_return_val_if_fail (MODEST_IS_TNY_ACCOUNT_STORE (self), NULL);
1431         
1432         priv = MODEST_TNY_ACCOUNT_STORE_GET_PRIVATE(self);
1433
1434         found = FALSE;
1435         iter = tny_list_create_iterator (priv->store_accounts);
1436         while (!tny_iterator_is_done (iter) && !found) {
1437                 TnyAccount *tmp_account;
1438
1439                 tmp_account = TNY_ACCOUNT (tny_iterator_get_current (iter));
1440                 if (modest_tny_account_is_virtual_local_folders (tmp_account)) {
1441                         account = g_object_ref (tmp_account);
1442                         found = TRUE;
1443                 }
1444                 g_object_unref (tmp_account);
1445                 tny_iterator_next (iter);
1446         }
1447         g_object_unref (iter);
1448
1449         /* Returns a new reference to the account */
1450         return account;
1451 }
1452
1453 TnyAccount*
1454 modest_tny_account_store_get_mmc_folders_account (ModestTnyAccountStore *self)
1455 {
1456         g_return_val_if_fail (MODEST_IS_TNY_ACCOUNT_STORE (self), NULL);
1457         
1458         /* New reference */
1459         return modest_tny_account_store_get_tny_account_by (self, MODEST_TNY_ACCOUNT_STORE_QUERY_ID,
1460                                                             MODEST_MMC_ACCOUNT_ID);
1461
1462 }
1463
1464 /*********************************************************************************/
1465 static void
1466 add_existing_accounts (ModestTnyAccountStore *self)
1467 {
1468         GSList *account_names = NULL, *iter = NULL;
1469         ModestTnyAccountStorePrivate *priv = NULL;
1470         
1471         priv = MODEST_TNY_ACCOUNT_STORE_GET_PRIVATE(self);
1472
1473         /* These are account names, not server_account names */
1474         account_names = modest_account_mgr_account_names (priv->account_mgr, FALSE);
1475
1476         for (iter = account_names; iter != NULL; iter = g_slist_next (iter)) {
1477                 const gchar *account_name = (const gchar*) iter->data;
1478                 
1479                 /* Insert all enabled accounts without notifying */
1480                 if (modest_account_mgr_get_enabled (priv->account_mgr, account_name))
1481                         insert_account (self, account_name, FALSE);
1482         }
1483         modest_account_mgr_free_account_names (account_names);
1484 }
1485
1486 static TnyAccount*
1487 create_tny_account (ModestTnyAccountStore *self,
1488                     const gchar *name,
1489                     TnyAccountType type)
1490 {
1491         TnyAccount *account = NULL;
1492         ModestTnyAccountStorePrivate *priv = NULL;
1493         
1494         priv = MODEST_TNY_ACCOUNT_STORE_GET_PRIVATE(self);
1495
1496         account = modest_tny_account_new_from_account (priv->account_mgr,
1497                                                        name, type, 
1498                                                        priv->session,
1499                                                        get_password,
1500                                                        forget_password);
1501
1502         if (account) {
1503                 /* Forget any cached password for the account, so that
1504                    we use a new account if any */
1505                 modest_tny_account_store_forget_password_in_memory (self, 
1506                                                                     tny_account_get_id (account));
1507                 /* Set the account store */                             
1508                 g_object_set_data (G_OBJECT(account), "account_store", self);
1509         } else {
1510                 g_printerr ("modest: failed to create account for %s\n", name);
1511         }
1512
1513         return account;
1514 }
1515
1516
1517 static void
1518 add_outbox_from_transport_account_to_global_outbox (ModestTnyAccountStore *self,
1519                                                     const gchar *account_name,
1520                                                     TnyAccount *transport_account)
1521 {
1522         TnyList *folders = NULL;
1523         TnyIterator *iter_folders = NULL;
1524         TnyAccount *local_account = NULL, *account_outbox = NULL;
1525         TnyFolder *per_account_outbox = NULL;
1526         ModestTnyAccountStorePrivate *priv = NULL;
1527
1528         priv = MODEST_TNY_ACCOUNT_STORE_GET_PRIVATE(self);
1529
1530         /* Create per account local outbox */
1531         account_outbox = 
1532                 modest_tny_account_new_for_per_account_local_outbox_folder (priv->account_mgr, 
1533                                                                             account_name, 
1534                                                                             priv->session);
1535         tny_list_append (priv->store_accounts_outboxes, G_OBJECT (account_outbox));
1536
1537         /* Get the outbox folder */
1538         folders = tny_simple_list_new ();
1539         tny_folder_store_get_folders (TNY_FOLDER_STORE (account_outbox), folders, NULL, NULL);
1540         if (tny_list_get_length (folders) != 1) {
1541                 g_warning ("%s: > 1 outbox found (%d)?!", __FUNCTION__,
1542                            tny_list_get_length (folders));
1543         }
1544                         
1545         iter_folders = tny_list_create_iterator (folders);
1546         per_account_outbox = TNY_FOLDER (tny_iterator_get_current (iter_folders));
1547         g_object_unref (iter_folders);
1548         g_object_unref (folders);
1549         g_object_unref (account_outbox);
1550
1551         /* Add the outbox of the new per-account-local-outbox account
1552            to the global local merged OUTBOX of the local folders
1553            account */
1554         local_account = modest_tny_account_store_get_local_folders_account (self);
1555         modest_tny_local_folders_account_add_folder_to_outbox (MODEST_TNY_LOCAL_FOLDERS_ACCOUNT (local_account),
1556                                                                per_account_outbox);
1557         /* Add the pair to the hash table */
1558         g_hash_table_insert (priv->outbox_of_transport,
1559                              transport_account,
1560                              per_account_outbox);
1561         
1562         g_object_unref (local_account);
1563         g_object_unref (per_account_outbox);
1564 }
1565
1566 /*
1567  * This function will be used for both adding new accounts and for the
1568  * initialization. In the initialization we do not want to emit
1569  * signals so notify will be FALSE, in the case of account additions
1570  * we do want to notify the observers
1571  */
1572 static void
1573 insert_account (ModestTnyAccountStore *self,
1574                 const gchar *account,
1575                 gboolean notify)
1576 {
1577         ModestTnyAccountStorePrivate *priv = NULL;
1578         TnyAccount *store_account = NULL, *transport_account = NULL;
1579         
1580         priv = MODEST_TNY_ACCOUNT_STORE_GET_PRIVATE(self);
1581
1582         /* Get the server and the transport account */
1583         store_account = create_tny_account (self, account, TNY_ACCOUNT_TYPE_STORE);
1584         if (!store_account || !TNY_IS_ACCOUNT(store_account)) {
1585                 g_warning ("%s: failed to create store account", __FUNCTION__);
1586                 return;
1587         }
1588
1589         transport_account = create_tny_account (self, account, TNY_ACCOUNT_TYPE_TRANSPORT);
1590         if (!transport_account || !TNY_IS_ACCOUNT(transport_account)) {
1591                 g_warning ("%s: failed to create transport account", __FUNCTION__);
1592                 g_object_unref (store_account);
1593                 return;
1594         }
1595
1596         /* Add accounts to the lists */
1597         tny_list_append (priv->store_accounts, G_OBJECT (store_account));
1598         tny_list_append (priv->transport_accounts, G_OBJECT (transport_account));
1599         
1600         /* Create a new pseudo-account with an outbox for this
1601            transport account and add it to the global outbox
1602            in the local account */
1603         add_outbox_from_transport_account_to_global_outbox (self, account, transport_account);
1604         
1605         /* Notify the observers. We do it after everything is
1606            created */
1607         if (notify) {
1608                 TnyAccount *local_account = NULL;
1609                 
1610                 /* Notify the observers about the new server & transport accounts */
1611                 g_signal_emit (G_OBJECT (self), signals [ACCOUNT_INSERTED_SIGNAL], 0, store_account);   
1612                 g_signal_emit (G_OBJECT (self), signals [ACCOUNT_INSERTED_SIGNAL], 0, transport_account);
1613
1614                 /* Notify that the local account changed */
1615                 local_account = modest_tny_account_store_get_local_folders_account (self);
1616                 g_signal_emit (G_OBJECT (self), signals [ACCOUNT_CHANGED_SIGNAL], 0, local_account);
1617                 g_object_unref (local_account);
1618         }
1619
1620         /* Frees */
1621         g_object_unref (store_account);
1622         g_object_unref (transport_account);
1623 }
1624
1625 static void
1626 on_account_inserted (ModestAccountMgr *acc_mgr, 
1627                      const gchar *account,
1628                      gpointer user_data)
1629 {
1630         /* Insert the account and notify the observers */
1631         insert_account (MODEST_TNY_ACCOUNT_STORE (user_data), account, TRUE);
1632 }
1633
1634 /* This is the callback of the tny_camel_account_set_online called in
1635    on_account_removed to disconnect the account */
1636 static void
1637 on_account_disconnect_when_removing (TnyCamelAccount *account, 
1638                                      gboolean canceled, 
1639                                      GError *err, 
1640                                      gpointer user_data)
1641 {
1642         ModestTnyAccountStore *self;
1643         ModestTnyAccountStorePrivate *priv;
1644
1645         self = MODEST_TNY_ACCOUNT_STORE (user_data);
1646         priv = MODEST_TNY_ACCOUNT_STORE_GET_PRIVATE (self);
1647
1648         /* Remove it from the list of accounts */
1649         if (TNY_IS_STORE_ACCOUNT (account))
1650                 tny_list_remove (priv->store_accounts, (GObject *) account);
1651         else
1652                 tny_list_remove (priv->transport_accounts, (GObject *) account);
1653
1654         /* Notify the observers */
1655         g_signal_emit (G_OBJECT (self), signals [ACCOUNT_REMOVED_SIGNAL], 
1656                        0, account);
1657         
1658         /* Unref the extra reference added by get_server_account */
1659         g_object_unref (account);
1660
1661         /* Cancel all pending operations */
1662         tny_account_cancel (TNY_ACCOUNT (account));
1663
1664         /* Clear the cache if it's an store account */
1665         if (TNY_IS_STORE_ACCOUNT (account))
1666                 tny_store_account_delete_cache (TNY_STORE_ACCOUNT (account));
1667 }
1668
1669 static void
1670 on_account_removed (ModestAccountMgr *acc_mgr, 
1671                     const gchar *account,
1672                     gpointer user_data)
1673 {
1674         TnyAccount *store_account = NULL, *transport_account = NULL;
1675         ModestTnyAccountStore *self;
1676         ModestTnyAccountStorePrivate *priv;
1677         
1678         self = MODEST_TNY_ACCOUNT_STORE (user_data);
1679         priv = MODEST_TNY_ACCOUNT_STORE_GET_PRIVATE (self);
1680
1681         /* Get the server and the transport account */
1682         store_account = 
1683                 modest_tny_account_store_get_server_account (self, account, TNY_ACCOUNT_TYPE_STORE);
1684         transport_account = 
1685                 modest_tny_account_store_get_server_account (self, account, TNY_ACCOUNT_TYPE_TRANSPORT);
1686         
1687         /* If there was any problem creating the account, for example,
1688            with the configuration system this could not exist */
1689         if (store_account) {
1690                 /* Disconnect before deleting the cache, because the
1691                    disconnection will rewrite the cache to the
1692                    disk */
1693                 tny_camel_account_set_online (TNY_CAMEL_ACCOUNT (store_account), FALSE,
1694                                               on_account_disconnect_when_removing, self);
1695         } else {
1696                 g_warning ("There is no store account for account %s\n", account);
1697         }
1698
1699         /* If there was any problem creating the account, for example,
1700            with the configuration system this could not exist */
1701         if (transport_account) {
1702                 TnyAccount *local_account = NULL;
1703                 TnyFolder *outbox = NULL;
1704                 ModestTnyAccountStorePrivate *priv = NULL;
1705         
1706                 /* Remove the OUTBOX of the account from the global outbox */
1707                 priv = MODEST_TNY_ACCOUNT_STORE_GET_PRIVATE(self);
1708                 outbox = g_hash_table_lookup (priv->outbox_of_transport, transport_account);
1709
1710                 if (TNY_IS_FOLDER (outbox)) {
1711                         local_account = modest_tny_account_store_get_local_folders_account (self);
1712                         modest_tny_local_folders_account_remove_folder_from_outbox (MODEST_TNY_LOCAL_FOLDERS_ACCOUNT (local_account),
1713                                                                                     outbox);
1714                         g_hash_table_remove (priv->outbox_of_transport, transport_account);
1715
1716                         /* Notify the change in the local account */
1717                         g_signal_emit (G_OBJECT (self), signals [ACCOUNT_CHANGED_SIGNAL], 0, local_account);
1718                         g_object_unref (local_account);
1719                 } else {
1720                         g_warning ("Removing a transport account that has no outbox");
1721                 }
1722
1723                 /* Disconnect and notify the observers. The callback will free the reference */
1724                 tny_camel_account_set_online (TNY_CAMEL_ACCOUNT (transport_account), FALSE,
1725                                               on_account_disconnect_when_removing, self);
1726         } else {
1727                 g_warning ("There is no transport account for account %s\n", account);
1728         }
1729 }
1730
1731 TnyTransportAccount *
1732 modest_tny_account_store_new_connection_specific_transport_account (ModestTnyAccountStore *self,
1733                                                                     const gchar *name)
1734 {
1735         ModestTnyAccountStorePrivate *priv = NULL;
1736         TnyAccount * tny_account = NULL;
1737
1738         priv = MODEST_TNY_ACCOUNT_STORE_GET_PRIVATE(self);
1739
1740         /* Add the account: */
1741         tny_account = 
1742                 modest_tny_account_new_from_server_account_name (priv->account_mgr, 
1743                                                                  priv->session, 
1744                                                                  name,
1745                                                                  get_password,
1746                                                                  forget_password);
1747         if (tny_account) {
1748                 g_object_set_data (G_OBJECT(tny_account), 
1749                                    "account_store", 
1750                                    (gpointer)self);
1751                 
1752                 tny_list_append (priv->transport_accounts, G_OBJECT (tny_account));
1753                 add_outbox_from_transport_account_to_global_outbox (self, 
1754                                                                     name, 
1755                                                                     tny_account);
1756                 
1757         } else
1758                 g_printerr ("modest: failed to create smtp-specific account for %s\n",
1759                             name);
1760
1761         return TNY_TRANSPORT_ACCOUNT (tny_account);
1762 }
1763
1764
1765 static void
1766 add_connection_specific_transport_accounts (ModestTnyAccountStore *self)
1767 {
1768         ModestTnyAccountStorePrivate *priv = NULL;
1769         GSList *list_specifics = NULL, *iter = NULL;
1770
1771         priv = MODEST_TNY_ACCOUNT_STORE_GET_PRIVATE(self);
1772
1773         ModestConf *conf = modest_runtime_get_conf ();
1774
1775         GError *err = NULL;
1776         list_specifics = modest_conf_get_list (conf,
1777                                                MODEST_CONF_CONNECTION_SPECIFIC_SMTP_LIST,
1778                                                MODEST_CONF_VALUE_STRING, &err);
1779         if (err) {
1780                 g_printerr ("modest: %s: error getting list: %s\n.", __FUNCTION__, err->message);
1781                 g_error_free (err);
1782                 err = NULL;
1783         }
1784                                 
1785         /* Look at each connection-specific transport account for the 
1786          * modest account: */
1787         iter = list_specifics;
1788         while (iter) {
1789                 /* The list alternates between the connection name and the transport name: */
1790                 iter = g_slist_next (iter);
1791                 if (iter) {
1792                         const gchar* transport_account_name = (const gchar*) (iter->data);
1793                         TnyTransportAccount * account = NULL;
1794                         account = modest_tny_account_store_new_connection_specific_transport_account (
1795                                 self, transport_account_name);
1796                         if (account)
1797                                 g_object_unref (account);
1798                 }                               
1799                 iter = g_slist_next (iter);
1800         }
1801 }
1802
1803 TnyMsg *
1804 modest_tny_account_store_find_msg_in_outboxes (ModestTnyAccountStore *self, 
1805                                                const gchar *uri,
1806                                                TnyAccount **ac_out)
1807 {
1808         TnyIterator *acc_iter;
1809         ModestTnyAccountStorePrivate *priv;
1810         TnyMsg *msg = NULL;
1811         TnyAccount *msg_account = NULL;
1812
1813         g_return_val_if_fail (MODEST_IS_TNY_ACCOUNT_STORE (self), NULL);
1814         priv = MODEST_TNY_ACCOUNT_STORE_GET_PRIVATE (self);
1815
1816         acc_iter = tny_list_create_iterator (priv->store_accounts_outboxes);
1817         while (!msg && !tny_iterator_is_done (acc_iter)) {
1818                 TnyList *folders = tny_simple_list_new ();
1819                 TnyAccount *account = TNY_ACCOUNT (tny_iterator_get_current (acc_iter));
1820                 TnyIterator *folders_iter = NULL;
1821
1822                 tny_folder_store_get_folders (TNY_FOLDER_STORE (account), folders, NULL, NULL);
1823                 folders_iter = tny_list_create_iterator (folders);
1824
1825                 while (msg == NULL && !tny_iterator_is_done (folders_iter)) {
1826                         TnyFolder *folder = TNY_FOLDER (tny_iterator_get_current (folders_iter));
1827                         msg = tny_folder_find_msg (folder, uri, NULL);
1828
1829                         if (msg)
1830                                 msg_account = g_object_ref (account);
1831
1832                         g_object_unref (folder);
1833                         tny_iterator_next (folders_iter);
1834                 }
1835                 g_object_unref (folders_iter);
1836
1837                 g_object_unref (folders);
1838                 g_object_unref (account);
1839                 tny_iterator_next (acc_iter);
1840         }
1841
1842         g_object_unref (acc_iter);
1843
1844         if (ac_out != NULL)
1845                 *ac_out = msg_account;
1846
1847         return msg;
1848 }
1849
1850 TnyTransportAccount *
1851 modest_tny_account_store_get_transport_account_from_outbox_header(ModestTnyAccountStore *self, TnyHeader *header)
1852 {
1853         TnyIterator *acc_iter;
1854         ModestTnyAccountStorePrivate *priv;
1855         TnyTransportAccount *header_acc = NULL;
1856         const gchar *msg_id;
1857
1858         g_return_val_if_fail (MODEST_IS_TNY_ACCOUNT_STORE (self), NULL);
1859         g_return_val_if_fail (TNY_IS_HEADER (header), NULL);
1860         priv = MODEST_TNY_ACCOUNT_STORE_GET_PRIVATE (self);
1861
1862         msg_id = modest_tny_send_queue_get_msg_id (header);
1863         acc_iter = tny_list_create_iterator (priv->transport_accounts);
1864         while (!header_acc && !tny_iterator_is_done (acc_iter)) {
1865                 TnyTransportAccount *account = TNY_TRANSPORT_ACCOUNT (tny_iterator_get_current (acc_iter));
1866                 ModestTnySendQueue *send_queue;
1867                 ModestTnySendQueueStatus status;
1868                 send_queue = modest_runtime_get_send_queue(TNY_TRANSPORT_ACCOUNT(account));
1869                 status = modest_tny_send_queue_get_msg_status(send_queue, msg_id);
1870                 if (status != MODEST_TNY_SEND_QUEUE_UNKNOWN) {
1871                         header_acc = g_object_ref(account);
1872                 }
1873                 g_object_unref (account);
1874                 tny_iterator_next (acc_iter);
1875         }
1876         g_object_unref(acc_iter);
1877
1878         /* New reference */
1879         return header_acc;
1880 }
1881
1882 GtkWidget *
1883 modest_tny_account_store_show_account_settings_dialog (ModestTnyAccountStore *self,
1884                                                       const gchar *account_name)
1885 {
1886         ModestTnyAccountStorePrivate *priv;
1887         gpointer dialog_as_gpointer = NULL;
1888         gboolean found;
1889
1890         priv = MODEST_TNY_ACCOUNT_STORE_GET_PRIVATE(self);
1891         found = g_hash_table_lookup_extended (priv->account_settings_dialog_hash,
1892                                               account_name, NULL, (gpointer*)&dialog_as_gpointer);
1893
1894         if (found)
1895                 return (GtkWidget *) dialog_as_gpointer;
1896         else {
1897                 ModestAccountSettings *settings;
1898                 GtkWidget *dialog;
1899                 dialog = (GtkWidget *) modest_account_settings_dialog_new ();
1900                 settings = modest_account_mgr_load_account_settings (priv->account_mgr, account_name);
1901                 modest_account_settings_dialog_set_account (MODEST_ACCOUNT_SETTINGS_DIALOG (dialog), settings);
1902                 g_object_unref (settings);
1903                 modest_account_settings_dialog_switch_to_user_info (MODEST_ACCOUNT_SETTINGS_DIALOG (dialog));
1904                 modest_window_mgr_set_modal (modest_runtime_get_window_mgr (), GTK_WINDOW (dialog));
1905                 
1906                 g_hash_table_insert (priv->account_settings_dialog_hash, g_strdup (account_name), dialog);
1907                 
1908                 g_signal_connect (G_OBJECT (dialog), "hide", G_CALLBACK (on_account_settings_hide), 
1909                                   g_strdup (account_name));
1910                         
1911                 /* Show it and delete it when it closes: */
1912                 g_signal_connect_swapped (dialog, 
1913                                           "response", 
1914                                           G_CALLBACK (gtk_widget_destroy), 
1915                                           dialog);
1916                 gtk_widget_show (GTK_WIDGET (dialog));
1917
1918                 return dialog;
1919         }
1920         
1921 }