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