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