version and bug fix info update for this testing image
[modest] / src / modest-tny-account-store.c
1 /* Copyright (c) 2006, Nokia Corporation
2  * All rights reserved.
3  *
4  * Redistribution and use in source and binary forms, with or without
5  * modification, are permitted provided that the following conditions are
6  * met:
7  *
8  * * Redistributions of source code must retain the above copyright
9  *   notice, this list of conditions and the following disclaimer.
10  * * Redistributions in binary form must reproduce the above copyright
11  *   notice, this list of conditions and the following disclaimer in the
12  *   documentation and/or other materials provided with the distribution.
13  * * Neither the name of the Nokia Corporation nor the names of its
14  *   contributors may be used to endorse or promote products derived from
15  *   this software without specific prior written permission.
16  *
17  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
18  * IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
19  * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
20  * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER
21  * OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
22  * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
23  * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
24  * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
25  * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
26  * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
27  * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
28  */
29
30 #include <string.h>
31 #include <glib/gi18n.h>
32
33 #include <tny-error.h>
34 #include <tny-account.h>
35 #include <tny-account-store.h>
36 #include <tny-store-account.h>
37 #include <tny-transport-account.h>
38 #include <tny-simple-list.h>
39 #include <tny-account-store.h>
40 #include <tny-camel-transport-account.h>
41 #include <tny-camel-imap-store-account.h>
42 #include <tny-camel-pop-store-account.h>
43 #include "modest-text-utils.h"
44 #include <modest-runtime.h>
45 #include <modest-marshal.h>
46 #include <modest-protocol-registry.h>
47 #include <modest-local-folder-info.h>
48 #include "modest-account-protocol.h"
49 #include <modest-tny-account.h>
50 #include <modest-tny-local-folders-account.h>
51 #include <modest-account-mgr.h>
52 #include <modest-account-mgr-helpers.h>
53 #include <widgets/modest-window-mgr.h>
54 #include <modest-signal-mgr.h>
55 #include <modest-debug.h>
56 #include "modest-utils.h"
57 #include <modest-defs.h>
58 #include "modest-tny-account-store.h"
59 #include "modest-tny-platform-factory.h"
60 #include <tny-gtk-lockable.h>
61 #include <camel/camel.h>
62 #include <modest-platform.h>
63 #include "modest-ui-actions.h"
64 #include <widgets/modest-account-settings-dialog.h>
65
66 #ifdef MODEST_PLATFORM_MAEMO
67 #include <tny-maemo-conic-device.h>
68 #include <maemo/modest-maemo-utils.h>
69 #endif
70
71 #include <libgnomevfs/gnome-vfs-volume-monitor.h>
72
73 /* 'private'/'protected' functions */
74 static void    modest_tny_account_store_class_init   (ModestTnyAccountStoreClass *klass);
75 static void    modest_tny_account_store_finalize     (GObject *obj);
76 static void    modest_tny_account_store_instance_init (ModestTnyAccountStore *obj);
77 static void    modest_tny_account_store_init          (gpointer g, gpointer iface_data);
78 static void    modest_tny_account_store_base_init     (gpointer g_class);
79
80 static void    on_account_inserted         (ModestAccountMgr *acc_mgr,
81                                             const gchar *account,
82                                             gpointer user_data);
83
84 static void   add_existing_accounts       (ModestTnyAccountStore *self);
85
86 static void    insert_account              (ModestTnyAccountStore *self,
87                                             const gchar *account,
88                                             gboolean is_new);
89
90 static void    on_account_removed          (ModestAccountMgr *acc_mgr, 
91                                             const gchar *account,
92                                             gpointer user_data);
93
94 static gchar*  get_password                (TnyAccount *account, 
95                                             const gchar * prompt_not_used, 
96                                             gboolean *cancel);
97
98 static void    forget_password             (TnyAccount *account);
99
100 static void    on_vfs_volume_mounted       (GnomeVFSVolumeMonitor *volume_monitor, 
101                                             GnomeVFSVolume *volume, 
102                                             gpointer user_data);
103
104 static void    on_vfs_volume_unmounted     (GnomeVFSVolumeMonitor *volume_monitor, 
105                                             GnomeVFSVolume *volume, 
106                                             gpointer user_data);
107
108 static void    forget_password_in_memory (ModestTnyAccountStore *self, 
109                                           const gchar *server_account_name);
110
111 static void    add_connection_specific_transport_accounts         (ModestTnyAccountStore *self);
112
113 static void    remove_connection_specific_transport_accounts      (ModestTnyAccountStore *self);
114
115 static inline gboolean only_local_accounts        (ModestTnyAccountStore *self);
116
117 /* list my signals */
118 enum {
119         ACCOUNT_CHANGED_SIGNAL,
120         ACCOUNT_INSERTED_SIGNAL,
121         ACCOUNT_REMOVED_SIGNAL,
122
123         PASSWORD_REQUESTED_SIGNAL,
124         LAST_SIGNAL
125 };
126
127 typedef struct _ModestTnyAccountStorePrivate ModestTnyAccountStorePrivate;
128 struct _ModestTnyAccountStorePrivate {
129         gchar              *cache_dir;  
130         GHashTable         *password_hash;
131         ModestAccountMgr   *account_mgr;
132         TnySessionCamel    *session;
133         TnyDevice          *device;
134
135         GSList *sighandlers;
136         
137         /* We cache the lists of accounts here */
138         TnyList             *store_accounts;
139         TnyList             *transport_accounts;
140         TnyList             *store_accounts_outboxes;
141         
142         /* Matches transport accounts and outbox folder */
143         GHashTable          *outbox_of_transport;
144
145         /* is sending mail blocked? */
146         gboolean send_mail_blocked;
147 };
148
149 #define MODEST_TNY_ACCOUNT_STORE_GET_PRIVATE(o)      (G_TYPE_INSTANCE_GET_PRIVATE((o), \
150                                                       MODEST_TYPE_TNY_ACCOUNT_STORE, \
151                                                       ModestTnyAccountStorePrivate))
152
153 /* 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         case TNY_SERVICE_ERROR_AUTHENTICATE:
1091                 /* It seems that there's no better error to show with
1092                  * POP and IMAP because TNY_SERVICE_ERROR_AUTHENTICATE
1093                  * may appear if there's a timeout during auth */
1094                 if (protocol) {
1095                         prompt = modest_protocol_get_translation (protocol, MODEST_PROTOCOL_TRANSLATION_AUTH_ERROR, server_name);
1096                 }
1097                 if (!prompt) {
1098                         g_return_val_if_reached (FALSE);
1099                 }
1100                 break;
1101         case TNY_SERVICE_ERROR_CERTIFICATE:
1102                 /* We'll show the proper dialog later */
1103                 break;
1104
1105         case TNY_SYSTEM_ERROR_MEMORY:
1106                 /* Can't allocate memory for this operation */
1107                 if (modest_tny_account_store_check_disk_full_error ((ModestTnyAccountStore*)self,
1108                                                                     NULL, error, account, NULL))
1109                         retval = FALSE;
1110                 break;
1111         case TNY_SERVICE_ERROR_UNKNOWN:
1112                 return FALSE;
1113         default:
1114                 /* We don't treat this as an error, but as a not handled message. Then,
1115                  * debug message, and return false */
1116                 g_debug ("Unexpected error %d (%s)", error->code, error->message);
1117                 return FALSE;
1118         }
1119
1120
1121         if (error->code == TNY_SERVICE_ERROR_CERTIFICATE)
1122                 retval = modest_platform_run_certificate_confirmation_dialog (server_name,
1123                                                                               error->message);
1124         else if (error->code == TNY_SERVICE_ERROR_AUTHENTICATE ||
1125                  error->code == TNY_SERVICE_ERROR_CONNECT) {
1126                 TnyDevice *device = modest_runtime_get_device ();
1127
1128                 modest_platform_run_information_dialog (NULL, prompt, TRUE);
1129
1130                 /* Show the account dialog. Checking the online status
1131                    allows us to minimize the number of times that we
1132                    incorrectly show the dialog */
1133                 if (tny_device_is_online (device))
1134                         show_wrong_password_dialog (account,
1135                                                     (error->code == TNY_SERVICE_ERROR_CONNECT) ? FALSE : TRUE);
1136
1137                 retval = TRUE;
1138         }
1139
1140         g_debug ("%s: error code %d (%s", __FUNCTION__, error->code, error->message);
1141
1142         if (prompt)
1143                 g_free (prompt);
1144
1145         return retval;
1146 }
1147
1148
1149 static void
1150 modest_tny_account_store_init (gpointer g, gpointer iface_data)
1151 {
1152         TnyAccountStoreIface *klass;
1153
1154         g_return_if_fail (g);
1155
1156         klass = (TnyAccountStoreIface *)g;
1157
1158         klass->get_accounts =
1159                 modest_tny_account_store_get_accounts;
1160         klass->get_cache_dir =
1161                 modest_tny_account_store_get_cache_dir;
1162         klass->get_device =
1163                 modest_tny_account_store_get_device;
1164         klass->alert =
1165                 modest_tny_account_store_alert;
1166         klass->find_account =
1167                 modest_tny_account_store_find_account_by_url;
1168 }
1169
1170 void
1171 modest_tny_account_store_set_get_pass_func (ModestTnyAccountStore *self,
1172                                             ModestTnyGetPassFunc func)
1173 {
1174         /* not implemented, we use signals */
1175         g_printerr ("modest: set_get_pass_func not implemented\n");
1176 }
1177
1178 TnySessionCamel*
1179 modest_tny_account_store_get_session  (TnyAccountStore *self)
1180 {
1181         g_return_val_if_fail (self, NULL);
1182         return MODEST_TNY_ACCOUNT_STORE_GET_PRIVATE (self)->session;
1183 }
1184
1185 static TnyAccount*
1186 get_tny_account_by (TnyList *accounts,
1187                     ModestTnyAccountStoreQueryType type,
1188                     const gchar *str)
1189 {
1190         TnyIterator *iter = NULL;
1191         gboolean found = FALSE;
1192         TnyAccount *retval = NULL;
1193
1194         g_return_val_if_fail (TNY_IS_LIST(accounts), NULL);
1195
1196         if (tny_list_get_length(accounts) == 0) {
1197                 g_warning ("%s: account list is empty", __FUNCTION__);
1198                 return NULL;
1199         }
1200         
1201         iter = tny_list_create_iterator (accounts);
1202         while (!tny_iterator_is_done (iter) && !found) {
1203                 TnyAccount *tmp_account = NULL;
1204                 const gchar *val = NULL;
1205
1206                 tmp_account = TNY_ACCOUNT (tny_iterator_get_current (iter));
1207                 if (!TNY_IS_ACCOUNT(tmp_account)) {
1208                         g_warning ("%s: not a valid account", __FUNCTION__);
1209                         tmp_account = NULL;
1210                         break;
1211                 }
1212
1213                 switch (type) {
1214                 case MODEST_TNY_ACCOUNT_STORE_QUERY_ID:
1215                         val = tny_account_get_id (tmp_account);
1216                         break;
1217                 case MODEST_TNY_ACCOUNT_STORE_QUERY_URL:
1218                         val = tny_account_get_url_string (tmp_account);
1219                         break;
1220                 }
1221                 
1222                 if (type == MODEST_TNY_ACCOUNT_STORE_QUERY_URL && 
1223                     tny_account_matches_url_string (tmp_account, str)) {
1224                         retval = g_object_ref (tmp_account);
1225                         found = TRUE;
1226                 } else {
1227                         if (val && str && strcmp (val, str) == 0) {
1228                                 retval = g_object_ref (tmp_account);
1229                                 found = TRUE;
1230                         }
1231                 }
1232                 g_object_unref (tmp_account);
1233                 tny_iterator_next (iter);
1234         }
1235         g_object_unref (iter);
1236
1237         return retval;
1238 }
1239
1240 TnyAccount*
1241 modest_tny_account_store_get_tny_account_by (ModestTnyAccountStore *self, 
1242                                              ModestTnyAccountStoreQueryType type,
1243                                              const gchar *str)
1244 {
1245         TnyAccount *account = NULL;
1246         ModestTnyAccountStorePrivate *priv;     
1247         
1248         g_return_val_if_fail (self, NULL);
1249         g_return_val_if_fail (str, NULL);
1250         
1251         priv = MODEST_TNY_ACCOUNT_STORE_GET_PRIVATE(self);
1252         
1253         /* Search in store accounts */
1254         account = get_tny_account_by (priv->store_accounts, type, str);
1255
1256         /* If we already found something, no need to search the transport accounts */
1257         if (!account) {
1258                 account = get_tny_account_by (priv->transport_accounts, type, str);
1259
1260                 /* If we already found something, no need to search the
1261                    per-account outbox accounts */
1262                 if (!account)
1263                         account = get_tny_account_by (priv->store_accounts_outboxes, type, str);
1264         }
1265
1266         /* Warn if nothing was found. This is generally unusual. */
1267         if (!account) {
1268                 g_warning("%s: Failed to find account with %s=%s\n", 
1269                           __FUNCTION__, 
1270                           (type == MODEST_TNY_ACCOUNT_STORE_QUERY_ID) ? "ID" : "URL",                     
1271                           str);
1272         }
1273
1274         /* Returns a new reference to the account if found */   
1275         return account;
1276 }
1277
1278
1279 TnyAccount*
1280 modest_tny_account_store_get_server_account (ModestTnyAccountStore *self,
1281                                              const gchar *account_name,
1282                                              TnyAccountType type)
1283 {
1284         ModestTnyAccountStorePrivate *priv = NULL;
1285         TnyAccount *retval = NULL;
1286         TnyList *account_list = NULL;
1287         TnyIterator *iter = NULL;
1288         gboolean found;
1289
1290         g_return_val_if_fail (self, NULL);
1291         g_return_val_if_fail (account_name, NULL);
1292         g_return_val_if_fail (type == TNY_ACCOUNT_TYPE_STORE || 
1293                               type == TNY_ACCOUNT_TYPE_TRANSPORT,
1294                               NULL);
1295         
1296         priv = MODEST_TNY_ACCOUNT_STORE_GET_PRIVATE(self);
1297
1298         account_list = (type == TNY_ACCOUNT_TYPE_STORE) ? 
1299                 priv->store_accounts : 
1300                 priv->transport_accounts;
1301
1302         if (!account_list) {
1303                 g_printerr ("%s: No server accounts of type %s\n", __FUNCTION__, 
1304                         (type == TNY_ACCOUNT_TYPE_STORE) ? "store" : "transport");
1305                 return NULL;
1306         }
1307         
1308         /* Look for the server account */
1309         found = FALSE;
1310         iter = tny_list_create_iterator (account_list);
1311         while (!tny_iterator_is_done (iter) && !found) {
1312                 const gchar *modest_acc_name;
1313                 TnyAccount *tmp_account;
1314
1315                 tmp_account = TNY_ACCOUNT (tny_iterator_get_current (iter));
1316                 modest_acc_name = 
1317                         modest_tny_account_get_parent_modest_account_name_for_server_account (tmp_account);
1318                 
1319                 if (account_name && modest_acc_name && !strcmp (account_name, modest_acc_name)) {
1320                         found = TRUE;
1321                         retval = g_object_ref (tmp_account);
1322                 }
1323                 /* Free and continue */
1324                 g_object_unref (tmp_account);
1325                 tny_iterator_next (iter);
1326         }
1327         g_object_unref (iter);
1328
1329         if (!found) {
1330                 g_printerr ("modest: %s: could not get tny %s account for %s\n." \
1331                             "Number of server accounts of this type=%d\n", __FUNCTION__,
1332                             (type == TNY_ACCOUNT_TYPE_STORE) ? "store" : "transport",
1333                             account_name, tny_list_get_length (account_list));
1334         }
1335
1336         /* Returns a new reference */
1337         return retval;
1338 }
1339
1340 TnyAccount*
1341 modest_tny_account_store_get_smtp_specific_transport_account_for_open_connection (
1342         ModestTnyAccountStore *self, const gchar *account_name)
1343 {
1344         TnyDevice *device;
1345
1346         g_return_val_if_fail (self && MODEST_IS_TNY_ACCOUNT_STORE(self), NULL);
1347         g_return_val_if_fail (account_name, NULL);
1348
1349         /* Get the current connection: */
1350         device = modest_runtime_get_device ();
1351
1352         if (!device) {
1353                 g_warning ("%s: could not get device", __FUNCTION__);
1354                 return NULL;
1355         }
1356                 
1357         if (!tny_device_is_online (device))
1358                 return NULL;
1359         
1360 #ifdef MODEST_HAVE_CONIC
1361         g_return_val_if_fail (TNY_IS_MAEMO_CONIC_DEVICE (device), NULL);
1362         
1363         TnyMaemoConicDevice *maemo_device = TNY_MAEMO_CONIC_DEVICE (device);    
1364         const gchar* iap_id = tny_maemo_conic_device_get_current_iap_id (maemo_device);
1365         /* printf ("DEBUG: %s: iap_id=%s\n", __FUNCTION__, iap_id); */
1366         if (!iap_id)
1367                 return NULL;
1368                 
1369         ConIcIap* connection = tny_maemo_conic_device_get_iap (maemo_device, iap_id);
1370         if (!connection)
1371                 return NULL;
1372                 
1373         const gchar *connection_id = con_ic_iap_get_id (connection);
1374         /* printf ("DEBUG: %s: connection_id=%s\n", __FUNCTION__, connection_id); */
1375         if (!connection_id)
1376                 return NULL;
1377         
1378         /*  Get the connection-specific transport acccount, if any: */
1379         ModestAccountMgr *account_manager = modest_runtime_get_account_mgr ();
1380
1381         /* Check if this account has connection-specific SMTP enabled */
1382         if (!modest_account_mgr_get_use_connection_specific_smtp (account_manager, account_name)) {
1383                 return NULL;
1384         }
1385
1386         gchar* server_account_name = modest_account_mgr_get_connection_specific_smtp (account_manager, 
1387                 connection_id);
1388
1389         /* printf ("DEBUG: %s: server_account_name=%s\n", __FUNCTION__, server_account_name); */
1390         if (!server_account_name) {
1391                 return NULL; /* No connection-specific SMTP server was specified for this connection. */
1392         }
1393
1394         TnyAccount* account = modest_tny_account_store_get_tny_account_by (self, 
1395                                                                            MODEST_TNY_ACCOUNT_STORE_QUERY_ID, 
1396                                                                            server_account_name);
1397
1398         /* printf ("DEBUG: %s: account=%p\n", __FUNCTION__, account); */
1399         g_free (server_account_name);
1400
1401         /* Unref the get()ed object, as required by the tny_maemo_conic_device_get_iap() documentation. */
1402         g_object_unref (connection);
1403
1404         return account;
1405 #else
1406         return NULL; /* TODO: Implement this for GNOME, instead of just Maemo? */
1407 #endif /* MODEST_HAVE_CONIC */
1408 }
1409
1410                                                                  
1411 TnyAccount*
1412 modest_tny_account_store_get_transport_account_for_open_connection (ModestTnyAccountStore *self,
1413                                                                     const gchar *account_name)
1414 {
1415         g_return_val_if_fail (self, NULL);
1416         g_return_val_if_fail (account_name, NULL);
1417
1418         if (!account_name || !self)
1419                 return NULL;
1420         
1421         /*  Get the connection-specific transport acccount, if any: */
1422         /* Note: This gives us a reference: */
1423         TnyAccount *account =
1424                 modest_tny_account_store_get_smtp_specific_transport_account_for_open_connection (self, account_name);
1425                         
1426         /* If there is no connection-specific transport account (the common case), 
1427          * just get the regular transport account: */
1428         if (!account) {
1429                 /* The special local folders don't have transport accounts. */
1430                 if (strcmp (account_name, MODEST_LOCAL_FOLDERS_ACCOUNT_ID) == 0)
1431                         account = NULL;
1432                 else {
1433                         /* Note: This gives us a reference: */
1434                         account = modest_tny_account_store_get_server_account (self, account_name, 
1435                                                      TNY_ACCOUNT_TYPE_TRANSPORT);
1436                 }
1437         }
1438                         
1439         /* returns a reference. */     
1440         return account;
1441 }
1442
1443 TnyAccount*
1444 modest_tny_account_store_get_local_folders_account (ModestTnyAccountStore *self)
1445 {
1446         TnyAccount *account = NULL;
1447         ModestTnyAccountStorePrivate *priv;
1448         TnyIterator *iter;
1449         gboolean found;
1450
1451         g_return_val_if_fail (MODEST_IS_TNY_ACCOUNT_STORE (self), NULL);
1452         
1453         priv = MODEST_TNY_ACCOUNT_STORE_GET_PRIVATE(self);
1454
1455         found = FALSE;
1456         iter = tny_list_create_iterator (priv->store_accounts);
1457         while (!tny_iterator_is_done (iter) && !found) {
1458                 TnyAccount *tmp_account;
1459
1460                 tmp_account = TNY_ACCOUNT (tny_iterator_get_current (iter));
1461                 if (modest_tny_account_is_virtual_local_folders (tmp_account)) {
1462                         account = g_object_ref (tmp_account);
1463                         found = TRUE;
1464                 }
1465                 g_object_unref (tmp_account);
1466                 tny_iterator_next (iter);
1467         }
1468         g_object_unref (iter);
1469
1470         /* Returns a new reference to the account */
1471         return account;
1472 }
1473
1474 TnyAccount*
1475 modest_tny_account_store_get_mmc_folders_account (ModestTnyAccountStore *self)
1476 {
1477         g_return_val_if_fail (MODEST_IS_TNY_ACCOUNT_STORE (self), NULL);
1478         
1479         /* New reference */
1480         return modest_tny_account_store_get_tny_account_by (self, MODEST_TNY_ACCOUNT_STORE_QUERY_ID,
1481                                                             MODEST_MMC_ACCOUNT_ID);
1482
1483 }
1484
1485 /*********************************************************************************/
1486 static void
1487 add_existing_accounts (ModestTnyAccountStore *self)
1488 {
1489         GSList *account_names = NULL, *iter = NULL;
1490         ModestTnyAccountStorePrivate *priv = NULL;
1491         
1492         priv = MODEST_TNY_ACCOUNT_STORE_GET_PRIVATE(self);
1493
1494         /* These are account names, not server_account names */
1495         account_names = modest_account_mgr_account_names (priv->account_mgr, FALSE);
1496
1497         for (iter = account_names; iter != NULL; iter = g_slist_next (iter)) {
1498                 const gchar *account_name = (const gchar*) iter->data;
1499                 
1500                 /* Insert all enabled accounts without notifying */
1501                 if (modest_account_mgr_get_enabled (priv->account_mgr, account_name))
1502                         insert_account (self, account_name, FALSE);
1503         }
1504         modest_account_mgr_free_account_names (account_names);
1505 }
1506
1507 static TnyAccount*
1508 create_tny_account (ModestTnyAccountStore *self,
1509                     const gchar *name,
1510                     TnyAccountType type,
1511                     gboolean notify)
1512 {
1513         TnyAccount *account = NULL;
1514         ModestTnyAccountStorePrivate *priv = NULL;
1515
1516         priv = MODEST_TNY_ACCOUNT_STORE_GET_PRIVATE(self);
1517
1518         account = modest_tny_account_new_from_account (priv->account_mgr,
1519                                                        name, type, 
1520                                                        priv->session,
1521                                                        get_password,
1522                                                        forget_password);
1523
1524         if (account) {
1525                 /* Forget any cached password for the account, so that
1526                    we use a new account if any */
1527                 forget_password_in_memory (self, tny_account_get_id (account));
1528
1529                 /* Set the account store */
1530                 g_object_set_data (G_OBJECT(account), "account_store", self);
1531         } else {
1532                 g_printerr ("modest: failed to create account for %s\n", name);
1533         }
1534
1535         return account;
1536 }
1537
1538 typedef struct _AddOutboxInfo {
1539         ModestTnyAccountStore *account_store;
1540         TnyAccount *transport_account;
1541 } AddOutboxInfo;
1542
1543 static void
1544 add_outbox_from_transport_account_to_global_outbox_get_folders_cb (TnyFolderStore *folder_store,
1545                                                                    gboolean cancelled,
1546                                                                    TnyList *list,
1547                                                                    GError *err,
1548                                                                    gpointer userdata)
1549 {
1550         TnyIterator *iter_folders;
1551         TnyFolder *per_account_outbox;
1552         TnyAccount *local_account = NULL;
1553         AddOutboxInfo *info = (AddOutboxInfo *) userdata;
1554         ModestTnyAccountStorePrivate *priv = NULL;
1555         ModestTnyAccountStore *self;
1556
1557         self = MODEST_TNY_ACCOUNT_STORE (info->account_store);
1558         priv = MODEST_TNY_ACCOUNT_STORE_GET_PRIVATE(self);
1559         
1560         /* Note that this could happen if there is not enough space
1561            available on disk, then the outbox folder could not be
1562            created */
1563         if (tny_list_get_length (list) != 1) {
1564                 g_warning ("%s: could not create outbox folder (%d folders found)", __FUNCTION__,
1565                            tny_list_get_length (list));
1566                 goto frees;
1567         }
1568                         
1569         iter_folders = tny_list_create_iterator (list);
1570         per_account_outbox = TNY_FOLDER (tny_iterator_get_current (iter_folders));
1571         g_object_unref (iter_folders);
1572         g_object_unref (list);
1573
1574         /* Add the outbox of the new per-account-local-outbox account
1575            to the global local merged OUTBOX of the local folders
1576            account */
1577         local_account = modest_tny_account_store_get_local_folders_account (info->account_store);
1578         modest_tny_local_folders_account_add_folder_to_outbox (MODEST_TNY_LOCAL_FOLDERS_ACCOUNT (local_account),
1579                                                                per_account_outbox);
1580         /* Add the pair to the hash table */
1581         g_hash_table_insert (priv->outbox_of_transport,
1582                              info->transport_account,
1583                              per_account_outbox);
1584         
1585         /* Notify that the local account changed */
1586         g_signal_emit (G_OBJECT (self), signals [ACCOUNT_CHANGED_SIGNAL], 0, local_account);
1587         g_object_unref (local_account);
1588         g_object_unref (per_account_outbox);
1589
1590  frees:
1591         g_object_unref (info->transport_account);
1592         g_slice_free (AddOutboxInfo, info);
1593 }
1594                                                                    
1595
1596 static void
1597 add_outbox_from_transport_account_to_global_outbox (ModestTnyAccountStore *self,
1598                                                     const gchar *account_name,
1599                                                     TnyAccount *transport_account)
1600 {
1601         TnyList *folders = NULL;
1602         TnyAccount *account_outbox = NULL;
1603         ModestTnyAccountStorePrivate *priv = NULL;
1604         AddOutboxInfo *info;
1605
1606         priv = MODEST_TNY_ACCOUNT_STORE_GET_PRIVATE(self);
1607
1608         /* Create per account local outbox */
1609         account_outbox = 
1610                 modest_tny_account_new_for_per_account_local_outbox_folder (priv->account_mgr, 
1611                                                                             account_name, 
1612                                                                             priv->session);
1613
1614         if (!G_IS_OBJECT (account_outbox)) {
1615                 g_warning ("%s: could not create per account local outbox folder", __FUNCTION__);
1616                 return;
1617         }
1618
1619         tny_list_append (priv->store_accounts_outboxes, G_OBJECT (account_outbox));
1620         
1621         /* Get the outbox folder */
1622         folders = tny_simple_list_new ();
1623         info = g_slice_new0 (AddOutboxInfo);
1624         info->account_store = self;
1625         info->transport_account = g_object_ref (transport_account);
1626         tny_folder_store_get_folders_async (TNY_FOLDER_STORE (account_outbox), folders, NULL, FALSE, 
1627                                             add_outbox_from_transport_account_to_global_outbox_get_folders_cb, NULL, (gpointer) info);
1628         g_object_unref (account_outbox);
1629 }
1630
1631 /*
1632  * This function will be used for both adding new accounts and for the
1633  * initialization. In the initialization we do not want to emit
1634  * signals so notify will be FALSE, in the case of account additions
1635  * we do want to notify the observers
1636  */
1637 static void
1638 insert_account (ModestTnyAccountStore *self,
1639                 const gchar *account,
1640                 gboolean is_new)
1641 {
1642         ModestTnyAccountStorePrivate *priv = NULL;
1643         TnyAccount *store_account = NULL, *transport_account = NULL;
1644
1645         priv = MODEST_TNY_ACCOUNT_STORE_GET_PRIVATE(self);
1646
1647         /* Get the server and the transport account */
1648         store_account = create_tny_account (self, account, TNY_ACCOUNT_TYPE_STORE, is_new);
1649         if (!store_account || !TNY_IS_ACCOUNT(store_account)) {
1650                 g_warning ("%s: failed to create store account", __FUNCTION__);
1651                 return;
1652         }
1653
1654         transport_account = create_tny_account (self, account, TNY_ACCOUNT_TYPE_TRANSPORT, is_new);
1655         if (!transport_account || !TNY_IS_ACCOUNT(transport_account)) {
1656                 g_warning ("%s: failed to create transport account", __FUNCTION__);
1657                 g_object_unref (store_account);
1658                 return;
1659         }
1660
1661         /* Add accounts to the lists */
1662         tny_list_append (priv->store_accounts, G_OBJECT (store_account));
1663         tny_list_append (priv->transport_accounts, G_OBJECT (transport_account));
1664
1665         /* Create a new pseudo-account with an outbox for this
1666            transport account and add it to the global outbox
1667            in the local account */
1668         add_outbox_from_transport_account_to_global_outbox (self, account, transport_account);
1669
1670         /* Force the creation of the send queue, this way send queues
1671            will automatically send missing emails when the connections
1672            become active */
1673         /* Notify the observers. We do it after everything is
1674            created */
1675         if (is_new) {
1676                 /* We only have to do this for new accounts, already
1677                    existing accounts at boot time are instantiated by
1678                    modest_tny_account_store_start_send_queues */
1679                 modest_runtime_get_send_queue ((TnyTransportAccount *) transport_account, TRUE);
1680
1681                 g_signal_emit (G_OBJECT (self), signals [ACCOUNT_INSERTED_SIGNAL], 0, store_account);
1682                 g_signal_emit (G_OBJECT (self), signals [ACCOUNT_INSERTED_SIGNAL], 0, transport_account);
1683         }
1684
1685         /* Frees */
1686         g_object_unref (store_account);
1687         g_object_unref (transport_account);
1688 }
1689
1690 static inline gboolean
1691 only_local_accounts (ModestTnyAccountStore *self)
1692 {
1693         return (modest_tny_account_store_get_num_remote_accounts (self) > 0) ? FALSE : TRUE;
1694 }
1695
1696 static void
1697 on_account_inserted (ModestAccountMgr *acc_mgr, 
1698                      const gchar *account,
1699                      gpointer user_data)
1700 {
1701         gboolean add_specific;
1702
1703         add_specific = only_local_accounts (MODEST_TNY_ACCOUNT_STORE (user_data));
1704
1705         /* Insert the account and notify the observers */
1706         insert_account (MODEST_TNY_ACCOUNT_STORE (user_data), account, TRUE);
1707
1708         /* If it's the first remote account then add the connection
1709            specific SMTP servers as well */
1710         if (add_specific)
1711                 add_connection_specific_transport_accounts (MODEST_TNY_ACCOUNT_STORE (user_data));
1712
1713 }
1714
1715 /* This is the callback of the tny_camel_account_set_online called in
1716    on_account_removed to disconnect the account */
1717 static void
1718 on_account_disconnect_when_removing (TnyCamelAccount *account, 
1719                                      gboolean canceled, 
1720                                      GError *err, 
1721                                      gpointer user_data)
1722 {
1723         ModestTnyAccountStore *self;
1724         ModestTnyAccountStorePrivate *priv;
1725
1726         self = MODEST_TNY_ACCOUNT_STORE (user_data);
1727         priv = MODEST_TNY_ACCOUNT_STORE_GET_PRIVATE (self);
1728
1729         /* Cancel all pending operations */
1730         tny_account_cancel (TNY_ACCOUNT (account));
1731         
1732         /* Unref the extra reference added by get_server_account */
1733         g_object_unref (account);
1734
1735         /* Clear the cache if it's an store account */
1736         if (TNY_IS_STORE_ACCOUNT (account)) {
1737                 tny_store_account_delete_cache (TNY_STORE_ACCOUNT (account));
1738         } else if (TNY_IS_TRANSPORT_ACCOUNT (account)) {
1739                 ModestTnySendQueue* send_queue;
1740                 send_queue = modest_runtime_get_send_queue (TNY_TRANSPORT_ACCOUNT (account), FALSE);
1741                 if (TNY_IS_SEND_QUEUE (send_queue)) {
1742                         if (modest_tny_send_queue_sending_in_progress (send_queue))
1743                                 tny_send_queue_cancel (TNY_SEND_QUEUE (send_queue),
1744                                                        TNY_SEND_QUEUE_CANCEL_ACTION_REMOVE, 
1745                                                        NULL);
1746                         modest_runtime_remove_send_queue (TNY_TRANSPORT_ACCOUNT (account));
1747                 }
1748         }
1749 }
1750
1751 /*
1752  * We use this one for both removing "normal" and "connection
1753  * specific" transport accounts
1754  */
1755 static void
1756 remove_transport_account (ModestTnyAccountStore *self,
1757                           TnyTransportAccount *transport_account)
1758 {
1759         ModestTnyAccountStorePrivate *priv;
1760         TnyFolder *outbox = NULL;
1761         
1762         priv = MODEST_TNY_ACCOUNT_STORE_GET_PRIVATE (self);
1763
1764         /* Remove it from the list of accounts and notify the
1765            observers. Do not need to wait for account
1766            disconnection */
1767         g_signal_emit (G_OBJECT (self), signals [ACCOUNT_REMOVED_SIGNAL], 0, transport_account);
1768         tny_list_remove (priv->transport_accounts, (GObject *) transport_account);
1769                 
1770         /* Remove the OUTBOX of the account from the global outbox */
1771         outbox = g_hash_table_lookup (priv->outbox_of_transport, transport_account);
1772
1773         if (TNY_IS_FOLDER (outbox)) {
1774                 TnyAccount *local_account = NULL;
1775                 TnyAccount *outbox_account = tny_folder_get_account (outbox);
1776
1777                 if (outbox_account) {
1778                         tny_list_remove (priv->store_accounts_outboxes, G_OBJECT (outbox_account));
1779                         /* Remove existing emails to send */
1780                         tny_store_account_delete_cache (TNY_STORE_ACCOUNT (outbox_account));
1781                         g_object_unref (outbox_account);
1782                 }
1783
1784                 local_account = modest_tny_account_store_get_local_folders_account (self);
1785                 modest_tny_local_folders_account_remove_folder_from_outbox (MODEST_TNY_LOCAL_FOLDERS_ACCOUNT (local_account),
1786                                                                             outbox);
1787
1788                 g_hash_table_remove (priv->outbox_of_transport, transport_account);
1789
1790                 /* Notify the change in the local account */
1791                 g_signal_emit (G_OBJECT (self), signals [ACCOUNT_CHANGED_SIGNAL], 0, local_account);
1792                 g_object_unref (local_account);
1793         } else {
1794                 g_warning ("Removing a transport account that has no outbox");
1795         }
1796
1797         /* Cancel all pending operations */
1798         tny_account_cancel (TNY_ACCOUNT (transport_account));
1799
1800         /* Disconnect and notify the observers. The callback will free the reference */
1801         tny_camel_account_set_online (TNY_CAMEL_ACCOUNT (transport_account), FALSE,
1802                                       on_account_disconnect_when_removing, self);
1803 }
1804
1805 static gboolean
1806 images_cache_remove_filter (TnyStreamCache *self, const gchar *id, const gchar *account_name)
1807 {
1808         gchar *account_name_with_separator;
1809         gboolean result;
1810         if (account_name == NULL || account_name[0] == '\0')
1811                 return FALSE;
1812
1813         if (id == NULL || id[0] == '\0')
1814                 return FALSE;
1815
1816         account_name_with_separator = g_strconcat (account_name, "__", NULL);
1817
1818         result = (g_str_has_prefix (id, account_name));
1819         g_free (account_name_with_separator);
1820
1821         return result;
1822 }
1823
1824 static void
1825 on_account_removed (ModestAccountMgr *acc_mgr, 
1826                     const gchar *account,
1827                     gpointer user_data)
1828 {
1829         TnyAccount *store_account = NULL, *transport_account = NULL;
1830         ModestTnyAccountStore *self;
1831         ModestTnyAccountStorePrivate *priv;
1832         TnyStreamCache *stream_cache;
1833         
1834         self = MODEST_TNY_ACCOUNT_STORE (user_data);
1835         priv = MODEST_TNY_ACCOUNT_STORE_GET_PRIVATE (self);
1836
1837         /* Get the server and the transport account */
1838         store_account = 
1839                 modest_tny_account_store_get_server_account (self, account, 
1840                                                              TNY_ACCOUNT_TYPE_STORE);
1841         transport_account = 
1842                 modest_tny_account_store_get_server_account (self, account,
1843                                                              TNY_ACCOUNT_TYPE_TRANSPORT);
1844         
1845         /* If there was any problem creating the account, for example,
1846            with the configuration system this could not exist */
1847         if (TNY_IS_STORE_ACCOUNT(store_account)) {
1848                 /* Forget any cached password for the account */
1849                 forget_password_in_memory (self, tny_account_get_id (store_account));
1850
1851                 /* Remove it from the list of accounts and notify the
1852                    observers. Do not need to wait for account
1853                    disconnection */
1854                 tny_list_remove (priv->store_accounts, (GObject *) store_account);
1855                 g_signal_emit (G_OBJECT (self), signals [ACCOUNT_REMOVED_SIGNAL], 0, store_account);
1856
1857                 /* Cancel all pending operations */
1858                 tny_account_cancel (TNY_ACCOUNT (store_account));
1859
1860                 /* Disconnect before deleting the cache, because the
1861                    disconnection will rewrite the cache to the
1862                    disk */
1863                 tny_camel_account_set_online (TNY_CAMEL_ACCOUNT (store_account), FALSE,
1864                                               on_account_disconnect_when_removing, self);
1865         } else {
1866                 g_warning ("%s: no store account for account %s\n", 
1867                            __FUNCTION__, account);
1868         }
1869
1870         /* If there was any problem creating the account, for example,
1871            with the configuration system this could not exist */
1872         if (TNY_IS_TRANSPORT_ACCOUNT(transport_account)) {
1873
1874                 /* Forget any cached password for the account */
1875                 forget_password_in_memory (self, tny_account_get_id (transport_account));
1876
1877                 /* Remove transport account. It'll free the reference
1878                    added by get_server_account */
1879                 remove_transport_account (self, TNY_TRANSPORT_ACCOUNT (transport_account));
1880         } else {
1881                 g_warning ("%s: no transport account for account %s\n", 
1882                            __FUNCTION__, account);
1883         }
1884
1885         /* Remove cached images */
1886         stream_cache = modest_runtime_get_images_cache ();
1887         tny_stream_cache_remove (stream_cache, (TnyStreamCacheRemoveFilter) images_cache_remove_filter, (gpointer) account);
1888
1889         /* If there are no more user accounts then delete the
1890            transport specific SMTP servers */
1891         if (only_local_accounts (self))
1892                 remove_connection_specific_transport_accounts (self);
1893 }
1894
1895 TnyTransportAccount *
1896 modest_tny_account_store_new_connection_specific_transport_account (ModestTnyAccountStore *self,
1897                                                                     const gchar *name)
1898 {
1899         ModestTnyAccountStorePrivate *priv = NULL;
1900         TnyAccount * tny_account = NULL;
1901
1902         priv = MODEST_TNY_ACCOUNT_STORE_GET_PRIVATE(self);
1903
1904         /* Add the account: */
1905         tny_account = 
1906                 modest_tny_account_new_from_server_account_name (priv->account_mgr, 
1907                                                                  priv->session, 
1908                                                                  name,
1909                                                                  get_password,
1910                                                                  forget_password);
1911         if (tny_account) {
1912                 g_object_set_data (G_OBJECT(tny_account), 
1913                                    "account_store", 
1914                                    (gpointer)self);
1915                 g_object_set_data (G_OBJECT(tny_account), 
1916                                    "connection_specific", 
1917                                    GINT_TO_POINTER (TRUE));
1918                 
1919                 tny_list_append (priv->transport_accounts, G_OBJECT (tny_account));
1920                 add_outbox_from_transport_account_to_global_outbox (self, 
1921                                                                     name, 
1922                                                                     tny_account);
1923                 
1924         } else
1925                 g_printerr ("modest: failed to create smtp-specific account for %s\n",
1926                             name);
1927
1928         return TNY_TRANSPORT_ACCOUNT (tny_account);
1929 }
1930
1931 static void 
1932 foreach_free_string(gpointer data,
1933                     gpointer user_data)
1934 {
1935         g_free (data);
1936 }
1937
1938 static void
1939 add_connection_specific_transport_accounts (ModestTnyAccountStore *self)
1940 {
1941         ModestTnyAccountStorePrivate *priv = NULL;
1942         GSList *list_specifics = NULL, *iter = NULL;
1943         GError *err = NULL;
1944
1945         priv = MODEST_TNY_ACCOUNT_STORE_GET_PRIVATE(self);
1946
1947         list_specifics = modest_conf_get_list (modest_runtime_get_conf (),
1948                                                MODEST_CONF_CONNECTION_SPECIFIC_SMTP_LIST,
1949                                                MODEST_CONF_VALUE_STRING, &err);
1950         if (err) {
1951                 g_error_free (err);
1952                 g_return_if_reached ();
1953                 return;
1954         }
1955                                 
1956         /* Look at each connection-specific transport account for the 
1957          * modest account: */
1958         iter = list_specifics;
1959         while (iter) {
1960                 /* The list alternates between the connection name and the transport name: */
1961                 iter = g_slist_next (iter);
1962                 if (iter) {
1963                         const gchar* transport_account_name = (const gchar*) (iter->data);
1964                         TnyTransportAccount * account = NULL;
1965                         account = modest_tny_account_store_new_connection_specific_transport_account (
1966                                 self, transport_account_name);
1967                         if (account)
1968                                 g_object_unref (account);
1969                 }                               
1970                 iter = g_slist_next (iter);
1971         }
1972
1973         /* Free the list */
1974         g_slist_foreach (list_specifics, foreach_free_string, NULL);
1975         g_slist_free (list_specifics);
1976 }
1977
1978 static void
1979 remove_connection_specific_transport_accounts (ModestTnyAccountStore *self)
1980 {
1981         ModestTnyAccountStorePrivate *priv = NULL;
1982         GSList *list_specifics = NULL, *iter = NULL;
1983         GError *err = NULL;
1984
1985         priv = MODEST_TNY_ACCOUNT_STORE_GET_PRIVATE(self);
1986
1987         err = NULL;
1988         list_specifics = modest_conf_get_list (modest_runtime_get_conf (),
1989                                                MODEST_CONF_CONNECTION_SPECIFIC_SMTP_LIST,
1990                                                MODEST_CONF_VALUE_STRING, &err);
1991         if (err) {
1992                 g_error_free (err);
1993                 g_return_if_reached ();
1994                 return;
1995         }
1996                                 
1997         /* Look at each connection-specific transport account for the 
1998          * modest account: */
1999         iter = list_specifics;
2000         while (iter) {
2001                 /* The list alternates between the connection name and the transport name: */
2002                 iter = g_slist_next (iter);
2003                 if (iter) {
2004                         const gchar* transport_account_name = (const gchar*) (iter->data);
2005                         TnyAccount * account;
2006                         account = modest_tny_account_store_get_server_account (self,
2007                                                                                transport_account_name,
2008                                                                                TNY_ACCOUNT_TYPE_TRANSPORT);
2009
2010                         /* the call will free the reference */
2011                         if (account)
2012                                 remove_transport_account (self, TNY_TRANSPORT_ACCOUNT (account));
2013                 }                               
2014                 iter = g_slist_next (iter);
2015         }
2016
2017         /* Free the list */
2018         g_slist_foreach (list_specifics, foreach_free_string, NULL);
2019         g_slist_free (list_specifics);
2020 }
2021
2022
2023 TnyMsg *
2024 modest_tny_account_store_find_msg_in_outboxes (ModestTnyAccountStore *self, 
2025                                                const gchar *uri,
2026                                                TnyAccount **ac_out)
2027 {
2028         TnyIterator *acc_iter;
2029         ModestTnyAccountStorePrivate *priv;
2030         TnyMsg *msg = NULL;
2031         TnyAccount *msg_account = NULL;
2032
2033         g_return_val_if_fail (MODEST_IS_TNY_ACCOUNT_STORE (self), NULL);
2034         priv = MODEST_TNY_ACCOUNT_STORE_GET_PRIVATE (self);
2035
2036         acc_iter = tny_list_create_iterator (priv->store_accounts_outboxes);
2037         while (!msg && !tny_iterator_is_done (acc_iter)) {
2038                 TnyList *folders = tny_simple_list_new ();
2039                 TnyAccount *account = TNY_ACCOUNT (tny_iterator_get_current (acc_iter));
2040                 TnyIterator *folders_iter = NULL;
2041
2042                 tny_folder_store_get_folders (TNY_FOLDER_STORE (account), folders, NULL, FALSE, NULL);
2043                 folders_iter = tny_list_create_iterator (folders);
2044
2045                 while (msg == NULL && !tny_iterator_is_done (folders_iter)) {
2046                         TnyFolder *folder = TNY_FOLDER (tny_iterator_get_current (folders_iter));
2047                         msg = tny_folder_find_msg (folder, uri, NULL);
2048
2049                         if (msg)
2050                                 msg_account = g_object_ref (account);
2051
2052                         g_object_unref (folder);
2053                         tny_iterator_next (folders_iter);
2054                 }
2055                 g_object_unref (folders_iter);
2056
2057                 g_object_unref (folders);
2058                 g_object_unref (account);
2059                 tny_iterator_next (acc_iter);
2060         }
2061
2062         g_object_unref (acc_iter);
2063
2064         if (ac_out != NULL)
2065                 *ac_out = msg_account;
2066
2067         return msg;
2068 }
2069
2070 TnyTransportAccount *
2071 modest_tny_account_store_get_transport_account_from_outbox_header(ModestTnyAccountStore *self, TnyHeader *header)
2072 {
2073         TnyIterator *acc_iter;
2074         ModestTnyAccountStorePrivate *priv;
2075         TnyTransportAccount *header_acc = NULL;
2076         gchar *msg_id;
2077
2078         g_return_val_if_fail (MODEST_IS_TNY_ACCOUNT_STORE (self), NULL);
2079         g_return_val_if_fail (TNY_IS_HEADER (header), NULL);
2080         priv = MODEST_TNY_ACCOUNT_STORE_GET_PRIVATE (self);
2081         msg_id = modest_tny_send_queue_get_msg_id (header);
2082         acc_iter = tny_list_create_iterator (priv->transport_accounts);
2083         while (!header_acc && !tny_iterator_is_done (acc_iter)) {
2084                 TnyTransportAccount *account = TNY_TRANSPORT_ACCOUNT (tny_iterator_get_current (acc_iter));
2085                 ModestTnySendQueue *send_queue;
2086                 ModestTnySendQueueStatus status;
2087                 send_queue = modest_runtime_get_send_queue(TNY_TRANSPORT_ACCOUNT(account), TRUE);
2088                 if (TNY_IS_SEND_QUEUE (send_queue)) {
2089                         status = modest_tny_send_queue_get_msg_status(send_queue, msg_id);
2090                         if (status != MODEST_TNY_SEND_QUEUE_UNKNOWN)
2091                                 header_acc = g_object_ref(account);
2092                 }
2093                 g_object_unref (account);
2094                 tny_iterator_next (acc_iter);
2095         }
2096         g_object_unref(acc_iter);
2097         g_free (msg_id);
2098
2099         /* New reference */
2100         return header_acc;
2101 }
2102
2103 typedef struct {
2104         ModestTnyAccountStore *account_store;
2105         ModestTnyAccountStoreShutdownCallback callback;
2106         gpointer userdata;
2107         gint pending;
2108 } ShutdownOpData;
2109
2110 static void
2111 account_shutdown_callback (TnyCamelAccount *account, gboolean canceled, GError *err, gpointer userdata)
2112 {
2113         ShutdownOpData *op_data = (ShutdownOpData *) userdata;
2114         op_data->pending--;
2115         if (op_data->pending == 0) {
2116                 if (op_data->callback)
2117                         op_data->callback (op_data->account_store, op_data->userdata);
2118                 g_object_unref (op_data->account_store);
2119                 g_free (op_data);
2120         } else {
2121                 g_object_unref (op_data->account_store);
2122         }
2123 }
2124
2125 static void
2126 account_shutdown (TnyAccount *account, ShutdownOpData *op_data)
2127 {
2128         g_return_if_fail (account && TNY_IS_ACCOUNT(account));
2129
2130         if (TNY_IS_STORE_ACCOUNT (account) && 
2131             !modest_tny_folder_store_is_remote (TNY_FOLDER_STORE (account)))
2132                 goto frees;
2133
2134         /* Disconnect account */
2135         if (tny_account_get_connection_status (account) == TNY_CONNECTION_STATUS_CONNECTED) {
2136                 tny_camel_account_set_online (TNY_CAMEL_ACCOUNT(account), FALSE, 
2137                                               account_shutdown_callback, op_data);
2138                 return;
2139         }
2140
2141  frees:
2142         op_data->pending--;
2143         g_object_unref (op_data->account_store);
2144 }
2145
2146
2147 void
2148 modest_tny_account_store_shutdown (ModestTnyAccountStore *self,
2149                                    ModestTnyAccountStoreShutdownCallback callback,
2150                                    gpointer userdata)
2151 {
2152         gint i, num_accounts;
2153         ShutdownOpData *op_data;
2154         ModestTnyAccountStorePrivate *priv = MODEST_TNY_ACCOUNT_STORE_GET_PRIVATE (self);
2155
2156         /* Get references */
2157         num_accounts = 0;
2158         if (priv->store_accounts)
2159                 num_accounts += tny_list_get_length (priv->store_accounts);
2160         if (priv->transport_accounts)
2161                 num_accounts += tny_list_get_length (priv->transport_accounts);
2162
2163         for (i = 0 ; i < num_accounts ; i++)
2164                 g_object_ref (self);
2165
2166         /* Create the helper object */
2167         op_data = g_new0 (ShutdownOpData, 1);
2168         op_data->callback = callback;
2169         op_data->userdata = userdata;
2170         op_data->pending = num_accounts;
2171         op_data->account_store = self;
2172
2173         /* Destroy all accounts. Disconnect all accounts before they are destroyed */
2174         if (priv->store_accounts) {
2175                 tny_list_foreach (priv->store_accounts, (GFunc)account_shutdown, op_data);
2176         }
2177
2178         if (priv->transport_accounts) {
2179                 tny_list_foreach (priv->transport_accounts, (GFunc)account_shutdown, op_data);
2180         }
2181
2182         if (op_data->pending == 0) {
2183                 if (op_data->callback)
2184                         op_data->callback (op_data->account_store, op_data->userdata);
2185                 g_free (op_data);
2186         }
2187 }
2188
2189 gboolean 
2190 modest_tny_account_store_is_send_mail_blocked (ModestTnyAccountStore *self)
2191 {
2192         ModestTnyAccountStorePrivate *priv;
2193
2194         priv = MODEST_TNY_ACCOUNT_STORE_GET_PRIVATE(self);
2195
2196         return priv->send_mail_blocked;
2197 }
2198
2199 void 
2200 modest_tny_account_store_set_send_mail_blocked (ModestTnyAccountStore *self, 
2201                                                 gboolean blocked)
2202 {
2203         ModestTnyAccountStorePrivate *priv;
2204
2205         priv = MODEST_TNY_ACCOUNT_STORE_GET_PRIVATE(self);
2206
2207         priv->send_mail_blocked = blocked;
2208 }
2209
2210 static void
2211 count_remote_accounts (gpointer data, gpointer user_data)
2212 {
2213         TnyFolderStore *account = TNY_FOLDER_STORE (data);
2214         gint *count = (gint *) user_data;
2215
2216         if (modest_tny_folder_store_is_remote (account))
2217                 (*count)++;
2218 }
2219
2220 guint
2221 modest_tny_account_store_get_num_remote_accounts (ModestTnyAccountStore *self)
2222 {
2223         ModestTnyAccountStorePrivate *priv;
2224         gint count = 0;
2225
2226         g_return_val_if_fail (MODEST_IS_TNY_ACCOUNT_STORE (self), 0);
2227
2228         /* Count remote accounts */
2229         priv = MODEST_TNY_ACCOUNT_STORE_GET_PRIVATE (self);
2230         tny_list_foreach (priv->store_accounts, (GFunc) count_remote_accounts, &count);
2231
2232         return count;
2233 }
2234
2235 static void
2236 init_send_queue (TnyAccount *account, gpointer user_data)
2237 {
2238         modest_runtime_get_send_queue ((TnyTransportAccount *) account, TRUE);
2239 }
2240
2241 void
2242 modest_tny_account_store_start_send_queues (ModestTnyAccountStore *self)
2243 {
2244         ModestTnyAccountStorePrivate *priv;
2245         TnyList *tmp;
2246
2247         g_return_if_fail (MODEST_IS_TNY_ACCOUNT_STORE (self));
2248
2249         priv = MODEST_TNY_ACCOUNT_STORE_GET_PRIVATE (self);
2250
2251         /* We need to create a copy of the list because if the send
2252            queues are created they'll directly access to the TnyList
2253            of transport accounts, and thus we'll end up blocked in the
2254            mutex the TnyList uses to synchronize accesses */
2255         tmp = tny_list_copy (priv->transport_accounts);
2256
2257         /* Just instantiate them. They'll begin to listen for
2258            connection changes to send messages ASAP */
2259         tny_list_foreach (tmp, (GFunc) init_send_queue, NULL);
2260         g_object_unref (tmp);
2261 }
2262
2263
2264 gboolean
2265 modest_tny_account_store_check_disk_full_error (ModestTnyAccountStore *self,
2266                                                 GtkWidget *parent_window,
2267                                                 GError *err,
2268                                                 TnyAccount *account,
2269                                                 const gchar *alternate)
2270 {
2271         if (err == NULL)
2272                 return FALSE;
2273
2274         if (modest_tny_account_store_is_disk_full_error (self, err, account)) {
2275                 gboolean is_mcc = modest_tny_account_is_memory_card_account (account);
2276                 if (is_mcc && alternate) {
2277                         modest_platform_information_banner (parent_window, NULL, alternate);
2278                 } else {
2279                         gchar *msg = g_strdup_printf (_KR("cerm_device_memory_full"), "");
2280                         modest_platform_information_banner (parent_window, NULL, msg);
2281                         g_free (msg);
2282                 }
2283         } else if (err->code == TNY_SYSTEM_ERROR_MEMORY)
2284                 /* If the account was created in memory full
2285                    conditions then tinymail won't be able to
2286                    connect so it'll return this error code */
2287                 modest_platform_information_banner (parent_window,
2288                                                     NULL, _("emev_ui_imap_inbox_select_error"));
2289         else
2290                 return FALSE;
2291
2292         return TRUE;
2293 }
2294
2295 gboolean
2296 modest_tny_account_store_is_disk_full_error (ModestTnyAccountStore *self,
2297                                              GError *error,
2298                                              TnyAccount *account)
2299 {
2300         gboolean enough_free_space = TRUE;
2301         GnomeVFSURI *cache_dir_uri;
2302         const gchar *cache_dir = NULL;
2303         GnomeVFSFileSize free_space;
2304
2305         /* Cache dir is different in case we're using an external storage (like MMC account) */
2306         if (account && modest_tny_account_is_memory_card_account (account))
2307                 cache_dir = g_getenv (MODEST_MMC1_VOLUMEPATH_ENV);
2308
2309         /* Get the default local cache dir */
2310         if (!cache_dir)
2311                 cache_dir = tny_account_store_get_cache_dir ((TnyAccountStore *) self);
2312
2313         cache_dir_uri = gnome_vfs_uri_new (cache_dir);
2314         if (cache_dir_uri) {
2315                 if (gnome_vfs_get_volume_free_space (cache_dir_uri, &free_space) == GNOME_VFS_OK) {
2316                         if (free_space < MODEST_TNY_ACCOUNT_STORE_MIN_FREE_SPACE)
2317                                 enough_free_space = FALSE;
2318                 }
2319                 gnome_vfs_uri_unref (cache_dir_uri);
2320         }
2321
2322         if ((error->code == TNY_SYSTEM_ERROR_MEMORY ||
2323              /* When asking for a mail and no space left on device
2324                 tinymail returns this error */
2325              error->code == TNY_SERVICE_ERROR_MESSAGE_NOT_AVAILABLE ||
2326              /* When the folder summary could not be read or
2327                 written */
2328              error->code == TNY_IO_ERROR_WRITE ||
2329              error->code == TNY_IO_ERROR_READ) &&
2330             !enough_free_space) {
2331                 return TRUE;
2332         } else {
2333                 return FALSE;
2334         }
2335 }