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