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