* remove some g_asserts, add warnings instead
[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_HAVE_CONIC
1375         g_return_val_if_fail (TNY_IS_MAEMO_CONIC_DEVICE (device), NULL);
1376         
1377         TnyMaemoConicDevice *maemo_device = TNY_MAEMO_CONIC_DEVICE (device);    
1378         const gchar* iap_id = tny_maemo_conic_device_get_current_iap_id (maemo_device);
1379         /* printf ("DEBUG: %s: iap_id=%s\n", __FUNCTION__, iap_id); */
1380         if (!iap_id)
1381                 return NULL;
1382                 
1383         ConIcIap* connection = tny_maemo_conic_device_get_iap (maemo_device, iap_id);
1384         if (!connection)
1385                 return NULL;
1386                 
1387         const gchar *connection_name = con_ic_iap_get_name (connection);
1388         /* printf ("DEBUG: %s: connection_name=%s\n", __FUNCTION__, connection_name); */
1389         if (!connection_name)
1390                 return NULL;
1391         
1392         /*  Get the connection-specific transport acccount, if any: */
1393         ModestAccountMgr *account_manager = modest_runtime_get_account_mgr ();
1394
1395         /* Check if this account has connection-specific SMTP enabled */
1396         if (!modest_account_mgr_get_use_connection_specific_smtp (account_manager, account_name)) {
1397                 return NULL;
1398         }
1399
1400         gchar* server_account_name = modest_account_mgr_get_connection_specific_smtp (account_manager, 
1401                 connection_name);
1402
1403         /* printf ("DEBUG: %s: server_account_name=%s\n", __FUNCTION__, server_account_name); */
1404         if (!server_account_name) {
1405                 return NULL; /* No connection-specific SMTP server was specified for this connection. */
1406         }
1407                 
1408         TnyAccount* account = modest_tny_account_store_get_tny_account_by (self, 
1409                                                                            MODEST_TNY_ACCOUNT_STORE_QUERY_ID, 
1410                                                                            server_account_name);
1411
1412         /* printf ("DEBUG: %s: account=%p\n", __FUNCTION__, account); */
1413         g_free (server_account_name);   
1414
1415         /* Unref the get()ed object, as required by the tny_maemo_conic_device_get_iap() documentation. */
1416         g_object_unref (connection);
1417         
1418         return account;
1419 #else
1420         return NULL; /* TODO: Implement this for GNOME, instead of just Maemo? */
1421 #endif /* MODEST_HAVE_CONIC */
1422 }
1423
1424                                                                  
1425 TnyAccount*
1426 modest_tny_account_store_get_transport_account_for_open_connection (ModestTnyAccountStore *self,
1427                                                                     const gchar *account_name)
1428 {
1429         g_return_val_if_fail (self, NULL);
1430         g_return_val_if_fail (account_name, NULL);
1431
1432         if (!account_name || !self)
1433                 return NULL;
1434         
1435         /*  Get the connection-specific transport acccount, if any: */
1436         /* Note: This gives us a reference: */
1437         TnyAccount *account =
1438                 modest_tny_account_store_get_smtp_specific_transport_account_for_open_connection (self, account_name);
1439                         
1440         /* If there is no connection-specific transport account (the common case), 
1441          * just get the regular transport account: */
1442         if (!account) {
1443                 /* The special local folders don't have transport accounts. */
1444                 if (strcmp (account_name, MODEST_LOCAL_FOLDERS_ACCOUNT_ID) == 0)
1445                         account = NULL;
1446                 else {
1447                         /* Note: This gives us a reference: */
1448                         account = modest_tny_account_store_get_server_account (self, account_name, 
1449                                                      TNY_ACCOUNT_TYPE_TRANSPORT);
1450                 }
1451         }
1452                         
1453         /* returns a reference. */     
1454         return account;
1455 }
1456
1457 TnyAccount*
1458 modest_tny_account_store_get_local_folders_account (ModestTnyAccountStore *self)
1459 {
1460         TnyAccount *account = NULL;
1461         ModestTnyAccountStorePrivate *priv;
1462         TnyIterator *iter;
1463         gboolean found;
1464
1465         g_return_val_if_fail (MODEST_IS_TNY_ACCOUNT_STORE (self), NULL);
1466         
1467         priv = MODEST_TNY_ACCOUNT_STORE_GET_PRIVATE(self);
1468
1469         found = FALSE;
1470         iter = tny_list_create_iterator (priv->store_accounts);
1471         while (!tny_iterator_is_done (iter) && !found) {
1472                 TnyAccount *tmp_account;
1473
1474                 tmp_account = TNY_ACCOUNT (tny_iterator_get_current (iter));
1475                 if (modest_tny_account_is_virtual_local_folders (tmp_account)) {
1476                         account = g_object_ref (tmp_account);
1477                         found = TRUE;
1478                 }
1479                 g_object_unref (tmp_account);
1480                 tny_iterator_next (iter);
1481         }
1482         g_object_unref (iter);
1483
1484         /* Returns a new reference to the account */
1485         return account;
1486 }
1487
1488 TnyAccount*
1489 modest_tny_account_store_get_mmc_folders_account (ModestTnyAccountStore *self)
1490 {
1491         g_return_val_if_fail (MODEST_IS_TNY_ACCOUNT_STORE (self), NULL);
1492         
1493         return modest_tny_account_store_get_tny_account_by (self, MODEST_TNY_ACCOUNT_STORE_QUERY_ID,
1494                                                             MODEST_MMC_ACCOUNT_ID);
1495
1496 }
1497
1498 /*********************************************************************************/
1499 static void
1500 add_existing_accounts (ModestTnyAccountStore *self)
1501 {
1502         GSList *account_names = NULL, *iter = NULL;
1503         ModestTnyAccountStorePrivate *priv = NULL;
1504         
1505         priv = MODEST_TNY_ACCOUNT_STORE_GET_PRIVATE(self);
1506
1507         /* These are account names, not server_account names */
1508         account_names = modest_account_mgr_account_names (priv->account_mgr, FALSE);
1509
1510         for (iter = account_names; iter != NULL; iter = g_slist_next (iter)) {
1511                 const gchar *account_name = (const gchar*) iter->data;
1512                 
1513                 /* Insert all enabled accounts without notifying */
1514                 if (modest_account_mgr_get_enabled (priv->account_mgr, account_name))
1515                         insert_account (self, account_name, FALSE);
1516         }
1517         modest_account_mgr_free_account_names (account_names);
1518 }
1519
1520 static TnyAccount*
1521 create_tny_account (ModestTnyAccountStore *self,
1522                     const gchar *name,
1523                     TnyAccountType type)
1524 {
1525         TnyAccount *account = NULL;
1526         ModestTnyAccountStorePrivate *priv = NULL;
1527         
1528         priv = MODEST_TNY_ACCOUNT_STORE_GET_PRIVATE(self);
1529
1530         account = modest_tny_account_new_from_account (priv->account_mgr,
1531                                                        name, type, 
1532                                                        priv->session,
1533                                                        get_password,
1534                                                        forget_password);
1535
1536         if (account) {
1537                 /* Forget any cached password for the account, so that
1538                    we use a new account if any */
1539                 modest_tny_account_store_forget_password_in_memory (self, 
1540                                                                     tny_account_get_id (account));
1541                 /* Set the account store */                             
1542                 g_object_set_data (G_OBJECT(account), "account_store", self);
1543         } else {
1544                 g_printerr ("modest: failed to create account for %s\n", name);
1545         }
1546
1547         return account;
1548 }
1549
1550
1551 static void
1552 add_outbox_from_transport_account_to_global_outbox (ModestTnyAccountStore *self,
1553                                                     const gchar *account_name,
1554                                                     TnyAccount *transport_account)
1555 {
1556         TnyList *folders = NULL;
1557         TnyIterator *iter_folders = NULL;
1558         TnyAccount *local_account = NULL, *account_outbox = NULL;
1559         TnyFolder *per_account_outbox = NULL;
1560         ModestTnyAccountStorePrivate *priv = NULL;
1561
1562         priv = MODEST_TNY_ACCOUNT_STORE_GET_PRIVATE(self);
1563
1564         /* Create per account local outbox */
1565         account_outbox = 
1566                 modest_tny_account_new_for_per_account_local_outbox_folder (priv->account_mgr, 
1567                                                                             account_name, 
1568                                                                             priv->session);
1569         tny_list_append (priv->store_accounts_outboxes, G_OBJECT (account_outbox));
1570
1571         /* Get the outbox folder */
1572         folders = tny_simple_list_new ();
1573         tny_folder_store_get_folders (TNY_FOLDER_STORE (account_outbox), folders, NULL, NULL);
1574         if (tny_list_get_length (folders) != 1) {
1575                 g_warning ("%s: > 1 outbox found (%d)?!", __FUNCTION__,
1576                            tny_list_get_length (folders));
1577         }
1578                         
1579         iter_folders = tny_list_create_iterator (folders);
1580         per_account_outbox = TNY_FOLDER (tny_iterator_get_current (iter_folders));
1581         g_object_unref (iter_folders);
1582         g_object_unref (folders);
1583         g_object_unref (account_outbox);
1584
1585         /* Add the outbox of the new per-account-local-outbox account
1586            to the global local merged OUTBOX of the local folders
1587            account */
1588         local_account = modest_tny_account_store_get_local_folders_account (self);
1589         modest_tny_local_folders_account_add_folder_to_outbox (MODEST_TNY_LOCAL_FOLDERS_ACCOUNT (local_account),
1590                                                                per_account_outbox);
1591         /* Add the pair to the hash table */
1592         g_hash_table_insert (priv->outbox_of_transport,
1593                              transport_account,
1594                              per_account_outbox);
1595         
1596         g_object_unref (local_account);
1597         g_object_unref (per_account_outbox);
1598 }
1599
1600 /*
1601  * This function will be used for both adding new accounts and for the
1602  * initialization. In the initialization we do not want to emit
1603  * signals so notify will be FALSE, in the case of account additions
1604  * we do want to notify the observers
1605  */
1606 static void
1607 insert_account (ModestTnyAccountStore *self,
1608                 const gchar *account,
1609                 gboolean notify)
1610 {
1611         ModestTnyAccountStorePrivate *priv = NULL;
1612         TnyAccount *store_account = NULL, *transport_account = NULL;
1613         
1614         priv = MODEST_TNY_ACCOUNT_STORE_GET_PRIVATE(self);
1615
1616         /* Get the server and the transport account */
1617         store_account = create_tny_account (self, account, TNY_ACCOUNT_TYPE_STORE);
1618         if (!store_account || !TNY_IS_ACCOUNT(store_account)) {
1619                 g_warning ("%s: failed to create store account", __FUNCTION__);
1620                 return;
1621         }
1622
1623         transport_account = create_tny_account (self, account, TNY_ACCOUNT_TYPE_TRANSPORT);
1624         if (!transport_account || !TNY_IS_ACCOUNT(transport_account)) {
1625                 g_warning ("%s: failed to create transport account", __FUNCTION__);
1626                 g_object_unref (store_account);
1627                 return;
1628         }
1629
1630         /* Add accounts to the lists */
1631         tny_list_append (priv->store_accounts, G_OBJECT (store_account));
1632         tny_list_append (priv->transport_accounts, G_OBJECT (transport_account));
1633         
1634         /* Create a new pseudo-account with an outbox for this
1635            transport account and add it to the global outbox
1636            in the local account */
1637         add_outbox_from_transport_account_to_global_outbox (self, account, transport_account);
1638         
1639         /* Notify the observers. We do it after everything is
1640            created */
1641         if (notify) {
1642                 TnyAccount *local_account = NULL;
1643                 
1644                 /* Notify the observers about the new server & transport accounts */
1645                 g_signal_emit (G_OBJECT (self), signals [ACCOUNT_INSERTED_SIGNAL], 0, store_account);   
1646                 g_signal_emit (G_OBJECT (self), signals [ACCOUNT_INSERTED_SIGNAL], 0, transport_account);
1647
1648                 /* Notify that the local account changed */
1649                 local_account = modest_tny_account_store_get_local_folders_account (self);
1650                 g_signal_emit (G_OBJECT (self), signals [ACCOUNT_CHANGED_SIGNAL], 0, local_account);
1651                 g_object_unref (local_account);
1652         }
1653
1654         /* Frees */
1655         g_object_unref (store_account);
1656         g_object_unref (transport_account);
1657 }
1658
1659 static void
1660 on_account_inserted (ModestAccountMgr *acc_mgr, 
1661                      const gchar *account,
1662                      gpointer user_data)
1663 {
1664         /* Insert the account and notify the observers */
1665         insert_account (MODEST_TNY_ACCOUNT_STORE (user_data), account, TRUE);
1666 }
1667
1668 /* This is the callback of the tny_camel_account_set_online called in
1669    on_account_removed to disconnect the account */
1670 static void
1671 on_account_disconnect_when_removing (TnyCamelAccount *account, 
1672                                      gboolean canceled, 
1673                                      GError *err, 
1674                                      gpointer user_data)
1675 {
1676         ModestTnyAccountStore *self;
1677         ModestTnyAccountStorePrivate *priv;
1678
1679         self = MODEST_TNY_ACCOUNT_STORE (user_data);
1680         priv = MODEST_TNY_ACCOUNT_STORE_GET_PRIVATE (self);
1681
1682         if (canceled || err) {
1683                 /* The account was not cancelled */
1684         } else {
1685                 /* Clear the cache if it's an store account */
1686                 if (TNY_IS_STORE_ACCOUNT (account))
1687                         tny_store_account_delete_cache (TNY_STORE_ACCOUNT (account));
1688         }
1689
1690         /* Remove it from the list of accounts */
1691         if (TNY_IS_STORE_ACCOUNT (account))
1692                 tny_list_remove (priv->store_accounts, (GObject *) account);
1693         else
1694                 tny_list_remove (priv->transport_accounts, (GObject *) account);
1695
1696         /* Notify the observers */
1697         g_signal_emit (G_OBJECT (self), signals [ACCOUNT_REMOVED_SIGNAL], 
1698                        0, account);
1699         
1700         /* Unref the extra reference added by get_server_account */
1701         g_object_unref (account);
1702 }
1703
1704 static void
1705 on_account_removed (ModestAccountMgr *acc_mgr, const gchar *account,
1706                     gpointer user_data)
1707 {
1708         TnyAccount *store_account = NULL, *transport_account = NULL;
1709         ModestTnyAccountStore *self;
1710         ModestTnyAccountStorePrivate *priv;
1711         
1712         self = MODEST_TNY_ACCOUNT_STORE (user_data);
1713         priv = MODEST_TNY_ACCOUNT_STORE_GET_PRIVATE (self);
1714
1715         /* Get the server and the transport account */
1716         store_account = 
1717                 modest_tny_account_store_get_server_account (self, account, TNY_ACCOUNT_TYPE_STORE);
1718         transport_account = 
1719                 modest_tny_account_store_get_server_account (self, account, TNY_ACCOUNT_TYPE_TRANSPORT);
1720         
1721         /* If there was any problem creating the account, for example,
1722            with the configuration system this could not exist */
1723         if (store_account) {
1724                 /* Disconnect before deleting the cache, because the
1725                    disconnection will rewrite the cache to the
1726                    disk */
1727                 tny_camel_account_set_online (TNY_CAMEL_ACCOUNT (store_account), FALSE,
1728                                               on_account_disconnect_when_removing, self);
1729         } else {
1730                 g_warning ("There is no store account for account %s\n", account);
1731         }
1732
1733         /* If there was any problem creating the account, for example,
1734            with the configuration system this could not exist */
1735         if (transport_account) {
1736                 TnyAccount *local_account = NULL;
1737                 TnyFolder *outbox = NULL;
1738                 ModestTnyAccountStorePrivate *priv = NULL;
1739         
1740                 /* Remove the OUTBOX of the account from the global outbox */
1741                 priv = MODEST_TNY_ACCOUNT_STORE_GET_PRIVATE(self);
1742                 outbox = g_hash_table_lookup (priv->outbox_of_transport, transport_account);
1743
1744                 if (TNY_IS_FOLDER (outbox)) {
1745                         local_account = modest_tny_account_store_get_local_folders_account (self);
1746                         modest_tny_local_folders_account_remove_folder_from_outbox (MODEST_TNY_LOCAL_FOLDERS_ACCOUNT (local_account),
1747                                                                                     outbox);
1748                         g_hash_table_remove (priv->outbox_of_transport, transport_account);
1749
1750                         /* Notify the change in the local account */
1751                         g_signal_emit (G_OBJECT (self), signals [ACCOUNT_CHANGED_SIGNAL], 0, local_account);
1752                         g_object_unref (local_account);
1753                 } else {
1754                         g_warning ("Removing a transport account that has no outbox");
1755                 }
1756
1757                 /* Disconnect and notify the observers. The callback will free the reference */
1758                 tny_camel_account_set_online (TNY_CAMEL_ACCOUNT (transport_account), FALSE,
1759                                               on_account_disconnect_when_removing, self);
1760         } else {
1761                 g_warning ("There is no transport account for account %s\n", account);
1762         }
1763 }
1764
1765 TnyTransportAccount *
1766 modest_tny_account_store_new_connection_specific_transport_account (ModestTnyAccountStore *self,
1767                                                                     const gchar *name)
1768 {
1769         ModestTnyAccountStorePrivate *priv = NULL;
1770         TnyAccount * tny_account = NULL;
1771
1772         priv = MODEST_TNY_ACCOUNT_STORE_GET_PRIVATE(self);
1773
1774         /* Add the account: */
1775         tny_account = 
1776                 modest_tny_account_new_from_server_account_name (priv->account_mgr, 
1777                                                                  priv->session, 
1778                                                                  name,
1779                                                                  get_password,
1780                                                                  forget_password);
1781         if (tny_account) {
1782                 g_object_set_data (G_OBJECT(tny_account), 
1783                                    "account_store", 
1784                                    (gpointer)self);
1785                 
1786                 tny_list_append (priv->transport_accounts, G_OBJECT (tny_account));
1787                 add_outbox_from_transport_account_to_global_outbox (self, 
1788                                                                     name, 
1789                                                                     tny_account);
1790                 
1791         } else
1792                 g_printerr ("modest: failed to create smtp-specific account for %s\n",
1793                             name);
1794
1795         return TNY_TRANSPORT_ACCOUNT (tny_account);
1796 }
1797
1798
1799 static void
1800 add_connection_specific_transport_accounts (ModestTnyAccountStore *self)
1801 {
1802         ModestTnyAccountStorePrivate *priv = NULL;
1803         GSList *list_specifics = NULL, *iter = NULL;
1804
1805         priv = MODEST_TNY_ACCOUNT_STORE_GET_PRIVATE(self);
1806
1807         ModestConf *conf = modest_runtime_get_conf ();
1808
1809         GError *err = NULL;
1810         list_specifics = modest_conf_get_list (conf,
1811                                                MODEST_CONF_CONNECTION_SPECIFIC_SMTP_LIST,
1812                                                MODEST_CONF_VALUE_STRING, &err);
1813         if (err) {
1814                 g_printerr ("modest: %s: error getting list: %s\n.", __FUNCTION__, err->message);
1815                 g_error_free (err);
1816                 err = NULL;
1817         }
1818                                 
1819         /* Look at each connection-specific transport account for the 
1820          * modest account: */
1821         iter = list_specifics;
1822         while (iter) {
1823                 /* The list alternates between the connection name and the transport name: */
1824                 iter = g_slist_next (iter);
1825                 if (iter) {
1826                         const gchar* transport_account_name = (const gchar*) (iter->data);
1827                         TnyTransportAccount * account = NULL;
1828                         account = modest_tny_account_store_new_connection_specific_transport_account (
1829                                 self, transport_account_name);
1830                         if (account)
1831                                 g_object_unref (account);
1832                 }                               
1833                 iter = g_slist_next (iter);
1834         }
1835 }
1836
1837 TnyMsg *
1838 modest_tny_account_store_find_msg_in_outboxes (ModestTnyAccountStore *self, 
1839                                                const gchar *uri,
1840                                                TnyAccount **ac_out)
1841 {
1842         TnyIterator *acc_iter;
1843         ModestTnyAccountStorePrivate *priv;
1844         TnyMsg *msg = NULL;
1845         TnyAccount *msg_account = NULL;
1846
1847         g_return_val_if_fail (MODEST_IS_TNY_ACCOUNT_STORE (self), NULL);
1848         priv = MODEST_TNY_ACCOUNT_STORE_GET_PRIVATE (self);
1849
1850         acc_iter = tny_list_create_iterator (priv->store_accounts_outboxes);
1851         while (!msg && !tny_iterator_is_done (acc_iter)) {
1852                 TnyList *folders = tny_simple_list_new ();
1853                 TnyAccount *account = TNY_ACCOUNT (tny_iterator_get_current (acc_iter));
1854                 TnyIterator *folders_iter = NULL;
1855
1856                 tny_folder_store_get_folders (TNY_FOLDER_STORE (account), folders, NULL, NULL);
1857                 folders_iter = tny_list_create_iterator (folders);
1858
1859                 while (msg == NULL && !tny_iterator_is_done (folders_iter)) {
1860                         TnyFolder *folder = TNY_FOLDER (tny_iterator_get_current (folders_iter));
1861                         msg = tny_folder_find_msg (folder, uri, NULL);
1862
1863                         if (msg)
1864                                 msg_account = g_object_ref (account);
1865
1866                         g_object_unref (folder);
1867                         tny_iterator_next (folders_iter);
1868                 }
1869
1870                 g_object_unref (folders);
1871                 g_object_unref (account);
1872                 tny_iterator_next (acc_iter);
1873         }
1874
1875         g_object_unref (acc_iter);
1876
1877         if (ac_out != NULL)
1878                 *ac_out = msg_account;
1879
1880         return msg;
1881 }
1882
1883 TnyTransportAccount *
1884 modest_tny_account_store_get_transport_account_from_outbox_header(ModestTnyAccountStore *self, TnyHeader *header)
1885 {
1886         TnyIterator *acc_iter;
1887         ModestTnyAccountStorePrivate *priv;
1888         TnyTransportAccount *header_acc = NULL;
1889         const gchar *msg_id;
1890
1891         g_return_val_if_fail (MODEST_IS_TNY_ACCOUNT_STORE (self), NULL);
1892         g_return_val_if_fail (TNY_IS_HEADER (header), NULL);
1893         priv = MODEST_TNY_ACCOUNT_STORE_GET_PRIVATE (self);
1894
1895         msg_id = modest_tny_send_queue_get_msg_id (header);
1896         acc_iter = tny_list_create_iterator (priv->transport_accounts);
1897         while (!header_acc && !tny_iterator_is_done (acc_iter)) {
1898                 TnyTransportAccount *account = TNY_TRANSPORT_ACCOUNT (tny_iterator_get_current (acc_iter));
1899                 ModestTnySendQueue *send_queue;
1900                 ModestTnySendQueueStatus status;
1901                 send_queue = modest_runtime_get_send_queue(TNY_TRANSPORT_ACCOUNT(account));
1902                 status = modest_tny_send_queue_get_msg_status(send_queue, msg_id);
1903                 if (status != MODEST_TNY_SEND_QUEUE_UNKNOWN) {
1904                         header_acc = g_object_ref(account);
1905                 }
1906                 g_object_unref (account);
1907                 tny_iterator_next (acc_iter);
1908         }
1909
1910         g_object_unref(acc_iter);
1911         return header_acc;
1912 }