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