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