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