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