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