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