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