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