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