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