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