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