Do not show the settings dialog if the device is offline
[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;
887
888         html_part = modest_tny_msg_find_body_part (msg, TRUE);
889         text_part = modest_tny_msg_find_body_part (msg, FALSE);
890
891         if (text_part && TNY_IS_MIME_PART (text_part) && html_part == text_part) {
892                 g_object_unref (text_part);
893                 text_part = NULL;
894         }
895
896         if (html_part && TNY_IS_MIME_PART (html_part)) {
897                 tny_list_prepend (list, G_OBJECT (html_part));
898                 g_object_unref (html_part);
899         }
900
901         if (text_part && TNY_IS_MIME_PART (text_part)) {
902                 tny_list_prepend (list, G_OBJECT (text_part));
903                 g_object_unref (text_part);
904         }
905 }
906
907
908 ModestTnyAccountStore*
909 modest_tny_account_store_new (ModestAccountMgr *account_mgr,
910                               TnyDevice *device)
911 {
912         GObject *obj;
913         ModestTnyAccountStorePrivate *priv;
914         TnyAccount *local_account = NULL;
915         TnyLockable *lockable;
916         GnomeVFSVolumeMonitor* monitor = NULL;
917         gboolean auto_update;
918         const gchar *mmc_path = NULL;
919
920         g_return_val_if_fail (account_mgr, NULL);
921         g_return_val_if_fail (device, NULL);
922
923         tny_camel_bs_msg_receive_strategy_set_global_bodies_filter (
924                 (TnyCamelBsMsgReceiveStrategyBodiesFilter) _bodies_filter);
925
926         obj  = G_OBJECT(g_object_new(MODEST_TYPE_TNY_ACCOUNT_STORE, NULL));
927         priv = MODEST_TNY_ACCOUNT_STORE_GET_PRIVATE(obj);
928
929         priv->account_mgr = g_object_ref (G_OBJECT(account_mgr));
930         priv->device = g_object_ref (device);
931
932         /* If autoupdate is off then we don't try to connect to the
933            accounts when they're added to the account store*/
934         auto_update = modest_conf_get_bool (modest_runtime_get_conf (),
935                                             MODEST_CONF_AUTO_UPDATE, NULL);
936         if (!auto_update)
937                 tny_device_force_offline (priv->device);
938
939         priv->session = tny_session_camel_new (TNY_ACCOUNT_STORE(obj));
940         if (!priv->session) {
941                 g_warning ("failed to get TnySessionCamel");
942                 return NULL;
943         }
944
945         /* Set the ui locker */
946         lockable = tny_gtk_lockable_new ();
947         tny_session_camel_set_ui_locker (priv->session, lockable);
948         g_object_unref (lockable);
949
950         /* Connect signals */
951         priv->sighandlers  =  modest_signal_mgr_connect (priv->sighandlers,
952                                                          G_OBJECT(account_mgr), "account_inserted",
953                                                          G_CALLBACK (on_account_inserted), obj);
954         priv->sighandlers  =  modest_signal_mgr_connect (priv->sighandlers,
955                                                          G_OBJECT(account_mgr), "account_changed",
956                                                          G_CALLBACK (on_account_changed), obj);
957         priv->sighandlers  =  modest_signal_mgr_connect (priv->sighandlers,
958                                                          G_OBJECT(account_mgr), "account_removed",
959                                                          G_CALLBACK (on_account_removed), obj);
960
961         /* Respond to volume mounts and unmounts, such as the
962            insertion/removal of the memory card. This is a singleton,
963            so it does not need to be unrefed */
964         monitor = gnome_vfs_get_volume_monitor();
965         priv->sighandlers = modest_signal_mgr_connect (priv->sighandlers,
966                                                        G_OBJECT(monitor),
967                                                        "volume-mounted",
968                                                        G_CALLBACK(on_vfs_volume_mounted),
969                                                        obj);
970         priv->sighandlers = modest_signal_mgr_connect (priv->sighandlers,
971                                                        G_OBJECT(monitor), "volume-unmounted",
972                                                        G_CALLBACK(on_vfs_volume_unmounted),
973                                                        obj);
974
975         /* Create the lists of accounts */
976         priv->store_accounts = tny_simple_list_new ();
977         priv->transport_accounts = tny_simple_list_new ();
978         priv->store_accounts_outboxes = tny_simple_list_new ();
979
980         /* Create the local folders account */
981         local_account =
982                 modest_tny_account_new_for_local_folders (priv->account_mgr, priv->session, NULL);
983         tny_list_append (priv->store_accounts, G_OBJECT(local_account));
984         g_object_unref (local_account);
985
986         /* Add the other remote accounts. Do this after adding the
987            local account, because we need to add our outboxes to the
988            global OUTBOX hosted in the local account */
989         add_existing_accounts (MODEST_TNY_ACCOUNT_STORE (obj));
990
991         /* Add connection-specific transport accounts if there are any
992            accounts available */
993         if (!only_local_accounts (MODEST_TNY_ACCOUNT_STORE(obj)))
994                 add_connection_specific_transport_accounts (MODEST_TNY_ACCOUNT_STORE(obj));
995
996         /* This is a singleton, so it does not need to be unrefed. */
997         mmc_path = g_getenv (MODEST_MMC1_VOLUMEPATH_ENV);
998         if (mmc_path && volume_path_is_mounted (mmc_path)) {
999                 /* It is mounted: */
1000                 add_mmc_account (MODEST_TNY_ACCOUNT_STORE (obj), FALSE /* don't emit the insert signal. */);
1001         }
1002
1003         /* Initialize session */
1004         tny_session_camel_set_initialized (priv->session);
1005
1006         return MODEST_TNY_ACCOUNT_STORE(obj);
1007 }
1008
1009 static void
1010 modest_tny_account_store_get_accounts  (TnyAccountStore *self, 
1011                                         TnyList *list,
1012                                         TnyGetAccountsRequestType request_type)
1013 {
1014         ModestTnyAccountStorePrivate *priv;
1015         
1016         g_return_if_fail (self);
1017         g_return_if_fail (TNY_IS_LIST(list));
1018         
1019         priv = MODEST_TNY_ACCOUNT_STORE_GET_PRIVATE(self);
1020         
1021         switch (request_type) {
1022         case TNY_ACCOUNT_STORE_BOTH:
1023                 tny_list_foreach (priv->store_accounts, foreach_account_append_to_list, list);
1024                 tny_list_foreach (priv->transport_accounts, foreach_account_append_to_list, list);
1025                 break;
1026         case TNY_ACCOUNT_STORE_STORE_ACCOUNTS:
1027                 tny_list_foreach (priv->store_accounts, foreach_account_append_to_list, list);
1028                 break;
1029         case TNY_ACCOUNT_STORE_TRANSPORT_ACCOUNTS:
1030                 tny_list_foreach (priv->transport_accounts, foreach_account_append_to_list, list);
1031                 break;
1032         default:
1033                 g_return_if_reached ();
1034         }
1035 }
1036
1037
1038 static const gchar*
1039 modest_tny_account_store_get_cache_dir (TnyAccountStore *self)
1040 {
1041         ModestTnyAccountStorePrivate *priv;
1042         priv = MODEST_TNY_ACCOUNT_STORE_GET_PRIVATE(self);
1043         
1044         if (!priv->cache_dir)
1045                 priv->cache_dir = g_build_filename (g_get_home_dir(), 
1046                                                     MODEST_DIR, MODEST_CACHE_DIR, NULL);
1047         return priv->cache_dir;
1048 }
1049
1050
1051 /*
1052  * callers need to unref
1053  */
1054 static TnyDevice*
1055 modest_tny_account_store_get_device (TnyAccountStore *self)
1056 {
1057         ModestTnyAccountStorePrivate *priv;
1058
1059         g_return_val_if_fail (self, NULL);
1060         
1061         priv = MODEST_TNY_ACCOUNT_STORE_GET_PRIVATE(self);
1062         
1063         if (priv->device) 
1064                 return g_object_ref (G_OBJECT(priv->device));
1065         else
1066                 return NULL;
1067 }
1068
1069
1070 static TnyAccount*
1071 modest_tny_account_store_find_account_by_url (TnyAccountStore *self, const gchar* url_string)
1072 {
1073         return modest_tny_account_store_get_tny_account_by (MODEST_TNY_ACCOUNT_STORE (self), 
1074                                                             MODEST_TNY_ACCOUNT_STORE_QUERY_URL,
1075                                                             url_string);
1076 }
1077
1078
1079
1080 static gboolean
1081 modest_tny_account_store_alert (TnyAccountStore *self, 
1082                                 TnyAccount *account, 
1083                                 TnyAlertType type,
1084                                 gboolean question, 
1085                                 GError *error)
1086 {
1087         ModestProtocolType protocol_type = MODEST_PROTOCOL_REGISTRY_TYPE_INVALID;
1088         ModestProtocol *protocol = NULL;
1089         const gchar* server_name = "";
1090         gchar *prompt = NULL;
1091         gboolean retval = TRUE;
1092
1093         /* NOTE: account may be NULL in some cases */
1094         if (!error)
1095                 return FALSE;
1096
1097         /* Get the server name: */
1098         if (account) {
1099                 server_name = tny_account_get_hostname (account);
1100                 protocol_type = modest_tny_account_get_protocol_type (account);
1101                 if (protocol_type == MODEST_PROTOCOL_REGISTRY_TYPE_INVALID){
1102                         g_warning("%s: account with id=%s has no proto.\n", __FUNCTION__, 
1103                                   tny_account_get_id (account));
1104                         return FALSE;
1105                 }
1106                 protocol = modest_protocol_registry_get_protocol_by_type (modest_runtime_get_protocol_registry (),
1107                                                                           protocol_type);
1108         }
1109
1110         switch (error->code) {
1111         case TNY_SYSTEM_ERROR_CANCEL:
1112                 /* Don't show waste the user's time by showing him a dialog telling 
1113                  * him that he has just cancelled something: */
1114                 return TRUE;
1115
1116         case TNY_SERVICE_ERROR_PROTOCOL:
1117                 /* Like a BAD from IMAP (protocol error) */
1118         case TNY_SERVICE_ERROR_LOST_CONNECTION:
1119                 /* Lost the connection with the service */
1120         case TNY_SERVICE_ERROR_UNAVAILABLE:
1121                 /* You must be working online for this operation */
1122         case TNY_SERVICE_ERROR_CONNECT:
1123                 if (protocol) {
1124                         prompt = modest_protocol_get_translation (protocol, MODEST_PROTOCOL_TRANSLATION_CONNECT_ERROR, server_name);
1125                 }
1126                 if (!prompt) {
1127                         g_return_val_if_reached (FALSE);
1128                 }
1129                 break;
1130         case TNY_SERVICE_ERROR_AUTHENTICATE:
1131                 /* It seems that there's no better error to show with
1132                  * POP and IMAP because TNY_SERVICE_ERROR_AUTHENTICATE
1133                  * may appear if there's a timeout during auth */
1134                 if (protocol) {
1135                         prompt = modest_protocol_get_translation (protocol, MODEST_PROTOCOL_TRANSLATION_AUTH_ERROR, server_name);
1136                 }
1137                 if (!prompt) {
1138                         g_return_val_if_reached (FALSE);
1139                 }
1140                 break;
1141         case TNY_SERVICE_ERROR_CERTIFICATE:
1142                 /* We'll show the proper dialog later */
1143                 break;
1144
1145         case TNY_SYSTEM_ERROR_MEMORY:
1146                 /* Can't allocate memory for this operation */
1147                 if (modest_tny_account_store_check_disk_full_error ((ModestTnyAccountStore*)self,
1148                                                                     NULL, error, account, NULL))
1149                         retval = FALSE;
1150                 break;
1151         case TNY_SERVICE_ERROR_UNKNOWN:
1152                 return FALSE;
1153         default:
1154                 /* We don't treat this as an error, but as a not handled message. Then,
1155                  * debug message, and return false */
1156                 g_debug ("Unexpected error %d (%s)", error->code, error->message);
1157                 return FALSE;
1158         }
1159
1160
1161         if (error->code == TNY_SERVICE_ERROR_CERTIFICATE)
1162                 retval = modest_platform_run_certificate_confirmation_dialog (server_name,
1163                                                                               error->message);
1164         else if (error->code == TNY_SERVICE_ERROR_AUTHENTICATE ||
1165                  error->code == TNY_SERVICE_ERROR_CONNECT) {
1166                 TnyDevice *device = modest_runtime_get_device ();
1167                 gboolean success;
1168
1169                 /* If we get the connection error after establishing a
1170                    proper connection then do not show the dialog as we
1171                    are probably behind a firewall, or in a network
1172                    with connection issues. We just keep this code to
1173                    detect situations were the user does not enter the
1174                    server info properly */
1175                 success = modest_account_mgr_get_server_account_username_has_succeeded (modest_runtime_get_account_mgr (),
1176                                                                                         tny_account_get_id (account));
1177
1178                 if (!success) {
1179                         gboolean show_banner;
1180
1181                         g_debug ("%s: %s alert received (%s)", __FUNCTION__,
1182                                  (error->code == TNY_SERVICE_ERROR_CONNECT) ? "connect" : "aunthenticate",
1183                                  error->message);
1184
1185                         if (tny_device_is_online (device) &&
1186                             modest_window_mgr_get_num_windows (modest_runtime_get_window_mgr ()))
1187                                 modest_platform_run_information_dialog (NULL, prompt, TRUE);
1188
1189                         /* Show the account dialog */
1190                         show_banner = (error->code == TNY_SERVICE_ERROR_CONNECT) ? FALSE : TRUE;
1191                         g_debug ("%s: going to show settings dialog", __FUNCTION__);
1192                         show_wrong_password_dialog (self, account, show_banner);
1193                         retval = TRUE;
1194                 }
1195         }
1196
1197         g_debug ("%s: error code %d (%s", __FUNCTION__, error->code, error->message);
1198
1199         if (prompt)
1200                 g_free (prompt);
1201
1202         return retval;
1203 }
1204
1205
1206 static void
1207 modest_tny_account_store_init (gpointer g, gpointer iface_data)
1208 {
1209         TnyAccountStoreIface *klass;
1210
1211         g_return_if_fail (g);
1212
1213         klass = (TnyAccountStoreIface *)g;
1214
1215         klass->get_accounts =
1216                 modest_tny_account_store_get_accounts;
1217         klass->get_cache_dir =
1218                 modest_tny_account_store_get_cache_dir;
1219         klass->get_device =
1220                 modest_tny_account_store_get_device;
1221         klass->alert =
1222                 modest_tny_account_store_alert;
1223         klass->find_account =
1224                 modest_tny_account_store_find_account_by_url;
1225 }
1226
1227 void
1228 modest_tny_account_store_set_get_pass_func (ModestTnyAccountStore *self,
1229                                             ModestTnyGetPassFunc func)
1230 {
1231         /* not implemented, we use signals */
1232         g_printerr ("modest: set_get_pass_func not implemented\n");
1233 }
1234
1235 TnySessionCamel*
1236 modest_tny_account_store_get_session  (TnyAccountStore *self)
1237 {
1238         g_return_val_if_fail (self, NULL);
1239         return MODEST_TNY_ACCOUNT_STORE_GET_PRIVATE (self)->session;
1240 }
1241
1242 static TnyAccount*
1243 get_tny_account_by (TnyList *accounts,
1244                     ModestTnyAccountStoreQueryType type,
1245                     const gchar *str)
1246 {
1247         TnyIterator *iter = NULL;
1248         gboolean found = FALSE;
1249         TnyAccount *retval = NULL;
1250
1251         g_return_val_if_fail (TNY_IS_LIST(accounts), NULL);
1252
1253         if (tny_list_get_length(accounts) == 0) {
1254                 g_warning ("%s: account list is empty", __FUNCTION__);
1255                 return NULL;
1256         }
1257         
1258         iter = tny_list_create_iterator (accounts);
1259         while (!tny_iterator_is_done (iter) && !found) {
1260                 TnyAccount *tmp_account = NULL;
1261                 const gchar *val = NULL;
1262
1263                 tmp_account = TNY_ACCOUNT (tny_iterator_get_current (iter));
1264                 if (!TNY_IS_ACCOUNT(tmp_account)) {
1265                         g_warning ("%s: not a valid account", __FUNCTION__);
1266                         tmp_account = NULL;
1267                         break;
1268                 }
1269
1270                 switch (type) {
1271                 case MODEST_TNY_ACCOUNT_STORE_QUERY_ID:
1272                         val = tny_account_get_id (tmp_account);
1273                         break;
1274                 case MODEST_TNY_ACCOUNT_STORE_QUERY_URL:
1275                         val = tny_account_get_url_string (tmp_account);
1276                         break;
1277                 }
1278                 
1279                 if (type == MODEST_TNY_ACCOUNT_STORE_QUERY_URL && 
1280                     tny_account_matches_url_string (tmp_account, str)) {
1281                         retval = g_object_ref (tmp_account);
1282                         found = TRUE;
1283                 } else {
1284                         if (val && str && strcmp (val, str) == 0) {
1285                                 retval = g_object_ref (tmp_account);
1286                                 found = TRUE;
1287                         }
1288                 }
1289                 g_object_unref (tmp_account);
1290                 tny_iterator_next (iter);
1291         }
1292         g_object_unref (iter);
1293
1294         return retval;
1295 }
1296
1297 TnyAccount*
1298 modest_tny_account_store_get_tny_account_by (ModestTnyAccountStore *self, 
1299                                              ModestTnyAccountStoreQueryType type,
1300                                              const gchar *str)
1301 {
1302         TnyAccount *account = NULL;
1303         ModestTnyAccountStorePrivate *priv;     
1304         
1305         g_return_val_if_fail (self, NULL);
1306         g_return_val_if_fail (str, NULL);
1307         
1308         priv = MODEST_TNY_ACCOUNT_STORE_GET_PRIVATE(self);
1309         
1310         /* Search in store accounts */
1311         account = get_tny_account_by (priv->store_accounts, type, str);
1312
1313         /* If we already found something, no need to search the transport accounts */
1314         if (!account) {
1315                 account = get_tny_account_by (priv->transport_accounts, type, str);
1316
1317                 /* If we already found something, no need to search the
1318                    per-account outbox accounts */
1319                 if (!account)
1320                         account = get_tny_account_by (priv->store_accounts_outboxes, type, str);
1321         }
1322
1323         /* Warn if nothing was found. This is generally unusual. */
1324         if (!account) {
1325                 g_warning("%s: Failed to find account with %s=%s\n", 
1326                           __FUNCTION__, 
1327                           (type == MODEST_TNY_ACCOUNT_STORE_QUERY_ID) ? "ID" : "URL",                     
1328                           str);
1329         }
1330
1331         /* Returns a new reference to the account if found */   
1332         return account;
1333 }
1334
1335
1336 TnyAccount*
1337 modest_tny_account_store_get_server_account (ModestTnyAccountStore *self,
1338                                              const gchar *account_name,
1339                                              TnyAccountType type)
1340 {
1341         ModestTnyAccountStorePrivate *priv = NULL;
1342         TnyAccount *retval = NULL;
1343         TnyList *account_list = NULL;
1344         TnyIterator *iter = NULL;
1345         gboolean found;
1346
1347         g_return_val_if_fail (self, NULL);
1348         g_return_val_if_fail (account_name, NULL);
1349         g_return_val_if_fail (type == TNY_ACCOUNT_TYPE_STORE || 
1350                               type == TNY_ACCOUNT_TYPE_TRANSPORT,
1351                               NULL);
1352         
1353         priv = MODEST_TNY_ACCOUNT_STORE_GET_PRIVATE(self);
1354
1355         account_list = (type == TNY_ACCOUNT_TYPE_STORE) ? 
1356                 priv->store_accounts : 
1357                 priv->transport_accounts;
1358
1359         if (!account_list) {
1360                 g_printerr ("%s: No server accounts of type %s\n", __FUNCTION__, 
1361                         (type == TNY_ACCOUNT_TYPE_STORE) ? "store" : "transport");
1362                 return NULL;
1363         }
1364         
1365         /* Look for the server account */
1366         found = FALSE;
1367         iter = tny_list_create_iterator (account_list);
1368         while (!tny_iterator_is_done (iter) && !found) {
1369                 const gchar *modest_acc_name;
1370                 TnyAccount *tmp_account;
1371
1372                 tmp_account = TNY_ACCOUNT (tny_iterator_get_current (iter));
1373                 modest_acc_name = 
1374                         modest_tny_account_get_parent_modest_account_name_for_server_account (tmp_account);
1375                 
1376                 if (account_name && modest_acc_name && !strcmp (account_name, modest_acc_name)) {
1377                         found = TRUE;
1378                         retval = g_object_ref (tmp_account);
1379                 }
1380                 /* Free and continue */
1381                 g_object_unref (tmp_account);
1382                 tny_iterator_next (iter);
1383         }
1384         g_object_unref (iter);
1385
1386         if (!found) {
1387                 g_printerr ("modest: %s: could not get tny %s account for %s\n." \
1388                             "Number of server accounts of this type=%d\n", __FUNCTION__,
1389                             (type == TNY_ACCOUNT_TYPE_STORE) ? "store" : "transport",
1390                             account_name, tny_list_get_length (account_list));
1391         }
1392
1393         /* Returns a new reference */
1394         return retval;
1395 }
1396
1397 TnyAccount*
1398 modest_tny_account_store_get_smtp_specific_transport_account_for_open_connection (
1399         ModestTnyAccountStore *self, const gchar *account_name)
1400 {
1401         TnyDevice *device;
1402
1403         g_return_val_if_fail (self && MODEST_IS_TNY_ACCOUNT_STORE(self), NULL);
1404         g_return_val_if_fail (account_name, NULL);
1405
1406         /* Get the current connection: */
1407         device = modest_runtime_get_device ();
1408
1409         if (!device) {
1410                 g_warning ("%s: could not get device", __FUNCTION__);
1411                 return NULL;
1412         }
1413                 
1414         if (!tny_device_is_online (device))
1415                 return NULL;
1416         
1417 #ifdef MODEST_HAVE_CONIC
1418         g_return_val_if_fail (TNY_IS_MAEMO_CONIC_DEVICE (device), NULL);
1419         
1420         TnyMaemoConicDevice *maemo_device = TNY_MAEMO_CONIC_DEVICE (device);    
1421         const gchar* iap_id = tny_maemo_conic_device_get_current_iap_id (maemo_device);
1422         /* printf ("DEBUG: %s: iap_id=%s\n", __FUNCTION__, iap_id); */
1423         if (!iap_id)
1424                 return NULL;
1425                 
1426         ConIcIap* connection = tny_maemo_conic_device_get_iap (maemo_device, iap_id);
1427         if (!connection)
1428                 return NULL;
1429                 
1430         const gchar *connection_id = con_ic_iap_get_id (connection);
1431         /* printf ("DEBUG: %s: connection_id=%s\n", __FUNCTION__, connection_id); */
1432         if (!connection_id)
1433                 return NULL;
1434         
1435         /*  Get the connection-specific transport acccount, if any: */
1436         ModestAccountMgr *account_manager = modest_runtime_get_account_mgr ();
1437
1438         /* Check if this account has connection-specific SMTP enabled */
1439         if (!modest_account_mgr_get_use_connection_specific_smtp (account_manager, account_name)) {
1440                 return NULL;
1441         }
1442
1443         gchar* server_account_name = modest_account_mgr_get_connection_specific_smtp (account_manager, 
1444                 connection_id);
1445
1446         /* printf ("DEBUG: %s: server_account_name=%s\n", __FUNCTION__, server_account_name); */
1447         if (!server_account_name) {
1448                 return NULL; /* No connection-specific SMTP server was specified for this connection. */
1449         }
1450
1451         TnyAccount* account = modest_tny_account_store_get_tny_account_by (self, 
1452                                                                            MODEST_TNY_ACCOUNT_STORE_QUERY_ID, 
1453                                                                            server_account_name);
1454
1455         /* printf ("DEBUG: %s: account=%p\n", __FUNCTION__, account); */
1456         g_free (server_account_name);
1457
1458         /* Unref the get()ed object, as required by the tny_maemo_conic_device_get_iap() documentation. */
1459         g_object_unref (connection);
1460
1461         return account;
1462 #else
1463         return NULL; /* TODO: Implement this for GNOME, instead of just Maemo? */
1464 #endif /* MODEST_HAVE_CONIC */
1465 }
1466
1467                                                                  
1468 TnyAccount*
1469 modest_tny_account_store_get_transport_account_for_open_connection (ModestTnyAccountStore *self,
1470                                                                     const gchar *account_name)
1471 {
1472         g_return_val_if_fail (self, NULL);
1473         g_return_val_if_fail (account_name, NULL);
1474
1475         if (!account_name || !self)
1476                 return NULL;
1477         
1478         /*  Get the connection-specific transport acccount, if any: */
1479         /* Note: This gives us a reference: */
1480         TnyAccount *account =
1481                 modest_tny_account_store_get_smtp_specific_transport_account_for_open_connection (self, account_name);
1482                         
1483         /* If there is no connection-specific transport account (the common case), 
1484          * just get the regular transport account: */
1485         if (!account) {
1486                 /* The special local folders don't have transport accounts. */
1487                 if (strcmp (account_name, MODEST_LOCAL_FOLDERS_ACCOUNT_ID) == 0)
1488                         account = NULL;
1489                 else {
1490                         /* Note: This gives us a reference: */
1491                         account = modest_tny_account_store_get_server_account (self, account_name, 
1492                                                      TNY_ACCOUNT_TYPE_TRANSPORT);
1493                 }
1494         }
1495                         
1496         /* returns a reference. */     
1497         return account;
1498 }
1499
1500 TnyAccount*
1501 modest_tny_account_store_get_local_folders_account (ModestTnyAccountStore *self)
1502 {
1503         TnyAccount *account = NULL;
1504         ModestTnyAccountStorePrivate *priv;
1505         TnyIterator *iter;
1506         gboolean found;
1507
1508         g_return_val_if_fail (MODEST_IS_TNY_ACCOUNT_STORE (self), NULL);
1509         
1510         priv = MODEST_TNY_ACCOUNT_STORE_GET_PRIVATE(self);
1511
1512         found = FALSE;
1513         iter = tny_list_create_iterator (priv->store_accounts);
1514         while (!tny_iterator_is_done (iter) && !found) {
1515                 TnyAccount *tmp_account;
1516
1517                 tmp_account = TNY_ACCOUNT (tny_iterator_get_current (iter));
1518                 if (modest_tny_account_is_virtual_local_folders (tmp_account)) {
1519                         account = g_object_ref (tmp_account);
1520                         found = TRUE;
1521                 }
1522                 g_object_unref (tmp_account);
1523                 tny_iterator_next (iter);
1524         }
1525         g_object_unref (iter);
1526
1527         /* Returns a new reference to the account */
1528         return account;
1529 }
1530
1531 TnyAccount*
1532 modest_tny_account_store_get_mmc_folders_account (ModestTnyAccountStore *self)
1533 {
1534         g_return_val_if_fail (MODEST_IS_TNY_ACCOUNT_STORE (self), NULL);
1535         
1536         /* New reference */
1537         return modest_tny_account_store_get_tny_account_by (self, MODEST_TNY_ACCOUNT_STORE_QUERY_ID,
1538                                                             MODEST_MMC_ACCOUNT_ID);
1539
1540 }
1541
1542 /*********************************************************************************/
1543 static void
1544 add_existing_accounts (ModestTnyAccountStore *self)
1545 {
1546         GSList *account_names = NULL, *iter = NULL;
1547         ModestTnyAccountStorePrivate *priv = NULL;
1548         
1549         priv = MODEST_TNY_ACCOUNT_STORE_GET_PRIVATE(self);
1550
1551         /* These are account names, not server_account names */
1552         account_names = modest_account_mgr_account_names (priv->account_mgr, FALSE);
1553
1554         for (iter = account_names; iter != NULL; iter = g_slist_next (iter)) {
1555                 const gchar *account_name = (const gchar*) iter->data;
1556                 
1557                 /* Insert all enabled accounts without notifying */
1558                 if (modest_account_mgr_get_enabled (priv->account_mgr, account_name))
1559                         insert_account (self, account_name, FALSE);
1560         }
1561         modest_account_mgr_free_account_names (account_names);
1562 }
1563
1564 static TnyAccount*
1565 create_tny_account (ModestTnyAccountStore *self,
1566                     const gchar *name,
1567                     TnyAccountType type,
1568                     gboolean notify)
1569 {
1570         TnyAccount *account = NULL;
1571         ModestTnyAccountStorePrivate *priv = NULL;
1572
1573         priv = MODEST_TNY_ACCOUNT_STORE_GET_PRIVATE(self);
1574
1575         account = modest_tny_account_new_from_account (priv->account_mgr,
1576                                                        name, type, 
1577                                                        priv->session,
1578                                                        get_password,
1579                                                        forget_password);
1580
1581         if (account) {
1582                 /* Forget any cached password for the account, so that
1583                    we use a new account if any */
1584                 modest_tny_account_store_forget_password_in_memory (self, tny_account_get_id (account));
1585
1586                 /* Set the account store */
1587                 g_object_set_data (G_OBJECT(account), "account_store", self);
1588         } else {
1589                 g_printerr ("modest: failed to create account for %s\n", name);
1590         }
1591
1592         return account;
1593 }
1594
1595 typedef struct _AddOutboxInfo {
1596         ModestTnyAccountStore *account_store;
1597         TnyAccount *transport_account;
1598 } AddOutboxInfo;
1599
1600 static void
1601 add_outbox_from_transport_account_to_global_outbox_get_folders_cb (TnyFolderStore *folder_store,
1602                                                                    gboolean cancelled,
1603                                                                    TnyList *list,
1604                                                                    GError *err,
1605                                                                    gpointer userdata)
1606 {
1607         TnyIterator *iter_folders;
1608         TnyFolder *per_account_outbox;
1609         TnyAccount *local_account = NULL;
1610         AddOutboxInfo *info = (AddOutboxInfo *) userdata;
1611         ModestTnyAccountStorePrivate *priv = NULL;
1612         ModestTnyAccountStore *self;
1613
1614         self = MODEST_TNY_ACCOUNT_STORE (info->account_store);
1615         priv = MODEST_TNY_ACCOUNT_STORE_GET_PRIVATE(self);
1616         
1617         /* Note that this could happen if there is not enough space
1618            available on disk, then the outbox folder could not be
1619            created */
1620         if (tny_list_get_length (list) != 1) {
1621                 g_warning ("%s: could not create outbox folder (%d folders found)", __FUNCTION__,
1622                            tny_list_get_length (list));
1623                 goto frees;
1624         }
1625                         
1626         iter_folders = tny_list_create_iterator (list);
1627         per_account_outbox = TNY_FOLDER (tny_iterator_get_current (iter_folders));
1628         g_object_unref (iter_folders);
1629         g_object_unref (list);
1630
1631         /* Add the outbox of the new per-account-local-outbox account
1632            to the global local merged OUTBOX of the local folders
1633            account */
1634         local_account = modest_tny_account_store_get_local_folders_account (info->account_store);
1635         modest_tny_local_folders_account_add_folder_to_outbox (MODEST_TNY_LOCAL_FOLDERS_ACCOUNT (local_account),
1636                                                                per_account_outbox);
1637         /* Add the pair to the hash table */
1638         g_hash_table_insert (priv->outbox_of_transport,
1639                              info->transport_account,
1640                              per_account_outbox);
1641         
1642         /* Notify that the local account changed */
1643         g_signal_emit (G_OBJECT (self), signals [ACCOUNT_CHANGED_SIGNAL], 0, local_account);
1644         g_object_unref (local_account);
1645         g_object_unref (per_account_outbox);
1646
1647  frees:
1648         g_object_unref (info->transport_account);
1649         g_slice_free (AddOutboxInfo, info);
1650 }
1651                                                                    
1652
1653 static void
1654 add_outbox_from_transport_account_to_global_outbox (ModestTnyAccountStore *self,
1655                                                     const gchar *account_name,
1656                                                     TnyAccount *transport_account)
1657 {
1658         TnyList *folders = NULL;
1659         TnyAccount *account_outbox = NULL;
1660         ModestTnyAccountStorePrivate *priv = NULL;
1661         AddOutboxInfo *info;
1662
1663         priv = MODEST_TNY_ACCOUNT_STORE_GET_PRIVATE(self);
1664
1665         /* Create per account local outbox */
1666         account_outbox = 
1667                 modest_tny_account_new_for_per_account_local_outbox_folder (priv->account_mgr, 
1668                                                                             account_name, 
1669                                                                             priv->session);
1670
1671         if (!G_IS_OBJECT (account_outbox)) {
1672                 g_warning ("%s: could not create per account local outbox folder", __FUNCTION__);
1673                 return;
1674         }
1675
1676         tny_list_append (priv->store_accounts_outboxes, G_OBJECT (account_outbox));
1677         
1678         /* Get the outbox folder */
1679         folders = tny_simple_list_new ();
1680         info = g_slice_new0 (AddOutboxInfo);
1681         info->account_store = self;
1682         info->transport_account = g_object_ref (transport_account);
1683         tny_folder_store_get_folders_async (TNY_FOLDER_STORE (account_outbox), folders, NULL, FALSE, 
1684                                             add_outbox_from_transport_account_to_global_outbox_get_folders_cb, NULL, (gpointer) info);
1685         g_object_unref (account_outbox);
1686 }
1687
1688 /*
1689  * This function will be used for both adding new accounts and for the
1690  * initialization. In the initialization we do not want to emit
1691  * signals so notify will be FALSE, in the case of account additions
1692  * we do want to notify the observers
1693  */
1694 static void
1695 insert_account (ModestTnyAccountStore *self,
1696                 const gchar *account,
1697                 gboolean is_new)
1698 {
1699         ModestTnyAccountStorePrivate *priv = NULL;
1700         TnyAccount *store_account = NULL, *transport_account = NULL;
1701
1702         priv = MODEST_TNY_ACCOUNT_STORE_GET_PRIVATE(self);
1703
1704         /* Get the server and the transport account */
1705         store_account = create_tny_account (self, account, TNY_ACCOUNT_TYPE_STORE, is_new);
1706         if (!store_account || !TNY_IS_ACCOUNT(store_account)) {
1707                 g_warning ("%s: failed to create store account", __FUNCTION__);
1708                 return;
1709         }
1710
1711         transport_account = create_tny_account (self, account, TNY_ACCOUNT_TYPE_TRANSPORT, is_new);
1712         if (!transport_account || !TNY_IS_ACCOUNT(transport_account)) {
1713                 g_warning ("%s: failed to create transport account", __FUNCTION__);
1714                 g_object_unref (store_account);
1715                 return;
1716         }
1717
1718         /* Add accounts to the lists */
1719         tny_list_append (priv->store_accounts, G_OBJECT (store_account));
1720         tny_list_append (priv->transport_accounts, G_OBJECT (transport_account));
1721
1722         /* Create a new pseudo-account with an outbox for this
1723            transport account and add it to the global outbox
1724            in the local account */
1725         add_outbox_from_transport_account_to_global_outbox (self, account, transport_account);
1726
1727         /* Force the creation of the send queue, this way send queues
1728            will automatically send missing emails when the connections
1729            become active */
1730         /* Notify the observers. We do it after everything is
1731            created */
1732         if (is_new) {
1733                 /* We only have to do this for new accounts, already
1734                    existing accounts at boot time are instantiated by
1735                    modest_tny_account_store_start_send_queues */
1736                 modest_runtime_get_send_queue ((TnyTransportAccount *) transport_account, TRUE);
1737
1738                 g_signal_emit (G_OBJECT (self), signals [ACCOUNT_INSERTED_SIGNAL], 0, store_account);
1739                 g_signal_emit (G_OBJECT (self), signals [ACCOUNT_INSERTED_SIGNAL], 0, transport_account);
1740         }
1741
1742         /* Frees */
1743         g_object_unref (store_account);
1744         g_object_unref (transport_account);
1745 }
1746
1747 static inline gboolean
1748 only_local_accounts (ModestTnyAccountStore *self)
1749 {
1750         return (modest_tny_account_store_get_num_remote_accounts (self) > 0) ? FALSE : TRUE;
1751 }
1752
1753 static void
1754 on_account_inserted (ModestAccountMgr *acc_mgr, 
1755                      const gchar *account,
1756                      gpointer user_data)
1757 {
1758         gboolean add_specific;
1759
1760         add_specific = only_local_accounts (MODEST_TNY_ACCOUNT_STORE (user_data));
1761
1762         /* Insert the account and notify the observers */
1763         insert_account (MODEST_TNY_ACCOUNT_STORE (user_data), account, TRUE);
1764
1765         /* If it's the first remote account then add the connection
1766            specific SMTP servers as well */
1767         if (add_specific)
1768                 add_connection_specific_transport_accounts (MODEST_TNY_ACCOUNT_STORE (user_data));
1769
1770 }
1771
1772 /* This is the callback of the tny_camel_account_set_online called in
1773    on_account_removed to disconnect the account */
1774 static void
1775 on_account_disconnect_when_removing (TnyCamelAccount *account, 
1776                                      gboolean canceled, 
1777                                      GError *err, 
1778                                      gpointer user_data)
1779 {
1780         ModestTnyAccountStore *self;
1781         ModestTnyAccountStorePrivate *priv;
1782
1783         self = MODEST_TNY_ACCOUNT_STORE (user_data);
1784         priv = MODEST_TNY_ACCOUNT_STORE_GET_PRIVATE (self);
1785
1786         /* Cancel all pending operations */
1787         tny_account_cancel (TNY_ACCOUNT (account));
1788         
1789         /* Unref the extra reference added by get_server_account */
1790         g_object_unref (account);
1791
1792         /* Clear the cache if it's an store account */
1793         if (TNY_IS_STORE_ACCOUNT (account)) {
1794                 tny_store_account_delete_cache (TNY_STORE_ACCOUNT (account));
1795         } else if (TNY_IS_TRANSPORT_ACCOUNT (account)) {
1796                 ModestTnySendQueue* send_queue;
1797                 send_queue = modest_runtime_get_send_queue (TNY_TRANSPORT_ACCOUNT (account), FALSE);
1798                 if (TNY_IS_SEND_QUEUE (send_queue)) {
1799                         if (modest_tny_send_queue_sending_in_progress (send_queue))
1800                                 tny_send_queue_cancel (TNY_SEND_QUEUE (send_queue),
1801                                                        TNY_SEND_QUEUE_CANCEL_ACTION_REMOVE, 
1802                                                        NULL);
1803                         modest_runtime_remove_send_queue (TNY_TRANSPORT_ACCOUNT (account));
1804                 }
1805         }
1806 }
1807
1808 /*
1809  * We use this one for both removing "normal" and "connection
1810  * specific" transport accounts
1811  */
1812 static void
1813 remove_transport_account (ModestTnyAccountStore *self,
1814                           TnyTransportAccount *transport_account)
1815 {
1816         ModestTnyAccountStorePrivate *priv;
1817         TnyFolder *outbox = NULL;
1818         
1819         priv = MODEST_TNY_ACCOUNT_STORE_GET_PRIVATE (self);
1820
1821         /* Remove it from the list of accounts and notify the
1822            observers. Do not need to wait for account
1823            disconnection */
1824         g_signal_emit (G_OBJECT (self), signals [ACCOUNT_REMOVED_SIGNAL], 0, transport_account);
1825         tny_list_remove (priv->transport_accounts, (GObject *) transport_account);
1826                 
1827         /* Remove the OUTBOX of the account from the global outbox */
1828         outbox = g_hash_table_lookup (priv->outbox_of_transport, transport_account);
1829
1830         if (TNY_IS_FOLDER (outbox)) {
1831                 TnyAccount *local_account = NULL;
1832                 TnyAccount *outbox_account = tny_folder_get_account (outbox);
1833
1834                 if (outbox_account) {
1835                         tny_list_remove (priv->store_accounts_outboxes, G_OBJECT (outbox_account));
1836                         /* Remove existing emails to send */
1837                         tny_store_account_delete_cache (TNY_STORE_ACCOUNT (outbox_account));
1838                         g_object_unref (outbox_account);
1839                 }
1840
1841                 local_account = modest_tny_account_store_get_local_folders_account (self);
1842                 modest_tny_local_folders_account_remove_folder_from_outbox (MODEST_TNY_LOCAL_FOLDERS_ACCOUNT (local_account),
1843                                                                             outbox);
1844
1845                 g_hash_table_remove (priv->outbox_of_transport, transport_account);
1846
1847                 /* Notify the change in the local account */
1848                 g_signal_emit (G_OBJECT (self), signals [ACCOUNT_CHANGED_SIGNAL], 0, local_account);
1849                 g_object_unref (local_account);
1850         } else {
1851                 g_warning ("Removing a transport account that has no outbox");
1852         }
1853
1854         /* Cancel all pending operations */
1855         tny_account_cancel (TNY_ACCOUNT (transport_account));
1856
1857         /* Disconnect and notify the observers. The callback will free the reference */
1858         tny_camel_account_set_online (TNY_CAMEL_ACCOUNT (transport_account), FALSE,
1859                                       on_account_disconnect_when_removing, self);
1860 }
1861
1862 static gboolean
1863 images_cache_remove_filter (TnyStreamCache *self, const gchar *id, const gchar *account_name)
1864 {
1865         gchar *account_name_with_separator;
1866         gboolean result;
1867         if (account_name == NULL || account_name[0] == '\0')
1868                 return FALSE;
1869
1870         if (id == NULL || id[0] == '\0')
1871                 return FALSE;
1872
1873         account_name_with_separator = g_strconcat (account_name, "__", NULL);
1874
1875         result = (g_str_has_prefix (id, account_name));
1876         g_free (account_name_with_separator);
1877
1878         return result;
1879 }
1880
1881 static void
1882 on_account_removed (ModestAccountMgr *acc_mgr, 
1883                     const gchar *account,
1884                     gpointer user_data)
1885 {
1886         TnyAccount *store_account = NULL, *transport_account = NULL;
1887         ModestTnyAccountStore *self;
1888         ModestTnyAccountStorePrivate *priv;
1889         TnyStreamCache *stream_cache;
1890         
1891         self = MODEST_TNY_ACCOUNT_STORE (user_data);
1892         priv = MODEST_TNY_ACCOUNT_STORE_GET_PRIVATE (self);
1893
1894         /* Get the server and the transport account */
1895         store_account = 
1896                 modest_tny_account_store_get_server_account (self, account, 
1897                                                              TNY_ACCOUNT_TYPE_STORE);
1898         transport_account = 
1899                 modest_tny_account_store_get_server_account (self, account,
1900                                                              TNY_ACCOUNT_TYPE_TRANSPORT);
1901         
1902         /* If there was any problem creating the account, for example,
1903            with the configuration system this could not exist */
1904         if (TNY_IS_STORE_ACCOUNT(store_account)) {
1905                 /* Forget any cached password for the account */
1906                 modest_tny_account_store_forget_password_in_memory (self, tny_account_get_id (store_account));
1907
1908                 /* Remove it from the list of accounts and notify the
1909                    observers. Do not need to wait for account
1910                    disconnection */
1911                 tny_list_remove (priv->store_accounts, (GObject *) store_account);
1912                 g_signal_emit (G_OBJECT (self), signals [ACCOUNT_REMOVED_SIGNAL], 0, store_account);
1913
1914                 /* Cancel all pending operations */
1915                 tny_account_cancel (TNY_ACCOUNT (store_account));
1916
1917                 /* Disconnect before deleting the cache, because the
1918                    disconnection will rewrite the cache to the
1919                    disk */
1920                 tny_camel_account_set_online (TNY_CAMEL_ACCOUNT (store_account), FALSE,
1921                                               on_account_disconnect_when_removing, self);
1922         } else {
1923                 g_warning ("%s: no store account for account %s\n", 
1924                            __FUNCTION__, account);
1925         }
1926
1927         /* If there was any problem creating the account, for example,
1928            with the configuration system this could not exist */
1929         if (TNY_IS_TRANSPORT_ACCOUNT(transport_account)) {
1930
1931                 /* Forget any cached password for the account */
1932                 modest_tny_account_store_forget_password_in_memory (self, tny_account_get_id (transport_account));
1933
1934                 /* Remove transport account. It'll free the reference
1935                    added by get_server_account */
1936                 remove_transport_account (self, TNY_TRANSPORT_ACCOUNT (transport_account));
1937         } else {
1938                 g_warning ("%s: no transport account for account %s\n", 
1939                            __FUNCTION__, account);
1940         }
1941
1942         /* Remove cached images */
1943         stream_cache = modest_runtime_get_images_cache ();
1944         tny_stream_cache_remove (stream_cache, (TnyStreamCacheRemoveFilter) images_cache_remove_filter, (gpointer) account);
1945
1946         /* If there are no more user accounts then delete the
1947            transport specific SMTP servers */
1948         if (only_local_accounts (self))
1949                 remove_connection_specific_transport_accounts (self);
1950 }
1951
1952 TnyTransportAccount *
1953 modest_tny_account_store_new_connection_specific_transport_account (ModestTnyAccountStore *self,
1954                                                                     const gchar *name)
1955 {
1956         ModestTnyAccountStorePrivate *priv = NULL;
1957         TnyAccount * tny_account = NULL;
1958
1959         priv = MODEST_TNY_ACCOUNT_STORE_GET_PRIVATE(self);
1960
1961         /* Add the account: */
1962         tny_account = 
1963                 modest_tny_account_new_from_server_account_name (priv->account_mgr, 
1964                                                                  priv->session, 
1965                                                                  name,
1966                                                                  get_password,
1967                                                                  forget_password);
1968         if (tny_account) {
1969                 g_object_set_data (G_OBJECT(tny_account), 
1970                                    "account_store", 
1971                                    (gpointer)self);
1972                 g_object_set_data (G_OBJECT(tny_account), 
1973                                    "connection_specific", 
1974                                    GINT_TO_POINTER (TRUE));
1975                 
1976                 tny_list_append (priv->transport_accounts, G_OBJECT (tny_account));
1977                 add_outbox_from_transport_account_to_global_outbox (self, 
1978                                                                     name, 
1979                                                                     tny_account);
1980                 
1981         } else
1982                 g_printerr ("modest: failed to create smtp-specific account for %s\n",
1983                             name);
1984
1985         return TNY_TRANSPORT_ACCOUNT (tny_account);
1986 }
1987
1988 static void 
1989 foreach_free_string(gpointer data,
1990                     gpointer user_data)
1991 {
1992         g_free (data);
1993 }
1994
1995 static void
1996 add_connection_specific_transport_accounts (ModestTnyAccountStore *self)
1997 {
1998         ModestTnyAccountStorePrivate *priv = NULL;
1999         GSList *list_specifics = NULL, *iter = NULL;
2000         GError *err = NULL;
2001
2002         priv = MODEST_TNY_ACCOUNT_STORE_GET_PRIVATE(self);
2003
2004         list_specifics = modest_conf_get_list (modest_runtime_get_conf (),
2005                                                MODEST_CONF_CONNECTION_SPECIFIC_SMTP_LIST,
2006                                                MODEST_CONF_VALUE_STRING, &err);
2007         if (err) {
2008                 g_error_free (err);
2009                 g_return_if_reached ();
2010                 return;
2011         }
2012                                 
2013         /* Look at each connection-specific transport account for the 
2014          * modest account: */
2015         iter = list_specifics;
2016         while (iter) {
2017                 /* The list alternates between the connection name and the transport name: */
2018                 iter = g_slist_next (iter);
2019                 if (iter) {
2020                         const gchar* transport_account_name = (const gchar*) (iter->data);
2021                         TnyTransportAccount * account = NULL;
2022                         account = modest_tny_account_store_new_connection_specific_transport_account (
2023                                 self, transport_account_name);
2024                         if (account)
2025                                 g_object_unref (account);
2026                 }                               
2027                 iter = g_slist_next (iter);
2028         }
2029
2030         /* Free the list */
2031         g_slist_foreach (list_specifics, foreach_free_string, NULL);
2032         g_slist_free (list_specifics);
2033 }
2034
2035 static void
2036 remove_connection_specific_transport_accounts (ModestTnyAccountStore *self)
2037 {
2038         ModestTnyAccountStorePrivate *priv = NULL;
2039         GSList *list_specifics = NULL, *iter = NULL;
2040         GError *err = NULL;
2041
2042         priv = MODEST_TNY_ACCOUNT_STORE_GET_PRIVATE(self);
2043
2044         err = NULL;
2045         list_specifics = modest_conf_get_list (modest_runtime_get_conf (),
2046                                                MODEST_CONF_CONNECTION_SPECIFIC_SMTP_LIST,
2047                                                MODEST_CONF_VALUE_STRING, &err);
2048         if (err) {
2049                 g_error_free (err);
2050                 g_return_if_reached ();
2051                 return;
2052         }
2053                                 
2054         /* Look at each connection-specific transport account for the 
2055          * modest account: */
2056         iter = list_specifics;
2057         while (iter) {
2058                 /* The list alternates between the connection name and the transport name: */
2059                 iter = g_slist_next (iter);
2060                 if (iter) {
2061                         const gchar* transport_account_name = (const gchar*) (iter->data);
2062                         TnyAccount * account;
2063                         account = modest_tny_account_store_get_server_account (self,
2064                                                                                transport_account_name,
2065                                                                                TNY_ACCOUNT_TYPE_TRANSPORT);
2066
2067                         /* the call will free the reference */
2068                         if (account)
2069                                 remove_transport_account (self, TNY_TRANSPORT_ACCOUNT (account));
2070                 }                               
2071                 iter = g_slist_next (iter);
2072         }
2073
2074         /* Free the list */
2075         g_slist_foreach (list_specifics, foreach_free_string, NULL);
2076         g_slist_free (list_specifics);
2077 }
2078
2079
2080 TnyMsg *
2081 modest_tny_account_store_find_msg_in_outboxes (ModestTnyAccountStore *self, 
2082                                                const gchar *uri,
2083                                                TnyAccount **ac_out)
2084 {
2085         TnyIterator *acc_iter;
2086         ModestTnyAccountStorePrivate *priv;
2087         TnyMsg *msg = NULL;
2088         TnyAccount *msg_account = NULL;
2089
2090         g_return_val_if_fail (MODEST_IS_TNY_ACCOUNT_STORE (self), NULL);
2091         priv = MODEST_TNY_ACCOUNT_STORE_GET_PRIVATE (self);
2092
2093         acc_iter = tny_list_create_iterator (priv->store_accounts_outboxes);
2094         while (!msg && !tny_iterator_is_done (acc_iter)) {
2095                 TnyList *folders = tny_simple_list_new ();
2096                 TnyAccount *account = TNY_ACCOUNT (tny_iterator_get_current (acc_iter));
2097                 TnyIterator *folders_iter = NULL;
2098
2099                 tny_folder_store_get_folders (TNY_FOLDER_STORE (account), folders, NULL, FALSE, NULL);
2100                 folders_iter = tny_list_create_iterator (folders);
2101
2102                 while (msg == NULL && !tny_iterator_is_done (folders_iter)) {
2103                         TnyFolder *folder = TNY_FOLDER (tny_iterator_get_current (folders_iter));
2104                         msg = tny_folder_find_msg (folder, uri, NULL);
2105
2106                         if (msg)
2107                                 msg_account = g_object_ref (account);
2108
2109                         g_object_unref (folder);
2110                         tny_iterator_next (folders_iter);
2111                 }
2112                 g_object_unref (folders_iter);
2113
2114                 g_object_unref (folders);
2115                 g_object_unref (account);
2116                 tny_iterator_next (acc_iter);
2117         }
2118
2119         g_object_unref (acc_iter);
2120
2121         if (ac_out != NULL)
2122                 *ac_out = msg_account;
2123
2124         return msg;
2125 }
2126
2127 TnyTransportAccount *
2128 modest_tny_account_store_get_transport_account_from_outbox_header(ModestTnyAccountStore *self, TnyHeader *header)
2129 {
2130         TnyIterator *acc_iter;
2131         ModestTnyAccountStorePrivate *priv;
2132         TnyTransportAccount *header_acc = NULL;
2133         gchar *msg_id;
2134
2135         g_return_val_if_fail (MODEST_IS_TNY_ACCOUNT_STORE (self), NULL);
2136         g_return_val_if_fail (TNY_IS_HEADER (header), NULL);
2137         priv = MODEST_TNY_ACCOUNT_STORE_GET_PRIVATE (self);
2138         msg_id = modest_tny_send_queue_get_msg_id (header);
2139         acc_iter = tny_list_create_iterator (priv->transport_accounts);
2140         while (!header_acc && !tny_iterator_is_done (acc_iter)) {
2141                 TnyTransportAccount *account = TNY_TRANSPORT_ACCOUNT (tny_iterator_get_current (acc_iter));
2142                 ModestTnySendQueue *send_queue;
2143                 ModestTnySendQueueStatus status;
2144                 send_queue = modest_runtime_get_send_queue(TNY_TRANSPORT_ACCOUNT(account), TRUE);
2145                 if (TNY_IS_SEND_QUEUE (send_queue)) {
2146                         status = modest_tny_send_queue_get_msg_status(send_queue, msg_id);
2147                         if (status != MODEST_TNY_SEND_QUEUE_UNKNOWN)
2148                                 header_acc = g_object_ref(account);
2149                 }
2150                 g_object_unref (account);
2151                 tny_iterator_next (acc_iter);
2152         }
2153         g_object_unref(acc_iter);
2154         g_free (msg_id);
2155
2156         /* New reference */
2157         return header_acc;
2158 }
2159
2160 typedef struct {
2161         ModestTnyAccountStore *account_store;
2162         ModestTnyAccountStoreShutdownCallback callback;
2163         gpointer userdata;
2164         gint pending;
2165 } ShutdownOpData;
2166
2167 static void
2168 account_shutdown_callback (TnyCamelAccount *account, gboolean canceled, GError *err, gpointer userdata)
2169 {
2170         ShutdownOpData *op_data = (ShutdownOpData *) userdata;
2171         op_data->pending--;
2172         if (op_data->pending == 0) {
2173                 if (op_data->callback)
2174                         op_data->callback (op_data->account_store, op_data->userdata);
2175                 g_object_unref (op_data->account_store);
2176                 g_free (op_data);
2177         }
2178 }
2179
2180 static void
2181 account_shutdown (TnyAccount *account, ShutdownOpData *op_data)
2182 {
2183         g_return_if_fail (account && TNY_IS_ACCOUNT(account));
2184
2185         if (TNY_IS_STORE_ACCOUNT (account) && 
2186             !modest_tny_folder_store_is_remote (TNY_FOLDER_STORE (account))) {
2187                 op_data->pending--;
2188                 return;
2189         }
2190
2191         /* Disconnect account */
2192         tny_camel_account_set_online (TNY_CAMEL_ACCOUNT(account), FALSE, 
2193                                       account_shutdown_callback, op_data);
2194
2195 }
2196
2197
2198 void
2199 modest_tny_account_store_shutdown (ModestTnyAccountStore *self,
2200                                    ModestTnyAccountStoreShutdownCallback callback,
2201                                    gpointer userdata)
2202 {
2203         gint num_accounts;
2204         ShutdownOpData *op_data;
2205         ModestTnyAccountStorePrivate *priv = MODEST_TNY_ACCOUNT_STORE_GET_PRIVATE (self);
2206
2207         /* Get references */
2208         num_accounts = 0;
2209         if (priv->store_accounts)
2210                 num_accounts += tny_list_get_length (priv->store_accounts);
2211         if (priv->transport_accounts)
2212                 num_accounts += tny_list_get_length (priv->transport_accounts);
2213
2214         /* Create the helper object */
2215         op_data = g_new0 (ShutdownOpData, 1);
2216         op_data->callback = callback;
2217         op_data->userdata = userdata;
2218         op_data->pending = num_accounts;
2219         op_data->account_store = g_object_ref (self);
2220
2221         /* Force the TnyDevice to be offline. This way new
2222            undesired connections won't be initiated */
2223         tny_device_force_offline (priv->device);
2224
2225         /* Destroy all accounts. Disconnect all accounts before they are destroyed */
2226         if (priv->store_accounts) {
2227                 tny_list_foreach (priv->store_accounts, (GFunc)account_shutdown, op_data);
2228         }
2229
2230         if (priv->transport_accounts) {
2231                 tny_list_foreach (priv->transport_accounts, (GFunc)account_shutdown, op_data);
2232         }
2233
2234         if (op_data->pending == 0) {
2235                 if (op_data->callback)
2236                         op_data->callback (op_data->account_store, op_data->userdata);
2237                 g_object_unref (op_data->account_store);
2238                 g_free (op_data);
2239         }
2240 }
2241
2242 gboolean 
2243 modest_tny_account_store_is_shutdown (ModestTnyAccountStore *self)
2244 {
2245         ModestTnyAccountStorePrivate *priv = MODEST_TNY_ACCOUNT_STORE_GET_PRIVATE (self);
2246         TnyIterator *iter;
2247         gboolean found;
2248
2249         found = FALSE;
2250
2251         for (iter = tny_list_create_iterator (priv->store_accounts);
2252              !found && !tny_iterator_is_done (iter);
2253              tny_iterator_next (iter)) {
2254                 TnyAccount *account;
2255
2256                 account = (TnyAccount *) tny_iterator_get_current (iter);
2257                 if (TNY_IS_ACCOUNT (account)) {
2258                         found = (tny_account_get_connection_status (account) == TNY_CONNECTION_STATUS_CONNECTED) ||
2259                                 (tny_account_get_connection_status (account) == TNY_CONNECTION_STATUS_RECONNECTING);
2260                 }
2261                 g_object_unref (account);
2262         }
2263         g_object_unref (iter);
2264
2265         if (found)
2266                 return !found;
2267
2268         for (iter = tny_list_create_iterator (priv->transport_accounts);
2269              !found && !tny_iterator_is_done (iter);
2270              tny_iterator_next (iter)) {
2271                 TnyAccount *account;
2272
2273                 account = (TnyAccount *) tny_iterator_get_current (iter);
2274                 if (TNY_IS_ACCOUNT (account)) {
2275                         found = (tny_account_get_connection_status (account) == TNY_CONNECTION_STATUS_CONNECTED) ||
2276                                 (tny_account_get_connection_status (account) == TNY_CONNECTION_STATUS_RECONNECTING);
2277                 }
2278                 g_object_unref (account);
2279         }
2280         g_object_unref (iter);
2281
2282         return !found;
2283
2284 }
2285
2286
2287 gboolean 
2288 modest_tny_account_store_is_send_mail_blocked (ModestTnyAccountStore *self)
2289 {
2290         ModestTnyAccountStorePrivate *priv;
2291
2292         priv = MODEST_TNY_ACCOUNT_STORE_GET_PRIVATE(self);
2293
2294         return priv->send_mail_blocked;
2295 }
2296
2297 void 
2298 modest_tny_account_store_set_send_mail_blocked (ModestTnyAccountStore *self, 
2299                                                 gboolean blocked)
2300 {
2301         ModestTnyAccountStorePrivate *priv;
2302
2303         priv = MODEST_TNY_ACCOUNT_STORE_GET_PRIVATE(self);
2304
2305         priv->send_mail_blocked = blocked;
2306 }
2307
2308 static void
2309 count_remote_accounts (gpointer data, gpointer user_data)
2310 {
2311         TnyFolderStore *account = TNY_FOLDER_STORE (data);
2312         gint *count = (gint *) user_data;
2313
2314         if (modest_tny_folder_store_is_remote (account))
2315                 (*count)++;
2316 }
2317
2318 guint
2319 modest_tny_account_store_get_num_remote_accounts (ModestTnyAccountStore *self)
2320 {
2321         ModestTnyAccountStorePrivate *priv;
2322         gint count = 0;
2323
2324         g_return_val_if_fail (MODEST_IS_TNY_ACCOUNT_STORE (self), 0);
2325
2326         /* Count remote accounts */
2327         priv = MODEST_TNY_ACCOUNT_STORE_GET_PRIVATE (self);
2328         tny_list_foreach (priv->store_accounts, (GFunc) count_remote_accounts, &count);
2329
2330         return count;
2331 }
2332
2333 static void
2334 init_send_queue (TnyAccount *account, gpointer user_data)
2335 {
2336         modest_runtime_get_send_queue ((TnyTransportAccount *) account, TRUE);
2337 }
2338
2339 void
2340 modest_tny_account_store_start_send_queues (ModestTnyAccountStore *self)
2341 {
2342         ModestTnyAccountStorePrivate *priv;
2343         TnyList *tmp;
2344
2345         g_return_if_fail (MODEST_IS_TNY_ACCOUNT_STORE (self));
2346
2347         priv = MODEST_TNY_ACCOUNT_STORE_GET_PRIVATE (self);
2348
2349         /* We need to create a copy of the list because if the send
2350            queues are created they'll directly access to the TnyList
2351            of transport accounts, and thus we'll end up blocked in the
2352            mutex the TnyList uses to synchronize accesses */
2353         tmp = tny_list_copy (priv->transport_accounts);
2354
2355         /* Just instantiate them. They'll begin to listen for
2356            connection changes to send messages ASAP */
2357         tny_list_foreach (tmp, (GFunc) init_send_queue, NULL);
2358         g_object_unref (tmp);
2359 }
2360
2361
2362 gboolean
2363 modest_tny_account_store_check_disk_full_error (ModestTnyAccountStore *self,
2364                                                 GtkWidget *parent_window,
2365                                                 GError *err,
2366                                                 TnyAccount *account,
2367                                                 const gchar *alternate)
2368 {
2369         if (err == NULL)
2370                 return FALSE;
2371
2372         if (modest_tny_account_store_is_disk_full_error (self, err, account)) {
2373                 gboolean is_mcc = modest_tny_account_is_memory_card_account (account);
2374                 if (is_mcc && alternate) {
2375                         modest_platform_information_banner (parent_window, NULL, alternate);
2376                 } else {
2377                         gchar *msg = g_strdup_printf (_KR("cerm_device_memory_full"), "");
2378                         modest_platform_information_banner (parent_window, NULL, msg);
2379                         g_free (msg);
2380                 }
2381         } else if (err->code == TNY_SYSTEM_ERROR_MEMORY)
2382                 /* If the account was created in memory full
2383                    conditions then tinymail won't be able to
2384                    connect so it'll return this error code */
2385                 modest_platform_information_banner (parent_window,
2386                                                     NULL, _("emev_ui_imap_inbox_select_error"));
2387         else
2388                 return FALSE;
2389
2390         return TRUE;
2391 }
2392
2393 gboolean
2394 modest_tny_account_store_is_disk_full_error (ModestTnyAccountStore *self,
2395                                              GError *error,
2396                                              TnyAccount *account)
2397 {
2398         gboolean enough_free_space = TRUE;
2399         GnomeVFSURI *cache_dir_uri;
2400         const gchar *cache_dir = NULL;
2401         GnomeVFSFileSize free_space;
2402
2403         /* Cache dir is different in case we're using an external storage (like MMC account) */
2404         if (account && modest_tny_account_is_memory_card_account (account))
2405                 cache_dir = g_getenv (MODEST_MMC1_VOLUMEPATH_ENV);
2406
2407         /* Get the default local cache dir */
2408         if (!cache_dir)
2409                 cache_dir = tny_account_store_get_cache_dir ((TnyAccountStore *) self);
2410
2411         cache_dir_uri = gnome_vfs_uri_new (cache_dir);
2412         if (cache_dir_uri) {
2413                 if (gnome_vfs_get_volume_free_space (cache_dir_uri, &free_space) == GNOME_VFS_OK) {
2414                         if (free_space < MODEST_TNY_ACCOUNT_STORE_MIN_FREE_SPACE)
2415                                 enough_free_space = FALSE;
2416                 }
2417                 gnome_vfs_uri_unref (cache_dir_uri);
2418         }
2419
2420         if ((error->code == TNY_SYSTEM_ERROR_MEMORY ||
2421              /* When asking for a mail and no space left on device
2422                 tinymail returns this error */
2423              error->code == TNY_SERVICE_ERROR_MESSAGE_NOT_AVAILABLE ||
2424              /* When the folder summary could not be read or
2425                 written */
2426              error->code == TNY_IO_ERROR_WRITE ||
2427              error->code == TNY_IO_ERROR_READ) &&
2428             !enough_free_space) {
2429                 return TRUE;
2430         } else {
2431                 return FALSE;
2432         }
2433 }