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