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