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