* some cleanups for debugging:
[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                 hildon_banner_show_information ( 
524                         GTK_WIDGET(main_window), NULL, _("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         hildon_banner_show_information (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                 modest_maemo_show_dialog_and_forget (GTK_WINDOW (main_window), GTK_DIALOG (dialog));
585         }
586         else {
587                 /* Just show it instead of showing it and deleting it when it closes,
588                  * though it is probably open already: */
589                 gtk_window_present (GTK_WINDOW (dialog));
590         }
591 }
592
593
594
595 static void
596 request_password_and_wait (ModestTnyAccountStore *account_store, 
597                                          const gchar* server_account_id,
598                                          gchar **username,
599                                          gchar **password,
600                                          gboolean *cancel, 
601                                          gboolean *remember)
602 {
603         g_signal_emit (G_OBJECT(account_store), signals[PASSWORD_REQUESTED_SIGNAL], 0,
604                        server_account_id, /* server_account_name */
605                        username, password, cancel, remember);
606 }
607
608 /* This callback will be called by Tinymail when it needs the password
609  * from the user or the account settings.
610  * It can also call forget_password() before calling this,
611  * so that we clear wrong passwords out of our account settings.
612  * Note that TnyAccount here will be the server account. */
613 static gchar*
614 get_password (TnyAccount *account, const gchar * prompt_not_used, gboolean *cancel)
615 {
616         /* TODO: Settting cancel to FALSE does not actually cancel everything.
617          * We still get multiple requests afterwards, so we end up showing the 
618          * same dialogs repeatedly.
619          */       
620         const TnyAccountStore *account_store = NULL;
621         ModestTnyAccountStore *self = NULL;
622         ModestTnyAccountStorePrivate *priv;
623         gchar *username = NULL;
624         gchar *pwd = NULL;
625         gpointer pwd_ptr = NULL;
626         gboolean already_asked = FALSE;
627
628         g_return_val_if_fail (account, NULL);
629         
630         MODEST_DEBUG_BLOCK(
631                 g_debug ("DEBUG: modest: %s: prompt (not shown) = %s\n", __FUNCTION__, prompt_not_used);
632         );
633         
634         /* Initialize the output parameter: */
635         if (cancel)
636                 *cancel = FALSE;
637                 
638         const gchar *server_account_name = tny_account_get_id (account);
639         account_store = TNY_ACCOUNT_STORE(g_object_get_data (G_OBJECT(account),
640                                                              "account_store"));
641
642         if (!server_account_name || !account_store) {
643                 g_warning ("modest: %s: could not retrieve account_store for account %s",
644                            __FUNCTION__, server_account_name ? server_account_name : "<NULL>");
645                 if (cancel)
646                         *cancel = TRUE;
647                 
648                 return NULL;
649         }
650
651         self = MODEST_TNY_ACCOUNT_STORE (account_store);
652         priv = MODEST_TNY_ACCOUNT_STORE_GET_PRIVATE(self);
653         
654         /* This hash map stores passwords, including passwords that are not stored in gconf. */
655         /* Is it in the hash? if it's already there, it must be wrong... */
656         pwd_ptr = (gpointer)&pwd; /* pwd_ptr so the compiler does not complained about
657                                    * type-punned ptrs...*/
658         already_asked = priv->password_hash && 
659                                 g_hash_table_lookup_extended (priv->password_hash,
660                                                       server_account_name,
661                                                       NULL,
662                                                       (gpointer*)&pwd_ptr);
663         MODEST_DEBUG_BLOCK(
664                 g_debug ("DEBUG: modest: %s: Already asked = %d\n", __FUNCTION__, already_asked);
665         );
666                 
667         /* If the password is not already there, try ModestConf */
668         if (!already_asked) {
669                 pwd  = modest_account_mgr_get_server_account_password (priv->account_mgr,
670                                                                        server_account_name);
671                 g_hash_table_insert (priv->password_hash, g_strdup (server_account_name), g_strdup (pwd));
672         }
673
674         /* If it was already asked, it must have been wrong, so ask again */
675         if (already_asked || !pwd || strlen(pwd) == 0) {
676                 /* As per the UI spec, if no password was set in the account settings, 
677                  * ask for it now. But if the password is wrong in the account settings, 
678                  * then show a banner and the account settings dialog so it can be corrected:
679                  */
680                 const gboolean settings_have_password = 
681                         modest_account_mgr_get_server_account_has_password (priv->account_mgr, server_account_name);
682                 MODEST_DEBUG_BLOCK(
683                         printf ("DEBUG: modest: %s: settings_have_password=%d\n",
684                                 __FUNCTION__, settings_have_password);
685                 );
686                 if (settings_have_password) {
687                         /* The password must be wrong, so show the account settings dialog so it can be corrected: */
688                         show_wrong_password_dialog (account);
689                         
690                         if (cancel)
691                                 *cancel = TRUE;
692                                 
693                         return NULL;
694                 }
695         
696                 /* we don't have it yet. Get the password from the user */
697                 const gchar* account_id = tny_account_get_id (account);
698                 gboolean remember = FALSE;
699                 pwd = NULL;
700                 
701                 if (already_asked) {
702                         /* Show an info banner, before we show the protected password dialog: */
703                         show_password_warning_only();
704                 }
705                 
706                 request_password_and_wait (self, account_id, 
707                                &username, &pwd, cancel, &remember);
708                 
709                 if (!*cancel) {
710                         /* The password will be returned as the result,
711                          * but we need to tell tinymail about the username too: */
712                         tny_account_set_user (account, username);
713                         
714                         /* Do not save the password in gconf, because
715                          * the UI spec says "The password will never
716                          * be saved in the account": */
717
718                         /* We need to dup the string even knowing that
719                            it's already a dup of the contents of an
720                            entry, because it if it's wrong, then camel
721                            will free it */
722                         g_hash_table_insert (priv->password_hash, g_strdup (server_account_name), g_strdup(pwd));
723                 } else {
724                         g_hash_table_remove (priv->password_hash, server_account_name);
725                         
726                         g_free (pwd);
727                         pwd = NULL;
728                 }
729
730                 g_free (username);
731                 username = NULL;
732         } else
733                 if (cancel)
734                         *cancel = FALSE;        
735         return pwd;
736 }
737
738 void 
739 modest_tny_account_store_forget_already_asked (ModestTnyAccountStore *self, TnyAccount *account)
740 {
741         g_return_if_fail (account);
742           
743         ModestTnyAccountStorePrivate *priv;
744         gchar *pwd = NULL;
745         gpointer pwd_ptr = NULL;
746         gboolean already_asked = FALSE;
747                 
748         const gchar *server_account_name = tny_account_get_id (account);
749
750         priv = MODEST_TNY_ACCOUNT_STORE_GET_PRIVATE(self);
751         
752         /* This hash map stores passwords, including passwords that are not stored in gconf. */
753         pwd_ptr = (gpointer)&pwd; /* pwd_ptr so the compiler does not complained about
754                                    * type-punned ptrs...*/
755         already_asked = priv->password_hash && 
756                                 g_hash_table_lookup_extended (priv->password_hash,
757                                                       server_account_name,
758                                                       NULL,
759                                                       (gpointer*)&pwd_ptr);
760
761         if (already_asked) {
762                 g_hash_table_remove (priv->password_hash, server_account_name);         
763                 g_free (pwd);
764                 pwd = NULL;
765         }
766
767         return;
768 }
769
770 /* tinymail calls this if the connection failed due to an incorrect password.
771  * And it seems to call this for any general connection failure. */
772 static void
773 forget_password (TnyAccount *account)
774 {
775         printf ("DEBUG: %s\n", __FUNCTION__);
776         ModestTnyAccountStore *self;
777         ModestTnyAccountStorePrivate *priv;
778         const TnyAccountStore *account_store;
779         gchar *pwd;
780         const gchar *key;
781         
782         account_store = TNY_ACCOUNT_STORE(g_object_get_data (G_OBJECT(account),
783                                                              "account_store"));
784         self = MODEST_TNY_ACCOUNT_STORE (account_store);
785         priv = MODEST_TNY_ACCOUNT_STORE_GET_PRIVATE(self);
786         key  = tny_account_get_id (account);
787
788         /* Do not remove the key, this will allow us to detect that we
789            have already asked for it at least once */
790         pwd = g_hash_table_lookup (priv->password_hash, key);
791         if (pwd) {
792                 memset (pwd, 0, strlen (pwd));
793                 g_hash_table_insert (priv->password_hash, g_strdup (key), NULL);
794         }
795
796         /* Remove from configuration system */
797         /*
798         modest_account_mgr_unset (priv->account_mgr,
799                                   key, MODEST_ACCOUNT_PASSWORD, TRUE);
800         */
801 }
802
803 static void
804 modest_tny_account_store_finalize (GObject *obj)
805 {
806         ModestTnyAccountStore *self        = MODEST_TNY_ACCOUNT_STORE(obj);
807         ModestTnyAccountStorePrivate *priv = MODEST_TNY_ACCOUNT_STORE_GET_PRIVATE(self);
808
809         g_free (priv->cache_dir);
810         priv->cache_dir = NULL;
811         
812         if (priv->password_hash) {
813                 g_hash_table_destroy (priv->password_hash);
814                 priv->password_hash = NULL;
815         }
816
817         if (priv->account_settings_dialog_hash) {
818                 g_hash_table_destroy (priv->account_settings_dialog_hash);
819                 priv->account_settings_dialog_hash = NULL;
820         }
821
822         if (priv->outbox_of_transport) {
823                 g_hash_table_destroy (priv->outbox_of_transport);
824                 priv->outbox_of_transport = NULL;
825         }
826
827         modest_signal_mgr_disconnect_all_and_destroy (priv->sighandlers);
828         priv->sighandlers = NULL;       
829
830         if (priv->account_mgr) {
831                 g_object_unref (G_OBJECT(priv->account_mgr));
832                 priv->account_mgr = NULL;
833         }
834
835         if (priv->device) {
836                 g_object_unref (G_OBJECT(priv->device));
837                 priv->device = NULL;
838         }
839
840         /* Destroy all accounts. Disconnect all accounts before they are destroyed */
841         if (priv->store_accounts) {
842                 tny_list_foreach (priv->store_accounts, (GFunc)account_disconnect, NULL);
843                 tny_list_foreach (priv->store_accounts, (GFunc)account_verify_last_ref, "store");
844                 g_object_unref (priv->store_accounts);
845                 priv->store_accounts = NULL;
846         }
847         
848         if (priv->transport_accounts) {
849                 tny_list_foreach (priv->transport_accounts, (GFunc)account_disconnect, NULL);
850                 tny_list_foreach (priv->transport_accounts, (GFunc)account_verify_last_ref, "transport");
851                 g_object_unref (priv->transport_accounts);
852                 priv->transport_accounts = NULL;
853         }
854
855         if (priv->store_accounts_outboxes) {
856                 g_object_unref (priv->store_accounts_outboxes);
857                 priv->store_accounts_outboxes = NULL;
858         }
859                 
860         if (priv->session) {
861                 camel_object_unref (CAMEL_OBJECT(priv->session));
862                 priv->session = NULL;
863         }
864         
865         G_OBJECT_CLASS(parent_class)->finalize (obj);
866 }
867
868 gboolean 
869 volume_path_is_mounted (const gchar* path)
870 {
871         g_return_val_if_fail (path, FALSE);
872
873         gboolean result = FALSE;
874         gchar * path_as_uri = g_filename_to_uri (path, NULL, NULL);
875         g_return_val_if_fail (path_as_uri, FALSE);
876
877         /* Get the monitor singleton: */
878         GnomeVFSVolumeMonitor *monitor = gnome_vfs_get_volume_monitor();
879
880         /* This seems like a simpler way to do this, but it returns a   
881          * GnomeVFSVolume even if the drive is not mounted: */
882         /*
883         GnomeVFSVolume *volume = gnome_vfs_volume_monitor_get_volume_for_path (monitor, 
884                 MODEST_MCC1_VOLUMEPATH);
885         if (volume) {
886                 gnome_vfs_volume_unref(volume);
887         }
888         */
889
890         /* Get the mounted volumes from the monitor: */
891         GList *list = gnome_vfs_volume_monitor_get_mounted_volumes (monitor);
892         GList *iter = list;
893         for (iter = list; iter; iter = g_list_next (iter)) {
894                 GnomeVFSVolume *volume = (GnomeVFSVolume*)iter->data;
895                 if (volume) {
896                         /*
897                         char *display_name = 
898                                 gnome_vfs_volume_get_display_name (volume);
899                         printf ("volume display name=%s\n", display_name);
900                         g_free (display_name);
901                         */
902                         
903                         char *uri = 
904                                 gnome_vfs_volume_get_activation_uri (volume);
905                         /* printf ("  uri=%s\n", uri); */
906                         if (uri && (strcmp (uri, path_as_uri) == 0))
907                                 result = TRUE;
908
909                         g_free (uri);
910
911                         gnome_vfs_volume_unref (volume);
912                 }
913         }
914
915         g_list_free (list);
916
917         g_free (path_as_uri);
918
919         return result;
920 }
921
922 ModestTnyAccountStore*
923 modest_tny_account_store_new (ModestAccountMgr *account_mgr, 
924                               TnyDevice *device) 
925 {
926         GObject *obj;
927         ModestTnyAccountStorePrivate *priv;
928         TnyAccount *local_account = NULL;
929         
930         g_return_val_if_fail (account_mgr, NULL);
931         g_return_val_if_fail (device, NULL);
932
933         obj  = G_OBJECT(g_object_new(MODEST_TYPE_TNY_ACCOUNT_STORE, NULL));
934         priv = MODEST_TNY_ACCOUNT_STORE_GET_PRIVATE(obj);
935
936         priv->account_mgr = g_object_ref (G_OBJECT(account_mgr));
937         priv->device = g_object_ref (device);
938         
939         priv->session = tny_session_camel_new (TNY_ACCOUNT_STORE(obj));
940         if (!priv->session) {
941                 g_warning ("failed to get TnySessionCamel");
942                 return NULL;
943         }
944
945         /* Set the ui locker */ 
946         tny_session_camel_set_ui_locker (priv->session,  tny_gtk_lockable_new ());
947         
948         /* Connect signals */
949         priv->sighandlers  =  modest_signal_mgr_connect (priv->sighandlers,
950                                                          G_OBJECT(account_mgr), "account_inserted",
951                                                          G_CALLBACK (on_account_inserted), obj);
952         priv->sighandlers  =  modest_signal_mgr_connect (priv->sighandlers,
953                                                          G_OBJECT(account_mgr), "account_changed",
954                                                          G_CALLBACK (on_account_changed), obj);
955         priv->sighandlers  =  modest_signal_mgr_connect (priv->sighandlers,
956                                                          G_OBJECT(account_mgr), "account_removed",
957                                                          G_CALLBACK (on_account_removed), obj);
958
959         /* Create the lists of accounts */
960         priv->store_accounts = tny_simple_list_new ();
961         priv->transport_accounts = tny_simple_list_new ();
962         priv->store_accounts_outboxes = tny_simple_list_new ();
963
964         /* Create the local folders account */
965         local_account = 
966                 modest_tny_account_new_for_local_folders (priv->account_mgr, priv->session, NULL);
967         tny_list_append (priv->store_accounts, G_OBJECT(local_account));
968         g_object_unref (local_account); 
969
970         /* Add the other remote accounts. Do this after adding the
971            local account, because we need to add our outboxes to the
972            global OUTBOX hosted in the local account */
973         add_existing_accounts (MODEST_TNY_ACCOUNT_STORE (obj));
974         
975         /* FIXME: I'm doing this (adding an "if (FALSE)"because this
976            stuff is not working properly and could cause SIGSEVs, for
977            example one send queue will be created for each connection
978            specific SMTP server, so when tinymail asks for the outbox
979            it will return NULL because there is no outbox folder for
980            this specific transport accounts, and it's a must that the
981            send queue returns an outbox */
982         if (TRUE)
983                 /* Add connection-specific transport accounts */
984                 add_connection_specific_transport_accounts (MODEST_TNY_ACCOUNT_STORE(obj));
985         
986         /* This is a singleton, so it does not need to be unrefed. */
987         if (volume_path_is_mounted (MODEST_MCC1_VOLUMEPATH)) {
988                 /* It is mounted: */
989                 add_mmc_account (MODEST_TNY_ACCOUNT_STORE (obj), FALSE /* don't emit the insert signal. */); 
990         }
991         
992         return MODEST_TNY_ACCOUNT_STORE(obj);
993 }
994
995 static void
996 modest_tny_account_store_get_accounts  (TnyAccountStore *self, 
997                                         TnyList *list,
998                                         TnyGetAccountsRequestType request_type)
999 {
1000         ModestTnyAccountStorePrivate *priv;
1001         
1002         g_return_if_fail (self);
1003         g_return_if_fail (TNY_IS_LIST(list));
1004         
1005         priv = MODEST_TNY_ACCOUNT_STORE_GET_PRIVATE(self);
1006         
1007         switch (request_type) {
1008         case TNY_ACCOUNT_STORE_BOTH:
1009                 tny_list_foreach (priv->store_accounts, foreach_account_append_to_list, list);
1010                 tny_list_foreach (priv->transport_accounts, foreach_account_append_to_list, list);
1011                 break;
1012         case TNY_ACCOUNT_STORE_STORE_ACCOUNTS:
1013                 tny_list_foreach (priv->store_accounts, foreach_account_append_to_list, list);
1014                 break;
1015         case TNY_ACCOUNT_STORE_TRANSPORT_ACCOUNTS:
1016                 tny_list_foreach (priv->transport_accounts, foreach_account_append_to_list, list);
1017                 break;
1018         default:
1019                 g_return_if_reached ();
1020         }
1021
1022         /* Initialize session. Why do we need this ??? */
1023         tny_session_camel_set_initialized (priv->session);
1024 }
1025
1026
1027 static const gchar*
1028 modest_tny_account_store_get_cache_dir (TnyAccountStore *self)
1029 {
1030         ModestTnyAccountStorePrivate *priv;
1031         priv = MODEST_TNY_ACCOUNT_STORE_GET_PRIVATE(self);
1032         
1033         if (!priv->cache_dir)
1034                 priv->cache_dir = g_build_filename (g_get_home_dir(), 
1035                                                     MODEST_DIR, MODEST_CACHE_DIR, NULL);
1036         return priv->cache_dir;
1037 }
1038
1039
1040 /*
1041  * callers need to unref
1042  */
1043 static TnyDevice*
1044 modest_tny_account_store_get_device (TnyAccountStore *self)
1045 {
1046         ModestTnyAccountStorePrivate *priv;
1047
1048         g_return_val_if_fail (self, NULL);
1049         
1050         priv = MODEST_TNY_ACCOUNT_STORE_GET_PRIVATE(self);
1051         
1052         if (priv->device) 
1053                 return g_object_ref (G_OBJECT(priv->device));
1054         else
1055                 return NULL;
1056 }
1057
1058
1059 static TnyAccount*
1060 modest_tny_account_store_find_account_by_url (TnyAccountStore *self, const gchar* url_string)
1061 {
1062         return modest_tny_account_store_get_tny_account_by (MODEST_TNY_ACCOUNT_STORE (self), 
1063                                                             MODEST_TNY_ACCOUNT_STORE_QUERY_URL,
1064                                                             url_string);
1065 }
1066
1067
1068
1069 static gboolean
1070 modest_tny_account_store_alert (TnyAccountStore *self, 
1071                                 TnyAccount *account, 
1072                                 TnyAlertType type,
1073                                 gboolean question, 
1074                                 const GError *error)
1075 {
1076         ModestTransportStoreProtocol proto =
1077                 MODEST_PROTOCOL_TRANSPORT_STORE_UNKNOWN; 
1078         const gchar* server_name = NULL;
1079         gchar *prompt = NULL;
1080         gboolean retval;
1081
1082
1083         g_return_val_if_fail (account, FALSE);
1084         g_return_val_if_fail (error, FALSE);
1085         
1086         if ((error->domain != TNY_ACCOUNT_ERROR) && (error->domain != TNY_ACCOUNT_STORE_ERROR))
1087                 return FALSE;
1088         
1089         /* Get the server name: */
1090         server_name = tny_account_get_hostname (account);
1091
1092         if (account) {
1093                 const gchar *proto_name = tny_account_get_proto (account);
1094                 if (proto_name)
1095                         proto = modest_protocol_info_get_transport_store_protocol (proto_name);
1096                 else {
1097                         g_warning("modest: %s: account with id=%s has no proto.\n", __FUNCTION__, 
1098                                   tny_account_get_id (account));
1099                         return FALSE;
1100                 }
1101         }
1102
1103         switch (error->code) {
1104         case TNY_ACCOUNT_STORE_ERROR_CANCEL_ALERT:
1105         case TNY_ACCOUNT_ERROR_TRY_CONNECT_USER_CANCEL:
1106                 /* Don't show waste the user's time by showing him a dialog telling 
1107                  * him that he has just cancelled something: */
1108                 return TRUE;
1109
1110         case TNY_ACCOUNT_ERROR_TRY_CONNECT_HOST_LOOKUP_FAILED:
1111         case TNY_ACCOUNT_ERROR_TRY_CONNECT_SERVICE_UNAVAILABLE:
1112                 /* TODO: Show the appropriate message, depending on whether it's POP or IMAP: */                
1113                 switch (proto) {
1114                 case MODEST_PROTOCOL_STORE_POP:
1115                         prompt = g_strdup_printf (_("emev_ni_ui_pop3_msg_connect_error"),
1116                                                   server_name);
1117                         break;
1118                 case MODEST_PROTOCOL_STORE_IMAP:
1119                         prompt = g_strdup_printf (_("emev_ni_ui_imap_connect_server_error"),
1120                                                   server_name);
1121                         break;
1122                 case MODEST_PROTOCOL_TRANSPORT_SMTP:
1123                         prompt = g_strdup_printf (_("emev_ib_ui_smtp_server_invalid"),
1124                                                   server_name);
1125                         break;
1126                 default:
1127                         g_return_val_if_reached (FALSE);
1128                 }
1129                 break;
1130                 
1131         case TNY_ACCOUNT_ERROR_TRY_CONNECT_AUTHENTICATION_NOT_SUPPORTED:
1132                 /* This is "Secure connection failed", even though the logical
1133                  * ID has _certificate_ in the name: */
1134                 prompt = g_strdup (_("mail_ni_ssl_certificate_error")); 
1135                 break;
1136                         
1137         case TNY_ACCOUNT_ERROR_TRY_CONNECT_CERTIFICATE:
1138                 /* We'll show the proper dialog later */
1139                 break;
1140                 
1141         case TNY_ACCOUNT_ERROR_TRY_CONNECT:
1142                 /* The tinymail camel implementation just sends us this for almost 
1143                  * everything, so we have to guess at the cause.
1144                  * It could be a wrong password, or inability to resolve a hostname, 
1145                  * or lack of network, or incorrect authentication method, or something entirely different: */
1146                 /* TODO: Fix camel to provide specific error codes, and then use the 
1147                  * specific dialog messages from Chapter 12 of the UI spec.
1148                  */
1149         case TNY_ACCOUNT_STORE_ERROR_UNKNOWN_ALERT: 
1150                 return FALSE;                   
1151         default:
1152                 g_return_val_if_reached (FALSE);
1153         }
1154         
1155
1156         if (error->code == TNY_ACCOUNT_ERROR_TRY_CONNECT_CERTIFICATE)
1157                 retval = modest_platform_run_certificate_confirmation_dialog (server_name,
1158                                                                               error->message);
1159         else
1160                 retval = modest_platform_run_alert_dialog (prompt, question);
1161         
1162         if (prompt)
1163                 g_free (prompt);
1164         
1165         return retval;
1166 }
1167
1168
1169 static void
1170 modest_tny_account_store_init (gpointer g, gpointer iface_data)
1171 {
1172         TnyAccountStoreIface *klass;
1173
1174         g_return_if_fail (g);
1175
1176         klass = (TnyAccountStoreIface *)g;
1177
1178         klass->get_accounts_func =
1179                 modest_tny_account_store_get_accounts;
1180         klass->get_cache_dir_func =
1181                 modest_tny_account_store_get_cache_dir;
1182         klass->get_device_func =
1183                 modest_tny_account_store_get_device;
1184         klass->alert_func =
1185                 modest_tny_account_store_alert;
1186         klass->find_account_func =
1187                 modest_tny_account_store_find_account_by_url;
1188 }
1189
1190 void
1191 modest_tny_account_store_set_get_pass_func (ModestTnyAccountStore *self,
1192                                             ModestTnyGetPassFunc func)
1193 {
1194         /* not implemented, we use signals */
1195         g_printerr ("modest: set_get_pass_func not implemented\n");
1196 }
1197
1198 TnySessionCamel*
1199 modest_tny_account_store_get_session  (TnyAccountStore *self)
1200 {
1201         g_return_val_if_fail (self, NULL);
1202         return MODEST_TNY_ACCOUNT_STORE_GET_PRIVATE (self)->session;
1203 }
1204
1205 static TnyAccount*
1206 get_tny_account_by (TnyList *accounts,
1207                     ModestTnyAccountStoreQueryType type,
1208                     const gchar *str)
1209 {
1210         TnyIterator *iter = NULL;
1211         gboolean found = FALSE;
1212         TnyAccount *retval = NULL;
1213
1214         iter = tny_list_create_iterator (accounts);
1215         while (!tny_iterator_is_done (iter) && !found) {
1216                 TnyAccount *tmp_account = NULL;
1217                 const gchar *val = NULL;
1218
1219                 tmp_account = TNY_ACCOUNT (tny_iterator_get_current (iter));
1220                 switch (type) {
1221                 case MODEST_TNY_ACCOUNT_STORE_QUERY_ID:
1222                         val = tny_account_get_id (tmp_account);
1223                         break;
1224                 case MODEST_TNY_ACCOUNT_STORE_QUERY_URL:
1225                         val = tny_account_get_url_string (tmp_account);
1226                         break;
1227                 }
1228                 
1229                 if (type == MODEST_TNY_ACCOUNT_STORE_QUERY_URL && 
1230                     tny_account_matches_url_string (tmp_account, str)) {
1231                         retval = g_object_ref (tmp_account);
1232                         found = TRUE;
1233                 } else {
1234                         if (strcmp (val, str) == 0) {
1235                                 retval = g_object_ref (tmp_account);
1236                                 found = TRUE;
1237                         }
1238                 }
1239                 g_object_unref (tmp_account);
1240                 tny_iterator_next (iter);
1241         }
1242         g_object_unref (iter);
1243
1244         return retval;
1245 }
1246
1247 TnyAccount*
1248 modest_tny_account_store_get_tny_account_by (ModestTnyAccountStore *self, 
1249                                              ModestTnyAccountStoreQueryType type,
1250                                              const gchar *str)
1251 {
1252         TnyAccount *account = NULL;
1253         ModestTnyAccountStorePrivate *priv;     
1254         
1255         g_return_val_if_fail (self, NULL);
1256         g_return_val_if_fail (str, NULL);
1257         
1258         priv = MODEST_TNY_ACCOUNT_STORE_GET_PRIVATE(self);
1259         
1260         /* Search in store accounts */
1261         account = get_tny_account_by (priv->store_accounts, type, str);
1262
1263         /* If we already found something, no need to search the transport accounts */
1264         if (!account) {
1265                 account = get_tny_account_by (priv->transport_accounts, type, str);
1266
1267                 /* If we already found something, no need to search the
1268                    per-account outbox accounts */
1269                 if (!account)
1270                         account = get_tny_account_by (priv->store_accounts_outboxes, type, str);
1271         }
1272
1273         /* Warn if nothing was found. This is generally unusual. */
1274         if (!account) {
1275                 g_warning("%s: Failed to find account with %s=%s\n", 
1276                           __FUNCTION__, 
1277                           (type == MODEST_TNY_ACCOUNT_STORE_QUERY_ID) ? "ID" : "URL",                     
1278                           str);
1279         }
1280
1281         /* Returns a new reference to the account if found */   
1282         return account;
1283 }
1284
1285
1286 TnyAccount*
1287 modest_tny_account_store_get_server_account (ModestTnyAccountStore *self,
1288                                              const gchar *account_name,
1289                                              TnyAccountType type)
1290 {
1291         ModestTnyAccountStorePrivate *priv = NULL;
1292         TnyAccount *retval = NULL;
1293         TnyList *account_list = NULL;
1294         TnyIterator *iter = NULL;
1295         gboolean found;
1296
1297         g_return_val_if_fail (self, NULL);
1298         g_return_val_if_fail (account_name, NULL);
1299         g_return_val_if_fail (type == TNY_ACCOUNT_TYPE_STORE || 
1300                               type == TNY_ACCOUNT_TYPE_TRANSPORT,
1301                               NULL);
1302         
1303         priv = MODEST_TNY_ACCOUNT_STORE_GET_PRIVATE(self);
1304
1305         account_list = (type == TNY_ACCOUNT_TYPE_STORE) ? 
1306                 priv->store_accounts : 
1307                 priv->transport_accounts;
1308
1309         if (!account_list) {
1310                 g_printerr ("%s: No server accounts of type %s\n", __FUNCTION__, 
1311                         (type == TNY_ACCOUNT_TYPE_STORE) ? "store" : "transport");
1312                 return NULL;
1313         }
1314         
1315         /* Look for the server account */
1316         found = FALSE;
1317         iter = tny_list_create_iterator (account_list);
1318         while (!tny_iterator_is_done (iter) && !found) {
1319                 const gchar *modest_acc_name;
1320                 TnyAccount *tmp_account;
1321
1322                 tmp_account = TNY_ACCOUNT (tny_iterator_get_current (iter));
1323                 modest_acc_name = 
1324                         modest_tny_account_get_parent_modest_account_name_for_server_account (tmp_account);
1325                 
1326                 if (!strcmp (account_name, modest_acc_name)) {
1327                         found = TRUE;
1328                         retval = g_object_ref (tmp_account);
1329                 }
1330                 /* Free and continue */
1331                 g_object_unref (tmp_account);
1332                 tny_iterator_next (iter);
1333         }
1334
1335         if (!found) {
1336                 g_printerr ("modest: %s: could not get tny %s account for %s\n." \
1337                             "Number of server accounts of this type=%d\n", __FUNCTION__,
1338                             (type == TNY_ACCOUNT_TYPE_STORE) ? "store" : "transport",
1339                             account_name, tny_list_get_length (account_list));
1340         }
1341
1342         /* Returns a new reference */
1343         return retval;
1344 }
1345
1346 TnyAccount*
1347 modest_tny_account_store_get_smtp_specific_transport_account_for_open_connection (ModestTnyAccountStore *self,
1348                                                                                   const gchar *account_name)
1349 {
1350         /* Get the current connection: */
1351         TnyDevice *device = modest_runtime_get_device ();
1352         
1353         if (!tny_device_is_online (device))
1354                 return NULL;
1355
1356         g_return_val_if_fail (self, NULL);
1357         
1358         
1359 #ifdef MODEST_PLATFORM_MAEMO
1360         g_assert (TNY_IS_MAEMO_CONIC_DEVICE (device));
1361         TnyMaemoConicDevice *maemo_device = TNY_MAEMO_CONIC_DEVICE (device);    
1362         const gchar* iap_id = tny_maemo_conic_device_get_current_iap_id (maemo_device);
1363         /* printf ("DEBUG: %s: iap_id=%s\n", __FUNCTION__, iap_id); */
1364         if (!iap_id)
1365                 return NULL;
1366                 
1367         ConIcIap* connection = tny_maemo_conic_device_get_iap (maemo_device, iap_id);
1368         if (!connection)
1369                 return NULL;
1370                 
1371         const gchar *connection_name = con_ic_iap_get_name (connection);
1372         /* printf ("DEBUG: %s: connection_name=%s\n", __FUNCTION__, connection_name); */
1373         if (!connection_name)
1374                 return NULL;
1375         
1376         /*  Get the connection-specific transport acccount, if any: */
1377         ModestAccountMgr *account_manager = modest_runtime_get_account_mgr ();
1378
1379         /* Check if this account has connection-specific SMTP enabled */
1380         if (!modest_account_mgr_get_use_connection_specific_smtp (account_manager, account_name)) {
1381                 return NULL;
1382         }
1383
1384         gchar* server_account_name = modest_account_mgr_get_connection_specific_smtp (account_manager, 
1385                 connection_name);
1386
1387         /* printf ("DEBUG: %s: server_account_name=%s\n", __FUNCTION__, server_account_name); */
1388         if (!server_account_name) {
1389                 return NULL; /* No connection-specific SMTP server was specified for this connection. */
1390         }
1391                 
1392         TnyAccount* account = modest_tny_account_store_get_tny_account_by (self, 
1393                                                                            MODEST_TNY_ACCOUNT_STORE_QUERY_ID, 
1394                                                                            server_account_name);
1395
1396         /* printf ("DEBUG: %s: account=%p\n", __FUNCTION__, account); */
1397         g_free (server_account_name);   
1398
1399         /* Unref the get()ed object, as required by the tny_maemo_conic_device_get_iap() documentation. */
1400         g_object_unref (connection);
1401         
1402         return account;
1403 #else
1404         return NULL; /* TODO: Implement this for GNOME, instead of just Maemo? */
1405 #endif /* MODEST_PLATFORM_MAEMO */
1406 }
1407
1408                                                                  
1409 TnyAccount*
1410 modest_tny_account_store_get_transport_account_for_open_connection (ModestTnyAccountStore *self,
1411                                                                     const gchar *account_name)
1412 {
1413         g_return_val_if_fail (self, NULL);
1414         g_return_val_if_fail (account_name, NULL);
1415
1416         if (!account_name || !self)
1417                 return NULL;
1418         
1419         /*  Get the connection-specific transport acccount, if any: */
1420         /* Note: This gives us a reference: */
1421         TnyAccount *account =
1422                 modest_tny_account_store_get_smtp_specific_transport_account_for_open_connection (self, account_name);
1423                         
1424         /* If there is no connection-specific transport account (the common case), 
1425          * just get the regular transport account: */
1426         if (!account) {
1427                 /* The special local folders don't have transport accounts. */
1428                 if (strcmp (account_name, MODEST_LOCAL_FOLDERS_ACCOUNT_ID) == 0)
1429                         account = NULL;
1430                 else {
1431                         /* Note: This gives us a reference: */
1432                         account = modest_tny_account_store_get_server_account (self, account_name, 
1433                                                      TNY_ACCOUNT_TYPE_TRANSPORT);
1434                 }
1435         }
1436                         
1437         /* returns a reference. */     
1438         return account;
1439 }
1440
1441 TnyAccount*
1442 modest_tny_account_store_get_local_folders_account (ModestTnyAccountStore *self)
1443 {
1444         TnyAccount *account = NULL;
1445         ModestTnyAccountStorePrivate *priv;
1446         TnyIterator *iter;
1447         gboolean found;
1448
1449         g_return_val_if_fail (MODEST_IS_TNY_ACCOUNT_STORE (self), NULL);
1450         
1451         priv = MODEST_TNY_ACCOUNT_STORE_GET_PRIVATE(self);
1452
1453         found = FALSE;
1454         iter = tny_list_create_iterator (priv->store_accounts);
1455         while (!tny_iterator_is_done (iter) && !found) {
1456                 TnyAccount *tmp_account;
1457
1458                 tmp_account = TNY_ACCOUNT (tny_iterator_get_current (iter));
1459                 if (modest_tny_account_is_virtual_local_folders (tmp_account)) {
1460                         account = g_object_ref (tmp_account);
1461                         found = TRUE;
1462                 }
1463                 g_object_unref (tmp_account);
1464                 tny_iterator_next (iter);
1465         }
1466         g_object_unref (iter);
1467
1468         /* Returns a new reference to the account */
1469         return account;
1470 }
1471
1472 TnyAccount*
1473 modest_tny_account_store_get_mmc_folders_account (ModestTnyAccountStore *self)
1474 {
1475         g_return_val_if_fail (MODEST_IS_TNY_ACCOUNT_STORE (self), NULL);
1476         
1477         return modest_tny_account_store_get_tny_account_by (self, MODEST_TNY_ACCOUNT_STORE_QUERY_ID,
1478                                                             MODEST_MMC_ACCOUNT_ID);
1479
1480 }
1481
1482 /*********************************************************************************/
1483 static void
1484 add_existing_accounts (ModestTnyAccountStore *self)
1485 {
1486         GSList *account_names = NULL, *iter = NULL;
1487         ModestTnyAccountStorePrivate *priv = NULL;
1488         
1489         priv = MODEST_TNY_ACCOUNT_STORE_GET_PRIVATE(self);
1490
1491         /* These are account names, not server_account names */
1492         account_names = modest_account_mgr_account_names (priv->account_mgr, FALSE);
1493
1494         for (iter = account_names; iter != NULL; iter = g_slist_next (iter)) {
1495                 const gchar *account_name = (const gchar*) iter->data;
1496                 
1497                 /* Insert all enabled accounts without notifying */
1498                 if (modest_account_mgr_get_enabled (priv->account_mgr, account_name))
1499                         insert_account (self, account_name, FALSE);
1500         }
1501         modest_account_mgr_free_account_names (account_names);
1502 }
1503
1504 static TnyAccount*
1505 create_tny_account (ModestTnyAccountStore *self,
1506                     const gchar *name,
1507                     TnyAccountType type)
1508 {
1509         TnyAccount *account = NULL;
1510         ModestTnyAccountStorePrivate *priv = NULL;
1511         
1512         priv = MODEST_TNY_ACCOUNT_STORE_GET_PRIVATE(self);
1513
1514         account = modest_tny_account_new_from_account (priv->account_mgr,
1515                                                        name, type, 
1516                                                        priv->session,
1517                                                        get_password,
1518                                                        forget_password);
1519
1520         if (account) {
1521                 /* Forget any cached password for the account, so that
1522                    we use a new account if any */
1523                 modest_tny_account_store_forget_password_in_memory (self, 
1524                                                                     tny_account_get_id (account));
1525                 /* Set the account store */                             
1526                 g_object_set_data (G_OBJECT(account), "account_store", self);
1527         } else {
1528                 g_printerr ("modest: failed to create account for %s\n", name);
1529         }
1530
1531         return account;
1532 }
1533
1534
1535 static void
1536 add_outbox_from_transport_account_to_global_outbox (ModestTnyAccountStore *self,
1537                                                     const gchar *account_name,
1538                                                     TnyAccount *transport_account)
1539 {
1540         TnyList *folders = NULL;
1541         TnyIterator *iter_folders = NULL;
1542         TnyAccount *local_account = NULL, *account_outbox = NULL;
1543         TnyFolder *per_account_outbox = NULL;
1544         ModestTnyAccountStorePrivate *priv = NULL;
1545
1546         priv = MODEST_TNY_ACCOUNT_STORE_GET_PRIVATE(self);
1547
1548         /* Create per account local outbox */
1549         account_outbox = 
1550                 modest_tny_account_new_for_per_account_local_outbox_folder (priv->account_mgr, 
1551                                                                             account_name, 
1552                                                                             priv->session);
1553         tny_list_append (priv->store_accounts_outboxes, G_OBJECT (account_outbox));
1554
1555         /* Get the outbox folder */
1556         folders = tny_simple_list_new ();
1557         tny_folder_store_get_folders (TNY_FOLDER_STORE (account_outbox), folders, NULL, NULL);
1558         g_assert (tny_list_get_length (folders) == 1);
1559                 
1560         iter_folders = tny_list_create_iterator (folders);
1561         per_account_outbox = TNY_FOLDER (tny_iterator_get_current (iter_folders));
1562         g_object_unref (iter_folders);
1563         g_object_unref (folders);
1564         g_object_unref (account_outbox);
1565
1566         /* Add the outbox of the new per-account-local-outbox account
1567            to the global local merged OUTBOX of the local folders
1568            account */
1569         local_account = modest_tny_account_store_get_local_folders_account (self);
1570         modest_tny_local_folders_account_add_folder_to_outbox (MODEST_TNY_LOCAL_FOLDERS_ACCOUNT (local_account),
1571                                                                per_account_outbox);
1572         /* Add the pair to the hash table */
1573         g_hash_table_insert (priv->outbox_of_transport,
1574                              transport_account,
1575                              per_account_outbox);
1576         
1577         g_object_unref (local_account);
1578         g_object_unref (per_account_outbox);
1579 }
1580
1581 /*
1582  * This function will be used for both adding new accounts and for the
1583  * initialization. In the initialization we do not want to emit
1584  * signals so notify will be FALSE, in the case of account additions
1585  * we do want to notify the observers
1586  */
1587 static void
1588 insert_account (ModestTnyAccountStore *self,
1589                 const gchar *account,
1590                 gboolean notify)
1591 {
1592         ModestTnyAccountStorePrivate *priv = NULL;
1593         TnyAccount *store_account = NULL, *transport_account = NULL;
1594         
1595         priv = MODEST_TNY_ACCOUNT_STORE_GET_PRIVATE(self);
1596
1597         /* Get the server and the transport account */
1598         store_account = create_tny_account (self, account, TNY_ACCOUNT_TYPE_STORE);
1599         transport_account = create_tny_account (self, account, TNY_ACCOUNT_TYPE_TRANSPORT);
1600
1601         g_assert (store_account);
1602         g_assert (transport_account);
1603
1604         /* Add accounts to the lists */
1605         tny_list_append (priv->store_accounts, G_OBJECT (store_account));
1606         tny_list_append (priv->transport_accounts, G_OBJECT (transport_account));
1607         
1608         /* Create a new pseudo-account with an outbox for this
1609            transport account and add it to the global outbox
1610            in the local account */
1611         add_outbox_from_transport_account_to_global_outbox (self, account, transport_account);
1612         
1613         /* Notify the observers. We do it after everything is
1614            created */
1615         if (notify) {
1616                 TnyAccount *local_account = NULL;
1617                 
1618                 /* Notify the observers about the new server & transport accounts */
1619                 g_signal_emit (G_OBJECT (self), signals [ACCOUNT_INSERTED_SIGNAL], 0, store_account);   
1620                 g_signal_emit (G_OBJECT (self), signals [ACCOUNT_INSERTED_SIGNAL], 0, transport_account);
1621
1622                 /* Notify that the local account changed */
1623                 local_account = modest_tny_account_store_get_local_folders_account (self);
1624                 g_signal_emit (G_OBJECT (self), signals [ACCOUNT_CHANGED_SIGNAL], 0, local_account);
1625                 g_object_unref (local_account);
1626         }
1627
1628         /* Frees */
1629         g_object_unref (store_account);
1630         g_object_unref (transport_account);
1631 }
1632
1633 static void
1634 on_account_inserted (ModestAccountMgr *acc_mgr, 
1635                      const gchar *account,
1636                      gpointer user_data)
1637 {
1638         /* Insert the account and notify the observers */
1639         insert_account (MODEST_TNY_ACCOUNT_STORE (user_data), account, TRUE);
1640 }
1641
1642 static void
1643 on_account_removed (ModestAccountMgr *acc_mgr, const gchar *account,
1644                     gpointer user_data)
1645 {
1646         TnyAccount *store_account = NULL, *transport_account = NULL;
1647         ModestTnyAccountStore *self = MODEST_TNY_ACCOUNT_STORE(user_data);
1648         
1649         /* Get the server and the transport account */
1650         store_account = 
1651                 modest_tny_account_store_get_server_account (self, account, TNY_ACCOUNT_TYPE_STORE);
1652         transport_account = 
1653                 modest_tny_account_store_get_server_account (self, account, TNY_ACCOUNT_TYPE_TRANSPORT);
1654         
1655         /* If there was any problem creating the account, for example,
1656            with the configuration system this could not exist */
1657         if (store_account) {
1658                 /* Clear the cache */
1659                 tny_store_account_delete_cache (TNY_STORE_ACCOUNT (store_account));
1660
1661                 /* Notify the observers */
1662                 g_signal_emit (G_OBJECT (self), signals [ACCOUNT_REMOVED_SIGNAL], 0, store_account);
1663                 account_disconnect (store_account); /* disconnect the account */
1664                 g_object_unref (store_account);
1665         } else {
1666                 g_warning ("There is no store account for account %s\n", account);
1667         }
1668
1669         /* If there was any problem creating the account, for example,
1670            with the configuration system this could not exist */
1671         if (transport_account) {
1672                 TnyAccount *local_account = NULL;
1673                 TnyFolder *outbox = NULL;
1674                 ModestTnyAccountStorePrivate *priv = NULL;
1675         
1676                 /* Remove the OUTBOX of the account from the global outbox */
1677                 priv = MODEST_TNY_ACCOUNT_STORE_GET_PRIVATE(self);
1678                 outbox = g_hash_table_lookup (priv->outbox_of_transport, transport_account);
1679
1680                 if (TNY_IS_FOLDER (outbox)) {
1681                         local_account = modest_tny_account_store_get_local_folders_account (self);
1682                         modest_tny_local_folders_account_remove_folder_from_outbox (MODEST_TNY_LOCAL_FOLDERS_ACCOUNT (local_account),
1683                                                                                     outbox);
1684                         g_hash_table_remove (priv->outbox_of_transport, transport_account);
1685
1686                         /* Notify the change in the local account */
1687                         g_signal_emit (G_OBJECT (self), signals [ACCOUNT_CHANGED_SIGNAL], 0, local_account);
1688                         g_object_unref (local_account);
1689                 } else {
1690                         g_warning ("Removing a transport account that has no outbox");
1691                 }
1692
1693                 /* Notify the observers */
1694                 g_signal_emit (G_OBJECT (self), signals [ACCOUNT_REMOVED_SIGNAL], 0, transport_account);
1695                 account_disconnect (transport_account); /* disconnect the account */
1696                 g_object_unref (transport_account);
1697         } else {
1698                 g_warning ("There is no transport account for account %s\n", account);
1699         }
1700 }
1701
1702 TnyTransportAccount *
1703 modest_tny_account_store_new_connection_specific_transport_account (ModestTnyAccountStore *self,
1704                                                                     const gchar *name)
1705 {
1706         ModestTnyAccountStorePrivate *priv = NULL;
1707         TnyAccount * tny_account = NULL;
1708
1709         priv = MODEST_TNY_ACCOUNT_STORE_GET_PRIVATE(self);
1710
1711         /* Add the account: */
1712         tny_account = 
1713                 modest_tny_account_new_from_server_account_name (priv->account_mgr, 
1714                                                                  priv->session, 
1715                                                                  name,
1716                                                                  get_password,
1717                                                                  forget_password);
1718         if (tny_account) {
1719                 g_object_set_data (G_OBJECT(tny_account), 
1720                                    "account_store", 
1721                                    (gpointer)self);
1722                 
1723                 tny_list_append (priv->transport_accounts, G_OBJECT (tny_account));
1724                 add_outbox_from_transport_account_to_global_outbox (self, 
1725                                                                     name, 
1726                                                                     tny_account);
1727                 
1728         } else
1729                 g_printerr ("modest: failed to create smtp-specific account for %s\n",
1730                             name);
1731
1732         return TNY_TRANSPORT_ACCOUNT (tny_account);
1733 }
1734
1735
1736 static void
1737 add_connection_specific_transport_accounts (ModestTnyAccountStore *self)
1738 {
1739         ModestTnyAccountStorePrivate *priv = NULL;
1740         GSList *list_specifics = NULL, *iter = NULL;
1741
1742         priv = MODEST_TNY_ACCOUNT_STORE_GET_PRIVATE(self);
1743
1744         ModestConf *conf = modest_runtime_get_conf ();
1745
1746         GError *err = NULL;
1747         list_specifics = modest_conf_get_list (conf,
1748                                                MODEST_CONF_CONNECTION_SPECIFIC_SMTP_LIST,
1749                                                MODEST_CONF_VALUE_STRING, &err);
1750         if (err) {
1751                 g_printerr ("modest: %s: error getting list: %s\n.", __FUNCTION__, err->message);
1752                 g_error_free (err);
1753                 err = NULL;
1754         }
1755                                 
1756         /* Look at each connection-specific transport account for the 
1757          * modest account: */
1758         iter = list_specifics;
1759         while (iter) {
1760                 /* The list alternates between the connection name and the transport name: */
1761                 iter = g_slist_next (iter);
1762                 if (iter) {
1763                         const gchar* transport_account_name = (const gchar*) (iter->data);
1764                         TnyTransportAccount * account = NULL;
1765                         account = modest_tny_account_store_new_connection_specific_transport_account (
1766                                 self, transport_account_name);
1767                         if (account)
1768                                 g_object_unref (account);
1769                 }                               
1770                 iter = g_slist_next (iter);
1771         }
1772 }
1773
1774 TnyMsg *
1775 modest_tny_account_store_find_msg_in_outboxes (ModestTnyAccountStore *self, 
1776                                                const gchar *uri,
1777                                                TnyAccount **ac_out)
1778 {
1779         TnyIterator *acc_iter;
1780         ModestTnyAccountStorePrivate *priv;
1781         TnyMsg *msg = NULL;
1782         TnyAccount *msg_account = NULL;
1783
1784         g_return_val_if_fail (MODEST_IS_TNY_ACCOUNT_STORE (self), NULL);
1785         priv = MODEST_TNY_ACCOUNT_STORE_GET_PRIVATE (self);
1786
1787         acc_iter = tny_list_create_iterator (priv->store_accounts_outboxes);
1788         while (!msg && !tny_iterator_is_done (acc_iter)) {
1789                 TnyList *folders = tny_simple_list_new ();
1790                 TnyAccount *account = TNY_ACCOUNT (tny_iterator_get_current (acc_iter));
1791                 TnyIterator *folders_iter = NULL;
1792
1793                 tny_folder_store_get_folders (TNY_FOLDER_STORE (account), folders, NULL, NULL);
1794                 folders_iter = tny_list_create_iterator (folders);
1795
1796                 while (msg == NULL && !tny_iterator_is_done (folders_iter)) {
1797                         TnyFolder *folder = TNY_FOLDER (tny_iterator_get_current (folders_iter));
1798                         msg = tny_folder_find_msg (folder, uri, NULL);
1799
1800                         if (msg)
1801                                 msg_account = g_object_ref (account);
1802
1803                         g_object_unref (folder);
1804                         tny_iterator_next (folders_iter);
1805                 }
1806
1807                 g_object_unref (folders);
1808                 g_object_unref (account);
1809                 tny_iterator_next (acc_iter);
1810         }
1811
1812         g_object_unref (acc_iter);
1813
1814         if (ac_out != NULL)
1815                 *ac_out = msg_account;
1816
1817         return msg;
1818 }
1819
1820 TnyTransportAccount *
1821 modest_tny_account_store_get_transport_account_from_outbox_header(ModestTnyAccountStore *self, TnyHeader *header)
1822 {
1823         TnyIterator *acc_iter;
1824         ModestTnyAccountStorePrivate *priv;
1825         TnyTransportAccount *header_acc = NULL;
1826         const gchar *msg_id;
1827
1828         g_return_val_if_fail (MODEST_IS_TNY_ACCOUNT_STORE (self), NULL);
1829         g_return_val_if_fail (TNY_IS_HEADER (header), NULL);
1830         priv = MODEST_TNY_ACCOUNT_STORE_GET_PRIVATE (self);
1831
1832         msg_id = modest_tny_send_queue_get_msg_id (header);
1833         acc_iter = tny_list_create_iterator (priv->transport_accounts);
1834         while (!header_acc && !tny_iterator_is_done (acc_iter)) {
1835                 TnyTransportAccount *account = TNY_TRANSPORT_ACCOUNT (tny_iterator_get_current (acc_iter));
1836                 ModestTnySendQueue *send_queue;
1837                 ModestTnySendQueueStatus status;
1838                 send_queue = modest_runtime_get_send_queue(TNY_TRANSPORT_ACCOUNT(account));
1839                 status = modest_tny_send_queue_get_msg_status(send_queue, msg_id);
1840                 if (status != MODEST_TNY_SEND_QUEUE_UNKNOWN) {
1841                         header_acc = g_object_ref(account);
1842                 }
1843                 g_object_unref (account);
1844                 tny_iterator_next (acc_iter);
1845         }
1846
1847         g_object_unref(acc_iter);
1848         return header_acc;
1849 }