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